From 5f7123913034b731caee04e92689b1daf1cd4463 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Wed, 17 Mar 2021 19:17:19 +0100 Subject: [PATCH] Further refine schema schema --- .../packages/schema/src/compiler.ts | 37 ++-- .../packages/schema/src/gen/schema.ts | 168 +++++++++++------- .../javascript/packages/schema/src/reader.ts | 22 +-- schema/schema.prs | 12 +- 4 files changed, 148 insertions(+), 91 deletions(-) diff --git a/implementations/javascript/packages/schema/src/compiler.ts b/implementations/javascript/packages/schema/src/compiler.ts index a8b04af..b1236a2 100644 --- a/implementations/javascript/packages/schema/src/compiler.ts +++ b/implementations/javascript/packages/schema/src/compiler.ts @@ -200,12 +200,12 @@ function decoderFor(ctx: FunctionContext, p: Definition, dest: string, recordFie const alt = recs[i]; if (alt.label !== M.$rec) throw new Error("Internal error"); // avoid a cast return [ - seq(`if (`, predicateFor(ctx, label, alt[0]), `) `, ctx.block(() => { + seq(`if (`, predicateFor(ctx, label, unname(alt[0])), `) `, ctx.block(() => { const fs = ctx.gentemp(); - return [... decoderFor(ctx, alt[1], fs, true), + return [... decoderFor(ctx, unname(alt[1]), fs, true), seq(`if (${fs} !== void 0) ${dest} = _.Record`, - anglebrackets(typeFor(ctx.mod, alt[0]), - typeFor(ctx.mod, alt[1])), + anglebrackets(typeFor(ctx.mod, unname(alt[0])), + typeFor(ctx.mod, unname(alt[1]))), parens(seq(label, ` as any`), seq(fs, ` as any`)))]; })), @@ -242,12 +242,13 @@ function decoderFor(ctx: FunctionContext, p: Definition, dest: string, recordFie case M.$and: switch (p[0].length) { case 0: return [`${dest} = d.next()`]; - case 1: return decoderFor(ctx, p[0][0], dest); + case 1: return decoderFor(ctx, unname(p[0][0]), dest); default: { const [pp0, ... ppN] = p[0]; - return [... decoderFor(ctx, pp0, dest), + return [... decoderFor(ctx, unname(pp0), dest), seq(`if (!`, opseq('true', ' && ', - ... ppN.map(pp => predicateFor(ctx, dest, pp))), + ... ppN.map(pp => + predicateFor(ctx, dest, unname(pp)))), `) ${dest} = void 0`)]; } } @@ -257,12 +258,13 @@ function decoderFor(ctx: FunctionContext, p: Definition, dest: string, recordFie // assume dest is already void 0 return [seq(`if (d.openRecord()) `, ctx.block(() => { const label = ctx.gentemp(); - return [... decoderFor(ctx, p[0], label), + return [... decoderFor(ctx, unname(p[0]), label), seq(`if (${label} !== void 0) `, ctx.block(() => { const fs = ctx.gentemp(); - return [... decoderFor(ctx, p[1], fs, true), + return [... decoderFor(ctx, unname(p[1]), fs, true), seq(`if (${fs} !== void 0) ${dest} = _.Record`, - anglebrackets(typeFor(ctx.mod, p[0]), typeFor(ctx.mod, p[1])), + anglebrackets(typeFor(ctx.mod, unname(p[0])), + typeFor(ctx.mod, unname(p[1]))), parens(seq(label, ` as any`), seq(fs, ` as any`)))]; }))]; @@ -330,7 +332,8 @@ function typeFor(mod: ModuleContext, p: Pattern): Item { case M.$pointer: return `_ptr`; case M.$rec: - return seq('_.Record', anglebrackets(typeFor(mod, p[0]), typeFor(mod, p[1]), '_ptr')); + return seq('_.Record', anglebrackets(typeFor(mod, unname(p[0])), + typeFor(mod, unname(p[1])), '_ptr')); case M.$tuple: return brackets(... p[0].map(pp => typeFor(mod, unname(pp)))); case M.$tuple_STAR_: @@ -371,7 +374,7 @@ function typeForDefinition(mod: ModuleContext, _name: symbol, d: Definition): It function typeForAlternative(mod: ModuleContext, a: Alternative): Item { if (a.label === M.$and) { - return opseq('_val', ' & ', ... a[0].map(p => typeFor(mod, p))); + return opseq('_val', ' & ', ... a[0].map(p => typeFor(mod, unname(p)))); } else { return typeFor(mod, a); } @@ -409,22 +412,22 @@ function predicateFor(ctx: FunctionContext, v: string, p: Definition, recordOkAs parens(opseq('false', ' || ', ... recs.map(r => (r.label !== M.$rec) ? '' : parens(seq( - predicateFor(ctx, `${v}.label`, r[0]), + predicateFor(ctx, `${v}.label`, unname(r[0])), ' && ', - predicateFor(ctx, v, r[1], true))))))); + predicateFor(ctx, v, 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, pp))); + return opseq('true', ' && ', ... p[0].map(pp => predicateFor(ctx, v, 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`, p[0]), - predicateFor(ctx, v, p[1], true)); + predicateFor(ctx, `${v}.label`, unname(p[0])), + predicateFor(ctx, v, unname(p[1]), true)); case M.$tuple: return opseq('true', ' && ', ... (recordOkAsTuple ? [] diff --git a/implementations/javascript/packages/schema/src/gen/schema.ts b/implementations/javascript/packages/schema/src/gen/schema.ts index a655ebf..c7968a4 100644 --- a/implementations/javascript/packages/schema/src/gen/schema.ts +++ b/implementations/javascript/packages/schema/src/gen/schema.ts @@ -35,7 +35,7 @@ export const Schema = _.Record.makeConstructor<{ { get(k: typeof $version): Version; get(k: typeof $pointer): PointerName; - get(k: typeof $definitions): _.KeyedDictionary; + get(k: typeof $definitions): Definitions; has(k: typeof $version): true; has(k: typeof $pointer): true; has(k: typeof $definitions): true; @@ -50,7 +50,7 @@ export type Schema = _.Record< { get(k: typeof $version): Version; get(k: typeof $pointer): PointerName; - get(k: typeof $definitions): _.KeyedDictionary; + get(k: typeof $definitions): Definitions; has(k: typeof $version): true; has(k: typeof $pointer): true; has(k: typeof $definitions): true; @@ -64,6 +64,8 @@ export type Version = (typeof $1); export type PointerName = (Ref | (typeof __lit5)); +export type Definitions = _.KeyedDictionary; + export type Definition = (_.Record<(typeof $or), [Array], _ptr> | Alternative); export type NamedAlternative = [symbol, Alternative]; @@ -80,7 +82,7 @@ export type SimplePattern = ( ); export type CompoundPattern = ( - _.Record<(typeof $rec), [Pattern, Pattern], _ptr> | + _.Record<(typeof $rec), [NamedPattern, NamedPattern], _ptr> | _.Record<(typeof $tuple), [Array], _ptr> | _.Record<(typeof $tuple_STAR_), [Array, NamedPattern], _ptr> | _.Record<(typeof $setof), [SimplePattern], _ptr> | @@ -88,7 +90,9 @@ export type CompoundPattern = ( _.Record<(typeof $dict), [DictionaryEntries], _ptr> ); -export type DictionaryEntries = _.KeyedDictionary<_val, NamedPattern, _ptr>; +export type DictionaryEntries = _.KeyedDictionary<_val, DictionaryEntryPattern, _ptr>; + +export type DictionaryEntryPattern = (NamedSimplePattern | SimplePattern); export type AtomKind = ( (typeof $Boolean) | @@ -100,7 +104,11 @@ export type AtomKind = ( (typeof $Symbol) ); -export type NamedPattern = (_.Record<(typeof $named), [symbol, SimplePattern], _ptr> | Pattern); +export type NamedPattern = (NamedSimplePattern | Pattern); + +export const NamedSimplePattern = _.Record.makeConstructor<{"name": symbol, "pattern": SimplePattern}, _ptr>()($named, ["name","pattern"]); + +export type NamedSimplePattern = _.Record<(typeof $named), [symbol, SimplePattern], _ptr>; export const Ref = _.Record.makeConstructor<{"module": ModulePath, "name": symbol}, _ptr>()($ref, ["module","name"]); @@ -122,18 +130,7 @@ export function isSchema(v: any): v is Schema { _.Dictionary.isDictionary<_ptr>(v[0]) && ((_tmp0 = v[0].get($version)) !== void 0 && isVersion(_tmp0)) && ((_tmp1 = v[0].get($pointer)) !== void 0 && isPointerName(_tmp1)) && - ( - (_tmp2 = v[0].get($definitions)) !== void 0 && ( - _.Dictionary.isDictionary<_ptr>(_tmp2) && - ((() => { - for (const e of _tmp2) { - if (!(typeof e[0] === 'symbol')) return false; - if (!(isDefinition(e[1]))) return false; - }; - return true; - })()) - ) - ) + ((_tmp2 = v[0].get($definitions)) !== void 0 && isDefinitions(_tmp2)) ) ) ); @@ -155,18 +152,7 @@ export function decodeSchema(d: _.TypedDecoder<_ptr>): Schema | undefined { _.Dictionary.isDictionary<_ptr>(_tmp2) && ((_tmp3 = _tmp2.get($version)) !== void 0 && isVersion(_tmp3)) && ((_tmp4 = _tmp2.get($pointer)) !== void 0 && isPointerName(_tmp4)) && - ( - (_tmp5 = _tmp2.get($definitions)) !== void 0 && ( - _.Dictionary.isDictionary<_ptr>(_tmp5) && - ((() => { - for (const e of _tmp5) { - if (!(typeof e[0] === 'symbol')) return false; - if (!(isDefinition(e[1]))) return false; - }; - return true; - })()) - ) - ) + ((_tmp5 = _tmp2.get($definitions)) !== void 0 && isDefinitions(_tmp5)) ))) _tmp2 = void 0; if (_tmp2 !== void 0) { if (d.closeCompound()) _tmp1 = [_tmp2] as [ @@ -174,7 +160,7 @@ export function decodeSchema(d: _.TypedDecoder<_ptr>): Schema | undefined { { get(k: typeof $version): Version; get(k: typeof $pointer): PointerName; - get(k: typeof $definitions): _.KeyedDictionary; + get(k: typeof $definitions): Definitions; has(k: typeof $version): true; has(k: typeof $pointer): true; has(k: typeof $definitions): true; @@ -189,7 +175,7 @@ export function decodeSchema(d: _.TypedDecoder<_ptr>): Schema | undefined { { get(k: typeof $version): Version; get(k: typeof $pointer): PointerName; - get(k: typeof $definitions): _.KeyedDictionary; + get(k: typeof $definitions): Definitions; has(k: typeof $version): true; has(k: typeof $pointer): true; has(k: typeof $definitions): true; @@ -225,6 +211,41 @@ export function decodePointerName(d: _.TypedDecoder<_ptr>): PointerName | undefi return result; } +export function isDefinitions(v: any): v is Definitions { + return ( + _.Dictionary.isDictionary<_ptr>(v) && + ((() => { + for (const e of v) { + if (!(typeof e[0] === 'symbol')) return false; + if (!(isDefinition(e[1]))) return false; + }; + return true; + })()) + ); +} + +export function asDefinitions(v: any): Definitions { + if (!isDefinitions(v)) {throw new TypeError(`Invalid Definitions: ${_.stringify(v)}`);} else {return v;}; +} + +export function decodeDefinitions(d: _.TypedDecoder<_ptr>): Definitions | undefined { + let result; + if (d.openDictionary()) { + let r: _.KeyedDictionary | undefined = new _.KeyedDictionary(); + while (!d.closeCompound()) { + let K: undefined | symbol = void 0; + K = d.nextSymbol(); + if (K === void 0) { r = void 0; break; }; + let V: undefined | Definition = void 0; + V = decodeDefinition(d); + if (V === void 0) { r = void 0; break; }; + r.set(K, V); + }; + result = r; + }; + return result; +} + export function isDefinition(v: any): v is Definition { return ( ( @@ -439,7 +460,7 @@ export function isCompoundPattern(v: any): v is CompoundPattern { return _.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) && ( ( ( - _.is(v.label, $rec) && ((v.length === 2) && isPattern(v[0]) && isPattern(v[1])) + _.is(v.label, $rec) && ((v.length === 2) && isNamedPattern(v[0]) && isNamedPattern(v[1])) ) || ( _.is(v.label, $tuple) && ( @@ -485,12 +506,12 @@ export function decodeCompoundPattern(d: _.TypedDecoder<_ptr>): CompoundPattern _tmp1 = d.mark(); if (_.is(_tmp0, $rec)) { let _tmp2, _tmp3, _tmp4: any; - _tmp3 = decodePattern(d); + _tmp3 = decodeNamedPattern(d); if (_tmp3 !== void 0) { - _tmp4 = decodePattern(d); - if (_tmp4 !== void 0) {if (d.closeCompound()) _tmp2 = [_tmp3, _tmp4] as [Pattern, Pattern];}; + _tmp4 = decodeNamedPattern(d); + if (_tmp4 !== void 0) {if (d.closeCompound()) _tmp2 = [_tmp3, _tmp4] as [NamedPattern, NamedPattern];}; }; - if (_tmp2 !== void 0) result = _.Record<(typeof $rec), [Pattern, Pattern]>(_tmp0 as any, _tmp2 as any); + if (_tmp2 !== void 0) result = _.Record<(typeof $rec), [NamedPattern, NamedPattern]>(_tmp0 as any, _tmp2 as any); }; if (result === void 0) { d.restoreMark(_tmp1); @@ -579,7 +600,10 @@ export function isDictionaryEntries(v: any): v is DictionaryEntries { return ( _.Dictionary.isDictionary<_ptr>(v) && ((() => { - for (const e of v) {if (!(true)) return false; if (!(isNamedPattern(e[1]))) return false;}; + for (const e of v) { + if (!(true)) return false; + if (!(isDictionaryEntryPattern(e[1]))) return false; + }; return true; })()) ); @@ -592,13 +616,13 @@ export function asDictionaryEntries(v: any): DictionaryEntries { export function decodeDictionaryEntries(d: _.TypedDecoder<_ptr>): DictionaryEntries | undefined { let result; if (d.openDictionary()) { - let r: _.KeyedDictionary<_val, NamedPattern, _ptr> | undefined = new _.KeyedDictionary(); + let r: _.KeyedDictionary<_val, DictionaryEntryPattern, _ptr> | undefined = new _.KeyedDictionary(); while (!d.closeCompound()) { let K: undefined | _val = void 0; K = d.next(); if (K === void 0) { r = void 0; break; }; - let V: undefined | NamedPattern = void 0; - V = decodeNamedPattern(d); + let V: undefined | DictionaryEntryPattern = void 0; + V = decodeDictionaryEntryPattern(d); if (V === void 0) { r = void 0; break; }; r.set(K, V); }; @@ -607,6 +631,21 @@ export function decodeDictionaryEntries(d: _.TypedDecoder<_ptr>): DictionaryEntr return result; } +export function isDictionaryEntryPattern(v: any): v is DictionaryEntryPattern {return (isNamedSimplePattern(v) || isSimplePattern(v));} + +export function asDictionaryEntryPattern(v: any): DictionaryEntryPattern { + if (!isDictionaryEntryPattern(v)) {throw new TypeError(`Invalid DictionaryEntryPattern: ${_.stringify(v)}`);} else {return v;}; +} + +export function decodeDictionaryEntryPattern(d: _.TypedDecoder<_ptr>): DictionaryEntryPattern | undefined { + let _tmp0: any; + let result; + _tmp0 = d.mark(); + result = decodeNamedSimplePattern(d); + if (result === void 0) {d.restoreMark(_tmp0); result = decodeSimplePattern(d);}; + return result; +} + export function isAtomKind(v: any): v is AtomKind { return ( _.is(v, $Boolean) || @@ -652,16 +691,7 @@ export function decodeAtomKind(d: _.TypedDecoder<_ptr>): AtomKind | undefined { return result; } -export function isNamedPattern(v: any): v is NamedPattern { - return ( - ( - _.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) && - _.is(v.label, $named) && - ((v.length === 2) && typeof v[0] === 'symbol' && isSimplePattern(v[1])) - ) || - isPattern(v) - ); -} +export function isNamedPattern(v: any): v is NamedPattern {return (isNamedSimplePattern(v) || isPattern(v));} export function asNamedPattern(v: any): NamedPattern { if (!isNamedPattern(v)) {throw new TypeError(`Invalid NamedPattern: ${_.stringify(v)}`);} else {return v;}; @@ -671,20 +701,38 @@ export function decodeNamedPattern(d: _.TypedDecoder<_ptr>): NamedPattern | unde let _tmp0: any; let result; _tmp0 = d.mark(); + result = decodeNamedSimplePattern(d); + if (result === void 0) {d.restoreMark(_tmp0); result = decodePattern(d);}; + return result; +} + +export function isNamedSimplePattern(v: any): v is NamedSimplePattern { + return ( + _.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) && + _.is(v.label, $named) && + ((v.length === 2) && typeof v[0] === 'symbol' && isSimplePattern(v[1])) + ); +} + +export function asNamedSimplePattern(v: any): NamedSimplePattern { + if (!isNamedSimplePattern(v)) {throw new TypeError(`Invalid NamedSimplePattern: ${_.stringify(v)}`);} else {return v;}; +} + +export function decodeNamedSimplePattern(d: _.TypedDecoder<_ptr>): NamedSimplePattern | undefined { + let result; if (d.openRecord()) { - let _tmp1: any; - _tmp1 = _.asLiteral(d.nextSymbol(), $named); - if (_tmp1 !== void 0) { - let _tmp2, _tmp3, _tmp4: any; - _tmp3 = d.nextSymbol(); - if (_tmp3 !== void 0) { - _tmp4 = decodeSimplePattern(d); - if (_tmp4 !== void 0) {if (d.closeCompound()) _tmp2 = [_tmp3, _tmp4] as [symbol, SimplePattern];}; + let _tmp0: any; + _tmp0 = _.asLiteral(d.nextSymbol(), $named); + if (_tmp0 !== void 0) { + let _tmp1, _tmp2, _tmp3: any; + _tmp2 = d.nextSymbol(); + if (_tmp2 !== void 0) { + _tmp3 = decodeSimplePattern(d); + if (_tmp3 !== void 0) {if (d.closeCompound()) _tmp1 = [_tmp2, _tmp3] as [symbol, SimplePattern];}; }; - if (_tmp2 !== void 0) result = _.Record<(typeof $named), [symbol, SimplePattern]>(_tmp1 as any, _tmp2 as any); + if (_tmp1 !== void 0) result = _.Record<(typeof $named), [symbol, SimplePattern]>(_tmp0 as any, _tmp1 as any); }; }; - if (result === void 0) {d.restoreMark(_tmp0); result = decodePattern(d);}; return result; } diff --git a/implementations/javascript/packages/schema/src/reader.ts b/implementations/javascript/packages/schema/src/reader.ts index 7d886c5..02bea29 100644 --- a/implementations/javascript/packages/schema/src/reader.ts +++ b/implementations/javascript/packages/schema/src/reader.ts @@ -1,8 +1,7 @@ import { Reader, Annotated, Dictionary, is, peel, preserves, Record, strip, Tuple, Position, position, ReaderOptions, stringify, isCompound } from '@preserves/core'; -import { Input, NamedPattern, Pattern, Schema, Alternative, Definition } from './meta'; +import { Input, Pattern, Schema, Alternative, Definition, CompoundPattern, NamedSimplePattern, SimplePattern } from './meta'; import * as M from './meta'; import { SchemaSyntaxError } from './error'; -import { CompoundPattern, SimplePattern } from 'gen/schema'; const positionTable = new WeakMap(); @@ -193,13 +192,16 @@ function parsePattern(name: symbol, body0: Array): Pattern { }); const walk = (b: Input): Pattern => parsePattern(name, [b]); - function maybeNamed(b: Input): NamedPattern { - const name = findName(b); - if (name === false) return walk(b); - return Record(M.$named, [name, parseSimple(b, () => { - throw new SchemaSyntaxError(`Named patterns must be Simple patterns`, position(b)); - })]); + function _maybeNamed(recur: (b: Input) => R): (b: Input) => NamedSimplePattern | R { + return (b: Input) => { + const name = findName(b); + if (name === false) return recur(b); + return Record(M.$named, [name, parseSimple(b, () => { + throw new SchemaSyntaxError(`Named patterns must be Simple patterns`, position(b)); + })]); + }; } + const maybeNamed = _maybeNamed(walk); if (Record.isRecord, never>(item)) { const label = item.label; @@ -232,8 +234,8 @@ function parsePattern(name: symbol, body0: Array): Pattern { const [[kp, vp]] = v.entries(); return Record(M.$dictof, [walkSimple(kp), walkSimple(vp)]); } else { - return Record(M.$dict, [item.mapEntries( - ([k, vp]) => [strip(k), walk(vp)])]); + return Record(M.$dict, [item.mapEntries( + ([k, vp]) => [strip(k), _maybeNamed(walkSimple)(vp)])]); } } else if (Set.isSet(item)) { if (item.size !== 1) complain(); diff --git a/schema/schema.prs b/schema/schema.prs index 8d4e6d5..3592e8b 100644 --- a/schema/schema.prs +++ b/schema/schema.prs @@ -5,7 +5,7 @@ version 1 . Schema = . ; version 1 . @@ -13,6 +13,8 @@ Version = 1 . PointerName = Ref / #f. +Definitions = { symbol: Definition ...:... }. + ; Pattern / Pattern / ... ; and the empty pattern is Definition = / Alternative . @@ -43,7 +45,7 @@ CompoundPattern = ;