diff options
| author | Xavier Morel <xmo@openerp.com> | 2011-09-29 15:54:49 +0200 |
|---|---|---|
| committer | Xavier Morel <xmo@openerp.com> | 2011-09-29 15:54:49 +0200 |
| commit | d32d0c5637e0b7d85c97278e4479d4e5f8ff1d69 (patch) | |
| tree | f4d710ddf8dd1f1d08f942ae3150efd77f2df536 /addons/web/static/lib/py.parse | |
| parent | c94872fc0236259e4c5155ab63f5e878f65e4556 (diff) | |
[IMP] update py.parse to 0.2
bzr revid: xmo@openerp.com-20110929135449-ssommvnyof6auy29
Diffstat (limited to 'addons/web/static/lib/py.parse')
| -rw-r--r-- | addons/web/static/lib/py.parse/.hg_archival.txt | 5 | ||||
| -rw-r--r-- | addons/web/static/lib/py.parse/README | 1 | ||||
| -rw-r--r-- | addons/web/static/lib/py.parse/TODO.rst | 14 | ||||
| -rw-r--r-- | addons/web/static/lib/py.parse/lib/py.js | 116 | ||||
| -rw-r--r-- | addons/web/static/lib/py.parse/test/test.js | 90 |
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'}));; |
