Add Capture support; repair (redo) converter accordingly
This commit is contained in:
parent
084f54f869
commit
d372977023
|
@ -3,7 +3,7 @@ import * as M from "./meta";
|
|||
import { CompilerOptions, ModuleContext } from "./compiler/context";
|
||||
import { brackets, Formatter, Item, parens, seq } from "./compiler/block";
|
||||
import { typeForDefinition } from "./compiler/type";
|
||||
import { decoderFor } from "./compiler/decoder";
|
||||
// import { decoderFor } from "./compiler/decoder";
|
||||
import { converterForDefinition } from "./compiler/converter";
|
||||
|
||||
export function compile(env: M.Environment, schema: M.Schema, options: CompilerOptions = {}): string {
|
||||
|
@ -21,7 +21,8 @@ export function compile(env: M.Environment, schema: M.Schema, options: CompilerO
|
|||
? '() => { throw new _.DecodeError("Pointers forbidden"); }'
|
||||
: seq(`(d: _.TypedDecoder<_ptr>) => `, ctx.block(() => [
|
||||
seq(`let result`),
|
||||
... decoderFor(ctx, pointerName, 'result'),
|
||||
seq(`/* TODO */`),
|
||||
// ... decoderFor(ctx, pointerName, 'result'),
|
||||
seq(`return result`)]))),
|
||||
`;`));
|
||||
|
||||
|
@ -35,7 +36,7 @@ export function compile(env: M.Environment, schema: M.Schema, options: CompilerO
|
|||
|
||||
mod.defineFunction(ctx =>
|
||||
seq(`export function as${name.description!}`,
|
||||
'(v: any): ', name.description!, ' ',
|
||||
'(v: _val): ', name.description!, ' ',
|
||||
ctx.block(() => [
|
||||
seq(`let result = to${name.description!}(v)`),
|
||||
seq(`if (result === void 0) `,
|
||||
|
@ -44,17 +45,18 @@ export function compile(env: M.Environment, schema: M.Schema, options: CompilerO
|
|||
|
||||
mod.defineFunction(ctx =>
|
||||
seq(`export function to${name.description!}`,
|
||||
'(v: any): ', name.description!, ' | undefined ',
|
||||
ctx.block(() => [seq(`let result`),
|
||||
'(v: _val): undefined | ', name.description!, ' ',
|
||||
ctx.block(() => [seq(`let result: undefined | `, name.description!),
|
||||
... converterForDefinition(ctx, def, 'v', 'result'),
|
||||
seq(`return result`)])));
|
||||
|
||||
mod.defineFunction(ctx =>
|
||||
seq(`export function decode${name.description!}`,
|
||||
`(d: _.TypedDecoder<_ptr>): `, name.description!, ` | undefined `,
|
||||
ctx.block(() => [seq(`let result`),
|
||||
... decoderFor(ctx, def, 'result'),
|
||||
seq(`return result`)])));
|
||||
// mod.defineFunction(ctx =>
|
||||
// seq(`export function decode${name.description!}`,
|
||||
// `(d: _.TypedDecoder<_ptr>): undefined | `, name.description!,
|
||||
// ctx.block(() => [seq(`let result: undefined | `, name.description!),
|
||||
// seq(`/* TODO */`),
|
||||
// // ... decoderFor(ctx, def, 'result'),
|
||||
// seq(`return result`)])));
|
||||
}
|
||||
|
||||
const f = new Formatter();
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
export type Item = Emittable | string;
|
||||
|
||||
export const DEFAULT_WIDTH = 80;
|
||||
|
||||
export class Formatter {
|
||||
width = 80;
|
||||
width = DEFAULT_WIDTH;
|
||||
indentDelta = ' ';
|
||||
currentIndent = '\n';
|
||||
buffer: Array<string> = [];
|
||||
|
@ -42,15 +44,21 @@ export class Formatter {
|
|||
}
|
||||
}
|
||||
|
||||
export abstract class Emittable {
|
||||
abstract writeOn(f: Formatter): void;
|
||||
export function formatItems(i: Item[], width = DEFAULT_WIDTH): string {
|
||||
const f = new Formatter();
|
||||
f.width = width;
|
||||
i.forEach(i => f.write(i));
|
||||
return f.toString();
|
||||
}
|
||||
|
||||
export class Sequence extends Emittable {
|
||||
export interface Emittable {
|
||||
writeOn(f: Formatter): void;
|
||||
}
|
||||
|
||||
export class Sequence implements Emittable {
|
||||
items: Array<Item>;
|
||||
|
||||
constructor(items: Array<Item>) {
|
||||
super();
|
||||
if (items.some(i => i === void 0)) throw new Error('aiee');
|
||||
this.items = items;
|
||||
}
|
||||
|
@ -158,6 +166,12 @@ export const opseq = (zero: string, op: string, ... items: Item[]) =>
|
|||
export const brackets = (... items: Item[]) => new Brackets(items);
|
||||
export const anglebrackets = (... items: Item[]) => new AngleBrackets(items);
|
||||
export const braces = (... items: Item[]) => new Braces(items);
|
||||
export const block = (... items: Item[]) => new Block(items);
|
||||
export const block = (... items: Item[]) => {
|
||||
if (items.length === 1 && items[0] instanceof Block) {
|
||||
return items[0];
|
||||
} else {
|
||||
return new Block(items);
|
||||
}
|
||||
}
|
||||
export const fnblock = (... items: Item[]) => seq('((() => ', block(... items), ')())');
|
||||
export const keyvalue = (k: Item, v: Item) => seq(k, ': ', v);
|
||||
export const keyvalue = (k: string, v: Item) => seq(JSON.stringify(k), ': ', v);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Dictionary, KeyedSet, Position } from "@preserves/core";
|
||||
import { refPosition } from "../reader";
|
||||
import * as M from "../meta";
|
||||
import { block, commas, Item, seq } from "./block";
|
||||
import { block, braces, commas, formatItems, Item, keyvalue, seq } from "./block";
|
||||
|
||||
export interface CompilerOptions {
|
||||
preservesModule?: string;
|
||||
|
@ -9,6 +9,11 @@ export interface CompilerOptions {
|
|||
warn?(message: string, pos: Position | null): void;
|
||||
}
|
||||
|
||||
export interface Capture {
|
||||
fieldName: string;
|
||||
sourceExpr: string;
|
||||
}
|
||||
|
||||
export class ModuleContext {
|
||||
readonly env: M.Environment;
|
||||
readonly schema: M.Schema;
|
||||
|
@ -61,32 +66,68 @@ export class FunctionContext {
|
|||
readonly mod: ModuleContext;
|
||||
|
||||
tempCounter = 0;
|
||||
temps: string[] = [];
|
||||
temps: Map<string, { type: Item, names: string[] }> = new Map();
|
||||
captures: Capture[] = [];
|
||||
variantName: string | undefined = void 0;
|
||||
|
||||
constructor(mod: ModuleContext) {
|
||||
this.mod = mod;
|
||||
}
|
||||
|
||||
gentemp(): string {
|
||||
const varname = '_tmp' + this.tempCounter++;
|
||||
this.temps.push(varname);
|
||||
return varname;
|
||||
gentempname(): string {
|
||||
return '_tmp' + this.tempCounter++;
|
||||
}
|
||||
|
||||
gentemps(n: number): string[] {
|
||||
const temps = [];
|
||||
while (temps.length < n) temps.push(this.gentemp());
|
||||
return temps;
|
||||
gentemp(... vartypePieces: Item[]): string {
|
||||
const vartype = vartypePieces.length === 0 ? '_val | undefined' : seq(... vartypePieces);
|
||||
const typestr = formatItems([vartype], Infinity);
|
||||
const varname = this.gentempname();
|
||||
let e = this.temps.get(typestr);
|
||||
if (e === void 0) {
|
||||
e = { type: vartype, names: [] };
|
||||
this.temps.set(typestr, e);
|
||||
}
|
||||
e.names.push(varname);
|
||||
return varname;
|
||||
}
|
||||
|
||||
block(f: () => Item[]): Item {
|
||||
const oldTemps = this.temps;
|
||||
this.temps = [];
|
||||
this.temps = new Map();
|
||||
const items = f();
|
||||
const ts = this.temps;
|
||||
this.temps = oldTemps;
|
||||
return block(
|
||||
... ts.length > 0 ? [seq(`let `, commas(... ts), ': any')] : [],
|
||||
... Array.from(ts).map(([_typestr, { type, names }]) =>
|
||||
seq(`let `, commas(... names), `: `, type)),
|
||||
... items);
|
||||
}
|
||||
|
||||
withCapture(fieldName: string | undefined,
|
||||
sourceExpr: string,
|
||||
ks: (sourceExpr: string) => Item[],
|
||||
withCheck = true): Item
|
||||
{
|
||||
if (fieldName !== void 0) this.captures.push({ fieldName, sourceExpr });
|
||||
const result = withCheck
|
||||
? seq(`if (${sourceExpr} !== void 0) `, this.block(() => ks(sourceExpr)))
|
||||
: block(... ks(sourceExpr));
|
||||
if (fieldName !== void 0) this.captures.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
buildCapturedCompound(dest: string): Item {
|
||||
return seq(`${dest} = `, braces(
|
||||
... variantInitFor(this.variantName),
|
||||
... this.captures.map(({ fieldName, sourceExpr }) =>
|
||||
keyvalue(fieldName, sourceExpr))));
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -1,44 +1,8 @@
|
|||
import { FunctionContext } from "./context";
|
||||
import * as M from '../meta';
|
||||
import { block, braces, Item, keyvalue, seq } from "./block";
|
||||
import { typeFor, variantFor, variantInitFor } from "./type";
|
||||
import { block, Item, seq } from "./block";
|
||||
import { typeFor } from "./type";
|
||||
import { refPosition } from "../reader";
|
||||
import { stringify } from "@preserves/core";
|
||||
|
||||
function converterForTuple(ctx: FunctionContext,
|
||||
ps: M.NamedPattern[],
|
||||
src: string,
|
||||
dest: string,
|
||||
variantName: string | undefined,
|
||||
recordFields: boolean,
|
||||
variablePattern: M.NamedSimplePattern | undefined): Item[]
|
||||
{
|
||||
const temps = ctx.gentemps(ps.length);
|
||||
|
||||
function loop(i: number): Item[] {
|
||||
if (i < ps.length) {
|
||||
return [...converterFor(ctx, M.unname(ps[i]), `${src}[${i}]`, temps[i]),
|
||||
seq(`if (${temps[i]} !== void 0) `, ctx.block(() => loop(i + 1)))];
|
||||
} else {
|
||||
if (variablePattern === void 0) {
|
||||
return [seq(`${dest} = `, braces(
|
||||
... variantInitFor(variantName),
|
||||
... ps.flatMap((pp, i) => converterField(pp, temps[i]))))];
|
||||
} else {
|
||||
return [ps.length > 0 ? `let vN = ${src}.slice(${ps.length})` : `let vN = ${src}`,
|
||||
converterForArray(ctx, M.unname(variablePattern), 'vN', dest, false)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const lengthCheck = variablePattern === void 0
|
||||
? seq(`${src}.length === ${ps.length}`)
|
||||
: seq(`${src}.length >= ${ps.length}`);
|
||||
|
||||
return recordFields
|
||||
? loop(0)
|
||||
: [seq(`if (_.Array.isArray(${src}) && `, lengthCheck, `) `, ctx.block(() => loop(0)))];
|
||||
}
|
||||
|
||||
export function converterForDefinition(
|
||||
ctx: FunctionContext,
|
||||
|
@ -48,65 +12,98 @@ export function converterForDefinition(
|
|||
{
|
||||
if (p.label === M.$or) {
|
||||
const alts = p[0];
|
||||
switch (alts.length) {
|
||||
case 0: return []; // assume dest is already void 0
|
||||
case 1: return converterForAlternative(ctx, alts[0][1], src, dest, alts[0][0]);
|
||||
default: {
|
||||
function loop(i: number): Item[] {
|
||||
return [
|
||||
... converterForAlternative(ctx, alts[i][1], src, dest, alts[i][0]),
|
||||
... (i < alts.length - 1)
|
||||
? [seq(`if (${dest} === void 0) `, ctx.block(() => loop(i + 1)))]
|
||||
: []];
|
||||
}
|
||||
return loop(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)))]
|
||||
: [])];
|
||||
}
|
||||
return alts.length === 0 ? [] : loop(0);
|
||||
} else {
|
||||
return converterForAlternative(ctx, p, src, dest, void 0);
|
||||
ctx.variantName = void 0;
|
||||
return converterForAlternative(ctx, p, src, dest);
|
||||
}
|
||||
}
|
||||
|
||||
function converterForAlternative(ctx: FunctionContext, p: M.Alternative, src: string, dest: string, variantName: string | undefined): Item[] {
|
||||
function converterForAlternative(
|
||||
ctx: FunctionContext,
|
||||
p: M.Alternative,
|
||||
src: string,
|
||||
dest: string): Item[]
|
||||
{
|
||||
if (p.label === M.$and) {
|
||||
switch (p[0].length) {
|
||||
case 0: return [`${dest} = ${src}`];
|
||||
case 1: {
|
||||
return converterFor(ctx, M.unname(p[0][0]), src, dest, variantName);
|
||||
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,
|
||||
() => [ctx.buildCapturedCompound(dest)],
|
||||
false)];
|
||||
} else {
|
||||
return [`${dest} = ${simpleValue}`];
|
||||
}
|
||||
default: {
|
||||
const alts = p[0];
|
||||
const temps = ctx.gentemps(alts.length);
|
||||
function loop(i: number): Item[] {
|
||||
return (i < temps.length)
|
||||
? [...converterFor(ctx, M.unname(alts[i]), src, temps[i]),
|
||||
seq(`if (${temps[i]} !== void 0) `, ctx.block(() => loop(i + 1)))]
|
||||
: [seq(`${dest} = `, braces(
|
||||
... variantInitFor(variantName),
|
||||
... alts.flatMap((pp, i) => converterField(pp, temps[i]))))];
|
||||
}
|
||||
return loop(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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)];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return converterFor(ctx, p, src, dest, variantName);
|
||||
}
|
||||
|
||||
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)))];
|
||||
}
|
||||
|
||||
function converterForArray(ctx: FunctionContext,
|
||||
arrayType: M.SimplePattern,
|
||||
arrayType: M.NamedSimplePattern,
|
||||
src: string,
|
||||
dest: string,
|
||||
checkArray: boolean): Item
|
||||
checkArray: boolean,
|
||||
k: (dest: string) => Item[]): Item
|
||||
{
|
||||
const postCheck = () => [
|
||||
seq(`let r: Array<`, typeFor(ctx.mod, arrayType), `> | undefined = []`),
|
||||
seq(`for (const v of ${src}) `, ctx.block(() => [
|
||||
seq(`let vv`),
|
||||
... converterFor(ctx, arrayType, 'v', 'vv'),
|
||||
seq(`if (vv === void 0) { r = void 0; break; }`),
|
||||
seq(`r.push(vv)`)])),
|
||||
seq(`${dest} = r`)];
|
||||
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`)])),
|
||||
ctx.withCapture(M.nameFor(arrayType), r, k)];
|
||||
};
|
||||
return (checkArray
|
||||
? seq(`if (_.Array.isArray(${src})) `, ctx.block(postCheck))
|
||||
: block(... postCheck()));
|
||||
|
@ -114,67 +111,61 @@ function converterForArray(ctx: FunctionContext,
|
|||
|
||||
function converterFor(
|
||||
ctx: FunctionContext,
|
||||
p: M.Pattern,
|
||||
np: M.NamedPattern,
|
||||
src: string,
|
||||
dest: string,
|
||||
variantName?: string,
|
||||
ks: (dest: string | undefined) => Item[],
|
||||
recordFields = false): Item[]
|
||||
{
|
||||
let converterItem: Item[];
|
||||
const unlabeled = variantName === void 0 ? dest : ctx.gentemp();
|
||||
let p = M.unname(np);
|
||||
let maybeName = M.nameFor(np);
|
||||
|
||||
if (M.isSimplePattern(p)) {
|
||||
converterItem = converterForSimple(ctx, p, src, unlabeled);
|
||||
const dest = ctx.gentemp(typeFor(ctx.mod, p), ` | undefined`);
|
||||
return [... converterForSimple(ctx, p, src, dest),
|
||||
ctx.withCapture(maybeName, dest, ks)];
|
||||
} else {
|
||||
switch (p.label) {
|
||||
case M.$setof:
|
||||
// assume dest is already void 0
|
||||
converterItem = [
|
||||
seq(`if (_.Set.isSet(${src})) `, ctx.block(() => [
|
||||
seq(`let r: `, typeFor(ctx.mod, p), ` | undefined = new _.KeyedSet()`),
|
||||
seq(`for (const v of ${src}) `, ctx.block(() => [
|
||||
seq(`let vv`),
|
||||
... converterFor(ctx, p[0], 'v', 'vv'),
|
||||
seq(`if (vv === void 0) { r = void 0; break; }`),
|
||||
seq(`r.add(vv)`)])),
|
||||
seq(`${unlabeled} = r`)]))];
|
||||
break;
|
||||
case M.$dictof:
|
||||
// assume dest is already void 0
|
||||
converterItem = [
|
||||
seq(`if (_.Dictionary.isDictionary(${src})) `, ctx.block(() => [
|
||||
seq(`let r: `, typeFor(ctx.mod, p), ` | undefined = new _.KeyedDictionary()`),
|
||||
seq(`for (const [k, v] of ${src}) `, ctx.block(() => [
|
||||
seq(`let kk`),
|
||||
... converterFor(ctx, p[0], 'k', 'kk'),
|
||||
seq(`if (kk === void 0) { r = void 0; break; }`),
|
||||
seq(`let vv`),
|
||||
... converterFor(ctx, p[1], 'v', 'vv'),
|
||||
seq(`if (vv === void 0) { r = void 0; break; }`),
|
||||
seq(`r.set(kk, vv)`)])),
|
||||
seq(`${unlabeled} = r`)]))];
|
||||
break;
|
||||
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`)])),
|
||||
ctx.withCapture(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`)])),
|
||||
ctx.withCapture(maybeName, r, ks)]))];
|
||||
}
|
||||
default: {
|
||||
const arrayType = M.simpleArray(p);
|
||||
if (arrayType === void 0) {
|
||||
return converterForCompound(ctx, p, src, dest, variantName, recordFields);
|
||||
return converterForCompound(ctx, p, src, recordFields, () => ks(void 0));
|
||||
} else {
|
||||
converterItem = [
|
||||
converterForArray(ctx, arrayType, src, unlabeled, !recordFields)];
|
||||
break;
|
||||
return [converterForArray(ctx, arrayType, src, !recordFields, ks)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (variantName === void 0) {
|
||||
return converterItem;
|
||||
} else {
|
||||
return [... converterItem,
|
||||
seq(`if (${unlabeled} !== void 0) ${dest} = `, braces(
|
||||
variantFor(variantName),
|
||||
keyvalue('value', unlabeled)))];
|
||||
}
|
||||
}
|
||||
|
||||
function converterForSimple(
|
||||
|
@ -198,11 +189,11 @@ function converterForSimple(
|
|||
return [seq(`${dest} = `, test, ` ? ${src} : void 0`)];
|
||||
}
|
||||
case M.$lit:
|
||||
return [`${dest} = _.is(${src}, ${ctx.mod.literal(p[0])}) ? ${src} : void 0`];
|
||||
return [`${dest} = _.is(${src}, ${ctx.mod.literal(p[0])}) ? ${ctx.mod.literal(p[0])} : void 0`];
|
||||
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, void 0),
|
||||
(p) => converterForAlternative(ctx, p, src, dest),
|
||||
(modId, modPath,_p) => {
|
||||
ctx.mod.imports.add([modId, modPath]);
|
||||
return [`${dest} = ${modId}.decode${p[1].description!}(${src})`];
|
||||
|
@ -219,74 +210,39 @@ function converterForCompound(
|
|||
ctx: FunctionContext,
|
||||
p: M.CompoundPattern,
|
||||
src: string,
|
||||
dest: string,
|
||||
variantName: string | undefined,
|
||||
recordFields: boolean): Item[]
|
||||
recordFields: boolean,
|
||||
ks: () => Item[]): Item[]
|
||||
{
|
||||
switch (p.label) {
|
||||
case M.$rec:
|
||||
// assume dest is already void 0
|
||||
return [seq(`if (_.Record.isRecord(${src})) `, ctx.block(() => {
|
||||
const label = ctx.gentemp();
|
||||
return [...converterFor(ctx, M.unname(p[0]), `${src}.label`, label),
|
||||
seq(`if (${label} !== void 0) `, ctx.block(() => {
|
||||
const fs = ctx.gentemp();
|
||||
return [...converterFor(ctx, M.unname(p[1]), src, fs, void 0, true),
|
||||
seq(`if (${fs} !== void 0) ${dest} = `,
|
||||
braces(... variantInitFor(variantName),
|
||||
... converterField(p[0], label),
|
||||
`... ${fs}`))];
|
||||
}))];
|
||||
}))];
|
||||
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))))];
|
||||
case M.$tuple:
|
||||
// assume dest is already void 0
|
||||
return converterForTuple(ctx, p[0], src, dest, variantName, recordFields, void 0);
|
||||
return converterForTuple(ctx, p[0], src, recordFields, void 0, ks);
|
||||
case M.$tuple_STAR_:
|
||||
// assume dest is already void 0
|
||||
return converterForTuple(ctx, p[0], src, dest, variantName, recordFields, p[1]);
|
||||
return converterForTuple(ctx, p[0], src, recordFields, p[1], ks);
|
||||
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]);
|
||||
const temps = ctx.gentemps(entries.length);
|
||||
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.unname(n), tmpSrc, temps[i]),
|
||||
seq(`if (${temps[i]} !== void 0) `, ctx.block(() =>
|
||||
loop(i + 1)))]))];
|
||||
return [seq(`if ((${tmpSrc} = ${src}.get(${ctx.mod.literal(k)})) !== void 0) `,
|
||||
ctx.block(() =>
|
||||
converterFor(ctx, M.addNameIfAbsent(n, k), tmpSrc, () =>
|
||||
loop(i + 1))))];
|
||||
} else {
|
||||
return [
|
||||
seq(`${dest} = `, braces(
|
||||
... variantInitFor(variantName),
|
||||
... entries.flatMap(([k, n], i) => converterField(n, temps[i], k))))];
|
||||
return ks();
|
||||
}
|
||||
}
|
||||
return [seq(`if (_.Dictionary.isDictionary(${src})) `, ctx.block(() => loop(0)))];
|
||||
return [seq(`if (_.Dictionary.isDictionary<_ptr>(${src})) `, ctx.block(() => loop(0)))];
|
||||
}
|
||||
default:
|
||||
((_p: never) => {})(p);
|
||||
throw new Error("Unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
function converterField(n: M.NamedPattern, src: string, k?: M.Input): Item[] {
|
||||
if (n.label === M.$named) {
|
||||
return [keyvalue(stringify(n[0]), src)];
|
||||
}
|
||||
if (k !== void 0) {
|
||||
const s = M.namelike(k);
|
||||
if (s !== void 0) {
|
||||
return [keyvalue(JSON.stringify(s), src)];
|
||||
}
|
||||
}
|
||||
if (M.isCompoundPattern(n)) {
|
||||
return [`... ${src}`];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/*
|
||||
import { FunctionContext } from "./context";
|
||||
import * as M from '../meta';
|
||||
import { anglebrackets, block, brackets, Item, opseq, parens, seq } from "./block";
|
||||
import { typeFor } from './type';
|
||||
import { refPosition } from "../reader";
|
||||
import { predicateFor } from "./predicate";
|
||||
|
||||
function decodeCompound(ctx: FunctionContext,
|
||||
p: M.Pattern,
|
||||
|
@ -206,3 +206,4 @@ export function decoderFor(ctx: FunctionContext, p: M.Definition, dest: string,
|
|||
throw new Error("Unreachable");
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
import { refPosition } from '../reader';
|
||||
import * as M from '../meta';
|
||||
import { block, fnblock, Item, opseq, parens, seq } from './block';
|
||||
import { FunctionContext } from './context';
|
||||
|
||||
export function predicateFor(ctx: FunctionContext, v: string, p: M.Definition, recordOkAsTuple = false): Item
|
||||
{
|
||||
switch (p.label) {
|
||||
case M.$atom:
|
||||
switch (p[0]) {
|
||||
case M.$Boolean: return `typeof ${v} === 'boolean'`;
|
||||
case M.$Float: return `_.Float.isSingle(${v})`;
|
||||
case M.$Double: return `_.Float.isDouble(${v})`;
|
||||
case M.$SignedInteger: return `typeof ${v} === 'number'`;
|
||||
case M.$String: return `typeof ${v} === 'string'`;
|
||||
case M.$ByteString: return `_.Bytes.isBytes(${v})`;
|
||||
case M.$Symbol: return `typeof ${v} === 'symbol'`;
|
||||
}
|
||||
case M.$lit:
|
||||
return `_.is(${v}, ${ctx.mod.literal(p[0])})`;
|
||||
case M.$ref:
|
||||
return M.lookup(refPosition(p), p, ctx.mod.env,
|
||||
(_p) => `is${M.Ref._.name(p).description!}(${v})`,
|
||||
(pp) => predicateFor(ctx, v, pp),
|
||||
(modId, modPath, _p) => {
|
||||
ctx.mod.imports.add([modId, modPath]);
|
||||
return `${modId}.is${M.Ref._.name(p).description!}(${v})`;
|
||||
});
|
||||
case M.$or: {
|
||||
const alts = p[0];
|
||||
const recs = alts.map(p => ctx.mod.derefPattern(p));
|
||||
if (recs.length > 1 && recs.every(pp => pp.label === M.$rec)) {
|
||||
return seq(
|
||||
`_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(${v}) && `,
|
||||
parens(opseq('false', ' || ',
|
||||
... recs.map(r =>
|
||||
(r.label !== M.$rec) ? '' : parens(seq(
|
||||
predicateFor(ctx, `${v}.label`, M.unname(r[0])),
|
||||
' && ',
|
||||
predicateFor(ctx, v, M.unname(r[1]), true)))))));
|
||||
} else {
|
||||
return opseq('false', ' || ', ... p[0].map(pp => predicateFor(ctx, v, pp[1])));
|
||||
}
|
||||
}
|
||||
case M.$and:
|
||||
return opseq('true', ' && ', ...p[0].map(pp => predicateFor(ctx, v, M.unname(pp))));
|
||||
case M.$pointer:
|
||||
return `_.isPointer(${v})`;
|
||||
case M.$rec:
|
||||
return opseq('true', ' && ',
|
||||
`_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(${v})`,
|
||||
predicateFor(ctx, `${v}.label`, M.unname(p[0])),
|
||||
predicateFor(ctx, v, M.unname(p[1]), true));
|
||||
case M.$tuple:
|
||||
return opseq('true', ' && ',
|
||||
... (recordOkAsTuple ? []
|
||||
: [`_.Array.isArray(${v})`, `!_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(${v})`]),
|
||||
`(${v}.length === ${p[0].length})`,
|
||||
...p[0].map((pp, i) => predicateFor(ctx, `${v}[${i}]`, M.unname(pp))));
|
||||
case M.$tuple_STAR_:
|
||||
return opseq('true', ' && ',
|
||||
... (recordOkAsTuple ? []
|
||||
: [`_.Array.isArray(${v})`, `!_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(${v})`]),
|
||||
`(${v}.length >= ${p[0].length})`,
|
||||
seq(p[0].length > 0 ? `${v}.slice(${p[0].length})` : v,
|
||||
`.every(v => `,
|
||||
parens(predicateFor(ctx, 'v', M.unname(p[1]))),
|
||||
`)`),
|
||||
...p[0].map((pp, i) => predicateFor(ctx, `${v}[${i}]`, M.unname(pp))));
|
||||
case M.$setof:
|
||||
return opseq('true', ' && ',
|
||||
`_.Set.isSet<_val>(${v})`,
|
||||
fnblock(
|
||||
seq(`for (const vv of ${v}) `, block(
|
||||
seq('if (!(', predicateFor(ctx, 'vv', p[0]), ')) return false'))),
|
||||
seq('return true')));
|
||||
case M.$dictof:
|
||||
return opseq('true', ' && ',
|
||||
`_.Dictionary.isDictionary<_ptr>(${v})`,
|
||||
fnblock(
|
||||
seq(`for (const e of ${v}) `, block(
|
||||
seq('if (!(', predicateFor(ctx, 'e[0]', p[0]), ')) return false'),
|
||||
seq('if (!(', predicateFor(ctx, 'e[1]', p[1]), ')) return false'))),
|
||||
seq('return true')));
|
||||
case M.$dict:
|
||||
return opseq('true', ' && ',
|
||||
`_.Dictionary.isDictionary<_ptr>(${v})`,
|
||||
... Array.from(p[0]).map(([k, vp]) => {
|
||||
const tmp = ctx.gentemp();
|
||||
return parens(seq(
|
||||
`(${tmp} = ${v}.get(${ctx.mod.literal(k)})) !== void 0 && `,
|
||||
predicateFor(ctx, tmp, M.unname(vp))));
|
||||
}));
|
||||
default:
|
||||
((_p: never) => {})(p);
|
||||
throw new Error("Unreachable");
|
||||
}
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
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";
|
||||
import { ModuleContext, variantFor, variantInitFor } from "./context";
|
||||
|
||||
export function typeFor(mod: ModuleContext, p: M.Pattern, variantName?: string): Item {
|
||||
let typeItem: Item;
|
||||
|
@ -71,7 +70,7 @@ function typeForSimple(mod: ModuleContext, p: M.SimplePattern): Item {
|
|||
|
||||
function typeField(mod: ModuleContext, n: M.NamedPattern): Item[] {
|
||||
return (n.label === M.$named)
|
||||
? [keyvalue(stringify(n[0]), typeForSimple(mod, n[1]))]
|
||||
? [keyvalue(n[0].description!, typeForSimple(mod, n[1]))]
|
||||
: (M.isCompoundPattern(n)
|
||||
? typeForCompound(mod, n)
|
||||
: []);
|
||||
|
@ -87,7 +86,7 @@ function typeForCompound(mod: ModuleContext, p: M.CompoundPattern): Item[] {
|
|||
const n = p[1];
|
||||
return [... p[0].flatMap(pp => typeField(mod, pp)),
|
||||
... ((n.label === M.$named)
|
||||
? [keyvalue(stringify(n[0]),
|
||||
? [keyvalue(n[0].description!,
|
||||
seq('Array<', typeForSimple(mod, n[1]), '>'))]
|
||||
: [])];
|
||||
}
|
||||
|
@ -101,7 +100,7 @@ function typeForCompound(mod: ModuleContext, p: M.CompoundPattern): Item[] {
|
|||
} else {
|
||||
const s = M.namelike(k);
|
||||
if (s !== void 0) {
|
||||
return [keyvalue(JSON.stringify(s), typeForSimple(mod, n))];
|
||||
return [keyvalue(s, typeForSimple(mod, n))];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
|
@ -121,14 +120,6 @@ export function typeForDefinition(mod: ModuleContext, d: M.Definition): Item {
|
|||
}
|
||||
}
|
||||
|
||||
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', ' & ',
|
||||
|
|
|
@ -83,6 +83,25 @@ export function unname<R extends M.Pattern | M.SimplePattern>(
|
|||
return (p.label === M.$named) ? p[1] : p;
|
||||
}
|
||||
|
||||
export function nameFor<R extends M.Pattern | M.SimplePattern>(
|
||||
p: M.NamedSimplePattern_ | R): string | undefined
|
||||
{
|
||||
return (p.label === M.$named) ? stringify(p[0]) : void 0;
|
||||
}
|
||||
|
||||
export function addNameIfAbsent(p: M.NamedSimplePattern, k: Input): M.NamedSimplePattern {
|
||||
if (p.label === M.$named) {
|
||||
return p;
|
||||
} else {
|
||||
const s = namelike(k);
|
||||
if (s !== void 0) {
|
||||
return M.NamedSimplePattern_(Symbol.for(s), p);
|
||||
} else {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Simple arrays at toplevel for convenience
|
||||
//
|
||||
export function simpleArray(p: M.CompoundPattern): M.SimplePattern | undefined {
|
||||
|
|
|
@ -19,7 +19,7 @@ Definitions = { symbol: Definition ...:... }.
|
|||
; and the empty pattern is <or []>
|
||||
Definition = <or [@patterns NamedAlternative ...]> / Alternative .
|
||||
|
||||
NamedAlternative = [@variant string @alternative Alternative].
|
||||
NamedAlternative = [@variantLabel string @alternative Alternative].
|
||||
|
||||
; Pattern & Pattern & ...
|
||||
; and the universal pattern, "any", is <and []>
|
||||
|
|
Loading…
Reference in New Issue