UNPKG

@gabliam/expression

Version:
306 lines (305 loc) 9.31 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Parser = exports.IS_STRING = void 0; /* eslint-disable */ const escodegen_1 = require("escodegen"); const FAIL = Symbol('FAIL'); exports.IS_STRING = Symbol('IS_STRING'); class Parser { constructor(ast) { this.ast = ast; } parse(vars = {}) { const result = this.walk(this.ast, vars); return result === FAIL ? undefined : result; } parseUnary(node, vars) { const val = this.walk(node.argument, vars); switch (node.operator) { case '+': return +val; case '-': return -val; case '~': return ~val; case '!': return !val; case 'typeof': return typeof val; case 'void': case 'delete': return undefined; default: return FAIL; } } parseArray(node, vars) { const xs = []; for (const nodeElement of node.elements) { const x = this.walk(nodeElement, vars); if (x === FAIL) { return FAIL; } xs.push(x); } return xs; } parseObject(node, vars) { const obj = {}; for (let i = 0; i < node.properties.length; i++) { const prop = node.properties[i]; const value = prop.value === null ? prop.value : this.walk(prop.value, vars); if (value === FAIL) { return FAIL; } const key = prop.key === null ? prop.key : this.walk(prop.key, vars); if (key === FAIL) { return FAIL; } obj[key] = value; } return obj; } parseLeftRight(node, vars) { const l = this.walk(node.left, vars); if (l === FAIL) { return [FAIL]; } const r = this.walk(node.right, vars); if (r === FAIL) { return [FAIL]; } return [l, r]; } parseBinary(node, vars) { const [l, r] = this.parseLeftRight(node, vars); if (l === FAIL) { return FAIL; } if (null === l && undefined === r && node.left.raw && node.right.type === 'Identifier') { return exports.IS_STRING; } switch (node.operator) { case '==': return l == r; case '!=': return l != r; case '===': return l === r; case '!==': return l !== r; case '<': return l < r; case '<=': return l <= r; case '>': return l > r; case '>=': return l >= r; case '<<': return l << r; case '>>': return l >> r; case '>>>': return l >>> r; case '+': return l + r; case '-': return l - r; case '*': return l * r; case '/': return l / r; case '%': return l % r; case '**': return l ** r; case '|': return l | r; case '^': return l ^ r; case '&': return l & r; case 'in': return l in r; case 'instanceof': return l instanceof r; } } parseLogical(node, vars) { const l = this.walk(node.left, vars); if (l === FAIL) { return FAIL; } if (l === false && node.operator === '&&') { return false; } const r = this.walk(node.right, vars); switch (node.operator) { case '||': return l || r; case '&&': return l && r; } } parseIdentifier(node, vars) { if ({}.hasOwnProperty.call(vars, node.name)) { return vars[node.name]; } if (global.hasOwnProperty(node.name)) { return global[node.name]; } return undefined; } parseThis(node, vars) { if ({}.hasOwnProperty.call(vars, 'this')) { return vars['this']; } return FAIL; } parseCall(node, vars) { const callee = this.walk(node.callee, vars); if (callee === FAIL) { return FAIL; } if (typeof callee !== 'function') { return FAIL; } let ctx = node.callee.object ? this.walk(node.callee.object, vars) : FAIL; if (ctx === FAIL) { ctx = null; } const args = []; for (const arg of node.arguments) { const x = this.walk(arg, vars); if (x === FAIL) { return FAIL; } args.push(x); } return callee.apply(ctx, args); } parseMember(node, vars) { const obj = this.walk(node.object, vars); if (obj === undefined) { return undefined; } if (obj === FAIL) { return FAIL; } if (node.property.type === 'Identifier') { return obj[node.property.name]; } const prop = this.walk(node.property, vars); if (prop === FAIL) { return FAIL; } return obj[prop]; } parseConditional(node, vars) { const val = this.walk(node.test, vars); if (val === FAIL) { return FAIL; } return val ? this.walk(node.consequent, vars) : this.walk(node.alternate, vars); } parseStatement(node, vars) { const val = this.walk(node.expression, vars); if (val === FAIL) { return FAIL; } return val; } parseReturnStatement(node, vars) { return this.walk(node.argument, vars); } parseFunction(node, vars) { const bodies = node.body.body; const newVars = Object.assign({}, vars); node.params.forEach((key) => { if (key.type === 'Identifier') { newVars[key.name] = null; } }); for (const i in bodies) { if (this.walk(bodies[i], newVars) === FAIL) { return FAIL; } } const keys = Object.keys(vars); const vals = keys.map((key) => { return vars[key]; }); // eslint-disable-next-line prefer-spread return Function(keys.join(', '), 'return ' + (0, escodegen_1.generate)(node)).apply(null, vals); } parseTemplateLiteral(node, vars) { let str = ''; let i; for (i = 0; i < node.expressions.length; i++) { str += this.walk(node.quasis[i], vars); str += this.walk(node.expressions[i], vars); } str += this.walk(node.quasis[i], vars); return str; } parseTaggedTemplate(node, vars) { const tag = this.walk(node.tag, vars); const quasi = node.quasi; const strings = quasi.quasis.map((q) => this.walk(q, vars)); const values = quasi.expressions.map((e) => this.walk(e, vars)); // eslint-disable-next-line prefer-spread return tag.apply(null, [strings].concat(values)); } walk(node, vars) { if (!node) { return FAIL; } switch (node.type) { case 'Literal': return node.value; case 'UnaryExpression': return this.parseUnary(node, vars); case 'ArrayExpression': return this.parseArray(node, vars); case 'ObjectExpression': return this.parseObject(node, vars); case 'BinaryExpression': return this.parseBinary(node, vars); case 'LogicalExpression': return this.parseLogical(node, vars); case 'Identifier': return this.parseIdentifier(node, vars); case 'ThisExpression': return this.parseThis(node, vars); case 'CallExpression': return this.parseCall(node, vars); case 'MemberExpression': return this.parseMember(node, vars); case 'ConditionalExpression': return this.parseConditional(node, vars); case 'ExpressionStatement': return this.parseStatement(node, vars); case 'ReturnStatement': return this.parseReturnStatement(node, vars); case 'FunctionExpression': return this.parseFunction(node, vars); case 'TemplateLiteral': return this.parseTemplateLiteral(node, vars); case 'TaggedTemplateExpression': return this.parseTaggedTemplate(node, vars); case 'TemplateElement': return node.value.cooked; default: return FAIL; } } } exports.Parser = Parser;