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

248 lines
9.6 KiB
TypeScript
Raw Normal View History

2021-03-18 10:15:10 +00:00
import { FunctionContext } from "./context";
import * as M from '../meta';
import { block, Item, seq } from "./block";
import { typeFor } from "./type";
2021-03-18 10:15:10 +00:00
import { refPosition } from "../reader";
export function converterForDefinition(
ctx: FunctionContext,
p: M.Definition,
src: string,
dest: string): Item[]
{
if (p.label === M.$or) {
const alts = p[0];
function loop(i: number): Item[] {
ctx.variantName = alts[i][0];
return [... converterForAlternative(ctx, alts[i][1], src, dest),
... ((i < alts.length - 1)
? [seq(`if (${dest} === void 0) `, ctx.block(() => loop(i + 1)))]
: [])];
2021-03-18 10:15:10 +00:00
}
return alts.length === 0 ? [] : loop(0);
2021-03-18 10:15:10 +00:00
} else {
ctx.variantName = void 0;
return converterForAlternative(ctx, p, src, dest);
2021-03-18 10:15:10 +00:00
}
}
function converterForAlternative(
ctx: FunctionContext,
p: M.Alternative,
src: string,
dest: string): Item[]
{
2021-03-18 10:15:10 +00:00
if (p.label === M.$and) {
const alts = p[0];
function loop(i: number): Item[] {
return (i < alts.length)
? converterFor(ctx, alts[i], src, () => loop(i + 1))
: [ctx.buildCapturedCompound(dest)];
}
return alts.length === 0 ? [seq(`${dest} = ${src}`)] : loop(0);
} else {
return converterFor(ctx, p, src, simpleValue => {
if (simpleValue === void 0) {
return [ctx.buildCapturedCompound(dest)];
} else if (ctx.variantName !== void 0) {
return [ctx.withCapture('value',
simpleValue,
2021-03-18 21:41:27 +00:00
() => ctx.buildCapturedCompound(dest))];
} else {
return [`${dest} = ${simpleValue}`];
2021-03-18 10:15:10 +00:00
}
});
}
}
function converterForTuple(ctx: FunctionContext,
ps: M.NamedPattern[],
src: string,
recordFields: boolean,
variablePattern: M.NamedSimplePattern | undefined,
k: () => Item[]): Item[]
{
function loop(i: number): Item[] {
if (i < ps.length) {
return converterFor(ctx, ps[i], `${src}[${i}]`, () => loop(i + 1));
} else {
if (variablePattern === void 0) {
return k();
} else {
const vN = ctx.gentemp(`Array<_val>`);
return [ps.length > 0 ? `${vN} = ${src}.slice(${ps.length})` : `${vN} = ${src}`,
converterForArray(ctx, variablePattern, vN, false, k)];
2021-03-18 10:15:10 +00:00
}
}
}
const lengthCheck = variablePattern === void 0
? seq(` && ${src}.length === ${ps.length}`)
: ((ps.length === 0) ? '' : seq(` && ${src}.length >= ${ps.length}`));
return recordFields
? loop(0)
: [seq(`if (_.Array.isArray(${src})`, lengthCheck, `) `, ctx.block(() => loop(0)))];
2021-03-18 10:15:10 +00:00
}
function converterForArray(ctx: FunctionContext,
arrayType: M.NamedSimplePattern,
2021-03-18 10:15:10 +00:00
src: string,
checkArray: boolean,
k: (dest: string) => Item[]): Item
2021-03-18 10:15:10 +00:00
{
const postCheck = () => {
const r = ctx.gentemp(
`Array<`, typeFor(ctx.mod, M.unname(arrayType)), `> | undefined`);
const v = ctx.gentempname();
return [
seq(`${r} = []`),
seq(`for (const ${v} of ${src}) `, ctx.block(() => [
... converterFor(ctx, arrayType, v, vv => [`${r}.push(${vv})`, `continue`]),
seq(`${r} = void 0`),
seq(`break`)])),
2021-03-18 21:41:27 +00:00
ctx.convertCapture(M.nameFor(arrayType), r, k)];
};
2021-03-18 10:15:10 +00:00
return (checkArray
? seq(`if (_.Array.isArray(${src})) `, ctx.block(postCheck))
: block(... postCheck()));
}
function converterFor(
ctx: FunctionContext,
np: M.NamedPattern,
2021-03-18 10:15:10 +00:00
src: string,
ks: (dest: string | undefined) => Item[],
2021-03-18 10:15:10 +00:00
recordFields = false): Item[]
{
let p = M.unname(np);
let maybeName = M.nameFor(np);
2021-03-18 10:15:10 +00:00
if (M.isSimplePattern(p)) {
const dest = ctx.gentemp(typeFor(ctx.mod, p), ` | undefined`);
return [... converterForSimple(ctx, p, src, dest),
2021-03-18 21:41:27 +00:00
ctx.convertCapture(maybeName, dest, ks)];
2021-03-18 10:15:10 +00:00
} else {
switch (p.label) {
case M.$setof: {
const setPattern = p[0];
const r = ctx.gentemp(typeFor(ctx.mod, p), ` | undefined`);
const v = ctx.gentempname();
return [
seq(`if (_.Set.isSet<_ptr>(${src})) `, ctx.block(() => [
seq(`${r} = new _.KeyedSet()`),
seq(`for (const ${v} of ${src}) `, ctx.block(() => [
... converterFor(ctx, setPattern, v, vv =>
[`${r}.add(${vv})`, `continue`]),
seq(`${r} = void 0`),
seq(`break`)])),
2021-03-18 21:41:27 +00:00
ctx.convertCapture(maybeName, r, ks)]))];
}
case M.$dictof: {
const keyPattern = p[0];
const valPattern = p[1];
const r = ctx.gentemp(typeFor(ctx.mod, p), ` | undefined`);
const v = ctx.gentempname();
const k = ctx.gentempname();
return [
seq(`if (_.Dictionary.isDictionary<_ptr>(${src})) `, ctx.block(() => [
seq(`${r} = new _.KeyedDictionary()`),
seq(`for (const [${k}, ${v}] of ${src}) `, ctx.block(() => [
... converterFor(ctx, keyPattern, k, kk =>
converterFor(ctx, valPattern, v, vv =>
[`${r}.set(${kk}, ${vv})`, `continue`])),
seq(`${r} = void 0`),
seq(`break`)])),
2021-03-18 21:41:27 +00:00
ctx.convertCapture(maybeName, r, ks)]))];
}
2021-03-18 10:15:10 +00:00
default: {
const arrayType = M.simpleArray(p);
if (arrayType === void 0) {
return converterForCompound(ctx, p, src, recordFields, () => ks(void 0));
2021-03-18 10:15:10 +00:00
} else {
return [converterForArray(ctx, arrayType, src, !recordFields, ks)];
2021-03-18 10:15:10 +00:00
}
}
}
}
}
function converterForSimple(
ctx: FunctionContext,
p: M.SimplePattern,
src: string,
dest: string): Item[]
{
switch (p.label) {
case M.$atom: {
let test: Item;
switch (p[0]) {
case M.$Boolean: test = `typeof ${src} === 'boolean'`; break;
case M.$Float: test = `_.Float.isSingle(${src})`; break;
case M.$Double: test =`_.Float.isDouble(${src})`; break;
case M.$SignedInteger: test = `typeof ${src} === 'number'`; break;
case M.$String: test = `typeof ${src} === 'string'`; break;
case M.$ByteString: test = `_.Bytes.isBytes(${src})`; break;
case M.$Symbol: test = `typeof ${src} === 'symbol'`; break;
}
return [seq(`${dest} = `, test, ` ? ${src} : void 0`)];
}
case M.$lit:
return [`${dest} = _.is(${src}, ${ctx.mod.literal(p[0])}) ? ${ctx.mod.literal(p[0])} : void 0`];
2021-03-18 10:15:10 +00:00
case M.$ref:
return M.lookup(refPosition(p), p, ctx.mod.env,
(_p) => [`${dest} = to${p[1].description!}(${src})`],
(p) => converterForAlternative(ctx, p, src, dest),
2021-03-18 10:15:10 +00:00
(modId, modPath,_p) => {
ctx.mod.imports.add([modId, modPath]);
return [`${dest} = ${modId}.decode${p[1].description!}(${src})`];
});
case M.$pointer:
return [`${dest} = _toPtr(${src})`];
default:
((_p: never) => {})(p);
throw new Error("Unreachable");
}
}
function converterForCompound(
ctx: FunctionContext,
p: M.CompoundPattern,
src: string,
recordFields: boolean,
ks: () => Item[]): Item[]
2021-03-18 10:15:10 +00:00
{
switch (p.label) {
case M.$rec:
return [seq(`if (_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(${src})) `, ctx.block(() =>
converterFor(ctx, p[0], `${src}.label`, () =>
converterFor(ctx, p[1], src, ks, true))))];
2021-03-18 10:15:10 +00:00
case M.$tuple:
return converterForTuple(ctx, p[0], src, recordFields, void 0, ks);
2021-03-18 10:15:10 +00:00
case M.$tuple_STAR_:
return converterForTuple(ctx, p[0], src, recordFields, p[1], ks);
2021-03-18 10:15:10 +00:00
case M.$setof:
case M.$dictof:
throw new Error('Internal error: setof and dictof are handled in converterFor()');
case M.$dict: {
const entries = Array.from(p[0]);
function loop(i: number): Item[] {
if (i < entries.length) {
const [k, n] = entries[i];
const tmpSrc = ctx.gentemp();
return [seq(`if ((${tmpSrc} = ${src}.get(${ctx.mod.literal(k)})) !== void 0) `,
ctx.block(() =>
converterFor(ctx, M.addNameIfAbsent(n, k), tmpSrc, () =>
loop(i + 1))))];
2021-03-18 10:15:10 +00:00
} else {
return ks();
2021-03-18 10:15:10 +00:00
}
}
return [seq(`if (_.Dictionary.isDictionary<_ptr>(${src})) `, ctx.block(() => loop(0)))];
2021-03-18 10:15:10 +00:00
}
default:
((_p: never) => {})(p);
throw new Error("Unreachable");
}
}