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

239 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 { Item, seq } from "./block";
import { simpleType, typeFor } from "../gentype";
2021-03-18 10:15:10 +00:00
import { refPosition } from "../reader";
import { ANY_TYPE, Type } from "../type";
2021-03-18 10:15:10 +00:00
export function converterForDefinition(
ctx: FunctionContext,
p: M.Definition,
src: string,
dest: string): Item[]
{
switch (p._variant) {
case 'or': {
const alts = [p.pattern0, p.pattern1, ... p.patternN];
function loop(i: number): Item[] {
ctx.variantName = alts[i].variantLabel;
return [... converterForPattern(ctx, alts[i].pattern, src, dest),
... ((i < alts.length - 1)
? [seq(`if (${dest} === void 0) `, ctx.block(() => loop(i + 1)))]
: [])];
}
return loop(0);
}
case 'and': {
const pcs = [p.pattern0, p.pattern1, ... p.patternN];
function loop(i: number): Item[] {
return (i < pcs.length)
? converterFor(ctx, pcs[i], src, () => loop(i + 1))
: [ctx.buildCapturedCompound(dest)];
}
return loop(0);
2021-03-18 10:15:10 +00:00
}
case 'Pattern':
ctx.variantName = void 0;
return converterForPattern(ctx, p.value, src, dest);
2021-03-18 10:15:10 +00:00
}
}
function converterForPattern(
ctx: FunctionContext,
p: M.Pattern,
src: string,
dest: string): Item[]
{
return converterFor(ctx, M.NamedPattern.anonymous(p), src, simpleValue => {
if (simpleValue === void 0) {
return [ctx.buildCapturedCompound(dest)];
} else if (ctx.variantName !== void 0) {
if (typeFor(ctx.mod.resolver(), p).kind === 'unit') {
return [ctx.buildCapturedCompound(dest)];
} else {
return [ctx.withCapture('value',
simpleValue,
() => ctx.buildCapturedCompound(dest))];
2021-03-18 10:15:10 +00:00
}
} else {
return [`${dest} = ${simpleValue}`];
}
});
}
function converterForTuple(ctx: FunctionContext,
ps: M.NamedPattern[],
src: string,
knownArray: 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 {
2021-03-19 22:42:43 +00:00
const vN = ctx.gentemp(Type.array(ANY_TYPE));
return [ps.length > 0 ? `${vN} = ${src}.slice(${ps.length})` : `${vN} = ${src}`,
... converterFor(ctx, M.promoteNamedSimplePattern(variablePattern), vN, k, true)];
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 knownArray
? loop(0)
: [seq(`if (_.Array.isArray(${src})`, lengthCheck, `) `, ctx.block(() => loop(0)))];
2021-03-18 10:15:10 +00:00
}
function converterFor(
ctx: FunctionContext,
np: M.NamedPattern,
2021-03-18 10:15:10 +00:00
src: string,
ks: (dest: string | undefined) => Item[],
knownArray = false): Item[]
2021-03-18 10:15:10 +00:00
{
2021-03-22 11:13:34 +00:00
let p = M.unnamePattern(np);
let maybeName = M.nameFor(np);
2021-03-18 10:15:10 +00:00
2021-03-22 11:13:34 +00:00
if (p._variant === 'SimplePattern') {
const dest = ctx.gentemp(simpleType(ctx.mod.resolver(), p.value));
return [... converterForSimple(ctx, p.value, src, dest, knownArray),
ctx.convertCapture(maybeName, dest, () => ks(dest))];
2021-03-18 10:15:10 +00:00
} else {
return converterForCompound(ctx, p.value, src, knownArray, () => ks(void 0));
2021-03-18 10:15:10 +00:00
}
}
2021-03-22 13:43:40 +00:00
export function converterForSimple(
2021-03-18 10:15:10 +00:00
ctx: FunctionContext,
p: M.SimplePattern,
src: string,
dest: string,
knownArray: boolean): Item[]
2021-03-18 10:15:10 +00:00
{
2021-03-22 11:13:34 +00:00
switch (p._variant) {
case 'any':
2021-03-19 22:42:43 +00:00
return [`${dest} = ${src}`];
2021-03-22 11:13:34 +00:00
case 'atom': {
2021-03-18 10:15:10 +00:00
let test: Item;
2021-03-23 10:36:55 +00:00
let valexp: Item = `${src}`;
2021-03-22 11:13:34 +00:00
switch (p.atomKind._variant) {
case 'Boolean': test = `typeof ${src} === 'boolean'`; break;
2021-03-23 10:36:55 +00:00
case 'Float': test = `_.Float.isSingle(${src})`; valexp = `${src}.value`; break;
case 'Double': test =`_.Float.isDouble(${src})`; valexp = `${src}.value`; break;
2021-03-22 11:13:34 +00:00
case 'SignedInteger': test = `typeof ${src} === 'number'`; break;
case 'String': test = `typeof ${src} === 'string'`; break;
case 'ByteString': test = `_.Bytes.isBytes(${src})`; break;
case 'Symbol': test = `typeof ${src} === 'symbol'`; break;
2021-03-18 10:15:10 +00:00
}
2021-03-23 10:36:55 +00:00
return [seq(`${dest} = `, test, ` ? `, valexp, ` : void 0`)];
2021-03-18 10:15:10 +00:00
}
case 'embedded':
return [`${dest} = _.isEmbedded<_embedded>(${src}) ? ${src}.embeddedValue : void 0`];
2021-03-22 11:13:34 +00:00
case 'lit':
return [`${dest} = _.is(${src}, ${ctx.mod.literal(p.value)}) ? null : void 0`];
case 'seqof': {
const kKnownArray = () => {
const v = ctx.gentempname();
return [
seq(`${dest} = []`),
seq(`for (const ${v} of ${src}) `, ctx.block(() => [
... converterFor(ctx, M.anonymousSimplePattern(p.pattern), v, vv =>
[`${dest}.push(${vv})`, `continue`]),
seq(`${dest} = void 0`),
seq(`break`)]))];
};
if (knownArray) {
return kKnownArray();
} else {
return [`${dest} = void 0`,
seq(`if (_.Array.isArray(${src})) `, ctx.block(kKnownArray))];
}
}
case 'setof':
return [`${dest} = void 0`,
seq(`if (_.Set.isSet<_embedded>(${src})) `, ctx.block(() => {
const v = ctx.gentempname();
return [
seq(`${dest} = new _.KeyedSet()`),
seq(`for (const ${v} of ${src}) `, ctx.block(() => [
... converterFor(ctx, M.anonymousSimplePattern(p.pattern), v, vv =>
[`${dest}.add(${vv})`, `continue`]),
seq(`${dest} = void 0`),
seq(`break`)]))];
}))];
case 'dictof':
return [`${dest} = void 0`,
seq(`if (_.Dictionary.isDictionary<_embedded>(${src})) `, ctx.block(() => {
const v = ctx.gentempname();
const k = ctx.gentempname();
return [
seq(`${dest} = new _.KeyedDictionary()`),
seq(`for (const [${k}, ${v}] of ${src}) `, ctx.block(() => [
... converterFor(ctx, M.anonymousSimplePattern(p.key), k, kk =>
converterFor(ctx, M.anonymousSimplePattern(p.value), v, vv =>
[`${dest}.set(${kk}, ${vv})`, `continue`])),
seq(`${dest} = void 0`),
seq(`break`)]))];
}))];
2021-03-22 11:13:34 +00:00
case 'Ref':
return M.lookup(refPosition(p.value), p.value, ctx.mod.env,
(_p) => [`${dest} = to${p.value.name.description!}(${src})`],
2021-03-18 10:15:10 +00:00
(modId, modPath,_p) => {
ctx.mod.imports.add([modId, modPath]);
2021-03-22 13:43:40 +00:00
return [`${dest} = ${modId}.to${p.value.name.description!}(${src})`];
2021-03-18 10:15:10 +00:00
});
default:
((_p: never) => {})(p);
throw new Error("Unreachable");
}
}
function converterForCompound(
ctx: FunctionContext,
p: M.CompoundPattern,
src: string,
knownArray: boolean,
ks: () => Item[]): Item[]
2021-03-18 10:15:10 +00:00
{
2021-03-22 11:13:34 +00:00
switch (p._variant) {
case 'rec':
return [seq(`if (_.Record.isRecord<_val, _.Tuple<_val>, _embedded>(${src})) `, ctx.block(() =>
2021-03-22 11:13:34 +00:00
converterFor(ctx, p.label, `${src}.label`, () =>
converterFor(ctx, p.fields, src, ks, true))))];
case 'tuple':
return converterForTuple(ctx, p.patterns, src, knownArray, void 0, ks);
2021-03-22 11:13:34 +00:00
case 'tuple*':
return converterForTuple(ctx, p.fixed, src, knownArray, p.variable, ks);
2021-03-22 11:13:34 +00:00
case 'dict': {
const entries = Array.from(p.entries);
2021-03-18 10:15:10 +00:00
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(() =>
2021-03-22 11:13:34 +00:00
converterFor(
ctx,
M.promoteNamedSimplePattern(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<_embedded>(${src})) `, ctx.block(() => loop(0)))];
2021-03-18 10:15:10 +00:00
}
default:
((_p: never) => {})(p);
throw new Error("Unreachable");
}
}