Skip to content

Instantly share code, notes, and snippets.

@alanrsoares
Created October 30, 2014 12:54
Show Gist options
  • Select an option

  • Save alanrsoares/a89e7220c94eab83d186 to your computer and use it in GitHub Desktop.

Select an option

Save alanrsoares/a89e7220c94eab83d186 to your computer and use it in GitHub Desktop.
<!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 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