2021-03-23 10:36:55 +00:00
|
|
|
import { refPosition } from '../reader';
|
|
|
|
import * as M from '../meta';
|
|
|
|
import { block, brackets, formatItems, Item, parens, seq } from './block';
|
|
|
|
import { FunctionContext } from "./context";
|
|
|
|
import { typeForAlternative } from './gentype';
|
|
|
|
import { FieldType, renderType, SimpleType } from './type';
|
|
|
|
|
|
|
|
export function unconverterForDefinition(
|
|
|
|
ctx: FunctionContext,
|
|
|
|
name: string,
|
|
|
|
def: M.Definition,
|
|
|
|
src: string): Item[]
|
|
|
|
{
|
|
|
|
if (def._variant === 'or') {
|
|
|
|
return [seq(`switch (${src}._variant) `, block(
|
|
|
|
... def.patterns.map(p =>
|
|
|
|
seq(`case `, JSON.stringify(p.variantLabel), `: `, ctx.block(() => {
|
|
|
|
const hasValueField =
|
|
|
|
p.alternative._variant === 'Pattern' &&
|
|
|
|
p.alternative.value._variant === 'SimplePattern';
|
|
|
|
return [seq(`return `, unconverterForAlternative(
|
|
|
|
ctx, name, p.alternative, hasValueField ? `${src}.value` : src))];
|
|
|
|
})))))];
|
|
|
|
} else {
|
|
|
|
return [seq(`return `, unconverterForAlternative(ctx, name, def.value, `${src}`))];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class InsufficientInformationError extends Error {}
|
|
|
|
|
|
|
|
function unconverterForAlternative(
|
|
|
|
ctx: FunctionContext,
|
|
|
|
name: string,
|
|
|
|
a: M.Alternative,
|
|
|
|
src: string): Item
|
|
|
|
{
|
|
|
|
const t = typeForAlternative(ctx.mod, a);
|
|
|
|
if (a._variant === 'and') {
|
|
|
|
const errs: [string, InsufficientInformationError][] = [];
|
|
|
|
const cs = a.patterns.flatMap(p => {
|
|
|
|
if (p._variant === 'anonymous' && p.value._variant === 'SimplePattern') {
|
|
|
|
return [];
|
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
return [unconverterForNamed(ctx, p, src, t)];
|
|
|
|
} catch (e) {
|
|
|
|
if (e instanceof InsufficientInformationError) {
|
|
|
|
errs.push([name + '/' + (M.nameFor(p) ?? '<anonymous>'), e]);
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (cs.length === 0 || errs.length > 0) {
|
|
|
|
throw new Error(`Cannot produce unconverter for ${name}: ` +
|
|
|
|
errs.map(e => `${e[0]}: ${e[1].message}`).join(', '));
|
|
|
|
}
|
|
|
|
return (cs.length === 1) ? cs[0] : seq(`_.merge`, parens(`(a, b) => (a === b) ? a : void 0`, ... cs));
|
|
|
|
} else {
|
2021-03-23 11:16:16 +00:00
|
|
|
try {
|
|
|
|
return unconverterFor(ctx, a.value, src, t);
|
|
|
|
} catch (e) {
|
|
|
|
if (e instanceof InsufficientInformationError) {
|
|
|
|
throw new Error(`Cannot produce unconverter for ${name}: ${e.message}`);
|
|
|
|
}
|
|
|
|
throw e;
|
|
|
|
}
|
2021-03-23 10:36:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function stepSource(
|
|
|
|
src: string,
|
|
|
|
t: SimpleType,
|
|
|
|
key: string): { steppedSrc: string, steppedType: FieldType }
|
|
|
|
{
|
|
|
|
if (t.kind !== 'record' || !t.fields.has(key)) {
|
|
|
|
throw new Error(
|
|
|
|
`Internal error: attempt to step type ` +
|
|
|
|
`${formatItems([renderType(t)])} with key ${key}`);
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
steppedSrc: `${src}[${JSON.stringify(key)}]`,
|
|
|
|
steppedType: t.fields.get(key)!
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function unconverterFor(ctx: FunctionContext, p: M.Pattern, src: string, t: SimpleType): Item {
|
|
|
|
switch (p._variant) {
|
|
|
|
case 'SimplePattern':
|
|
|
|
return ((p: M.SimplePattern) => {
|
2021-03-23 11:16:16 +00:00
|
|
|
if (p._variant !== 'lit' && (t.kind === 'record' || t.kind === 'unit')) {
|
2021-03-23 10:36:55 +00:00
|
|
|
throw new InsufficientInformationError(`A Cannot produce unconverter TODO ${src} ${formatItems([renderType(t)])}`);
|
|
|
|
}
|
|
|
|
switch (p._variant) {
|
|
|
|
case 'any':
|
|
|
|
return `${src}`;
|
|
|
|
case 'atom':
|
|
|
|
switch (p.atomKind._variant) {
|
|
|
|
case 'Float': return `_.Single(${src})`;
|
|
|
|
case 'Double': return `_.Double(${src})`;
|
|
|
|
default: return `${src}`;
|
|
|
|
}
|
|
|
|
case 'lit':
|
|
|
|
return ctx.mod.literal(p.value);
|
|
|
|
case 'pointer':
|
2021-03-23 11:08:58 +00:00
|
|
|
return `${src}`;
|
2021-03-23 10:36:55 +00:00
|
|
|
case 'Ref':
|
|
|
|
return M.lookup(
|
|
|
|
refPosition(p.value), p.value, ctx.mod.env,
|
|
|
|
(_p) => `from${p.value.name.description!}(${src})`,
|
|
|
|
(modId, modPath, _p) => {
|
|
|
|
ctx.mod.imports.add([modId, modPath]);
|
|
|
|
return `${modId}.from${p.value.name.description!}(${src})`;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
})(p.value);
|
|
|
|
case 'CompoundPattern':
|
|
|
|
return ((p: M.CompoundPattern) => {
|
|
|
|
switch (p._variant) {
|
|
|
|
case 'rec':
|
|
|
|
return seq(`_.Record`, parens(
|
|
|
|
unconverterForNamed(ctx, p.label, src, t),
|
|
|
|
unconverterForNamed(ctx, p.fields, src, t)));
|
|
|
|
case 'tuple':
|
|
|
|
return brackets(... p.patterns.map(pp =>
|
|
|
|
unconverterForNamed(ctx, pp, src, t)));
|
|
|
|
case 'tuple*': {
|
|
|
|
let varexp: Item;
|
|
|
|
if (p.variable._variant === 'named') {
|
|
|
|
const { steppedSrc, steppedType } =
|
|
|
|
stepSource(src, t, p.variable.value.name.description!);
|
|
|
|
if (steppedType.kind !== 'array') {
|
|
|
|
throw new Error(
|
|
|
|
`Internal error: attempt to visit element type of ` +
|
|
|
|
`${formatItems([renderType(steppedType)])} after ` +
|
|
|
|
`stepping by key ${p.variable.value.name.description!}`);
|
|
|
|
}
|
|
|
|
varexp = seq(steppedSrc, `.map`, parens(
|
|
|
|
seq(`v => `, unconverterFor(
|
|
|
|
ctx,
|
|
|
|
M.Pattern.SimplePattern(p.variable.value.pattern),
|
|
|
|
`v`,
|
|
|
|
steppedType.type))));
|
|
|
|
} else {
|
|
|
|
if (t.kind !== 'array') {
|
|
|
|
throw new InsufficientInformationError(
|
|
|
|
`B Cannot produce unconverter TODO ${src} ${formatItems([renderType(t)])}`);
|
|
|
|
}
|
|
|
|
varexp = seq(src, `.map`, parens(
|
|
|
|
seq(`v => `, unconverterFor(
|
|
|
|
ctx,
|
|
|
|
M.Pattern.SimplePattern(p.variable.value),
|
|
|
|
`v`,
|
|
|
|
t.type))));
|
|
|
|
}
|
|
|
|
if (p.fixed.length === 0) {
|
|
|
|
return varexp;
|
|
|
|
} else {
|
|
|
|
return brackets(
|
|
|
|
... p.fixed.map(pp => unconverterForNamed(ctx, pp, src, t)),
|
|
|
|
seq(`... `, varexp));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case 'setof':
|
|
|
|
if (t.kind !== 'set') {
|
|
|
|
throw new InsufficientInformationError(
|
|
|
|
`C Cannot produce unconverter TODO ${src} ${formatItems([renderType(t)])}`);
|
|
|
|
}
|
|
|
|
return seq(`new _.Set<_ptr>`, parens(
|
|
|
|
`_.Array.from(${src}.values()).map(v => `,
|
|
|
|
unconverterFor(ctx, M.Pattern.SimplePattern(p.pattern), 'v', t),
|
|
|
|
`)`));
|
|
|
|
case 'dictof':
|
|
|
|
if (t.kind !== 'dictionary') {
|
|
|
|
throw new InsufficientInformationError(
|
|
|
|
`D Cannot produce unconverter TODO ${src} ${formatItems([renderType(t)])}`);
|
|
|
|
}
|
|
|
|
return seq(`new _.Dictionary<_ptr>`, parens(seq(
|
|
|
|
`_.Array.from(${src}.entries()).map(([k, v]) => `,
|
|
|
|
brackets(
|
|
|
|
unconverterFor(ctx, M.Pattern.SimplePattern(p.key), 'k', t),
|
|
|
|
unconverterFor(ctx, M.Pattern.SimplePattern(p.value), 'v', t)),
|
|
|
|
`)`)));
|
|
|
|
case 'dict':
|
|
|
|
return seq(`new _.Dictionary<_ptr>`, parens(
|
|
|
|
brackets(... Array.from(p.entries.entries()).map(([k, n]) =>
|
|
|
|
brackets(
|
|
|
|
ctx.mod.literal(k),
|
|
|
|
unconverterForNamedSimple(ctx, M.addNameIfAbsent(n, k), src, t))))));
|
|
|
|
}
|
|
|
|
})(p.value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function unconverterForNamed(ctx: FunctionContext, p: M.NamedPattern, src: string, t: SimpleType): Item {
|
|
|
|
if (p._variant === 'named') {
|
|
|
|
const { steppedSrc, steppedType } = stepSource(src, t, p.value.name.description!);
|
|
|
|
return unconverterFor(ctx, M.Pattern.SimplePattern(p.value.pattern), steppedSrc, steppedType);
|
|
|
|
} else {
|
|
|
|
return unconverterFor(ctx, p.value, src, t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function unconverterForNamedSimple(ctx: FunctionContext, p: M.NamedSimplePattern, src: string, t: SimpleType): Item {
|
|
|
|
if (p._variant === 'named') {
|
|
|
|
const { steppedSrc, steppedType } = stepSource(src, t, p.value.name.description!);
|
|
|
|
return unconverterFor(ctx, M.Pattern.SimplePattern(p.value.pattern), steppedSrc, steppedType);
|
|
|
|
} else {
|
|
|
|
return unconverterFor(ctx, M.Pattern.SimplePattern(p.value), src, t);
|
|
|
|
}
|
|
|
|
}
|