return page history
α-wwwiki
::
history/extended_evaluator/20130709-220152.txt
editor : alpha [82.249.195.58] 2013/07/09 22:01:52 _h1 extended evaluator {div {@ style="font:bold 1.0em georgia;text-align:center;color:red;background:#eee;box-shadow:0 0 8px black;"} [[basic_evaluator]] | extended_evaluator | [[regexp_evaluator]] | [[alphalisp|data/alphalisp/]]} _p This is another one s-expression evaluator coming with two special operators "quote" and "def", allowing to extend the set of basic functions. The number of functions has strongly increased in the 'dictionary' (see dico in the code). Note that the functions' body accept only JS expressions. The evaluator is written in 20 JS lines. This is how to use the console : _ul first "init the evaluator", only once, _ul copy a line from the examples in the code, _ul paste it into the field below, _ul then click on the button "evaluate" _ul and do it again with new values. _p This parser cannot work on multiple s-expressions. When testing a define followed by a call, evaluate each of them separately. See [[alphalisp|data/alphalisp/]] for a multiline editor. {hr} {input {@ id="input" type="texte" value="(def (quote (carre (a) return a*a))) (carre 12)" style="width:99%; font:normal 1em courier; color:red;background:#ffc;"}} First {input {@ type="submit" value="init evaluator" onclick="°° var js = document.createElement('script'); var input = getId('code').innerHTML; js.innerHTML = decode_html_entities(input); // < ,> document.head.appendChild( js ); this.value = 'OK, evaluator inited !'; this.disabled='disabled'; °°"}} then {input {@ type="submit" value="evaluate" onclick="do_evaluate()"}} {pre {@ id="output"}} _h3 examples {pre // 1) basic operators (+ 1 2 3 4 5 6) -> 21 (* 1 2 3 4 5 6) -> 720 (- 1) -> -1 (- 1 2) -> -1 (/ 3) -> 0.3333333333333333 (/ 1 2) -> 0.5 (/ 1 2 2) -> 0.25 (/ 1 2 2 2) -> 0.125 (+ (* 3 3) (* 4 4) ) -> 5 (++ 3) -> 4 (-- 3) -> 2 (< 1 2) -> true (> 1 2) -> false (= 1.0 1.000) -> true (not true) -> false (and true false) -> false (or true false) -> true ///////////////////////////////// // 2) the JS Math functions (sqrt 2) -> 1.4142135623730951 (pow 2 8) -> 256 (log (E)) -> 1 (exp 1) -> 2.718281828459045 (max 1 3 2 23 1 56) -> 56 (min -10 3 2 23 1 56) -> 10 (acos 1) -> 0 (sqrt (+ (* 3 3) (* 4 4) ) ) -> 5 (+ (square (cos 0)) (square (sin 0)) ) ///////////////////////////////// // 3) the core defined user functions (square 12) -> 144 (hypo 3 4) -> 5 (fac 6) -> 720 (rand 1000) -> an integer in [1 1000) ///////////////////////////////// // 4) inline defined user functions // 41) defining a constant (def (quote (PI () return Math.PI) ) ) (PI) -> 3.141592653589793 // 42) defining a function with one argument (def (quote (carre (a) return a*a) ) ) (carre 12) -> 144 // 43) defining a function with two arguments (def (quote (pytha (a b) return Math.sqrt(a*a+b*b)) ) ) (pytha 3 4) -> 5 } _h3 evaluator's code {pre {@ id="code"}°° // 1) HTML interface function do_evaluate() { var input = getId('input').value; getId('output').innerHTML = evaluate(build_tree(tokenize(input))); } // 2) parser var tokenize = function ( s ) { return s.replace(/\(/g, ' ( ') .replace(/\)/g, ' ) ') .replace(/\s+/g, ' ') .replace(/^\s+|\s+$/g, '') .split(' '); }; var build_tree = function (tokens) { var token = tokens.shift(); if (token !== '(') return token; var arr = [], i = 0; while (tokens[0] !== ')') { i++ ; if (i>1000) return 'unbalanced s-expression : game over !'; arr.push( build_tree(tokens) ); } tokens.shift(); return arr; }; // 3) evaluator var evaluate = function(expression) { var first = '', rest = []; if (!Array.isArray(expression)) return expression; // it's a Number or a String first = expression.shift(); if (first == 'quote') // don't evaluate return expression; if (first == 'def') { // extend dico var body = expression[0][1], name = body.shift(), args = body.shift(); dico[name] = new Function( args.join(','), body.join(' ') ); return name; } if (dico.hasOwnProperty(first)) { // evaluate recursively expression.map(function(a){rest.push(evaluate(a))}); return dico[first].apply(null,rest); } return 'Sorry, [' + first + '] is unknown !'; } // 4) dictionary var dico = {}; // basic operators dico['+'] = function() { for (var r=0, i=0; i< arguments.length; i++) r += Number(arguments[i]); return r; }; dico['*'] = function() { for (var r=1, i=0; i< arguments.length; i++) r *= arguments[i]; return r; }; dico['-'] = function () { // (- 1 2 3 4) -> 1-2-3-4 var r = arguments[0]; if (arguments.length == 1) r = -r; // case (- 1) -> -1 else for (var i=1; i< arguments.length; i++) r -= arguments[i]; return r; }; dico['/'] = function () { // (/ 1 2 3 4) -> 1/2/3/4 var r = arguments[0]; if (arguments.length == 1) r = 1/r; // case (/ 2) -> 1/2 else for (var i=1; i< arr.length; i++) r /= arguments[i]; return r; }; dico['++'] = function(a) { return a + 1 }; // (++ 3) -> 4 dico['--'] = function(a) { return a - 1 }; // (-- 3) -> 2 dico['>'] = function(a,b) { return a > b }; // (> 2 1) -> true dico['< '] = function(a,b) { return a< b }; // (< 2 1) -> false dico['='] = function(a,b) { return !(a< b) && !(b< a) }; // (= 1 1) -> true dico['not'] = function (a) { return (a)? false : true }; // (not (= 1 1)) -> false dico['or'] = function () { // (or (= 1 1) (= 1 2)) -> true for (var ret=false, i=0; i< arguments.length; i++) if (arguments[i]) return true; return ret; }; dico['and'] = function () { // (and (= 1 1) (= 1 2)) -> false for (var ret=true, i=0; i< arguments.length; i++) if (!arguments[i]) return false; return ret; }; // JS Math functions var mathfns = ['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'random', 'round', 'sin', 'sqrt', 'tan']; for (var i=0; i< mathfns.length; i++) dico[mathfns[i]] = Math[mathfns[i]]; dico['PI'] = function () { return Math.PI; }; dico['E'] = function () { return Math.E; }; // some user JS functions dico['square'] = function(a) { return a*a }; dico['hypo'] = function(a,b) { return Math.sqrt(a*a+b*b) }; dico['fac'] = function(a) { return (a< 2)? 1 : a*arguments.callee(a-1); }; dico['rand'] = function(a) { return Math.floor((Math.random()*a)+1) }; // in [1,a] dico['mod'] = function(a,b) { return a % b }; // and so on ... °°}