From 10380e451aa9bf80f5a9008c544abc0853e9b832 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Thu, 20 May 2021 22:34:20 +0200 Subject: [PATCH] Simplify schemas by allowing at most one of "&" or "/" in a definition --- .../packages/schema/src/compiler.ts | 2 +- .../packages/schema/src/compiler/context.ts | 11 +- .../schema/src/compiler/genconverter.ts | 73 ++++----- .../packages/schema/src/compiler/gentype.ts | 28 ++-- .../schema/src/compiler/genunconverter.ts | 96 ++++++------ .../packages/schema/src/gen/schema.ts | 146 +++++++----------- .../javascript/packages/schema/src/meta.ts | 3 - .../javascript/packages/schema/src/reader.ts | 117 ++++++-------- schema/schema.bin | 6 +- schema/schema.prs | 17 +- 10 files changed, 224 insertions(+), 275 deletions(-) diff --git a/implementations/javascript/packages/schema/src/compiler.ts b/implementations/javascript/packages/schema/src/compiler.ts index 4c63e25..adc7fbb 100644 --- a/implementations/javascript/packages/schema/src/compiler.ts +++ b/implementations/javascript/packages/schema/src/compiler.ts @@ -16,7 +16,7 @@ export function compile(env: M.Environment, schema: M.Schema, options: CompilerO mod.defineType(seq(`export type _embedded = `, renderType(embeddedName._variant === 'false' ? Type.ref('any') - : typeForDefinition(mod, M.Definition.Alternative(M.Alternative.Pattern(M.Pattern.SimplePattern(M.SimplePattern.Ref(embeddedName.value)))))), + : typeForDefinition(mod, M.Definition.Pattern(M.Pattern.SimplePattern(M.SimplePattern.Ref(embeddedName.value))))), `;`)); mod.defineType(`export type _val = _.Value<_embedded>;`); diff --git a/implementations/javascript/packages/schema/src/compiler/context.ts b/implementations/javascript/packages/schema/src/compiler/context.ts index e10b306..692b07b 100644 --- a/implementations/javascript/packages/schema/src/compiler/context.ts +++ b/implementations/javascript/packages/schema/src/compiler/context.ts @@ -46,13 +46,12 @@ export class ModuleContext { if (refCount > RECURSION_LIMIT) { throw new Error('Recursion limit exceeded'); } - if (p._variant === 'Alternative' && - p.value._variant === 'Pattern' && - p.value.value._variant === 'SimplePattern' && - p.value.value.value._variant === 'Ref') + if (p._variant === 'Pattern' && + p.value._variant === 'SimplePattern' && + p.value.value._variant === 'Ref') { - return M.lookup(refPosition(p.value.value.value.value), - p.value.value.value.value, + return M.lookup(refPosition(p.value.value.value), + p.value.value.value, this.env, (p) => this.derefPattern(p, refCount + 1), (_modId, _modPath, pp) => this.derefPattern(pp ?? p, refCount + 1)); diff --git a/implementations/javascript/packages/schema/src/compiler/genconverter.ts b/implementations/javascript/packages/schema/src/compiler/genconverter.ts index 4f5ad0c..df9a8c0 100644 --- a/implementations/javascript/packages/schema/src/compiler/genconverter.ts +++ b/implementations/javascript/packages/schema/src/compiler/genconverter.ts @@ -11,53 +11,54 @@ export function converterForDefinition( src: string, dest: string): Item[] { - if (p._variant === 'or') { - const alts = [p.pattern, ... p.patterns]; - function loop(i: number): Item[] { - ctx.variantName = alts[i].variantLabel; - return [... converterForAlternative(ctx, alts[i].alternative, src, dest), - ... ((i < alts.length - 1) - ? [seq(`if (${dest} === void 0) `, ctx.block(() => loop(i + 1)))] - : [])]; + switch (p._variant) { + case 'or': { + const alts = [p.pattern, ... p.patterns]; + function loop(i: number): Item[] { + ctx.variantName = alts[i].variantLabel; + return [... converterForPattern(ctx, alts[i].pattern, src, dest), + ... ((i < alts.length - 1) + ? [seq(`if (${dest} === void 0) `, ctx.block(() => loop(i + 1)))] + : [])]; + } + return loop(0); } - return loop(0); - } else { - ctx.variantName = void 0; - return converterForAlternative(ctx, p.value, src, dest); + case 'and': { + const pcs = [p.pattern, ... p.patterns]; + function loop(i: number): Item[] { + return (i < pcs.length) + ? converterFor(ctx, pcs[i], src, () => loop(i + 1)) + : [ctx.buildCapturedCompound(dest)]; + } + return loop(0); + } + case 'Pattern': + ctx.variantName = void 0; + return converterForPattern(ctx, p.value, src, dest); } } -function converterForAlternative( +function converterForPattern( ctx: FunctionContext, - p: M.Alternative, + p: M.Pattern, src: string, dest: string): Item[] { - if (p._variant === 'and') { - const alts = [p.pattern, ... p.patterns]; - function loop(i: number): Item[] { - return (i < alts.length) - ? converterFor(ctx, alts[i], src, () => loop(i + 1)) - : [ctx.buildCapturedCompound(dest)]; - } - return loop(0); - } else { - return converterFor(ctx, M.NamedPattern.anonymous(p.value), src, simpleValue => { - if (simpleValue === void 0) { + return converterFor(ctx, M.NamedPattern.anonymous(p), src, simpleValue => { + if (simpleValue === void 0) { + return [ctx.buildCapturedCompound(dest)]; + } else if (ctx.variantName !== void 0) { + if (typeFor(ctx.mod, p).kind === 'unit') { return [ctx.buildCapturedCompound(dest)]; - } else if (ctx.variantName !== void 0) { - if (typeFor(ctx.mod, p.value).kind === 'unit') { - return [ctx.buildCapturedCompound(dest)]; - } else { - return [ctx.withCapture('value', - simpleValue, - () => ctx.buildCapturedCompound(dest))]; - } } else { - return [`${dest} = ${simpleValue}`]; + return [ctx.withCapture('value', + simpleValue, + () => ctx.buildCapturedCompound(dest))]; } - }); - } + } else { + return [`${dest} = ${simpleValue}`]; + } + }); } function converterForTuple(ctx: FunctionContext, diff --git a/implementations/javascript/packages/schema/src/compiler/gentype.ts b/implementations/javascript/packages/schema/src/compiler/gentype.ts index a9abe6c..a8ffa67 100644 --- a/implementations/javascript/packages/schema/src/compiler/gentype.ts +++ b/implementations/javascript/packages/schema/src/compiler/gentype.ts @@ -4,24 +4,22 @@ import { ModuleContext } from "./context"; import { ANY_TYPE, AtomicType, CollectionType, FieldMap, SimpleType, Type } from "./type"; export function typeForDefinition(mod: ModuleContext, d: M.Definition): Type { - if (d._variant === 'or') { - return Type.union( - new Map([d.pattern, ... d.patterns].map(a => - [a.variantLabel, typeForAlternative(mod, a.alternative)]))); - } else { - return typeForAlternative(mod, d.value); + switch (d._variant) { + case 'or': + return Type.union( + new Map([d.pattern, ... d.patterns].map(a => + [a.variantLabel, typeFor(mod, a.pattern)]))); + case 'and': + return typeForIntersection(mod, [d.pattern, ... d.patterns]); + case 'Pattern': + return typeFor(mod, d.value); } } -export function typeForAlternative(mod: ModuleContext, a: M.Alternative): SimpleType { - if (a._variant === 'and') { - const fs = new Map(); - gatherFields(fs, mod, a.pattern); - a.patterns.forEach(n => gatherFields(fs, mod, n)); - return fs.size > 0 ? Type.record(fs) : Type.unit(); - } else { - return typeFor(mod, a.value); - } +export function typeForIntersection(mod: ModuleContext, ps: M.NamedPattern[]): SimpleType { + const fs = new Map(); + ps.forEach(p => gatherFields(fs, mod, p)); + return fs.size > 0 ? Type.record(fs) : Type.unit(); } export function setType(mod: ModuleContext, p: M.SimplePattern): CollectionType { diff --git a/implementations/javascript/packages/schema/src/compiler/genunconverter.ts b/implementations/javascript/packages/schema/src/compiler/genunconverter.ts index d03855b..332db2f 100644 --- a/implementations/javascript/packages/schema/src/compiler/genunconverter.ts +++ b/implementations/javascript/packages/schema/src/compiler/genunconverter.ts @@ -2,8 +2,8 @@ import { refPosition } from '../reader'; import * as M from '../meta'; import { block, brackets, formatItems, Item, parens, seq } from './block'; import { FunctionContext } from "./context"; -import { typeForAlternative } from './gentype'; import { FieldType, renderType, SimpleType } from './type'; +import { typeFor, typeForIntersection } from './gentype'; export function unconverterForDefinition( ctx: FunctionContext, @@ -11,61 +11,59 @@ export function unconverterForDefinition( def: M.Definition, src: string): Item[] { - if (def._variant === 'or') { - return [seq(`switch (${src}._variant) `, block( - ... [def.pattern, ... def.patterns].map(p => - seq(`case `, JSON.stringify(p.variantLabel), `: `, ctx.block(() => { - const hasValueField = - p.alternative._variant === 'Pattern' && - p.alternative.value._variant === 'SimplePattern'; - return [seq(`return `, unconverterForAlternative( - ctx, name, p.alternative, hasValueField ? `${src}.value` : src))]; - })))))]; - } else { - return [seq(`return `, unconverterForAlternative(ctx, name, def.value, `${src}`))]; + switch (def._variant) { + case 'or': + return [seq(`switch (${src}._variant) `, block( + ... [def.pattern, ... def.patterns].map(p => + seq(`case `, JSON.stringify(p.variantLabel), `: `, ctx.block(() => { + const hasValueField = p.pattern._variant === 'SimplePattern'; + return [seq(`return `, unconverterForPattern( + ctx, name, p.pattern, hasValueField ? `${src}.value` : src))]; + })))))]; + case 'and': { + const ps = [def.pattern , ... def.patterns]; + const t = typeForIntersection(ctx.mod, ps); + const errs: [string, InsufficientInformationError][] = []; + const cs = ps.flatMap(p => { + if (p._variant === 'anonymous' && p.value._variant === 'SimplePattern') { + return []; + } else { + try { + return [unconverterForNamed(ctx, p, src, t)]; + } catch (e) { + if (e instanceof InsufficientInformationError) { + errs.push([name + '/' + (M.nameFor(p) ?? ''), e]); + return []; + } + throw e; + } + } + }); + if (cs.length === 0 || errs.length > 0) { + throw new Error(`Cannot produce unconverter for ${name}: ` + + errs.map(e => `${e[0]}: ${e[1].message}`).join(', ')); + } + return [seq(`return `, (cs.length === 1) + ? cs[0] + : seq(`_.merge`, parens(`(a, b) => (a === b) ? a : void 0`, ... cs)))]; + } + case 'Pattern': + return [seq(`return `, unconverterForPattern(ctx, name, def.value, `${src}`))]; } } export class InsufficientInformationError extends Error {} -function unconverterForAlternative( - ctx: FunctionContext, - name: string, - a: M.Alternative, - src: string): Item +function unconverterForPattern(ctx: FunctionContext, name: string, a: M.Pattern, src: string): Item { - const t = typeForAlternative(ctx.mod, a); - if (a._variant === 'and') { - const errs: [string, InsufficientInformationError][] = []; - const cs = [a.pattern, ... a.patterns].flatMap(p => { - if (p._variant === 'anonymous' && p.value._variant === 'SimplePattern') { - return []; - } else { - try { - return [unconverterForNamed(ctx, p, src, t)]; - } catch (e) { - if (e instanceof InsufficientInformationError) { - errs.push([name + '/' + (M.nameFor(p) ?? ''), e]); - return []; - } - throw e; - } - } - }); - if (cs.length === 0 || errs.length > 0) { - throw new Error(`Cannot produce unconverter for ${name}: ` + - errs.map(e => `${e[0]}: ${e[1].message}`).join(', ')); - } - return (cs.length === 1) ? cs[0] : seq(`_.merge`, parens(`(a, b) => (a === b) ? a : void 0`, ... cs)); - } else { - try { - return unconverterFor(ctx, a.value, src, t); - } catch (e) { - if (e instanceof InsufficientInformationError) { - throw new Error(`Cannot produce unconverter for ${name}: ${e.message}`); - } - throw e; + const t = typeFor(ctx.mod, a); + try { + return unconverterFor(ctx, a, src, t); + } catch (e) { + if (e instanceof InsufficientInformationError) { + throw new Error(`Cannot produce unconverter for ${name}: ${e.message}`); } + throw e; } } diff --git a/implementations/javascript/packages/schema/src/gen/schema.ts b/implementations/javascript/packages/schema/src/gen/schema.ts index bd9b96e..fc9585b 100644 --- a/implementations/javascript/packages/schema/src/gen/schema.ts +++ b/implementations/javascript/packages/schema/src/gen/schema.ts @@ -55,12 +55,6 @@ export type Definition = ( "pattern": NamedAlternative, "patterns": Array } | - {"_variant": "Alternative", "value": Alternative} -); - -export type NamedAlternative = {"variantLabel": string, "alternative": Alternative}; - -export type Alternative = ( {"_variant": "and", "pattern": NamedPattern, "patterns": Array} | {"_variant": "Pattern", "value": Pattern} ); @@ -103,6 +97,8 @@ export type AtomKind = ( {"_variant": "Symbol"} ); +export type NamedAlternative = {"variantLabel": string, "pattern": Pattern}; + export type NamedSimplePattern = ( {"_variant": "named", "value": NamedSimplePattern_} | {"_variant": "anonymous", "value": SimplePattern} @@ -143,14 +139,8 @@ export namespace Definition { export function or( {pattern, patterns}: {pattern: NamedAlternative, patterns: Array} ): Definition {return {"_variant": "or", "pattern": pattern, "patterns": patterns};}; - export function Alternative(value: Alternative): Definition {return {"_variant": "Alternative", "value": value};}; -} - -export function NamedAlternative({variantLabel, alternative}: {variantLabel: string, alternative: Alternative}): NamedAlternative {return {"variantLabel": variantLabel, "alternative": alternative};} - -export namespace Alternative { - export function and({pattern, patterns}: {pattern: NamedPattern, patterns: Array}): Alternative {return {"_variant": "and", "pattern": pattern, "patterns": patterns};}; - export function Pattern(value: Pattern): Alternative {return {"_variant": "Pattern", "value": value};}; + export function and({pattern, patterns}: {pattern: NamedPattern, patterns: Array}): Definition {return {"_variant": "and", "pattern": pattern, "patterns": patterns};}; + export function Pattern(value: Pattern): Definition {return {"_variant": "Pattern", "value": value};}; } export namespace Pattern { @@ -187,6 +177,8 @@ export namespace AtomKind { export function Symbol(): AtomKind {return {"_variant": "Symbol"};}; } +export function NamedAlternative({variantLabel, pattern}: {variantLabel: string, pattern: Pattern}): NamedAlternative {return {"variantLabel": variantLabel, "pattern": pattern};} + export namespace NamedSimplePattern { export function named(value: NamedSimplePattern_): NamedSimplePattern {return {"_variant": "named", "value": value};}; export function anonymous(value: SimplePattern): NamedSimplePattern {return {"_variant": "anonymous", "value": value};}; @@ -415,9 +407,37 @@ export function toDefinition(v: _val): undefined | Definition { }; }; if (result === void 0) { - let _tmp6: (Alternative) | undefined; - _tmp6 = toAlternative(v); - if (_tmp6 !== void 0) {result = {"_variant": "Alternative", "value": _tmp6};}; + if (_.Record.isRecord<_val, _.Tuple<_val>, _embedded>(v)) { + let _tmp6: (null) | undefined; + _tmp6 = _.is(v.label, $and) ? null : void 0; + if (_tmp6 !== void 0) { + if (_.Array.isArray(v[0]) && v[0].length >= 1) { + let _tmp7: (NamedPattern) | undefined; + _tmp7 = toNamedPattern(v[0][0]); + if (_tmp7 !== void 0) { + let _tmp8: (Array<_val>) | undefined; + let _tmp9: (Array) | undefined; + _tmp8 = v[0].slice(1); + { + _tmp9 = []; + for (const _tmp10 of _tmp8) { + let _tmp11: (NamedPattern) | undefined; + _tmp11 = toNamedPattern(_tmp10); + if (_tmp11 !== void 0) {_tmp9.push(_tmp11); continue;}; + _tmp9 = void 0; + break; + }; + if (_tmp9 !== void 0) {result = {"_variant": "and", "pattern": _tmp7, "patterns": _tmp9};}; + }; + }; + }; + }; + }; + if (result === void 0) { + let _tmp12: (Pattern) | undefined; + _tmp12 = toPattern(v); + if (_tmp12 !== void 0) {result = {"_variant": "Pattern", "value": _tmp12};}; + }; }; return result; } @@ -435,76 +455,6 @@ export function fromDefinition(_v: Definition): _val { ] ); }; - case "Alternative": {return fromAlternative(_v.value);}; - }; -} - -export function asNamedAlternative(v: _val): NamedAlternative { - let result = toNamedAlternative(v); - if (result === void 0) throw new TypeError(`Invalid NamedAlternative: ${_.stringify(v)}`); - return result; -} - -export function toNamedAlternative(v: _val): undefined | NamedAlternative { - let result: undefined | NamedAlternative; - if (_.Array.isArray(v) && v.length === 2) { - let _tmp0: (string) | undefined; - _tmp0 = typeof v[0] === 'string' ? v[0] : void 0; - if (_tmp0 !== void 0) { - let _tmp1: (Alternative) | undefined; - _tmp1 = toAlternative(v[1]); - if (_tmp1 !== void 0) {result = {"variantLabel": _tmp0, "alternative": _tmp1};}; - }; - }; - return result; -} - -export function fromNamedAlternative(_v: NamedAlternative): _val {return [_v["variantLabel"], fromAlternative(_v["alternative"])];} - -export function asAlternative(v: _val): Alternative { - let result = toAlternative(v); - if (result === void 0) throw new TypeError(`Invalid Alternative: ${_.stringify(v)}`); - return result; -} - -export function toAlternative(v: _val): undefined | Alternative { - let result: undefined | Alternative; - if (_.Record.isRecord<_val, _.Tuple<_val>, _embedded>(v)) { - let _tmp0: (null) | undefined; - _tmp0 = _.is(v.label, $and) ? null : void 0; - if (_tmp0 !== void 0) { - if (_.Array.isArray(v[0]) && v[0].length >= 1) { - let _tmp1: (NamedPattern) | undefined; - _tmp1 = toNamedPattern(v[0][0]); - if (_tmp1 !== void 0) { - let _tmp2: (Array<_val>) | undefined; - let _tmp3: (Array) | undefined; - _tmp2 = v[0].slice(1); - { - _tmp3 = []; - for (const _tmp4 of _tmp2) { - let _tmp5: (NamedPattern) | undefined; - _tmp5 = toNamedPattern(_tmp4); - if (_tmp5 !== void 0) {_tmp3.push(_tmp5); continue;}; - _tmp3 = void 0; - break; - }; - if (_tmp3 !== void 0) {result = {"_variant": "and", "pattern": _tmp1, "patterns": _tmp3};}; - }; - }; - }; - }; - }; - if (result === void 0) { - let _tmp6: (Pattern) | undefined; - _tmp6 = toPattern(v); - if (_tmp6 !== void 0) {result = {"_variant": "Pattern", "value": _tmp6};}; - }; - return result; -} - -export function fromAlternative(_v: Alternative): _val { - switch (_v._variant) { case "and": { return _.Record( $and, @@ -827,6 +777,28 @@ export function fromAtomKind(_v: AtomKind): _val { }; } +export function asNamedAlternative(v: _val): NamedAlternative { + let result = toNamedAlternative(v); + if (result === void 0) throw new TypeError(`Invalid NamedAlternative: ${_.stringify(v)}`); + return result; +} + +export function toNamedAlternative(v: _val): undefined | NamedAlternative { + let result: undefined | NamedAlternative; + if (_.Array.isArray(v) && v.length === 2) { + let _tmp0: (string) | undefined; + _tmp0 = typeof v[0] === 'string' ? v[0] : void 0; + if (_tmp0 !== void 0) { + let _tmp1: (Pattern) | undefined; + _tmp1 = toPattern(v[1]); + if (_tmp1 !== void 0) {result = {"variantLabel": _tmp0, "pattern": _tmp1};}; + }; + }; + return result; +} + +export function fromNamedAlternative(_v: NamedAlternative): _val {return [_v["variantLabel"], fromPattern(_v["pattern"])];} + export function asNamedSimplePattern(v: _val): NamedSimplePattern { let result = toNamedSimplePattern(v); if (result === void 0) throw new TypeError(`Invalid NamedSimplePattern: ${_.stringify(v)}`); diff --git a/implementations/javascript/packages/schema/src/meta.ts b/implementations/javascript/packages/schema/src/meta.ts index 708c139..8d839b1 100644 --- a/implementations/javascript/packages/schema/src/meta.ts +++ b/implementations/javascript/packages/schema/src/meta.ts @@ -1,13 +1,10 @@ import { is, Position } from '@preserves/core'; import * as M from './gen/schema'; import { SchemaSyntaxError } from './error'; -import type { AtomicType } from './compiler/type'; import { isJsKeyword } from './compiler/jskw'; export * from './gen/schema'; -export type Builtin = { type: AtomicType, pattern: M.Alternative }; - export type Input = M._val; export function isValidToken(s: string): boolean { diff --git a/implementations/javascript/packages/schema/src/reader.ts b/implementations/javascript/packages/schema/src/reader.ts index b35fb68..fcefdf6 100644 --- a/implementations/javascript/packages/schema/src/reader.ts +++ b/implementations/javascript/packages/schema/src/reader.ts @@ -1,5 +1,5 @@ import { Reader, Annotated, Dictionary, is, peel, preserves, Record, strip, Tuple, Position, position, ReaderOptions, stringify, isCompound, KeyedDictionary } from '@preserves/core'; -import { Input, Pattern, Schema, Alternative, Definition, CompoundPattern, SimplePattern } from './meta'; +import { Input, Pattern, Schema, Definition, CompoundPattern, SimplePattern } from './meta'; import * as M from './meta'; import { SchemaSyntaxError } from './error'; @@ -71,9 +71,7 @@ export function parseSchema(toplevelTokens: Array, function process(toplevelTokens: Array): void { const toplevelClauses = splitBy(peel(toplevelTokens) as Array, M.DOT); for (const clause of toplevelClauses) { - if (!Array.isArray(clause)) { - invalidClause(clause); - } else if (clause.length >= 2 && is(clause[1], M.EQUALS)) { + if (clause.length >= 2 && is(clause[1], M.EQUALS)) { const pos = position(clause[0]); const name = peel(clause[0]); if (typeof name !== 'symbol') invalidClause(clause); @@ -126,51 +124,45 @@ function namedMustBeSimple(p: Position | null): never { } function parseDefinition(name: symbol, pos: Position | null, body: Array): Definition { - let nextAnonymousAlternativeNumber = 0; - function alternativeName([input, p]: readonly [Array, Alternative]) - : M.NamedAlternative + function alternativeName(input: Array): M.NamedAlternative { const n = findName(input) || findName(input[0]); + const p = parsePattern(name, input); if (n !== false) { - return M.NamedAlternative({ variantLabel: n.description!, alternative: p }); + return M.NamedAlternative({ variantLabel: n.description!, pattern: p }); } - if (p._variant === 'Pattern' && - p.value._variant === 'CompoundPattern' && - p.value.value._variant === 'rec' && - p.value.value.label._variant === 'anonymous' && - p.value.value.label.value._variant === 'SimplePattern' && - p.value.value.label.value.value._variant === 'lit' && - typeof p.value.value.label.value.value.value === 'symbol') + if (p._variant === 'CompoundPattern' && + p.value._variant === 'rec' && + p.value.label._variant === 'anonymous' && + p.value.label.value._variant === 'SimplePattern' && + p.value.label.value.value._variant === 'lit' && + typeof p.value.label.value.value.value === 'symbol') { return M.NamedAlternative({ - variantLabel: p.value.value.label.value.value.value.description!, - alternative: p + variantLabel: p.value.label.value.value.value.description!, + pattern: p }); } - if (p._variant === 'Pattern' && - p.value._variant === 'SimplePattern' && - p.value.value._variant === 'Ref') + if (p._variant === 'SimplePattern' && + p.value._variant === 'Ref') { return M.NamedAlternative({ - variantLabel: p.value.value.value.name.description!, - alternative: p + variantLabel: p.value.value.name.description!, + pattern: p }); } - if (p._variant === 'Pattern' && - p.value._variant === 'SimplePattern' && - p.value.value._variant === 'lit') + if (p._variant === 'SimplePattern' && + p.value._variant === 'lit') { - const s = M.namelike(p.value.value.value); - if (s !== void 0) return M.NamedAlternative({ variantLabel: s, alternative: p }); + const s = M.namelike(p.value.value); + if (s !== void 0) return M.NamedAlternative({ variantLabel: s, pattern: p }); } - return M.NamedAlternative({ - variantLabel: '_anonymous' + nextAnonymousAlternativeNumber++, - alternative: p - }); + throw new SchemaSyntaxError(preserves`Name missing for alternative: ${input}`, pos); } - function patternName([input, p]: readonly [Array, Pattern]) : M.NamedPattern { + function patternName(input: Array): M.NamedPattern { const n = findName(input) || findName(input[0]); + const p = parsePattern(name, input); if (n !== false) { if (p._variant !== 'SimplePattern') namedMustBeSimple(position(input[0])); return M.NamedPattern.named(M.NamedSimplePattern_({ name: n, pattern: p.value })); @@ -178,28 +170,32 @@ function parseDefinition(name: symbol, pos: Position | null, body: Array) return M.NamedPattern.anonymous(p); } - // TODO: deal with situation where there's an or of ands, where - // the branches of the and are named. The parsing is ambiguous, and - // with the current code I think (?) you end up with the same name - // attached to the or-branch as to the leftmost and-branch. + const andPieces = splitBy(body, M.ANDSYM); + const orPieces = splitBy(body, M.ORSYM); - return parseOp(pos, - body, - M.ORSYM, - p => [p, parseOp(pos, - p, - M.ANDSYM, - p => [p, parsePattern(name, p)] as const, - (p0, ps) => M.Alternative.and({ - pattern: patternName(p0), - patterns: ps.map(patternName) - }), - p => M.Alternative.Pattern(p[1]))] as const, - (p0, ps) => M.Definition.or({ - pattern: alternativeName(p0), - patterns: ps.map(alternativeName) - }), - p => M.Definition.Alternative(p[1])); + if (andPieces.length === 0 || orPieces.length === 0) { + throw new SchemaSyntaxError(preserves`Invalid Schema clause: ${body}`, pos); + } + + if (andPieces.length > 1 && orPieces.length > 1) { + throw new SchemaSyntaxError(preserves`Mixed "or" and "and" clause: ${body}`, pos); + } + + if (andPieces.length > 1) { + return M.Definition.and({ + pattern: patternName(andPieces[0]), + patterns: andPieces.slice(1).map(patternName), + }); + } + + if (orPieces.length > 1) { + return M.Definition.or({ + pattern: alternativeName(orPieces[0]), + patterns: orPieces.slice(1).map(alternativeName), + }); + } + + return M.Definition.Pattern(parsePattern(name, orPieces[0])); } function parsePattern(name: symbol, body0: Array): Pattern { @@ -348,21 +344,6 @@ function parsePattern(name: symbol, body0: Array): Pattern { () => M.Pattern.CompoundPattern(parseCompound(body[0]))); } -function parseOp(pos: Position | null, - body: Array, - op: Input, - each: (p: Array) => Each, - combineN: (p0: Each, ps: Array) => Combined, - finish1: (p: Each) => Combined): Combined -{ - const pieces = splitBy(body, op).map(each); - if (pieces.length === 0) { - throw new SchemaSyntaxError( - preserves`Invalid Schema clause: cannot handle empty ${op}`, pos); - } - return (pieces.length === 1) ? finish1(pieces[0]) : combineN(pieces[0], pieces.slice(1)); -} - function findName(x: Input): symbol | false { if (!Annotated.isAnnotated(x)) return false; for (const a0 of x.annotations) { diff --git a/schema/schema.bin b/schema/schema.bin index 5f613fb..2c1b1df 100644 --- a/schema/schema.bin +++ b/schema/schema.bin @@ -3,6 +3,6 @@ ModulePath ModulePath„´łrefµ„łSchema„„łPattern´łorµµ± SimplePattern´łrefµ„ł SimplePattern„„µ±CompoundPattern´łrefµ„łCompoundPattern„„„„łVersion´łlit‘„łAtomKind´łorµµ±Boolean´łlitłBoolean„„µ±Float´łlitłFloat„„µ±Double´łlitłDouble„„µ± SignedInteger´łlitł SignedInteger„„µ±String´łlitłString„„µ± ByteString´łlitł ByteString„„µ±Symbol´łlitłSymbol„„„„ł -Definition´łorµµ±or´łrec´łlitłor„´łtupleµ´łtuple*µ´łnamedłpattern´łrefµ„łNamedAlternative„„„´łnamedłpatterns´łrefµ„łNamedAlternative„„„„„„„µ± Alternative´łrefµ„ł Alternative„„„„ł -ModulePath´łtuple*µ„´łatomłSymbol„„ł Alternative´łorµµ±and´łrec´łlitłand„´łtupleµ´łtuple*µ´łnamedłpattern´łrefµ„ł NamedPattern„„„´łnamedłpatterns´łrefµ„ł NamedPattern„„„„„„„µ±Pattern´łrefµ„łPattern„„„„ł Definitions´łdictof´łatomłSymbol„´łrefµ„ł -Definition„„ł NamedPattern´łorµµ±named´łrefµ„łNamedSimplePattern_„„µ± anonymous´łrefµ„łPattern„„„„ł SimplePattern´łorµµ±any´łlitłany„„µ±atom´łrec´łlitłatom„´łtupleµ´łnamedłatomKind´łrefµ„łAtomKind„„„„„„µ±embedded´łrec´łlitłembedded„´łtupleµ„„„„µ±lit´łrec´łlitłlit„´łtupleµ´łnamedłvaluełany„„„„„µ±Ref´łrefµ„łRef„„„„łCompoundPattern´łorµµ±rec´łrec´łlitłrec„´łtupleµ´łnamedłlabel´łrefµ„ł NamedPattern„„´łnamedłfields´łrefµ„ł NamedPattern„„„„„„µ±tuple´łrec´łlitłtuple„´łtupleµ´łtuple*µ„´łnamedłpatterns´łrefµ„ł NamedPattern„„„„„„„µ±tuple*´łrec´łlitłtuple*„´łtupleµ´łtuple*µ„´łnamedłfixed´łrefµ„ł NamedPattern„„„´łnamedłvariable´łrefµ„łNamedSimplePattern„„„„„„µ±setof´łrec´łlitłsetof„´łtupleµ´łnamedłpattern´łrefµ„ł SimplePattern„„„„„„µ±dictof´łrec´łlitłdictof„´łtupleµ´łnamedłkey´łrefµ„ł SimplePattern„„´łnamedłvalue´łrefµ„ł SimplePattern„„„„„„µ±dict´łrec´łlitłdict„´łtupleµ´łnamedłentries´łrefµ„łDictionaryEntries„„„„„„„„łEmbeddedTypeName´łorµµ±Ref´łrefµ„łRef„„µ±false´łlit€„„„„łNamedAlternative´łtupleµ´łnamedł variantLabel´łatomłString„„´łnamedł alternative´łrefµ„ł Alternative„„„„łDictionaryEntries´łdictofłany´łrefµ„łNamedSimplePattern„„łNamedSimplePattern´łorµµ±named´łrefµ„łNamedSimplePattern_„„µ± anonymous´łrefµ„ł SimplePattern„„„„łNamedSimplePattern_´łrec´łlitłnamed„´łtupleµ´łnamedłname´łatomłSymbol„„´łnamedłpattern´łrefµ„ł SimplePattern„„„„„„ł embeddedType€„„ \ No newline at end of file +Definition´łorµµ±or´łrec´łlitłor„´łtupleµ´łtuple*µ´łnamedłpattern´łrefµ„łNamedAlternative„„„´łnamedłpatterns´łrefµ„łNamedAlternative„„„„„„„µ±and´łrec´łlitłand„´łtupleµ´łtuple*µ´łnamedłpattern´łrefµ„ł NamedPattern„„„´łnamedłpatterns´łrefµ„ł NamedPattern„„„„„„„µ±Pattern´łrefµ„łPattern„„„„ł +ModulePath´łtuple*µ„´łatomłSymbol„„ł Definitions´łdictof´łatomłSymbol„´łrefµ„ł +Definition„„ł NamedPattern´łorµµ±named´łrefµ„łNamedSimplePattern_„„µ± anonymous´łrefµ„łPattern„„„„ł SimplePattern´łorµµ±any´łlitłany„„µ±atom´łrec´łlitłatom„´łtupleµ´łnamedłatomKind´łrefµ„łAtomKind„„„„„„µ±embedded´łrec´łlitłembedded„´łtupleµ„„„„µ±lit´łrec´łlitłlit„´łtupleµ´łnamedłvaluełany„„„„„µ±Ref´łrefµ„łRef„„„„łCompoundPattern´łorµµ±rec´łrec´łlitłrec„´łtupleµ´łnamedłlabel´łrefµ„ł NamedPattern„„´łnamedłfields´łrefµ„ł NamedPattern„„„„„„µ±tuple´łrec´łlitłtuple„´łtupleµ´łtuple*µ„´łnamedłpatterns´łrefµ„ł NamedPattern„„„„„„„µ±tuple*´łrec´łlitłtuple*„´łtupleµ´łtuple*µ„´łnamedłfixed´łrefµ„ł NamedPattern„„„´łnamedłvariable´łrefµ„łNamedSimplePattern„„„„„„µ±setof´łrec´łlitłsetof„´łtupleµ´łnamedłpattern´łrefµ„ł SimplePattern„„„„„„µ±dictof´łrec´łlitłdictof„´łtupleµ´łnamedłkey´łrefµ„ł SimplePattern„„´łnamedłvalue´łrefµ„ł SimplePattern„„„„„„µ±dict´łrec´łlitłdict„´łtupleµ´łnamedłentries´łrefµ„łDictionaryEntries„„„„„„„„łEmbeddedTypeName´łorµµ±Ref´łrefµ„łRef„„µ±false´łlit€„„„„łNamedAlternative´łtupleµ´łnamedł variantLabel´łatomłString„„´łnamedłpattern´łrefµ„łPattern„„„„łDictionaryEntries´łdictofłany´łrefµ„łNamedSimplePattern„„łNamedSimplePattern´łorµµ±named´łrefµ„łNamedSimplePattern_„„µ± anonymous´łrefµ„ł SimplePattern„„„„łNamedSimplePattern_´łrec´łlitłnamed„´łtupleµ´łnamedłname´łatomłSymbol„„´łnamedłpattern´łrefµ„ł SimplePattern„„„„„„ł embeddedType€„„ \ No newline at end of file diff --git a/schema/schema.prs b/schema/schema.prs index 8e7a685..6e4f0b9 100644 --- a/schema/schema.prs +++ b/schema/schema.prs @@ -21,15 +21,16 @@ EmbeddedTypeName = Ref / #f. Definitions = { symbol: Definition ...:... }. -; Pattern / Pattern / ... -; and the empty pattern is -Definition = / Alternative . +Definition = + ; Pattern / Pattern / ... + / -NamedAlternative = [@variantLabel string @alternative Alternative]. + ; Pattern & Pattern & ... + / -; Pattern & Pattern & ... -; and the universal pattern, "any", is -Alternative = / Pattern . + ; Pattern + / Pattern +. Pattern = SimplePattern / CompoundPattern . @@ -76,6 +77,8 @@ DictionaryEntries = { any: NamedSimplePattern ...:... }. AtomKind = =Boolean / =Float / =Double / =SignedInteger / =String / =ByteString / =Symbol . +NamedAlternative = [@variantLabel string @pattern Pattern]. + NamedSimplePattern = @named NamedSimplePattern_ / @anonymous SimplePattern . NamedPattern = @named NamedSimplePattern_ / @anonymous Pattern .