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

95 lines
3.7 KiB
TypeScript
Raw Normal View History

2021-03-23 11:08:58 +00:00
import { stringify } from "@preserves/core";
2021-03-18 10:15:10 +00:00
import * as M from "./meta";
import { CompilerOptions, ModuleContext } from "./compiler/context";
2021-03-23 11:08:58 +00:00
import { Formatter, block, seq } from "./compiler/block";
2021-03-19 22:42:43 +00:00
import { typeForDefinition } from "./compiler/gentype";
import { converterForDefinition } from "./compiler/genconverter";
import { renderType, Type } from "./compiler/type";
2021-03-21 21:02:34 +00:00
import { genConstructor } from "./compiler/genctor";
2021-03-23 10:36:55 +00:00
import { unconverterForDefinition } from "./compiler/genunconverter";
import { sourceCodeFor } from "./compiler/value";
2021-03-18 10:15:10 +00:00
export function compile(env: M.Environment, schema: M.Schema, options: CompilerOptions = {}): string {
2021-03-17 14:59:46 +00:00
const mod = new ModuleContext(env, schema, options);
2021-03-09 14:59:40 +00:00
const embeddedName = schema.embeddedType;
mod.defineType(seq(`export type _embedded = `,
renderType(embeddedName._variant === 'false'
? Type.ref('any')
: typeForDefinition(mod, M.Definition.Pattern(M.Pattern.SimplePattern(M.SimplePattern.Ref(embeddedName.value))))),
2021-03-17 15:25:29 +00:00
`;`));
mod.defineType(`export type _val = _.Value<_embedded>;`);
2021-03-17 15:25:29 +00:00
2021-03-22 11:13:34 +00:00
for (const [name, def] of schema.definitions) {
2021-03-21 21:02:34 +00:00
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));
}
2021-03-17 15:25:29 +00:00
}
2021-03-22 11:13:34 +00:00
for (const [name0, def] of schema.definitions) {
2021-03-17 15:25:29 +00:00
const name = name0 as symbol;
2021-03-17 14:59:46 +00:00
mod.defineFunction(ctx =>
seq(`export function as${name.description!}`,
'(v: _val): ', name.description!, ' ',
2021-03-17 14:59:46 +00:00
ctx.block(() => [
2021-03-18 10:15:10 +00:00
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!),
2021-03-18 10:15:10 +00:00
... converterForDefinition(ctx, def, 'v', 'result'),
seq(`return result`)])));
2021-03-23 10:36:55 +00:00
mod.defineFunction(ctx =>
seq(`export function from${name.description!}`,
2021-03-23 11:08:58 +00:00
'(_v: ', name.description!, '): _val ',
ctx.block(() => unconverterForDefinition(ctx, name.description!, def, '_v'))));
2021-03-09 14:59:40 +00:00
}
const f = new Formatter();
2021-03-11 16:59:40 +00:00
f.write(`import * as _ from ${JSON.stringify(options.preservesModule ?? '@preserves/core')};\n`);
2021-03-17 14:59:46 +00:00
mod.imports.forEach(([identifier, path]) => {
f.write(`import * as ${identifier} from ${JSON.stringify(path)};\n`);
});
2021-03-09 14:59:40 +00:00
f.newline();
2021-03-17 14:59:46 +00:00
const sortedLiterals = Array.from(mod.literals);
2021-03-09 15:45:57 +00:00
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`));
2021-03-09 14:59:40 +00:00
}
f.newline();
2021-03-17 14:59:46 +00:00
mod.typedefs.forEach(t => {
2021-03-09 14:59:40 +00:00
f.write(t);
f.newline();
2021-03-09 15:45:57 +00:00
f.newline();
2021-03-09 14:59:40 +00:00
});
f.newline();
2021-03-17 14:59:46 +00:00
mod.functiondefs.forEach(p => {
2021-03-09 14:59:40 +00:00
f.write(p);
f.newline();
f.newline();
});
return f.toString();
}