diff --git a/src/parser.js b/src/parser.js index a0b4784..33a4b62 100644 --- a/src/parser.js +++ b/src/parser.js @@ -19,19 +19,30 @@ class HiParser extends CstParser { { ALT: () => $.SUBRULE($.expressionStatement) }, ]); }); - + $.RULE('declaration', () => { $.CONSUME(T.Identifier); $.CONSUME(T.Colon); $.SUBRULE($.expression); }); - $.RULE('assignment', () => { + // assignable supports: Identifier(.Identifier | [expression])* + $.RULE('assignable', () => { $.CONSUME(T.Identifier); + $.MANY(() => { + $.OR([ + { ALT: () => { $.CONSUME(T.Dot); $.CONSUME2(T.Identifier); } }, + { ALT: () => { $.CONSUME(T.LBracket); $.SUBRULE($.expression); $.CONSUME(T.RBracket); } } + ]); + }); + }); + + $.RULE('assignment', () => { + $.SUBRULE($.assignable, { LABEL: 'left' }); $.CONSUME(T.Eq); $.SUBRULE($.expression); }); - + $.RULE('returnStatement', () => { $.CONSUME(T.Caret); $.OPTION(() => $.SUBRULE($.expression)); @@ -39,32 +50,14 @@ class HiParser extends CstParser { $.RULE('expressionStatement', () => $.SUBRULE($.expression)); - // --- Expressions (ordered by precedence, highest to lowest) --- + // --- Primary and building blocks --- - // Primary is the highest precedence expression. - $.RULE('primary', () => { - $.OR([ - { ALT: () => $.SUBRULE($.literal) }, - { ALT: () => $.CONSUME(T.Identifier) }, - { ALT: () => $.CONSUME(T.At) }, - { ALT: () => $.SUBRULE($.arrowExpression) }, - { ALT: () => $.SUBRULE($.block) }, - { ALT: () => $.SUBRULE($.arrayLiteral) }, - { ALT: () => { - $.CONSUME(T.LParen); - $.SUBRULE($.expression); - $.CONSUME(T.RParen); - }} - ]); - }); - - // Components used by Primary $.RULE('literal', () => $.OR([ { ALT: () => $.CONSUME(T.Number) }, { ALT: () => $.CONSUME(T.String) }, { ALT: () => $.CONSUME(T.Null) } ])); - + $.RULE('arrayLiteral', () => { $.CONSUME(T.LBracket); $.OPTION(() => { @@ -88,21 +81,36 @@ class HiParser extends CstParser { }); $.CONSUME(T.RParen); }); - - $.RULE('block', () => { - $.OPTION(() => $.SUBRULE($.parameterList)); + + // A code block with statements (used in arrow bodies or as expression blocks) + $.RULE('codeBlock', () => { $.CONSUME(T.LBrace); $.SUBRULE($.statements); $.CONSUME(T.RBrace); }); - - $.RULE('arrowExpression', () => { + + // Object literal: { key: expr, key2: expr, ... } (commas optional via newlines) + $.RULE('objectLiteral', () => { + $.CONSUME(T.LBrace); + $.MANY(() => { + $.CONSUME(T.Identifier); + $.CONSUME(T.Colon); + $.SUBRULE($.expression); + }); + $.CONSUME(T.RBrace); + }); + + // (a, b) { ... } function sugar + $.RULE('functionExpression', () => { $.SUBRULE($.parameterList); - $.CONSUME(T.Arrow); - $.OR([ - { ALT: () => $.SUBRULE($.block) }, - { ALT: () => $.SUBRULE($.expression) } - ]); + $.SUBRULE($.codeBlock); + }); + + // ( ... ) parenthesized expression + $.RULE('parenthesizedExpression', () => { + $.CONSUME(T.LParen); + $.SUBRULE($.expression); + $.CONSUME(T.RParen); }); // MemberExpression consumes a Primary. @@ -125,7 +133,7 @@ class HiParser extends CstParser { $.CONSUME(T.RParen); }); }); - + $.RULE('argumentList', () => { $.SUBRULE($.expression); $.MANY(() => { @@ -134,7 +142,36 @@ class HiParser extends CstParser { }); }); - // Binary Expressions are defined with a helper. + // --- Arrow and Primary disambiguation --- + + $.RULE('arrowExpression', () => { + $.SUBRULE($.parameterList); + $.CONSUME(T.Arrow); + $.OR([ + { GATE: () => this.LA(1).tokenType === T.LBrace, ALT: () => $.SUBRULE($.codeBlock) }, + { ALT: () => $.SUBRULE($.expression) } + ]); + }); + + $.RULE('primary', () => { + $.OR([ + { ALT: () => $.SUBRULE($.literal) }, + { ALT: () => $.CONSUME(T.Identifier) }, + { ALT: () => $.CONSUME(T.At) }, + // Try arrow first: ( ... ) => + { GATE: this.BACKTRACK($.arrowExpression), ALT: () => $.SUBRULE($.arrowExpression) }, + // Then function sugar: ( ... ) { ... } + { GATE: this.BACKTRACK($.functionExpression), ALT: () => $.SUBRULE($.functionExpression) }, + // Prefer object literal over code block when both start with { ... } + { GATE: this.BACKTRACK($.objectLiteral), ALT: () => $.SUBRULE($.objectLiteral) }, + { ALT: () => $.SUBRULE($.codeBlock) }, + { ALT: () => $.SUBRULE($.arrayLiteral) }, + { ALT: () => $.SUBRULE($.parenthesizedExpression) } + ]); + }); + + // --- Binary Expressions with correct precedence --- + const buildBinaryExpressionRule = (name, higherPrecRule, operators) => { $.RULE(name, () => { $.SUBRULE(higherPrecRule, { LABEL: 'left' }); @@ -145,16 +182,23 @@ class HiParser extends CstParser { }); }; - // Define binary expressions from highest to lowest precedence. + // multiplicative -> call buildBinaryExpressionRule('multiplicativeExpression', $.callExpression, [ { ALT: () => $.CONSUME(T.Star) }, { ALT: () => $.CONSUME(T.Slash) } ]); + // additive -> multiplicative buildBinaryExpressionRule('additiveExpression', $.multiplicativeExpression, [ { ALT: () => $.CONSUME(T.Plus) }, { ALT: () => $.CONSUME(T.Minus) } ]); - buildBinaryExpressionRule('equalityExpression', $.additiveExpression, [ + // relational -> additive + buildBinaryExpressionRule('relationalExpression', $.additiveExpression, [ + { ALT: () => $.CONSUME(T.Gt) }, { ALT: () => $.CONSUME(T.Lt) } + ]); + + // equality -> relational + buildBinaryExpressionRule('equalityExpression', $.relationalExpression, [ { ALT: () => $.CONSUME(T.EqEq) } ]); @@ -170,12 +214,13 @@ class HiParser extends CstParser { }); }); }); - + // Expression is the lowest precedence rule, forming the entry point for the chain. $.RULE('expression', () => $.SUBRULE($.conditionalExpression)); - + this.performSelfAnalysis(); } } export const parser = new HiParser(); +