diff --git a/examples/arithmetics/src/language-server/generated/ast.ts b/examples/arithmetics/src/language-server/generated/ast.ts index 088626640..a4085cb4e 100644 --- a/examples/arithmetics/src/language-server/generated/ast.ts +++ b/examples/arithmetics/src/language-server/generated/ast.ts @@ -35,26 +35,32 @@ export type ArithmeticsTokenNames = ArithmeticsTerminalNames | ArithmeticsKeywor export type AbstractDefinition = DeclaredParameter | Definition; -export const AbstractDefinition = 'AbstractDefinition'; +export const AbstractDefinition = { + $type: 'AbstractDefinition' +} as const; export function isAbstractDefinition(item: unknown): item is AbstractDefinition { - return reflection.isInstance(item, AbstractDefinition); + return reflection.isInstance(item, AbstractDefinition.$type); } export type Expression = BinaryExpression | FunctionCall | NumberLiteral; -export const Expression = 'Expression'; +export const Expression = { + $type: 'Expression' +} as const; export function isExpression(item: unknown): item is Expression { - return reflection.isInstance(item, Expression); + return reflection.isInstance(item, Expression.$type); } export type Statement = Definition | Evaluation; -export const Statement = 'Statement'; +export const Statement = { + $type: 'Statement' +} as const; export function isStatement(item: unknown): item is Statement { - return reflection.isInstance(item, Statement); + return reflection.isInstance(item, Statement.$type); } export interface BinaryExpression extends langium.AstNode { @@ -65,10 +71,15 @@ export interface BinaryExpression extends langium.AstNode { right: Expression; } -export const BinaryExpression = 'BinaryExpression'; +export const BinaryExpression = { + $type: 'BinaryExpression', + left: 'left', + operator: 'operator', + right: 'right' +} as const; export function isBinaryExpression(item: unknown): item is BinaryExpression { - return reflection.isInstance(item, BinaryExpression); + return reflection.isInstance(item, BinaryExpression.$type); } export interface DeclaredParameter extends langium.AstNode { @@ -77,10 +88,13 @@ export interface DeclaredParameter extends langium.AstNode { name: string; } -export const DeclaredParameter = 'DeclaredParameter'; +export const DeclaredParameter = { + $type: 'DeclaredParameter', + name: 'name' +} as const; export function isDeclaredParameter(item: unknown): item is DeclaredParameter { - return reflection.isInstance(item, DeclaredParameter); + return reflection.isInstance(item, DeclaredParameter.$type); } export interface Definition extends langium.AstNode { @@ -91,10 +105,15 @@ export interface Definition extends langium.AstNode { name: string; } -export const Definition = 'Definition'; +export const Definition = { + $type: 'Definition', + args: 'args', + expr: 'expr', + name: 'name' +} as const; export function isDefinition(item: unknown): item is Definition { - return reflection.isInstance(item, Definition); + return reflection.isInstance(item, Definition.$type); } export interface Evaluation extends langium.AstNode { @@ -103,10 +122,13 @@ export interface Evaluation extends langium.AstNode { expression: Expression; } -export const Evaluation = 'Evaluation'; +export const Evaluation = { + $type: 'Evaluation', + expression: 'expression' +} as const; export function isEvaluation(item: unknown): item is Evaluation { - return reflection.isInstance(item, Evaluation); + return reflection.isInstance(item, Evaluation.$type); } export interface FunctionCall extends langium.AstNode { @@ -116,10 +138,14 @@ export interface FunctionCall extends langium.AstNode { func: langium.Reference; } -export const FunctionCall = 'FunctionCall'; +export const FunctionCall = { + $type: 'FunctionCall', + args: 'args', + func: 'func' +} as const; export function isFunctionCall(item: unknown): item is FunctionCall { - return reflection.isInstance(item, FunctionCall); + return reflection.isInstance(item, FunctionCall.$type); } export interface Module extends langium.AstNode { @@ -128,10 +154,14 @@ export interface Module extends langium.AstNode { statements: Array; } -export const Module = 'Module'; +export const Module = { + $type: 'Module', + name: 'name', + statements: 'statements' +} as const; export function isModule(item: unknown): item is Module { - return reflection.isInstance(item, Module); + return reflection.isInstance(item, Module.$type); } export interface NumberLiteral extends langium.AstNode { @@ -140,10 +170,13 @@ export interface NumberLiteral extends langium.AstNode { value: number; } -export const NumberLiteral = 'NumberLiteral'; +export const NumberLiteral = { + $type: 'NumberLiteral', + value: 'value' +} as const; export function isNumberLiteral(item: unknown): item is NumberLiteral { - return reflection.isInstance(item, NumberLiteral); + return reflection.isInstance(item, NumberLiteral.$type); } export type ArithmeticsAstType = { @@ -160,117 +193,93 @@ export type ArithmeticsAstType = { } export class ArithmeticsAstReflection extends langium.AbstractAstReflection { - - getAllTypes(): string[] { - return [AbstractDefinition, BinaryExpression, DeclaredParameter, Definition, Evaluation, Expression, FunctionCall, Module, NumberLiteral, Statement]; - } - - protected override computeIsSubtype(subtype: string, supertype: string): boolean { - switch (subtype) { - case BinaryExpression: - case FunctionCall: - case NumberLiteral: { - return this.isSubtype(Expression, supertype); - } - case DeclaredParameter: { - return this.isSubtype(AbstractDefinition, supertype); - } - case Definition: { - return this.isSubtype(AbstractDefinition, supertype) || this.isSubtype(Statement, supertype); - } - case Evaluation: { - return this.isSubtype(Statement, supertype); - } - default: { - return false; - } - } - } - - getReferenceType(refInfo: langium.ReferenceInfo): string { - const referenceId = `${refInfo.container.$type}:${refInfo.property}`; - switch (referenceId) { - case 'FunctionCall:func': { - return AbstractDefinition; - } - default: { - throw new Error(`${referenceId} is not a valid reference id.`); - } - } - } - - getTypeMetaData(type: string): langium.TypeMetaData { - switch (type) { - case BinaryExpression: { - return { - name: BinaryExpression, - properties: [ - { name: 'left' }, - { name: 'operator' }, - { name: 'right' } - ] - }; - } - case DeclaredParameter: { - return { - name: DeclaredParameter, - properties: [ - { name: 'name' } - ] - }; - } - case Definition: { - return { - name: Definition, - properties: [ - { name: 'args', defaultValue: [] }, - { name: 'expr' }, - { name: 'name' } - ] - }; - } - case Evaluation: { - return { - name: Evaluation, - properties: [ - { name: 'expression' } - ] - }; - } - case FunctionCall: { - return { - name: FunctionCall, - properties: [ - { name: 'args', defaultValue: [] }, - { name: 'func' } - ] - }; - } - case Module: { - return { - name: Module, - properties: [ - { name: 'name' }, - { name: 'statements', defaultValue: [] } - ] - }; - } - case NumberLiteral: { - return { - name: NumberLiteral, - properties: [ - { name: 'value' } - ] - }; - } - default: { - return { - name: type, - properties: [] - }; - } - } - } + override readonly types = { + BinaryExpression: { + name: BinaryExpression.$type, + properties: { + left: { + name: 'left' + }, + operator: { + name: 'operator' + }, + right: { + name: 'right' + } + }, + superTypes: ['Expression'] + }, + DeclaredParameter: { + name: DeclaredParameter.$type, + properties: { + name: { + name: 'name' + } + }, + superTypes: ['AbstractDefinition'] + }, + Definition: { + name: Definition.$type, + properties: { + args: { + name: 'args', + defaultValue: [] + }, + expr: { + name: 'expr' + }, + name: { + name: 'name' + } + }, + superTypes: ['AbstractDefinition', 'Statement'] + }, + Evaluation: { + name: Evaluation.$type, + properties: { + expression: { + name: 'expression' + } + }, + superTypes: ['Statement'] + }, + FunctionCall: { + name: FunctionCall.$type, + properties: { + args: { + name: 'args', + defaultValue: [] + }, + func: { + name: 'func', + referenceType: 'AbstractDefinition' + } + }, + superTypes: ['Expression'] + }, + Module: { + name: Module.$type, + properties: { + name: { + name: 'name' + }, + statements: { + name: 'statements', + defaultValue: [] + } + }, + superTypes: [] + }, + NumberLiteral: { + name: NumberLiteral.$type, + properties: { + value: { + name: 'value' + } + }, + superTypes: ['Expression'] + }, + } as const satisfies langium.AstMetaData } export const reflection = new ArithmeticsAstReflection(); diff --git a/examples/domainmodel/src/language-server/generated/ast.ts b/examples/domainmodel/src/language-server/generated/ast.ts index 2b642cbea..46ce45836 100644 --- a/examples/domainmodel/src/language-server/generated/ast.ts +++ b/examples/domainmodel/src/language-server/generated/ast.ts @@ -30,10 +30,12 @@ export type DomainModelTokenNames = DomainModelTerminalNames | DomainModelKeywor export type AbstractElement = PackageDeclaration | Type; -export const AbstractElement = 'AbstractElement'; +export const AbstractElement = { + $type: 'AbstractElement' +} as const; export function isAbstractElement(item: unknown): item is AbstractElement { - return reflection.isInstance(item, AbstractElement); + return reflection.isInstance(item, AbstractElement.$type); } export type QualifiedName = string; @@ -44,10 +46,12 @@ export function isQualifiedName(item: unknown): item is QualifiedName { export type Type = DataType | Entity; -export const Type = 'Type'; +export const Type = { + $type: 'Type' +} as const; export function isType(item: unknown): item is Type { - return reflection.isInstance(item, Type); + return reflection.isInstance(item, Type.$type); } export interface DataType extends langium.AstNode { @@ -56,10 +60,13 @@ export interface DataType extends langium.AstNode { name: string; } -export const DataType = 'DataType'; +export const DataType = { + $type: 'DataType', + name: 'name' +} as const; export function isDataType(item: unknown): item is DataType { - return reflection.isInstance(item, DataType); + return reflection.isInstance(item, DataType.$type); } export interface Domainmodel extends langium.AstNode { @@ -67,10 +74,13 @@ export interface Domainmodel extends langium.AstNode { elements: Array; } -export const Domainmodel = 'Domainmodel'; +export const Domainmodel = { + $type: 'Domainmodel', + elements: 'elements' +} as const; export function isDomainmodel(item: unknown): item is Domainmodel { - return reflection.isInstance(item, Domainmodel); + return reflection.isInstance(item, Domainmodel.$type); } export interface Entity extends langium.AstNode { @@ -81,10 +91,15 @@ export interface Entity extends langium.AstNode { superType?: langium.Reference; } -export const Entity = 'Entity'; +export const Entity = { + $type: 'Entity', + features: 'features', + name: 'name', + superType: 'superType' +} as const; export function isEntity(item: unknown): item is Entity { - return reflection.isInstance(item, Entity); + return reflection.isInstance(item, Entity.$type); } export interface Feature extends langium.AstNode { @@ -95,10 +110,15 @@ export interface Feature extends langium.AstNode { type: langium.Reference; } -export const Feature = 'Feature'; +export const Feature = { + $type: 'Feature', + many: 'many', + name: 'name', + type: 'type' +} as const; export function isFeature(item: unknown): item is Feature { - return reflection.isInstance(item, Feature); + return reflection.isInstance(item, Feature.$type); } export interface PackageDeclaration extends langium.AstNode { @@ -108,10 +128,14 @@ export interface PackageDeclaration extends langium.AstNode { name: QualifiedName; } -export const PackageDeclaration = 'PackageDeclaration'; +export const PackageDeclaration = { + $type: 'PackageDeclaration', + elements: 'elements', + name: 'name' +} as const; export function isPackageDeclaration(item: unknown): item is PackageDeclaration { - return reflection.isInstance(item, PackageDeclaration); + return reflection.isInstance(item, PackageDeclaration.$type); } export type DomainModelAstType = { @@ -125,97 +149,74 @@ export type DomainModelAstType = { } export class DomainModelAstReflection extends langium.AbstractAstReflection { - - getAllTypes(): string[] { - return [AbstractElement, DataType, Domainmodel, Entity, Feature, PackageDeclaration, Type]; - } - - protected override computeIsSubtype(subtype: string, supertype: string): boolean { - switch (subtype) { - case DataType: - case Entity: { - return this.isSubtype(Type, supertype); - } - case PackageDeclaration: - case Type: { - return this.isSubtype(AbstractElement, supertype); - } - default: { - return false; - } - } - } - - getReferenceType(refInfo: langium.ReferenceInfo): string { - const referenceId = `${refInfo.container.$type}:${refInfo.property}`; - switch (referenceId) { - case 'Entity:superType': { - return Entity; - } - case 'Feature:type': { - return Type; - } - default: { - throw new Error(`${referenceId} is not a valid reference id.`); - } - } - } - - getTypeMetaData(type: string): langium.TypeMetaData { - switch (type) { - case DataType: { - return { - name: DataType, - properties: [ - { name: 'name' } - ] - }; - } - case Domainmodel: { - return { - name: Domainmodel, - properties: [ - { name: 'elements', defaultValue: [] } - ] - }; - } - case Entity: { - return { - name: Entity, - properties: [ - { name: 'features', defaultValue: [] }, - { name: 'name' }, - { name: 'superType' } - ] - }; - } - case Feature: { - return { - name: Feature, - properties: [ - { name: 'many', defaultValue: false }, - { name: 'name' }, - { name: 'type' } - ] - }; - } - case PackageDeclaration: { - return { - name: PackageDeclaration, - properties: [ - { name: 'elements', defaultValue: [] }, - { name: 'name' } - ] - }; - } - default: { - return { - name: type, - properties: [] - }; - } - } - } + override readonly types = { + DataType: { + name: DataType.$type, + properties: { + name: { + name: 'name' + } + }, + superTypes: ['Type'] + }, + Domainmodel: { + name: Domainmodel.$type, + properties: { + elements: { + name: 'elements', + defaultValue: [] + } + }, + superTypes: [] + }, + Entity: { + name: Entity.$type, + properties: { + features: { + name: 'features', + defaultValue: [] + }, + name: { + name: 'name' + }, + superType: { + name: 'superType', + referenceType: 'Entity' + } + }, + superTypes: ['Type'] + }, + Feature: { + name: Feature.$type, + properties: { + many: { + name: 'many', + defaultValue: false + }, + name: { + name: 'name' + }, + type: { + name: 'type', + referenceType: 'Type' + } + }, + superTypes: [] + }, + PackageDeclaration: { + name: PackageDeclaration.$type, + properties: { + elements: { + name: 'elements', + defaultValue: [] + }, + name: { + name: 'name' + } + }, + superTypes: ['AbstractElement'] + }, + } as const satisfies langium.AstMetaData } export const reflection = new DomainModelAstReflection(); diff --git a/examples/requirements/src/language-server/generated/ast.ts b/examples/requirements/src/language-server/generated/ast.ts index d569d177a..04541babc 100644 --- a/examples/requirements/src/language-server/generated/ast.ts +++ b/examples/requirements/src/language-server/generated/ast.ts @@ -37,10 +37,13 @@ export interface Contact extends langium.AstNode { user_name: string; } -export const Contact = 'Contact'; +export const Contact = { + $type: 'Contact', + user_name: 'user_name' +} as const; export function isContact(item: unknown): item is Contact { - return reflection.isInstance(item, Contact); + return reflection.isInstance(item, Contact.$type); } export interface Environment extends langium.AstNode { @@ -50,10 +53,14 @@ export interface Environment extends langium.AstNode { name: string; } -export const Environment = 'Environment'; +export const Environment = { + $type: 'Environment', + description: 'description', + name: 'name' +} as const; export function isEnvironment(item: unknown): item is Environment { - return reflection.isInstance(item, Environment); + return reflection.isInstance(item, Environment.$type); } export interface Requirement extends langium.AstNode { @@ -64,10 +71,15 @@ export interface Requirement extends langium.AstNode { text: string; } -export const Requirement = 'Requirement'; +export const Requirement = { + $type: 'Requirement', + environments: 'environments', + name: 'name', + text: 'text' +} as const; export function isRequirement(item: unknown): item is Requirement { - return reflection.isInstance(item, Requirement); + return reflection.isInstance(item, Requirement.$type); } export interface RequirementModel extends langium.AstNode { @@ -77,10 +89,15 @@ export interface RequirementModel extends langium.AstNode { requirements: Array; } -export const RequirementModel = 'RequirementModel'; +export const RequirementModel = { + $type: 'RequirementModel', + contact: 'contact', + environments: 'environments', + requirements: 'requirements' +} as const; export function isRequirementModel(item: unknown): item is RequirementModel { - return reflection.isInstance(item, RequirementModel); + return reflection.isInstance(item, RequirementModel.$type); } export interface Test extends langium.AstNode { @@ -92,10 +109,16 @@ export interface Test extends langium.AstNode { testFile?: string; } -export const Test = 'Test'; +export const Test = { + $type: 'Test', + environments: 'environments', + name: 'name', + requirements: 'requirements', + testFile: 'testFile' +} as const; export function isTest(item: unknown): item is Test { - return reflection.isInstance(item, Test); + return reflection.isInstance(item, Test.$type); } export interface TestModel extends langium.AstNode { @@ -104,10 +127,14 @@ export interface TestModel extends langium.AstNode { tests: Array; } -export const TestModel = 'TestModel'; +export const TestModel = { + $type: 'TestModel', + contact: 'contact', + tests: 'tests' +} as const; export function isTestModel(item: unknown): item is TestModel { - return reflection.isInstance(item, TestModel); + return reflection.isInstance(item, TestModel.$type); } export type RequirementsAndTestsAstType = { @@ -120,102 +147,98 @@ export type RequirementsAndTestsAstType = { } export class RequirementsAndTestsAstReflection extends langium.AbstractAstReflection { - - getAllTypes(): string[] { - return [Contact, Environment, Requirement, RequirementModel, Test, TestModel]; - } - - protected override computeIsSubtype(subtype: string, supertype: string): boolean { - switch (subtype) { - default: { - return false; - } - } - } - - getReferenceType(refInfo: langium.ReferenceInfo): string { - const referenceId = `${refInfo.container.$type}:${refInfo.property}`; - switch (referenceId) { - case 'Requirement:environments': - case 'Test:environments': { - return Environment; - } - case 'Test:requirements': { - return Requirement; - } - default: { - throw new Error(`${referenceId} is not a valid reference id.`); - } - } - } - - getTypeMetaData(type: string): langium.TypeMetaData { - switch (type) { - case Contact: { - return { - name: Contact, - properties: [ - { name: 'user_name' } - ] - }; - } - case Environment: { - return { - name: Environment, - properties: [ - { name: 'description' }, - { name: 'name' } - ] - }; - } - case Requirement: { - return { - name: Requirement, - properties: [ - { name: 'environments', defaultValue: [] }, - { name: 'name' }, - { name: 'text' } - ] - }; - } - case RequirementModel: { - return { - name: RequirementModel, - properties: [ - { name: 'contact' }, - { name: 'environments', defaultValue: [] }, - { name: 'requirements', defaultValue: [] } - ] - }; - } - case Test: { - return { - name: Test, - properties: [ - { name: 'environments', defaultValue: [] }, - { name: 'name' }, - { name: 'requirements', defaultValue: [] }, - { name: 'testFile' } - ] - }; - } - case TestModel: { - return { - name: TestModel, - properties: [ - { name: 'contact' }, - { name: 'tests', defaultValue: [] } - ] - }; - } - default: { - return { - name: type, - properties: [] - }; - } + override readonly types = { + Contact: { + name: Contact.$type, + properties: { + user_name: { + name: 'user_name' + } + }, + superTypes: [] + }, + Environment: { + name: Environment.$type, + properties: { + description: { + name: 'description' + }, + name: { + name: 'name' + } + }, + superTypes: [] + }, + Requirement: { + name: Requirement.$type, + properties: { + environments: { + name: 'environments', + defaultValue: [], + referenceType: 'Environment' + }, + name: { + name: 'name' + }, + text: { + name: 'text' + } + }, + superTypes: [] + }, + RequirementModel: { + name: RequirementModel.$type, + properties: { + contact: { + name: 'contact' + }, + environments: { + name: 'environments', + defaultValue: [] + }, + requirements: { + name: 'requirements', + defaultValue: [] + } + }, + superTypes: [] + }, + Test: { + name: Test.$type, + properties: { + environments: { + name: 'environments', + defaultValue: [], + referenceType: 'Environment' + }, + name: { + name: 'name' + }, + requirements: { + name: 'requirements', + defaultValue: [], + referenceType: 'Requirement' + }, + testFile: { + name: 'testFile' + } + }, + superTypes: [] + }, + TestModel: { + name: TestModel.$type, + properties: { + contact: { + name: 'contact' + }, + tests: { + name: 'tests', + defaultValue: [] + } + }, + superTypes: [] } - } + } as const satisfies langium.AstMetaData } export const reflection = new RequirementsAndTestsAstReflection(); diff --git a/examples/statemachine/src/language-server/generated/ast.ts b/examples/statemachine/src/language-server/generated/ast.ts index baaff157f..5d25092c7 100644 --- a/examples/statemachine/src/language-server/generated/ast.ts +++ b/examples/statemachine/src/language-server/generated/ast.ts @@ -35,10 +35,13 @@ export interface Command extends langium.AstNode { name: string; } -export const Command = 'Command'; +export const Command = { + $type: 'Command', + name: 'name' +} as const; export function isCommand(item: unknown): item is Command { - return reflection.isInstance(item, Command); + return reflection.isInstance(item, Command.$type); } /** An event is the trigger for a transition */ @@ -48,10 +51,13 @@ export interface Event extends langium.AstNode { name: string; } -export const Event = 'Event'; +export const Event = { + $type: 'Event', + name: 'name' +} as const; export function isEvent(item: unknown): item is Event { - return reflection.isInstance(item, Event); + return reflection.isInstance(item, Event.$type); } /** A description of the status of a system */ @@ -64,10 +70,15 @@ export interface State extends langium.AstNode { transitions: Array; } -export const State = 'State'; +export const State = { + $type: 'State', + actions: 'actions', + name: 'name', + transitions: 'transitions' +} as const; export function isState(item: unknown): item is State { - return reflection.isInstance(item, State); + return reflection.isInstance(item, State.$type); } /** A textual represntation of a state machine */ @@ -84,10 +95,17 @@ export interface Statemachine extends langium.AstNode { states: Array; } -export const Statemachine = 'Statemachine'; +export const Statemachine = { + $type: 'Statemachine', + commands: 'commands', + events: 'events', + init: 'init', + name: 'name', + states: 'states' +} as const; export function isStatemachine(item: unknown): item is Statemachine { - return reflection.isInstance(item, Statemachine); + return reflection.isInstance(item, Statemachine.$type); } /** A change from one state to another */ @@ -100,10 +118,14 @@ export interface Transition extends langium.AstNode { state: langium.Reference; } -export const Transition = 'Transition'; +export const Transition = { + $type: 'Transition', + event: 'event', + state: 'state' +} as const; export function isTransition(item: unknown): item is Transition { - return reflection.isInstance(item, Transition); + return reflection.isInstance(item, Transition.$type); } export type StatemachineAstType = { @@ -115,95 +137,83 @@ export type StatemachineAstType = { } export class StatemachineAstReflection extends langium.AbstractAstReflection { - - getAllTypes(): string[] { - return [Command, Event, State, Statemachine, Transition]; - } - - protected override computeIsSubtype(subtype: string, supertype: string): boolean { - switch (subtype) { - default: { - return false; - } - } - } - - getReferenceType(refInfo: langium.ReferenceInfo): string { - const referenceId = `${refInfo.container.$type}:${refInfo.property}`; - switch (referenceId) { - case 'State:actions': { - return Command; - } - case 'Statemachine:init': - case 'Transition:state': { - return State; - } - case 'Transition:event': { - return Event; - } - default: { - throw new Error(`${referenceId} is not a valid reference id.`); - } - } - } - - getTypeMetaData(type: string): langium.TypeMetaData { - switch (type) { - case Command: { - return { - name: Command, - properties: [ - { name: 'name' } - ] - }; - } - case Event: { - return { - name: Event, - properties: [ - { name: 'name' } - ] - }; - } - case State: { - return { - name: State, - properties: [ - { name: 'actions', defaultValue: [] }, - { name: 'name' }, - { name: 'transitions', defaultValue: [] } - ] - }; - } - case Statemachine: { - return { - name: Statemachine, - properties: [ - { name: 'commands', defaultValue: [] }, - { name: 'events', defaultValue: [] }, - { name: 'init' }, - { name: 'name' }, - { name: 'states', defaultValue: [] } - ] - }; - } - case Transition: { - return { - name: Transition, - properties: [ - { name: 'event' }, - { name: 'state' } - ] - }; - } - default: { - return { - name: type, - properties: [] - }; - } + override readonly types = { + Command: { + name: Command.$type, + properties: { + name: { + name: 'name' + } + }, + superTypes: [] + }, + Event: { + name: Event.$type, + properties: { + name: { + name: 'name' + } + }, + superTypes: [] + }, + State: { + name: State.$type, + properties: { + actions: { + name: 'actions', + defaultValue: [], + referenceType: 'Command' + }, + name: { + name: 'name' + }, + transitions: { + name: 'transitions', + defaultValue: [] + } + }, + superTypes: [] + }, + Statemachine: { + name: Statemachine.$type, + properties: { + commands: { + name: 'commands', + defaultValue: [] + }, + events: { + name: 'events', + defaultValue: [] + }, + init: { + name: 'init', + referenceType: 'State' + }, + name: { + name: 'name' + }, + states: { + name: 'states', + defaultValue: [] + } + }, + superTypes: [] + }, + Transition: { + name: Transition.$type, + properties: { + event: { + name: 'event', + referenceType: 'Event' + }, + state: { + name: 'state', + referenceType: 'State' + } + }, + superTypes: [] } - } + } as const satisfies langium.AstMetaData } export const reflection = new StatemachineAstReflection(); diff --git a/packages/langium-cli/src/generator/ast-generator.ts b/packages/langium-cli/src/generator/ast-generator.ts index ff62a19f4..8baaf086a 100644 --- a/packages/langium-cli/src/generator/ast-generator.ts +++ b/packages/langium-cli/src/generator/ast-generator.ts @@ -7,7 +7,6 @@ import type { Grammar, LangiumCoreServices } from 'langium'; import { EOL, type Generated, expandToNode, joinToNode, toString } from 'langium/generate'; import type { AstTypes, Property, PropertyDefaultValue } from 'langium/grammar'; import type { LangiumConfig } from '../package-types.js'; -import { MultiMap } from 'langium'; import { collectAst, collectTypeHierarchy, findReferenceTypes, isAstType, mergeTypesAndInterfaces, escapeQuotes } from 'langium/grammar'; import { generatedHeader } from './node-util.js'; import { collectKeywords, collectTerminalRegexps } from './langium-util.js'; @@ -37,79 +36,62 @@ function generateAstReflection(config: LangiumConfig, astTypes: AstTypes): Gener const typeNames: string[] = astTypes.interfaces.map(t => t.name) .concat(astTypes.unions.map(t => t.name)) .sort(); - const crossReferenceTypes = buildCrossReferenceTypes(astTypes); + const typeHierarchy = collectTypeHierarchy(mergeTypesAndInterfaces(astTypes)); + return expandToNode` export type ${config.projectName}AstType = { ${joinToNode(typeNames, name => name + ': ' + name, { appendNewLineIfNotEmpty: true })} } export class ${config.projectName}AstReflection extends langium.AbstractAstReflection { - - getAllTypes(): string[] { - return [${typeNames.join(', ')}]; - } - - protected override computeIsSubtype(subtype: string, supertype: string): boolean { - ${buildIsSubtypeMethod(astTypes)} - } - - getReferenceType(refInfo: langium.ReferenceInfo): string { - ${buildReferenceTypeMethod(crossReferenceTypes)} - } - - getTypeMetaData(type: string): langium.TypeMetaData { - ${buildTypeMetaDataMethod(astTypes)} - } + override readonly types = { + ${joinToNode(typeNames, typeName => { + const interfaceType = astTypes.interfaces.find(t => t.name === typeName); + if (interfaceType) { + const props = interfaceType.superProperties; + const superTypes = typeHierarchy.superTypes.get(typeName) || []; + return expandToNode` + ${typeName}: { + name: ${typeName}.$type, + properties: { + ${buildPropertyMetaData(props)} + }, + superTypes: [${superTypes.map(t => `'${t}'`).join(', ')}] + } + `; + } + return undefined; + }, { separator: ',', appendNewLineIfNotEmpty: true })} + } as const satisfies langium.AstMetaData } export const reflection = new ${config.projectName}AstReflection(); `.appendNewLine(); } -function buildTypeMetaDataMethod(astTypes: AstTypes): Generated { - return expandToNode` - switch (type) { - ${ - joinToNode( - astTypes.interfaces, - interfaceType => { - const props = interfaceType.superProperties; - return (props.length > 0) - ? expandToNode` - case ${interfaceType.name}: { - return { - name: ${interfaceType.name}, - properties: [ - ${buildPropertyType(props)} - ] - }; - } - ` - : undefined; - }, - { - appendNewLineIfNotEmpty: true - } - ) - } - default: { - return { - name: type, - properties: [] - }; - } - } - `; -} - -function buildPropertyType(props: Property[]): Generated { +function buildPropertyMetaData(props: Property[]): Generated { const all = props.sort((a, b) => a.name.localeCompare(b.name)); return joinToNode( all, property => { const defaultValue = stringifyDefaultValue(property.defaultValue); - return `{ name: '${escapeQuotes(property.name, "'")}'${defaultValue ? `, defaultValue: ${defaultValue}` : ''} }`; + const refTypes = findReferenceTypes(property.type); + const refType = refTypes.length > 0 ? refTypes[0] : undefined; + + const attributes: string[] = [`name: '${escapeQuotes(property.name, "'")}'`]; + if (defaultValue) { + attributes.push(`defaultValue: ${defaultValue}`); + } + if (refType) { + attributes.push(`referenceType: '${refType}'`); + } + + return expandToNode` + ${property.name}: { + ${joinToNode(attributes, attr => attr, { separator: ',', appendNewLineIfNotEmpty: true })} + } + `; }, { separator: ',', appendNewLineIfNotEmpty: true} ); @@ -128,97 +110,6 @@ function stringifyDefaultValue(value?: PropertyDefaultValue): string | undefined } } -function buildReferenceTypeMethod(crossReferenceTypes: CrossReferenceType[]): Generated { - const buckets = new MultiMap(crossReferenceTypes.map(e => [e.referenceType, `${e.type}:${e.feature}`])); - return expandToNode` - const referenceId = ${'`${refInfo.container.$type}:${refInfo.property}`'}; - switch (referenceId) { - ${ - joinToNode( - buckets.entriesGroupedByKey(), - ([target, refs]) => expandToNode` - ${joinToNode(refs, ref => `case '${escapeQuotes(ref, "'")}':`, { appendNewLineIfNotEmpty: true, skipNewLineAfterLastItem: true})} { - return ${target}; - } - `, - { appendNewLineIfNotEmpty: true } - ) - } - default: { - throw new Error(${'`${referenceId} is not a valid reference id.`'}); - } - } - `; -} - -type CrossReferenceType = { - type: string, - feature: string, - referenceType: string -} - -function buildCrossReferenceTypes(astTypes: AstTypes): CrossReferenceType[] { - const crossReferences = new MultiMap(); - for (const typeInterface of astTypes.interfaces) { - for (const property of typeInterface.properties.sort((a, b) => a.name.localeCompare(b.name))) { - const refTypes = findReferenceTypes(property.type); - for (const refType of refTypes) { - crossReferences.add(typeInterface.name, { - type: typeInterface.name, - feature: property.name, - referenceType: refType - }); - } - } - } - - for (const typeInterface of astTypes.interfaces) { - const superFeatures = new Set(); - for (const superType of typeInterface.interfaceSuperTypes) { - for (const superTypeCrossReference of crossReferences.get(superType.name)) { - if (!superFeatures.has(superTypeCrossReference.feature)) { - crossReferences.add(typeInterface.name, { ...superTypeCrossReference, type: typeInterface.name }); - superFeatures.add(superTypeCrossReference.feature); - } - } - } - } - - return Array.from(crossReferences.values()).sort((a, b) => a.type.localeCompare(b.type)); -} - -function buildIsSubtypeMethod(astTypes: AstTypes): Generated { - const groups = groupBySupertypes(astTypes); - return expandToNode` - switch (subtype) { - ${ - joinToNode( - groups.entriesGroupedByKey(), - ([superTypes, typeGroup]) => expandToNode` - ${joinToNode(typeGroup, typeName => `case ${typeName}:`, { appendNewLineIfNotEmpty: true, skipNewLineAfterLastItem: true })} { - return ${superTypes.split(':').sort().map(e => `this.isSubtype(${e}, supertype)`).join(' || ')}; - } - `, - { appendNewLineIfNotEmpty: true} - ) - } - default: { - return false; - } - } - `; -} - -function groupBySupertypes(astTypes: AstTypes): MultiMap { - const hierarchy = collectTypeHierarchy(mergeTypesAndInterfaces(astTypes)); - const superToChild = new MultiMap(); - for (const [name, superTypes] of hierarchy.superTypes.entriesGroupedByKey()) { - superToChild.add(superTypes.join(':'), name); - } - - return superToChild; -} - function generateTerminalConstants(grammars: Grammar[], config: LangiumConfig): Generated { let collection: Record = {}; const keywordTokens = new Set(); diff --git a/packages/langium-cli/test/generator/ast-generator.test.ts b/packages/langium-cli/test/generator/ast-generator.test.ts index 09769651f..3237c097f 100644 --- a/packages/langium-cli/test/generator/ast-generator.test.ts +++ b/packages/langium-cli/test/generator/ast-generator.test.ts @@ -20,7 +20,7 @@ describe('Ast generator', () => { testGeneratedAst('should generate checker functions for datatype rules comprised of a single string', ` grammar TestGrammar - + A returns string: 'a'; @@ -28,7 +28,7 @@ describe('Ast generator', () => { terminal ID: /[_a-zA-Z][\\w_]*/; `, expandToString` export type A = 'a'; - + export function isA(item: unknown): item is A { return item === 'a'; } @@ -36,7 +36,7 @@ describe('Ast generator', () => { testGeneratedAst('should generate checker functions for datatype rules comprised of a multiple strings', ` grammar TestGrammar - + A returns string: 'a' | 'b' | 'c'; @@ -44,7 +44,7 @@ describe('Ast generator', () => { terminal ID: /[_a-zA-Z][\\w_]*/; `, expandToString` export type A = 'a' | 'b' | 'c'; - + export function isA(item: unknown): item is A { return item === 'a' || item === 'b' || item === 'c'; } @@ -52,7 +52,7 @@ describe('Ast generator', () => { testGeneratedAst('should generate checker functions for datatype rules with subtypes', ` grammar TestGrammar - + A returns string: 'a'; @@ -63,11 +63,11 @@ describe('Ast generator', () => { terminal ID: /[_a-zA-Z][\\w_]*/; `, expandToString` export type A = 'a'; - + export function isA(item: unknown): item is A { return item === 'a'; } - + export type AB = 'b' | A; export function isAB(item: unknown): item is AB { @@ -77,7 +77,7 @@ describe('Ast generator', () => { testGeneratedAst('should generate checker functions for datatype rules referencing a terminal', ` grammar TestGrammar - + A returns string: ID; @@ -85,7 +85,7 @@ describe('Ast generator', () => { terminal ID: /[_a-zA-Z][\\w_]*/; `, expandToString` export type A = string; - + export function isA(item: unknown): item is A { return (typeof item === 'string' && (/[_a-zA-Z][\\w_]*/.test(item))); } @@ -93,7 +93,7 @@ describe('Ast generator', () => { testGeneratedAst('should generate checker functions for datatype rules referencing multiple terminals', ` grammar TestGrammar - + A returns string: ID | STRING; @@ -102,7 +102,7 @@ describe('Ast generator', () => { terminal STRING: /"(\\\\.|[^"\\\\])*"|'(\\\\.|[^'\\\\])*'/; `, expandToString` export type A = string; - + export function isA(item: unknown): item is A { return (typeof item === 'string' && (/[_a-zA-Z][\\w_]*/.test(item) || /"(\\\\.|[^"\\\\])*"|'(\\\\.|[^'\\\\])*'/.test(item))); } @@ -110,7 +110,7 @@ describe('Ast generator', () => { testGeneratedAst('should generate checker functions for datatype rules with nested union', ` grammar TestGrammar - + A returns string: 'a'; @@ -127,19 +127,19 @@ describe('Ast generator', () => { terminal ID: /[_a-zA-Z][\\w_]*/; `, expandToString` export type A = 'a'; - + export function isA(item: unknown): item is A { return item === 'a'; } - + export type ABC = (B | C) | A; - + export function isABC(item: unknown): item is ABC { return isA(item) || isB(item) || isC(item); } export type B = 'b'; - + export function isB(item: unknown): item is B { return item === 'b'; } @@ -153,7 +153,7 @@ describe('Ast generator', () => { testGeneratedAst('should generate checker functions for datatype rules with repeated terminals', ` grammar TestGrammar - + A returns string: ID ('.' ID)*; @@ -161,7 +161,7 @@ describe('Ast generator', () => { terminal ID: /[_a-zA-Z][\\w_]*/; `, expandToString` export type A = string; - + export function isA(item: unknown): item is A { return typeof item === 'string'; } @@ -169,7 +169,7 @@ describe('Ast generator', () => { testGeneratedInterface('should escape string delimiters in property type', ` grammar TestGrammar - + entry Test: value="'test'"; hidden terminal WS: /\\s+/; @@ -179,10 +179,13 @@ describe('Ast generator', () => { value: '\\\'test\\\''; } - export const Test = 'Test'; + export const Test = { + $type: 'Test', + value: 'value' + } as const; export function isTest(item: unknown): item is Test { - return reflection.isInstance(item, Test); + return reflection.isInstance(item, Test.$type); } `); @@ -190,12 +193,12 @@ describe('Ast generator', () => { grammar TestGrammar A returns number: '1'; - + hidden terminal WS: /\\s+/; terminal ID: /[_a-zA-Z][\\w_]*/; `, expandToString` export type A = number; - + export function isA(item: unknown): item is A { return typeof item === 'number'; } @@ -206,12 +209,12 @@ describe('Ast generator', () => { Node: num=A; A returns number: '1'; - + hidden terminal WS: /\\s+/; terminal ID: /[_a-zA-Z][\\w_]*/; `, expandToString` export type A = number; - + export function isA(item: unknown): item is A { return typeof item === 'number'; } @@ -221,10 +224,13 @@ describe('Ast generator', () => { num: A; } - export const Node = 'Node'; + export const Node = { + $type: 'Node', + num: 'num' + } as const; export function isNode(item: unknown): item is Node { - return reflection.isInstance(item, Node); + return reflection.isInstance(item, Node.$type); } `); @@ -233,12 +239,12 @@ describe('Ast generator', () => { Node: num+=A*; A returns number: '1'; - + hidden terminal WS: /\\s+/; terminal ID: /[_a-zA-Z][\\w_]*/; `, expandToString` export type A = number; - + export function isA(item: unknown): item is A { return typeof item === 'number'; } @@ -248,10 +254,13 @@ describe('Ast generator', () => { num: Array; } - export const Node = 'Node'; + export const Node = { + $type: 'Node', + num: 'num' + } as const; export function isNode(item: unknown): item is Node { - return reflection.isInstance(item, Node); + return reflection.isInstance(item, Node.$type); } `); @@ -259,12 +268,12 @@ describe('Ast generator', () => { grammar TestGrammar A returns boolean: 'on'; - + hidden terminal WS: /\\s+/; terminal ID: /[_a-zA-Z][\\w_]*/; `, expandToString` export type A = boolean; - + export function isA(item: unknown): item is A { return typeof item === 'boolean'; } @@ -274,12 +283,12 @@ describe('Ast generator', () => { grammar TestGrammar A returns bigint: '1'; - + hidden terminal WS: /\\s+/; terminal ID: /[_a-zA-Z][\\w_]*/; `, expandToString` export type A = bigint; - + export function isA(item: unknown): item is A { return typeof item === 'bigint'; } @@ -289,12 +298,12 @@ describe('Ast generator', () => { grammar TestGrammar A returns Date: '2023-01-01'; - + hidden terminal WS: /\\s+/; terminal ID: /[_a-zA-Z][\\w_]*/; `, expandToString` export type A = Date; - + export function isA(item: unknown): item is A { return item instanceof Date; } @@ -302,12 +311,12 @@ describe('Ast generator', () => { test('should generate terminal names and regular expressions', () => testTerminalConstants(` grammar TestGrammar - + entry Hello: 'Hello, ' name=ID '!'; hidden terminal WS: /\\s+/; - + terminal ID: /[_a-zA-Z][\\w_]*/; `, expandToString` export const TestTerminals = { @@ -318,12 +327,12 @@ describe('Ast generator', () => { test('should generate terminal constants with range operator', () => testTerminalConstants(` grammar TestGrammar - + entry Amount: value=NUMBER; hidden terminal WS: /\\s+/; - + terminal NUMBER: '0'..'9'+; `, expandToString` export const TestTerminals = { @@ -334,12 +343,12 @@ describe('Ast generator', () => { test('should generate terminal constants with fragments', () => testTerminalConstants(` grammar TestGrammar - + entry Amount: value=NUMBER; hidden terminal WS: /\\s+/; - + terminal NUMBER: DIGIT+; terminal fragment DIGIT: '0'..'9'; `, expandToString` @@ -351,7 +360,7 @@ describe('Ast generator', () => { test('should generate terminal constants with slashes', () => testTerminalConstants(` grammar TestGrammar - + entry Model: value=COMMENT; @@ -364,84 +373,76 @@ describe('Ast generator', () => { testTypeMetaData('should generate property metadata for super types', ` grammar TestGrammar - + interface IAmArray { elements: ArrayContent[]; } interface DeclaredArray extends IAmArray{ } - + DeclaredArray returns DeclaredArray: 'declared' (elements+=ArrayContent)* ';'; hidden terminal WS: /\\s+/; terminal ID: /[_a-zA-Z][\\w_]*/; `, expandToString` - getTypeMetaData(type: string): langium.TypeMetaData { - switch (type) { - case IAmArray: { - return { - name: IAmArray, - properties: [ - { name: 'elements', defaultValue: [] } - ] - }; - } - case DeclaredArray: { - return { - name: DeclaredArray, - properties: [ - { name: 'elements', defaultValue: [] } - ] - }; - } - default: { - return { - name: type, - properties: [] - }; - } + export class testAstReflection extends langium.AbstractAstReflection { + override readonly types = { + DeclaredArray: { + name: DeclaredArray.$type, + properties: { + elements: { + name: 'elements', + defaultValue: [] + } + }, + superTypes: ['IAmArray'] + }, + IAmArray: { + name: IAmArray.$type, + properties: { + elements: { + name: 'elements', + defaultValue: [] + } + }, + superTypes: [] } - } + } as const satisfies langium.AstMetaData }` ); testTypeMetaData('should generate escaped default value', ` grammar TestGrammar - + interface Test { value: string = "'test'"; } - + Test returns Test: value=ID; hidden terminal WS: /\\s+/; terminal ID: /[_a-zA-Z][\\w_]*/; `, expandToString` - getTypeMetaData(type: string): langium.TypeMetaData { - switch (type) { - case Test: { - return { - name: Test, - properties: [ - { name: 'value', defaultValue: '\\'test\\'' } - ] - }; - } - default: { - return { - name: type, - properties: [] - }; - } + export class testAstReflection extends langium.AbstractAstReflection { + override readonly types = { + Test: { + name: Test.$type, + properties: { + value: { + name: 'value', + defaultValue: '\\'test\\'' + } + }, + superTypes: [] } - } + } as const satisfies langium.AstMetaData }` ); testReferenceType('check all referenceIds are properly generated', ` grammar TestGrammar - + interface A { refA1: @A refB1: @B @@ -456,35 +457,86 @@ describe('Ast generator', () => { refD1: @D } `, expandToString` - getReferenceType(refInfo: langium.ReferenceInfo): string { - const referenceId = \`\${refInfo.container.$type}:\${refInfo.property}\`; - switch (referenceId) { - case 'A:refA1': - case 'B:refA1': - case 'C:refA1': - case 'D:refA1': { - return A; - } - case 'A:refB1': - case 'B:refB2': - case 'B:refB1': - case 'C:refB1': - case 'C:refB2': - case 'D:refB1': - case 'D:refB2': { - return B; - } - case 'C:refC1': { - return C; - } - case 'D:refD1': { - return D; - } - default: { - throw new Error(\`\${referenceId} is not a valid reference id.\`); - } + export class testAstReflection extends langium.AbstractAstReflection { + override readonly types = { + A: { + name: A.$type, + properties: { + refA1: { + name: 'refA1', + referenceType: 'A' + }, + refB1: { + name: 'refB1', + referenceType: 'B' + } + }, + superTypes: [] + }, + B: { + name: B.$type, + properties: { + refA1: { + name: 'refA1', + referenceType: 'A' + }, + refB1: { + name: 'refB1', + referenceType: 'B' + }, + refB2: { + name: 'refB2', + referenceType: 'B' + } + }, + superTypes: ['A'] + }, + C: { + name: C.$type, + properties: { + refA1: { + name: 'refA1', + referenceType: 'A' + }, + refB1: { + name: 'refB1', + referenceType: 'B' + }, + refB2: { + name: 'refB2', + referenceType: 'B' + }, + refC1: { + name: 'refC1', + referenceType: 'C' + } + }, + superTypes: ['A', 'B'] + }, + D: { + name: D.$type, + properties: { + refA1: { + name: 'refA1', + referenceType: 'A' + }, + refB1: { + name: 'refB1', + referenceType: 'B' + }, + refB2: { + name: 'refB2', + referenceType: 'B' + }, + refD1: { + name: 'refD1', + referenceType: 'D' + } + }, + superTypes: ['A', 'B'] } - }` + } as const satisfies langium.AstMetaData + }` ); }); @@ -513,11 +565,11 @@ function testGeneratedAst(name: string, grammar: string, expected: string): void } function testTypeMetaData(name: string, grammar: string, expected: string): void { - testGenerated(name, grammar, expected, 'getTypeMetaData', 'export const reflection'); + testGenerated(name, grammar, expected, 'export class testAstReflection', 'export const reflection'); } function testReferenceType(name: string, grammar: string, expected: string): void { - testGenerated(name, grammar, expected, 'getReferenceType', 'getTypeMetaData'); + testGenerated(name, grammar, expected, 'export class testAstReflection', 'export const reflection'); } function testGenerated(name: string, grammar: string, expected: string, start: string, end: string, startCount = 0): void { test(name, async () => { diff --git a/packages/langium/src/grammar/ast-reflection-interpreter.ts b/packages/langium/src/grammar/ast-reflection-interpreter.ts index e4cd042db..00f2fdc98 100644 --- a/packages/langium/src/grammar/ast-reflection-interpreter.ts +++ b/packages/langium/src/grammar/ast-reflection-interpreter.ts @@ -4,7 +4,7 @@ * terms of the MIT License, which is available in the project root. ******************************************************************************/ -import type { AstReflection, ReferenceInfo, TypeProperty, TypeMetaData } from '../syntax-tree.js'; +import type { AstReflection, PropertyMetaData, TypeMetaData } from '../syntax-tree.js'; import type { LangiumCoreServices } from '../index.js'; import type { Grammar } from '../languages/generated/ast.js'; import type { AstTypes, Property } from './type-system/type-collector/types.js'; @@ -38,47 +38,65 @@ export function interpretAstReflection(grammarOrTypes: Grammar | AstTypes, servi class InterpretedAstReflection extends AbstractAstReflection { - private readonly allTypes: string[]; - private readonly references: Map; - private readonly metaData: Map; - private readonly superTypes: MultiMap; - constructor(options: { allTypes: string[] references: Map metaData: Map superTypes: MultiMap }) { - super(); - this.allTypes = options.allTypes; - this.references = options.references; - this.metaData = options.metaData; - this.superTypes = options.superTypes; - } + // Build the types object required by AbstractAstReflection + const types: { [type: string]: TypeMetaData } = {}; - getAllTypes(): string[] { - return this.allTypes; - } + for (const typeName of options.allTypes) { + const typeMetaData = options.metaData.get(typeName); + if (typeMetaData) { + const properties: { [name: string]: PropertyMetaData } = {}; + + // Convert properties array to object and add reference types + if (Array.isArray(typeMetaData.properties)) { + for (const prop of typeMetaData.properties) { + const referenceKey = `${typeName}:${prop.name}`; + const referenceType = options.references.get(referenceKey); + + properties[prop.name] = { + name: prop.name, + defaultValue: prop.defaultValue, + ...(referenceType && { referenceType }) + }; + } + } else { + // If properties is already an object, copy it and add reference types + for (const [propName, prop] of Object.entries(typeMetaData.properties)) { + const referenceKey = `${typeName}:${propName}`; + const referenceType = options.references.get(referenceKey); + + properties[propName] = { + ...prop, + ...(referenceType && { referenceType }) + }; + } + } - getReferenceType(refInfo: ReferenceInfo): string { - const referenceId = `${refInfo.container.$type}:${refInfo.property}`; - const referenceType = this.references.get(referenceId); - if (referenceType) { - return referenceType; + types[typeName] = { + name: typeName, + properties, + superTypes: Array.from(options.superTypes.get(typeName)) + }; + } } - throw new Error('Could not find reference type for ' + referenceId); - } - getTypeMetaData(type: string): TypeMetaData { - return this.metaData.get(type) ?? { - name: type, - properties: [] - }; + super(); + // Initialize the readonly types field + Object.defineProperty(this, 'types', { value: types }); } protected computeIsSubtype(subtype: string, originalSuperType: string): boolean { - const superTypes = this.superTypes.get(subtype); - for (const superType of superTypes) { + const typeMetaData = this.types[subtype]; + if (!typeMetaData) { + return false; + } + + for (const superType of typeMetaData.superTypes) { if (this.isSubtype(superType, originalSuperType)) { return true; } @@ -114,21 +132,21 @@ function buildTypeMetaData(astTypes: AstTypes): Map { const props = interfaceType.superProperties; map.set(interfaceType.name, { name: interfaceType.name, - properties: buildPropertyMetaData(props) + properties: buildPropertyMetaData(props), + superTypes: [] // Will be populated later from superTypes data }); } return map; } -function buildPropertyMetaData(props: Property[]): TypeProperty[] { - const array: TypeProperty[] = []; +function buildPropertyMetaData(props: Property[]): { [name: string]: PropertyMetaData } { + const properties: { [name: string]: PropertyMetaData } = {}; const all = props.sort((a, b) => a.name.localeCompare(b.name)); for (const property of all) { - const mandatoryProperty: TypeProperty = { + properties[property.name] = { name: property.name, defaultValue: property.defaultValue }; - array.push(mandatoryProperty); } - return array; + return properties; } diff --git a/packages/langium/src/grammar/references/grammar-scope.ts b/packages/langium/src/grammar/references/grammar-scope.ts index 3fb0a90cf..8131814d4 100644 --- a/packages/langium/src/grammar/references/grammar-scope.ts +++ b/packages/langium/src/grammar/references/grammar-scope.ts @@ -17,7 +17,7 @@ import { DefaultScopeProvider } from '../../references/scope-provider.js'; import { findRootNode, getContainerOfType, getDocument, streamAllContents } from '../../utils/ast-utils.js'; import { toDocumentSegment } from '../../utils/cst-utils.js'; import { stream } from '../../utils/stream.js'; -import { AbstractType, InferredType, Interface, isAction, isGrammar, isInfixRule, isParserRule, isReturnType, isRuleCall, NamedArgument, Type } from '../../languages/generated/ast.js'; +import { AbstractType, InferredType, Interface, NamedArgument, Type, isAction, isGrammar, isInfixRule, isParserRule, isReturnType, isRuleCall } from '../../languages/generated/ast.js'; import { resolveImportUri } from '../internal-grammar-util.js'; export class LangiumGrammarScopeProvider extends DefaultScopeProvider { @@ -30,11 +30,11 @@ export class LangiumGrammarScopeProvider extends DefaultScopeProvider { } override getScope(context: ReferenceInfo): Scope { - if (context.container.$type === NamedArgument && context.property === 'parameter') { + if (context.container.$type === NamedArgument.$type && context.property === 'parameter') { return this.getNamedArgumentScope(context); } const referenceType = this.reflection.getReferenceType(context); - if (referenceType === AbstractType) { + if (referenceType === AbstractType.$type) { return this.getTypeScope(referenceType, context); } else { return super.getScope(context); @@ -60,7 +60,7 @@ export class LangiumGrammarScopeProvider extends DefaultScopeProvider { if (precomputed && rootNode) { const allDescriptions = precomputed.get(rootNode); if (allDescriptions.length > 0) { - localScope = stream(allDescriptions).filter(des => des.type === Interface || des.type === Type || des.type === InferredType); + localScope = stream(allDescriptions).filter(des => des.type === Interface.$type || des.type === Type.$type || des.type === InferredType.$type); } } @@ -80,8 +80,8 @@ export class LangiumGrammarScopeProvider extends DefaultScopeProvider { const importedUris = new Set(); this.gatherImports(grammar, importedUris); let importedElements = this.indexManager.allElements(referenceType, importedUris); - if (referenceType === AbstractType) { - importedElements = importedElements.filter(des => des.type === Interface || des.type === Type || des.type === InferredType); + if (referenceType === AbstractType.$type) { + importedElements = importedElements.filter(des => des.type === Interface.$type || des.type === Type.$type || des.type === InferredType.$type); } return new MapScope(importedElements); } @@ -190,7 +190,7 @@ export class LangiumGrammarScopeComputation extends DefaultScopeComputation { return nameSegmentGetter(); }, selectionSegment: toDocumentSegment(node.$cstNode), - type: InferredType, + type: InferredType.$type, documentUri: document.uri, path: this.astNodeLocator.getAstNodePath(node) }; diff --git a/packages/langium/src/grammar/type-system/type-collector/types.ts b/packages/langium/src/grammar/type-system/type-collector/types.ts index f53530fe0..dbc19d49e 100644 --- a/packages/langium/src/grammar/type-system/type-collector/types.ts +++ b/packages/langium/src/grammar/type-system/type-collector/types.ts @@ -249,7 +249,7 @@ export class InterfaceType { if (reflectionInfo) { interfaceNode .appendNewLine() - .append(addReflectionInfo(this.name)); + .append(addReflectionInfo(this.name, this.superProperties)); } return toString(interfaceNode); @@ -451,12 +451,15 @@ export function isMandatoryPropertyType(propertyType: PropertyType): boolean { } } -function addReflectionInfo(name: string): Generated { +function addReflectionInfo(name: string, properties: Property[] = []): Generated { return expandToNode` - export const ${name} = '${name}'; + export const ${name} = { + $type: '${name}'${properties.length > 0 ? ',' : ''} + ${joinToNode(properties.sort((a, b) => a.name.localeCompare(b.name)), prop => `${prop.name}: '${escapeQuotes(prop.name, "'")}'`, { separator: ',', appendNewLineIfNotEmpty: true })} + } as const; export function is${name}(item: unknown): item is ${name} { - return reflection.isInstance(item, ${name}); + return reflection.isInstance(item, ${name}.$type); } `.appendNewLine(); } diff --git a/packages/langium/src/languages/generated/ast.ts b/packages/langium/src/languages/generated/ast.ts index 3820edcee..58c8720ac 100644 --- a/packages/langium/src/languages/generated/ast.ts +++ b/packages/langium/src/languages/generated/ast.ts @@ -79,18 +79,22 @@ export type LangiumGrammarTokenNames = LangiumGrammarTerminalNames | LangiumGram export type AbstractRule = InfixRule | ParserRule | TerminalRule; -export const AbstractRule = 'AbstractRule'; +export const AbstractRule = { + $type: 'AbstractRule' +} as const; export function isAbstractRule(item: unknown): item is AbstractRule { - return reflection.isInstance(item, AbstractRule); + return reflection.isInstance(item, AbstractRule.$type); } export type AbstractType = InferredType | InfixRule | Interface | ParserRule | Type; -export const AbstractType = 'AbstractType'; +export const AbstractType = { + $type: 'AbstractType' +} as const; export function isAbstractType(item: unknown): item is AbstractType { - return reflection.isInstance(item, AbstractType); + return reflection.isInstance(item, AbstractType.$type); } export type Associativity = 'left' | 'right'; @@ -101,10 +105,12 @@ export function isAssociativity(item: unknown): item is Associativity { export type Condition = BooleanLiteral | Conjunction | Disjunction | Negation | ParameterReference; -export const Condition = 'Condition'; +export const Condition = { + $type: 'Condition' +} as const; export function isCondition(item: unknown): item is Condition { - return reflection.isInstance(item, Condition); + return reflection.isInstance(item, Condition.$type); } export type FeatureName = 'assoc' | 'current' | 'entry' | 'extends' | 'false' | 'fragment' | 'grammar' | 'hidden' | 'import' | 'infer' | 'infers' | 'infix' | 'interface' | 'left' | 'on' | 'returns' | 'right' | 'terminal' | 'true' | 'type' | 'with' | PrimitiveType | string; @@ -121,18 +127,22 @@ export function isPrimitiveType(item: unknown): item is PrimitiveType { export type TypeDefinition = ArrayType | ReferenceType | SimpleType | UnionType; -export const TypeDefinition = 'TypeDefinition'; +export const TypeDefinition = { + $type: 'TypeDefinition' +} as const; export function isTypeDefinition(item: unknown): item is TypeDefinition { - return reflection.isInstance(item, TypeDefinition); + return reflection.isInstance(item, TypeDefinition.$type); } export type ValueLiteral = ArrayLiteral | BooleanLiteral | NumberLiteral | StringLiteral; -export const ValueLiteral = 'ValueLiteral'; +export const ValueLiteral = { + $type: 'ValueLiteral' +} as const; export function isValueLiteral(item: unknown): item is ValueLiteral { - return reflection.isInstance(item, ValueLiteral); + return reflection.isInstance(item, ValueLiteral.$type); } export interface AbstractElement extends langium.AstNode { @@ -141,10 +151,14 @@ export interface AbstractElement extends langium.AstNode { lookahead?: '?!' | '?; } -export const ArrayLiteral = 'ArrayLiteral'; +export const ArrayLiteral = { + $type: 'ArrayLiteral', + elements: 'elements' +} as const; export function isArrayLiteral(item: unknown): item is ArrayLiteral { - return reflection.isInstance(item, ArrayLiteral); + return reflection.isInstance(item, ArrayLiteral.$type); } export interface ArrayType extends langium.AstNode { @@ -165,10 +182,13 @@ export interface ArrayType extends langium.AstNode { elementType: TypeDefinition; } -export const ArrayType = 'ArrayType'; +export const ArrayType = { + $type: 'ArrayType', + elementType: 'elementType' +} as const; export function isArrayType(item: unknown): item is ArrayType { - return reflection.isInstance(item, ArrayType); + return reflection.isInstance(item, ArrayType.$type); } export interface BooleanLiteral extends langium.AstNode { @@ -177,10 +197,13 @@ export interface BooleanLiteral extends langium.AstNode { true: boolean; } -export const BooleanLiteral = 'BooleanLiteral'; +export const BooleanLiteral = { + $type: 'BooleanLiteral', + true: 'true' +} as const; export function isBooleanLiteral(item: unknown): item is BooleanLiteral { - return reflection.isInstance(item, BooleanLiteral); + return reflection.isInstance(item, BooleanLiteral.$type); } export interface Conjunction extends langium.AstNode { @@ -190,10 +213,14 @@ export interface Conjunction extends langium.AstNode { right: Condition; } -export const Conjunction = 'Conjunction'; +export const Conjunction = { + $type: 'Conjunction', + left: 'left', + right: 'right' +} as const; export function isConjunction(item: unknown): item is Conjunction { - return reflection.isInstance(item, Conjunction); + return reflection.isInstance(item, Conjunction.$type); } export interface Disjunction extends langium.AstNode { @@ -203,10 +230,14 @@ export interface Disjunction extends langium.AstNode { right: Condition; } -export const Disjunction = 'Disjunction'; +export const Disjunction = { + $type: 'Disjunction', + left: 'left', + right: 'right' +} as const; export function isDisjunction(item: unknown): item is Disjunction { - return reflection.isInstance(item, Disjunction); + return reflection.isInstance(item, Disjunction.$type); } export interface Grammar extends langium.AstNode { @@ -219,10 +250,18 @@ export interface Grammar extends langium.AstNode { types: Array; } -export const Grammar = 'Grammar'; +export const Grammar = { + $type: 'Grammar', + imports: 'imports', + interfaces: 'interfaces', + isDeclared: 'isDeclared', + name: 'name', + rules: 'rules', + types: 'types' +} as const; export function isGrammar(item: unknown): item is Grammar { - return reflection.isInstance(item, Grammar); + return reflection.isInstance(item, Grammar.$type); } export interface GrammarImport extends langium.AstNode { @@ -231,10 +270,13 @@ export interface GrammarImport extends langium.AstNode { path: string; } -export const GrammarImport = 'GrammarImport'; +export const GrammarImport = { + $type: 'GrammarImport', + path: 'path' +} as const; export function isGrammarImport(item: unknown): item is GrammarImport { - return reflection.isInstance(item, GrammarImport); + return reflection.isInstance(item, GrammarImport.$type); } export interface InferredType extends langium.AstNode { @@ -243,10 +285,13 @@ export interface InferredType extends langium.AstNode { name: string; } -export const InferredType = 'InferredType'; +export const InferredType = { + $type: 'InferredType', + name: 'name' +} as const; export function isInferredType(item: unknown): item is InferredType { - return reflection.isInstance(item, InferredType); + return reflection.isInstance(item, InferredType.$type); } export interface InfixRule extends langium.AstNode { @@ -258,10 +303,16 @@ export interface InfixRule extends langium.AstNode { parameters: Array; } -export const InfixRule = 'InfixRule'; +export const InfixRule = { + $type: 'InfixRule', + call: 'call', + name: 'name', + operators: 'operators', + parameters: 'parameters' +} as const; export function isInfixRule(item: unknown): item is InfixRule { - return reflection.isInstance(item, InfixRule); + return reflection.isInstance(item, InfixRule.$type); } export interface InfixRuleOperatorList extends langium.AstNode { @@ -271,10 +322,14 @@ export interface InfixRuleOperatorList extends langium.AstNode { operators: Array; } -export const InfixRuleOperatorList = 'InfixRuleOperatorList'; +export const InfixRuleOperatorList = { + $type: 'InfixRuleOperatorList', + associativity: 'associativity', + operators: 'operators' +} as const; export function isInfixRuleOperatorList(item: unknown): item is InfixRuleOperatorList { - return reflection.isInstance(item, InfixRuleOperatorList); + return reflection.isInstance(item, InfixRuleOperatorList.$type); } export interface InfixRuleOperators extends langium.AstNode { @@ -283,10 +338,13 @@ export interface InfixRuleOperators extends langium.AstNode { precedences: Array; } -export const InfixRuleOperators = 'InfixRuleOperators'; +export const InfixRuleOperators = { + $type: 'InfixRuleOperators', + precedences: 'precedences' +} as const; export function isInfixRuleOperators(item: unknown): item is InfixRuleOperators { - return reflection.isInstance(item, InfixRuleOperators); + return reflection.isInstance(item, InfixRuleOperators.$type); } export interface Interface extends langium.AstNode { @@ -297,10 +355,15 @@ export interface Interface extends langium.AstNode { superTypes: Array>; } -export const Interface = 'Interface'; +export const Interface = { + $type: 'Interface', + attributes: 'attributes', + name: 'name', + superTypes: 'superTypes' +} as const; export function isInterface(item: unknown): item is Interface { - return reflection.isInstance(item, Interface); + return reflection.isInstance(item, Interface.$type); } export interface NamedArgument extends langium.AstNode { @@ -311,10 +374,15 @@ export interface NamedArgument extends langium.AstNode { value: Condition; } -export const NamedArgument = 'NamedArgument'; +export const NamedArgument = { + $type: 'NamedArgument', + calledByName: 'calledByName', + parameter: 'parameter', + value: 'value' +} as const; export function isNamedArgument(item: unknown): item is NamedArgument { - return reflection.isInstance(item, NamedArgument); + return reflection.isInstance(item, NamedArgument.$type); } export interface Negation extends langium.AstNode { @@ -323,10 +391,13 @@ export interface Negation extends langium.AstNode { value: Condition; } -export const Negation = 'Negation'; +export const Negation = { + $type: 'Negation', + value: 'value' +} as const; export function isNegation(item: unknown): item is Negation { - return reflection.isInstance(item, Negation); + return reflection.isInstance(item, Negation.$type); } export interface NumberLiteral extends langium.AstNode { @@ -335,10 +406,13 @@ export interface NumberLiteral extends langium.AstNode { value: number; } -export const NumberLiteral = 'NumberLiteral'; +export const NumberLiteral = { + $type: 'NumberLiteral', + value: 'value' +} as const; export function isNumberLiteral(item: unknown): item is NumberLiteral { - return reflection.isInstance(item, NumberLiteral); + return reflection.isInstance(item, NumberLiteral.$type); } export interface Parameter extends langium.AstNode { @@ -347,10 +421,13 @@ export interface Parameter extends langium.AstNode { name: string; } -export const Parameter = 'Parameter'; +export const Parameter = { + $type: 'Parameter', + name: 'name' +} as const; export function isParameter(item: unknown): item is Parameter { - return reflection.isInstance(item, Parameter); + return reflection.isInstance(item, Parameter.$type); } export interface ParameterReference extends langium.AstNode { @@ -359,10 +436,13 @@ export interface ParameterReference extends langium.AstNode { parameter: langium.Reference; } -export const ParameterReference = 'ParameterReference'; +export const ParameterReference = { + $type: 'ParameterReference', + parameter: 'parameter' +} as const; export function isParameterReference(item: unknown): item is ParameterReference { - return reflection.isInstance(item, ParameterReference); + return reflection.isInstance(item, ParameterReference.$type); } export interface ParserRule extends langium.AstNode { @@ -378,10 +458,20 @@ export interface ParserRule extends langium.AstNode { returnType?: langium.Reference; } -export const ParserRule = 'ParserRule'; +export const ParserRule = { + $type: 'ParserRule', + dataType: 'dataType', + definition: 'definition', + entry: 'entry', + fragment: 'fragment', + inferredType: 'inferredType', + name: 'name', + parameters: 'parameters', + returnType: 'returnType' +} as const; export function isParserRule(item: unknown): item is ParserRule { - return reflection.isInstance(item, ParserRule); + return reflection.isInstance(item, ParserRule.$type); } export interface ReferenceType extends langium.AstNode { @@ -390,10 +480,13 @@ export interface ReferenceType extends langium.AstNode { referenceType: TypeDefinition; } -export const ReferenceType = 'ReferenceType'; +export const ReferenceType = { + $type: 'ReferenceType', + referenceType: 'referenceType' +} as const; export function isReferenceType(item: unknown): item is ReferenceType { - return reflection.isInstance(item, ReferenceType); + return reflection.isInstance(item, ReferenceType.$type); } export interface ReturnType extends langium.AstNode { @@ -402,10 +495,13 @@ export interface ReturnType extends langium.AstNode { name: PrimitiveType | string; } -export const ReturnType = 'ReturnType'; +export const ReturnType = { + $type: 'ReturnType', + name: 'name' +} as const; export function isReturnType(item: unknown): item is ReturnType { - return reflection.isInstance(item, ReturnType); + return reflection.isInstance(item, ReturnType.$type); } export interface SimpleType extends langium.AstNode { @@ -416,10 +512,15 @@ export interface SimpleType extends langium.AstNode { typeRef?: langium.Reference; } -export const SimpleType = 'SimpleType'; +export const SimpleType = { + $type: 'SimpleType', + primitiveType: 'primitiveType', + stringType: 'stringType', + typeRef: 'typeRef' +} as const; export function isSimpleType(item: unknown): item is SimpleType { - return reflection.isInstance(item, SimpleType); + return reflection.isInstance(item, SimpleType.$type); } export interface StringLiteral extends langium.AstNode { @@ -428,10 +529,13 @@ export interface StringLiteral extends langium.AstNode { value: string; } -export const StringLiteral = 'StringLiteral'; +export const StringLiteral = { + $type: 'StringLiteral', + value: 'value' +} as const; export function isStringLiteral(item: unknown): item is StringLiteral { - return reflection.isInstance(item, StringLiteral); + return reflection.isInstance(item, StringLiteral.$type); } export interface TerminalRule extends langium.AstNode { @@ -444,10 +548,17 @@ export interface TerminalRule extends langium.AstNode { type?: ReturnType; } -export const TerminalRule = 'TerminalRule'; +export const TerminalRule = { + $type: 'TerminalRule', + definition: 'definition', + fragment: 'fragment', + hidden: 'hidden', + name: 'name', + type: 'type' +} as const; export function isTerminalRule(item: unknown): item is TerminalRule { - return reflection.isInstance(item, TerminalRule); + return reflection.isInstance(item, TerminalRule.$type); } export interface Type extends langium.AstNode { @@ -457,10 +568,14 @@ export interface Type extends langium.AstNode { type: TypeDefinition; } -export const Type = 'Type'; +export const Type = { + $type: 'Type', + name: 'name', + type: 'type' +} as const; export function isType(item: unknown): item is Type { - return reflection.isInstance(item, Type); + return reflection.isInstance(item, Type.$type); } export interface TypeAttribute extends langium.AstNode { @@ -472,10 +587,16 @@ export interface TypeAttribute extends langium.AstNode { type: TypeDefinition; } -export const TypeAttribute = 'TypeAttribute'; +export const TypeAttribute = { + $type: 'TypeAttribute', + defaultValue: 'defaultValue', + isOptional: 'isOptional', + name: 'name', + type: 'type' +} as const; export function isTypeAttribute(item: unknown): item is TypeAttribute { - return reflection.isInstance(item, TypeAttribute); + return reflection.isInstance(item, TypeAttribute.$type); } export interface UnionType extends langium.AstNode { @@ -484,10 +605,13 @@ export interface UnionType extends langium.AstNode { types: Array; } -export const UnionType = 'UnionType'; +export const UnionType = { + $type: 'UnionType', + types: 'types' +} as const; export function isUnionType(item: unknown): item is UnionType { - return reflection.isInstance(item, UnionType); + return reflection.isInstance(item, UnionType.$type); } export interface Action extends AbstractElement { @@ -498,10 +622,18 @@ export interface Action extends AbstractElement { type?: langium.Reference; } -export const Action = 'Action'; +export const Action = { + $type: 'Action', + cardinality: 'cardinality', + feature: 'feature', + inferredType: 'inferredType', + lookahead: 'lookahead', + operator: 'operator', + type: 'type' +} as const; export function isAction(item: unknown): item is Action { - return reflection.isInstance(item, Action); + return reflection.isInstance(item, Action.$type); } export interface Alternatives extends AbstractElement { @@ -509,10 +641,15 @@ export interface Alternatives extends AbstractElement { elements: Array; } -export const Alternatives = 'Alternatives'; +export const Alternatives = { + $type: 'Alternatives', + cardinality: 'cardinality', + elements: 'elements', + lookahead: 'lookahead' +} as const; export function isAlternatives(item: unknown): item is Alternatives { - return reflection.isInstance(item, Alternatives); + return reflection.isInstance(item, Alternatives.$type); } export interface Assignment extends AbstractElement { @@ -523,10 +660,18 @@ export interface Assignment extends AbstractElement { terminal: AbstractElement; } -export const Assignment = 'Assignment'; +export const Assignment = { + $type: 'Assignment', + cardinality: 'cardinality', + feature: 'feature', + lookahead: 'lookahead', + operator: 'operator', + predicate: 'predicate', + terminal: 'terminal' +} as const; export function isAssignment(item: unknown): item is Assignment { - return reflection.isInstance(item, Assignment); + return reflection.isInstance(item, Assignment.$type); } export interface CharacterRange extends AbstractElement { @@ -535,10 +680,16 @@ export interface CharacterRange extends AbstractElement { right?: Keyword; } -export const CharacterRange = 'CharacterRange'; +export const CharacterRange = { + $type: 'CharacterRange', + cardinality: 'cardinality', + left: 'left', + lookahead: 'lookahead', + right: 'right' +} as const; export function isCharacterRange(item: unknown): item is CharacterRange { - return reflection.isInstance(item, CharacterRange); + return reflection.isInstance(item, CharacterRange.$type); } export interface CrossReference extends AbstractElement { @@ -548,20 +699,31 @@ export interface CrossReference extends AbstractElement { type: langium.Reference; } -export const CrossReference = 'CrossReference'; +export const CrossReference = { + $type: 'CrossReference', + cardinality: 'cardinality', + deprecatedSyntax: 'deprecatedSyntax', + lookahead: 'lookahead', + terminal: 'terminal', + type: 'type' +} as const; export function isCrossReference(item: unknown): item is CrossReference { - return reflection.isInstance(item, CrossReference); + return reflection.isInstance(item, CrossReference.$type); } export interface EndOfFile extends AbstractElement { readonly $type: 'EndOfFile'; } -export const EndOfFile = 'EndOfFile'; +export const EndOfFile = { + $type: 'EndOfFile', + cardinality: 'cardinality', + lookahead: 'lookahead' +} as const; export function isEndOfFile(item: unknown): item is EndOfFile { - return reflection.isInstance(item, EndOfFile); + return reflection.isInstance(item, EndOfFile.$type); } export interface Group extends AbstractElement { @@ -571,10 +733,17 @@ export interface Group extends AbstractElement { predicate?: '->' | '=>'; } -export const Group = 'Group'; +export const Group = { + $type: 'Group', + cardinality: 'cardinality', + elements: 'elements', + guardCondition: 'guardCondition', + lookahead: 'lookahead', + predicate: 'predicate' +} as const; export function isGroup(item: unknown): item is Group { - return reflection.isInstance(item, Group); + return reflection.isInstance(item, Group.$type); } export interface Keyword extends AbstractElement { @@ -584,10 +753,16 @@ export interface Keyword extends AbstractElement { value: string; } -export const Keyword = 'Keyword'; +export const Keyword = { + $type: 'Keyword', + cardinality: 'cardinality', + lookahead: 'lookahead', + predicate: 'predicate', + value: 'value' +} as const; export function isKeyword(item: unknown): item is Keyword { - return reflection.isInstance(item, Keyword); + return reflection.isInstance(item, Keyword.$type); } export interface NegatedToken extends AbstractElement { @@ -595,10 +770,15 @@ export interface NegatedToken extends AbstractElement { terminal: AbstractElement; } -export const NegatedToken = 'NegatedToken'; +export const NegatedToken = { + $type: 'NegatedToken', + cardinality: 'cardinality', + lookahead: 'lookahead', + terminal: 'terminal' +} as const; export function isNegatedToken(item: unknown): item is NegatedToken { - return reflection.isInstance(item, NegatedToken); + return reflection.isInstance(item, NegatedToken.$type); } export interface RegexToken extends AbstractElement { @@ -606,10 +786,15 @@ export interface RegexToken extends AbstractElement { regex: string; } -export const RegexToken = 'RegexToken'; +export const RegexToken = { + $type: 'RegexToken', + cardinality: 'cardinality', + lookahead: 'lookahead', + regex: 'regex' +} as const; export function isRegexToken(item: unknown): item is RegexToken { - return reflection.isInstance(item, RegexToken); + return reflection.isInstance(item, RegexToken.$type); } export interface RuleCall extends AbstractElement { @@ -620,10 +805,17 @@ export interface RuleCall extends AbstractElement { rule: langium.Reference; } -export const RuleCall = 'RuleCall'; +export const RuleCall = { + $type: 'RuleCall', + arguments: 'arguments', + cardinality: 'cardinality', + lookahead: 'lookahead', + predicate: 'predicate', + rule: 'rule' +} as const; export function isRuleCall(item: unknown): item is RuleCall { - return reflection.isInstance(item, RuleCall); + return reflection.isInstance(item, RuleCall.$type); } export interface TerminalAlternatives extends AbstractElement { @@ -631,10 +823,15 @@ export interface TerminalAlternatives extends AbstractElement { elements: Array; } -export const TerminalAlternatives = 'TerminalAlternatives'; +export const TerminalAlternatives = { + $type: 'TerminalAlternatives', + cardinality: 'cardinality', + elements: 'elements', + lookahead: 'lookahead' +} as const; export function isTerminalAlternatives(item: unknown): item is TerminalAlternatives { - return reflection.isInstance(item, TerminalAlternatives); + return reflection.isInstance(item, TerminalAlternatives.$type); } export interface TerminalGroup extends AbstractElement { @@ -642,10 +839,15 @@ export interface TerminalGroup extends AbstractElement { elements: Array; } -export const TerminalGroup = 'TerminalGroup'; +export const TerminalGroup = { + $type: 'TerminalGroup', + cardinality: 'cardinality', + elements: 'elements', + lookahead: 'lookahead' +} as const; export function isTerminalGroup(item: unknown): item is TerminalGroup { - return reflection.isInstance(item, TerminalGroup); + return reflection.isInstance(item, TerminalGroup.$type); } export interface TerminalRuleCall extends AbstractElement { @@ -653,10 +855,15 @@ export interface TerminalRuleCall extends AbstractElement { rule: langium.Reference; } -export const TerminalRuleCall = 'TerminalRuleCall'; +export const TerminalRuleCall = { + $type: 'TerminalRuleCall', + cardinality: 'cardinality', + lookahead: 'lookahead', + rule: 'rule' +} as const; export function isTerminalRuleCall(item: unknown): item is TerminalRuleCall { - return reflection.isInstance(item, TerminalRuleCall); + return reflection.isInstance(item, TerminalRuleCall.$type); } export interface UnorderedGroup extends AbstractElement { @@ -664,10 +871,15 @@ export interface UnorderedGroup extends AbstractElement { elements: Array; } -export const UnorderedGroup = 'UnorderedGroup'; +export const UnorderedGroup = { + $type: 'UnorderedGroup', + cardinality: 'cardinality', + elements: 'elements', + lookahead: 'lookahead' +} as const; export function isUnorderedGroup(item: unknown): item is UnorderedGroup { - return reflection.isInstance(item, UnorderedGroup); + return reflection.isInstance(item, UnorderedGroup.$type); } export interface UntilToken extends AbstractElement { @@ -675,20 +887,29 @@ export interface UntilToken extends AbstractElement { terminal: AbstractElement; } -export const UntilToken = 'UntilToken'; +export const UntilToken = { + $type: 'UntilToken', + cardinality: 'cardinality', + lookahead: 'lookahead', + terminal: 'terminal' +} as const; export function isUntilToken(item: unknown): item is UntilToken { - return reflection.isInstance(item, UntilToken); + return reflection.isInstance(item, UntilToken.$type); } export interface Wildcard extends AbstractElement { readonly $type: 'Wildcard'; } -export const Wildcard = 'Wildcard'; +export const Wildcard = { + $type: 'Wildcard', + cardinality: 'cardinality', + lookahead: 'lookahead' +} as const; export function isWildcard(item: unknown): item is Wildcard { - return reflection.isInstance(item, Wildcard); + return reflection.isInstance(item, Wildcard.$type); } export type LangiumGrammarAstType = { @@ -744,537 +965,677 @@ export type LangiumGrammarAstType = { } export class LangiumGrammarAstReflection extends langium.AbstractAstReflection { - - getAllTypes(): string[] { - return [AbstractElement, AbstractRule, AbstractType, Action, Alternatives, ArrayLiteral, ArrayType, Assignment, BooleanLiteral, CharacterRange, Condition, Conjunction, CrossReference, Disjunction, EndOfFile, Grammar, GrammarImport, Group, InferredType, InfixRule, InfixRuleOperatorList, InfixRuleOperators, Interface, Keyword, NamedArgument, NegatedToken, Negation, NumberLiteral, Parameter, ParameterReference, ParserRule, ReferenceType, RegexToken, ReturnType, RuleCall, SimpleType, StringLiteral, TerminalAlternatives, TerminalGroup, TerminalRule, TerminalRuleCall, Type, TypeAttribute, TypeDefinition, UnionType, UnorderedGroup, UntilToken, ValueLiteral, Wildcard]; - } - - protected override computeIsSubtype(subtype: string, supertype: string): boolean { - switch (subtype) { - case Action: - case Alternatives: - case Assignment: - case CharacterRange: - case CrossReference: - case EndOfFile: - case Group: - case Keyword: - case NegatedToken: - case RegexToken: - case RuleCall: - case TerminalAlternatives: - case TerminalGroup: - case TerminalRuleCall: - case UnorderedGroup: - case UntilToken: - case Wildcard: { - return this.isSubtype(AbstractElement, supertype); - } - case ArrayLiteral: - case NumberLiteral: - case StringLiteral: { - return this.isSubtype(ValueLiteral, supertype); - } - case ArrayType: - case ReferenceType: - case SimpleType: - case UnionType: { - return this.isSubtype(TypeDefinition, supertype); - } - case BooleanLiteral: { - return this.isSubtype(Condition, supertype) || this.isSubtype(ValueLiteral, supertype); - } - case Conjunction: - case Disjunction: - case Negation: - case ParameterReference: { - return this.isSubtype(Condition, supertype); - } - case InferredType: - case Interface: - case Type: { - return this.isSubtype(AbstractType, supertype); - } - case InfixRule: - case ParserRule: { - return this.isSubtype(AbstractRule, supertype) || this.isSubtype(AbstractType, supertype); - } - case TerminalRule: { - return this.isSubtype(AbstractRule, supertype); - } - default: { - return false; - } - } - } - - getReferenceType(refInfo: langium.ReferenceInfo): string { - const referenceId = `${refInfo.container.$type}:${refInfo.property}`; - switch (referenceId) { - case 'Action:type': - case 'CrossReference:type': - case 'Interface:superTypes': - case 'ParserRule:returnType': - case 'SimpleType:typeRef': { - return AbstractType; - } - case 'NamedArgument:parameter': - case 'ParameterReference:parameter': { - return Parameter; - } - case 'RuleCall:rule': { - return AbstractRule; - } - case 'TerminalRuleCall:rule': { - return TerminalRule; - } - default: { - throw new Error(`${referenceId} is not a valid reference id.`); - } - } - } - - getTypeMetaData(type: string): langium.TypeMetaData { - switch (type) { - case AbstractElement: { - return { - name: AbstractElement, - properties: [ - { name: 'cardinality' }, - { name: 'lookahead' } - ] - }; - } - case ArrayLiteral: { - return { - name: ArrayLiteral, - properties: [ - { name: 'elements', defaultValue: [] } - ] - }; - } - case ArrayType: { - return { - name: ArrayType, - properties: [ - { name: 'elementType' } - ] - }; - } - case BooleanLiteral: { - return { - name: BooleanLiteral, - properties: [ - { name: 'true', defaultValue: false } - ] - }; - } - case Conjunction: { - return { - name: Conjunction, - properties: [ - { name: 'left' }, - { name: 'right' } - ] - }; - } - case Disjunction: { - return { - name: Disjunction, - properties: [ - { name: 'left' }, - { name: 'right' } - ] - }; - } - case Grammar: { - return { - name: Grammar, - properties: [ - { name: 'imports', defaultValue: [] }, - { name: 'interfaces', defaultValue: [] }, - { name: 'isDeclared', defaultValue: false }, - { name: 'name' }, - { name: 'rules', defaultValue: [] }, - { name: 'types', defaultValue: [] } - ] - }; - } - case GrammarImport: { - return { - name: GrammarImport, - properties: [ - { name: 'path' } - ] - }; - } - case InferredType: { - return { - name: InferredType, - properties: [ - { name: 'name' } - ] - }; - } - case InfixRule: { - return { - name: InfixRule, - properties: [ - { name: 'call' }, - { name: 'name' }, - { name: 'operators' }, - { name: 'parameters', defaultValue: [] } - ] - }; - } - case InfixRuleOperatorList: { - return { - name: InfixRuleOperatorList, - properties: [ - { name: 'associativity' }, - { name: 'operators', defaultValue: [] } - ] - }; - } - case InfixRuleOperators: { - return { - name: InfixRuleOperators, - properties: [ - { name: 'precedences', defaultValue: [] } - ] - }; - } - case Interface: { - return { - name: Interface, - properties: [ - { name: 'attributes', defaultValue: [] }, - { name: 'name' }, - { name: 'superTypes', defaultValue: [] } - ] - }; - } - case NamedArgument: { - return { - name: NamedArgument, - properties: [ - { name: 'calledByName', defaultValue: false }, - { name: 'parameter' }, - { name: 'value' } - ] - }; - } - case Negation: { - return { - name: Negation, - properties: [ - { name: 'value' } - ] - }; - } - case NumberLiteral: { - return { - name: NumberLiteral, - properties: [ - { name: 'value' } - ] - }; - } - case Parameter: { - return { - name: Parameter, - properties: [ - { name: 'name' } - ] - }; - } - case ParameterReference: { - return { - name: ParameterReference, - properties: [ - { name: 'parameter' } - ] - }; - } - case ParserRule: { - return { - name: ParserRule, - properties: [ - { name: 'dataType' }, - { name: 'definition' }, - { name: 'entry', defaultValue: false }, - { name: 'fragment', defaultValue: false }, - { name: 'inferredType' }, - { name: 'name' }, - { name: 'parameters', defaultValue: [] }, - { name: 'returnType' } - ] - }; - } - case ReferenceType: { - return { - name: ReferenceType, - properties: [ - { name: 'referenceType' } - ] - }; - } - case ReturnType: { - return { - name: ReturnType, - properties: [ - { name: 'name' } - ] - }; - } - case SimpleType: { - return { - name: SimpleType, - properties: [ - { name: 'primitiveType' }, - { name: 'stringType' }, - { name: 'typeRef' } - ] - }; - } - case StringLiteral: { - return { - name: StringLiteral, - properties: [ - { name: 'value' } - ] - }; - } - case TerminalRule: { - return { - name: TerminalRule, - properties: [ - { name: 'definition' }, - { name: 'fragment', defaultValue: false }, - { name: 'hidden', defaultValue: false }, - { name: 'name' }, - { name: 'type' } - ] - }; - } - case Type: { - return { - name: Type, - properties: [ - { name: 'name' }, - { name: 'type' } - ] - }; - } - case TypeAttribute: { - return { - name: TypeAttribute, - properties: [ - { name: 'defaultValue' }, - { name: 'isOptional', defaultValue: false }, - { name: 'name' }, - { name: 'type' } - ] - }; - } - case UnionType: { - return { - name: UnionType, - properties: [ - { name: 'types', defaultValue: [] } - ] - }; - } - case Action: { - return { - name: Action, - properties: [ - { name: 'cardinality' }, - { name: 'feature' }, - { name: 'inferredType' }, - { name: 'lookahead' }, - { name: 'operator' }, - { name: 'type' } - ] - }; - } - case Alternatives: { - return { - name: Alternatives, - properties: [ - { name: 'cardinality' }, - { name: 'elements', defaultValue: [] }, - { name: 'lookahead' } - ] - }; - } - case Assignment: { - return { - name: Assignment, - properties: [ - { name: 'cardinality' }, - { name: 'feature' }, - { name: 'lookahead' }, - { name: 'operator' }, - { name: 'predicate' }, - { name: 'terminal' } - ] - }; - } - case CharacterRange: { - return { - name: CharacterRange, - properties: [ - { name: 'cardinality' }, - { name: 'left' }, - { name: 'lookahead' }, - { name: 'right' } - ] - }; - } - case CrossReference: { - return { - name: CrossReference, - properties: [ - { name: 'cardinality' }, - { name: 'deprecatedSyntax', defaultValue: false }, - { name: 'lookahead' }, - { name: 'terminal' }, - { name: 'type' } - ] - }; - } - case EndOfFile: { - return { - name: EndOfFile, - properties: [ - { name: 'cardinality' }, - { name: 'lookahead' } - ] - }; - } - case Group: { - return { - name: Group, - properties: [ - { name: 'cardinality' }, - { name: 'elements', defaultValue: [] }, - { name: 'guardCondition' }, - { name: 'lookahead' }, - { name: 'predicate' } - ] - }; - } - case Keyword: { - return { - name: Keyword, - properties: [ - { name: 'cardinality' }, - { name: 'lookahead' }, - { name: 'predicate' }, - { name: 'value' } - ] - }; - } - case NegatedToken: { - return { - name: NegatedToken, - properties: [ - { name: 'cardinality' }, - { name: 'lookahead' }, - { name: 'terminal' } - ] - }; - } - case RegexToken: { - return { - name: RegexToken, - properties: [ - { name: 'cardinality' }, - { name: 'lookahead' }, - { name: 'regex' } - ] - }; - } - case RuleCall: { - return { - name: RuleCall, - properties: [ - { name: 'arguments', defaultValue: [] }, - { name: 'cardinality' }, - { name: 'lookahead' }, - { name: 'predicate' }, - { name: 'rule' } - ] - }; - } - case TerminalAlternatives: { - return { - name: TerminalAlternatives, - properties: [ - { name: 'cardinality' }, - { name: 'elements', defaultValue: [] }, - { name: 'lookahead' } - ] - }; - } - case TerminalGroup: { - return { - name: TerminalGroup, - properties: [ - { name: 'cardinality' }, - { name: 'elements', defaultValue: [] }, - { name: 'lookahead' } - ] - }; - } - case TerminalRuleCall: { - return { - name: TerminalRuleCall, - properties: [ - { name: 'cardinality' }, - { name: 'lookahead' }, - { name: 'rule' } - ] - }; - } - case UnorderedGroup: { - return { - name: UnorderedGroup, - properties: [ - { name: 'cardinality' }, - { name: 'elements', defaultValue: [] }, - { name: 'lookahead' } - ] - }; - } - case UntilToken: { - return { - name: UntilToken, - properties: [ - { name: 'cardinality' }, - { name: 'lookahead' }, - { name: 'terminal' } - ] - }; - } - case Wildcard: { - return { - name: Wildcard, - properties: [ - { name: 'cardinality' }, - { name: 'lookahead' } - ] - }; - } - default: { - return { - name: type, - properties: [] - }; - } + override readonly types = { + AbstractElement: { + name: AbstractElement.$type, + properties: { + cardinality: { + name: 'cardinality' + }, + lookahead: { + name: 'lookahead' + } + }, + superTypes: [] + }, + Action: { + name: Action.$type, + properties: { + cardinality: { + name: 'cardinality' + }, + feature: { + name: 'feature' + }, + inferredType: { + name: 'inferredType' + }, + lookahead: { + name: 'lookahead' + }, + operator: { + name: 'operator' + }, + type: { + name: 'type', + referenceType: 'AbstractType' + } + }, + superTypes: ['AbstractElement'] + }, + Alternatives: { + name: Alternatives.$type, + properties: { + cardinality: { + name: 'cardinality' + }, + elements: { + name: 'elements', + defaultValue: [] + }, + lookahead: { + name: 'lookahead' + } + }, + superTypes: ['AbstractElement'] + }, + ArrayLiteral: { + name: ArrayLiteral.$type, + properties: { + elements: { + name: 'elements', + defaultValue: [] + } + }, + superTypes: ['ValueLiteral'] + }, + ArrayType: { + name: ArrayType.$type, + properties: { + elementType: { + name: 'elementType' + } + }, + superTypes: ['TypeDefinition'] + }, + Assignment: { + name: Assignment.$type, + properties: { + cardinality: { + name: 'cardinality' + }, + feature: { + name: 'feature' + }, + lookahead: { + name: 'lookahead' + }, + operator: { + name: 'operator' + }, + predicate: { + name: 'predicate' + }, + terminal: { + name: 'terminal' + } + }, + superTypes: ['AbstractElement'] + }, + BooleanLiteral: { + name: BooleanLiteral.$type, + properties: { + true: { + name: 'true', + defaultValue: false + } + }, + superTypes: ['Condition', 'ValueLiteral'] + }, + CharacterRange: { + name: CharacterRange.$type, + properties: { + cardinality: { + name: 'cardinality' + }, + left: { + name: 'left' + }, + lookahead: { + name: 'lookahead' + }, + right: { + name: 'right' + } + }, + superTypes: ['AbstractElement'] + }, + Conjunction: { + name: Conjunction.$type, + properties: { + left: { + name: 'left' + }, + right: { + name: 'right' + } + }, + superTypes: ['Condition'] + }, + CrossReference: { + name: CrossReference.$type, + properties: { + cardinality: { + name: 'cardinality' + }, + deprecatedSyntax: { + name: 'deprecatedSyntax', + defaultValue: false + }, + lookahead: { + name: 'lookahead' + }, + terminal: { + name: 'terminal' + }, + type: { + name: 'type', + referenceType: 'AbstractType' + } + }, + superTypes: ['AbstractElement'] + }, + Disjunction: { + name: Disjunction.$type, + properties: { + left: { + name: 'left' + }, + right: { + name: 'right' + } + }, + superTypes: ['Condition'] + }, + EndOfFile: { + name: EndOfFile.$type, + properties: { + cardinality: { + name: 'cardinality' + }, + lookahead: { + name: 'lookahead' + } + }, + superTypes: ['AbstractElement'] + }, + Grammar: { + name: Grammar.$type, + properties: { + imports: { + name: 'imports', + defaultValue: [] + }, + interfaces: { + name: 'interfaces', + defaultValue: [] + }, + isDeclared: { + name: 'isDeclared', + defaultValue: false + }, + name: { + name: 'name' + }, + rules: { + name: 'rules', + defaultValue: [] + }, + types: { + name: 'types', + defaultValue: [] + } + }, + superTypes: [] + }, + GrammarImport: { + name: GrammarImport.$type, + properties: { + path: { + name: 'path' + } + }, + superTypes: [] + }, + Group: { + name: Group.$type, + properties: { + cardinality: { + name: 'cardinality' + }, + elements: { + name: 'elements', + defaultValue: [] + }, + guardCondition: { + name: 'guardCondition' + }, + lookahead: { + name: 'lookahead' + }, + predicate: { + name: 'predicate' + } + }, + superTypes: ['AbstractElement'] + }, + InferredType: { + name: InferredType.$type, + properties: { + name: { + name: 'name' + } + }, + superTypes: ['AbstractType'] + }, + InfixRule: { + name: InfixRule.$type, + properties: { + call: { + name: 'call' + }, + name: { + name: 'name' + }, + operators: { + name: 'operators' + }, + parameters: { + name: 'parameters', + defaultValue: [] + } + }, + superTypes: ['AbstractRule', 'AbstractType'] + }, + InfixRuleOperatorList: { + name: InfixRuleOperatorList.$type, + properties: { + associativity: { + name: 'associativity' + }, + operators: { + name: 'operators', + defaultValue: [] + } + }, + superTypes: [] + }, + InfixRuleOperators: { + name: InfixRuleOperators.$type, + properties: { + precedences: { + name: 'precedences', + defaultValue: [] + } + }, + superTypes: [] + }, + Interface: { + name: Interface.$type, + properties: { + attributes: { + name: 'attributes', + defaultValue: [] + }, + name: { + name: 'name' + }, + superTypes: { + name: 'superTypes', + defaultValue: [], + referenceType: 'AbstractType' + } + }, + superTypes: ['AbstractType'] + }, + Keyword: { + name: Keyword.$type, + properties: { + cardinality: { + name: 'cardinality' + }, + lookahead: { + name: 'lookahead' + }, + predicate: { + name: 'predicate' + }, + value: { + name: 'value' + } + }, + superTypes: ['AbstractElement'] + }, + NamedArgument: { + name: NamedArgument.$type, + properties: { + calledByName: { + name: 'calledByName', + defaultValue: false + }, + parameter: { + name: 'parameter', + referenceType: 'Parameter' + }, + value: { + name: 'value' + } + }, + superTypes: [] + }, + NegatedToken: { + name: NegatedToken.$type, + properties: { + cardinality: { + name: 'cardinality' + }, + lookahead: { + name: 'lookahead' + }, + terminal: { + name: 'terminal' + } + }, + superTypes: ['AbstractElement'] + }, + Negation: { + name: Negation.$type, + properties: { + value: { + name: 'value' + } + }, + superTypes: ['Condition'] + }, + NumberLiteral: { + name: NumberLiteral.$type, + properties: { + value: { + name: 'value' + } + }, + superTypes: ['ValueLiteral'] + }, + Parameter: { + name: Parameter.$type, + properties: { + name: { + name: 'name' + } + }, + superTypes: [] + }, + ParameterReference: { + name: ParameterReference.$type, + properties: { + parameter: { + name: 'parameter', + referenceType: 'Parameter' + } + }, + superTypes: ['Condition'] + }, + ParserRule: { + name: ParserRule.$type, + properties: { + dataType: { + name: 'dataType' + }, + definition: { + name: 'definition' + }, + entry: { + name: 'entry', + defaultValue: false + }, + fragment: { + name: 'fragment', + defaultValue: false + }, + inferredType: { + name: 'inferredType' + }, + name: { + name: 'name' + }, + parameters: { + name: 'parameters', + defaultValue: [] + }, + returnType: { + name: 'returnType', + referenceType: 'AbstractType' + } + }, + superTypes: ['AbstractRule', 'AbstractType'] + }, + ReferenceType: { + name: ReferenceType.$type, + properties: { + referenceType: { + name: 'referenceType' + } + }, + superTypes: ['TypeDefinition'] + }, + RegexToken: { + name: RegexToken.$type, + properties: { + cardinality: { + name: 'cardinality' + }, + lookahead: { + name: 'lookahead' + }, + regex: { + name: 'regex' + } + }, + superTypes: ['AbstractElement'] + }, + ReturnType: { + name: ReturnType.$type, + properties: { + name: { + name: 'name' + } + }, + superTypes: [] + }, + RuleCall: { + name: RuleCall.$type, + properties: { + arguments: { + name: 'arguments', + defaultValue: [] + }, + cardinality: { + name: 'cardinality' + }, + lookahead: { + name: 'lookahead' + }, + predicate: { + name: 'predicate' + }, + rule: { + name: 'rule', + referenceType: 'AbstractRule' + } + }, + superTypes: ['AbstractElement'] + }, + SimpleType: { + name: SimpleType.$type, + properties: { + primitiveType: { + name: 'primitiveType' + }, + stringType: { + name: 'stringType' + }, + typeRef: { + name: 'typeRef', + referenceType: 'AbstractType' + } + }, + superTypes: ['TypeDefinition'] + }, + StringLiteral: { + name: StringLiteral.$type, + properties: { + value: { + name: 'value' + } + }, + superTypes: ['ValueLiteral'] + }, + TerminalAlternatives: { + name: TerminalAlternatives.$type, + properties: { + cardinality: { + name: 'cardinality' + }, + elements: { + name: 'elements', + defaultValue: [] + }, + lookahead: { + name: 'lookahead' + } + }, + superTypes: ['AbstractElement'] + }, + TerminalGroup: { + name: TerminalGroup.$type, + properties: { + cardinality: { + name: 'cardinality' + }, + elements: { + name: 'elements', + defaultValue: [] + }, + lookahead: { + name: 'lookahead' + } + }, + superTypes: ['AbstractElement'] + }, + TerminalRule: { + name: TerminalRule.$type, + properties: { + definition: { + name: 'definition' + }, + fragment: { + name: 'fragment', + defaultValue: false + }, + hidden: { + name: 'hidden', + defaultValue: false + }, + name: { + name: 'name' + }, + type: { + name: 'type' + } + }, + superTypes: ['AbstractRule'] + }, + TerminalRuleCall: { + name: TerminalRuleCall.$type, + properties: { + cardinality: { + name: 'cardinality' + }, + lookahead: { + name: 'lookahead' + }, + rule: { + name: 'rule', + referenceType: 'TerminalRule' + } + }, + superTypes: ['AbstractElement'] + }, + Type: { + name: Type.$type, + properties: { + name: { + name: 'name' + }, + type: { + name: 'type' + } + }, + superTypes: ['AbstractType'] + }, + TypeAttribute: { + name: TypeAttribute.$type, + properties: { + defaultValue: { + name: 'defaultValue' + }, + isOptional: { + name: 'isOptional', + defaultValue: false + }, + name: { + name: 'name' + }, + type: { + name: 'type' + } + }, + superTypes: [] + }, + UnionType: { + name: UnionType.$type, + properties: { + types: { + name: 'types', + defaultValue: [] + } + }, + superTypes: ['TypeDefinition'] + }, + UnorderedGroup: { + name: UnorderedGroup.$type, + properties: { + cardinality: { + name: 'cardinality' + }, + elements: { + name: 'elements', + defaultValue: [] + }, + lookahead: { + name: 'lookahead' + } + }, + superTypes: ['AbstractElement'] + }, + UntilToken: { + name: UntilToken.$type, + properties: { + cardinality: { + name: 'cardinality' + }, + lookahead: { + name: 'lookahead' + }, + terminal: { + name: 'terminal' + } + }, + superTypes: ['AbstractElement'] + }, + Wildcard: { + name: Wildcard.$type, + properties: { + cardinality: { + name: 'cardinality' + }, + lookahead: { + name: 'lookahead' + } + }, + superTypes: ['AbstractElement'] } - } + } as const satisfies langium.AstMetaData } export const reflection = new LangiumGrammarAstReflection(); diff --git a/packages/langium/src/syntax-tree.ts b/packages/langium/src/syntax-tree.ts index cbdc8bbbd..6b7f8a0fe 100644 --- a/packages/langium/src/syntax-tree.ts +++ b/packages/langium/src/syntax-tree.ts @@ -133,12 +133,13 @@ export function isLinkingError(obj: unknown): obj is LinkingError { * all involved languages, so it operates on the superset of types of these languages. */ export interface AstReflection { + readonly types: AstMetaData getAllTypes(): string[] - getAllSubTypes(type: string): string[] getReferenceType(refInfo: ReferenceInfo): string getTypeMetaData(type: string): TypeMetaData isInstance(node: unknown, type: string): boolean isSubtype(subtype: string, supertype: string): boolean + getAllSubTypes(type: string): string[] } /** @@ -147,13 +148,37 @@ export interface AstReflection { */ export abstract class AbstractAstReflection implements AstReflection { + readonly types: AstMetaData; protected subtypes: Record> = {}; protected allSubtypes: Record = {}; - abstract getAllTypes(): string[]; - abstract getReferenceType(refInfo: ReferenceInfo): string; - abstract getTypeMetaData(type: string): TypeMetaData; - protected abstract computeIsSubtype(subtype: string, supertype: string): boolean; + getAllTypes(): string[] { + return Object.keys(this.types); + } + + getReferenceType(refInfo: ReferenceInfo): string { + const metaData = this.types[refInfo.container.$type]; + if (!metaData) { + throw new Error(`Type ${refInfo.container.$type || 'undefined'} not found.`); + } + const referenceType = metaData.properties[refInfo.property]?.referenceType; + if (!referenceType) { + throw new Error(`Property ${refInfo.property || 'undefined'} of type ${refInfo.container.$type} is not a reference.`); + } + return referenceType; + } + + getTypeMetaData(type: string): TypeMetaData { + const result = this.types[type]; + if (!result) { + return { + name: type, + properties: {}, + superTypes: [] + }; + } + return result; + } isInstance(node: unknown, type: string): boolean { return isAstNode(node) && this.isSubtype(node.$type, type); @@ -171,7 +196,8 @@ export abstract class AbstractAstReflection implements AstReflection { if (existing !== undefined) { return existing; } else { - const result = this.computeIsSubtype(subtype, supertype); + const metaData = this.types[subtype]; + const result = metaData ? metaData.superTypes.some(s => this.isSubtype(s, supertype)) : false; nested[supertype] = result; return result; } @@ -196,24 +222,44 @@ export abstract class AbstractAstReflection implements AstReflection { } /** - * Represents runtime meta data about a meta model type. + * A map of all AST node types and their meta data. + */ +export interface AstMetaData { + [type: string]: TypeMetaData +} + +/** + * Represents runtime meta data about an AST node type. */ export interface TypeMetaData { - /** The name of this meta model type. Corresponds to the `AstNode.$type` value. */ + /** The name of this AST node type. Corresponds to the `AstNode.$type` value. */ name: string - /** A list of properties. They can contain default values for their respective property in the AST. */ - properties: TypeProperty[] + /** A list of properties with their relevant meta data. */ + properties: { + [name: string]: PropertyMetaData + } + /** The super types of this AST node type. */ + superTypes: string[] + /** All property names of this AST node type, as a shortcut `_p` corresponding to `properties[p].name`. */ + [name: `_${string}`]: string } /** * Describes the meta data of a property of an AST node. - * - * The optional `defaultValue` indicates that the property is mandatory in the AST node. - * For example, if an AST node contains an array, but no elements of this array have been parsed, we still expect an empty array instead of `undefined`. */ -export interface TypeProperty { +export interface PropertyMetaData { + /** The name of this property. */ name: string + /** + * Indicates that the property is mandatory in the AST node. + * For example, if an AST node contains an array, but no elements of this array have been parsed, + * we still expect an empty array instead of `undefined`. + */ defaultValue?: PropertyType + /** + * If the property is a reference, this is the type of the reference target. + */ + referenceType?: string } /** diff --git a/packages/langium/src/utils/ast-utils.ts b/packages/langium/src/utils/ast-utils.ts index 7a50a2d42..d6c2bdb1f 100644 --- a/packages/langium/src/utils/ast-utils.ts +++ b/packages/langium/src/utils/ast-utils.ts @@ -249,7 +249,7 @@ export function findLocalReferences(targetNode: AstNode, lookup = getDocument(ta export function assignMandatoryProperties(reflection: AstReflection, node: AstNode): void { const typeMetaData = reflection.getTypeMetaData(node.$type); const genericNode = node as GenericAstNode; - for (const property of typeMetaData.properties) { + for (const property of Object.values(typeMetaData.properties)) { // Only set the value if the property is not already set and if it has a default value if (property.defaultValue !== undefined && genericNode[property.name] === undefined) { genericNode[property.name] = copyDefaultValue(property.defaultValue); diff --git a/packages/langium/test/grammar/ast-reflection-interpreter.test.ts b/packages/langium/test/grammar/ast-reflection-interpreter.test.ts index c74944fff..11a1223b3 100644 --- a/packages/langium/test/grammar/ast-reflection-interpreter.test.ts +++ b/packages/langium/test/grammar/ast-reflection-interpreter.test.ts @@ -83,19 +83,20 @@ describe('AST reflection interpreter', () => { test('Creates metadata with super types', () => { const superMetadata = reflectionForInheritance.getTypeMetaData('Super'); - expect(superMetadata.properties).toHaveLength(2); - expect(superMetadata.properties[0].name).toBe('A'); - expect(superMetadata.properties[0].defaultValue).toBe('a'); - expect(superMetadata.properties[1].name).toBe('Ref'); - expect(superMetadata.properties[1].defaultValue).toBeUndefined(); + expect(Object.keys(superMetadata.properties)).toHaveLength(2); + expect(superMetadata.properties.A.name).toBe('A'); + expect(superMetadata.properties.A.defaultValue).toBe('a'); + expect(superMetadata.properties.Ref.name).toBe('Ref'); + expect(superMetadata.properties.Ref.defaultValue).toBeUndefined(); + const subMetadata = reflectionForInheritance.getTypeMetaData('Sub'); - expect(subMetadata.properties).toHaveLength(3); - expect(subMetadata.properties[0].name).toBe('A'); - expect(subMetadata.properties[0].defaultValue).toBe('a'); - expect(subMetadata.properties[1].name).toBe('B'); - expect(subMetadata.properties[1].defaultValue).toBe('b'); - expect(subMetadata.properties[2].name).toBe('Ref'); - expect(subMetadata.properties[2].defaultValue).toBeUndefined(); + expect(Object.keys(subMetadata.properties)).toHaveLength(3); + expect(subMetadata.properties.A.name).toBe('A'); + expect(subMetadata.properties.A.defaultValue).toBe('a'); + expect(subMetadata.properties.B.name).toBe('B'); + expect(subMetadata.properties.B.defaultValue).toBe('b'); + expect(subMetadata.properties.Ref.name).toBe('Ref'); + expect(subMetadata.properties.Ref.defaultValue).toBeUndefined(); }); }); diff --git a/packages/langium/test/parser/value-converter.test.ts b/packages/langium/test/parser/value-converter.test.ts index 6e00cb0dc..bbd7e188d 100644 --- a/packages/langium/test/parser/value-converter.test.ts +++ b/packages/langium/test/parser/value-converter.test.ts @@ -21,7 +21,7 @@ describe('DefaultValueConverter', () => { `); const terminalA = doc.parseResult.value.rules[0] as GrammarTypes.TerminalRule; expect(terminalA).toBeDefined(); - expect(terminalA.definition.$type).toBe(GrammarAST.CharacterRange); + expect(terminalA.definition.$type).toBe(GrammarAST.CharacterRange.$type); expect((terminalA.definition as GrammarAST.CharacterRange).left.value).toBe('a\'\n\t\\'); }); diff --git a/packages/langium/test/parser/worker-thread-async-parser.test.ts b/packages/langium/test/parser/worker-thread-async-parser.test.ts index 96f396832..709174d49 100644 --- a/packages/langium/test/parser/worker-thread-async-parser.test.ts +++ b/packages/langium/test/parser/worker-thread-async-parser.test.ts @@ -97,7 +97,7 @@ describe('WorkerThreadAsyncParser', () => { // Assert that the CST node has a reference to the correct AST node const astNode = node?.astNode as GrammarAST.ParserRule; expect(astNode).toBeDefined(); - expect(astNode.$type).toBe(GrammarAST.ParserRule); + expect(astNode.$type).toBe(GrammarAST.ParserRule.$type); expect(astNode.name).toBe('TestRule0'); });