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

125 lines
5.2 KiB
TypeScript

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 { brackets, Formatter, Item, parens, seq } from "./compiler/block";
import { typeForDefinition } from "./compiler/type";
import { decoderFor } from "./compiler/decoder";
import { converterForDefinition } from "./compiler/converter";
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 = `,
pointerName === false ? 'never' : 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`),
... decoderFor(ctx, pointerName, 'result'),
seq(`return result`)]))),
`;`));
for (const [name, def] of M.Schema._._field0(schema).get(M.$definitions)) {
mod.defineType(
seq(`export type ${stringify(name)} = `, typeForDefinition(mod, def), `;`));
}
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: any): ', 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: any): ', name.description!, ' | undefined ',
ctx.block(() => [seq(`let result`),
... converterForDefinition(ctx, def, 'v', 'result'),
seq(`return result`)])));
mod.defineFunction(ctx =>
seq(`export function decode${name.description!}`,
`(d: _.TypedDecoder<_ptr>): `, name.description!, ` | undefined `,
ctx.block(() => [seq(`let result`),
... 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<any>): 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<Value<any>, Tuple<Value<any>>, any>, k: Fold<any, Item>): Item {
return seq(`_.Record<_val, _.Tuple<_val>, _ptr>`, parens(k(r.label), brackets(... r.map(k))));
},
array(a: Array<Value<any>>, k: Fold<any, Item>): Item {
return brackets(... a.map(k));
},
set(s: Set<any>, k: Fold<any, Item>): Item {
return seq('new _.Set<_val>', parens(brackets(... Array.from(s).map(k))));
},
dictionary(d: Dictionary<any>, k: Fold<any, Item>): Item {
return seq('new _.Dictionary<_ptr>', parens(brackets(... Array.from(d).map(([kk,vv]) =>
brackets(k(kk), k(vv))))));
},
annotated(a: Annotated<any>, k: Fold<any, Item>): Item {
return seq('_.annotate<_ptr>', parens(k(a.item), ... a.annotations.map(k)));
},
pointer(t: any, _k: Fold<any, Item>): Item {
throw new Error(`Cannot emit source code for construction of pointer ${stringify(t)}`);
},
});
}