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

@@ -26,8 +26,19 @@ class HiParser extends CstParser {
$.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);
}); });
@@ -39,26 +50,8 @@ 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) },
@@ -89,20 +82,35 @@ 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.
@@ -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) }
]); ]);
@@ -179,3 +223,4 @@ class HiParser extends CstParser {
} }
export const parser = new HiParser(); export const parser = new HiParser();