UNPKG

typir

Version:

General purpose type checking library

156 lines 8.85 kB
/****************************************************************************** * 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