Skip to content

About text-expression evaluation #8

Description

@masbicudo

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);
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions