Skip to content

Instantly share code, notes, and snippets.

@karoltarasiuk
Created October 22, 2014 22:06
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save karoltarasiuk/98f0b7ffc34563fdb2b8 to your computer and use it in GitHub Desktop.
Save karoltarasiuk/98f0b7ffc34563fdb2b8 to your computer and use it in GitHub Desktop.
Function Generator - PEG.js
{
// from underscore lib
var extend = function (obj) {
var source, prop;
for (var i = 1, length = arguments.length; i < length; i++) {
source = arguments[i];
for (prop in source) {
if (hasOwnProperty.call(source, prop)) {
obj[prop] = source[prop];
}
}
}
return obj;
};
//----------
// ABSTRACT
//----------
var Expression = function Expression(children, operator) {
this.initialize(children, operator);
};
extend(Expression.prototype, {
numberOfChildren: 0,
initialize: function (children, operator) {
this.setOperator(operator);
this.setChildren(children);
},
getName: function() {
var funcNameRegex = /function (.{1,})\(/,
results = (funcNameRegex).exec((this).constructor.toString());
return (results && results.length > 1) ? results[1] : "";
},
setOperator: function (operator) {
this.operator = operator;
},
setChildren: function (children) {
this.children = children;
this.checkNumberOfChildren(this.numberOfChildren);
},
checkNumberOfChildren: function () {
if (this.numberOfChildren > this.children.length) {
throw this.getName() + "(" + this.operator + ") requires " + this.numberOfChildren + " children";
}
},
execute: function (params) {
throw "Expression.execute is not implemented";
}
});
var Primary = function Primary(child) {
this.initialize(child);
};
extend(Primary.prototype, Expression.prototype, {
numberOfChildren: 1,
initialize: function (child) {
Expression.prototype.initialize.call(this, [child], '');
}
});
//---------
// REGULAR
//---------
var Empty = function Empty(child) {
this.initialize(child);
};
extend(Empty.prototype, Primary.prototype, {
execute: function (params) {
return this.children[0];
}
});
var Param = function Param(child) {
this.initialize(child);
};
extend(Param.prototype, Primary.prototype, {
execute: function (params) {
var param = this.children[0];
if ("undefined" !== typeof params[param]) {
if (params[param] === 'evaluate') {
return -1;
} else {
return params[param];
}
} else {
throw param + " param not provided";
}
}
});
var Brackets = function Brackets(child) {
this.initialize(child);
};
extend(Brackets.prototype, Primary.prototype, {
execute: function (params) {
return this.children[0].execute(params);
}
});
var Integer = function Integer(child) {
this.initialize(child);
};
extend(Integer.prototype, Primary.prototype, {
execute: function (params) {
return this.children[0];
}
});
var Addition = function Addition(children) {
this.initialize(children);
};
extend(Addition.prototype, Expression.prototype, {
numberOfChildren: 2,
initialize: function (children) {
Expression.prototype.initialize.call(this, children, '+');
},
execute: function (params) {
return this.children[0].execute(params) + this.children[1].execute(params);
}
});
var Subtraction = function Subtraction(children) {
this.initialize(children);
};
extend(Subtraction.prototype, Expression.prototype, {
numberOfChildren: 2,
initialize: function (children) {
Expression.prototype.initialize.call(this, children, '-');
},
execute: function (params) {
return this.children[0].execute(params) - this.children[1].execute(params);
}
});
var Multiplication = function Multiplication(children) {
this.initialize(children);
};
extend(Multiplication.prototype, Expression.prototype, {
numberOfChildren: 2,
initialize: function (children) {
Expression.prototype.initialize.call(this, children, '*');
},
execute: function (params) {
return this.children[0].execute(params) * this.children[1].execute(params);
}
});
var Division = function Division(children) {
this.initialize(children);
};
extend(Division.prototype, Expression.prototype, {
numberOfChildren: 2,
initialize: function (children) {
Expression.prototype.initialize.call(this, children, '/');
},
execute: function (params) {
var numerator = this.children[0].execute(params),
denominator = this.children[1].execute(params);
if (denominator === 0) {
throw "Division by 0: " + numerator + "/" + denominator;
}
return numerator / denominator;
}
});
}
start
= expression
brackets
= "(" inside:expression ")" { return new Brackets(inside); }
multiplication
= left:primary "*" right:primary { return new Multiplication([left, right]); }
division
= left:primary "/" right:primary { return new Division([left, right]); }
addition
= left:primary "+" right:primary { return new Addition([left, right]); }
subtraction
= left:primary "-" right:primary { return new Subtraction([left, right]); }
param
= letters:[a-z]+ { return new Param(letters.join("")); }
integer "integer"
= digits:[0-9]+ { return new Integer(parseInt(digits.join(""), 10)); }
empty
= "" { return new Empty(0); }
expression
= operation
/ primary
operation
= multiplication
/ division
/ addition
/ subtraction
primary
= brackets
/ integer
/ param
/ empty
var parserObject,
parser = PEG.buildParser(parserScript);
parserObject = parser.parse('x+((2*3)-2)');
console.log(parserObject.execute({x:3}));
parserObject = parser.parse('x+((2*a)-2)');
console.log(parserObject.execute({x:3,a:5}));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment