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>([
[M.$version, 1],
[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('bool'), Record(M.$atom, [M.$Boolean])],
[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,
recordFields: boolean,
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)));
} else {
emit(block(
seq(`let vN: Array<`, typeFor(variablePattern),
`> | undefined = `, brackets(... temps)),
seq(`let vN: `, typeFor(tuplePattern),
` | undefined = `, brackets(... temps)),
accumulateCompound(variablePattern,
() => [`vN = void 0`],
(t) => [`vN.push(${t})`]),
@ -245,11 +246,11 @@ export function compile(env: Environment, schema: Schema, options: CompilerOptio
break;
case M.$tuple:
// 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;
case M.$tuple_STAR_:
// 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;
case M.$setof:
// 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) {
case M.$atom:
switch (p[0]) {
@ -305,15 +306,11 @@ export function compile(env: Environment, schema: Schema, options: CompilerOptio
case M.$ref:
return lookup(refPosition(p), p, env,
(_p) => p[1].description!,
(p) => typeFor(p),
(p) => typeForAlternative(p),
(mod, modPath,_p) => {
imports.add([mod, modPath]);
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:
return `_ptr`;
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 {
switch (p.label) {
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)));
}
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;
temps = [];
const recognizer = predicateFor('v', pattern);
if (pattern.label === M.$rec &&
pattern[0].label === M.$lit &&
pattern[1].label === M.$tuple)
const recognizer = predicateFor('v', def);
if (def.label === M.$rec &&
def[0].label === M.$lit &&
def[1].label === M.$tuple)
{
types.push(
seq(`export const ${name.description!} = _.Record.makeConstructor<`,
braces(... pattern[1][0].map(fieldEntry)),
`, _ptr>()(${literal(pattern[0][0])}, `,
JSON.stringify(pattern[1][0].map(fieldName)), `);`));
braces(... def[1][0].map(fieldEntry)),
`, _ptr>()(${literal(def[0][0])}, `,
JSON.stringify(def[1][0].map(fieldName)), `);`));
}
types.push(
seq(`export type ${name.description!} = `, typeFor(pattern), `;`));
seq(`export type ${name.description!} = `, typeForDefinition(name, def), `;`));
functions.push(
seq(`export function 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 `,
collectBody(() => {
emit(seq(`let result`));
decoderFor(pattern, 'result');
decoderFor(def, 'result');
emit(seq(`return result`));
})));
}

View File

@ -1,5 +1,5 @@
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 { SchemaSyntaxError } from './error';
@ -42,7 +42,7 @@ export function lookup<R>(namePos: Position | null,
name: Ref,
env: Environment,
kLocal: (p: Definition) => R,
kBase: (p: Definition) => R,
kBase: (p: Alternative) => R,
kOther: (mod: string, modPath: string, p: Definition | null) => R): R
{
for (const e of env) {
@ -67,7 +67,7 @@ export function lookup<R>(namePos: Position | null,
if (Ref._.module(name).length === 0) {
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);