From b3e269b3b45a3893febaff7087ee16a2f8a71bde Mon Sep 17 00:00:00 2001 From: Johannes Meier Date: Tue, 10 Dec 2024 18:29:25 +0100 Subject: [PATCH 1/7] designed new API and simple example, joint work with Insa, still WIP --- packages/typir/src/index.ts | 1 + packages/typir/src/kinds/class/class-type.ts | 2 +- .../typir/src/kinds/function/function-kind.ts | 5 +- .../src/kinds/primitive/primitive-kind.ts | 18 +-- packages/typir/src/services/factory.ts | 78 ++++++++++ packages/typir/src/services/operator.ts | 24 +-- packages/typir/src/typir.ts | 24 ++- packages/typir/test/api-example.test.ts | 145 ++++++++++++++++++ packages/typir/test/type-definitions.test.ts | 20 +-- 9 files changed, 275 insertions(+), 42 deletions(-) create mode 100644 packages/typir/src/services/factory.ts create mode 100644 packages/typir/test/api-example.test.ts diff --git a/packages/typir/src/index.ts b/packages/typir/src/index.ts index 2534af0..9a0284d 100644 --- a/packages/typir/src/index.ts +++ b/packages/typir/src/index.ts @@ -40,6 +40,7 @@ export * from './services/equality.js'; export * from './services/inference.js'; export * from './services/kind-registry.js'; export * from './services/operator.js'; +export * from './services/factory.js'; export * from './services/printing.js'; export * from './services/subtype.js'; export * from './services/validation.js'; diff --git a/packages/typir/src/kinds/class/class-type.ts b/packages/typir/src/kinds/class/class-type.ts index 8384fde..e4a7a2c 100644 --- a/packages/typir/src/kinds/class/class-type.ts +++ b/packages/typir/src/kinds/class/class-type.ts @@ -88,7 +88,7 @@ export class ClassType extends Type { // resolve methods this.methods = typeDetails.methods.map(method => { - type: new TypeReference(kind.getMethodKind().createFunctionType(method), kind.services), + type: new TypeReference(kind.getMethodKind().create(method), kind.services), }); const refMethods = this.methods.map(m => m.type); // the uniqueness of methods can be checked with the predefined UniqueMethodValidation below diff --git a/packages/typir/src/kinds/function/function-kind.ts b/packages/typir/src/kinds/function/function-kind.ts index fd430e2..9f890da 100644 --- a/packages/typir/src/kinds/function/function-kind.ts +++ b/packages/typir/src/kinds/function/function-kind.ts @@ -18,6 +18,7 @@ import { TypeCheckStrategy, checkTypes, checkValueForConflict, createTypeCheckSt import { Kind, isKind } from '../kind.js'; import { FunctionTypeInitializer } from './function-initializer.js'; import { FunctionType, isFunctionType } from './function-type.js'; +import { FunctionPredefinedService } from '../../services/factory.js'; export interface FunctionKindOptions { @@ -112,7 +113,7 @@ export type InferFunctionCall = { * - optional parameters * - parameters which are used for output AND input */ -export class FunctionKind implements Kind, TypeGraphListener { +export class FunctionKind implements Kind, TypeGraphListener, FunctionPredefinedService { readonly $name: 'FunctionKind'; readonly services: TypirServices; readonly options: Readonly; @@ -238,7 +239,7 @@ export class FunctionKind implements Kind, TypeGraphListener { return new TypeReference(() => this.calculateIdentifier(typeDetails), this.services); } - createFunctionType(typeDetails: CreateFunctionTypeDetails): TypeInitializer { + create(typeDetails: CreateFunctionTypeDetails): TypeInitializer { return new FunctionTypeInitializer(this.services, this, typeDetails); } diff --git a/packages/typir/src/kinds/primitive/primitive-kind.ts b/packages/typir/src/kinds/primitive/primitive-kind.ts index 7009157..de9d7f0 100644 --- a/packages/typir/src/kinds/primitive/primitive-kind.ts +++ b/packages/typir/src/kinds/primitive/primitive-kind.ts @@ -4,6 +4,7 @@ * terms of the MIT License, which is available in the project root. ******************************************************************************/ +import { PrimitiveFactoryService } from '../../services/factory.js'; import { InferenceRuleNotApplicable } from '../../services/inference.js'; import { TypirServices } from '../../typir.js'; import { assertTrue, toArray } from '../../utils/utils.js'; @@ -20,7 +21,7 @@ export type InferPrimitiveType = (domainElement: unknown) => boolean; export const PrimitiveKindName = 'PrimitiveKind'; -export class PrimitiveKind implements Kind { +export class PrimitiveKind implements Kind, PrimitiveFactoryService { readonly $name: 'PrimitiveKind'; readonly services: TypirServices; @@ -30,22 +31,13 @@ export class PrimitiveKind implements Kind { this.services.kinds.register(this); } - getPrimitiveType(typeDetails: PrimitiveTypeDetails): PrimitiveType | undefined { + get(typeDetails: PrimitiveTypeDetails): PrimitiveType | undefined { const key = this.calculateIdentifier(typeDetails); return this.services.graph.getType(key) as PrimitiveType; } - getOrCreatePrimitiveType(typeDetails: PrimitiveTypeDetails): PrimitiveType { - const primitiveType = this.getPrimitiveType(typeDetails); - if (primitiveType) { - this.registerInferenceRules(typeDetails, primitiveType); - return primitiveType; - } - return this.createPrimitiveType(typeDetails); - } - - createPrimitiveType(typeDetails: PrimitiveTypeDetails): PrimitiveType { - assertTrue(this.getPrimitiveType(typeDetails) === undefined); + create(typeDetails: PrimitiveTypeDetails): PrimitiveType { + assertTrue(this.get(typeDetails) === undefined); // create the primitive type const primitiveType = new PrimitiveType(this, this.calculateIdentifier(typeDetails)); diff --git a/packages/typir/src/services/factory.ts b/packages/typir/src/services/factory.ts new file mode 100644 index 0000000..1edf55a --- /dev/null +++ b/packages/typir/src/services/factory.ts @@ -0,0 +1,78 @@ +/****************************************************************************** + * Copyright 2024 TypeFox GmbH + * This program and the accompanying materials are made available under the + * terms of the MIT License, which is available in the project root. +******************************************************************************/ + +import { TypeInitializer } from '../initialization/type-initializer.js'; +import { CreateFunctionTypeDetails, FunctionKind } from '../kinds/function/function-kind.js'; +import { FunctionType } from '../kinds/function/function-type.js'; +import { PrimitiveKind, PrimitiveTypeDetails } from '../kinds/primitive/primitive-kind.js'; +import { PrimitiveType } from '../kinds/primitive/primitive-type.js'; +import { TypirServices } from '../typir.js'; +import { OperatorManager } from './operator.js'; + +export interface FactoryService { + primitives: PrimitiveFactoryService; + functions: FunctionPredefinedService; + operators: OperatorManager; +} + +export interface PrimitiveFactoryService { + create(typeDetails: PrimitiveTypeDetails): PrimitiveType; + get(typeDetails: PrimitiveTypeDetails): PrimitiveType | undefined; + // getKind(): PrimitiveKind; // erstmal nicht rausreichen +} + +export class DefaultPrimitivePredefinedService implements PrimitiveFactoryService { + protected primitiveKind: PrimitiveKind; + + constructor(services: TypirServices) { + this.initializePrimitives(services); + } + + protected initializePrimitives(services: TypirServices): void { + this.primitiveKind = new PrimitiveKind(services); + } + + create(typeDetails: PrimitiveTypeDetails): PrimitiveType { + return this.primitiveKind.create(typeDetails); + } + + get(typeDetails: PrimitiveTypeDetails): PrimitiveType | undefined { + return this.primitiveKind.get(typeDetails); + } +} + +export interface FunctionPredefinedService { + create(typeDetails: CreateFunctionTypeDetails): TypeInitializer; +} + + +export class DefaultPredefinedService implements FactoryService { + protected readonly services: TypirServices; + + primitives: PrimitiveFactoryService; + functions: FunctionPredefinedService; + operators: OperatorManager; + + constructor(services: TypirServices) { + this.services = services; + + // primitives + this.initializePrimitives(); + + // functions + const functionKind = new FunctionKind(this.services); + this.functions = { + create: (typeDetails: CreateFunctionTypeDetails) => functionKind.create(typeDetails), + }; + + this.operators = services.operators; + } + + protected initializePrimitives(): void { + this.primitives = new DefaultPrimitivePredefinedService(this.services); + } + +} diff --git a/packages/typir/src/services/operator.ts b/packages/typir/src/services/operator.ts index 8848135..aa7daa7 100644 --- a/packages/typir/src/services/operator.ts +++ b/packages/typir/src/services/operator.ts @@ -66,12 +66,12 @@ export interface GenericOperatorDetails { // TODO rename it to "OperatorFactory", when there are no more responsibilities! export interface OperatorManager { - createUnaryOperator(typeDetails: UnaryOperatorDetails): TypeInitializers - createBinaryOperator(typeDetails: BinaryOperatorDetails): TypeInitializers - createTernaryOperator(typeDetails: TernaryOperatorDetails): TypeInitializers + createUnary(typeDetails: UnaryOperatorDetails): TypeInitializers + createBinary(typeDetails: BinaryOperatorDetails): TypeInitializers + createTernary(typeDetails: TernaryOperatorDetails): TypeInitializers /** This function allows to create a single operator with arbitrary input operands. */ - createGenericOperator(typeDetails: GenericOperatorDetails): TypeInitializer; + createGeneric(typeDetails: GenericOperatorDetails): TypeInitializer; } /** @@ -99,11 +99,11 @@ export class DefaultOperatorManager implements OperatorManager { this.services = services; } - createUnaryOperator(typeDetails: UnaryOperatorDetails): TypeInitializers { + createUnary(typeDetails: UnaryOperatorDetails): TypeInitializers { const signatures = toArray(typeDetails.signature); const result: Array> = []; for (const signature of signatures) { - result.push(this.createGenericOperator({ + result.push(this.createGeneric({ name: typeDetails.name, outputType: signature.return, inferenceRule: typeDetails.inferenceRule, // the same inference rule is used (and required) for all overloads, since multiple FunctionTypes are created! @@ -115,11 +115,11 @@ export class DefaultOperatorManager implements OperatorManager { return result.length === 1 ? result[0] : result; } - createBinaryOperator(typeDetails: BinaryOperatorDetails): TypeInitializers { + createBinary(typeDetails: BinaryOperatorDetails): TypeInitializers { const signatures = toArray(typeDetails.signature); const result: Array> = []; for (const signature of signatures) { - result.push(this.createGenericOperator({ + result.push(this.createGeneric({ name: typeDetails.name, outputType: signature.return, inferenceRule: typeDetails.inferenceRule, // the same inference rule is used (and required) for all overloads, since multiple FunctionTypes are created! @@ -132,11 +132,11 @@ export class DefaultOperatorManager implements OperatorManager { return result.length === 1 ? result[0] : result; } - createTernaryOperator(typeDetails: TernaryOperatorDetails): TypeInitializers { + createTernary(typeDetails: TernaryOperatorDetails): TypeInitializers { const signatures = toArray(typeDetails.signature); const result: Array> = []; for (const signature of signatures) { - result.push(this.createGenericOperator({ + result.push(this.createGeneric({ name: typeDetails.name, outputType: signature.return, inferenceRule: typeDetails.inferenceRule, // the same inference rule is used (and required) for all overloads, since multiple FunctionTypes are created! @@ -150,12 +150,12 @@ export class DefaultOperatorManager implements OperatorManager { return result.length === 1 ? result[0] : result; } - createGenericOperator(typeDetails: GenericOperatorDetails): TypeInitializer { + createGeneric(typeDetails: GenericOperatorDetails): TypeInitializer { // define/register the wanted operator as "special" function const functionKind = this.getFunctionKind(); // create the operator as type of kind 'function' - const newOperatorType = functionKind.createFunctionType({ + const newOperatorType = functionKind.create({ functionName: typeDetails.name, outputParameter: { name: NO_PARAMETER_NAME, type: typeDetails.outputType }, inputParameters: typeDetails.inputParameter, diff --git a/packages/typir/src/typir.ts b/packages/typir/src/typir.ts index c0fdb83..2c9c9e6 100644 --- a/packages/typir/src/typir.ts +++ b/packages/typir/src/typir.ts @@ -4,17 +4,20 @@ * terms of the MIT License, which is available in the project root. ******************************************************************************/ +import { TypeGraph } from './graph/type-graph.js'; +import { FunctionKind } from './kinds/function/function-kind.js'; +import { PrimitiveKind } from './kinds/primitive/primitive-kind.js'; import { DefaultTypeAssignability, TypeAssignability } from './services/assignability.js'; import { DefaultDomainElementInferenceCaching, DefaultTypeRelationshipCaching, DomainElementInferenceCaching, TypeRelationshipCaching } from './services/caching.js'; import { DefaultTypeConversion, TypeConversion } from './services/conversion.js'; import { DefaultTypeEquality, TypeEquality } from './services/equality.js'; +import { FunctionPredefinedService, PrimitiveFactoryService } from './services/factory.js'; import { DefaultTypeInferenceCollector, TypeInferenceCollector } from './services/inference.js'; +import { DefaultKindRegistry, KindRegistry } from './services/kind-registry.js'; import { DefaultOperatorManager, OperatorManager } from './services/operator.js'; import { DefaultTypeConflictPrinter, ProblemPrinter } from './services/printing.js'; import { DefaultSubType, SubType } from './services/subtype.js'; import { DefaultValidationCollector, DefaultValidationConstraints, ValidationCollector, ValidationConstraints } from './services/validation.js'; -import { TypeGraph } from './graph/type-graph.js'; -import { DefaultKindRegistry, KindRegistry } from './services/kind-registry.js'; import { inject, Module } from './utils/dependency-injection.js'; /** @@ -48,12 +51,18 @@ export type TypirServices = { }; readonly graph: TypeGraph; readonly kinds: KindRegistry; - readonly operators: OperatorManager; + // readonly operators: OperatorManager; readonly printer: ProblemPrinter; readonly validation: { readonly collector: ValidationCollector; readonly constraints: ValidationConstraints; }; + // readonly factory: PredefinedService; // alternative names: predefined, library, types + readonly factory: { + primitives: PrimitiveFactoryService; + functions: FunctionPredefinedService; + operators: OperatorManager; + }; }; export const DefaultTypirServiceModule: Module = { @@ -67,13 +76,20 @@ export const DefaultTypirServiceModule: Module = { typeRelationships: (services) => new DefaultTypeRelationshipCaching(services), domainElementInference: () => new DefaultDomainElementInferenceCaching() }, - operators: (services) => new DefaultOperatorManager(services), + // operators: (services) => new DefaultOperatorManager(services), kinds: () => new DefaultKindRegistry(), printer: () => new DefaultTypeConflictPrinter(), validation: { collector: (services) => new DefaultValidationCollector(services), constraints: (services) => new DefaultValidationConstraints(services), }, + // factory: (services) => new DefaultPredefinedService(services), + factory: { + // primitives: (services) => new DefaultPrimitivePredefinedService(services), + primitives: (services) => new PrimitiveKind(services), + functions: (services) => new FunctionKind(services, { subtypeParameterChecking: 'ASSIGNABLE_TYPE' }), + operators: (services) => new DefaultOperatorManager(services), + }, }; /** diff --git a/packages/typir/test/api-example.test.ts b/packages/typir/test/api-example.test.ts new file mode 100644 index 0000000..77cb723 --- /dev/null +++ b/packages/typir/test/api-example.test.ts @@ -0,0 +1,145 @@ +/****************************************************************************** + * Copyright 2024 TypeFox GmbH + * This program and the accompanying materials are made available under the + * terms of the MIT License, which is available in the project root. + ******************************************************************************/ + +import { describe, expect, test } from 'vitest'; +import { InferenceRuleNotApplicable, InferOperatorWithMultipleOperands, ValidationMessageDetails } from '../src/index.js'; +import { createTypirServices } from '../src/typir.js'; + +describe('Tests for the new API', () => { + test('Experiments', async () => { + const typir = createTypirServices(); + + const booleanType = typir.factory.primitives.create({ primitiveName: 'boolean' }); + expect(booleanType).toBeTruthy(); + const getBool = typir.factory.primitives.get({ primitiveName: 'boolean' }); + expect(getBool).toBe(booleanType); + + typir.factory.functions.create({ functionName: 'myFunction', inputParameters: [], outputParameter: undefined }); + + // operators + typir.factory.operators.createBinary({ name: '&&', signature: [{ left: booleanType, right: booleanType, return: booleanType }] }); + // typir.operators.createBinary({ name: '&&', signature: [{ left: booleanType, right: booleanType, return: booleanType }] }); // TODO entfernen! + }); + + + test('Tiny Typir', async () => { + const typir = createTypirServices(); // set-up the type system + + // primitive types + const numberType = typir.factory.primitives.create({ primitiveName: 'number', inferenceRules: node => node instanceof NumberLiteral }); + const stringType = typir.factory.primitives.create({ primitiveName: 'string', inferenceRules: node => node instanceof StringLiteral }); + + // operators + const inferenceRule: InferOperatorWithMultipleOperands = { + filter: node => node instanceof BinaryExpression, + matching: (node, operatorName) => node.operator === operatorName, + operands: node => [node.left, node.right], + }; + typir.factory.operators.createBinary({ name: '+', signature: [ + { left: numberType, right: numberType, return: numberType }, // 2 + 3 + { left: stringType, right: stringType, return: stringType }, // "2" + "3" + ], inferenceRule }); + typir.factory.operators.createBinary({ name: '-', signature: [{ left: numberType, right: numberType, return: numberType }], inferenceRule }); // 2 - 3 + + // numbers are implicitly convertable to strings + typir.conversion.markAsConvertible(numberType, stringType, 'IMPLICIT_EXPLICIT'); + + // specify, how Typir can detect the type of a variable + typir.inference.addInferenceRule(node => { + if (node instanceof Variable) { + return node.initialValue; // the type of the variable is the type of its initial value + } + return InferenceRuleNotApplicable; + }); + + // register a type-related validation + typir.validation.collector.addValidationRule(node => { + if (node instanceof Statement) { + return typir.validation.constraints.ensureNodeIsAssignable(node.right, node.left, (actual, expected) => { message: + `The type '${actual.name}' is not assignable to the type '${expected.name}'.` }); + } + return []; + }); + + // 2 + 3 => OK + const example1 = new BinaryExpression(new NumberLiteral(2), '+', new NumberLiteral(3)); + expect(typir.validation.collector.validate(example1)).toHaveLength(0); + + // 2 + "3" => OK + const example2 = new BinaryExpression(new NumberLiteral(2), '+', new StringLiteral('3')); + expect(typir.validation.collector.validate(example2)).toHaveLength(0); + + // 2 - "3" => wrong + const example3 = new BinaryExpression(new NumberLiteral(2), '-', new StringLiteral('3')); + const errors1 = typir.validation.collector.validate(example3); + const errorStack = typir.printer.printTypirProblem(errors1[0]); // the problem comes with "sub-problems" to describe the reasons in more detail + expect(errorStack).includes("The parameter 'right' at index 1 got a value with a wrong type."); + expect(errorStack).includes("For property 'right', the types 'string' and 'number' do not match."); + + // 123 is assignable to a string variable + const varString = new Variable('v1', new StringLiteral('Hello')); + const assignNumberToString = new Statement(varString, new NumberLiteral(123)); + expect(typir.validation.collector.validate(assignNumberToString)).toHaveLength(0); + + // "123" is not assignable to a number variable + const varNumber = new Variable('v2', new NumberLiteral(456)); + const assignStringToNumber = new Statement(varNumber, new StringLiteral('123')); + const errors2 = typir.validation.collector.validate(assignStringToNumber); + expect(errors2[0].message).toBe("The type 'string' is not assignable to the type 'number'."); + }); + +}); + +abstract class AstElement { + // empty +} + +class NumberLiteral extends AstElement { + value: number; + constructor(value: number) { + super(); + this.value = value; + } +} +class StringLiteral extends AstElement { + value: string; + constructor(value: string) { + super(); + this.value = value; + } +} + +class BinaryExpression extends AstElement { + left: AstElement; + operator: string; + right: AstElement; + constructor(left: AstElement, operator: string, right: AstElement) { + super(); + this.left = left; + this.operator = operator; + this.right = right; + } +} + +class Variable extends AstElement { + name: string; + initialValue: AstElement; + constructor(name: string, initialValue: AstElement) { + super(); + this.name = name; + this.initialValue = initialValue; + } +} + +class Statement extends AstElement { + left: Variable; + right: AstElement; + constructor(left: Variable, right: AstElement) { + super(); + this.left = left; + this.right = right; + } +} diff --git a/packages/typir/test/type-definitions.test.ts b/packages/typir/test/type-definitions.test.ts index b746c29..7c2760e 100644 --- a/packages/typir/test/type-definitions.test.ts +++ b/packages/typir/test/type-definitions.test.ts @@ -27,10 +27,10 @@ describe('Tests for Typir', () => { const functionKind = new FunctionKind(typir); // create some primitive types - const typeInt = primitiveKind.createPrimitiveType({ primitiveName: 'Integer' }); - const typeString = primitiveKind.createPrimitiveType({ primitiveName: 'String', + const typeInt = primitiveKind.create({ primitiveName: 'Integer' }); + const typeString = primitiveKind.create({ primitiveName: 'String', inferenceRules: domainElement => typeof domainElement === 'string'}); // combine type definition with a dedicated inference rule for it - const typeBoolean = primitiveKind.createPrimitiveType({ primitiveName: 'Boolean' }); + const typeBoolean = primitiveKind.create({ primitiveName: 'Boolean' }); // create class type Person with 1 firstName and 1..2 lastNames and an age properties const typeOneOrTwoStrings = multiplicityKind.createMultiplicityType({ constrainedType: typeString, lowerBound: 1, upperBound: 2 }); @@ -57,25 +57,25 @@ describe('Tests for Typir', () => { const typeListInt = listKind.createFixedParameterType({ parameterTypes: typeInt }); const typeListString = listKind.createFixedParameterType({ parameterTypes: typeString }); // const typeMapStringPerson = mapKind.createFixedParameterType({ parameterTypes: [typeString, typePerson] }); - const typeFunctionStringLength = functionKind.createFunctionType({ + const typeFunctionStringLength = functionKind.create({ functionName: 'length', outputParameter: { name: NO_PARAMETER_NAME, type: typeInt }, inputParameters: [{ name: 'value', type: typeString }] }); // binary operators on Integers - const opAdd = typir.operators.createBinaryOperator({ name: '+', signature: { left: typeInt, right: typeInt, return: typeInt } }); - const opMinus = typir.operators.createBinaryOperator({ name: '-', signature: { left: typeInt, right: typeInt, return: typeInt } }); - const opLess = typir.operators.createBinaryOperator({ name: '<', signature: { left: typeInt, right: typeInt, return: typeBoolean } }); - const opEqualInt = typir.operators.createBinaryOperator({ name: '==', signature: { left: typeInt, right: typeInt, return: typeBoolean }, + const opAdd = typir.operators.createBinary({ name: '+', signature: { left: typeInt, right: typeInt, return: typeInt } }); + const opMinus = typir.operators.createBinary({ name: '-', signature: { left: typeInt, right: typeInt, return: typeInt } }); + const opLess = typir.operators.createBinary({ name: '<', signature: { left: typeInt, right: typeInt, return: typeBoolean } }); + const opEqualInt = typir.operators.createBinary({ name: '==', signature: { left: typeInt, right: typeInt, return: typeBoolean }, inferenceRule: { filter: (domainElement): domainElement is string => typeof domainElement === 'string', matching: domainElement => domainElement.includes('=='), operands: domainElement => [] }}); // binary operators on Booleans - const opEqualBool = typir.operators.createBinaryOperator({ name: '==', signature: { left: typeBoolean, right: typeBoolean, return: typeBoolean } }); - const opAnd = typir.operators.createBinaryOperator({ name: '&&', signature: { left: typeBoolean, right: typeBoolean, return: typeBoolean } }); + const opEqualBool = typir.operators.createBinary({ name: '==', signature: { left: typeBoolean, right: typeBoolean, return: typeBoolean } }); + const opAnd = typir.operators.createBinary({ name: '&&', signature: { left: typeBoolean, right: typeBoolean, return: typeBoolean } }); // unary operators const opNotBool = typir.operators.createUnaryOperator({ name: '!', signature: { operand: typeBoolean, return: typeBoolean }, inferenceRule: { From db3192cf426ee51534511c786915d2a4d46db634 Mon Sep 17 00:00:00 2001 From: Johannes Meier Date: Tue, 10 Dec 2024 19:27:49 +0100 Subject: [PATCH 2/7] implemented new API for most of the other predefined types, removed getOrCreate functions (since they are not supported/needed), moved new interfaces into existing kind files --- packages/typir/src/index.ts | 1 - .../typir/src/kinds/bottom/bottom-kind.ts | 22 +++--- packages/typir/src/kinds/class/class-kind.ts | 11 ++- .../typir/src/kinds/function/function-kind.ts | 12 ++- .../src/kinds/primitive/primitive-kind.ts | 6 +- packages/typir/src/kinds/top/top-kind.ts | 22 +++--- packages/typir/src/services/factory.ts | 78 ------------------- packages/typir/src/typir.ts | 27 ++++--- packages/typir/test/type-definitions.test.ts | 20 ++--- 9 files changed, 64 insertions(+), 135 deletions(-) delete mode 100644 packages/typir/src/services/factory.ts diff --git a/packages/typir/src/index.ts b/packages/typir/src/index.ts index 9a0284d..2534af0 100644 --- a/packages/typir/src/index.ts +++ b/packages/typir/src/index.ts @@ -40,7 +40,6 @@ export * from './services/equality.js'; export * from './services/inference.js'; export * from './services/kind-registry.js'; export * from './services/operator.js'; -export * from './services/factory.js'; export * from './services/printing.js'; export * from './services/subtype.js'; export * from './services/validation.js'; diff --git a/packages/typir/src/kinds/bottom/bottom-kind.ts b/packages/typir/src/kinds/bottom/bottom-kind.ts index eb59b4c..97366cb 100644 --- a/packages/typir/src/kinds/bottom/bottom-kind.ts +++ b/packages/typir/src/kinds/bottom/bottom-kind.ts @@ -23,7 +23,12 @@ export type InferBottomType = (domainElement: unknown) => boolean; export const BottomKindName = 'BottomKind'; -export class BottomKind implements Kind { +export interface BottomFactoryService { + create(typeDetails: BottomTypeDetails): BottomType; + get(typeDetails: BottomTypeDetails): BottomType | undefined; +} + +export class BottomKind implements Kind, BottomFactoryService { readonly $name: 'BottomKind'; readonly services: TypirServices; readonly options: Readonly; @@ -41,22 +46,13 @@ export class BottomKind implements Kind { }; } - getBottomType(typeDetails: BottomTypeDetails): BottomType | undefined { + get(typeDetails: BottomTypeDetails): BottomType | undefined { const key = this.calculateIdentifier(typeDetails); return this.services.graph.getType(key) as BottomType; } - getOrCreateBottomType(typeDetails: BottomTypeDetails): BottomType { - const bottomType = this.getBottomType(typeDetails); - if (bottomType) { - this.registerInferenceRules(typeDetails, bottomType); - return bottomType; - } - return this.createBottomType(typeDetails); - } - - createBottomType(typeDetails: BottomTypeDetails): BottomType { - assertTrue(this.getBottomType(typeDetails) === undefined); + create(typeDetails: BottomTypeDetails): BottomType { + assertTrue(this.get(typeDetails) === undefined); // create the bottom type (singleton) if (this.instance) { // note, that the given inference rules are ignored in this case! diff --git a/packages/typir/src/kinds/class/class-kind.ts b/packages/typir/src/kinds/class/class-kind.ts index 7ca61f6..6084aba 100644 --- a/packages/typir/src/kinds/class/class-kind.ts +++ b/packages/typir/src/kinds/class/class-kind.ts @@ -58,6 +58,11 @@ export type InferClassLiteral = { }; +export interface ClassFactoryService { + create(typeDetails: CreateClassTypeDetails): TypeInitializer; + get(typeDetails: ClassTypeDetails | string): TypeReference; +} + /** * Classes have a name and have an arbitrary number of fields, consisting of a name and a type, and an arbitrary number of super-classes. * Fields have exactly one type and no multiplicity (which can be realized with a type of kind 'MultiplicityKind'). @@ -65,7 +70,7 @@ export type InferClassLiteral = { * The field name is used to identify fields of classes. * The order of fields is not defined, i.e. there is no order of fields. */ -export class ClassKind implements Kind { +export class ClassKind implements Kind, ClassFactoryService { readonly $name: 'ClassKind'; readonly services: TypirServices; readonly options: Readonly; @@ -91,7 +96,7 @@ export class ClassKind implements Kind { * @param typeDetails all information needed to identify the class * @returns a reference to the class type, which might be resolved in the future, if the class type does not yet exist */ - getClassType(typeDetails: ClassTypeDetails | string): TypeReference { // string for nominal typing + get(typeDetails: ClassTypeDetails | string): TypeReference { // string for nominal typing if (typeof typeDetails === 'string') { // nominal typing return new TypeReference(typeDetails, this.services); @@ -108,7 +113,7 @@ export class ClassKind implements Kind { * @param typeDetails all information needed to create a new class * @returns an initializer which creates and returns the new class type, when all depending types are resolved */ - createClassType(typeDetails: CreateClassTypeDetails): TypeInitializer { + create(typeDetails: CreateClassTypeDetails): TypeInitializer { return new ClassTypeInitializer(this.services, this, typeDetails); } diff --git a/packages/typir/src/kinds/function/function-kind.ts b/packages/typir/src/kinds/function/function-kind.ts index 9f890da..5161d65 100644 --- a/packages/typir/src/kinds/function/function-kind.ts +++ b/packages/typir/src/kinds/function/function-kind.ts @@ -4,21 +4,20 @@ * terms of the MIT License, which is available in the project root. ******************************************************************************/ -import { CompositeTypeInferenceRule } from '../../services/inference.js'; -import { ValidationProblem } from '../../services/validation.js'; import { TypeEdge } from '../../graph/type-edge.js'; import { TypeGraphListener } from '../../graph/type-graph.js'; import { Type } from '../../graph/type-node.js'; import { TypeInitializer } from '../../initialization/type-initializer.js'; import { TypeReference, resolveTypeSelector } from '../../initialization/type-reference.js'; import { TypeSelector } from '../../initialization/type-selector.js'; +import { CompositeTypeInferenceRule } from '../../services/inference.js'; +import { ValidationProblem } from '../../services/validation.js'; import { TypirServices } from '../../typir.js'; import { NameTypePair } from '../../utils/utils-definitions.js'; import { TypeCheckStrategy, checkTypes, checkValueForConflict, createTypeCheckStrategy } from '../../utils/utils-type-comparison.js'; import { Kind, isKind } from '../kind.js'; import { FunctionTypeInitializer } from './function-initializer.js'; import { FunctionType, isFunctionType } from './function-type.js'; -import { FunctionPredefinedService } from '../../services/factory.js'; export interface FunctionKindOptions { @@ -97,6 +96,11 @@ export type InferFunctionCall = { */ +export interface FunctionPredefinedService { + create(typeDetails: CreateFunctionTypeDetails): TypeInitializer; + get(typeDetails: FunctionTypeDetails): TypeReference; +} + /** * Represents signatures of executable code. * @@ -235,7 +239,7 @@ export class FunctionKind implements Kind, TypeGraphListener, FunctionPredefined ); } - getFunctionType(typeDetails: FunctionTypeDetails): TypeReference { + get(typeDetails: FunctionTypeDetails): TypeReference { return new TypeReference(() => this.calculateIdentifier(typeDetails), this.services); } diff --git a/packages/typir/src/kinds/primitive/primitive-kind.ts b/packages/typir/src/kinds/primitive/primitive-kind.ts index de9d7f0..74f73f1 100644 --- a/packages/typir/src/kinds/primitive/primitive-kind.ts +++ b/packages/typir/src/kinds/primitive/primitive-kind.ts @@ -4,7 +4,6 @@ * terms of the MIT License, which is available in the project root. ******************************************************************************/ -import { PrimitiveFactoryService } from '../../services/factory.js'; import { InferenceRuleNotApplicable } from '../../services/inference.js'; import { TypirServices } from '../../typir.js'; import { assertTrue, toArray } from '../../utils/utils.js'; @@ -21,6 +20,11 @@ export type InferPrimitiveType = (domainElement: unknown) => boolean; export const PrimitiveKindName = 'PrimitiveKind'; +export interface PrimitiveFactoryService { + create(typeDetails: PrimitiveTypeDetails): PrimitiveType; + get(typeDetails: PrimitiveTypeDetails): PrimitiveType | undefined; +} + export class PrimitiveKind implements Kind, PrimitiveFactoryService { readonly $name: 'PrimitiveKind'; readonly services: TypirServices; diff --git a/packages/typir/src/kinds/top/top-kind.ts b/packages/typir/src/kinds/top/top-kind.ts index b5660d6..f9a387b 100644 --- a/packages/typir/src/kinds/top/top-kind.ts +++ b/packages/typir/src/kinds/top/top-kind.ts @@ -23,7 +23,12 @@ export type InferTopType = (domainElement: unknown) => boolean; export const TopKindName = 'TopKind'; -export class TopKind implements Kind { +export interface TopFactoryService { + create(typeDetails: TopTypeDetails): TopType; + get(typeDetails: TopTypeDetails): TopType | undefined; +} + +export class TopKind implements Kind, TopFactoryService { readonly $name: 'TopKind'; readonly services: TypirServices; readonly options: Readonly; @@ -41,22 +46,13 @@ export class TopKind implements Kind { }; } - getTopType(typeDetails: TopTypeDetails): TopType | undefined { + get(typeDetails: TopTypeDetails): TopType | undefined { const key = this.calculateIdentifier(typeDetails); return this.services.graph.getType(key) as TopType; } - getOrCreateTopType(typeDetails: TopTypeDetails): TopType { - const topType = this.getTopType(typeDetails); - if (topType) { - this.registerInferenceRules(typeDetails, topType); - return topType; - } - return this.createTopType(typeDetails); - } - - createTopType(typeDetails: TopTypeDetails): TopType { - assertTrue(this.getTopType(typeDetails) === undefined); + create(typeDetails: TopTypeDetails): TopType { + assertTrue(this.get(typeDetails) === undefined); // create the top type (singleton) if (this.instance) { diff --git a/packages/typir/src/services/factory.ts b/packages/typir/src/services/factory.ts deleted file mode 100644 index 1edf55a..0000000 --- a/packages/typir/src/services/factory.ts +++ /dev/null @@ -1,78 +0,0 @@ -/****************************************************************************** - * Copyright 2024 TypeFox GmbH - * This program and the accompanying materials are made available under the - * terms of the MIT License, which is available in the project root. -******************************************************************************/ - -import { TypeInitializer } from '../initialization/type-initializer.js'; -import { CreateFunctionTypeDetails, FunctionKind } from '../kinds/function/function-kind.js'; -import { FunctionType } from '../kinds/function/function-type.js'; -import { PrimitiveKind, PrimitiveTypeDetails } from '../kinds/primitive/primitive-kind.js'; -import { PrimitiveType } from '../kinds/primitive/primitive-type.js'; -import { TypirServices } from '../typir.js'; -import { OperatorManager } from './operator.js'; - -export interface FactoryService { - primitives: PrimitiveFactoryService; - functions: FunctionPredefinedService; - operators: OperatorManager; -} - -export interface PrimitiveFactoryService { - create(typeDetails: PrimitiveTypeDetails): PrimitiveType; - get(typeDetails: PrimitiveTypeDetails): PrimitiveType | undefined; - // getKind(): PrimitiveKind; // erstmal nicht rausreichen -} - -export class DefaultPrimitivePredefinedService implements PrimitiveFactoryService { - protected primitiveKind: PrimitiveKind; - - constructor(services: TypirServices) { - this.initializePrimitives(services); - } - - protected initializePrimitives(services: TypirServices): void { - this.primitiveKind = new PrimitiveKind(services); - } - - create(typeDetails: PrimitiveTypeDetails): PrimitiveType { - return this.primitiveKind.create(typeDetails); - } - - get(typeDetails: PrimitiveTypeDetails): PrimitiveType | undefined { - return this.primitiveKind.get(typeDetails); - } -} - -export interface FunctionPredefinedService { - create(typeDetails: CreateFunctionTypeDetails): TypeInitializer; -} - - -export class DefaultPredefinedService implements FactoryService { - protected readonly services: TypirServices; - - primitives: PrimitiveFactoryService; - functions: FunctionPredefinedService; - operators: OperatorManager; - - constructor(services: TypirServices) { - this.services = services; - - // primitives - this.initializePrimitives(); - - // functions - const functionKind = new FunctionKind(this.services); - this.functions = { - create: (typeDetails: CreateFunctionTypeDetails) => functionKind.create(typeDetails), - }; - - this.operators = services.operators; - } - - protected initializePrimitives(): void { - this.primitives = new DefaultPrimitivePredefinedService(this.services); - } - -} diff --git a/packages/typir/src/typir.ts b/packages/typir/src/typir.ts index 2c9c9e6..55f6287 100644 --- a/packages/typir/src/typir.ts +++ b/packages/typir/src/typir.ts @@ -5,13 +5,15 @@ ******************************************************************************/ import { TypeGraph } from './graph/type-graph.js'; -import { FunctionKind } from './kinds/function/function-kind.js'; -import { PrimitiveKind } from './kinds/primitive/primitive-kind.js'; +import { BottomFactoryService, BottomKind } from './kinds/bottom/bottom-kind.js'; +import { ClassFactoryService, ClassKind } from './kinds/class/class-kind.js'; +import { FunctionKind, FunctionPredefinedService } from './kinds/function/function-kind.js'; +import { PrimitiveFactoryService, PrimitiveKind } from './kinds/primitive/primitive-kind.js'; +import { TopFactoryService, TopKind } from './kinds/top/top-kind.js'; import { DefaultTypeAssignability, TypeAssignability } from './services/assignability.js'; import { DefaultDomainElementInferenceCaching, DefaultTypeRelationshipCaching, DomainElementInferenceCaching, TypeRelationshipCaching } from './services/caching.js'; import { DefaultTypeConversion, TypeConversion } from './services/conversion.js'; import { DefaultTypeEquality, TypeEquality } from './services/equality.js'; -import { FunctionPredefinedService, PrimitiveFactoryService } from './services/factory.js'; import { DefaultTypeInferenceCollector, TypeInferenceCollector } from './services/inference.js'; import { DefaultKindRegistry, KindRegistry } from './services/kind-registry.js'; import { DefaultOperatorManager, OperatorManager } from './services/operator.js'; @@ -51,17 +53,18 @@ export type TypirServices = { }; readonly graph: TypeGraph; readonly kinds: KindRegistry; - // readonly operators: OperatorManager; readonly printer: ProblemPrinter; readonly validation: { readonly collector: ValidationCollector; readonly constraints: ValidationConstraints; }; - // readonly factory: PredefinedService; // alternative names: predefined, library, types readonly factory: { - primitives: PrimitiveFactoryService; - functions: FunctionPredefinedService; - operators: OperatorManager; + readonly primitives: PrimitiveFactoryService; + readonly functions: FunctionPredefinedService; + readonly classes: ClassFactoryService; + readonly top: TopFactoryService; + readonly bottom: BottomFactoryService; + readonly operators: OperatorManager; }; }; @@ -76,18 +79,18 @@ export const DefaultTypirServiceModule: Module = { typeRelationships: (services) => new DefaultTypeRelationshipCaching(services), domainElementInference: () => new DefaultDomainElementInferenceCaching() }, - // operators: (services) => new DefaultOperatorManager(services), kinds: () => new DefaultKindRegistry(), printer: () => new DefaultTypeConflictPrinter(), validation: { collector: (services) => new DefaultValidationCollector(services), constraints: (services) => new DefaultValidationConstraints(services), }, - // factory: (services) => new DefaultPredefinedService(services), factory: { - // primitives: (services) => new DefaultPrimitivePredefinedService(services), primitives: (services) => new PrimitiveKind(services), - functions: (services) => new FunctionKind(services, { subtypeParameterChecking: 'ASSIGNABLE_TYPE' }), + functions: (services) => new FunctionKind(services), + classes: (services) => new ClassKind(services), + top: (services) => new TopKind(services), + bottom: (services) => new BottomKind(services), operators: (services) => new DefaultOperatorManager(services), }, }; diff --git a/packages/typir/test/type-definitions.test.ts b/packages/typir/test/type-definitions.test.ts index 7c2760e..7b32940 100644 --- a/packages/typir/test/type-definitions.test.ts +++ b/packages/typir/test/type-definitions.test.ts @@ -34,7 +34,7 @@ describe('Tests for Typir', () => { // create class type Person with 1 firstName and 1..2 lastNames and an age properties const typeOneOrTwoStrings = multiplicityKind.createMultiplicityType({ constrainedType: typeString, lowerBound: 1, upperBound: 2 }); - const typePerson = classKind.createClassType({ + const typePerson = classKind.create({ className: 'Person', fields: [ { name: 'firstName', type: typeString }, @@ -44,7 +44,7 @@ describe('Tests for Typir', () => { methods: [], }); console.log(typePerson.getTypeFinal()!.getUserRepresentation()); - const typeStudent = classKind.createClassType({ + const typeStudent = classKind.create({ className: 'Student', superClasses: typePerson, // a Student is a special Person fields: [ @@ -64,27 +64,27 @@ describe('Tests for Typir', () => { }); // binary operators on Integers - const opAdd = typir.operators.createBinary({ name: '+', signature: { left: typeInt, right: typeInt, return: typeInt } }); - const opMinus = typir.operators.createBinary({ name: '-', signature: { left: typeInt, right: typeInt, return: typeInt } }); - const opLess = typir.operators.createBinary({ name: '<', signature: { left: typeInt, right: typeInt, return: typeBoolean } }); - const opEqualInt = typir.operators.createBinary({ name: '==', signature: { left: typeInt, right: typeInt, return: typeBoolean }, + const opAdd = typir.factory.operators.createBinary({ name: '+', signature: { left: typeInt, right: typeInt, return: typeInt } }); + const opMinus = typir.factory.operators.createBinary({ name: '-', signature: { left: typeInt, right: typeInt, return: typeInt } }); + const opLess = typir.factory.operators.createBinary({ name: '<', signature: { left: typeInt, right: typeInt, return: typeBoolean } }); + const opEqualInt = typir.factory.operators.createBinary({ name: '==', signature: { left: typeInt, right: typeInt, return: typeBoolean }, inferenceRule: { filter: (domainElement): domainElement is string => typeof domainElement === 'string', matching: domainElement => domainElement.includes('=='), operands: domainElement => [] }}); // binary operators on Booleans - const opEqualBool = typir.operators.createBinary({ name: '==', signature: { left: typeBoolean, right: typeBoolean, return: typeBoolean } }); - const opAnd = typir.operators.createBinary({ name: '&&', signature: { left: typeBoolean, right: typeBoolean, return: typeBoolean } }); + const opEqualBool = typir.factory.operators.createBinary({ name: '==', signature: { left: typeBoolean, right: typeBoolean, return: typeBoolean } }); + const opAnd = typir.factory.operators.createBinary({ name: '&&', signature: { left: typeBoolean, right: typeBoolean, return: typeBoolean } }); // unary operators - const opNotBool = typir.operators.createUnaryOperator({ name: '!', signature: { operand: typeBoolean, return: typeBoolean }, + const opNotBool = typir.factory.operators.createUnary({ name: '!', signature: { operand: typeBoolean, return: typeBoolean }, inferenceRule: { filter: (domainElement): domainElement is string => typeof domainElement === 'string', matching: domainElement => domainElement.includes('NOT'), operand: domainElement => [] }}); // ternary operator - const opTernaryIf = typir.operators.createTernaryOperator({ name: 'if', signature: { first: typeBoolean, second: typeInt, third: typeInt, return: typeInt } }); + const opTernaryIf = typir.factory.operators.createTernary({ name: 'if', signature: { first: typeBoolean, second: typeInt, third: typeInt, return: typeInt } }); // automated conversion from int to string // it is possible to define multiple sources and/or targets at the same time: From 0ae76c04a92f8fd692991b55e597e4d813d149d4 Mon Sep 17 00:00:00 2001 From: Johannes Meier Date: Tue, 10 Dec 2024 19:53:52 +0100 Subject: [PATCH 3/7] fixed bugs how to use kinds by other kinds --- packages/typir/src/kinds/class/class-kind.ts | 16 +++++++--------- packages/typir/src/kinds/class/class-type.ts | 2 +- .../typir/src/kinds/function/function-kind.ts | 1 + packages/typir/src/services/operator.ts | 12 +++++------- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/packages/typir/src/kinds/class/class-kind.ts b/packages/typir/src/kinds/class/class-kind.ts index 6084aba..fd43ec9 100644 --- a/packages/typir/src/kinds/class/class-kind.ts +++ b/packages/typir/src/kinds/class/class-kind.ts @@ -5,19 +5,19 @@ ******************************************************************************/ import { assertUnreachable } from 'langium'; -import { InferenceRuleNotApplicable } from '../../services/inference.js'; import { TypeInitializer } from '../../initialization/type-initializer.js'; import { TypeReference, resolveTypeSelector } from '../../initialization/type-reference.js'; import { TypeSelector } from '../../initialization/type-selector.js'; +import { InferenceRuleNotApplicable } from '../../services/inference.js'; import { TypirServices } from '../../typir.js'; import { TypeCheckStrategy } from '../../utils/utils-type-comparison.js'; import { assertTrue, assertType, toArray } from '../../utils/utils.js'; -import { CreateFunctionTypeDetails, FunctionKind, FunctionKindName, isFunctionKind } from '../function/function-kind.js'; +import { CreateFunctionTypeDetails, FunctionPredefinedService } from '../function/function-kind.js'; import { Kind, isKind } from '../kind.js'; +import { ClassTypeInitializer } from './class-initializer.js'; +import { ClassType, isClassType } from './class-type.js'; import { TopClassKind, TopClassKindName, TopClassTypeDetails, isTopClassKind } from './top-class-kind.js'; import { TopClassType } from './top-class-type.js'; -import { ClassType, isClassType } from './class-type.js'; -import { ClassTypeInitializer } from './class-initializer.js'; export interface ClassKindOptions { typing: 'Structural' | 'Nominal', // JS classes are nominal, TS structures are structural @@ -143,7 +143,7 @@ export class ClassKind implements Kind, ClassFactoryService { .sort() // the order of fields does not matter, therefore we need a stable order to make the identifiers comparable .join(','); // methods - const functionKind = this.getMethodKind(); + const functionKind = this.getMethodFactory(); const methods: string = typeDetails.methods .map(createMethodDetails => { return functionKind.calculateIdentifier(createMethodDetails); // reuse the Identifier for Functions here! @@ -180,10 +180,8 @@ export class ClassKind implements Kind, ClassFactoryService { return `${this.getIdentifierPrefix()}${typeDetails.className}`; } - getMethodKind(): FunctionKind { - // ensure, that Typir uses the predefined 'function' kind for methods - const kind = this.services.kinds.get(FunctionKindName); - return isFunctionKind(kind) ? kind : new FunctionKind(this.services); + getMethodFactory(): FunctionPredefinedService { + return this.services.factory.functions; } getOrCreateTopClassType(typeDetails: TopClassTypeDetails): TopClassType { diff --git a/packages/typir/src/kinds/class/class-type.ts b/packages/typir/src/kinds/class/class-type.ts index e4a7a2c..cc98223 100644 --- a/packages/typir/src/kinds/class/class-type.ts +++ b/packages/typir/src/kinds/class/class-type.ts @@ -88,7 +88,7 @@ export class ClassType extends Type { // resolve methods this.methods = typeDetails.methods.map(method => { - type: new TypeReference(kind.getMethodKind().create(method), kind.services), + type: new TypeReference(kind.getMethodFactory().create(method), kind.services), }); const refMethods = this.methods.map(m => m.type); // the uniqueness of methods can be checked with the predefined UniqueMethodValidation below diff --git a/packages/typir/src/kinds/function/function-kind.ts b/packages/typir/src/kinds/function/function-kind.ts index 5161d65..f668d7a 100644 --- a/packages/typir/src/kinds/function/function-kind.ts +++ b/packages/typir/src/kinds/function/function-kind.ts @@ -99,6 +99,7 @@ export type InferFunctionCall = { export interface FunctionPredefinedService { create(typeDetails: CreateFunctionTypeDetails): TypeInitializer; get(typeDetails: FunctionTypeDetails): TypeReference; + calculateIdentifier(typeDetails: FunctionTypeDetails): string; } /** diff --git a/packages/typir/src/services/operator.ts b/packages/typir/src/services/operator.ts index aa7daa7..10beefa 100644 --- a/packages/typir/src/services/operator.ts +++ b/packages/typir/src/services/operator.ts @@ -5,9 +5,9 @@ ******************************************************************************/ import { Type } from '../graph/type-node.js'; -import { FunctionKind, FunctionKindName, isFunctionKind, NO_PARAMETER_NAME } from '../kinds/function/function-kind.js'; -import { TypirServices } from '../typir.js'; import { TypeInitializer } from '../initialization/type-initializer.js'; +import { FunctionPredefinedService, NO_PARAMETER_NAME } from '../kinds/function/function-kind.js'; +import { TypirServices } from '../typir.js'; import { NameTypePair, TypeInitializers } from '../utils/utils-definitions.js'; import { toArray } from '../utils/utils.js'; @@ -152,7 +152,7 @@ export class DefaultOperatorManager implements OperatorManager { createGeneric(typeDetails: GenericOperatorDetails): TypeInitializer { // define/register the wanted operator as "special" function - const functionKind = this.getFunctionKind(); + const functionKind = this.getFunctionFactory(); // create the operator as type of kind 'function' const newOperatorType = functionKind.create({ @@ -174,9 +174,7 @@ export class DefaultOperatorManager implements OperatorManager { return newOperatorType as unknown as TypeInitializer; } - protected getFunctionKind(): FunctionKind { - // ensure, that Typir uses the predefined 'function' kind - const kind = this.services.kinds.get(FunctionKindName); - return isFunctionKind(kind) ? kind : new FunctionKind(this.services); + protected getFunctionFactory(): FunctionPredefinedService { + return this.services.factory.functions; } } From 350c547f22688820bfe013c0afb5764acd60e1fd Mon Sep 17 00:00:00 2001 From: Johannes Meier Date: Wed, 11 Dec 2024 17:41:30 +0100 Subject: [PATCH 4/7] renaming in the example --- packages/typir/test/api-example.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/typir/test/api-example.test.ts b/packages/typir/test/api-example.test.ts index 77cb723..9b2548a 100644 --- a/packages/typir/test/api-example.test.ts +++ b/packages/typir/test/api-example.test.ts @@ -38,7 +38,7 @@ describe('Tests for the new API', () => { matching: (node, operatorName) => node.operator === operatorName, operands: node => [node.left, node.right], }; - typir.factory.operators.createBinary({ name: '+', signature: [ + typir.factory.operators.createBinary({ name: '+', signature: [ // operator overloading { left: numberType, right: numberType, return: numberType }, // 2 + 3 { left: stringType, right: stringType, return: stringType }, // "2" + "3" ], inferenceRule }); @@ -57,7 +57,7 @@ describe('Tests for the new API', () => { // register a type-related validation typir.validation.collector.addValidationRule(node => { - if (node instanceof Statement) { + if (node instanceof AssignmentStatement) { return typir.validation.constraints.ensureNodeIsAssignable(node.right, node.left, (actual, expected) => { message: `The type '${actual.name}' is not assignable to the type '${expected.name}'.` }); } @@ -81,12 +81,12 @@ describe('Tests for the new API', () => { // 123 is assignable to a string variable const varString = new Variable('v1', new StringLiteral('Hello')); - const assignNumberToString = new Statement(varString, new NumberLiteral(123)); + const assignNumberToString = new AssignmentStatement(varString, new NumberLiteral(123)); expect(typir.validation.collector.validate(assignNumberToString)).toHaveLength(0); // "123" is not assignable to a number variable const varNumber = new Variable('v2', new NumberLiteral(456)); - const assignStringToNumber = new Statement(varNumber, new StringLiteral('123')); + const assignStringToNumber = new AssignmentStatement(varNumber, new StringLiteral('123')); const errors2 = typir.validation.collector.validate(assignStringToNumber); expect(errors2[0].message).toBe("The type 'string' is not assignable to the type 'number'."); }); @@ -134,7 +134,7 @@ class Variable extends AstElement { } } -class Statement extends AstElement { +class AssignmentStatement extends AstElement { left: Variable; right: AstElement; constructor(left: Variable, right: AstElement) { From 2abd993a72ce2a651d1979d1332389f1d95d8e7c Mon Sep 17 00:00:00 2001 From: Johannes Meier Date: Wed, 11 Dec 2024 17:46:33 +0100 Subject: [PATCH 5/7] reworked the existing examples to use the new factory API --- .../language/type-system/lox-type-checking.ts | 49 +++++++------------ examples/ox/src/language/ox-type-checking.ts | 29 +++++------ packages/typir/src/typir.ts | 2 +- packages/typir/test/type-definitions.test.ts | 22 +++++---- 4 files changed, 42 insertions(+), 60 deletions(-) diff --git a/examples/lox/src/language/type-system/lox-type-checking.ts b/examples/lox/src/language/type-system/lox-type-checking.ts index 9c1c999..bdd311f 100644 --- a/examples/lox/src/language/type-system/lox-type-checking.ts +++ b/examples/lox/src/language/type-system/lox-type-checking.ts @@ -6,60 +6,47 @@ import { AstNode, AstUtils, Module, assertUnreachable } from 'langium'; import { LangiumSharedServices } from 'langium/lsp'; -import { ClassKind, CreateFieldDetails, CreateFunctionTypeDetails, CreateParameterDetails, FunctionKind, InferOperatorWithMultipleOperands, InferOperatorWithSingleOperand, InferenceRuleNotApplicable, NO_PARAMETER_NAME, OperatorManager, PrimitiveKind, TopKind, TypirServices, UniqueClassValidation, UniqueFunctionValidation, UniqueMethodValidation, createNoSuperClassCyclesValidation, ValidationMessageDetails } from 'typir'; +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, isClassMember, isFieldMember, isForStatement, isFunctionDeclaration, isIfStatement, isMemberCall, isMethodMember, isNilLiteral, isNumberLiteral, isParameter, isPrintStatement, isReturnStatement, isStringLiteral, isTypeReference, isUnaryExpression, isVariableDeclaration, isWhileStatement } from '../generated/ast.js'; /* eslint-disable @typescript-eslint/no-unused-vars */ export class LoxTypeCreator extends AbstractLangiumTypeCreator { protected readonly typir: TypirServices; - protected readonly primitiveKind: PrimitiveKind; - protected readonly functionKind: FunctionKind; - protected readonly classKind: ClassKind; - protected readonly anyKind: TopKind; - protected readonly operators: OperatorManager; constructor(typirServices: TypirServices, langiumServices: LangiumSharedServices) { super(typirServices, langiumServices); this.typir = typirServices; - - this.primitiveKind = new PrimitiveKind(this.typir); - this.functionKind = new FunctionKind(this.typir); - this.classKind = new ClassKind(this.typir, { - typing: 'Nominal', - }); - this.anyKind = new TopKind(this.typir); - this.operators = this.typir.operators; } onInitialize(): void { // primitive types // typeBool, typeNumber and typeVoid are specific types for OX, ... - const typeBool = this.primitiveKind.createPrimitiveType({ primitiveName: 'boolean', + const typeBool = this.typir.factory.primitives.create({ primitiveName: 'boolean', inferenceRules: [ isBooleanLiteral, (node: unknown) => isTypeReference(node) && node.primitive === 'boolean' ]}); // ... but their primitive kind is provided/preset by Typir - const typeNumber = this.primitiveKind.createPrimitiveType({ primitiveName: 'number', + const typeNumber = this.typir.factory.primitives.create({ primitiveName: 'number', inferenceRules: [ isNumberLiteral, (node: unknown) => isTypeReference(node) && node.primitive === 'number' ]}); - const typeString = this.primitiveKind.createPrimitiveType({ primitiveName: 'string', + const typeString = this.typir.factory.primitives.create({ primitiveName: 'string', inferenceRules: [ isStringLiteral, (node: unknown) => isTypeReference(node) && node.primitive === 'string' ]}); - const typeVoid = this.primitiveKind.createPrimitiveType({ primitiveName: 'void', + const typeVoid = this.typir.factory.primitives.create({ primitiveName: 'void', inferenceRules: [ (node: unknown) => isTypeReference(node) && node.primitive === 'void', isPrintStatement, (node: unknown) => isReturnStatement(node) && node.value === undefined ] }); - const typeNil = this.primitiveKind.createPrimitiveType({ primitiveName: 'nil', + const typeNil = this.typir.factory.primitives.create({ primitiveName: 'nil', inferenceRules: isNilLiteral }); // From "Crafting Interpreters" no value, like null in other languages. Uninitialised variables default to nil. When the execution reaches the end of the block of a function body without hitting a return, nil is implicitly returned. - const typeAny = this.anyKind.createTopType({}); + const typeAny = this.typir.factory.top.create({}); // extract inference rules, which is possible here thanks to the unified structure of the Langium grammar (but this is not possible in general!) const binaryInferenceRule: InferOperatorWithMultipleOperands = { @@ -75,9 +62,9 @@ export class LoxTypeCreator extends AbstractLangiumTypeCreator { // binary operators: numbers => number for (const operator of ['-', '*', '/']) { - this.operators.createBinaryOperator({ name: operator, signature: { left: typeNumber, right: typeNumber, return: typeNumber }, inferenceRule: binaryInferenceRule }); + this.typir.factory.operators.createBinary({ name: operator, signature: { left: typeNumber, right: typeNumber, return: typeNumber }, inferenceRule: binaryInferenceRule }); } - this.operators.createBinaryOperator({ name: '+', signature: [ + this.typir.factory.operators.createBinary({ name: '+', signature: [ { left: typeNumber, right: typeNumber, return: typeNumber }, { left: typeString, right: typeString, return: typeString }, { left: typeNumber, right: typeString, return: typeString }, @@ -86,24 +73,24 @@ export class LoxTypeCreator extends AbstractLangiumTypeCreator { // binary operators: numbers => boolean for (const operator of ['<', '<=', '>', '>=']) { - this.operators.createBinaryOperator({ name: operator, signature: { left: typeNumber, right: typeNumber, return: typeBool }, inferenceRule: binaryInferenceRule }); + this.typir.factory.operators.createBinary({ name: operator, signature: { left: typeNumber, right: typeNumber, return: typeBool }, inferenceRule: binaryInferenceRule }); } // binary operators: booleans => boolean for (const operator of ['and', 'or']) { - this.operators.createBinaryOperator({ name: operator, signature: { left: typeBool, right: typeBool, return: typeBool }, inferenceRule: binaryInferenceRule }); + this.typir.factory.operators.createBinary({ name: operator, signature: { left: typeBool, right: typeBool, return: typeBool }, inferenceRule: binaryInferenceRule }); } // ==, != for all data types (the warning for different types is realized below) for (const operator of ['==', '!=']) { - this.operators.createBinaryOperator({ name: operator, signature: { left: typeAny, right: typeAny, return: typeBool }, inferenceRule: binaryInferenceRule }); + this.typir.factory.operators.createBinary({ name: operator, signature: { left: typeAny, right: typeAny, return: typeBool }, inferenceRule: binaryInferenceRule }); } // = for SuperType = SubType (TODO integrate the validation here? should be replaced!) - this.operators.createBinaryOperator({ name: '=', signature: { left: typeAny, right: typeAny, return: typeAny }, inferenceRule: binaryInferenceRule }); + this.typir.factory.operators.createBinary({ name: '=', signature: { left: typeAny, right: typeAny, return: typeAny }, inferenceRule: binaryInferenceRule }); // unary operators - this.operators.createUnaryOperator({ name: '!', signature: { operand: typeBool, return: typeBool }, inferenceRule: unaryInferenceRule }); - this.operators.createUnaryOperator({ name: '-', signature: { operand: typeNumber, return: typeNumber }, inferenceRule: unaryInferenceRule }); + this.typir.factory.operators.createUnary({ name: '!', signature: { operand: typeBool, return: typeBool }, inferenceRule: unaryInferenceRule }); + this.typir.factory.operators.createUnary({ name: '-', signature: { operand: typeNumber, return: typeNumber }, inferenceRule: unaryInferenceRule }); // additional inference rules for ... this.typir.inference.addInferenceRule((domainElement: unknown) => { @@ -208,7 +195,7 @@ export class LoxTypeCreator extends AbstractLangiumTypeCreator { // function types: they have to be updated after each change of the Langium document, since they are derived from FunctionDeclarations! if (isFunctionDeclaration(node)) { - this.functionKind.createFunctionType(createFunctionDetails(node)); // this logic is reused for methods of classes, since the LOX grammar defines them very similar + this.typir.factory.functions.create(createFunctionDetails(node)); // this logic is reused for methods of classes, since the LOX grammar defines them very similar } // TODO support lambda (type references)! @@ -216,7 +203,7 @@ export class LoxTypeCreator extends AbstractLangiumTypeCreator { // class types (nominal typing): if (isClass(node)) { const className = node.name; - const classType = this.classKind.createClassType({ + const classType = this.typir.factory.classes.create({ className, superClasses: node.superClass?.ref, // note that type inference is used here fields: node.members @@ -250,7 +237,7 @@ export class LoxTypeCreator extends AbstractLangiumTypeCreator { // any class !== all classes; here we want to say, that 'nil' is assignable to each concrete Class type! // this.typir.conversion.markAsConvertible(typeNil, this.classKind.getOrCreateTopClassType({}), 'IMPLICIT_EXPLICIT'); classType.addListener(type => { - this.typir.conversion.markAsConvertible(this.primitiveKind.getPrimitiveType({ primitiveName: 'nil' })!, type, 'IMPLICIT_EXPLICIT'); + this.typir.conversion.markAsConvertible(this.typir.factory.primitives.get({ primitiveName: 'nil' })!, type, 'IMPLICIT_EXPLICIT'); }); } } diff --git a/examples/ox/src/language/ox-type-checking.ts b/examples/ox/src/language/ox-type-checking.ts index ba7a6e7..271988a 100644 --- a/examples/ox/src/language/ox-type-checking.ts +++ b/examples/ox/src/language/ox-type-checking.ts @@ -6,39 +6,32 @@ import { AstNode, AstUtils, Module, assertUnreachable } from 'langium'; import { LangiumSharedServices } from 'langium/lsp'; -import { CreateParameterDetails, FunctionKind, InferOperatorWithMultipleOperands, InferOperatorWithSingleOperand, InferenceRuleNotApplicable, NO_PARAMETER_NAME, OperatorManager, PrimitiveKind, TypirServices, UniqueFunctionValidation } from 'typir'; +import { CreateParameterDetails, InferOperatorWithMultipleOperands, InferOperatorWithSingleOperand, InferenceRuleNotApplicable, NO_PARAMETER_NAME, TypirServices, UniqueFunctionValidation } from 'typir'; import { AbstractLangiumTypeCreator, LangiumServicesForTypirBinding, PartialTypirLangiumServices } from 'typir-langium'; import { ValidationMessageDetails } from '../../../../packages/typir/lib/services/validation.js'; import { BinaryExpression, MemberCall, UnaryExpression, isAssignmentStatement, isBinaryExpression, isBooleanLiteral, isForStatement, isFunctionDeclaration, isIfStatement, isMemberCall, isNumberLiteral, isParameter, isReturnStatement, isTypeReference, isUnaryExpression, isVariableDeclaration, isWhileStatement } from './generated/ast.js'; export class OxTypeCreator extends AbstractLangiumTypeCreator { protected readonly typir: TypirServices; - protected readonly primitiveKind: PrimitiveKind; - protected readonly functionKind: FunctionKind; - protected readonly operators: OperatorManager; constructor(typirServices: TypirServices, langiumServices: LangiumSharedServices) { super(typirServices, langiumServices); this.typir = typirServices; - - this.primitiveKind = new PrimitiveKind(this.typir); - this.functionKind = new FunctionKind(this.typir); - this.operators = this.typir.operators; } onInitialize(): void { // define primitive types // typeBool, typeNumber and typeVoid are specific types for OX, ... - const typeBool = this.primitiveKind.createPrimitiveType({ primitiveName: 'boolean', inferenceRules: [ + const typeBool = this.typir.factory.primitives.create({ primitiveName: 'boolean', inferenceRules: [ isBooleanLiteral, (node: unknown) => isTypeReference(node) && node.primitive === 'boolean', ]}); // ... but their primitive kind is provided/preset by Typir - const typeNumber = this.primitiveKind.createPrimitiveType({ primitiveName: 'number', inferenceRules: [ + const typeNumber = this.typir.factory.primitives.create({ primitiveName: 'number', inferenceRules: [ isNumberLiteral, (node: unknown) => isTypeReference(node) && node.primitive === 'number', ]}); - const typeVoid = this.primitiveKind.createPrimitiveType({ primitiveName: 'void', inferenceRules: + const typeVoid = this.typir.factory.primitives.create({ primitiveName: 'void', inferenceRules: (node: unknown) => isTypeReference(node) && node.primitive === 'void' }); @@ -57,29 +50,29 @@ export class OxTypeCreator extends AbstractLangiumTypeCreator { // define operators // binary operators: numbers => number for (const operator of ['+', '-', '*', '/']) { - this.operators.createBinaryOperator({ name: operator, signature: { left: typeNumber, right: typeNumber, return: typeNumber }, inferenceRule: binaryInferenceRule }); + this.typir.factory.operators.createBinary({ name: operator, signature: { left: typeNumber, right: typeNumber, return: typeNumber }, inferenceRule: binaryInferenceRule }); } // TODO better name: overloads, overloadRules, selectors, signatures // TODO better name for "inferenceRule": astSelectors // binary operators: numbers => boolean for (const operator of ['<', '<=', '>', '>=']) { - this.operators.createBinaryOperator({ name: operator, signature: { left: typeNumber, right: typeNumber, return: typeBool }, inferenceRule: binaryInferenceRule }); + this.typir.factory.operators.createBinary({ name: operator, signature: { left: typeNumber, right: typeNumber, return: typeBool }, inferenceRule: binaryInferenceRule }); } // binary operators: booleans => boolean for (const operator of ['and', 'or']) { - this.operators.createBinaryOperator({ name: operator, signature: { left: typeBool, right: typeBool, return: typeBool }, inferenceRule: binaryInferenceRule }); + this.typir.factory.operators.createBinary({ name: operator, signature: { left: typeBool, right: typeBool, return: typeBool }, inferenceRule: binaryInferenceRule }); } // ==, != for booleans and numbers for (const operator of ['==', '!=']) { - this.operators.createBinaryOperator({ name: operator, signature: [ + this.typir.factory.operators.createBinary({ name: operator, signature: [ { left: typeNumber, right: typeNumber, return: typeBool }, { left: typeBool, right: typeBool, return: typeBool }, ], inferenceRule: binaryInferenceRule }); } // unary operators - this.operators.createUnaryOperator({ name: '!', signature: { operand: typeBool, return: typeBool }, inferenceRule: unaryInferenceRule }); - this.operators.createUnaryOperator({ name: '-', signature: { operand: typeNumber, return: typeNumber }, inferenceRule: unaryInferenceRule }); + this.typir.factory.operators.createUnary({ name: '!', signature: { operand: typeBool, return: typeBool }, inferenceRule: unaryInferenceRule }); + this.typir.factory.operators.createUnary({ name: '-', signature: { operand: typeNumber, return: typeNumber }, inferenceRule: unaryInferenceRule }); /** Hints regarding the order of Typir configurations for OX: * - In general, Typir aims to not depend on the order of configurations. @@ -171,7 +164,7 @@ export class OxTypeCreator extends AbstractLangiumTypeCreator { if (isFunctionDeclaration(domainElement)) { const functionName = domainElement.name; // define function type - this.functionKind.createFunctionType({ + this.typir.factory.functions.create({ functionName, // note that the following two lines internally use type inference here in order to map language types to Typir types outputParameter: { name: NO_PARAMETER_NAME, type: domainElement.returnType }, diff --git a/packages/typir/src/typir.ts b/packages/typir/src/typir.ts index 55f6287..e551e90 100644 --- a/packages/typir/src/typir.ts +++ b/packages/typir/src/typir.ts @@ -88,7 +88,7 @@ export const DefaultTypirServiceModule: Module = { factory: { primitives: (services) => new PrimitiveKind(services), functions: (services) => new FunctionKind(services), - classes: (services) => new ClassKind(services), + classes: (services) => new ClassKind(services, { typing: 'Nominal' }), top: (services) => new TopKind(services), bottom: (services) => new BottomKind(services), operators: (services) => new DefaultOperatorManager(services), diff --git a/packages/typir/test/type-definitions.test.ts b/packages/typir/test/type-definitions.test.ts index 7b32940..3d135a4 100644 --- a/packages/typir/test/type-definitions.test.ts +++ b/packages/typir/test/type-definitions.test.ts @@ -16,25 +16,27 @@ import { FixedParameterKind } from '../src/kinds/fixed-parameters/fixed-paramete describe('Tests for Typir', () => { test('Define some types', async () => { // start the type system - const typir = createTypirServices(); + const typir = createTypirServices({ + // customize some default factories for predefined types + factory: { + classes: (services) =>new ClassKind(typir, { typing: 'Structural', maximumNumberOfSuperClasses: 1, subtypeFieldChecking: 'SUB_TYPE' }), + }, + }); // reuse predefined kinds - const primitiveKind = new PrimitiveKind(typir); const multiplicityKind = new MultiplicityKind(typir, { symbolForUnlimited: '*' }); - const classKind = new ClassKind(typir, { typing: 'Structural', maximumNumberOfSuperClasses: 1, subtypeFieldChecking: 'SUB_TYPE' }); const listKind = new FixedParameterKind(typir, 'List', { parameterSubtypeCheckingStrategy: 'EQUAL_TYPE' }, 'entry'); const mapKind = new FixedParameterKind(typir, 'Map', { parameterSubtypeCheckingStrategy: 'EQUAL_TYPE' }, 'key', 'value'); - const functionKind = new FunctionKind(typir); // create some primitive types - const typeInt = primitiveKind.create({ primitiveName: 'Integer' }); - const typeString = primitiveKind.create({ primitiveName: 'String', + const typeInt = typir.factory.primitives.create({ primitiveName: 'Integer' }); + const typeString = typir.factory.primitives.create({ primitiveName: 'String', inferenceRules: domainElement => typeof domainElement === 'string'}); // combine type definition with a dedicated inference rule for it - const typeBoolean = primitiveKind.create({ primitiveName: 'Boolean' }); + const typeBoolean = typir.factory.primitives.create({ primitiveName: 'Boolean' }); // create class type Person with 1 firstName and 1..2 lastNames and an age properties const typeOneOrTwoStrings = multiplicityKind.createMultiplicityType({ constrainedType: typeString, lowerBound: 1, upperBound: 2 }); - const typePerson = classKind.create({ + const typePerson = typir.factory.classes.create({ className: 'Person', fields: [ { name: 'firstName', type: typeString }, @@ -44,7 +46,7 @@ describe('Tests for Typir', () => { methods: [], }); console.log(typePerson.getTypeFinal()!.getUserRepresentation()); - const typeStudent = classKind.create({ + const typeStudent = typir.factory.classes.create({ className: 'Student', superClasses: typePerson, // a Student is a special Person fields: [ @@ -57,7 +59,7 @@ describe('Tests for Typir', () => { const typeListInt = listKind.createFixedParameterType({ parameterTypes: typeInt }); const typeListString = listKind.createFixedParameterType({ parameterTypes: typeString }); // const typeMapStringPerson = mapKind.createFixedParameterType({ parameterTypes: [typeString, typePerson] }); - const typeFunctionStringLength = functionKind.create({ + const typeFunctionStringLength = typir.factory.functions.create({ functionName: 'length', outputParameter: { name: NO_PARAMETER_NAME, type: typeInt }, inputParameters: [{ name: 'value', type: typeString }] From 190684d16697dad3d1ba11b91647e147ff20094c Mon Sep 17 00:00:00 2001 From: Johannes Meier Date: Wed, 11 Dec 2024 19:35:09 +0100 Subject: [PATCH 6/7] renamed some variables --- packages/typir/src/kinds/class/class-kind.ts | 4 ++-- packages/typir/src/services/operator.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/typir/src/kinds/class/class-kind.ts b/packages/typir/src/kinds/class/class-kind.ts index fd43ec9..e0a7c9a 100644 --- a/packages/typir/src/kinds/class/class-kind.ts +++ b/packages/typir/src/kinds/class/class-kind.ts @@ -143,10 +143,10 @@ export class ClassKind implements Kind, ClassFactoryService { .sort() // the order of fields does not matter, therefore we need a stable order to make the identifiers comparable .join(','); // methods - const functionKind = this.getMethodFactory(); + const functionFactory = this.getMethodFactory(); const methods: string = typeDetails.methods .map(createMethodDetails => { - return functionKind.calculateIdentifier(createMethodDetails); // reuse the Identifier for Functions here! + return functionFactory.calculateIdentifier(createMethodDetails); // reuse the Identifier for Functions here! }) .sort() // the order of methods does not matter, therefore we need a stable order to make the identifiers comparable .join(','); diff --git a/packages/typir/src/services/operator.ts b/packages/typir/src/services/operator.ts index 10beefa..3d31a02 100644 --- a/packages/typir/src/services/operator.ts +++ b/packages/typir/src/services/operator.ts @@ -152,10 +152,10 @@ export class DefaultOperatorManager implements OperatorManager { createGeneric(typeDetails: GenericOperatorDetails): TypeInitializer { // define/register the wanted operator as "special" function - const functionKind = this.getFunctionFactory(); + const functionFactory = this.getFunctionFactory(); // create the operator as type of kind 'function' - const newOperatorType = functionKind.create({ + const newOperatorType = functionFactory.create({ functionName: typeDetails.name, outputParameter: { name: NO_PARAMETER_NAME, type: typeDetails.outputType }, inputParameters: typeDetails.inputParameter, From 17ee2543172b6c23f9cbe562b69268c58293e651 Mon Sep 17 00:00:00 2001 From: Johannes Meier Date: Thu, 12 Dec 2024 15:30:17 +0100 Subject: [PATCH 7/7] renaming according to the review --- packages/typir/src/kinds/class/class-kind.ts | 4 ++-- packages/typir/src/kinds/function/function-kind.ts | 4 ++-- packages/typir/src/services/operator.ts | 4 ++-- packages/typir/src/typir.ts | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/typir/src/kinds/class/class-kind.ts b/packages/typir/src/kinds/class/class-kind.ts index e0a7c9a..415efd3 100644 --- a/packages/typir/src/kinds/class/class-kind.ts +++ b/packages/typir/src/kinds/class/class-kind.ts @@ -12,7 +12,7 @@ import { InferenceRuleNotApplicable } from '../../services/inference.js'; import { TypirServices } from '../../typir.js'; import { TypeCheckStrategy } from '../../utils/utils-type-comparison.js'; import { assertTrue, assertType, toArray } from '../../utils/utils.js'; -import { CreateFunctionTypeDetails, FunctionPredefinedService } from '../function/function-kind.js'; +import { CreateFunctionTypeDetails, FunctionFactoryService } from '../function/function-kind.js'; import { Kind, isKind } from '../kind.js'; import { ClassTypeInitializer } from './class-initializer.js'; import { ClassType, isClassType } from './class-type.js'; @@ -180,7 +180,7 @@ export class ClassKind implements Kind, ClassFactoryService { return `${this.getIdentifierPrefix()}${typeDetails.className}`; } - getMethodFactory(): FunctionPredefinedService { + getMethodFactory(): FunctionFactoryService { return this.services.factory.functions; } diff --git a/packages/typir/src/kinds/function/function-kind.ts b/packages/typir/src/kinds/function/function-kind.ts index f668d7a..1720dfc 100644 --- a/packages/typir/src/kinds/function/function-kind.ts +++ b/packages/typir/src/kinds/function/function-kind.ts @@ -96,7 +96,7 @@ export type InferFunctionCall = { */ -export interface FunctionPredefinedService { +export interface FunctionFactoryService { create(typeDetails: CreateFunctionTypeDetails): TypeInitializer; get(typeDetails: FunctionTypeDetails): TypeReference; calculateIdentifier(typeDetails: FunctionTypeDetails): string; @@ -118,7 +118,7 @@ export interface FunctionPredefinedService { * - optional parameters * - parameters which are used for output AND input */ -export class FunctionKind implements Kind, TypeGraphListener, FunctionPredefinedService { +export class FunctionKind implements Kind, TypeGraphListener, FunctionFactoryService { readonly $name: 'FunctionKind'; readonly services: TypirServices; readonly options: Readonly; diff --git a/packages/typir/src/services/operator.ts b/packages/typir/src/services/operator.ts index 3d31a02..83fc502 100644 --- a/packages/typir/src/services/operator.ts +++ b/packages/typir/src/services/operator.ts @@ -6,7 +6,7 @@ import { Type } from '../graph/type-node.js'; import { TypeInitializer } from '../initialization/type-initializer.js'; -import { FunctionPredefinedService, NO_PARAMETER_NAME } from '../kinds/function/function-kind.js'; +import { FunctionFactoryService, NO_PARAMETER_NAME } from '../kinds/function/function-kind.js'; import { TypirServices } from '../typir.js'; import { NameTypePair, TypeInitializers } from '../utils/utils-definitions.js'; import { toArray } from '../utils/utils.js'; @@ -174,7 +174,7 @@ export class DefaultOperatorManager implements OperatorManager { return newOperatorType as unknown as TypeInitializer; } - protected getFunctionFactory(): FunctionPredefinedService { + protected getFunctionFactory(): FunctionFactoryService { return this.services.factory.functions; } } diff --git a/packages/typir/src/typir.ts b/packages/typir/src/typir.ts index e551e90..ee10ee4 100644 --- a/packages/typir/src/typir.ts +++ b/packages/typir/src/typir.ts @@ -7,7 +7,7 @@ import { TypeGraph } from './graph/type-graph.js'; import { BottomFactoryService, BottomKind } from './kinds/bottom/bottom-kind.js'; import { ClassFactoryService, ClassKind } from './kinds/class/class-kind.js'; -import { FunctionKind, FunctionPredefinedService } from './kinds/function/function-kind.js'; +import { FunctionKind, FunctionFactoryService } from './kinds/function/function-kind.js'; import { PrimitiveFactoryService, PrimitiveKind } from './kinds/primitive/primitive-kind.js'; import { TopFactoryService, TopKind } from './kinds/top/top-kind.js'; import { DefaultTypeAssignability, TypeAssignability } from './services/assignability.js'; @@ -60,7 +60,7 @@ export type TypirServices = { }; readonly factory: { readonly primitives: PrimitiveFactoryService; - readonly functions: FunctionPredefinedService; + readonly functions: FunctionFactoryService; readonly classes: ClassFactoryService; readonly top: TopFactoryService; readonly bottom: BottomFactoryService;