preserves/implementations/javascript/packages/schema/src/compiler/context.ts

93 lines
2.5 KiB
TypeScript

import { Dictionary, KeyedSet, Position } from "@preserves/core";
import { refPosition } from "../reader";
import * as M from "../meta";
import { block, commas, Item, seq } from "./block";
export interface CompilerOptions {
preservesModule?: string;
defaultPointer?: M.Ref;
warn?(message: string, pos: Position | null): void;
}
export class ModuleContext {
readonly env: M.Environment;
readonly schema: M.Schema;
readonly options: CompilerOptions;
readonly literals = new Dictionary<never, string>();
readonly typedefs: Item[] = [];
readonly functiondefs: Item[] = [];
readonly imports = new KeyedSet<[string, string]>();
constructor(env: M.Environment, schema: M.Schema, options: CompilerOptions) {
this.env = env;
this.schema = schema;
this.options = options;
}
literal(v: M.Input): Item {
let varname = this.literals.get(v);
if (varname === void 0) {
const s = v.asPreservesText()
.replace('_', '__')
.replace('*', '_STAR_');
varname = M.isValidToken('_' + s, true) ? '$' + s : '__lit' + this.literals.size;
this.literals.set(v, varname);
}
return varname;
}
derefPattern([_name, p]: [string, M.Alternative]): M.Definition {
if (p.label === M.$ref) {
return M.lookup(refPosition(p), p, this.env,
(p) => p,
(p) => p,
(_modId, _modPath, pp) => pp ?? p);
} else {
return p;
}
}
defineType(f: Item): void {
this.typedefs.push(f);
}
defineFunction(f: (ctx: FunctionContext) => Item): void {
this.functiondefs.push(f(new FunctionContext(this)));
}
}
export class FunctionContext {
readonly mod: ModuleContext;
tempCounter = 0;
temps: string[] = [];
constructor(mod: ModuleContext) {
this.mod = mod;
}
gentemp(): string {
const varname = '_tmp' + this.tempCounter++;
this.temps.push(varname);
return varname;
}
gentemps(n: number): string[] {
const temps = [];
while (temps.length < n) temps.push(this.gentemp());
return temps;
}
block(f: () => Item[]): Item {
const oldTemps = this.temps;
this.temps = [];
const items = f();
const ts = this.temps;
this.temps = oldTemps;
return block(
... ts.length > 0 ? [seq(`let `, commas(... ts), ': any')] : [],
... items);
}
}