import { Annotated, Bytes, Dictionary, Fold, fold, Record, stringify, Tuple, Value } from "@preserves/core"; import * as M from "./meta"; import { CompilerOptions, ModuleContext } from "./compiler/context"; import { block, brackets, Formatter, Item, parens, seq } from "./compiler/block"; import { typeForDefinition } from "./compiler/gentype"; // import { decoderFor } from "./compiler/decoder"; import { converterForDefinition } from "./compiler/genconverter"; import { EMPTY_TYPE, renderType } from "./compiler/type"; import { genConstructor } from "./compiler/genctor"; export function compile(env: M.Environment, schema: M.Schema, options: CompilerOptions = {}): string { const mod = new ModuleContext(env, schema, options); const pointerName = M.Schema._._field0(schema).get(M.$pointer); mod.defineType(seq(`export type _ptr = `, renderType(pointerName === false ? EMPTY_TYPE : typeForDefinition(mod, pointerName)), `;`)); mod.defineType(`export type _val = _.Value<_ptr>;`); mod.defineFunction(ctx => seq(`export const _decodePtr = `, (pointerName === false ? '() => { throw new _.DecodeError("Pointers forbidden"); }' : seq(`(d: _.TypedDecoder<_ptr>) => `, ctx.block(() => [ seq(`let result`), seq(`/* TODO */`), // ... decoderFor(ctx, pointerName, 'result'), seq(`return result`)]))), `;`)); for (const [name, def] of M.Schema._._field0(schema).get(M.$definitions)) { const t = typeForDefinition(mod, def); const nameStr = stringify(name); mod.defineType(seq(`export type ${nameStr} = `, renderType(t), `;`)); if (t.kind === 'union') { mod.defineFunction(_ctx => seq(`export namespace ${nameStr} `, block( ... Array.from(t.variants).map(([vn, vt]) => genConstructor(vn, vn, vt, nameStr)) ))); } else { mod.defineFunction(_ctx => genConstructor(nameStr, void 0, t, nameStr)); } } for (const [name0, def] of M.Schema._._field0(schema).get(M.$definitions)) { const name = name0 as symbol; mod.defineFunction(ctx => seq(`export function as${name.description!}`, '(v: _val): ', name.description!, ' ', ctx.block(() => [ seq(`let result = to${name.description!}(v)`), seq(`if (result === void 0) `, `throw new TypeError(\`Invalid ${name.description!}: \${_.stringify(v)}\`)`), seq(`return result`)]))); mod.defineFunction(ctx => seq(`export function to${name.description!}`, '(v: _val): undefined | ', name.description!, ' ', ctx.block(() => [seq(`let result: undefined | `, name.description!), ... converterForDefinition(ctx, def, 'v', 'result'), seq(`return result`)]))); // mod.defineFunction(ctx => // seq(`export function decode${name.description!}`, // `(d: _.TypedDecoder<_ptr>): undefined | `, name.description!, // ctx.block(() => [seq(`let result: undefined | `, name.description!), // seq(`/* TODO */`), // // ... decoderFor(ctx, def, 'result'), // seq(`return result`)]))); } const f = new Formatter(); f.write(`import * as _ from ${JSON.stringify(options.preservesModule ?? '@preserves/core')};\n`); mod.imports.forEach(([identifier, path]) => { f.write(`import * as ${identifier} from ${JSON.stringify(path)};\n`); }); f.newline(); const sortedLiterals = Array.from(mod.literals); sortedLiterals.sort((a, b) => a[1] < b[1] ? -1 : a[1] === b[1] ? 0 : 1); for (const [lit, varname] of sortedLiterals) { f.write(seq(`export const ${varname} = `, sourceCodeFor(lit), `;\n`)); } f.newline(); mod.typedefs.forEach(t => { f.write(t); f.newline(); f.newline(); }); f.newline(); mod.functiondefs.forEach(p => { f.write(p); f.newline(); f.newline(); }); return f.toString(); } export function sourceCodeFor(v: Value): Item { return fold(v, { boolean(b: boolean): Item { return b.toString(); }, single(f: number): Item { return f.toString(); }, double(f: number): Item { return f.toString(); }, integer(i: number): Item { return i.toString(); }, string(s: string): Item { return JSON.stringify(s); }, bytes(b: Bytes): Item { return seq(`Uint8Array.from(`, brackets(... Array.from(b).map(b => b.toString())), `)`); }, symbol(s: symbol): Item { return `Symbol.for(${JSON.stringify(s.description!)})`; }, record(r: Record, Tuple>, any>, k: Fold): Item { return seq(`_.Record<_val, _.Tuple<_val>, _ptr>`, parens(k(r.label), brackets(... r.map(k)))); }, array(a: Array>, k: Fold): Item { return brackets(... a.map(k)); }, set(s: Set, k: Fold): Item { return seq('new _.Set<_val>', parens(brackets(... Array.from(s).map(k)))); }, dictionary(d: Dictionary, k: Fold): Item { return seq('new _.Dictionary<_ptr>', parens(brackets(... Array.from(d).map(([kk,vv]) => brackets(k(kk), k(vv)))))); }, annotated(a: Annotated, k: Fold): Item { return seq('_.annotate<_ptr>', parens(k(a.item), ... a.annotations.map(k))); }, pointer(t: any, _k: Fold): Item { throw new Error(`Cannot emit source code for construction of pointer ${stringify(t)}`); }, }); }