summaryrefslogtreecommitdiff
path: root/addons/web/static/lib
diff options
context:
space:
mode:
authorXavier Morel <xmo@openerp.com>2011-09-29 15:54:49 +0200
committerXavier Morel <xmo@openerp.com>2011-09-29 15:54:49 +0200
commitd32d0c5637e0b7d85c97278e4479d4e5f8ff1d69 (patch)
treef4d710ddf8dd1f1d08f942ae3150efd77f2df536 /addons/web/static/lib
parentc94872fc0236259e4c5155ab63f5e878f65e4556 (diff)
[IMP] update py.parse to 0.2
bzr revid: xmo@openerp.com-20110929135449-ssommvnyof6auy29
Diffstat (limited to 'addons/web/static/lib')
-rw-r--r--addons/web/static/lib/py.parse/.hg_archival.txt5
-rw-r--r--addons/web/static/lib/py.parse/README1
-rw-r--r--addons/web/static/lib/py.parse/TODO.rst14
-rw-r--r--addons/web/static/lib/py.parse/lib/py.js116
-rw-r--r--addons/web/static/lib/py.parse/test/test.js90
5 files changed, 196 insertions, 30 deletions
diff --git a/addons/web/static/lib/py.parse/.hg_archival.txt b/addons/web/static/lib/py.parse/.hg_archival.txt
new file mode 100644
index 00000000000..43d5d80a9e4
--- /dev/null
+++ b/addons/web/static/lib/py.parse/.hg_archival.txt
@@ -0,0 +1,5 @@
+repo: 076b192d0d8ab2b92d1dbcfa3da055382f30eaea
+node: 87fb1b67d6a13f10a1a328104ee4d4b2c36801ec
+branch: default
+latesttag: 0.2
+latesttagdistance: 1
diff --git a/addons/web/static/lib/py.parse/README b/addons/web/static/lib/py.parse/README
new file mode 100644
index 00000000000..453f001ee15
--- /dev/null
+++ b/addons/web/static/lib/py.parse/README
@@ -0,0 +1 @@
+Parser and evaluator of Python expressions
diff --git a/addons/web/static/lib/py.parse/TODO.rst b/addons/web/static/lib/py.parse/TODO.rst
new file mode 100644
index 00000000000..3638b681ddc
--- /dev/null
+++ b/addons/web/static/lib/py.parse/TODO.rst
@@ -0,0 +1,14 @@
+* Parser
+ since parsing expressions, try with a pratt parser
+ http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
+ http://effbot.org/zone/simple-top-down-parsing.htm
+
+Evaluator
+---------
+
+* Stop busyworking trivial binary operator
+* Make it *trivial* to build Python type-wrappers
+* Implement Python's `data model
+ protocols<http://docs.python.org/reference/datamodel.html#basic-customization>`_
+ for *all* supported operations, optimizations can come later
+* Automatically type-wrap everything (for now anyway)
diff --git a/addons/web/static/lib/py.parse/lib/py.js b/addons/web/static/lib/py.parse/lib/py.js
index d5cf304bb58..0f2415f1252 100644
--- a/addons/web/static/lib/py.parse/lib/py.js
+++ b/addons/web/static/lib/py.parse/lib/py.js
@@ -5,7 +5,7 @@ var py = {};
NAME = /^[a-zA-Z0-9_]$/;
var create = function (o, props) {
- function F() {};
+ function F() {}
F.prototype = o;
var inst = new F;
for(var name in props) {
@@ -13,7 +13,7 @@ var py = {};
inst[name] = props[name];
}
return inst;
- }
+ };
var symbols = {};
var comparators = {};
@@ -26,17 +26,17 @@ var py = {};
} else if (this.id === '(end)') {
return '(end)';
} else if (this.id === '(comparator)' ) {
- var out = ['(comparator', this.expressions[0]];
+ var repr = ['(comparator', this.expressions[0]];
for (var i=0;i<this.operators.length; ++i) {
- out.push(this.operators[i], this.expressions[i+1]);
+ repr.push(this.operators[i], this.expressions[i+1]);
}
- return out.join(' ') + ')';
+ return repr.join(' ') + ')';
}
var out = [this.id, this.first, this.second, this.third]
.filter(function (r){return r}).join(' ');
return '(' + out + ')';
}
- }
+ };
function symbol(id, bp) {
bp = bp || 0;
var s = symbols[id];
@@ -57,7 +57,7 @@ var py = {};
this.value = id;
return this;
};
- };
+ }
function prefix(id, bp, nud) {
symbol(id).nud = nud || function () {
this.first = expression(bp);
@@ -149,7 +149,7 @@ var py = {};
infix('*', 120); infix('/', 120);
infix('//', 120), infix('%', 120);
- prefix('-', 130); prefix('+', 130); prefix('~', 130)
+ prefix('-', 130); prefix('+', 130); prefix('~', 130);
infixr('**', 140);
@@ -170,7 +170,7 @@ var py = {};
if (token.id === ')') {
break;
}
- this.first.push(expression())
+ this.first.push(expression());
if (token.id !== ',') {
break;
}
@@ -202,11 +202,11 @@ var py = {};
};
infix('[', 150, function (left) {
- this.first = left
- this.second = expression()
- advance("]")
+ this.first = left;
+ this.second = expression();
+ advance("]");
return this;
- })
+ });
symbol('[').nud = function () {
this.first = [];
if (token.id !== ']') {
@@ -253,8 +253,8 @@ var py = {};
'!': ['='],
'=': ['='],
'/': ['/']
- }
- function Tokenizer(str) {
+ };
+ function Tokenizer() {
this.states = ['initial'];
this.tokens = [];
}
@@ -369,17 +369,18 @@ var py = {};
this.builder().push(character);
return index + 1;
}
- }
+ };
exports.tokenize = function tokenize(str) {
var index = 0,
- str = str + '\0',
tokenizer = new Tokenizer(str);
+ str += '\0';
+
do {
index = tokenizer.feed(str, index);
- } while (index !== str.length)
+ } while (index !== str.length);
return tokenizer.tokens;
- }
+ };
var token, next;
function expression(rbp) {
@@ -403,6 +404,7 @@ var py = {};
}
exports.object = create({}, {});
+ exports.bool = function (arg) { return !!arg; };
exports.tuple = create(exports.object, {
__contains__: function (value) {
for(var i=0, len=this.values.length; i<len; ++i) {
@@ -411,6 +413,15 @@ var py = {};
}
}
return false;
+ },
+ toJSON: function () {
+ return this.values;
+ }
+ });
+ exports.list = exports.tuple;
+ exports.dict = create(exports.object, {
+ toJSON: function () {
+ return this.values;
}
});
@@ -420,7 +431,7 @@ var py = {};
next = function () { return toks[++index]; };
return expression();
};
- evaluate_operator = function (operator, a, b) {
+ var evaluate_operator = function (operator, a, b) {
switch (operator) {
case '==': case 'is': return a === b;
case '!=': case 'is not': return a !== b;
@@ -428,11 +439,19 @@ var py = {};
case '<=': return a <= b;
case '>': return a > b;
case '>=': return a >= b;
- case 'in': return b.__contains__(a);
- case 'not in': return !b.__contains__(a);
+ case 'in':
+ if (typeof b === 'string') {
+ return b.indexOf(a) !== -1;
+ }
+ return b.__contains__(a);
+ case 'not in':
+ if (typeof b === 'string') {
+ return b.indexOf(a) === -1;
+ }
+ return !b.__contains__(a);
}
throw new Error('SyntaxError: unknown comparator [[' + operator + ']]');
- }
+ };
exports.evaluate = function (expr, context) {
switch (expr.id) {
case '(name)':
@@ -444,6 +463,14 @@ var py = {};
case '(string)':
case '(number)':
return expr.value;
+ case '(constant)':
+ if (expr.value === 'None')
+ return null;
+ else if (expr.value === 'False')
+ return false;
+ else if (expr.value === 'True')
+ return true;
+ throw new Error("SyntaxError: unknown constant '" + expr.value + "'");
case '(comparator)':
var result, left = exports.evaluate(expr.expressions[0], context);
for(var i=0; i<expr.operators.length; ++i) {
@@ -455,10 +482,12 @@ var py = {};
}
return true;
case '-':
- if (this.second) {
+ if (expr.second) {
throw new Error('SyntaxError: binary [-] not implemented yet');
}
return -(exports.evaluate(expr.first, context));
+ case 'not':
+ return !(exports.evaluate(expr.first, context));
case 'and':
return (exports.evaluate(expr.first, context)
&& exports.evaluate(expr.second, context));
@@ -466,16 +495,43 @@ var py = {};
return (exports.evaluate(expr.first, context)
|| exports.evaluate(expr.second, context));
case '(':
- if (this.second) {
- throw new Error('SyntaxError: functions not implemented yet');
+ if (expr.second) {
+ var fn = exports.evaluate(expr.first, context), args=[];
+ for (var jj=0; jj<expr.second.length; ++jj) {
+ args.push(exports.evaluate(
+ expr.second[jj], context));
+ }
+ return fn.apply(null, args);
}
var tuple_exprs = expr.first,
tuple_values = [];
- for (var i=0, len=tuple_exprs.length; i<len; ++i) {
+ for (var j=0, len=tuple_exprs.length; j<len; ++j) {
tuple_values.push(exports.evaluate(
- tuple_exprs[i], context));
+ tuple_exprs[j], context));
}
return create(exports.tuple, {values: tuple_values});
+ case '[':
+ if (expr.second) {
+ throw new Error('SyntaxError: indexing not implemented yet');
+ }
+ var list_exprs = expr.first, list_values = [];
+ for (var k=0; k<list_exprs.length; ++k) {
+ list_values.push(exports.evaluate(
+ list_exprs[k], context));
+ }
+ return create(exports.list, {values: list_values});
+ case '{':
+ var dict_exprs = expr.first, dict_values = {};
+ for(var l=0; l<dict_exprs.length; ++l) {
+ dict_values[exports.evaluate(dict_exprs[l][0], context)] =
+ exports.evaluate(dict_exprs[l][1], context);
+ }
+ return create(exports.dict, {values: dict_values});
+ case '.':
+ if (expr.second.id !== '(name)') {
+ throw new Error('SyntaxError: ' + expr);
+ }
+ return exports.evaluate(expr.first, context)[expr.second.value];
default:
throw new Error('SyntaxError: Unknown node [[' + expr.id + ']]');
}
@@ -485,6 +541,6 @@ var py = {};
exports.parse(
exports.tokenize(
str)),
- context);;
+ context);
}
-})(typeof exports === 'undefined' ? py : exports)
+})(typeof exports === 'undefined' ? py : exports);
diff --git a/addons/web/static/lib/py.parse/test/test.js b/addons/web/static/lib/py.parse/test/test.js
new file mode 100644
index 00000000000..0b4ddc1b623
--- /dev/null
+++ b/addons/web/static/lib/py.parse/test/test.js
@@ -0,0 +1,90 @@
+var py = require('../lib/py.js'),
+ assert = require('assert');
+
+// Literals
+assert.strictEqual(py.eval('1'), 1);
+assert.strictEqual(py.eval('None'), null);
+assert.strictEqual(py.eval('False'), false);
+assert.strictEqual(py.eval('True'), true);
+assert.strictEqual(py.eval('"somestring"'), 'somestring');
+assert.strictEqual(py.eval("'somestring'"), 'somestring');
+assert.deepEqual(py.eval("()").toJSON(), []);
+assert.deepEqual(py.eval("[]").toJSON(), []);
+assert.deepEqual(py.eval("{}").toJSON(), {});
+assert.deepEqual(py.eval("(None, True, False, 0, 1, 'foo')").toJSON(),
+ [null, true, false, 0, 1, 'foo']);
+assert.deepEqual(py.eval("[None, True, False, 0, 1, 'foo']").toJSON(),
+ [null, true, false, 0, 1, 'foo']);
+assert.deepEqual(py.eval("{'foo': 1, foo: 2}", {foo: 'bar'}).toJSON(),
+ {foo: 1, bar: 2});
+
+// Equality tests
+assert.ok(py.eval(
+ "foo == 'foo'", {foo: 'foo'}));
+// Inequality
+assert.ok(py.eval(
+ "foo != bar", {foo: 'foo', bar: 'bar'}));
+
+// Comparisons
+assert.ok(py.eval('3 < 5'));
+assert.ok(py.eval('5 >= 3'));
+assert.ok(py.eval('3 >= 3'));
+assert.ok(!py.eval('5 < 3'));
+assert.ok(py.eval('1 < 3 < 5'));
+assert.ok(py.eval('5 > 3 > 1'));
+assert.ok(py.eval('1 < 3 > 2 == 2 > -2 not in (0, 1, 2)'));
+// string rich comparisons
+assert.ok(py.eval(
+ 'date >= current', {date: '2010-06-08', current: '2010-06-05'}));
+
+// Boolean operators
+assert.ok(py.eval(
+ "foo == 'foo' or foo == 'bar'", {foo: 'bar'}));
+assert.ok(py.eval(
+ "foo == 'foo' and bar == 'bar'", {foo: 'foo', bar: 'bar'}));
+// - lazyness, second clauses NameError if not short-circuited
+assert.ok(py.eval(
+ "foo == 'foo' or bar == 'bar'", {foo: 'foo'}));
+assert.ok(!py.eval(
+ "foo == 'foo' and bar == 'bar'", {foo: 'bar'}));
+
+// contains (in)
+assert.ok(py.eval(
+ "foo in ('foo', 'bar')", {foo: 'bar'}));
+assert.ok(py.eval('1 in (1, 2, 3, 4)'));
+assert.ok(!py.eval('1 in (2, 3, 4)'));
+assert.ok(py.eval('type in ("url",)', {type: 'url'}));
+assert.ok(!py.eval('type in ("url",)', {type: 'ur'}));
+assert.ok(py.eval('1 not in (2, 3, 4)'));
+assert.ok(py.eval('type not in ("url",)', {type: 'ur'}));
+
+assert.ok(py.eval(
+ "foo in ['foo', 'bar']", {foo: 'bar'}));
+// string contains
+assert.ok(py.eval('type in "view"', {type: 'view'}));
+assert.ok(!py.eval('type in "view"', {type: 'bob'}));
+assert.ok(py.eval('type in "url"', {type: 'ur'}));
+
+// Literals
+assert.strictEqual(py.eval('False'), false);
+assert.strictEqual(py.eval('True'), true);
+assert.strictEqual(py.eval('None'), null);
+assert.ok(py.eval('foo == False', {foo: false}));
+assert.ok(!py.eval('foo == False', {foo: true}));
+
+// conversions
+assert.strictEqual(
+ py.eval('bool(date_deadline)', {bool: py.bool, date_deadline: '2008'}),
+ true);
+
+// getattr
+assert.ok(py.eval('foo.bar', {foo: {bar: true}}));
+assert.ok(!py.eval('foo.bar', {foo: {bar: false}}));
+
+// complex expressions
+assert.ok(py.eval(
+ "state=='pending' and not(date_deadline and (date_deadline < current_date))",
+ {state: 'pending', date_deadline: false}));
+assert.ok(py.eval(
+ "state=='pending' and not(date_deadline and (date_deadline < current_date))",
+ {state: 'pending', date_deadline: '2010-05-08', current_date: '2010-05-08'}));;