Skip to content

Commit 38d5f16

Browse files
authored
Fix empty infix parser error (#2011)
1 parent 51f5d0a commit 38d5f16

File tree

2 files changed

+21
-15
lines changed

2 files changed

+21
-15
lines changed

packages/langium/src/parser/langium-parser.ts

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ interface InfixElement {
4444
$infix: true;
4545
$type: string;
4646
$cstNode: CompositeCstNode;
47-
parts: AstNode[];
48-
operators: string[];
47+
parts?: AstNode[];
48+
operators?: string[];
4949
}
5050

5151
function isDataTypeNode(node: { $type: string | symbol | undefined }): node is DataTypeNode {
@@ -467,21 +467,23 @@ export class LangiumParser extends AbstractLangiumParser {
467467
}
468468

469469
private constructInfix(obj: InfixElement, precedence: Map<string, OperatorPrecedence>): any {
470-
if (obj.parts.length === 1) {
470+
const parts = obj.parts;
471+
if (!Array.isArray(parts) || parts.length === 0) {
472+
// Likely the result of a syntax error, simply return undefined
473+
return undefined;
474+
}
475+
const operators = obj.operators;
476+
if (!Array.isArray(operators) || parts.length < 2) {
471477
// Captured just a single, non-binary expression
472478
// Simply return the expression as is.
473-
return obj.parts[0];
474-
}
475-
if (obj.parts.length === 0) {
476-
// This can happen if the expression is incomplete
477-
return undefined;
479+
return parts[0];
478480
}
479481
// Find the operator with the lowest precedence (highest value in precedence map)
480482
let lowestPrecedenceIdx = 0;
481483
let lowestPrecedenceValue = -1;
482484

483-
for (let i = 0; i < obj.operators.length; i++) {
484-
const operator = obj.operators[i];
485+
for (let i = 0; i < operators.length; i++) {
486+
const operator = operators[i];
485487
const opPrecedence = precedence.get(operator) ?? {
486488
precedence: Infinity,
487489
rightAssoc: false
@@ -505,11 +507,11 @@ export class LangiumParser extends AbstractLangiumParser {
505507
}
506508

507509
// Split the expression at the lowest precedence operator
508-
const leftOperators = obj.operators.slice(0, lowestPrecedenceIdx);
509-
const rightOperators = obj.operators.slice(lowestPrecedenceIdx + 1);
510+
const leftOperators = operators.slice(0, lowestPrecedenceIdx);
511+
const rightOperators = operators.slice(lowestPrecedenceIdx + 1);
510512

511-
const leftParts = obj.parts.slice(0, lowestPrecedenceIdx + 1);
512-
const rightParts = obj.parts.slice(lowestPrecedenceIdx + 1);
513+
const leftParts = parts.slice(0, lowestPrecedenceIdx + 1);
514+
const rightParts = parts.slice(lowestPrecedenceIdx + 1);
513515

514516
// Create sub-expressions
515517
const leftInfix: InfixElement = {
@@ -536,7 +538,7 @@ export class LangiumParser extends AbstractLangiumParser {
536538
$type: obj.$type,
537539
$cstNode: obj.$cstNode,
538540
left: leftTree,
539-
operator: obj.operators[lowestPrecedenceIdx],
541+
operator: operators[lowestPrecedenceIdx],
540542
right: rightTree
541543
};
542544
}

packages/langium/test/parser/langium-parser.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ describe('Infix operator parsing', async () => {
139139
await expectExpr('1', '1');
140140
});
141141

142+
test('Should not throw when parsing an empty expression', async () => {
143+
await expectExpr('', 'undefined', 1);
144+
});
145+
142146
test('Should parse infix operator with two expressions', async () => {
143147
await expectExpr('1 + 2', '(1 + 2)');
144148
});

0 commit comments

Comments
 (0)