typir
Version:
General purpose type checking library
156 lines • 8.85 kB
TypeScript
/******************************************************************************
* 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 { TypeReference } from '../initialization/type-reference.js';
import { WaitingForIdentifiableAndCompletedTypeReferences, WaitingForInvalidTypeReferences } from '../initialization/type-waiting.js';
import { Kind } from '../kinds/kind.js';
import { TypirSpecifics } from '../typir.js';
import { TypirProblem } from '../utils/utils-definitions.js';
import { TypeEdge } from './type-edge.js';
/**
* The transitions between the states of a type are depicted as state machine:
* ```mermaid
stateDiagram-v2
[*] --> Invalid
Invalid --> Identifiable
Identifiable --> Completed
Completed --> Invalid
Identifiable --> Invalid
```
* A state is 'Completed', when all its dependencies are available, i.e. the types of all its properties are available.
* A state is 'Identifiable', when all those dependencies are available which are required to calculate the identifier of the type.
* A state is 'Invalid' otherwise.
* 'Invalid' is made explicit, since it might require less dependencies than 'Completed' and therefore speed-ups the resolution of dependencies.
*/
export type TypeInitializationState = 'Invalid' | 'Identifiable' | 'Completed';
export interface PreconditionsForInitializationState {
referencesToBeIdentifiable?: Array<TypeReference<Type>>;
referencesToBeCompleted?: Array<TypeReference<Type>>;
}
/**
* Contains properties which are be relevant for all types to create,
* i.e. it is used for specifying details of all types to create.
*/
export interface TypeDetails<Specifics extends TypirSpecifics> {
/** A node from the language might be associated with the new type to create,
* e.g. the declaration node in the AST (e.g. a FunctionDeclarationNode is associated with the corresponding FunctionType). */
associatedLanguageNode?: Specifics['LanguageType'];
}
/**
* Design decisions:
* - features of types are realized/determined by their kinds
* - Identifiers of types must be unique!
*/
export declare abstract class Type {
readonly kind: Kind;
protected identifier: string | undefined;
protected readonly edgesIncoming: Map<string, TypeEdge[]>;
protected readonly edgesOutgoing: Map<string, TypeEdge[]>;
/**
* A node from the language might be associated with the current type,
* e.g. the declaration node in the AST (e.g. a FunctionDeclarationNode is associated with the corresponding FunctionType)
* This language node is _not_ used for managing the lifecycles of this type,
* since it should be usable for any domain-specific purpose.
* Therefore, the use and update of this feature is under the responsibility of the user of Typir.
*/
readonly associatedLanguageNode: unknown | undefined;
constructor(identifier: string | undefined, typeDetails: TypeDetails<TypirSpecifics>);
/**
* Identifiers must be unique and stable for all types known in a single Typir instance, since they are used as key to store types in maps.
* Identifiers might have a naming schema for calculatable values.
*/
getIdentifier(): string;
/**
* Returns a string value containing a short representation of the type to be shown to users of the type-checked language nodes.
* This value don't need to be unique for all types.
* This name should be quite short.
* Services should not call this function directly, but typir.printer.printTypeName(...) instead.
* @returns a short string value to show to the user
*/
abstract getName(): string;
/**
* Calculates a string value which might be shown to users of the type-checked language nodes.
* This value don't need to be unique for all types.
* This representation might be longer and show lots of details of the type.
* Services should not call this function directly, but typir.printer.printTypeUserRepresentation(...) instead.
* @returns a longer string value to show to the user
*/
abstract getUserRepresentation(): string;
protected initializationState: TypeInitializationState;
getInitializationState(): TypeInitializationState;
protected assertState(expectedState: TypeInitializationState): void;
protected assertNotState(expectedState: TypeInitializationState): void;
protected assertStateOrLater(expectedState: TypeInitializationState): void;
isInState(state: TypeInitializationState): boolean;
isNotInState(state: TypeInitializationState): boolean;
isInStateOrLater(state: TypeInitializationState): boolean;
protected stateListeners: TypeStateListener[];
addListener(newListeners: TypeStateListener, informIfNotInvalidAnymore: boolean): void;
removeListener(listener: TypeStateListener): void;
protected onIdentification: () => void;
protected onCompletion: () => void;
protected onInvalidation: () => void;
protected waitForIdentifiable: WaitingForIdentifiableAndCompletedTypeReferences<Type>;
protected waitForCompleted: WaitingForIdentifiableAndCompletedTypeReferences<Type>;
protected waitForInvalid: WaitingForInvalidTypeReferences<Type>;
/**
* Use this method to specify, how THIS new type should be initialized.
*
* This method has(!) to be called at the end(!) of the constructor of each specific Type implementation, even if nothing has to be specified,
* since calling this method starts the initialization process!
* If you forget the call this method, the new type remains invalid and invisible for Typir and you will not be informed about this problem!
*
* @param preconditions all possible options for the initialization process
*/
protected defineTheInitializationProcessOfThisType(preconditions: {
/** Contains only those TypeReferences which are required to do the initialization. */
preconditionsForIdentifiable?: PreconditionsForInitializationState;
/** Contains only those TypeReferences which are required to do the completion.
* TypeReferences which are required only for the initialization, but not for the completion,
* don't need to be repeated here, since the completion is done only after the initialization. */
preconditionsForCompleted?: PreconditionsForInitializationState;
/** Must contain all(!) TypeReferences of a type. */
referencesRelevantForInvalidation?: Array<TypeReference<Type>>;
/** typical use cases: calculate the identifier, register inference rules for the type object already now! */
onIdentifiable?: () => void;
/** typical use cases: do some internal checks for the completed properties */
onCompleted?: () => void;
onInvalidated?: () => void;
}): void;
/**
* This is an internal method to ignore some types during the initialization process in order to prevent dependency cycles.
* Usually there is no need to call this method on your own.
* @param additionalTypesToIgnore the new types to ignore during
*/
ignoreDependingTypesDuringInitialization(additionalTypesToIgnore: Set<Type>): void;
dispose(): void;
protected switchFromInvalidToIdentifiable(): void;
protected switchFromIdentifiableToCompleted(): void;
protected switchFromCompleteOrIdentifiableToInvalid(): void;
/**
* Analyzes, whether two types are equal.
* @param otherType to be compared with the current type
* @returns an empty array, if both types are equal, otherwise some problems which might point to found differences/conflicts between the two types.
* These problems are presented to users in order to support them with useful information about the result of this analysis.
*/
abstract analyzeTypeEqualityProblems(otherType: Type): TypirProblem[];
addIncomingEdge(edge: TypeEdge): void;
addOutgoingEdge(edge: TypeEdge): void;
removeIncomingEdge(edge: TypeEdge): boolean;
removeOutgoingEdge(edge: TypeEdge): boolean;
getIncomingEdges<T extends TypeEdge>($relation: T['$relation']): T[];
getOutgoingEdges<T extends TypeEdge>($relation: T['$relation']): T[];
getEdges<T extends TypeEdge>($relation: T['$relation']): T[];
getAllIncomingEdges(): TypeEdge[];
getAllOutgoingEdges(): TypeEdge[];
getAllEdges(): TypeEdge[];
}
export declare function isType(type: unknown): type is Type;
export interface TypeStateListener {
onSwitchedToInvalid(type: Type): void;
onSwitchedToIdentifiable(type: Type): void;
onSwitchedToCompleted(type: Type): void;
}
//# sourceMappingURL=type-node.d.ts.map