UNPKG

mframejs

Version:
303 lines (234 loc) 7.27 kB
import { AST } from './ast'; /** * Using Douglas Crockford paper on Vaughan Pratt "Top Down Operator Precedence" * For more info about this look here * https://crockford.com/javascript/tdop/tdop.html (added to docs folder so I dont loose it) * Not everything is used * * This function adds symbols to our AST * */ export function addSymbols(astInstance: AST) { const ast = astInstance; /** * Symbol id and an optional binding power that defaults to 0 and returns a symbol object for that id. * If the symbol already exists in the symbol_table, the function returns that symbol object. * Otherwise, it makes a new symbol object that inherits from the symbolTemplate, stores it in the symbol container, and returns it. * A symbol object initially contains an id, a value, a left binding power, and the stuff it inherits from the symbolTemplate. * */ ast.symbol('(literal)').nud = function () { return this; }; ast.symbol('(variable)').nud = function () { return this; }; ast.symbol('(function)').nud = function () { return this; }; ast.symbol('(end)'); ast.symbol('(name)'); ast.symbol(':'); ast.symbol(';'); ast.symbol(')'); ast.symbol(']'); ast.symbol('}'); ast.symbol(','); /** * The infix function takes an id, a binding power, and an optional led function. * If a led function is not provided, the infix function supplies a default led that is useful in most cases. * */ ast.infix('+', 50); ast.infix('-', 50); ast.infix('*', 60); ast.infix('%', 60); ast.infix('/', 60); ast.infix('===', 40); ast.infix('!==', 40); ast.infix('==', 40); ast.infix('!=', 40); ast.infix('<', 40); ast.infix('<=', 40); ast.infix('>', 40); ast.infix('^', 70); ast.infix('>=', 40); ast.infix('?', 20, function (left: any) { this.first = left; this.second = ast.expression(0); ast.advance(':'); this.third = ast.expression(0); this.arity = 'ternary'; return this; }); ast.infix('.', 80, function (left: any) { this.first = left; if (ast.currentToken.arity !== 'variable') { ast.currentToken.error('Expected a property name.'); } ast.currentToken.arity = 'variable'; this.second = ast.currentToken; this.arity = 'binary'; this.isObject = true; ast.advance(); return this; }); ast.infix('[', 80, function (left: any) { this.first = left; this.second = ast.expression(0); this.arity = 'binary'; this.isObject = true; this.contextReset = true; ast.advance(']'); return this; }); ast.infix('(', 80, function (left: any) { if (!left) { return ast.expression(0); } const a: any[] = []; this.arity = 'binary'; this.isFunction = true; this.first = left; this.second = a; if (ast.currentToken.id !== ')') { while (true) { a.push(ast.expression(0)); if (ast.currentToken.id !== ',') { break; } ast.advance(','); } } ast.advance(')'); return this; }); /** * Prefix operators are right associative. * A prefix does not have a left binding power because it does not bind to the left. * Prefix operators can also sometimes be reserved words. * */ ast.prefix('-'); ast.prefix('!'); // ast.prefix('typeof'); // TODO: I dont have anything for this in my tokenizer ast.prefix('(', function () { const e = ast.expression(0); // TODO: check this one... will this even happend ? if (ast.currentToken.value === '|') { ast.valueConverter = ast.expression(0); } if (ast.currentToken.value === '&') { ast.behavior = ast.expression(0); } else { ast.advance(')'); } return e; }); ast.prefix('{', function () { const a = []; if (ast.currentToken.value !== '}') { while (true) { const n = ast.currentToken; ast.advance(); ast.advance(':'); const v = ast.expression(0); v.key = n.value; a.push(v); if (ast.currentToken.value !== ',') { break; } ast.advance(','); } } ast.advance('}'); this.first = a; this.arity = 'unary'; return this; }); /* ast.stmt('{', function () { const x = ast.statements(); ast.advance('}'); return x; }); */ ast.assignment('='); ast.assignment('+='); ast.assignment('-='); ast.assignment('*='); ast.assignment('/='); /** * valueConverter * */ ast.prefix('|', function () { this.arity = 'valueConverter'; const value = ast.expression(0); this.value = value.value; const a: any = []; this.args = a; while (true) { if (ast.currentToken.id !== ':') { break; } ast.advance(','); a.push(ast.expression(0)); } // use last statement as argument this.args.unshift(ast.statementsArray.pop()); ast.currentStatement = this; return this; }); /** * behavior * */ ast.prefix('&', function () { this.arity = 'behavior'; const value = ast.expression(0); this.value = value.value; const a: any = []; this.args = a; while (true) { if (ast.currentToken.id !== ':') { break; } ast.advance(','); a.push(ast.expression(0)); } // do I want to add last argument ? // this.args.push(ast.statementsArray); return this; }); /** * expression * */ ast.prefix('${', function () { this.e = ast.expression(0); ast.advance('}'); return this.e; }); /** * expression, alternative to ${ * */ ast.prefix('@{', function () { const e = ast.expression(0); ast.advance('}'); return e; }); /** * Those infix operators are left associative. * We can also make right associative operators, such as short-circuiting logical operators, by reducing the right binding power. * */ ast.infixr('&&', 30); ast.infixr('||', 30); /** * expressions/part before expression is split with ';' operator * */ ast.symbol(';').nud = function (): null { return null; }; }