diff --git a/doc/operators.txt b/doc/operators.txt index f436169..b0c4fb3 100644 --- a/doc/operators.txt +++ b/doc/operators.txt @@ -5,51 +5,52 @@ Operators in Pivot: +-------------+------------+----------------------------------+--------------------+-----------------+ | Precedence | Operator | Description | Operands | Associativity | +-------------+------------+----------------------------------+--------------------+-----------------+ -| 16 | () | Grouping | internal | n/a | +| 16 | () | Grouping | internal | n/a | grouping() +-------------+------------+----------------------------------+--------------------+-----------------+ -| 15 | . | Member Access | dual | Left to Right | -| 15 | [] | Computed Member Access | before, internal | Left to Right | -| 15 | () | Function Call | before, internal | Left to Right | +| 15 | . | Member Access | dual | Left to Right | memberAccess() +| 15 | [] | Computed Member Access | before, internal | Left to Right | computedMemberAccess() +| 15 | () | Function Call | before, internal | Left to Right | functionCall() +-------------+------------+----------------------------------+--------------------+-----------------+ -| 14 | let | Variable Creation | after | Right to Left | -| 14 | const | Constant Creation | after | Right to Left | -| 14 | new | Object Creation | after | Right to Left | -| 14 | () {} | Function Creation | internal | n/a | +| 14 | let | Variable Creation | after | Right to Left | keywords() +| 14 | const | Constant Creation | after | Right to Left | keywords() +| 14 | new | Object Creation | after | Right to Left | keywords() +| 14 | return | Function Return | after | n/a | keywords() +| 14 | () {} | Function Creation | internal | n/a | functionCreation() +-------------+------------+----------------------------------+--------------------+-----------------+ -| 13 | ++ | Postfix Increment | before | Left to Right | -| 13 | -- | Postfix Decrement | before | Left to Right | +| 13 | ++ | Postfix Increment | before | Left to Right | postfixOperators() +| 13 | -- | Postfix Decrement | before | Left to Right | postfixOperators() +-------------+------------+----------------------------------+--------------------+-----------------+ -| 12 | ! | Logical NOT | after | Right to Left | -| 12 | - | Unary Negation | after | Right to Left | +| 12 | ! | Logical NOT | after | Right to Left | prefixOperators() +| 12 | - | Unary Negation | after | Right to Left | prefixOperators() +-------------+------------+----------------------------------+--------------------+-----------------+ -| 11 | ** | Exponentiation | dual | Right to Left | +| 11 | ** | Exponentiation | dual | Right to Left | mathOperators(0) +-------------+------------+----------------------------------+--------------------+-----------------+ -| 10 | * | Multiplication | dual | Left to Right | -| 10 | / | Division | dual | Left to Right | -| 10 | % | Modulus | dual | Left to Right | +| 10 | * | Multiplication | dual | Left to Right | mathOperators(1) +| 10 | / | Division | dual | Left to Right | mathOperators(1) +| 10 | % | Modulus | dual | Left to Right | mathOperators(1) +-------------+------------+----------------------------------+--------------------+-----------------+ -| 9 | + | Addition | dual | Left to Right | -| 9 | - | Subtraction | dual | Left to Right | +| 9 | + | Addition | dual | Left to Right | mathOperators(2) +| 9 | - | Subtraction | dual | Left to Right | mathOperators(2) +-------------+------------+----------------------------------+--------------------+-----------------+ -| 7 | < | Less Than | dual | Left to Right | -| 7 | <= | Less Than or Equal | dual | Left to Right | -| 7 | > | Greater Than | dual | Left to Right | -| 7 | >= | Greater Than or Equal | dual | Left to Right | +| 7 | < | Less Than | dual | Left to Right | comparisonOperators() +| 7 | <= | Less Than or Equal | dual | Left to Right | comparisonOperators() +| 7 | > | Greater Than | dual | Left to Right | comparisonOperators() +| 7 | >= | Greater Than or Equal | dual | Left to Right | comparisonOperators() +-------------+------------+----------------------------------+--------------------+-----------------+ -| 6 | == | Equality | dual | Left to Right | -| 6 | != | Inequality | dual | Left to Right | +| 6 | == | Equality | dual | Left to Right | assign() +| 6 | != | Inequality | dual | Left to Right | assign() +-------------+------------+----------------------------------+--------------------+-----------------+ -| 4 | && | Logical AND | dual | Left to Right | -| 4 | ^^ | Logical XOR | dual | Left to Right | -| 4 | || | Logical OR | dual | Left to Right | +| 4 | && | Logical AND | dual | Left to Right | logicOperators() +| 4 | ^^ | Logical XOR | dual | Left to Right | logicOperators() +| 4 | || | Logical OR | dual | Left to Right | logicOperators() +-------------+------------+----------------------------------+--------------------+-----------------+ -| 3 | = | Assignment | dual | Right to Left | -| 3 | += | Add and Assign | dual | Right to Left | -| 3 | -= | Subtract and Assign | dual | Right to Left | -| 3 | **= | Exponentiate and Assign | dual | Right to Left | -| 3 | *= | Multiply and Assign | dual | Right to Left | -| 3 | /= | Divide and Assign | dual | Right to Left | -| 3 | %= | Modulo and Assign | dual | Right to Left | +| 3 | = | Assignment | dual | Right to Left | opAssign() +| 3 | += | Add and Assign | dual | Right to Left | opAssign() +| 3 | -= | Subtract and Assign | dual | Right to Left | opAssign() +| 3 | **= | Exponentiate and Assign | dual | Right to Left | opAssign() +| 3 | *= | Multiply and Assign | dual | Right to Left | opAssign() +| 3 | /= | Divide and Assign | dual | Right to Left | opAssign() +| 3 | %= | Modulo and Assign | dual | Right to Left | opAssign() +-------------+------------+----------------------------------+--------------------+-----------------+ | 2 | , | Comma | none | Left to Right | +-------------+------------+----------------------------------+--------------------+-----------------+ diff --git a/src/parser.js b/src/parser.js index 57a6ce6..8c22311 100644 --- a/src/parser.js +++ b/src/parser.js @@ -22,12 +22,21 @@ function parse(tokens) { ast = addIndexes(ast); ast = addLevels(ast); - // Start grouping by precedence + // Start grouping by precedence. + + // Precedence 16. ast = grouping(ast); + // Precedence 15. ast = memberAccess(ast); + ast = computedMemberAccess(ast); + ast = functionCall(ast); + // Precedence 14. + ast = keywords(ast); + ast = functionCreation(ast); + // Precedence 13. + // Precedence 12. ast = postfixOperators(ast); ast = prefixOperators(ast); - console.log(ast); return ast; } @@ -123,13 +132,15 @@ function grouping(tokens) { * @function memberAccess * @desc Combine groups of tokens by member access. * @param {Token[]} tokens The tokens. - * @returns {Token[]} The grouped tokens, or the basic ast. + * @returns {Token[]} The ast with grouped member access. * @private */ +// TODO: Member access function memberAccess(ast) { + console.log(ast); for (let i = 0; i < ast.length; i++) { if (ast[i].type == 'group') - memberAccess(ast[i].tokens); // Recursively order the groups. + ast[i].tokens = memberAccess(ast[i].tokens); // Recursively order the groups. else if (ast[i].type == 'operator' && ast[i].value == '.') { // Member access operator. if (typeof ast[i - 1] == 'undefined' || typeof ast[i + 1] == 'undefined') throw new SyntaxError('Operator requires two operands.'); @@ -143,6 +154,115 @@ function memberAccess(ast) { return ast; } +/** + * @function computedMemberAccess + * @desc Combine groups of tokens by computed member access. + * @param {Token[]} tokens The tokens. + * @returns {Token[]} The ast with grouped computed member access. + * @private + */ +function computedMemberAccess(ast) { + // Computed member access is Variable, Bracket Group. + for (let i = 0; i < ast.length; i++) { + if (ast[i].type == 'group') + ast[i].tokens = computedMemberAccess(ast[i].tokens); // Recursively order the groups. + else if (ast[i].type == 'name' && ast[i].subtype == 'variable') { // Member access operator. + if (typeof ast[i + 1] == 'undefined') + continue; // Nothing after the variable; skip this loop. + if (ast[i + 1].type == 'group' && ast[i + 1].subtype == 'bracket') { + ast[i + 1].tokens = computedMemberAccess(ast[i + 1].tokens); // Order the group that we care about before we mess with it. + let op = new Operator('n/a', 'member access', [ast[i], ast[i + 1]]); + op.index = ast[i].index; + op.level = ast[i].level; + ast.splice(i, 2, op); + // Removed 2 tokens, put in 1, skip 1 token. Don't reduce the counter. + } else continue; // Not what we need. + } + } + return ast; +} + +/** + * @function functionCall + * @desc Combine groups of tokens by function calls. + * @param {Token[]} tokens The tokens. + * @returns {Token[]} The ast with grouped function calls. + * @private + */ +function functionCall(ast) { + // Function call is Variable, Parenthesis Group. + for (let i = 0; i < ast.length; i++) { + if (ast[i].type == 'group') + ast[i].tokens = functionCall(ast[i].tokens); // Recursively order the groups. + else if (ast[i].type == 'name' && ast[i].subtype == 'variable') { // Member access operator. + if (typeof ast[i + 1] == 'undefined') + continue; // Nothing after the variable; skip this loop. + if (ast[i + 1].type == 'group' && ast[i + 1].subtype == 'parenthesis') { + ast[i + 1].tokens = functionCall(ast[i + 1].tokens); // Order the group that we care about before we mess with it. + let op = new Operator('function call', ast[i].value, [ast[i], ast[i + 1]]); + op.index = ast[i].index; + op.level = ast[i].level; + ast.splice(i, 2, op); + // Removed 2 tokens, put in 1, skip 1 token. Don't reduce the counter. + } else continue; // Not what we need. + } + } + return ast; +} + +/** + * @function keywords + * @desc Combine groups of tokens by keywords. + * @param {Token[]} tokens The tokens. + * @returns {Token[]} The ast with grouped keywords. + * @private + */ +function keywords(ast) { + for (let i = ast.length - 1; i >= 0; i--) { // Keywords are rtl associative, so loop backwards. + if (ast[i].type == 'group') + ast[i].tokens = keywords(ast[i].tokens); // Recursively order the groups. + else if (ast[i].type == 'name' && ast[i].subtype == 'keyword') { + if (typeof ast[i + 1] == 'undefined') + throw new SyntaxError('Keyword requires one operand after it.'); + let key = new Operator('keyword', ast[i].value, [ast[i + 1]]); + key.level = ast[i].level; + key.index = ast[i].index; + ast.splice(i, 2, key); + // Looping backwards and didn't remove any items before the current one. Don't reduce the counter. + } + } + return ast; +} + +/** + * @function functionCreation + * @desc Combine groups of tokens by function creation. + * @param {Token[]} tokens The tokens. + * @returns {Token[]} The ast with grouped function creation. + * @private + */ +function functionCreation(ast) { + // Function call is Parenthesis Group, Brace Group. + console.log(ast); + for (let i = 0; i < ast.length; i++) { + if (ast[i].type == 'group') + ast[i].tokens = functionCreation(ast[i].tokens); // Recursively order the groups. + else if (ast[i].type == 'group' && ast[i].subtype == 'parenthesis') { + if (typeof ast[i + 1] == 'undefined') + continue; // Nothing after this group, ignore it. + else if (ast[i + 1] == 'group' && ast[i].subtype == 'brace') { + ast[i + 1].tokens = functionCreation(ast[i + 1].tokens); // Order the group that we care about before we mess with it. + let op = new Operator('n/a', 'function creation', [ast[i], ast[i + 1]]); + op.index = ast[i].index; + op.level = ast[i].level; + ast.splice(i, 2, op); + // Removed 2 tokens, put in 1, skip 1 token. Don't reduce the counter. + } else continue; + } + } + return ast; +} + /** * @function postfixOperators * @desc Recursively structures the postfix operators. @@ -181,7 +301,7 @@ function prefixOperators(ast) { if (ast[i].type == 'group') ast[i].tokens = postfixOperators(ast[i].tokens); else if (ast[i].type == 'operator' && ast[i].subtype == 'prefix') { // The operand is on the right. - if (typeof ast[i + 1] == 'undefined') + if (typeof ast[i - 1] == 'undefined') throw new SyntaxError('Prefix operator requires one operand after it.'); let op = new Operator(ast[i].subtype, ast[i].value, [ast[i + 1]]); op.index = ast[i].index; diff --git a/src/tokenizer.js b/src/tokenizer.js index 4a3ecbd..8438ec8 100644 --- a/src/tokenizer.js +++ b/src/tokenizer.js @@ -327,7 +327,7 @@ function determineCharType(char) { * @private */ function determineType(str) { - if (/let|return/.test(str)) + if (/let|const|new|return/.test(str)) return 'keyword'; else return 'unknown'; };