Skip to content

Commit

Permalink
implemented new API for most of the other predefined types, removed g…
Browse files Browse the repository at this point in the history
…etOrCreate functions (since they are not supported/needed), moved new interfaces into existing kind files
  • Loading branch information
JohannesMeierSE committed Dec 10, 2024
1 parent b3e269b commit db3192c
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 135 deletions.
1 change: 0 additions & 1 deletion packages/typir/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
22 changes: 9 additions & 13 deletions packages/typir/src/kinds/bottom/bottom-kind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<BottomKindOptions>;
Expand All @@ -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!
Expand Down
11 changes: 8 additions & 3 deletions packages/typir/src/kinds/class/class-kind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,19 @@ export type InferClassLiteral<T = unknown> = {
};


export interface ClassFactoryService {
create<T, T1, T2>(typeDetails: CreateClassTypeDetails<T, T1, T2>): TypeInitializer<ClassType>;
get<T>(typeDetails: ClassTypeDetails<T> | string): TypeReference<ClassType>;
}

/**
* 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').
* Fields have exactly one name which must be unique for the current class (TODO what about same field names in extended class?).
* 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<ClassKindOptions>;
Expand All @@ -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<T>(typeDetails: ClassTypeDetails<T> | string): TypeReference<ClassType> { // string for nominal typing
get<T>(typeDetails: ClassTypeDetails<T> | string): TypeReference<ClassType> { // string for nominal typing
if (typeof typeDetails === 'string') {
// nominal typing
return new TypeReference(typeDetails, this.services);
Expand All @@ -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<T, T1, T2>(typeDetails: CreateClassTypeDetails<T, T1, T2>): TypeInitializer<ClassType> {
create<T, T1, T2>(typeDetails: CreateClassTypeDetails<T, T1, T2>): TypeInitializer<ClassType> {
return new ClassTypeInitializer(this.services, this, typeDetails);
}

Expand Down
12 changes: 8 additions & 4 deletions packages/typir/src/kinds/function/function-kind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -97,6 +96,11 @@ export type InferFunctionCall<T = unknown> = {
*/


export interface FunctionPredefinedService {
create<T>(typeDetails: CreateFunctionTypeDetails<T>): TypeInitializer<FunctionType>;
get(typeDetails: FunctionTypeDetails): TypeReference<FunctionType>;
}

/**
* Represents signatures of executable code.
*
Expand Down Expand Up @@ -235,7 +239,7 @@ export class FunctionKind implements Kind, TypeGraphListener, FunctionPredefined
);
}

getFunctionType(typeDetails: FunctionTypeDetails): TypeReference<FunctionType> {
get(typeDetails: FunctionTypeDetails): TypeReference<FunctionType> {
return new TypeReference(() => this.calculateIdentifier(typeDetails), this.services);
}

Expand Down
6 changes: 5 additions & 1 deletion packages/typir/src/kinds/primitive/primitive-kind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand Down
22 changes: 9 additions & 13 deletions packages/typir/src/kinds/top/top-kind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<TopKindOptions>;
Expand All @@ -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) {
Expand Down
78 changes: 0 additions & 78 deletions packages/typir/src/services/factory.ts

This file was deleted.

27 changes: 15 additions & 12 deletions packages/typir/src/typir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
};
};

Expand All @@ -76,18 +79,18 @@ export const DefaultTypirServiceModule: Module<TypirServices> = {
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),
},
};
Expand Down
20 changes: 10 additions & 10 deletions packages/typir/test/type-definitions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand All @@ -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: [
Expand All @@ -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:
Expand Down

0 comments on commit db3192c

Please sign in to comment.