-
Notifications
You must be signed in to change notification settings - Fork 1
Open
Labels
Description
Some time ago, we talked about text expressions... and I have implemented it generating a function that was able to be called passing an object. Since that comment, I have evolved it a little.
Example 1: createEvaluator does not depend on an existing instance of the object to create the evaluator function:
var fn = createEvaluator("x+y+z");
var result = fn({x: 1, y: 2, z: 3});
// result now contains 6
Example 2: it allows you to use variable names in the code, without requiring them to be set on the source object:
var fn = createEvaluator("x+(y||0)+(z||0)");
var result = fn({x: 1});
// result now contains 1
I haven't tested it widely though... but it works in current versions of FF and Chrome.
Take a look at tell me what you do think.
The code:
function rndStr() {
return Math.floor((1 + Math.random()) * 0x100000000).toString(16).substring(1);
}
function escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
function getVarNameExtractor() {
var errMsg = null;
var rgx = null;
for (var tryes = 0; tryes < 10; tryes++) {
var n = rndStr() + rndStr() + rndStr() + rndStr();
n = "__Invalid_Reference__" + n + "__";
try {
var func = Function.apply(
null,
["'use strict'\nreturn "+n+";"]);
func();
}
catch (e) {
errMsg = e.message;
var escMsg = escapeRegExp(errMsg);
var rgxStr = escMsg.replace(new RegExp(escapeRegExp(n),'g'), "(.*)");
if (rgxStr !== escMsg) {
var rgx = new RegExp(rgxStr, 'g');;
return function(error) {
rgx.lastIndex = 0;
var match = rgx.exec(error.message);
return (match && match[1]) || null;
}
}
}
}
return null;
}
function getVarNames(expr, model) {
var vne = getVarNameExtractor();
var names = (model && Object.keys(model)) || [];
if (vne) {
while (true) {
var func = Function.apply(
null,
names.concat(["'use strict';\nreturn ("+expr+");"]));
try {
func(model);
break;
}
catch (e) {
var missedName = vne(e);
if (missedName)
names.push(missedName);
else
break;
}
}
}
return names;
}
function createEvaluator(expr, model) {
var names = getVarNames(expr, model);
var func = Function.apply(
null,
names.concat(["'use strict';\nreturn ("+expr+");"]));
var func2 = Function.apply(
null,
[
"func",
"'use strict';\nreturn function(o) {"+
"return func("+names.map(function(x){return "o."+x;}).join(",")+");"+
"};"
]);
return func2(func);
}