Skip to content

Commit

Permalink
refactorings, improved comments
Browse files Browse the repository at this point in the history
  • Loading branch information
JohannesMeierSE committed Nov 18, 2024
1 parent 36708f0 commit c1f02ec
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 27 deletions.
33 changes: 19 additions & 14 deletions packages/typir/src/graph/type-node.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 { isClassType } from '../kinds/class-kind.js';
import { Kind, isKind } from '../kinds/kind.js';
import { TypeReference, TypirProblem, WaitingForInvalidTypeReferences, WaitingForResolvedTypeReferences } from '../utils/utils-definitions.js';
import { assertTrue, assertUnreachable } from '../utils/utils.js';
Expand Down Expand Up @@ -156,42 +155,48 @@ export abstract class Type {
onCompletion?: () => void,
onInvalidation?: () => void,
}): void {
// specify the preconditions:
// invalid --> identifiable
// store the reactions
this.onIdentification = preconditions.onIdentification ?? (() => {});
this.onCompletion = preconditions.onCompletion ?? (() => {});
this.onInvalidation = preconditions.onInvalidation ?? (() => {});

if (this.kind.$name === 'ClassKind') {
console.log('');
}
// preconditions for Identifiable
const init1 = new WaitingForResolvedTypeReferences(
preconditions.preconditionsForInitialization?.refsToBeIdentified,
preconditions.preconditionsForInitialization?.refsToBeCompleted,
this,
);
// identifiable --> completed
// preconditions for Completed
const init2 = new WaitingForResolvedTypeReferences(
preconditions.preconditionsForCompletion?.refsToBeIdentified,
preconditions.preconditionsForCompletion?.refsToBeCompleted,
this,
);
// completed --> invalid
// preconditions for Invalid
const init3 = new WaitingForInvalidTypeReferences(
preconditions.referencesRelevantForInvalidation ?? [],
);

// store the reactions
this.onIdentification = preconditions.onIdentification ?? (() => {});
this.onCompletion = preconditions.onCompletion ?? (() => {});
this.onInvalidation = preconditions.onInvalidation ?? (() => {});

// specify the transitions between the states:
// invalid --> identifiable
init1.addListener(() => {
this.switchFromInvalidToIdentifiable();
if (init2.isFulfilled()) {
// this is required to ensure the stric order Identifiable --> Completed, since 'init2' might already be triggered
this.switchFromIdentifiableToCompleted();
}
}, true);
// identifiable --> completed
init2.addListener(() => {
if (init1.isFulfilled()) {
this.switchFromIdentifiableToCompleted();
} else {
// switching will be done later by 'init1' in order to conform to the stric order Identifiable --> Completed
}
}, false); // not required, since init1 will switch to Completed as well!
// identifiable/completed --> invalid
init3.addListener(() => {
if (this.isNotInState('Invalid')) {
this.switchFromCompleteOrIdentifiableToInvalid();
Expand All @@ -207,21 +212,21 @@ export abstract class Type {
this.assertState('Invalid');
this.onIdentification();
this.initialization = 'Identifiable';
this.stateListeners.forEach(listener => listener.switchedToIdentifiable(this));
this.stateListeners.slice().forEach(listener => listener.switchedToIdentifiable(this)); // slice() prevents issues with removal of listeners during notifications
}

protected switchFromIdentifiableToCompleted(): void {
this.assertState('Identifiable');
this.onCompletion();
this.initialization = 'Completed';
this.stateListeners.forEach(listener => listener.switchedToCompleted(this));
this.stateListeners.slice().forEach(listener => listener.switchedToCompleted(this)); // slice() prevents issues with removal of listeners during notifications
}

protected switchFromCompleteOrIdentifiableToInvalid(): void {
this.assertNotState('Invalid');
this.onInvalidation();
this.initialization = 'Invalid';
this.stateListeners.forEach(listener => listener.switchedToInvalid(this));
this.stateListeners.slice().forEach(listener => listener.switchedToInvalid(this)); // slice() prevents issues with removal of listeners during notifications
}


Expand Down
28 changes: 17 additions & 11 deletions packages/typir/src/kinds/class-kind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ export class ClassType extends Type {
const refMethods = this.methods.map(m => m.type);
// the uniqueness of methods can be checked with the predefined UniqueMethodValidation below

// calculate the Identifier, based on the resolved type references
// const all: Array<TypeReference<Type | FunctionType>> = [];
const fieldsAndMethods: Array<TypeReference<Type>> = [];
fieldsAndMethods.push(...refFields);
Expand All @@ -85,7 +84,7 @@ export class ClassType extends Type {
refsToBeIdentified: fieldsAndMethods,
},
preconditionsForCompletion: {
refsToBeCompleted: this.superClasses as unknown as Array<TypeReference<Type>>,
refsToBeCompleted: this.superClasses as unknown as Array<TypeReference<Type>>, // TODO here we are waiting for the same/current (identifiable) ClassType!!
},
referencesRelevantForInvalidation: [...fieldsAndMethods, ...(this.superClasses as unknown as Array<TypeReference<Type>>)],
onIdentification: () => {
Expand Down Expand Up @@ -387,6 +386,7 @@ export interface ClassTypeDetails<T = unknown> {
}
export interface CreateClassTypeDetails<T = unknown, T1 = unknown, T2 = unknown> extends ClassTypeDetails<T> { // TODO the generics look very bad!
inferenceRuleForDeclaration?: (domainElement: unknown) => boolean, // TODO what is the purpose for this? what is the difference to literals?
// TODO rename to Constructor call??
inferenceRuleForLiteral?: InferClassLiteral<T1>, // InferClassLiteral<T> | Array<InferClassLiteral<T>>, does not work: https://stackoverflow.com/questions/65129070/defining-an-array-of-differing-generic-types-in-typescript
inferenceRuleForReference?: InferClassLiteral<T2>,
inferenceRuleForFieldAccess?: (domainElement: unknown) => string | unknown | InferenceRuleNotApplicable, // name of the field | element to infer the type of the field (e.g. the type) | rule not applicable
Expand Down Expand Up @@ -553,19 +553,25 @@ export class ClassTypeInitializer<T = unknown, T1 = unknown, T2 = unknown> exten
}

switchedToIdentifiable(classType: Type): void {
// TODO Vorsicht, dass hier nicht 2x derselbe Type angefangen wird zu erstellen und dann zwei Typen auf ihre Vervollständigung warten!
// 2x TypeResolver erstellen, beide müssen später denselben ClassType zurückliefern!
// bei Node { children: Node[] } muss der Zyklus erkannt und behandelt werden!!
this.producedType(classType as ClassType);
registerInferenceRules<T, T1, T2>(this.services, this.typeDetails, this.kind, classType as ClassType);
}
/* Important explanations:
* - This logic here (and 'producedType(...)') ensures, that the same ClassType is not registered twice in the type graph.
* - By waiting untile the new class has its identifier, 'producedType(...)' is able to check, whether this class type is already existing!
* - Accordingly, 'classType' and 'readyClassType' might have different values!
*/
const readyClassType = this.producedType(classType as ClassType);

switchedToCompleted(classType: Type): void {
// register inference rules
classType.removeListener(this); // the work of this initializer is done now
registerInferenceRules<T, T1, T2>(this.services, this.typeDetails, this.kind, readyClassType);

// the work of this initializer is done now
classType.removeListener(this);
}

switchedToCompleted(_classType: Type): void {
// do nothing
}

switchedToInvalid(_type: Type): void {
switchedToInvalid(_previousClassType: Type): void {
// do nothing
}
}
Expand Down
8 changes: 6 additions & 2 deletions packages/typir/src/utils/type-initialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ export abstract class TypeInitializer<T extends Type = Type> {
this.services = services;
}

protected producedType(newType: T): void {
protected producedType(newType: T): T {
const key = newType.getIdentifier();
if (!key) {
throw new Error('missing identifier!');
}
const existingType = this.services.graph.getType(key);
if (existingType) {
// ensure, that the same type is not duplicated!
Expand All @@ -31,8 +34,9 @@ export abstract class TypeInitializer<T extends Type = Type> {
}

// inform and clear all listeners
this.listeners.forEach(listener => listener(this.typeToReturn!));
this.listeners.slice().forEach(listener => listener(this.typeToReturn!));
this.listeners = [];
return this.typeToReturn;
}

getType(): T | undefined {
Expand Down

0 comments on commit c1f02ec

Please sign in to comment.