Created
October 30, 2014 12:54
-
-
Save alanrsoares/a89e7220c94eab83d186 to your computer and use it in GitHub Desktop.
// source http://jsbin.com/xunofe
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>JS Bin</title> | |
| </head> | |
| <body> | |
| <div id="test-out"></div> | |
| <script id="jsbin-javascript"> | |
| /* | |
| This is an implementation of an regEx based arithmetic expression parser in LiveScript | |
| Author: Alan R. Soares | |
| Github: github.com/alanrsoares | |
| */ | |
| var Tokenizer, Arithmetx, log, logTestCases, run, arx, testCases; | |
| Tokenizer = (function(){ | |
| Tokenizer.displayName = 'Tokenizer'; | |
| var prototype = Tokenizer.prototype, constructor = Tokenizer; | |
| function Tokenizer(){ | |
| this.matchers = { | |
| sub: /\(([\d\.\^\*\+\-]+)\)/, | |
| expr: /(-?\d+(\.\d+)?)+([\^\/*\+\-])(-?\d+(\.\d+)?)/, | |
| 'const': /^(-?\d+(\.\d+)?)$/, | |
| prec: [/(-?\d+(\.\d+)?)([\\^])(\-?\d+(\.\d+)?)/, /(-?\d+(\.\d+)?)([\/*])(\-?\d+(\.\d+)?)/, /(-?\d+(\.\d+)?)([\+-])(\-?\d+(\.\d+)?)/] | |
| }; | |
| } | |
| prototype.isSub = function(it){ | |
| return this.matchers.sub.test(it); | |
| }; | |
| prototype.isConst = function(it){ | |
| return this.matchers['const'].test(it); | |
| }; | |
| prototype.isExp = function(it){ | |
| return this.matchers.expr.test(it); | |
| }; | |
| prototype.getSub = function(it){ | |
| return function(it){ | |
| return { | |
| input: it[0], | |
| value: it[1] | |
| }; | |
| }( | |
| this.matchers.sub.exec(it)); | |
| }; | |
| prototype.getExp = function(it){ | |
| var p; | |
| p = this.matchers.prec; | |
| return function(it){ | |
| return { | |
| input: it[0], | |
| operator: it[3], | |
| left: parseFloat( | |
| it[1]), | |
| right: parseFloat( | |
| it[4]) | |
| }; | |
| }( | |
| p[0].exec(it) || p[1].exec(it) || p[2].exec(it)); | |
| }; | |
| return Tokenizer; | |
| }()); | |
| Arithmetx = (function(){ | |
| Arithmetx.displayName = 'Arithmetx'; | |
| var prototype = Arithmetx.prototype, constructor = Arithmetx; | |
| function Arithmetx(){ | |
| this.t = new Tokenizer(); | |
| } | |
| prototype.calculate = function(it){ | |
| var ref$; | |
| switch (ref$ = [it.operator], false) { | |
| case '^' !== ref$[0]: | |
| return Math.pow(it.left, it.right); | |
| case '/' !== ref$[0]: | |
| return it.left / it.right; | |
| case '*' !== ref$[0]: | |
| return it.left * it.right; | |
| case '+' !== ref$[0]: | |
| return it.left + it.right; | |
| case '-' !== ref$[0]: | |
| return it.left - it.right; | |
| } | |
| }; | |
| prototype.parse = function(it){ | |
| var s, e; | |
| switch (false) { | |
| case !this.t.isConst(it): | |
| return parseFloat(it); | |
| case !this.t.isSub(it): | |
| s = this.t.getSub(it); | |
| return this.parse(it.replace(s.input, this.parse(s.value))); | |
| case !this.t.isExp(it): | |
| e = this.t.getExp(it); | |
| return this.parse(it.replace(e.input, this.calculate(e))); | |
| } | |
| }; | |
| return Arithmetx; | |
| }()); | |
| log = function(x){ | |
| var y; | |
| y = typeof x === 'string' | |
| ? x | |
| : JSON.stringify(x); | |
| document.querySelector('#test-out').innerHTML += "<br/>" + y; | |
| return y; | |
| }; | |
| logTestCases = function(cases, parser){ | |
| return cases.forEach(function(c){ | |
| var result; | |
| result = parser.parse(c.exp); | |
| return log( | |
| "It should evaluate '" + c.exp + "' to be " + c.res + "; <br/>[" + (c.res === result ? 'SUCCESS' : 'FAIL') + "] - Result : " + result + "<hr/>"); | |
| }); | |
| }; | |
| run = logTestCases; | |
| arx = new Arithmetx(); | |
| testCases = [ | |
| { | |
| exp: '3^(3+(1+2-3))*4/5', | |
| res: 21.6 | |
| }, { | |
| exp: '3^(-0.32^10)', | |
| res: 1.0000123693512344 | |
| }, { | |
| exp: '42', | |
| res: 42 | |
| }, { | |
| exp: '11^20', | |
| res: 672749994932560000000 | |
| }, { | |
| exp: '1+(2*(2+3)*-2)*2', | |
| res: -39 | |
| }, { | |
| exp: '1+1', | |
| res: 2 | |
| }, { | |
| exp: '3+-2', | |
| res: 1 | |
| }, { | |
| exp: '1-1+3-4', | |
| res: -1 | |
| }, { | |
| exp: '3+2', | |
| res: 5 | |
| }, { | |
| exp: '3-2', | |
| res: 1 | |
| }, { | |
| exp: '3-4', | |
| res: -1 | |
| }, { | |
| exp: '3*2', | |
| res: 6 | |
| }, { | |
| exp: '3*-2', | |
| res: -6 | |
| }, { | |
| exp: '6/2', | |
| res: 3 | |
| }, { | |
| exp: '-6/2', | |
| res: -3 | |
| }, { | |
| exp: '6/-2', | |
| res: -3 | |
| }, { | |
| exp: '-6/-2', | |
| res: 3 | |
| }, { | |
| exp: '2^-2', | |
| res: 0.25 | |
| }, { | |
| exp: '-2^2', | |
| res: 4 | |
| } | |
| ]; | |
| run(testCases, arx); | |
| </script> | |
| <script id="jsbin-source-javascript" type="text/javascript">/* | |
| This is an implementation of an regEx based arithmetic expression parser in LiveScript | |
| Author: Alan R. Soares | |
| Github: github.com/alanrsoares | |
| */ | |
| class Tokenizer | |
| -> | |
| @matchers = | |
| sub : /\(([\d\.\^\*\+\-]+)\)/ | |
| expr : /(-?\d+(\.\d+)?)+([\^\/*\+\-])(-?\d+(\.\d+)?)/ | |
| const : /^(-?\d+(\.\d+)?)$/ | |
| prec: | |
| * /(-?\d+(\.\d+)?)([\\^])(\-?\d+(\.\d+)?)/ | |
| * /(-?\d+(\.\d+)?)([\/*])(\-?\d+(\.\d+)?)/ | |
| * /(-?\d+(\.\d+)?)([\+-])(\-?\d+(\.\d+)?)/ | |
| isSub : -> @matchers.sub.test it | |
| isConst : -> @matchers.const.test it | |
| isExp : -> @matchers.expr.test it | |
| getSub : -> | |
| @matchers.sub.exec it | |
| |> -> | |
| input: it.0 | |
| value: it.1 | |
| getExp : -> | |
| p = @matchers.prec | |
| p.0.exec it or p.1.exec it or p.2.exec it | |
| |> -> | |
| input: it.0 | |
| operator: it.3 | |
| left: it.1 |> parseFloat | |
| right: it.4 |> parseFloat | |
| class Arithmetx | |
| -> | |
| @t = new Tokenizer! | |
| calculate: -> | |
| match it.operator | |
| | \^ => Math.pow it.left, it.right | |
| | \/ => it.left / it.right | |
| | \* => it.left * it.right | |
| | \+ => it.left + it.right | |
| | \- => it.left - it.right | |
| parse : -> | |
| | @t.isConst it => parseFloat it | |
| | @t.isSub it => | |
| s = @t.getSub it | |
| @parse it.replace(s.input, @parse s.value) | |
| | @t.isExp it => | |
| e = @t.getExp it | |
| @parse it.replace(e.input, @calculate e) | |
| # tests go here | |
| # helper funcion for logging to the html output | |
| log = (x) -> | |
| y = if typeof x is \string then x else JSON.stringify x | |
| document.querySelector('#test-out').innerHTML += "<br/>#{y}" | |
| y | |
| logTestCases = (cases, parser) -> | |
| cases.forEach (c) -> | |
| result = parser.parse c.exp | |
| "It should evaluate '#{c.exp}' to be #{c.res}; <br/> | |
| [#{ if c.res is result then \SUCCESS else \FAIL }] | |
| - Result : #result | |
| <hr/>" | |
| |> log | |
| run = logTestCases | |
| arx = new Arithmetx! | |
| testCases = | |
| * exp: '3^(3+(1+2-3))*4/5' | |
| res: 21.6 | |
| * exp: '3^(-0.32^10)' | |
| res: 1.0000123693512344 | |
| * exp: '42' | |
| res: 42 | |
| * exp: '11^20' | |
| res: 672749994932560000000 | |
| * exp: '1+(2*(2+3)*-2)*2' | |
| res: -39 | |
| * exp: '1+1' | |
| res: 2 | |
| * exp: '3+-2' | |
| res: 1 | |
| * exp: '1-1+3-4' | |
| res: -1 | |
| * exp: '3+2' | |
| res: 5 | |
| * exp: '3-2' | |
| res: 1 | |
| * exp: '3-4' | |
| res: -1 | |
| * exp: '3*2' | |
| res: 6 | |
| * exp: '3*-2' | |
| res: -6 | |
| * exp: '6/2' | |
| res: 3 | |
| * exp: '-6/2' | |
| res: -3 | |
| * exp: '6/-2' | |
| res: -3 | |
| * exp: '-6/-2' | |
| res: 3 | |
| * exp: '2^-2' | |
| res: 0.25 | |
| * exp: '-2^2' | |
| res: 4 | |
| run testCases, arx</script></body> | |
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| This is an implementation of an regEx based arithmetic expression parser in LiveScript | |
| Author: Alan R. Soares | |
| Github: github.com/alanrsoares | |
| */ | |
| var Tokenizer, Arithmetx, log, logTestCases, run, arx, testCases; | |
| Tokenizer = (function(){ | |
| Tokenizer.displayName = 'Tokenizer'; | |
| var prototype = Tokenizer.prototype, constructor = Tokenizer; | |
| function Tokenizer(){ | |
| this.matchers = { | |
| sub: /\(([\d\.\^\*\+\-]+)\)/, | |
| expr: /(-?\d+(\.\d+)?)+([\^\/*\+\-])(-?\d+(\.\d+)?)/, | |
| 'const': /^(-?\d+(\.\d+)?)$/, | |
| prec: [/(-?\d+(\.\d+)?)([\\^])(\-?\d+(\.\d+)?)/, /(-?\d+(\.\d+)?)([\/*])(\-?\d+(\.\d+)?)/, /(-?\d+(\.\d+)?)([\+-])(\-?\d+(\.\d+)?)/] | |
| }; | |
| } | |
| prototype.isSub = function(it){ | |
| return this.matchers.sub.test(it); | |
| }; | |
| prototype.isConst = function(it){ | |
| return this.matchers['const'].test(it); | |
| }; | |
| prototype.isExp = function(it){ | |
| return this.matchers.expr.test(it); | |
| }; | |
| prototype.getSub = function(it){ | |
| return function(it){ | |
| return { | |
| input: it[0], | |
| value: it[1] | |
| }; | |
| }( | |
| this.matchers.sub.exec(it)); | |
| }; | |
| prototype.getExp = function(it){ | |
| var p; | |
| p = this.matchers.prec; | |
| return function(it){ | |
| return { | |
| input: it[0], | |
| operator: it[3], | |
| left: parseFloat( | |
| it[1]), | |
| right: parseFloat( | |
| it[4]) | |
| }; | |
| }( | |
| p[0].exec(it) || p[1].exec(it) || p[2].exec(it)); | |
| }; | |
| return Tokenizer; | |
| }()); | |
| Arithmetx = (function(){ | |
| Arithmetx.displayName = 'Arithmetx'; | |
| var prototype = Arithmetx.prototype, constructor = Arithmetx; | |
| function Arithmetx(){ | |
| this.t = new Tokenizer(); | |
| } | |
| prototype.calculate = function(it){ | |
| var ref$; | |
| switch (ref$ = [it.operator], false) { | |
| case '^' !== ref$[0]: | |
| return Math.pow(it.left, it.right); | |
| case '/' !== ref$[0]: | |
| return it.left / it.right; | |
| case '*' !== ref$[0]: | |
| return it.left * it.right; | |
| case '+' !== ref$[0]: | |
| return it.left + it.right; | |
| case '-' !== ref$[0]: | |
| return it.left - it.right; | |
| } | |
| }; | |
| prototype.parse = function(it){ | |
| var s, e; | |
| switch (false) { | |
| case !this.t.isConst(it): | |
| return parseFloat(it); | |
| case !this.t.isSub(it): | |
| s = this.t.getSub(it); | |
| return this.parse(it.replace(s.input, this.parse(s.value))); | |
| case !this.t.isExp(it): | |
| e = this.t.getExp(it); | |
| return this.parse(it.replace(e.input, this.calculate(e))); | |
| } | |
| }; | |
| return Arithmetx; | |
| }()); | |
| log = function(x){ | |
| var y; | |
| y = typeof x === 'string' | |
| ? x | |
| : JSON.stringify(x); | |
| document.querySelector('#test-out').innerHTML += "<br/>" + y; | |
| return y; | |
| }; | |
| logTestCases = function(cases, parser){ | |
| return cases.forEach(function(c){ | |
| var result; | |
| result = parser.parse(c.exp); | |
| return log( | |
| "It should evaluate '" + c.exp + "' to be " + c.res + "; <br/>[" + (c.res === result ? 'SUCCESS' : 'FAIL') + "] - Result : " + result + "<hr/>"); | |
| }); | |
| }; | |
| run = logTestCases; | |
| arx = new Arithmetx(); | |
| testCases = [ | |
| { | |
| exp: '3^(3+(1+2-3))*4/5', | |
| res: 21.6 | |
| }, { | |
| exp: '3^(-0.32^10)', | |
| res: 1.0000123693512344 | |
| }, { | |
| exp: '42', | |
| res: 42 | |
| }, { | |
| exp: '11^20', | |
| res: 672749994932560000000 | |
| }, { | |
| exp: '1+(2*(2+3)*-2)*2', | |
| res: -39 | |
| }, { | |
| exp: '1+1', | |
| res: 2 | |
| }, { | |
| exp: '3+-2', | |
| res: 1 | |
| }, { | |
| exp: '1-1+3-4', | |
| res: -1 | |
| }, { | |
| exp: '3+2', | |
| res: 5 | |
| }, { | |
| exp: '3-2', | |
| res: 1 | |
| }, { | |
| exp: '3-4', | |
| res: -1 | |
| }, { | |
| exp: '3*2', | |
| res: 6 | |
| }, { | |
| exp: '3*-2', | |
| res: -6 | |
| }, { | |
| exp: '6/2', | |
| res: 3 | |
| }, { | |
| exp: '-6/2', | |
| res: -3 | |
| }, { | |
| exp: '6/-2', | |
| res: -3 | |
| }, { | |
| exp: '-6/-2', | |
| res: 3 | |
| }, { | |
| exp: '2^-2', | |
| res: 0.25 | |
| }, { | |
| exp: '-2^2', | |
| res: 4 | |
| } | |
| ]; | |
| run(testCases, arx); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment