Simplify schemas by allowing at most one of "&" or "/" in a definition

This commit is contained in:
Tony Garnock-Jones 2021-05-20 22:34:20 +02:00
parent 436b14e2fe
commit 10380e451a
10 changed files with 224 additions and 275 deletions

View File

@ -16,7 +16,7 @@ export function compile(env: M.Environment, schema: M.Schema, options: CompilerO
mod.defineType(seq(`export type _embedded = `, mod.defineType(seq(`export type _embedded = `,
renderType(embeddedName._variant === 'false' renderType(embeddedName._variant === 'false'
? Type.ref('any') ? 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>;`); mod.defineType(`export type _val = _.Value<_embedded>;`);

View File

@ -46,13 +46,12 @@ export class ModuleContext {
if (refCount > RECURSION_LIMIT) { if (refCount > RECURSION_LIMIT) {
throw new Error('Recursion limit exceeded'); throw new Error('Recursion limit exceeded');
} }
if (p._variant === 'Alternative' && if (p._variant === 'Pattern' &&
p.value._variant === 'Pattern' && p.value._variant === 'SimplePattern' &&
p.value.value._variant === 'SimplePattern' && p.value.value._variant === 'Ref')
p.value.value.value._variant === 'Ref')
{ {
return M.lookup(refPosition(p.value.value.value.value), return M.lookup(refPosition(p.value.value.value),
p.value.value.value.value, p.value.value.value,
this.env, this.env,
(p) => this.derefPattern(p, refCount + 1), (p) => this.derefPattern(p, refCount + 1),
(_modId, _modPath, pp) => this.derefPattern(pp ?? p, refCount + 1)); (_modId, _modPath, pp) => this.derefPattern(pp ?? p, refCount + 1));

View File

@ -11,53 +11,54 @@ export function converterForDefinition(
src: string, src: string,
dest: string): Item[] dest: string): Item[]
{ {
if (p._variant === 'or') { switch (p._variant) {
const alts = [p.pattern, ... p.patterns]; case 'or': {
function loop(i: number): Item[] { const alts = [p.pattern, ... p.patterns];
ctx.variantName = alts[i].variantLabel; function loop(i: number): Item[] {
return [... converterForAlternative(ctx, alts[i].alternative, src, dest), ctx.variantName = alts[i].variantLabel;
... ((i < alts.length - 1) return [... converterForPattern(ctx, alts[i].pattern, src, dest),
? [seq(`if (${dest} === void 0) `, ctx.block(() => loop(i + 1)))] ... ((i < alts.length - 1)
: [])]; ? [seq(`if (${dest} === void 0) `, ctx.block(() => loop(i + 1)))]
: [])];
}
return loop(0);
} }
return loop(0); case 'and': {
} else { const pcs = [p.pattern, ... p.patterns];
ctx.variantName = void 0; function loop(i: number): Item[] {
return converterForAlternative(ctx, p.value, src, dest); 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, ctx: FunctionContext,
p: M.Alternative, p: M.Pattern,
src: string, src: string,
dest: string): Item[] dest: string): Item[]
{ {
if (p._variant === 'and') { return converterFor(ctx, M.NamedPattern.anonymous(p), src, simpleValue => {
const alts = [p.pattern, ... p.patterns]; if (simpleValue === void 0) {
function loop(i: number): Item[] { return [ctx.buildCapturedCompound(dest)];
return (i < alts.length) } else if (ctx.variantName !== void 0) {
? converterFor(ctx, alts[i], src, () => loop(i + 1)) if (typeFor(ctx.mod, p).kind === 'unit') {
: [ctx.buildCapturedCompound(dest)];
}
return loop(0);
} else {
return converterFor(ctx, M.NamedPattern.anonymous(p.value), src, simpleValue => {
if (simpleValue === void 0) {
return [ctx.buildCapturedCompound(dest)]; 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 { } else {
return [`${dest} = ${simpleValue}`]; return [ctx.withCapture('value',
simpleValue,
() => ctx.buildCapturedCompound(dest))];
} }
}); } else {
} return [`${dest} = ${simpleValue}`];
}
});
} }
function converterForTuple(ctx: FunctionContext, function converterForTuple(ctx: FunctionContext,

View File

@ -4,24 +4,22 @@ import { ModuleContext } from "./context";
import { ANY_TYPE, AtomicType, CollectionType, FieldMap, SimpleType, Type } from "./type"; import { ANY_TYPE, AtomicType, CollectionType, FieldMap, SimpleType, Type } from "./type";
export function typeForDefinition(mod: ModuleContext, d: M.Definition): Type { export function typeForDefinition(mod: ModuleContext, d: M.Definition): Type {
if (d._variant === 'or') { switch (d._variant) {
return Type.union( case 'or':
new Map([d.pattern, ... d.patterns].map(a => return Type.union(
[a.variantLabel, typeForAlternative(mod, a.alternative)]))); new Map([d.pattern, ... d.patterns].map(a =>
} else { [a.variantLabel, typeFor(mod, a.pattern)])));
return typeForAlternative(mod, d.value); 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 { export function typeForIntersection(mod: ModuleContext, ps: M.NamedPattern[]): SimpleType {
if (a._variant === 'and') { const fs = new Map();
const fs = new Map(); ps.forEach(p => gatherFields(fs, mod, p));
gatherFields(fs, mod, a.pattern); return fs.size > 0 ? Type.record(fs) : Type.unit();
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 setType(mod: ModuleContext, p: M.SimplePattern): CollectionType { export function setType(mod: ModuleContext, p: M.SimplePattern): CollectionType {

View File

@ -2,8 +2,8 @@ import { refPosition } from '../reader';
import * as M from '../meta'; import * as M from '../meta';
import { block, brackets, formatItems, Item, parens, seq } from './block'; import { block, brackets, formatItems, Item, parens, seq } from './block';
import { FunctionContext } from "./context"; import { FunctionContext } from "./context";
import { typeForAlternative } from './gentype';
import { FieldType, renderType, SimpleType } from './type'; import { FieldType, renderType, SimpleType } from './type';
import { typeFor, typeForIntersection } from './gentype';
export function unconverterForDefinition( export function unconverterForDefinition(
ctx: FunctionContext, ctx: FunctionContext,
@ -11,61 +11,59 @@ export function unconverterForDefinition(
def: M.Definition, def: M.Definition,
src: string): Item[] src: string): Item[]
{ {
if (def._variant === 'or') { switch (def._variant) {
return [seq(`switch (${src}._variant) `, block( case 'or':
... [def.pattern, ... def.patterns].map(p => return [seq(`switch (${src}._variant) `, block(
seq(`case `, JSON.stringify(p.variantLabel), `: `, ctx.block(() => { ... [def.pattern, ... def.patterns].map(p =>
const hasValueField = seq(`case `, JSON.stringify(p.variantLabel), `: `, ctx.block(() => {
p.alternative._variant === 'Pattern' && const hasValueField = p.pattern._variant === 'SimplePattern';
p.alternative.value._variant === 'SimplePattern'; return [seq(`return `, unconverterForPattern(
return [seq(`return `, unconverterForAlternative( ctx, name, p.pattern, hasValueField ? `${src}.value` : src))];
ctx, name, p.alternative, hasValueField ? `${src}.value` : src))]; })))))];
})))))]; case 'and': {
} else { const ps = [def.pattern , ... def.patterns];
return [seq(`return `, unconverterForAlternative(ctx, name, def.value, `${src}`))]; 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) ?? '<anonymous>'), 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 {} export class InsufficientInformationError extends Error {}
function unconverterForAlternative( function unconverterForPattern(ctx: FunctionContext, name: string, a: M.Pattern, src: string): Item
ctx: FunctionContext,
name: string,
a: M.Alternative,
src: string): Item
{ {
const t = typeForAlternative(ctx.mod, a); const t = typeFor(ctx.mod, a);
if (a._variant === 'and') { try {
const errs: [string, InsufficientInformationError][] = []; return unconverterFor(ctx, a, src, t);
const cs = [a.pattern, ... a.patterns].flatMap(p => { } catch (e) {
if (p._variant === 'anonymous' && p.value._variant === 'SimplePattern') { if (e instanceof InsufficientInformationError) {
return []; throw new Error(`Cannot produce unconverter for ${name}: ${e.message}`);
} else {
try {
return [unconverterForNamed(ctx, p, src, t)];
} catch (e) {
if (e instanceof InsufficientInformationError) {
errs.push([name + '/' + (M.nameFor(p) ?? '<anonymous>'), 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;
} }
throw e;
} }
} }

View File

@ -55,12 +55,6 @@ export type Definition = (
"pattern": NamedAlternative, "pattern": NamedAlternative,
"patterns": Array<NamedAlternative> "patterns": Array<NamedAlternative>
} | } |
{"_variant": "Alternative", "value": Alternative}
);
export type NamedAlternative = {"variantLabel": string, "alternative": Alternative};
export type Alternative = (
{"_variant": "and", "pattern": NamedPattern, "patterns": Array<NamedPattern>} | {"_variant": "and", "pattern": NamedPattern, "patterns": Array<NamedPattern>} |
{"_variant": "Pattern", "value": Pattern} {"_variant": "Pattern", "value": Pattern}
); );
@ -103,6 +97,8 @@ export type AtomKind = (
{"_variant": "Symbol"} {"_variant": "Symbol"}
); );
export type NamedAlternative = {"variantLabel": string, "pattern": Pattern};
export type NamedSimplePattern = ( export type NamedSimplePattern = (
{"_variant": "named", "value": NamedSimplePattern_} | {"_variant": "named", "value": NamedSimplePattern_} |
{"_variant": "anonymous", "value": SimplePattern} {"_variant": "anonymous", "value": SimplePattern}
@ -143,14 +139,8 @@ export namespace Definition {
export function or( export function or(
{pattern, patterns}: {pattern: NamedAlternative, patterns: Array<NamedAlternative>} {pattern, patterns}: {pattern: NamedAlternative, patterns: Array<NamedAlternative>}
): Definition {return {"_variant": "or", "pattern": pattern, "patterns": patterns};}; ): Definition {return {"_variant": "or", "pattern": pattern, "patterns": patterns};};
export function Alternative(value: Alternative): Definition {return {"_variant": "Alternative", "value": value};}; export function and({pattern, patterns}: {pattern: NamedPattern, patterns: Array<NamedPattern>}): Definition {return {"_variant": "and", "pattern": pattern, "patterns": patterns};};
} export function Pattern(value: Pattern): Definition {return {"_variant": "Pattern", "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<NamedPattern>}): Alternative {return {"_variant": "and", "pattern": pattern, "patterns": patterns};};
export function Pattern(value: Pattern): Alternative {return {"_variant": "Pattern", "value": value};};
} }
export namespace Pattern { export namespace Pattern {
@ -187,6 +177,8 @@ export namespace AtomKind {
export function Symbol(): AtomKind {return {"_variant": "Symbol"};}; 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 namespace NamedSimplePattern {
export function named(value: NamedSimplePattern_): NamedSimplePattern {return {"_variant": "named", "value": value};}; export function named(value: NamedSimplePattern_): NamedSimplePattern {return {"_variant": "named", "value": value};};
export function anonymous(value: SimplePattern): NamedSimplePattern {return {"_variant": "anonymous", "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) { if (result === void 0) {
let _tmp6: (Alternative) | undefined; if (_.Record.isRecord<_val, _.Tuple<_val>, _embedded>(v)) {
_tmp6 = toAlternative(v); let _tmp6: (null) | undefined;
if (_tmp6 !== void 0) {result = {"_variant": "Alternative", "value": _tmp6};}; _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<NamedPattern>) | 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; 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<NamedPattern>) | 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": { case "and": {
return _.Record( return _.Record(
$and, $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 { export function asNamedSimplePattern(v: _val): NamedSimplePattern {
let result = toNamedSimplePattern(v); let result = toNamedSimplePattern(v);
if (result === void 0) throw new TypeError(`Invalid NamedSimplePattern: ${_.stringify(v)}`); if (result === void 0) throw new TypeError(`Invalid NamedSimplePattern: ${_.stringify(v)}`);

View File

@ -1,13 +1,10 @@
import { is, Position } from '@preserves/core'; import { is, Position } from '@preserves/core';
import * as M from './gen/schema'; import * as M from './gen/schema';
import { SchemaSyntaxError } from './error'; import { SchemaSyntaxError } from './error';
import type { AtomicType } from './compiler/type';
import { isJsKeyword } from './compiler/jskw'; import { isJsKeyword } from './compiler/jskw';
export * from './gen/schema'; export * from './gen/schema';
export type Builtin = { type: AtomicType, pattern: M.Alternative };
export type Input = M._val; export type Input = M._val;
export function isValidToken(s: string): boolean { export function isValidToken(s: string): boolean {

View File

@ -1,5 +1,5 @@
import { Reader, Annotated, Dictionary, is, peel, preserves, Record, strip, Tuple, Position, position, ReaderOptions, stringify, isCompound, KeyedDictionary } from '@preserves/core'; 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 * as M from './meta';
import { SchemaSyntaxError } from './error'; import { SchemaSyntaxError } from './error';
@ -71,9 +71,7 @@ export function parseSchema(toplevelTokens: Array<Input>,
function process(toplevelTokens: Array<Input>): void { function process(toplevelTokens: Array<Input>): void {
const toplevelClauses = splitBy(peel(toplevelTokens) as Array<Input>, M.DOT); const toplevelClauses = splitBy(peel(toplevelTokens) as Array<Input>, M.DOT);
for (const clause of toplevelClauses) { for (const clause of toplevelClauses) {
if (!Array.isArray(clause)) { if (clause.length >= 2 && is(clause[1], M.EQUALS)) {
invalidClause(clause);
} else if (clause.length >= 2 && is(clause[1], M.EQUALS)) {
const pos = position(clause[0]); const pos = position(clause[0]);
const name = peel(clause[0]); const name = peel(clause[0]);
if (typeof name !== 'symbol') invalidClause(clause); 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<Input>): Definition { function parseDefinition(name: symbol, pos: Position | null, body: Array<Input>): Definition {
let nextAnonymousAlternativeNumber = 0; function alternativeName(input: Array<Input>): M.NamedAlternative
function alternativeName([input, p]: readonly [Array<Input>, Alternative])
: M.NamedAlternative
{ {
const n = findName(input) || findName(input[0]); const n = findName(input) || findName(input[0]);
const p = parsePattern(name, input);
if (n !== false) { if (n !== false) {
return M.NamedAlternative({ variantLabel: n.description!, alternative: p }); return M.NamedAlternative({ variantLabel: n.description!, pattern: p });
} }
if (p._variant === 'Pattern' && if (p._variant === 'CompoundPattern' &&
p.value._variant === 'CompoundPattern' && p.value._variant === 'rec' &&
p.value.value._variant === 'rec' && p.value.label._variant === 'anonymous' &&
p.value.value.label._variant === 'anonymous' && p.value.label.value._variant === 'SimplePattern' &&
p.value.value.label.value._variant === 'SimplePattern' && p.value.label.value.value._variant === 'lit' &&
p.value.value.label.value.value._variant === 'lit' && typeof p.value.label.value.value.value === 'symbol')
typeof p.value.value.label.value.value.value === 'symbol')
{ {
return M.NamedAlternative({ return M.NamedAlternative({
variantLabel: p.value.value.label.value.value.value.description!, variantLabel: p.value.label.value.value.value.description!,
alternative: p pattern: p
}); });
} }
if (p._variant === 'Pattern' && if (p._variant === 'SimplePattern' &&
p.value._variant === 'SimplePattern' && p.value._variant === 'Ref')
p.value.value._variant === 'Ref')
{ {
return M.NamedAlternative({ return M.NamedAlternative({
variantLabel: p.value.value.value.name.description!, variantLabel: p.value.value.name.description!,
alternative: p pattern: p
}); });
} }
if (p._variant === 'Pattern' && if (p._variant === 'SimplePattern' &&
p.value._variant === 'SimplePattern' && p.value._variant === 'lit')
p.value.value._variant === 'lit')
{ {
const s = M.namelike(p.value.value.value); const s = M.namelike(p.value.value);
if (s !== void 0) return M.NamedAlternative({ variantLabel: s, alternative: p }); if (s !== void 0) return M.NamedAlternative({ variantLabel: s, pattern: p });
} }
return M.NamedAlternative({ throw new SchemaSyntaxError(preserves`Name missing for alternative: ${input}`, pos);
variantLabel: '_anonymous' + nextAnonymousAlternativeNumber++,
alternative: p
});
} }
function patternName([input, p]: readonly [Array<Input>, Pattern]) : M.NamedPattern { function patternName(input: Array<Input>): M.NamedPattern {
const n = findName(input) || findName(input[0]); const n = findName(input) || findName(input[0]);
const p = parsePattern(name, input);
if (n !== false) { if (n !== false) {
if (p._variant !== 'SimplePattern') namedMustBeSimple(position(input[0])); if (p._variant !== 'SimplePattern') namedMustBeSimple(position(input[0]));
return M.NamedPattern.named(M.NamedSimplePattern_({ name: n, pattern: p.value })); return M.NamedPattern.named(M.NamedSimplePattern_({ name: n, pattern: p.value }));
@ -178,28 +170,32 @@ function parseDefinition(name: symbol, pos: Position | null, body: Array<Input>)
return M.NamedPattern.anonymous(p); return M.NamedPattern.anonymous(p);
} }
// TODO: deal with situation where there's an or of ands, where const andPieces = splitBy(body, M.ANDSYM);
// the branches of the and are named. The parsing is ambiguous, and const orPieces = splitBy(body, M.ORSYM);
// with the current code I think (?) you end up with the same name
// attached to the or-branch as to the leftmost and-branch.
return parseOp(pos, if (andPieces.length === 0 || orPieces.length === 0) {
body, throw new SchemaSyntaxError(preserves`Invalid Schema clause: ${body}`, pos);
M.ORSYM, }
p => [p, parseOp(pos,
p, if (andPieces.length > 1 && orPieces.length > 1) {
M.ANDSYM, throw new SchemaSyntaxError(preserves`Mixed "or" and "and" clause: ${body}`, pos);
p => [p, parsePattern(name, p)] as const, }
(p0, ps) => M.Alternative.and({
pattern: patternName(p0), if (andPieces.length > 1) {
patterns: ps.map(patternName) return M.Definition.and({
}), pattern: patternName(andPieces[0]),
p => M.Alternative.Pattern(p[1]))] as const, patterns: andPieces.slice(1).map(patternName),
(p0, ps) => M.Definition.or({ });
pattern: alternativeName(p0), }
patterns: ps.map(alternativeName)
}), if (orPieces.length > 1) {
p => M.Definition.Alternative(p[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<Input>): Pattern { function parsePattern(name: symbol, body0: Array<Input>): Pattern {
@ -348,21 +344,6 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
() => M.Pattern.CompoundPattern(parseCompound(body[0]))); () => M.Pattern.CompoundPattern(parseCompound(body[0])));
} }
function parseOp<Each, Combined>(pos: Position | null,
body: Array<Input>,
op: Input,
each: (p: Array<Input>) => Each,
combineN: (p0: Each, ps: Array<Each>) => 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 { function findName(x: Input): symbol | false {
if (!Annotated.isAnnotated<never>(x)) return false; if (!Annotated.isAnnotated<never>(x)) return false;
for (const a0 of x.annotations) { for (const a0 of x.annotations) {

View File

@ -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„„µ± 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´³lit³
ByteString„„µ±Symbol´³lit³Symbol„„„„³ ByteString„„µ±Symbol´³lit³Symbol„„„„³
Definition´³orµµ±or´³rec´³lit³or„´³tupleµ´³tuple*µ´³named³pattern´³refµ„³NamedAlternative„„„´³named³patterns´³refµ„³NamedAlternative„„„„„„„µ± Alternative´³refµ„³ Alternative„„„„³ 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„„³ Alternative´³orµµ±and´³rec´³lit³and„´³tupleµ´³tuple*µ´³named³pattern´³refµ„³ NamedPattern„„„´³named³patterns´³refµ„³ NamedPattern„„„„„„„µ±Pattern´³refµ„³Pattern„„„„³ Definitions´³dictof´³atom³Symbol„´³refµ„³ 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³ 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€„„ 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€„„

View File

@ -21,15 +21,16 @@ EmbeddedTypeName = Ref / #f.
Definitions = { symbol: Definition ...:... }. Definitions = { symbol: Definition ...:... }.
; Pattern / Pattern / ... Definition =
; and the empty pattern is <or []> ; Pattern / Pattern / ...
Definition = <or [@pattern NamedAlternative @patterns NamedAlternative ...]> / Alternative . / <or [@pattern NamedAlternative @patterns NamedAlternative ...]>
NamedAlternative = [@variantLabel string @alternative Alternative]. ; Pattern & Pattern & ...
/ <and [@pattern NamedPattern @patterns NamedPattern ...]>
; Pattern & Pattern & ... ; Pattern
; and the universal pattern, "any", is <and []> / Pattern
Alternative = <and [@pattern NamedPattern @patterns NamedPattern ...]> / Pattern . .
Pattern = SimplePattern / CompoundPattern . Pattern = SimplePattern / CompoundPattern .
@ -76,6 +77,8 @@ DictionaryEntries = { any: NamedSimplePattern ...:... }.
AtomKind = =Boolean / =Float / =Double / =SignedInteger / =String / =ByteString / =Symbol . AtomKind = =Boolean / =Float / =Double / =SignedInteger / =String / =ByteString / =Symbol .
NamedAlternative = [@variantLabel string @pattern Pattern].
NamedSimplePattern = @named NamedSimplePattern_ / @anonymous SimplePattern . NamedSimplePattern = @named NamedSimplePattern_ / @anonymous SimplePattern .
NamedPattern = @named NamedSimplePattern_ / @anonymous Pattern . NamedPattern = @named NamedSimplePattern_ / @anonymous Pattern .