Fix: Disambiguate grammar; add objects/rel ops/assignable

This commit is contained in:
2025-09-26 10:09:35 -07:00
parent 91db5422dd
commit edf64a5b55

View File

@@ -19,19 +19,30 @@ class HiParser extends CstParser {
{ ALT: () => $.SUBRULE($.expressionStatement) }, { ALT: () => $.SUBRULE($.expressionStatement) },
]); ]);
}); });
$.RULE('declaration', () => { $.RULE('declaration', () => {
$.CONSUME(T.Identifier); $.CONSUME(T.Identifier);
$.CONSUME(T.Colon); $.CONSUME(T.Colon);
$.SUBRULE($.expression); $.SUBRULE($.expression);
}); });
$.RULE('assignment', () => { // assignable supports: Identifier(.Identifier | [expression])*
$.RULE('assignable', () => {
$.CONSUME(T.Identifier); $.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); $.CONSUME(T.Eq);
$.SUBRULE($.expression); $.SUBRULE($.expression);
}); });
$.RULE('returnStatement', () => { $.RULE('returnStatement', () => {
$.CONSUME(T.Caret); $.CONSUME(T.Caret);
$.OPTION(() => $.SUBRULE($.expression)); $.OPTION(() => $.SUBRULE($.expression));
@@ -39,32 +50,14 @@ class HiParser extends CstParser {
$.RULE('expressionStatement', () => $.SUBRULE($.expression)); $.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([ $.RULE('literal', () => $.OR([
{ ALT: () => $.CONSUME(T.Number) }, { ALT: () => $.CONSUME(T.Number) },
{ ALT: () => $.CONSUME(T.String) }, { ALT: () => $.CONSUME(T.String) },
{ ALT: () => $.CONSUME(T.Null) } { ALT: () => $.CONSUME(T.Null) }
])); ]));
$.RULE('arrayLiteral', () => { $.RULE('arrayLiteral', () => {
$.CONSUME(T.LBracket); $.CONSUME(T.LBracket);
$.OPTION(() => { $.OPTION(() => {
@@ -88,21 +81,36 @@ class HiParser extends CstParser {
}); });
$.CONSUME(T.RParen); $.CONSUME(T.RParen);
}); });
$.RULE('block', () => { // A code block with statements (used in arrow bodies or as expression blocks)
$.OPTION(() => $.SUBRULE($.parameterList)); $.RULE('codeBlock', () => {
$.CONSUME(T.LBrace); $.CONSUME(T.LBrace);
$.SUBRULE($.statements); $.SUBRULE($.statements);
$.CONSUME(T.RBrace); $.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); $.SUBRULE($.parameterList);
$.CONSUME(T.Arrow); $.SUBRULE($.codeBlock);
$.OR([ });
{ ALT: () => $.SUBRULE($.block) },
{ ALT: () => $.SUBRULE($.expression) } // ( ... ) parenthesized expression
]); $.RULE('parenthesizedExpression', () => {
$.CONSUME(T.LParen);
$.SUBRULE($.expression);
$.CONSUME(T.RParen);
}); });
// MemberExpression consumes a Primary. // MemberExpression consumes a Primary.
@@ -125,7 +133,7 @@ class HiParser extends CstParser {
$.CONSUME(T.RParen); $.CONSUME(T.RParen);
}); });
}); });
$.RULE('argumentList', () => { $.RULE('argumentList', () => {
$.SUBRULE($.expression); $.SUBRULE($.expression);
$.MANY(() => { $.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) => { const buildBinaryExpressionRule = (name, higherPrecRule, operators) => {
$.RULE(name, () => { $.RULE(name, () => {
$.SUBRULE(higherPrecRule, { LABEL: 'left' }); $.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, [ buildBinaryExpressionRule('multiplicativeExpression', $.callExpression, [
{ ALT: () => $.CONSUME(T.Star) }, { ALT: () => $.CONSUME(T.Slash) } { ALT: () => $.CONSUME(T.Star) }, { ALT: () => $.CONSUME(T.Slash) }
]); ]);
// additive -> multiplicative
buildBinaryExpressionRule('additiveExpression', $.multiplicativeExpression, [ buildBinaryExpressionRule('additiveExpression', $.multiplicativeExpression, [
{ ALT: () => $.CONSUME(T.Plus) }, { ALT: () => $.CONSUME(T.Minus) } { 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) } { 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. // Expression is the lowest precedence rule, forming the entry point for the chain.
$.RULE('expression', () => $.SUBRULE($.conditionalExpression)); $.RULE('expression', () => $.SUBRULE($.conditionalExpression));
this.performSelfAnalysis(); this.performSelfAnalysis();
} }
} }
export const parser = new HiParser(); export const parser = new HiParser();