Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 17 additions & 15 deletions packages/langium/src/parser/langium-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ interface DataTypeNode {
interface InfixElement {
$type: string;
$cstNode: CompositeCstNode;
parts: AstNode[];
operators: string[];
parts?: AstNode[];
operators?: string[];
}

function isDataTypeNode(node: { $type: string | symbol | undefined }): node is DataTypeNode {
Expand Down Expand Up @@ -466,21 +466,23 @@ export class LangiumParser extends AbstractLangiumParser {
}

private constructInfix(obj: InfixElement, precedence: Map<string, OperatorPrecedence>): any {
if (obj.parts.length === 1) {
const parts = obj.parts;
if (!Array.isArray(parts) || parts.length === 0) {
// Likely the result of a syntax error, simply return undefined
return undefined;
}
const operators = obj.operators;
if (!Array.isArray(operators) || parts.length < 2) {
// Captured just a single, non-binary expression
// Simply return the expression as is.
return obj.parts[0];
}
if (obj.parts.length === 0) {
// This can happen if the expression is incomplete
return undefined;
return parts[0];
}
// Find the operator with the lowest precedence (highest value in precedence map)
let lowestPrecedenceIdx = 0;
let lowestPrecedenceValue = -1;

for (let i = 0; i < obj.operators.length; i++) {
const operator = obj.operators[i];
for (let i = 0; i < operators.length; i++) {
const operator = operators[i];
const opPrecedence = precedence.get(operator) ?? {
precedence: Infinity,
rightAssoc: false
Expand All @@ -504,11 +506,11 @@ export class LangiumParser extends AbstractLangiumParser {
}

// Split the expression at the lowest precedence operator
const leftOperators = obj.operators.slice(0, lowestPrecedenceIdx);
const rightOperators = obj.operators.slice(lowestPrecedenceIdx + 1);
const leftOperators = operators.slice(0, lowestPrecedenceIdx);
const rightOperators = operators.slice(lowestPrecedenceIdx + 1);

const leftParts = obj.parts.slice(0, lowestPrecedenceIdx + 1);
const rightParts = obj.parts.slice(lowestPrecedenceIdx + 1);
const leftParts = parts.slice(0, lowestPrecedenceIdx + 1);
const rightParts = parts.slice(lowestPrecedenceIdx + 1);

// Create sub-expressions
const leftInfix: InfixElement = {
Expand All @@ -533,7 +535,7 @@ export class LangiumParser extends AbstractLangiumParser {
$type: obj.$type,
$cstNode: obj.$cstNode,
left: leftTree,
operator: obj.operators[lowestPrecedenceIdx],
operator: operators[lowestPrecedenceIdx],
right: rightTree
};
}
Expand Down
4 changes: 4 additions & 0 deletions packages/langium/test/parser/langium-parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ describe('Infix operator parsing', async () => {
await expectExpr('1', '1');
});

test('Should not throw when parsing an empty expression', async () => {
await expectExpr('', 'undefined', 1);
});

test('Should parse infix operator with two expressions', async () => {
await expectExpr('1 + 2', '(1 + 2)');
});
Expand Down
Loading