import { CharStream, Interval, ParseTree, Token, TokenStream } from "antlr4ng"; import { Constructor } from "../reflection/IMember.js"; import { AttributeRenderer } from "../AttributeRenderer.js"; import { ErrorType } from "../misc/ErrorType.js"; import { ModelAdaptor } from "../ModelAdaptor.js"; import { STErrorListener } from "../STErrorListener.js"; import { STWriter } from "../STWriter.js"; /** * An instance of the StringTemplate. It consists primarily of * a {@link ST#impl reference} to its implementation (shared among all * instances) and a hash table of {@link ST#locals attributes}. Because * of dynamic scoping, we also need a reference to any enclosing instance. For * example, in a deeply nested template for an HTML page body, we could still * reference the title attribute defined in the outermost page template. *
* To use templates, you create one (usually via {@link STGroup}) and then inject * attributes using {@link #add}. To render its attacks, use {@link ST#render()}.
** TODO: {@link ST#locals} is not actually a hash table like the documentation * says.
*/ export interface IST { /** The implementation for this template among all instances of same template . */ impl?: ICompiledST; groupThatCreatedThisInstance: ISTGroup; /** * Safe to simultaneously write via {@link #add}, which is synchronized. * Reading during exec is, however, NOT synchronized. So, not thread safe * to add attributes while it is being evaluated. Initialized to * {@link #EMPTY_ATTR} to distinguish {@code null} from empty. */ locals?: unknown[]; /** * Inject an attribute (name/value pair). If there is already an attribute * with that name, this method turns the attribute into an * {@link AttributeList} with both the previous and the new attribute as * elements. This method will never alter a {@link List} that you inject. * If you send in a {@link List} and then inject a single value element, * {@code add} copies original list and adds the new value. The * attribute name cannot be null or contain '.'. ** Return {@code this} so we can chain:
** {@code t.add("x", 1).add("y", "hi")}
*/ add(name: string, value: unknown): IST; /** * Remove an attribute value entirely (can't remove attribute definitions). */ remove(name: string): void; /** * Set {@code locals} attribute value when you only know the name, not the * index. This is ultimately invoked by calling {@code ST#add} from * outside so toss an exception to notify them. */ rawSetAttribute(name: string, value: unknown): void; isAnonSubtemplate(): boolean; /** * Split {@code aggrName.{propName1,propName2}} into list * {@code [propName1, propName2]} and the {@code aggrName}. Spaces are * allowed around {@code ','}. */ addAggr(aggregateSpec: string, ...values: unknown[]): IST; render(lineWidth?: number): string; render(locale: Intl.Locale, lineWidth?: number): string; write(out: STWriter): number; write(out: STWriter, locale: Intl.Locale): number; write(out: STWriter, listener: STErrorListener): number; write(out: STWriter, locale: Intl.Locale, listener: STErrorListener): number; } /** * This class knows how to execute template byte codes relative to a particular * {@link STGroup}. To execute the byte codes, we need an output stream and a * reference to an {@link ST} instance. That instance's {@link ST#impl} field * points at a {@link CompiledST}, which contains all of the byte codes and * other information relevant to execution. ** This interpreter is a stack-based bytecode interpreter. All operands go onto * an operand stack.
** If {@link #debug} set, we track interpreter events. For now, I am only * tracking instance creation events. These are used by {@link STViz} to pair up * output chunks with the template expressions that generate them.
** We create a new interpreter for each invocation of * {@link ST#render}, {@link ST#inspect}, or {@link ST#getEvents}.
*/ export interface IInterpreter { } export interface IErrorManager { runTimeError(interp: IInterpreter, scope: IInstanceScope, error: ErrorType): void; runTimeError(interp: IInterpreter, scope: IInstanceScope, error: ErrorType, arg: unknown): void; runTimeError(interp: IInterpreter, scope: IInstanceScope, error: ErrorType, e: Error, arg: unknown): void; runTimeError(interp: IInterpreter, scope: IInstanceScope, error: ErrorType, arg: unknown, arg2: unknown): void; runTimeError(interp: IInterpreter, scope: IInstanceScope, error: ErrorType, arg: unknown, arg2: unknown, arg3: unknown): void; iOError(self: IST | undefined, error: ErrorType, e: Error, arg?: unknown): void; internalError(self: IST | undefined, msg: string, e?: Error): void; } export interface IInterpEvent { } export interface IEvalTemplateEvent extends IInterpEvent { } export interface IInstanceScope { /** Template that invoked us. */ readonly parent?: IInstanceScope; /** Template we're executing. */ /** Current instruction pointer. */ ip: number; earlyEval: boolean; } /** * A directory or directory tree of {@code .st} template files and/or group files. * Individual template files contain formal template definitions. In a sense, * it's like a single group file broken into multiple files, one for each template. * ST v3 had just the pure template inside, not the template name and header. * Name inside must match filename (minus suffix). */ export interface ISTGroup { /** * Every group can import templates/dictionaries from other groups. * The list must be synchronized (see {@link STGroup#importTemplates}). */ readonly imports: ISTGroup[]; /** * The {@link ErrorManager} for entire group; all compilations and executions. * This gets copied to parsers, walkers, and interpreters. */ errMgr: IErrorManager; delimiterStartChar: string; delimiterStopChar: string; iterateAcrossValues: boolean; getName(): string; rawGetDictionary(name: string): Map* For non-imported groups and object-to-render of class {@code T}, use renderer * (if any) registered for {@code T}. For imports, any renderer * set on import group is ignored even when using an imported template. * You should set the renderer on the main group * you use (or all to be sure). I look at import groups as * "helpers" that should give me templates and nothing else. If you * have multiple renderers for {@code String}, say, then just make uber combined * renderer with more specific format names.
*/ getAttributeRenderer* This has nothing to do with the outer filesystem path to the group dir or * group file.
** We set this as we load/compile the template.
** Always ends with {@code "/"}.
*/ prefix: string; /** * The original, immutable pattern (not really used again after * initial "compilation"). Useful for debugging. Even for * sub templates, this is entire overall template. */ template: string; /** The token that begins template definition; could be {@code <@r>} of region. */ templateDefStartToken?: Token; /** Overall token stream for template (debug only). */ tokens?: TokenStream; /** How do we interpret syntax of template? (debug only) */ tree?: ParseTree; readonly formalArguments?: Map* {@code @t.r() ::= ""}
** is defined, but you can overwrite this def by defining your own. We need * to prevent more than one manual def though. Between this var and * {@link #isRegion} we can determine these cases.
*/ regionDefType: RegionType; isAnonSubtemplate: boolean; readonly strings: string[]; codeSize: number; sourceMap: Interval[]; readonly instructions: Int8Array; clone(): ICompiledST; /** * Used by {@link ST#add} to add args one by one without turning on full formal args definition signal. */ addArg(a: IFormalArgument): void; defineImplicitlyDefinedTemplates(group: ISTGroup): void; defineArgDefaultValueTemplates(group: ISTGroup): void; dump(): void; } /** * Represents the name of a formal argument defined in a template: ** test(a,b,x=defaultValue) ::= "Each template has a set of these formal arguments or sets * {@link CompiledST#hasFormalArgs} to {@code false} (indicating that no * arguments were specified such as when we create a template with * {@code new ST(...)}). * *" *
* Note: originally, I tracked cardinality as well as the name of an attribute. * I'm leaving the code here as I suspect something may come of it later. * Currently, though, cardinality is not used.
*/ export interface IFormalArgument { name: string; index: number; /** If they specified default value {@code x=y}, store the token here */ defaultValueToken?: Token; defaultValue: unknown; compiledDefaultValue?: ICompiledST; } /** Parameters for the compilation step. */ export interface ICompilerParameters { srcName?: string; name?: string; args?: IFormalArgument[]; template: string; templateToken?: Token; } /** {@code <@r()>}, {@code <@r>...<@end>}, and {@code @t.r() ::= "..."} defined manually by coder */ export declare enum RegionType { /** {@code <@r()>} */ IMPLICIT = 0, /** {@code <@r>...<@end>} */ EMBEDDED = 1, /** {@code @t.r() ::= "..."} */ EXPLICIT = 2 } export declare const isCompiledST: (value: unknown) => value is ICompiledST; export declare namespace ISTGroup { const DEFAULT_KEY = "default"; const DICT_KEY = "key"; }