In at least one situation (that I don't know how to generalize yet), an incorrect TypeScript interface gets generated. This happens for an inferred type from a parser rule that uses tree-rewriting actions. The result is a TypeScript interface that contains a field with a type that is too specific, i.e. the field type was not "abstract enough" to properly represent what the field could actually contain.
I guess, the example below is more telling than this description.
Langium version: 4.0.0
Package name: langium
Steps To Reproduce
yo langium
- Replace the
.langium file with the code below
npm run langium:generate
- Check the code in the
ast.ts file
Code example:
grammar Demo
entry Model:
Expression;
Expression infers AbstractExpressionChainMember:
{infer AtomicExpression} atom=Atom (
({infer MemberAccess.predecessor=current} '.' member=ID)
)*
;
Atom:
name=ID
;
terminal ID: /[_a-zA-Z][\w_]*/;
hidden terminal WS: /\s+/;
The current behavior
For the MemberAccess parser rule, the following interface gets generated - note the type of the predecessor field:
export interface MemberAccess extends langium.AstNode {
readonly $type: 'MemberAccess';
member: string;
predecessor: AtomicExpression;
}
The expected behavior
The predecessor field should actually be of type AbstractExpressionChainMember.
Reason for why the expected behavior is expected
The reason can be demonstrated with this input example:
test.test.test.test
In this example, the syntax tree looks like this:
{
$type:"MemberAccess",
predecessor: {
$type:"MemberAccess",
predecessor: {
$type:"MemberAccess",
predecessor: {
$type:"AtomicExpression",
atom: {
$type:"Atom",
name:"test"
}
},
member:"test"
},
member:"test"
},
member:"test"
}
You can see: The predecessor field will points to objects that are either of type MemberAccess or of type AtomicExpression. So this example disproves the assumption that the predecessor field will always point to AtomicExpression-typed objects. But this is what the generated TypeScript interface definition implies.
In at least one situation (that I don't know how to generalize yet), an incorrect TypeScript interface gets generated. This happens for an inferred type from a parser rule that uses tree-rewriting actions. The result is a TypeScript interface that contains a field with a type that is too specific, i.e. the field type was not "abstract enough" to properly represent what the field could actually contain.
I guess, the example below is more telling than this description.
Langium version: 4.0.0
Package name: langium
Steps To Reproduce
yo langium.langiumfile with the code belownpm run langium:generateast.tsfileCode example:
The current behavior
For the
MemberAccessparser rule, the following interface gets generated - note the type of thepredecessorfield:The expected behavior
The
predecessorfield should actually be of typeAbstractExpressionChainMember.Reason for why the expected behavior is expected
The reason can be demonstrated with this input example:
test.test.test.testIn this example, the syntax tree looks like this:
You can see: The
predecessorfield will points to objects that are either of typeMemberAccessor of typeAtomicExpression. So this example disproves the assumption that thepredecessorfield will always point toAtomicExpression-typed objects. But this is what the generated TypeScript interface definition implies.