Prepare for alternative compiler output

This commit is contained in:
Tony Garnock-Jones 2021-03-17 12:38:11 +01:00
parent b9019d03f1
commit e6f99ae2e1
3 changed files with 38 additions and 25 deletions

View File

@ -4,7 +4,7 @@ import * as M from './gen/schema';
export const BASE: M.Schema = M.asSchema(Record(M.$schema, [new Dictionary<never>([ export const BASE: M.Schema = M.asSchema(Record(M.$schema, [new Dictionary<never>([
[M.$version, 1], [M.$version, 1],
[M.$pointer, false], [M.$pointer, false],
[M.$definitions, new Dictionary<never>([ [M.$definitions, new Dictionary<never, M.Alternative>([
[Symbol.for('any'), Record(M.$and, [[] as M.Pattern[]])], [Symbol.for('any'), Record(M.$and, [[] as M.Pattern[]])],
[Symbol.for('bool'), Record(M.$atom, [M.$Boolean])], [Symbol.for('bool'), Record(M.$atom, [M.$Boolean])],
[Symbol.for('float'), Record(M.$atom, [M.$Float])], [Symbol.for('float'), Record(M.$atom, [M.$Float])],

View File

@ -88,7 +88,8 @@ export function compile(env: Environment, schema: Schema, options: CompilerOptio
} }
} }
function decoderForTuple(ps: Pattern[], function decoderForTuple(tuplePattern: Pattern,
ps: Pattern[],
dest: string, dest: string,
recordFields: boolean, recordFields: boolean,
variablePattern: Pattern | undefined): void { variablePattern: Pattern | undefined): void {
@ -103,8 +104,8 @@ export function compile(env: Environment, schema: Schema, options: CompilerOptio
emit(seq(`if (d.closeCompound()) ${dest} = `, brackets(... temps))); emit(seq(`if (d.closeCompound()) ${dest} = `, brackets(... temps)));
} else { } else {
emit(block( emit(block(
seq(`let vN: Array<`, typeFor(variablePattern), seq(`let vN: `, typeFor(tuplePattern),
`> | undefined = `, brackets(... temps)), ` | undefined = `, brackets(... temps)),
accumulateCompound(variablePattern, accumulateCompound(variablePattern,
() => [`vN = void 0`], () => [`vN = void 0`],
(t) => [`vN.push(${t})`]), (t) => [`vN.push(${t})`]),
@ -245,11 +246,11 @@ export function compile(env: Environment, schema: Schema, options: CompilerOptio
break; break;
case M.$tuple: case M.$tuple:
// assume dest is already void 0 // assume dest is already void 0
decoderForTuple(p[0].map(unname), dest, recordFields, void 0); decoderForTuple(p, p[0].map(unname), dest, recordFields, void 0);
break; break;
case M.$tuple_STAR_: case M.$tuple_STAR_:
// assume dest is already void 0 // assume dest is already void 0
decoderForTuple(p[0].map(unname), dest, recordFields, unname(p[1])); decoderForTuple(p, p[0].map(unname), dest, recordFields, unname(p[1]));
break; break;
case M.$setof: case M.$setof:
// assume dest is already void 0 // assume dest is already void 0
@ -288,7 +289,7 @@ export function compile(env: Environment, schema: Schema, options: CompilerOptio
} }
} }
function typeFor(p: Definition): Item { function typeFor(p: Pattern): Item {
switch (p.label) { switch (p.label) {
case M.$atom: case M.$atom:
switch (p[0]) { switch (p[0]) {
@ -305,15 +306,11 @@ export function compile(env: Environment, schema: Schema, options: CompilerOptio
case M.$ref: case M.$ref:
return lookup(refPosition(p), p, env, return lookup(refPosition(p), p, env,
(_p) => p[1].description!, (_p) => p[1].description!,
(p) => typeFor(p), (p) => typeForAlternative(p),
(mod, modPath,_p) => { (mod, modPath,_p) => {
imports.add([mod, modPath]); imports.add([mod, modPath]);
return `${mod}.${p[1].description!}`; return `${mod}.${p[1].description!}`;
}); });
case M.$or:
return opseq('never', ' | ', ... p[0].map(pp => typeFor(pp[1])));
case M.$and:
return opseq('_val', ' & ', ... p[0].map(pp => typeFor(pp)));
case M.$pointer: case M.$pointer:
return `_ptr`; return `_ptr`;
case M.$rec: case M.$rec:
@ -345,6 +342,22 @@ export function compile(env: Environment, schema: Schema, options: CompilerOptio
} }
} }
function typeForDefinition(name: symbol, d: Definition): Item {
if (d.label === M.$or) {
return opseq('never', ' | ', ... d[0].map(a => typeForAlternative(a[1])));
} else {
return typeForAlternative(d);
}
}
function typeForAlternative(a: Alternative): Item {
if (a.label === M.$and) {
return opseq('_val', ' & ', ... a[0].map(p => typeFor(p)));
} else {
return typeFor(a);
}
}
function predicateFor(v: string, p: Definition, recordOkAsTuple = false): Item { function predicateFor(v: string, p: Definition, recordOkAsTuple = false): Item {
switch (p.label) { switch (p.label) {
case M.$atom: case M.$atom:
@ -450,22 +463,22 @@ export function compile(env: Environment, schema: Schema, options: CompilerOptio
return seq(JSON.stringify(fieldName(np, index)), ': ', typeFor(unname(np))); return seq(JSON.stringify(fieldName(np, index)), ': ', typeFor(unname(np)));
} }
for (const [name0, pattern] of Schema._.details(schema).get(M.$definitions)) { for (const [name0, def] of Schema._.details(schema).get(M.$definitions)) {
const name = name0 as symbol; const name = name0 as symbol;
temps = []; temps = [];
const recognizer = predicateFor('v', pattern); const recognizer = predicateFor('v', def);
if (pattern.label === M.$rec && if (def.label === M.$rec &&
pattern[0].label === M.$lit && def[0].label === M.$lit &&
pattern[1].label === M.$tuple) def[1].label === M.$tuple)
{ {
types.push( types.push(
seq(`export const ${name.description!} = _.Record.makeConstructor<`, seq(`export const ${name.description!} = _.Record.makeConstructor<`,
braces(... pattern[1][0].map(fieldEntry)), braces(... def[1][0].map(fieldEntry)),
`, _ptr>()(${literal(pattern[0][0])}, `, `, _ptr>()(${literal(def[0][0])}, `,
JSON.stringify(pattern[1][0].map(fieldName)), `);`)); JSON.stringify(def[1][0].map(fieldName)), `);`));
} }
types.push( types.push(
seq(`export type ${name.description!} = `, typeFor(pattern), `;`)); seq(`export type ${name.description!} = `, typeForDefinition(name, def), `;`));
functions.push( functions.push(
seq(`export function is${name.description!}`, seq(`export function is${name.description!}`,
'(v: any): v is ', name.description!, ' ', '(v: any): v is ', name.description!, ' ',
@ -486,7 +499,7 @@ export function compile(env: Environment, schema: Schema, options: CompilerOptio
`(d: _.TypedDecoder<_ptr>): ${name.description!} | undefined `, `(d: _.TypedDecoder<_ptr>): ${name.description!} | undefined `,
collectBody(() => { collectBody(() => {
emit(seq(`let result`)); emit(seq(`let result`));
decoderFor(pattern, 'result'); decoderFor(def, 'result');
emit(seq(`return result`)); emit(seq(`return result`));
}))); })));
} }

View File

@ -1,5 +1,5 @@
import { Value, is, Position } from '@preserves/core'; import { Value, is, Position } from '@preserves/core';
import { ModulePath, Ref, Schema, $definitions, Definition } from './gen/schema'; import { ModulePath, Ref, Schema, $definitions, Definition, Alternative } from './gen/schema';
import { BASE } from './base'; import { BASE } from './base';
import { SchemaSyntaxError } from './error'; import { SchemaSyntaxError } from './error';
@ -42,7 +42,7 @@ export function lookup<R>(namePos: Position | null,
name: Ref, name: Ref,
env: Environment, env: Environment,
kLocal: (p: Definition) => R, kLocal: (p: Definition) => R,
kBase: (p: Definition) => R, kBase: (p: Alternative) => R,
kOther: (mod: string, modPath: string, p: Definition | null) => R): R kOther: (mod: string, modPath: string, p: Definition | null) => R): R
{ {
for (const e of env) { for (const e of env) {
@ -67,7 +67,7 @@ export function lookup<R>(namePos: Position | null,
if (Ref._.module(name).length === 0) { if (Ref._.module(name).length === 0) {
const p = Schema._.details(BASE).get($definitions).get(Ref._.name(name)); const p = Schema._.details(BASE).get($definitions).get(Ref._.name(name));
if (p !== void 0) return kBase(p); if (p !== void 0) return kBase(p as Alternative);
} }
throw new SchemaSyntaxError(`Undefined reference: ${formatRef(name)}`, namePos); throw new SchemaSyntaxError(`Undefined reference: ${formatRef(name)}`, namePos);