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

141 lines
5.1 KiB
TypeScript

import { refPosition } from "../reader";
import * as M from "../meta";
import { anglebrackets, braces, Item, keyvalue, opseq, seq } from "./block";
import { ModuleContext } from "./context";
import { stringify } from "@preserves/core";
export function typeFor(mod: ModuleContext, p: M.Pattern, variantName?: string): Item {
let typeItem: Item;
if (M.isSimplePattern(p)) {
typeItem = typeForSimple(mod, p);
} else {
switch (p.label) {
case M.$setof:
typeItem = seq(`_.KeyedSet`, anglebrackets(typeForSimple(mod, p[0]), '_ptr'));
break;
case M.$dictof:
typeItem = seq(`_.KeyedDictionary`, anglebrackets(typeForSimple(mod, p[0]),
typeForSimple(mod, p[1]),
'_ptr'));
break;
default: {
const arrayType = M.simpleArray(p);
if (arrayType === void 0) {
return braces(... variantInitFor(variantName),
... typeForCompound(mod, p));
} else {
typeItem = seq('Array<', typeForSimple(mod, arrayType), '>');
break;
}
}
}
}
if (variantName === void 0) {
return typeItem;
} else {
return braces(variantFor(variantName), keyvalue('value', typeItem));
}
}
function typeForSimple(mod: ModuleContext, p: M.SimplePattern): Item {
switch (p.label) {
case M.$atom:
switch (p[0]) {
case M.$Boolean: return `boolean`;
case M.$Float: return `_.SingleFloat`;
case M.$Double: return `_.DoubleFloat`;
case M.$SignedInteger: return `number`;
case M.$String: return `string`;
case M.$ByteString: return `_.Bytes`;
case M.$Symbol: return `symbol`;
}
case M.$pointer:
return `_ptr`;
case M.$lit:
return `(typeof ${mod.literal(p[0])})`;
case M.$ref:
return M.lookup(refPosition(p), p, mod.env,
(_p) => p[1].description!,
(p) => typeForAlternative(mod, p, void 0),
(modId, modPath,_p) => {
mod.imports.add([modId, modPath]);
return `${modId}.${p[1].description!}`;
});
default:
((_p: never) => {})(p);
throw new Error("Unreachable");
}
}
function typeField(mod: ModuleContext, n: M.NamedPattern): Item[] {
return (n.label === M.$named)
? [keyvalue(stringify(n[0]), typeForSimple(mod, n[1]))]
: (M.isCompoundPattern(n)
? typeForCompound(mod, n)
: []);
}
function typeForCompound(mod: ModuleContext, p: M.CompoundPattern): Item[] {
switch (p.label) {
case M.$rec:
return [... typeField(mod, p[0]), ... typeField(mod, p[1])];
case M.$tuple:
return p[0].flatMap(pp => typeField(mod, pp));
case M.$tuple_STAR_: {
const n = p[1];
return [... p[0].flatMap(pp => typeField(mod, pp)),
... ((n.label === M.$named)
? [keyvalue(stringify(n[0]),
seq('Array<', typeForSimple(mod, n[1]), '>'))]
: [])];
}
case M.$setof:
case M.$dictof:
return [];
case M.$dict:
return Array.from(p[0]).flatMap(([k, n]) => {
if (n.label === M.$named) {
return typeField(mod, n);
} else {
const s = M.namelike(k);
if (s !== void 0) {
return [keyvalue(JSON.stringify(s), typeForSimple(mod, n))];
} else {
return [];
}
}
});
default:
((_p: never) => {})(p);
throw new Error("Unreachable");
}
}
export function typeForDefinition(mod: ModuleContext, d: M.Definition): Item {
if (d.label === M.$or) {
return opseq('never', ' | ', ... d[0].map(a => typeForAlternative(mod, a[1], a[0])));
} else {
return typeForAlternative(mod, d, void 0);
}
}
export function variantInitFor(variantName: string | undefined) : Item[] {
return variantName === void 0 ? [] : [variantFor(variantName)];
}
export function variantFor(variantName: string): Item {
return keyvalue('_variant', JSON.stringify(variantName));
}
function typeForAlternative(mod: ModuleContext, a: M.Alternative, variantName: string | undefined): Item {
if (a.label === M.$and) {
return opseq('_val', ' & ',
... variantName === void 0 ? [] : [braces(variantFor(variantName))],
...a[0].map(p => typeFor(mod, M.unname(p))));
} else {
return typeFor(mod, a, variantName);
}
}