Skip to content

Instantly share code, notes, and snippets.

@probil
Created January 11, 2018 11:34
Show Gist options
  • Select an option

  • Save probil/624c8b3f62871c40e122ae7aa75681c6 to your computer and use it in GitHub Desktop.

Select an option

Save probil/624c8b3f62871c40e122ae7aa75681c6 to your computer and use it in GitHub Desktop.

Revisions

  1. probil created this gist Jan 11, 2018.
    352 changes: 352 additions & 0 deletions acorn_and_runner.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,352 @@
    const acorn = require("acorn")
    const walk = require("acorn/dist/walk")

    var getTarget = function (field, st, cb) {

    // next lets see its an identifier, if so, its right on one of the state objects
    var isId = field.type == 'Identifier';
    var name = field.name;
    var value, obj, prop;

    var isObj = field.hasOwnProperty('object') && field.hasOwnProperty('property');


    if (isId) {
    // we keep track of how many we found to avoid duplicates in different objects
    var found = 0;

    var len = st.refs && st.refs.length;
    for (var i = 0; i < len; i++) {
    var ref = st.refs[i];
    if (ref && ref[name] != undefined) {
    obj = ref;
    prop = name;
    value = ref[name];
    found++;
    }
    }
    if (found > 1) throw new Error('environment has duplicate keys named ' + name);
    return cb(value, obj, prop);
    }
    if (isObj) {
    // if we didn't find it we'll try
    obj = field.object && field.object.value;

    if (!obj) {
    var found = 0;

    var len = st.refs && st.refs.length;
    for (var i = 0; i < len; i++) {
    var ref = st.refs[i];
    if (ref && ref[field.object.name] != undefined) {
    obj = ref[field.object.name];
    found++;
    }
    }
    if (found > 1) throw new Error('environment has duplicate keys named ' + name);
    }

    prop = field.property && (field.property.name || field.property.value);
    if (obj) {
    value = obj[prop];
    } else {
    throw new Error('no such object ' + obj)
    }
    return cb(value, obj, prop);
    }

    // see if we set a value on the node from a prior operation.
    if (field.hasOwnProperty('value')) return cb(field.value);

    }

    var handlers = {
    ExpressionStatement: function (node, st) {
    getTarget(node.expression, st, function (v) {
    st.result = v;
    })
    },
    ObjectExpression: function (node, st) {
    var obj = {};
    if (node.properties) {
    for (var i = 0; i < node.properties.length; i++) {
    var val = node.properties[i].value;
    obj[node.properties[i].key.name] = val && val.value;
    }
    }
    node.value = obj;
    },
    SequenceExpression: function (node, st) {
    node.value = (node.expressions && node.expressions.length) ? node.expressions[node.expressions.length - 1].value : null;
    },
    AssignmentExpression: function (node, st) {
    // var operator = node.operator, prefix = node.prefix, obj = node.left.object && node.left.object.value,
    // prop = node.left.property && node.left.property.name || node.left.name, val = node.right.value;

    var operator = node.operator, obj, prop, val;

    getTarget(node.left, st, function (v, o, p) {
    obj = o;
    prop = p;
    });
    getTarget(node.right, st, function (v, o, p) {
    val = v;
    });

    if (!obj)
    throw new Error('cannot operate on variables like "' + prop + '" must be an object in the environment');

    if (operator == '=') {
    obj[prop] = val;
    node.value = val;
    } else {
    throw new Error('operator ' + operator + ' not recognized for assignment operation')
    }
    },
    ConditionalExpression: function (node, st) {
    node.value = (node.test.value) ? node.consequent.value : node.alternate.value;
    },
    UpdateExpression: function (node, st) {
    var obj, prop, value, operator = node.operator, prefix = node.prefix;
    getTarget(node.argument, st, function (val, o, p) {
    obj = o;
    prop = p;
    value = val;
    });
    var newVal = value;
    switch (operator) {
    case '++':
    newVal = ++obj[prop];
    break;
    case '--':
    newVal = --obj[prop];
    break;
    default :
    throw new Error('update using operator ' + operator + ' not supported');
    }
    node.value = (prefix) ? newVal : value;
    },
    UnaryExpression: function (node, st) {
    var obj, prop, value, operator = node.operator;
    getTarget(node.argument, st, function (val, o, p) {
    obj = o;
    prop = p;
    value = val;
    });
    var newVal = value;
    switch (operator) {
    case '!':
    newVal = !obj[prop];
    break;
    case '~':
    newVal = ~obj[prop];
    break;
    case '-':
    newVal = -obj[prop];
    break;
    case 'empty':
    newVal = obj === null || prop === null || obj === undefined || prop === undefined || obj[prop] === null || obj[prop] === undefined || obj[prop].length === 0;
    break;
    default :
    throw new Error('unary using operator ' + operator + ' not supported');
    }
    node.value = newVal;
    },
    MemberExpression: function (node, st) {
    // if (!node.value){
    // look it up in state
    // var obj = st[node.object.name];
    var obj, prop, value, operator = node.operator, prefix = node.prefix;
    getTarget(node, st, function (val, o, p) {
    obj = o;
    prop = p;
    value = val;
    });
    node.object.value = obj;
    node.property.value = prop;
    if (obj)
    node.value = obj[prop];
    // }
    },
    Identifier: function (node, st) {
    // if (!node.value){
    // look it up in state
    // var obj = st[node.object.name];
    var obj, prop, value, operator = node.operator, prefix = node.prefix;
    getTarget(node, st, function (val, o, p) {
    obj = o;
    prop = p;
    value = val;
    });

    if (!node.object)
    node.object = {};

    node.object.value = obj;
    if (!node.property)
    node.property = {};

    node.property.value = prop;
    if (obj)
    node.value = obj[prop];
    // }
    },
    CallExpression: function (node, st) {
    // var func = node.callee.name;
    // var found = 0;

    var args = [];
    if (node.args) {
    for (var i = 0; i < node.args.length; i++) {
    args.push(node.args[i].value);
    }
    }

    var obj, prop;
    getTarget(node.callee, st, function (val, o, p) {
    obj = o;
    prop = p;
    })
    if (typeof obj[prop] == 'function') {
    node.value = obj[prop].apply(obj, args)
    } else {
    throw new Error('property ' + prop + ' on ' + node.callee.name + ' is not a function');
    }
    },
    ArrayExpression: function (node, st) {
    var arr = [];
    if (node.elements) {
    for (var i = 0; i < node.elements.length; i++) {
    arr.push(node.elements[i].value);
    }
    }
    node.value = arr;
    },
    LogicalExpression: function (node, st) {
    var left, right, operator = node.operator, result = null;
    getTarget(node.left, st, function (val) {
    left = val;
    })
    getTarget(node.right, st, function (val) {
    right = val;
    });
    switch (operator) {
    case 'and':
    case '&&':
    result = left && right;
    break;
    case 'or':
    case '||':
    result = left || right;
    break;
    }
    node.value = result;
    },
    BinaryExpression: function (node, st) {
    var left, right, operator = node.operator, result = null;
    getTarget(node.left, st, function (val) {
    left = val;
    })
    getTarget(node.right, st, function (val) {
    right = val;
    });
    switch (operator) {
    // case '=':
    // result = left = right;
    // break;
    case '+':
    result = left + right;
    break;
    case '-':
    result = left - right;
    break;
    case '*':
    result = left * right;
    break;
    case 'div':
    case '/':
    result = left / right;
    break;
    case 'mod':
    case '%':
    result = left % right;
    break;
    case 'eq':
    case '==':
    result = left == right;
    break;
    case '===':
    result = left === right;
    break;
    case 'ne':
    case '!=':
    result = left != right;
    break;
    case '!==':
    result = left !== right;
    break;
    case 'gt':
    case '>':
    result = left > right;
    break;
    case 'lt':
    case '<':
    result = left < right;
    break;
    case 'ge':
    case '>=':
    result = left >= right;
    break;
    case 'le':
    case '<=':
    result = left <= right;
    break;
    case '&':
    result = left & right;
    break;
    case '|':
    result = left | right;
    break;
    case '^':
    result = left ^ right;
    break;
    case '>>':
    result = left >> right;
    break;
    case '>>>':
    result = left >>> right;
    break;
    case '<<':
    result = left << right;
    break;
    case 'and':
    case '&&':
    result = left && right;
    break;
    case 'or':
    case '||':
    result = left || right;
    break;
    default:
    throw new Error('operator ' + operator + ' not supported');
    }
    node.value = result;
    }
    };

    const run = (p, obj1, obj2) => {
    var args = Array.prototype.slice.call(arguments, 1);
    var input = {refs: args};
    walk.simple(p, handlers, null, input);
    return input.result;
    }







    const parsed = acorn.parse("1 + 1");
    // helper function for traversal
    run(parsed);