diff --git a/src/parser.js b/src/parser.js index b7f89c6..53731cc 100644 --- a/src/parser.js +++ b/src/parser.js @@ -1,14 +1,9 @@ import { CstParser } from 'chevrotain'; -import { - allTokens, - Identifier, Number, String, - LBrace, RBrace, LParen, RParen, - Dot, Plus, Comma, Colon, Eq -} from './lexer.js'; +import * as T from './lexer.js'; class HiParser extends CstParser { constructor() { - super(allTokens); + super(T.allTokens); const $ = this; @@ -19,86 +14,136 @@ class HiParser extends CstParser { $.OR([ { ALT: () => $.SUBRULE($.declaration) }, { ALT: () => $.SUBRULE($.assignment) }, + { ALT: () => $.SUBRULE($.returnStatement) }, { ALT: () => $.SUBRULE($.expressionStatement) }, ]); }); $.RULE('expressionStatement', () => $.SUBRULE($.expression)); + + $.RULE('returnStatement', () => { + $.CONSUME(T.Caret); + $.OPTION(() => $.SUBRULE($.expression)); + }); $.RULE('declaration', () => { - $.CONSUME(Identifier); - $.CONSUME(Colon); + $.CONSUME(T.Identifier); + $.CONSUME(T.Colon); $.SUBRULE($.expression); }); $.RULE('assignment', () => { - $.CONSUME(Identifier); - $.CONSUME(Eq); + // Note: This only allows simple identifiers for now, not member expressions. + $.CONSUME(T.Identifier); + $.CONSUME(T.Eq); $.SUBRULE($.expression); }); - $.RULE('expression', () => $.SUBRULE($.additiveExpression)); + $.RULE('expression', () => $.SUBRULE($.conditionalExpression)); - $.RULE('additiveExpression', () => { - $.SUBRULE($.callExpression, { LABEL: 'left' }); - $.MANY(() => { - $.CONSUME(Plus); - $.SUBRULE2($.callExpression, { LABEL: 'right' }); - }); - }); - - $.RULE('callExpression', () => { - $.SUBRULE($.memberExpression); + $.RULE('conditionalExpression', () => { + $.SUBRULE($.equalityExpression, { LABEL: 'condition' }); $.OPTION(() => { - $.CONSUME(LParen); - $.OPTION2(() => $.SUBRULE($.argumentList)); - $.CONSUME(RParen); + $.CONSUME(T.Question); + $.SUBRULE2($.expression, { LABEL: 'consequent' }); + $.OPTION2(() => { + $.CONSUME(T.Colon); + $.SUBRULE3($.expression, { LABEL: 'alternate' }); + }); }); }); - $.RULE('argumentList', () => { - $.SUBRULE($.expression); + const buildBinaryExpressionRule = (name, higherPrecRule, operators) => { + $.RULE(name, () => { + $.SUBRULE(higherPrecRule, { LABEL: 'left' }); + $.MANY(() => { + $.CONSUME($.OR(operators)); + $.SUBRULE2(higherPrecRule, { LABEL: 'right' }); + }); + }); + }; + + buildBinaryExpressionRule('equalityExpression', $.additiveExpression, [ + { ALT: () => T.EqEq } + ]); + + buildBinaryExpressionRule('additiveExpression', $.multiplicativeExpression, [ + { ALT: () => T.Plus }, { ALT: () => T.Minus } + ]); + + buildBinaryExpressionRule('multiplicativeExpression', $.callExpression, [ + { ALT: () => T.Star }, { ALT: () => T.Slash } + ]); + + $.RULE('callExpression', () => { + $.SUBRULE($.memberExpression, { LABEL: 'callee' }); $.MANY(() => { - $.CONSUME(Comma); - $.SUBRULE2($.expression); + $.CONSUME(T.LParen); + $.OPTION(() => $.SUBRULE($.argumentList)); + $.CONSUME(T.RParen); }); }); + $.RULE('argumentList', () => $.SEPERATED_LIST($.expression, T.Comma)); + $.RULE('memberExpression', () => { - $.SUBRULE($.primary); + $.SUBRULE($.primary, { LABEL: 'object' }); $.MANY(() => { - $.CONSUME(Dot); - $.CONSUME(Identifier); + $.OR([ + { ALT: () => { $.CONSUME(T.Dot); $.CONSUME(T.Identifier); }}, + { ALT: () => { $.CONSUME(T.LBracket); $.SUBRULE($.expression); $.CONSUME(T.RBracket); }} + ]); }); }); $.RULE('primary', () => { $.OR([ { ALT: () => $.SUBRULE($.literal) }, - { ALT: () => $.CONSUME(Identifier) }, + { ALT: () => $.CONSUME(T.Identifier) }, + { ALT: () => $.CONSUME(T.At) }, + { ALT: () => $.SUBRULE($.arrowExpression) }, { ALT: () => $.SUBRULE($.block) }, + { ALT: () => $.SUBRULE($.arrayLiteral) }, { ALT: () => { - $.CONSUME(LParen); + $.CONSUME(T.LParen); $.SUBRULE($.expression); - $.CONSUME(RParen); + $.CONSUME(T.RParen); }} ]); }); - $.RULE('literal', () => $.OR([{ ALT: () => $.CONSUME(Number) }, { ALT: () => $.CONSUME(String) }])); + $.RULE('literal', () => $.OR([ + { ALT: () => $.CONSUME(T.Number) }, + { ALT: () => $.CONSUME(T.String) }, + { ALT: () => $.CONSUME(T.Null) } + ])); + + $.RULE('arrowExpression', () => { + $.SUBRULE($.parameterList); + $.CONSUME(T.Arrow); + $.OR([ + { ALT: () => $.SUBRULE($.block) }, + { ALT: () => $.SUBRULE($.expression) } + ]); + }); + + $.RULE('arrayLiteral', () => { + $.CONSUME(T.LBracket); + $.OPTION(() => $.SEPERATED_LIST($.expression, T.Comma)); + $.CONSUME(T.RBracket); + }); $.RULE('block', () => { - $.CONSUME(LBrace); - $.OPTION(() => $.SUBRULE($.keyValuePairs)); - $.CONSUME(RBrace); + $.OPTION(() => $.SUBRULE($.parameterList)); + $.CONSUME(T.LBrace); + $.SUBRULE($.statements); + $.CONSUME(T.RBrace); }); - $.RULE('keyValuePairs', () => $.AT_LEAST_ONE(() => $.SUBRULE($.keyValuePair))); - - $.RULE('keyValuePair', () => { - $.CONSUME(Identifier); - $.CONSUME(Colon); - $.SUBRULE($.expression); + $.RULE('parameterList', () => { + $.CONSUME(T.LParen); + $.OPTION(() => $.SEPERATED_LIST(T.Identifier, T.Comma)); + $.CONSUME(T.RParen); }); this.performSelfAnalysis();