Skip to content

Commit

Permalink
refactoring for LOX and OX: don't merge Langium+Typir services, but p…
Browse files Browse the repository at this point in the history
…rovide the Typir services via an "typir"-property/service within the Langium services
  • Loading branch information
JohannesMeierSE committed Dec 14, 2024
1 parent de86f99 commit 7c499a1
Show file tree
Hide file tree
Showing 11 changed files with 112 additions and 108 deletions.
41 changes: 22 additions & 19 deletions examples/lox/src/language/lox-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* terms of the MIT License, which is available in the project root.
******************************************************************************/

import { Module, PartialLangiumCoreServices, createDefaultCoreModule, inject } from 'langium';
import { LangiumSharedCoreServices, Module, PartialLangiumCoreServices, createDefaultCoreModule, inject } from 'langium';
import { DefaultSharedModuleContext, LangiumServices, LangiumSharedServices, createDefaultSharedModule } from 'langium/lsp';
import { LangiumServicesForTypirBinding, createLangiumModuleForTypirBinding, initializeLangiumTypirServices } from 'typir-langium';
import { LoxGeneratedModule, LoxGeneratedSharedModule } from './generated/module.js';
Expand All @@ -18,29 +18,37 @@ import { createLoxTypirModule } from './type-system/lox-type-checking.js';
export type LoxAddedServices = {
validation: {
LoxValidator: LoxValidator
}
},
typir: LangiumServicesForTypirBinding,
}

/**
* Union of Langium default services and your custom services - use this as constructor parameter
* of custom service classes.
*/
export type LoxServices = LangiumServices & LoxAddedServices & LangiumServicesForTypirBinding
export type LoxServices = LangiumServices & LoxAddedServices

/**
* Dependency injection module that overrides Langium default services and contributes the
* declared custom services. The Langium defaults can be partially specified to override only
* selected services, while the custom services must be fully specified.
*/
export const LoxModule: Module<LoxServices, PartialLangiumCoreServices & LoxAddedServices> = {
validation: {
ValidationRegistry: (services) => new LoxValidationRegistry(services),
LoxValidator: () => new LoxValidator()
},
references: {
ScopeProvider: (services) => new LoxScopeProvider(services)
}
};
export function createLoxModule(shared: LangiumSharedCoreServices): Module<LoxServices, PartialLangiumCoreServices & LoxAddedServices> {
return {
validation: {
ValidationRegistry: (services) => new LoxValidationRegistry(services),
LoxValidator: () => new LoxValidator()
},
// For type checking with Typir, inject and merge these modules:
typir: () => inject(Module.merge(
createLangiumModuleForTypirBinding(shared), // the Typir default services
createLoxTypirModule(shared), // custom Typir services for LOX
)),
references: {
ScopeProvider: (services) => new LoxScopeProvider(services)
},
};
}

/**
* Create the full set of services required by Langium.
Expand All @@ -53,9 +61,6 @@ export const LoxModule: Module<LoxServices, PartialLangiumCoreServices & LoxAdde
* - Langium default language-specific services
* - Services generated by langium-cli
* - Services specified in this file
* For type checking with Typir, merge two more modules:
* - Typir default services
* - custom services for LOX
*
* @param context Optional module context with the LSP connection
* @returns An object wrapping the shared services and the language-specific services
Expand All @@ -71,11 +76,9 @@ export function createLoxServices(context: DefaultSharedModuleContext): {
const Lox = inject(
createDefaultCoreModule({ shared }),
LoxGeneratedModule,
LoxModule,
createLangiumModuleForTypirBinding(shared),
createLoxTypirModule(shared),
createLoxModule(shared),
);
shared.ServiceRegistry.register(Lox);
initializeLangiumTypirServices(Lox);
initializeLangiumTypirServices(Lox, Lox.typir);
return { shared, Lox };
}
7 changes: 3 additions & 4 deletions examples/lox/src/language/type-system/lox-type-checking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
* terms of the MIT License, which is available in the project root.
******************************************************************************/

