Unconverter
This commit is contained in:
parent
1d73289345
commit
7d8453a806
|
@ -6,6 +6,8 @@ import { typeForDefinition } from "./compiler/gentype";
|
||||||
import { converterForDefinition, converterForSimple } from "./compiler/genconverter";
|
import { converterForDefinition, converterForSimple } from "./compiler/genconverter";
|
||||||
import { EMPTY_TYPE, renderType } from "./compiler/type";
|
import { EMPTY_TYPE, renderType } from "./compiler/type";
|
||||||
import { genConstructor } from "./compiler/genctor";
|
import { genConstructor } from "./compiler/genctor";
|
||||||
|
import { unconverterForDefinition } from "./compiler/genunconverter";
|
||||||
|
import { sourceCodeFor } from "./compiler/value";
|
||||||
|
|
||||||
export function compile(env: M.Environment, schema: M.Schema, options: CompilerOptions = {}): string {
|
export function compile(env: M.Environment, schema: M.Schema, options: CompilerOptions = {}): string {
|
||||||
const mod = new ModuleContext(env, schema, options);
|
const mod = new ModuleContext(env, schema, options);
|
||||||
|
@ -65,6 +67,11 @@ export function compile(env: M.Environment, schema: M.Schema, options: CompilerO
|
||||||
ctx.block(() => [seq(`let result: undefined | `, name.description!),
|
ctx.block(() => [seq(`let result: undefined | `, name.description!),
|
||||||
... converterForDefinition(ctx, def, 'v', 'result'),
|
... converterForDefinition(ctx, def, 'v', 'result'),
|
||||||
seq(`return result`)])));
|
seq(`return result`)])));
|
||||||
|
|
||||||
|
mod.defineFunction(ctx =>
|
||||||
|
seq(`export function from${name.description!}`,
|
||||||
|
'(v: ', name.description!, '): _val ',
|
||||||
|
ctx.block(() => unconverterForDefinition(ctx, name.description!, def, 'v'))));
|
||||||
}
|
}
|
||||||
|
|
||||||
const f = new Formatter();
|
const f = new Formatter();
|
||||||
|
@ -96,39 +103,3 @@ export function compile(env: M.Environment, schema: M.Schema, options: CompilerO
|
||||||
|
|
||||||
return f.toString();
|
return f.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sourceCodeFor(v: Value<any>): Item {
|
|
||||||
return fold(v, {
|
|
||||||
boolean(b: boolean): Item { return b.toString(); },
|
|
||||||
single(f: number): Item { return f.toString(); },
|
|
||||||
double(f: number): Item { return f.toString(); },
|
|
||||||
integer(i: number): Item { return i.toString(); },
|
|
||||||
string(s: string): Item { return JSON.stringify(s); },
|
|
||||||
bytes(b: Bytes): Item {
|
|
||||||
return seq(`Uint8Array.from(`, brackets(... Array.from(b).map(b => b.toString())), `)`);
|
|
||||||
},
|
|
||||||
symbol(s: symbol): Item { return `Symbol.for(${JSON.stringify(s.description!)})`; },
|
|
||||||
|
|
||||||
record(r: Record<Value<any>, Tuple<Value<any>>, any>, k: Fold<any, Item>): Item {
|
|
||||||
return seq(`_.Record<_val, _.Tuple<_val>, _ptr>`, parens(k(r.label), brackets(... r.map(k))));
|
|
||||||
},
|
|
||||||
array(a: Array<Value<any>>, k: Fold<any, Item>): Item {
|
|
||||||
return brackets(... a.map(k));
|
|
||||||
},
|
|
||||||
set(s: Set<any>, k: Fold<any, Item>): Item {
|
|
||||||
return seq('new _.Set<_val>', parens(brackets(... Array.from(s).map(k))));
|
|
||||||
},
|
|
||||||
dictionary(d: Dictionary<any>, k: Fold<any, Item>): Item {
|
|
||||||
return seq('new _.Dictionary<_ptr>', parens(brackets(... Array.from(d).map(([kk,vv]) =>
|
|
||||||
brackets(k(kk), k(vv))))));
|
|
||||||
},
|
|
||||||
|
|
||||||
annotated(a: Annotated<any>, k: Fold<any, Item>): Item {
|
|
||||||
return seq('_.annotate<_ptr>', parens(k(a.item), ... a.annotations.map(k)));
|
|
||||||
},
|
|
||||||
|
|
||||||
pointer(t: any, _k: Fold<any, Item>): Item {
|
|
||||||
throw new Error(`Cannot emit source code for construction of pointer ${stringify(t)}`);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -15,6 +15,8 @@ export interface Capture {
|
||||||
sourceExpr: string;
|
sourceExpr: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const RECURSION_LIMIT = 128;
|
||||||
|
|
||||||
export class ModuleContext {
|
export class ModuleContext {
|
||||||
readonly env: M.Environment;
|
readonly env: M.Environment;
|
||||||
readonly schema: M.Schema;
|
readonly schema: M.Schema;
|
||||||
|
@ -40,17 +42,22 @@ export class ModuleContext {
|
||||||
return varname;
|
return varname;
|
||||||
}
|
}
|
||||||
|
|
||||||
derefPattern([_name, p]: [string, M.Alternative]): M.Definition {
|
derefPattern(p: M.Definition, refCount = 0): M.Definition {
|
||||||
if (p._variant === 'Pattern' &&
|
if (refCount > RECURSION_LIMIT) {
|
||||||
p.value._variant === 'SimplePattern' &&
|
throw new Error('Recursion limit exceeded');
|
||||||
p.value.value._variant === 'Ref')
|
}
|
||||||
|
if (p._variant === 'Alternative' &&
|
||||||
|
p.value._variant === 'Pattern' &&
|
||||||
|
p.value.value._variant === 'SimplePattern' &&
|
||||||
|
p.value.value.value._variant === 'Ref')
|
||||||
{
|
{
|
||||||
return M.lookup(refPosition(p.value.value.value), p.value.value.value, this.env,
|
return M.lookup(refPosition(p.value.value.value.value),
|
||||||
(p) => p,
|
p.value.value.value.value,
|
||||||
(_modId, _modPath, pp) => pp ?? M.Definition.Alternative(
|
this.env,
|
||||||
M.Alternative.Pattern(p.value)));
|
(p) => this.derefPattern(p, refCount + 1),
|
||||||
|
(_modId, _modPath, pp) => this.derefPattern(pp ?? p, refCount + 1));
|
||||||
} else {
|
} else {
|
||||||
return M.Definition.Alternative(p);
|
return p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,9 +129,11 @@ export class FunctionContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildCapturedCompound(dest: string): Item {
|
buildCapturedCompound(dest: string): Item {
|
||||||
return seq(`${dest} = `, braces(
|
const fields = [
|
||||||
... variantInitFor(this.variantName),
|
... variantInitFor(this.variantName),
|
||||||
... this.captures.map(({ fieldName, sourceExpr }) =>
|
... this.captures.map(({ fieldName, sourceExpr }) =>
|
||||||
keyvalue(fieldName, sourceExpr))));
|
keyvalue(fieldName, sourceExpr))
|
||||||
|
];
|
||||||
|
return seq(`${dest} = `, fields.length === 0 ? `null` : braces(... fields));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,16 +184,17 @@ export function converterForSimple(
|
||||||
return [`${dest} = ${src}`];
|
return [`${dest} = ${src}`];
|
||||||
case 'atom': {
|
case 'atom': {
|
||||||
let test: Item;
|
let test: Item;
|
||||||
|
let valexp: Item = `${src}`;
|
||||||
switch (p.atomKind._variant) {
|
switch (p.atomKind._variant) {
|
||||||
case 'Boolean': test = `typeof ${src} === 'boolean'`; break;
|
case 'Boolean': test = `typeof ${src} === 'boolean'`; break;
|
||||||
case 'Float': test = `_.Float.isSingle(${src})`; break;
|
case 'Float': test = `_.Float.isSingle(${src})`; valexp = `${src}.value`; break;
|
||||||
case 'Double': test =`_.Float.isDouble(${src})`; break;
|
case 'Double': test =`_.Float.isDouble(${src})`; valexp = `${src}.value`; break;
|
||||||
case 'SignedInteger': test = `typeof ${src} === 'number'`; break;
|
case 'SignedInteger': test = `typeof ${src} === 'number'`; break;
|
||||||
case 'String': test = `typeof ${src} === 'string'`; break;
|
case 'String': test = `typeof ${src} === 'string'`; break;
|
||||||
case 'ByteString': test = `_.Bytes.isBytes(${src})`; break;
|
case 'ByteString': test = `_.Bytes.isBytes(${src})`; break;
|
||||||
case 'Symbol': test = `typeof ${src} === 'symbol'`; break;
|
case 'Symbol': test = `typeof ${src} === 'symbol'`; break;
|
||||||
}
|
}
|
||||||
return [seq(`${dest} = `, test, ` ? ${src} : void 0`)];
|
return [seq(`${dest} = `, test, ` ? `, valexp, ` : 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)}) ? null : void 0`];
|
||||||
|
|
|
@ -12,11 +12,11 @@ export function typeForDefinition(mod: ModuleContext, d: M.Definition): Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function typeForAlternative(mod: ModuleContext, a: M.Alternative): SimpleType {
|
export function typeForAlternative(mod: ModuleContext, a: M.Alternative): SimpleType {
|
||||||
if (a._variant === 'and') {
|
if (a._variant === 'and') {
|
||||||
const fs = new Map();
|
const fs = new Map();
|
||||||
a.patterns.forEach(n => gatherFields(fs, mod, n));
|
a.patterns.forEach(n => gatherFields(fs, mod, n));
|
||||||
return Type.record(fs);
|
return fs.size > 0 ? Type.record(fs) : Type.unit();
|
||||||
} else {
|
} else {
|
||||||
return typeFor(mod, a.value);
|
return typeFor(mod, a.value);
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ export function typeFor(mod: ModuleContext, p: M.Pattern): SimpleType {
|
||||||
if (arrayType === void 0) {
|
if (arrayType === void 0) {
|
||||||
const fs = new Map();
|
const fs = new Map();
|
||||||
compoundFields(fs, mod, p.value);
|
compoundFields(fs, mod, p.value);
|
||||||
return Type.record(fs);
|
return fs.size > 0 ? Type.record(fs) : Type.unit();
|
||||||
} else {
|
} else {
|
||||||
return Type.array(simpleType(mod, arrayType));
|
return Type.array(simpleType(mod, arrayType));
|
||||||
}
|
}
|
||||||
|
@ -63,8 +63,8 @@ export function simpleType(mod: ModuleContext, p: M.SimplePattern): AtomicType {
|
||||||
case 'atom':
|
case 'atom':
|
||||||
switch (p.atomKind._variant) {
|
switch (p.atomKind._variant) {
|
||||||
case 'Boolean': return Type.ref(`boolean`);
|
case 'Boolean': return Type.ref(`boolean`);
|
||||||
case 'Float': return Type.ref(`_.SingleFloat`);
|
case 'Float': return Type.ref(`number`);
|
||||||
case 'Double': return Type.ref(`_.DoubleFloat`);
|
case 'Double': return Type.ref(`number`);
|
||||||
case 'SignedInteger': return Type.ref(`number`);
|
case 'SignedInteger': return Type.ref(`number`);
|
||||||
case 'String': return Type.ref(`string`);
|
case 'String': return Type.ref(`string`);
|
||||||
case 'ByteString': return Type.ref(`_.Bytes`);
|
case 'ByteString': return Type.ref(`_.Bytes`);
|
||||||
|
|
|
@ -0,0 +1,206 @@
|
||||||
|
import { SchemaSyntaxError } from '../error';
|
||||||
|
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 {
|
||||||
|
return unconverterFor(ctx, a.value, src, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
if (p._variant !== 'lit' && t.kind === 'record') {
|
||||||
|
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':
|
||||||
|
return `_fromPtr(${src})`;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { Annotated, Bytes, Dictionary, Fold, fold, Record, Tuple, Value, stringify } from "@preserves/core";
|
||||||
|
import { brackets, Item, parens, seq } from "./block";
|
||||||
|
|
||||||
|
export function sourceCodeFor(v: Value<any>): Item {
|
||||||
|
return fold(v, {
|
||||||
|
boolean(b: boolean): Item { return b.toString(); },
|
||||||
|
single(f: number): Item { return f.toString(); },
|
||||||
|
double(f: number): Item { return f.toString(); },
|
||||||
|
integer(i: number): Item { return i.toString(); },
|
||||||
|
string(s: string): Item { return JSON.stringify(s); },
|
||||||
|
bytes(b: Bytes): Item {
|
||||||
|
return seq(`Uint8Array.from(`, brackets(... Array.from(b).map(b => b.toString())), `)`);
|
||||||
|
},
|
||||||
|
symbol(s: symbol): Item { return `Symbol.for(${JSON.stringify(s.description!)})`; },
|
||||||
|
|
||||||
|
record(r: Record<Value<any>, Tuple<Value<any>>, any>, k: Fold<any, Item>): Item {
|
||||||
|
return seq(`_.Record<_val, _.Tuple<_val>, _ptr>`, parens(k(r.label), brackets(... r.map(k))));
|
||||||
|
},
|
||||||
|
array(a: Array<Value<any>>, k: Fold<any, Item>): Item {
|
||||||
|
return brackets(... a.map(k));
|
||||||
|
},
|
||||||
|
set(s: Set<any>, k: Fold<any, Item>): Item {
|
||||||
|
return seq('new _.Set<_val>', parens(brackets(... Array.from(s).map(k))));
|
||||||
|
},
|
||||||
|
dictionary(d: Dictionary<any>, k: Fold<any, Item>): Item {
|
||||||
|
return seq('new _.Dictionary<_ptr>', parens(brackets(... Array.from(d).map(([kk,vv]) =>
|
||||||
|
brackets(k(kk), k(vv))))));
|
||||||
|
},
|
||||||
|
|
||||||
|
annotated(a: Annotated<any>, k: Fold<any, Item>): Item {
|
||||||
|
return seq('_.annotate<_ptr>', parens(k(a.item), ... a.annotations.map(k)));
|
||||||
|
},
|
||||||
|
|
||||||
|
pointer(t: any, _k: Fold<any, Item>): Item {
|
||||||
|
throw new Error(`Cannot emit source code for construction of pointer ${stringify(t)}`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
|
@ -170,7 +170,7 @@ function parseDefinition(name: symbol, body: Array<Input>): Definition {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: deal with situation where there's an or of ands, where
|
// TODO: deal with situation where there's an or of ands, where
|
||||||
// the branches of the and arenamed. The parsing is ambiguous, and
|
// the branches of the and are named. The parsing is ambiguous, and
|
||||||
// with the current code I think (?) you end up with the same name
|
// with the current code I think (?) you end up with the same name
|
||||||
// attached to the or-branch as to the leftmost and-branch.
|
// attached to the or-branch as to the leftmost and-branch.
|
||||||
|
|
||||||
|
@ -261,6 +261,17 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
|
||||||
const maybeNamedSimple =
|
const maybeNamedSimple =
|
||||||
_maybeNamed(M.NamedSimplePattern.named, M.NamedSimplePattern.anonymous, walkSimple);
|
_maybeNamed(M.NamedSimplePattern.named, M.NamedSimplePattern.anonymous, walkSimple);
|
||||||
|
|
||||||
|
function parseArrayLike(item: Array<Input>): CompoundPattern {
|
||||||
|
if (is(item[item.length - 1], M.DOTDOTDOT)) {
|
||||||
|
if (item.length < 2) complain();
|
||||||
|
return M.CompoundPattern.tuple$STAR$(
|
||||||
|
item.slice(0, item.length - 2).map(maybeNamed),
|
||||||
|
maybeNamedSimple(item[item.length - 2]));
|
||||||
|
} else {
|
||||||
|
return M.CompoundPattern.tuple(item.map(maybeNamed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Record.isRecord<Input, Tuple<Input>, never>(item)) {
|
if (Record.isRecord<Input, Tuple<Input>, never>(item)) {
|
||||||
const label = item.label;
|
const label = item.label;
|
||||||
if (Record.isRecord<Input, [], never>(label)) {
|
if (Record.isRecord<Input, [], never>(label)) {
|
||||||
|
@ -275,18 +286,10 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
|
||||||
} else {
|
} else {
|
||||||
return M.CompoundPattern.rec(
|
return M.CompoundPattern.rec(
|
||||||
M.NamedPattern.anonymous(M.Pattern.SimplePattern(M.SimplePattern.lit(label))),
|
M.NamedPattern.anonymous(M.Pattern.SimplePattern(M.SimplePattern.lit(label))),
|
||||||
M.NamedPattern.anonymous(M.Pattern.CompoundPattern(
|
M.NamedPattern.anonymous(M.Pattern.CompoundPattern(parseArrayLike(item))));
|
||||||
M.CompoundPattern.tuple(item.map(maybeNamed)))));
|
|
||||||
}
|
}
|
||||||
} else if (Array.isArray(item)) {
|
} else if (Array.isArray(item)) {
|
||||||
if (is(item[item.length - 1], M.DOTDOTDOT)) {
|
return parseArrayLike(item);
|
||||||
if (item.length < 2) complain();
|
|
||||||
return M.CompoundPattern.tuple$STAR$(
|
|
||||||
item.slice(0, item.length - 2).map(maybeNamed),
|
|
||||||
maybeNamedSimple(item[item.length - 2]));
|
|
||||||
} else {
|
|
||||||
return M.CompoundPattern.tuple(item.map(maybeNamed));
|
|
||||||
}
|
|
||||||
} else if (Dictionary.isDictionary<never, Input>(item)) {
|
} else if (Dictionary.isDictionary<never, Input>(item)) {
|
||||||
if (item.size === 2 && item.has(M.DOTDOTDOT)) {
|
if (item.size === 2 && item.has(M.DOTDOTDOT)) {
|
||||||
const v = item.clone();
|
const v = item.clone();
|
||||||
|
|
Loading…
Reference in New Issue