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 { CstParser } from 'chevrotain';
import { import * as T from './lexer.js';
allTokens,
Identifier, Number, String,
LBrace, RBrace, LParen, RParen,
Dot, Plus, Comma, Colon, Eq
} from './lexer.js';
class HiParser extends CstParser { class HiParser extends CstParser {
constructor() { constructor() {
super(allTokens); super(T.allTokens);
const $ = this; const $ = this;
@@ -19,86 +14,136 @@ class HiParser extends CstParser {
$.OR([ $.OR([
{ ALT: () => $.SUBRULE($.declaration) }, { ALT: () => $.SUBRULE($.declaration) },
{ ALT: () => $.SUBRULE($.assignment) }, { ALT: () => $.SUBRULE($.assignment) },
{ ALT: () => $.SUBRULE($.returnStatement) },
{ ALT: () => $.SUBRULE($.expressionStatement) }, { ALT: () => $.SUBRULE($.expressionStatement) },
]); ]);
}); });
$.RULE('expressionStatement', () => $.SUBRULE($.expression)); $.RULE('expressionStatement', () => $.SUBRULE($.expression));
$.RULE('returnStatement', () => {
$.CONSUME(T.Caret);
$.OPTION(() => $.SUBRULE($.expression));
});
$.RULE('declaration', () => { $.RULE('declaration', () => {
$.CONSUME(Identifier); $.CONSUME(T.Identifier);
$.CONSUME(Colon); $.CONSUME(T.Colon);
$.SUBRULE($.expression); $.SUBRULE($.expression);
}); });
$.RULE('assignment', () => { $.RULE('assignment', () => {
$.CONSUME(Identifier); // Note: This only allows simple identifiers for now, not member expressions.
$.CONSUME(Eq); $.CONSUME(T.Identifier);
$.CONSUME(T.Eq);
$.SUBRULE($.expression); $.SUBRULE($.expression);
}); });
$.RULE('expression', () => $.SUBRULE($.additiveExpression)); $.RULE('expression', () => $.SUBRULE($.conditionalExpression));
$.RULE('additiveExpression', () => { $.RULE('conditionalExpression', () => {
$.SUBRULE($.callExpression, { LABEL: 'left' }); $.SUBRULE($.equalityExpression, { LABEL: 'condition' });
$.MANY(() => { $.OPTION(() => {
$.CONSUME(Plus); $.CONSUME(T.Question);
$.SUBRULE2($.callExpression, { LABEL: 'right' }); $.SUBRULE2($.expression, { LABEL: 'consequent' });
$.OPTION2(() => {
$.CONSUME(T.Colon);
$.SUBRULE3($.expression, { LABEL: 'alternate' });
});
}); });
}); });
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', () => { $.RULE('callExpression', () => {
$.SUBRULE($.memberExpression); $.SUBRULE($.memberExpression, { LABEL: 'callee' });
$.OPTION(() => { $.MANY(() => {
$.CONSUME(LParen); $.CONSUME(T.LParen);
$.OPTION2(() => $.SUBRULE($.argumentList)); $.OPTION(() => $.SUBRULE($.argumentList));
$.CONSUME(RParen); $.CONSUME(T.RParen);
}); });
}); });
$.RULE('argumentList', () => { $.RULE('argumentList', () => $.SEPERATED_LIST($.expression, T.Comma));
$.SUBRULE($.expression);
$.MANY(() => {
$.CONSUME(Comma);
$.SUBRULE2($.expression);
});
});
$.RULE('memberExpression', () => { $.RULE('memberExpression', () => {
$.SUBRULE($.primary); $.SUBRULE($.primary, { LABEL: 'object' });
$.MANY(() => { $.MANY(() => {
$.CONSUME(Dot); $.OR([
$.CONSUME(Identifier); { ALT: () => { $.CONSUME(T.Dot); $.CONSUME(T.Identifier); }},
{ ALT: () => { $.CONSUME(T.LBracket); $.SUBRULE($.expression); $.CONSUME(T.RBracket); }}
]);
}); });
}); });
$.RULE('primary', () => { $.RULE('primary', () => {
$.OR([ $.OR([
{ ALT: () => $.SUBRULE($.literal) }, { ALT: () => $.SUBRULE($.literal) },
{ ALT: () => $.CONSUME(Identifier) }, { ALT: () => $.CONSUME(T.Identifier) },
{ ALT: () => $.CONSUME(T.At) },
{ ALT: () => $.SUBRULE($.arrowExpression) },
{ ALT: () => $.SUBRULE($.block) }, { ALT: () => $.SUBRULE($.block) },
{ ALT: () => $.SUBRULE($.arrayLiteral) },
{ ALT: () => { { ALT: () => {
$.CONSUME(LParen); $.CONSUME(T.LParen);
$.SUBRULE($.expression); $.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('block', () => { $.RULE('arrowExpression', () => {
$.CONSUME(LBrace); $.SUBRULE($.parameterList);
$.OPTION(() => $.SUBRULE($.keyValuePairs)); $.CONSUME(T.Arrow);
$.CONSUME(RBrace); $.OR([
{ ALT: () => $.SUBRULE($.block) },
{ ALT: () => $.SUBRULE($.expression) }
]);
}); });
$.RULE('keyValuePairs', () => $.AT_LEAST_ONE(() => $.SUBRULE($.keyValuePair))); $.RULE('arrayLiteral', () => {
$.CONSUME(T.LBracket);
$.OPTION(() => $.SEPERATED_LIST($.expression, T.Comma));
$.CONSUME(T.RBracket);
});
$.RULE('keyValuePair', () => { $.RULE('block', () => {
$.CONSUME(Identifier); $.OPTION(() => $.SUBRULE($.parameterList));
$.CONSUME(Colon); $.CONSUME(T.LBrace);
$.SUBRULE($.expression); $.SUBRULE($.statements);
$.CONSUME(T.RBrace);
});
$.RULE('parameterList', () => {
$.CONSUME(T.LParen);
$.OPTION(() => $.SEPERATED_LIST(T.Identifier, T.Comma));
$.CONSUME(T.RParen);
}); });
this.performSelfAnalysis(); this.performSelfAnalysis();