import { AstNode, AstUtils, Module, assertUnreachable } from 'langium';
import { LangiumSharedServices } from 'langium/lsp';
import { AstNode, AstUtils, LangiumSharedCoreServices, Module, assertUnreachable } from 'langium';
import { CreateFieldDetails, CreateFunctionTypeDetails, CreateParameterDetails, InferOperatorWithMultipleOperands, InferOperatorWithSingleOperand, InferenceRuleNotApplicable, NO_PARAMETER_NAME, TypirServices, UniqueClassValidation, UniqueFunctionValidation, UniqueMethodValidation, ValidationMessageDetails, createNoSuperClassCyclesValidation } from 'typir';
import { AbstractLangiumTypeCreator, LangiumServicesForTypirBinding, PartialTypirLangiumServices } from 'typir-langium';
import { BinaryExpression, FunctionDeclaration, MemberCall, MethodMember, TypeReference, UnaryExpression, isBinaryExpression, isBooleanLiteral, isClass, isFieldMember, isForStatement, isFunctionDeclaration, isIfStatement, isMemberCall, isMethodMember, isNilLiteral, isNumberLiteral, isParameter, isPrintStatement, isReturnStatement, isStringLiteral, isTypeReference, isUnaryExpression, isVariableDeclaration, isWhileStatement } from '../generated/ast.js';
Expand All @@ -14,7 +13,7 @@ import { BinaryExpression, FunctionDeclaration, MemberCall, MethodMember, TypeRe
export class LoxTypeCreator extends AbstractLangiumTypeCreator {
protected readonly typir: TypirServices;

constructor(typirServices: TypirServices, langiumServices: LangiumSharedServices) {
constructor(typirServices: TypirServices, langiumServices: LangiumSharedCoreServices) {
super(typirServices, langiumServices);
this.typir = typirServices;
}
Expand Down Expand Up @@ -259,7 +258,7 @@ function createFunctionDetails(node: FunctionDeclaration | MethodMember): Create
};
}

export function createLoxTypirModule(langiumServices: LangiumSharedServices): Module<LangiumServicesForTypirBinding, PartialTypirLangiumServices> {
export function createLoxTypirModule(langiumServices: LangiumSharedCoreServices): Module<LangiumServicesForTypirBinding, PartialTypirLangiumServices> {
return {
// specific configurations for LOX
TypeCreator: (typirServices) => new LoxTypeCreator(typirServices, langiumServices),
Expand Down
30 changes: 15 additions & 15 deletions examples/lox/test/lox-type-checking-classes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('Test type checking for classes', () => {
class MyClass2 < MyClass1 {}
var v1: MyClass1 = MyClass2();
`, 0);
expectTypirTypes(loxServices, isClassType, 'MyClass1', 'MyClass2');
expectTypirTypes(loxServices.typir, isClassType, 'MyClass1', 'MyClass2');
});

test('Class inheritance for assignments: wrong', async () => {
Expand All @@ -26,7 +26,7 @@ describe('Test type checking for classes', () => {
class MyClass2 < MyClass1 {}
var v1: MyClass2 = MyClass1();
`, 1);
expectTypirTypes(loxServices, isClassType, 'MyClass1', 'MyClass2');
expectTypirTypes(loxServices.typir, isClassType, 'MyClass1', 'MyClass2');
});

test('Class fields: correct values', async () => {
Expand All @@ -36,7 +36,7 @@ describe('Test type checking for classes', () => {
v1.name = "Bob";
v1.age = 42;
`, 0);
expectTypirTypes(loxServices, isClassType, 'MyClass1');
expectTypirTypes(loxServices.typir, isClassType, 'MyClass1');
});

test('Class fields: wrong values', async () => {
Expand All @@ -46,7 +46,7 @@ describe('Test type checking for classes', () => {
v1.name = 42;
v1.age = "Bob";
`, 2);
expectTypirTypes(loxServices, isClassType, 'MyClass1');
expectTypirTypes(loxServices.typir, isClassType, 'MyClass1');
});

test('Classes must be unique by name 2', async () => {
Expand All @@ -57,7 +57,7 @@ describe('Test type checking for classes', () => {
'Declared classes need to be unique (MyClass1).',
'Declared classes need to be unique (MyClass1).',
]);
expectTypirTypes(loxServices, isClassType, 'MyClass1');
expectTypirTypes(loxServices.typir, isClassType, 'MyClass1');
});

test('Classes must be unique by name 3', async () => {
Expand All @@ -70,7 +70,7 @@ describe('Test type checking for classes', () => {
'Declared classes need to be unique (MyClass2).',
'Declared classes need to be unique (MyClass2).',
]);
expectTypirTypes(loxServices, isClassType, 'MyClass2');
expectTypirTypes(loxServices.typir, isClassType, 'MyClass2');
});

test('Class methods: OK', async () => {
Expand All @@ -83,7 +83,7 @@ describe('Test type checking for classes', () => {
var v1: MyClass1 = MyClass1();
var v2: number = v1.method1(456);
`, []);
expectTypirTypes(loxServices, isClassType, 'MyClass1');
expectTypirTypes(loxServices.typir, isClassType, 'MyClass1');
});

test('Class methods: wrong return value', async () => {
Expand All @@ -96,7 +96,7 @@ describe('Test type checking for classes', () => {
var v1: MyClass1 = MyClass1();
var v2: number = v1.method1(456);
`, 1);
expectTypirTypes(loxServices, isClassType, 'MyClass1');
expectTypirTypes(loxServices.typir, isClassType, 'MyClass1');
});

test('Class methods: method return type does not fit to variable type', async () => {
Expand All @@ -109,7 +109,7 @@ describe('Test type checking for classes', () => {
var v1: MyClass1 = MyClass1();
var v2: boolean = v1.method1(456);
`, 1);
expectTypirTypes(loxServices, isClassType, 'MyClass1');
expectTypirTypes(loxServices.typir, isClassType, 'MyClass1');
});

test('Class methods: value for input parameter does not fit to the type of the input parameter', async () => {
Expand All @@ -122,7 +122,7 @@ describe('Test type checking for classes', () => {
var v1: MyClass1 = MyClass1();
var v2: number = v1.method1(true);
`, 1);
expectTypirTypes(loxServices, isClassType, 'MyClass1');
expectTypirTypes(loxServices.typir, isClassType, 'MyClass1');
});

test('Class methods: methods are not distinguishable', async () => {
Expand All @@ -139,7 +139,7 @@ describe('Test type checking for classes', () => {
'Declared methods need to be unique (class-MyClass1.method1(number)).',
'Declared methods need to be unique (class-MyClass1.method1(number)).',
]);
expectTypirTypes(loxServices, isClassType, 'MyClass1');
expectTypirTypes(loxServices.typir, isClassType, 'MyClass1');
});

});
Expand All @@ -151,15 +151,15 @@ describe('Class literals', () => {
class MyClass { name: string age: number }
var v1 = MyClass(); // constructor call
`, []);
expectTypirTypes(loxServices, isClassType, 'MyClass');
expectTypirTypes(loxServices.typir, isClassType, 'MyClass');
});

test('Class literals 2', async () => {
await validateLox(`
class MyClass { name: string age: number }
var v1: MyClass = MyClass(); // constructor call
`, []);
expectTypirTypes(loxServices, isClassType, 'MyClass');
expectTypirTypes(loxServices.typir, isClassType, 'MyClass');
});

test('Class literals 3', async () => {
Expand All @@ -168,7 +168,7 @@ describe('Class literals', () => {
class MyClass2 {}
var v1: boolean = MyClass1() == MyClass2(); // comparing objects with each other
`, [], 1);
expectTypirTypes(loxServices, isClassType, 'MyClass1', 'MyClass2');
expectTypirTypes(loxServices.typir, isClassType, 'MyClass1', 'MyClass2');
});

test('nil is assignable to any Class', async () => {
Expand All @@ -180,7 +180,7 @@ describe('Class literals', () => {
v1 = nil;
v2 = nil;
`, []);
expectTypirTypes(loxServices, isClassType, 'MyClass1', 'MyClass2');
expectTypirTypes(loxServices.typir, isClassType, 'MyClass1', 'MyClass2');
});

});
Loading

0 comments on commit 7c499a1

Please sign in to comment.