Feat: Implement operator precedence and new syntax rules

This commit is contained in:
2025-09-26 08:29:46 -07:00
parent 71bd4b500e
commit 5c39bae274

View File

@@ -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();