/** * @fileoverview Public types present in the interface between the compiler and * language plugins. */ import type { z } from "zod"; // ----------------------------------------------------------------------------- // CODE GENERATION // ----------------------------------------------------------------------------- /** * Generates code in one programming language from a set of parsed Soia modules. */ export interface CodeGenerator { readonly id: string; readonly version: string; readonly configType: z.ZodType; generateCode(input: CodeGenerator.Input): CodeGenerator.Output; } export declare namespace CodeGenerator { export interface Input { readonly modules: ReadonlyArray; readonly recordMap: ReadonlyMap; readonly config: Config; } export interface Output { readonly files: readonly OutputFile[]; } export interface OutputFile { readonly path: string; readonly code: string; } } // ----------------------------------------------------------------------------- // LEXICAL ANALYSIS // ----------------------------------------------------------------------------- /** One line in a module. */ export interface CodeLine { /** Zero-based. */ readonly lineNumber: number; readonly line: string; readonly position: number; readonly modulePath: string; } /** A lexical token. */ export interface Token { /** Empty if the token is the special token for EOF. */ readonly text: string; /** Measured in number of characters from the start of the module. */ readonly position: number; readonly line: CodeLine; /** Zero-based. */ readonly colNumber: number; } export type Casing = | "lowerCamel" | "lower_underscore" | "UpperCamel" | "UPPER_UNDERSCORE"; // ----------------------------------------------------------------------------- // ERROR HANDLING // ----------------------------------------------------------------------------- /** A user error in a module. */ export type SoiaError = | { readonly token: Token; /** Convention: starts with a capital letter. */ readonly message: string; readonly expected?: undefined; readonly errorIsInOtherModule?: true; } | { readonly token: Token; /** Convention: starts with a lowercase letter. */ readonly expected: string; readonly message?: undefined; readonly errorIsInOtherModule?: undefined; }; export interface ErrorSink { push(error: SoiaError): void; } /** * Holds a value of type T and possible errors registered during the computation * of the value. */ export interface Result { readonly result: T; readonly errors: readonly SoiaError[]; } // ----------------------------------------------------------------------------- // SYNTACTIC ANALYSIS // ----------------------------------------------------------------------------- export type Primitive = | "bool" | "int32" | "int64" | "uint64" | "float32" | "float64" | "timestamp" | "string" | "bytes"; export interface PrimitiveType { kind: "primitive"; primitive: Primitive; } /** * A reference to a record, e.g. 'Foobar' or 'foo.Bar' or '.Foo.Bar'. * One of the possible unresolved value types. The corresponding resolved value * type is `ResolvedRecordRef`. */ export interface UnresolvedRecordRef { readonly kind: "record"; /** * The different pieces in the name, e.g. ['Foo', 'Bar']. * Cannot be empty. */ readonly nameParts: readonly Token[]; /** * True if the reference starts with a dot, e.g. ".Foo.Bar". * In this example, "Foo" is to be found at the top-level of the module. */ readonly absolute: boolean; } /** Uniquely identifies a record within a module set. */ export type RecordKey = string; export interface ResolvedRecordRef { readonly kind: "record"; readonly key: RecordKey; readonly recordType: "struct" | "enum"; readonly nameParts: ReadonlyArray<{ /** * A single name in the type reference. * Can refer to either a report or a module alias. */ token: Token; /** * Either the definition of the record or the aliased import declaration. */ declaration: Record | ImportAlias; }>; /** * Last token in the type reference. For example, if the type reference is * ".Foo.Bar", this is the token for "Bar". */ readonly refToken: Token; } export interface MutableFieldPathItem { /** The field name token in the field path. */ readonly name: Token; /** The field declaration in the record definition. */ declaration?: Field; } /** A field or a sequence of field for keying items in an array. */ export interface MutableFieldPath { /** The "|" token. */ readonly pipeToken: Token; /** * A non-empty sequence of field names. * If the key type is an enum type, the last field name is guaranteed to be * "kind". */ readonly path: ReadonlyArray< Mutable extends true ? MutableFieldPathItem : Readonly >; /** The type used to key every item: either a primitive or an enum. */ keyType: PrimitiveType | ResolvedRecordRef; } export type FieldPath = // Mutable extends true // ? MutableFieldPath : Readonly>; export interface ArrayType< Type = ResolvedType, Mutable extends boolean = boolean, > { readonly kind: "array"; readonly item: Type; /** * If set, the value obtained by following the field path from an item can be * used as a unique key for the item. The user takes responsibility for * ensuring that not two items in the array have the same key. * The item type must be a struct type. */ readonly key: FieldPath | undefined; } export type UnresolvedArrayType = ArrayType; export type MutableArrayType = ArrayType; export interface OptionalType { readonly kind: "optional"; readonly other: Type; } /** * Result of parsing a type from a `.soia` file, without resolving the record * references. */ export type UnresolvedType = | PrimitiveType | UnresolvedRecordRef | UnresolvedArrayType | OptionalType; /** * Result of recursively resolving the record references in an unresolved type. * When Mutable is true, the optional field path of an array type can be * modified. */ export type ResolvedType = | PrimitiveType | ResolvedRecordRef | ArrayType | OptionalType>; export type MutableResolvedType = ResolvedType; /** Field of a struct or enum. */ export interface MutableField { readonly kind: "field"; readonly name: Token; readonly number: number; /** May only be undefined if the field is a constant in an enum. */ readonly unresolvedType: UnresolvedType | undefined; /** May only be undefined if the field is a constant in an enum. */ type: ResolvedType | undefined; /** * Evaluates to true if the value type of the field depends on the record * where the field is defined. If `Struct.DEFAULT.field` is or contains * `Struct.DEFAULT`, then the dependency is "hard", otherwise it is "soft". * * Examples: * struct A { s: string; } // false * struct B { b: B; } // "hard" * struct C { c: C?; } // "soft" * struct D { d: [D]; } // "soft" * struct E { f: F; } // "hard" * struct F { e: E; } // "hard" * struct G { b: B; } // false * struct H { i: I; } // "soft" * enum I { h: H; } // "soft" */ isRecursive: false | "soft" | "hard"; } /** Field of a struct or enum. */ export type Field = // Mutable extends true ? MutableField : Readonly>; /** A 'removed' declaration in a struct or enum. */ export interface Removed { readonly kind: "removed"; /** The 'removed' keyword token. */ readonly removedToken: Token; readonly numbers: readonly number[]; } /** A declaration within a record. */ export type RecordLevelDeclaration = | Field | Removed | Record; export type MutableRecordLevelDeclaration = RecordLevelDeclaration; export type Numbering = // The record does not have fields. | "" // Field numbers are not explicit in the schema. | "implicit" // Field numbers are explicit in the schema. | "explicit" // The record has both fields with implicit and explicit numbering. | "broken"; /** Definition of a struct or enum type. */ export interface Record { readonly kind: "record"; /** Uniquely identifies the record within the module set. */ readonly key: RecordKey; readonly name: Token; readonly recordType: "struct" | "enum"; /** Maps a field or nested record name to the corresponding declaration. */ readonly nameToDeclaration: { [n: string]: RecordLevelDeclaration }; readonly declarations: ReadonlyArray>; readonly fields: ReadonlyArray>; readonly nestedRecords: ReadonlyArray>; readonly numbering: Numbering; readonly removedNumbers: readonly number[]; readonly stableId: number | null; /** * If the record is a struct, 1 + the maximum field number. * Zero if the record is an enum. */ readonly numSlots: number; /** * If the record is a struct, 1 + the maximum of all field numbers and removed * numbers. * Zero if the record is an enum. */ readonly numSlotsInclRemovedNumbers: number; } export type MutableRecord = Record; export interface MutableImport { readonly kind: "import"; readonly importedNames: Token[]; /** The token corresponding to the quoted string. */ readonly modulePath: Token; resolvedModulePath?: string; } export type Import = Mutable extends true ? MutableImport : Readonly; export interface MutableImportAlias { readonly kind: "import-alias"; /** The alias. */ readonly name: Token; /** The token corresponding to the quoted string. */ readonly modulePath: Token; resolvedModulePath?: string; } export type ImportAlias = Mutable extends true ? MutableImportAlias : Readonly; export interface MutableMethod { readonly kind: "method"; readonly name: Token; readonly unresolvedRequestType: UnresolvedType; readonly unresolvedResponseType: UnresolvedType; requestType: ResolvedType | undefined; responseType: ResolvedType | undefined; // A hash of the name, or the explicit number specified after "=" if any. readonly number: number; } export type Method = // Mutable extends true ? MutableMethod : Readonly>; /** A `const` declaration. */ export interface MutableConstant { readonly kind: "constant"; readonly name: Token; readonly unresolvedType: UnresolvedType; type: ResolvedType | undefined; readonly value: Value; valueAsDenseJson: DenseJson | undefined; } export type Constant = // Mutable extends true // ? MutableConstant // : Readonly>; /** A name:value entry of an object. */ export interface MutableObjectEntry { readonly name: Token; readonly value: Value; } export type ObjectEntry = // Mutable extends true // ? MutableObjectEntry // : Readonly>; /** An object value, for example `{r: 255, g: 0, b: 0}`. */ export interface MutableObjectValue { readonly kind: "object"; readonly token: Token; readonly entries: Readonly<{ [f: string]: ObjectEntry }>; partial: boolean; type?: RecordKey; } export type ObjectValue = // Mutable extends true // ? MutableObjectValue : Readonly>; /** An array value, for example `[0, 1, 2]`. */ export interface MutableArrayValue { readonly kind: "array"; readonly token: Token; readonly items: ReadonlyArray>; key?: FieldPath | undefined; } export type ArrayValue = // Mutable extends true // ? MutableArrayValue // : Readonly>; /** One of: a quoted string, a number, `true`, `false`. */ export interface MutableLiteralValue { readonly kind: "literal"; readonly token: Token; type?: PrimitiveType | { kind: "enum"; key: RecordKey } | { kind: "null" }; } export type LiteralValue = // Mutable extends true // ? MutableLiteralValue // : Readonly; /** The value on the right side of the `=` symbol of a `const` declaration. */ export type Value = | ObjectValue | ArrayValue | LiteralValue; export type MutableValue = Value; /** Result of serializing a Soia value to dense JSON format. */ export type DenseJson = null | boolean | number | string | readonly DenseJson[]; /** A declaration which can appear at the top-level of a module. */ export type ModuleLevelDeclaration = | Record | Import | ImportAlias | Method | Constant; export type MutableModuleLevelDeclaration = ModuleLevelDeclaration; export type Declaration = | RecordLevelDeclaration | ModuleLevelDeclaration; export type MutableDeclaration = Declaration; /** * Contains the definition of a record and information about where the record * was defined. */ export interface RecordLocation { readonly kind: "record-location"; readonly record: Record; /** * Chain of records from the top-level record to `record` included. * Every record is nested within the record preceding it in the chain. */ readonly recordAncestors: readonly Record[]; readonly modulePath: string; } export type MutableRecordLocation = RecordLocation; /** The set of names from one module imported to another module. */ export type ImportedNames = | { kind: "all"; alias: string } | { kind: "some"; names: ReadonlySet }; export interface Module { readonly kind: "module"; readonly path: string; readonly nameToDeclaration: { [n: string]: ModuleLevelDeclaration }; readonly declarations: ReadonlyArray>; /** * Maps the path (to another module) to the corresponding import declarations in * this module. */ readonly pathToImportedNames: { [path: string]: ImportedNames }; /** * All the record declared in the module, at the top-level or not. * Depth-first: "Foo.Bar" will appear before "Foo". */ readonly records: // Mutable extends true // ? MutableRecordLocation[] : readonly RecordLocation[]; readonly methods: ReadonlyArray>; readonly constants: ReadonlyArray>; } /** Can be assigned to a `Module`. */ export type MutableModule = Module;