From e6f99ae2e12b84ef6b26c45f856a02f705b24e12 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Wed, 17 Mar 2021 12:38:11 +0100 Subject: [PATCH] Prepare for alternative compiler output --- .../javascript/packages/schema/src/base.ts | 2 +- .../packages/schema/src/compiler.ts | 55 ++++++++++++------- .../javascript/packages/schema/src/meta.ts | 6 +- 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/implementations/javascript/packages/schema/src/base.ts b/implementations/javascript/packages/schema/src/base.ts index dc4a593..762d0a6 100644 --- a/implementations/javascript/packages/schema/src/base.ts +++ b/implementations/javascript/packages/schema/src/base.ts @@ -4,7 +4,7 @@ import * as M from './gen/schema'; export const BASE: M.Schema = M.asSchema(Record(M.$schema, [new Dictionary([ [M.$version, 1], [M.$pointer, false], - [M.$definitions, new Dictionary([ + [M.$definitions, new Dictionary([ [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])], diff --git a/implementations/javascript/packages/schema/src/compiler.ts b/implementations/javascript/packages/schema/src/compiler.ts index c3755a0..fdeb4df 100644 --- a/implementations/javascript/packages/schema/src/compiler.ts +++ b/implementations/javascript/packages/schema/src/compiler.ts @@ -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`)); }))); } diff --git a/implementations/javascript/packages/schema/src/meta.ts b/implementations/javascript/packages/schema/src/meta.ts index 3b9cbe6..5d19169 100644 --- a/implementations/javascript/packages/schema/src/meta.ts +++ b/implementations/javascript/packages/schema/src/meta.ts @@ -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(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(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);