Expose a touch more static information, e.g. __as_preserve__ and __from_preserve__.
This commit is contained in:
parent
d8615175a5
commit
ae6eaa663e
|
@ -1,7 +1,7 @@
|
||||||
import { encode, stringify } from "@preserves/core";
|
import { encode, stringify } from "@preserves/core";
|
||||||
import * as M from "./meta";
|
import * as M from "./meta";
|
||||||
import { CompilerOptions, ModuleContext } from "./compiler/context";
|
import { CompilerOptions, ModuleContext } from "./compiler/context";
|
||||||
import { Formatter, block, seq, braces } from "./compiler/block";
|
import { Formatter, block, seq, braces, opseq } from "./compiler/block";
|
||||||
import { typeForDefinition } from "./compiler/gentype";
|
import { typeForDefinition } from "./compiler/gentype";
|
||||||
import { converterForDefinition } from "./compiler/genconverter";
|
import { converterForDefinition } from "./compiler/genconverter";
|
||||||
import { renderType } from "./compiler/rendertype";
|
import { renderType } from "./compiler/rendertype";
|
||||||
|
@ -32,19 +32,19 @@ export function compile(
|
||||||
for (const [name, def] of schema.definitions) {
|
for (const [name, def] of schema.definitions) {
|
||||||
const t = typeForDefinition(mod.resolver(), def);
|
const t = typeForDefinition(mod.resolver(), def);
|
||||||
const nameStr = stringify(name);
|
const nameStr = stringify(name);
|
||||||
const resultTypeItem = nameStr + mod.genericArgsFor(t);
|
const resultTypeItem = mod.withAsPreserveMixinType(nameStr, t);
|
||||||
|
|
||||||
mod.defineType(seq(`export type ${nameStr}`, mod.genericParametersFor(t),
|
mod.defineType(seq(`export type ${nameStr}`, mod.genericParametersFor(t),
|
||||||
` = `, renderType(mod, t), `;`));
|
` = `, renderType(mod, t), `;`));
|
||||||
|
|
||||||
if (t.kind === 'union') {
|
if (t.kind === 'union') {
|
||||||
mod.defineFunctions(_ctx =>
|
mod.defineFunctions(nameStr, _ctx =>
|
||||||
[seq(`export namespace ${nameStr} `, block(
|
[seq(`export namespace ${nameStr} `, block(
|
||||||
... Array.from(t.variants).flatMap(([vn, vt]) =>
|
... Array.from(t.variants).flatMap(([vn, vt]) =>
|
||||||
genConstructor(mod, nameStr, vn, vn, vt, t, resultTypeItem))
|
genConstructor(mod, nameStr, vn, vn, vt, t, resultTypeItem))
|
||||||
))]);
|
))]);
|
||||||
} else {
|
} else {
|
||||||
mod.defineFunctions(_ctx =>
|
mod.defineFunctions(nameStr, _ctx =>
|
||||||
genConstructor(mod, nameStr, nameStr, void 0, t, t, resultTypeItem));
|
genConstructor(mod, nameStr, nameStr, void 0, t, t, resultTypeItem));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,24 +52,30 @@ export function compile(
|
||||||
for (const [name0, def] of schema.definitions) {
|
for (const [name0, def] of schema.definitions) {
|
||||||
const t = typeForDefinition(mod.resolver(), def);
|
const t = typeForDefinition(mod.resolver(), def);
|
||||||
const name = name0 as symbol;
|
const name = name0 as symbol;
|
||||||
|
const nameStr = name0.description!;
|
||||||
|
const resultTypeItem = mod.withAsPreserveMixinType(nameStr, t);
|
||||||
|
|
||||||
mod.defineFunctions(ctx =>
|
mod.defineFunctions(nameStr, ctx =>
|
||||||
[seq(`export function as${name.description!}`, mod.genericParameters(),
|
[seq(`export function as${name.description!}`, mod.genericParameters(),
|
||||||
`(v: _.Value<_embedded>): `, name.description!, mod.genericArgsFor(t), ` `,
|
`(v: _.Value<_embedded>): `, resultTypeItem, ` `,
|
||||||
ctx.block(() => [
|
ctx.block(() => [
|
||||||
seq(`let result = to${name.description!}(v)`),
|
seq(`let result = to${name.description!}(v)`),
|
||||||
seq(`if (result === void 0) `,
|
seq(`if (result === void 0) `,
|
||||||
`throw new TypeError(\`Invalid ${name.description!}: \${_.stringify(v)}\`)`),
|
`throw new TypeError(\`Invalid ${name.description!}: \${_.stringify(v)}\`)`),
|
||||||
seq(`return result`)]))]);
|
seq(`return result`)]))]);
|
||||||
|
|
||||||
mod.defineFunctions(ctx =>
|
mod.defineFunctions(nameStr, ctx =>
|
||||||
[seq(`export function to${name.description!}`, mod.genericParameters(),
|
[seq(`export function to${name.description!}`, mod.genericParameters(),
|
||||||
`(v: _.Value<_embedded>): undefined | `, name.description!, mod.genericArgsFor(t), ` `,
|
`(v: _.Value<_embedded>): undefined | `, resultTypeItem, ` `,
|
||||||
ctx.block(() => [seq(`let result: undefined | `, name.description!, mod.genericArgsFor(t)),
|
ctx.block(() => [seq(`let result: undefined | `, resultTypeItem),
|
||||||
... converterForDefinition(ctx, def, `v`, `result`),
|
... converterForDefinition(ctx, def, `v`, `result`),
|
||||||
seq(`return result`)]))]);
|
seq(`return result`)])),
|
||||||
|
(t.kind === 'union')
|
||||||
|
? seq(`export namespace ${M.jsId(name.description!)} `, block(
|
||||||
|
seq(`export const __from_preserve__ = to${name.description!}`)))
|
||||||
|
: seq(`${M.jsId(name.description!)}.__from_preserve__ = to${name.description!};`)]);
|
||||||
|
|
||||||
mod.defineFunctions(ctx =>
|
mod.defineFunctions(nameStr, ctx =>
|
||||||
[seq(`export function from${name.description!}`, mod.genericParameters(),
|
[seq(`export function from${name.description!}`, mod.genericParameters(),
|
||||||
`(_v: `, name.description!, mod.genericArgsFor(t), `): _.Value<_embedded> `,
|
`(_v: `, name.description!, mod.genericArgsFor(t), `): _.Value<_embedded> `,
|
||||||
ctx.block(() => unconverterForDefinition(ctx, def, `_v`)))]);
|
ctx.block(() => unconverterForDefinition(ctx, def, `_v`)))]);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Dictionary, KeyedSet, FlexSet, Position, stringify, is } from "@preserves/core";
|
import { Dictionary, KeyedSet, FlexSet, Position, stringify, is } from "@preserves/core";
|
||||||
import { refPosition } from "../reader";
|
import { refPosition } from "../reader";
|
||||||
import * as M from "../meta";
|
import * as M from "../meta";
|
||||||
import { anglebrackets, block, braces, commas, formatItems, Item, keyvalue, seq } from "./block";
|
import { anglebrackets, block, braces, commas, formatItems, Item, keyvalue, seq, opseq } from "./block";
|
||||||
import { ANY_TYPE, RefType, Type } from "./type";
|
import { ANY_TYPE, RefType, Type } from "./type";
|
||||||
import { renderType, variantInitFor } from "./rendertype";
|
import { renderType, variantInitFor } from "./rendertype";
|
||||||
import { typeForDefinition } from "./gentype";
|
import { typeForDefinition } from "./gentype";
|
||||||
|
@ -89,8 +89,8 @@ export class ModuleContext {
|
||||||
this.typedefs.push(f);
|
this.typedefs.push(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
defineFunctions(f: (ctx: FunctionContext) => Item[]): void {
|
defineFunctions(definitionName: string, f: (ctx: FunctionContext) => Item[]): void {
|
||||||
this.functiondefs.push(... f(new FunctionContext(this)));
|
this.functiondefs.push(... f(new FunctionContext(this, definitionName)));
|
||||||
}
|
}
|
||||||
|
|
||||||
resolver(modulePath?: M.ModulePath): (ref: M.Ref) => RefType {
|
resolver(modulePath?: M.ModulePath): (ref: M.Ref) => RefType {
|
||||||
|
@ -209,10 +209,24 @@ export class ModuleContext {
|
||||||
|
|
||||||
return walk(t);
|
return walk(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
withAsPreserveMixinType(name: string, t: Type): Item {
|
||||||
|
if (t.kind === 'unit' || t.kind === 'record' || t.kind === 'union') {
|
||||||
|
return opseq('any', ' & ',
|
||||||
|
seq(name, this.genericArgsFor(t)),
|
||||||
|
braces(seq('__as_preserve__',
|
||||||
|
this.hasEmbedded(t) ? '' : this.genericParameters(),
|
||||||
|
'()',
|
||||||
|
': _.Value', this.genericArgs())));
|
||||||
|
} else {
|
||||||
|
return seq(name, this.genericArgsFor(t));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FunctionContext {
|
export class FunctionContext {
|
||||||
readonly mod: ModuleContext;
|
readonly mod: ModuleContext;
|
||||||
|
readonly definitionName: string;
|
||||||
|
|
||||||
tempCounter = 0;
|
tempCounter = 0;
|
||||||
temps: Map<string, { type: Item, names: string[] }> = new Map();
|
temps: Map<string, { type: Item, names: string[] }> = new Map();
|
||||||
|
@ -220,8 +234,9 @@ export class FunctionContext {
|
||||||
captures: Capture[] = [];
|
captures: Capture[] = [];
|
||||||
variantName: string | undefined = void 0;
|
variantName: string | undefined = void 0;
|
||||||
|
|
||||||
constructor(mod: ModuleContext) {
|
constructor(mod: ModuleContext, definitionName: string) {
|
||||||
this.mod = mod;
|
this.mod = mod;
|
||||||
|
this.definitionName = definitionName;
|
||||||
}
|
}
|
||||||
|
|
||||||
gentempname(): string {
|
gentempname(): string {
|
||||||
|
@ -273,9 +288,10 @@ export class FunctionContext {
|
||||||
const fields = [
|
const fields = [
|
||||||
... variantInitFor(this.variantName),
|
... variantInitFor(this.variantName),
|
||||||
... this.captures.map(({ fieldName, sourceExpr }) =>
|
... this.captures.map(({ fieldName, sourceExpr }) =>
|
||||||
keyvalue(fieldName, sourceExpr))
|
keyvalue(fieldName, sourceExpr)),
|
||||||
|
seq(`__as_preserve__() `, block(`return from${this.definitionName}(this)`))
|
||||||
];
|
];
|
||||||
return seq(`${dest} = `, fields.length === 0 ? `null` : braces(... fields));
|
return seq(`${dest} = `, braces(... fields));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,11 @@ function converterForPattern(
|
||||||
() => ctx.buildCapturedCompound(dest))];
|
() => ctx.buildCapturedCompound(dest))];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return [`${dest} = ${simpleValue}`];
|
if (typeFor(ctx.mod.resolver(), p).kind === 'unit') {
|
||||||
|
return [ctx.buildCapturedCompound(dest)];
|
||||||
|
} else {
|
||||||
|
return [`${dest} = ${simpleValue}`];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -136,7 +140,7 @@ export function converterForSimple(
|
||||||
case 'embedded':
|
case 'embedded':
|
||||||
return [`${dest} = _.isEmbedded<_embedded>(${src}) ? ${src}.embeddedValue : void 0`];
|
return [`${dest} = _.isEmbedded<_embedded>(${src}) ? ${src}.embeddedValue : void 0`];
|
||||||
case 'lit':
|
case 'lit':
|
||||||
return [`${dest} = _.is(${src}, ${ctx.mod.literal(p.value)}) ? null : void 0`];
|
return [`${dest} = _.is(${src}, ${ctx.mod.literal(p.value)}) ? {} : void 0`];
|
||||||
|
|
||||||
case 'seqof': {
|
case 'seqof': {
|
||||||
const kKnownArray = () => {
|
const kKnownArray = () => {
|
||||||
|
|
|
@ -26,7 +26,7 @@ export function genConstructor(
|
||||||
arg.fields.forEach(examine);
|
arg.fields.forEach(examine);
|
||||||
} else {
|
} else {
|
||||||
examine(arg, 'value');
|
examine(arg, 'value');
|
||||||
simpleValue = variant === void 0;
|
simpleValue = (variant === void 0) && (arg.kind !== 'unit');
|
||||||
}
|
}
|
||||||
|
|
||||||
const initializers: Item[] = (variant !== void 0)
|
const initializers: Item[] = (variant !== void 0)
|
||||||
|
@ -34,6 +34,8 @@ export function genConstructor(
|
||||||
: [];
|
: [];
|
||||||
formals.forEach(([n, _t]) => initializers.push(seq(JSON.stringify(n), ': ', M.jsId(n))));
|
formals.forEach(([n, _t]) => initializers.push(seq(JSON.stringify(n), ': ', M.jsId(n))));
|
||||||
|
|
||||||
|
initializers.push(seq(`__as_preserve__() `, block(`return from${M.jsId(definitionName)}(this)`)));
|
||||||
|
|
||||||
const declArgs: Array<Item> = (formals.length > 1)
|
const declArgs: Array<Item> = (formals.length > 1)
|
||||||
? [seq(braces(...formals.map(f => M.jsId(f[0]))), ': ',
|
? [seq(braces(...formals.map(f => M.jsId(f[0]))), ': ',
|
||||||
braces(...formals.map(f => seq(M.jsId(f[0]), ': ', renderType(mod, f[1])))))]
|
braces(...formals.map(f => seq(M.jsId(f[0]), ': ', renderType(mod, f[1])))))]
|
||||||
|
@ -44,20 +46,15 @@ export function genConstructor(
|
||||||
parens(...declArgs),
|
parens(...declArgs),
|
||||||
': ', resultTypeItem, ' ', block(
|
': ', resultTypeItem, ' ', block(
|
||||||
seq(`return `,
|
seq(`return `,
|
||||||
((arg.kind === 'unit' && initializers.length === 0)
|
(simpleValue
|
||||||
? 'null'
|
? 'value'
|
||||||
: (simpleValue
|
: braces(...initializers))))),
|
||||||
? 'value'
|
|
||||||
: braces(...initializers)))))),
|
|
||||||
seq(`${M.jsId(name)}.schema = function () `, block(
|
seq(`${M.jsId(name)}.schema = function () `, block(
|
||||||
seq(`return `, braces(
|
seq(`return `, braces(
|
||||||
`schema: _schema()`,
|
`schema: _schema()`,
|
||||||
`imports: _imports`,
|
`imports: _imports`,
|
||||||
`definitionName: _.Symbol.for(${JSON.stringify(definitionName)})`,
|
`definitionName: _.Symbol.for(${JSON.stringify(definitionName)})`,
|
||||||
... (variant === void 0) ? [] : [`variant: _.Symbol.for(${JSON.stringify(variant)})`],
|
... (variant === void 0) ? [] : [`variant: _.Symbol.for(${JSON.stringify(variant)})`],
|
||||||
`unparse: from${definitionName}`,
|
|
||||||
`parse: to${definitionName}`,
|
|
||||||
`cast: as${definitionName}`,
|
|
||||||
)))),
|
)))),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,33 +10,37 @@ export function variantFor(variantName: string): Item {
|
||||||
return keyvalue('_variant', JSON.stringify(variantName));
|
return keyvalue('_variant', JSON.stringify(variantName));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderVariant(ctxt: ModuleContext, [variantName, t]: [string, SimpleType]): Item {
|
function simpleTypeFields(ctxt: ModuleContext, baseType: Type, t: SimpleType): Item[] {
|
||||||
let fields: Item[];
|
|
||||||
switch (t.kind) {
|
switch (t.kind) {
|
||||||
case 'unit':
|
case 'unit':
|
||||||
fields = [];
|
return [];
|
||||||
break;
|
|
||||||
case 'ref':
|
case 'ref':
|
||||||
case 'set':
|
case 'set':
|
||||||
case 'dictionary':
|
case 'dictionary':
|
||||||
case 'array':
|
case 'array':
|
||||||
fields = [keyvalue('value', renderType(ctxt, t))];
|
return [keyvalue('value', renderType(ctxt, t))];
|
||||||
break;
|
|
||||||
case 'record':
|
case 'record':
|
||||||
fields = Array.from(t.fields).map(([nn, tt]) => keyvalue(nn, renderType(ctxt, tt)));
|
return Array.from(t.fields).map(([nn, tt]) => keyvalue(nn, renderType(ctxt, tt)));
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
((_: never) => {})(t);
|
((_: never) => {})(t);
|
||||||
throw new Error("Unreachable");
|
throw new Error("Unreachable");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderVariant(
|
||||||
|
ctxt: ModuleContext,
|
||||||
|
baseType: Type,
|
||||||
|
[variantName, t]: [string, SimpleType],
|
||||||
|
): Item {
|
||||||
|
let fields = simpleTypeFields(ctxt, baseType, t);
|
||||||
return braces(variantFor(variantName), ... fields);
|
return braces(variantFor(variantName), ... fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderType(ctxt: ModuleContext, t: Type): Item {
|
export function renderType(ctxt: ModuleContext, t: Type): Item {
|
||||||
switch (t.kind) {
|
switch (t.kind) {
|
||||||
case 'union': return opseq('never', ' | ', ...
|
case 'union': return opseq('never', ' | ', ...
|
||||||
Array.from(t.variants).flatMap(entry => renderVariant(ctxt, entry)));
|
Array.from(t.variants).flatMap(entry => renderVariant(ctxt, t, entry)));
|
||||||
case 'unit': return 'null';
|
case 'unit': return braces(... simpleTypeFields(ctxt, t, t));
|
||||||
case 'ref':
|
case 'ref':
|
||||||
if (t.ref === null && t.typeName === '_embedded') {
|
if (t.ref === null && t.typeName === '_embedded') {
|
||||||
return t.typeName;
|
return t.typeName;
|
||||||
|
@ -51,8 +55,7 @@ export function renderType(ctxt: ModuleContext, t: Type): Item {
|
||||||
renderType(ctxt, t.value),
|
renderType(ctxt, t.value),
|
||||||
'_embedded'));
|
'_embedded'));
|
||||||
case 'array': return seq('Array', anglebrackets(renderType(ctxt, t.type)));
|
case 'array': return seq('Array', anglebrackets(renderType(ctxt, t.type)));
|
||||||
case 'record': return braces(... Array.from(t.fields).map(([nn, tt]) =>
|
case 'record': return braces(... simpleTypeFields(ctxt, t, t));
|
||||||
keyvalue(nn, renderType(ctxt, tt))));
|
|
||||||
default:
|
default:
|
||||||
((_: never) => {})(t);
|
((_: never) => {})(t);
|
||||||
throw new Error("Unreachable");
|
throw new Error("Unreachable");
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,10 @@
|
||||||
|
import type { Value } from '@preserves/core';
|
||||||
|
|
||||||
|
export type Imports = { [modulePath: string]: any }; // TODO: better than any
|
||||||
|
|
||||||
|
export interface SchemaDefinition {
|
||||||
|
schema: Value,
|
||||||
|
imports: Imports,
|
||||||
|
definitionName: symbol,
|
||||||
|
variant: symbol | undefined,
|
||||||
|
}
|
Loading…
Reference in New Issue