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) },
]);
});
$.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();