Refactor schema schema

This commit is contained in:
Tony Garnock-Jones 2021-03-23 19:40:06 +01:00
parent 94f6959ac8
commit 121bcc7a53
6 changed files with 105 additions and 61 deletions

View File

@ -12,7 +12,7 @@ export function converterForDefinition(
dest: string): Item[]
{
if (p._variant === 'or') {
const alts = p.patterns;
const alts = [p.pattern, ... p.patterns];
function loop(i: number): Item[] {
ctx.variantName = alts[i].variantLabel;
return [... converterForAlternative(ctx, alts[i].alternative, src, dest),
@ -20,7 +20,7 @@ export function converterForDefinition(
? [seq(`if (${dest} === void 0) `, ctx.block(() => loop(i + 1)))]
: [])];
}
return alts.length === 0 ? [] : loop(0);
return loop(0);
} else {
ctx.variantName = void 0;
return converterForAlternative(ctx, p.value, src, dest);
@ -34,13 +34,13 @@ function converterForAlternative(
dest: string): Item[]
{
if (p._variant === 'and') {
const alts = p.patterns;
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 alts.length === 0 ? [seq(`${dest} = ${src}`)] : loop(0);
return loop(0);
} else {
return converterFor(ctx, M.NamedPattern.anonymous(p.value), src, simpleValue => {
if (simpleValue === void 0) {

View File

@ -6,7 +6,8 @@ import { ANY_TYPE, AtomicType, CollectionType, FieldMap, SimpleType, Type } from
export function typeForDefinition(mod: ModuleContext, d: M.Definition): Type {
if (d._variant === 'or') {
return Type.union(
new Map(d.patterns.map(a => [a.variantLabel, typeForAlternative(mod, a.alternative)])));
new Map([d.pattern, ... d.patterns].map(a =>
[a.variantLabel, typeForAlternative(mod, a.alternative)])));
} else {
return typeForAlternative(mod, d.value);
}
@ -15,6 +16,7 @@ export function typeForDefinition(mod: ModuleContext, d: M.Definition): Type {
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 {

View File

@ -13,7 +13,7 @@ export function unconverterForDefinition(
{
if (def._variant === 'or') {
return [seq(`switch (${src}._variant) `, block(
... def.patterns.map(p =>
... [def.pattern, ... def.patterns].map(p =>
seq(`case `, JSON.stringify(p.variantLabel), `: `, ctx.block(() => {
const hasValueField =
p.alternative._variant === 'Pattern' &&
@ -37,7 +37,7 @@ function unconverterForAlternative(
const t = typeForAlternative(ctx.mod, a);
if (a._variant === 'and') {
const errs: [string, InsufficientInformationError][] = [];
const cs = a.patterns.flatMap(p => {
const cs = [a.pattern, ... a.patterns].flatMap(p => {
if (p._variant === 'anonymous' && p.value._variant === 'SimplePattern') {
return [];
} else {

View File

@ -40,14 +40,18 @@ export type PointerName = ({"_variant": "Ref", "value": Ref} | {"_variant": "fal
export type Definitions = _.KeyedDictionary<symbol, Definition, _ptr>;
export type Definition = (
{"_variant": "or", "patterns": Array<NamedAlternative>} |
{
"_variant": "or",
"pattern": NamedAlternative,
"patterns": Array<NamedAlternative>
} |
{"_variant": "Alternative", "value": Alternative}
);
export type NamedAlternative = {"variantLabel": string, "alternative": Alternative};
export type Alternative = (
{"_variant": "and", "patterns": Array<NamedPattern>} |
{"_variant": "and", "pattern": NamedPattern, "patterns": Array<NamedPattern>} |
{"_variant": "Pattern", "value": Pattern}
);
@ -122,14 +126,16 @@ export namespace PointerName {
export function Definitions(value: _.KeyedDictionary<symbol, Definition, _ptr>): Definitions {return value;}
export namespace Definition {
export function or(patterns: Array<NamedAlternative>): Definition {return {"_variant": "or", "patterns": patterns};};
export function or(
{pattern, patterns}: {pattern: NamedAlternative, patterns: Array<NamedAlternative>}
): 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(patterns: Array<NamedPattern>): Alternative {return {"_variant": "and", "patterns": patterns};};
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};};
}
@ -319,35 +325,49 @@ export function toDefinition(v: _val): undefined | Definition {
let _tmp0: (null) | undefined;
_tmp0 = _.is(v.label, $or) ? null : void 0;
if (_tmp0 !== void 0) {
if (_.Array.isArray(v[0])) {
let _tmp1: (Array<_val>) | undefined;
let _tmp2: (Array<NamedAlternative>) | undefined;
_tmp1 = v[0];
{
_tmp2 = [];
for (const _tmp3 of _tmp1) {
let _tmp4: (NamedAlternative) | undefined;
_tmp4 = toNamedAlternative(_tmp3);
if (_tmp4 !== void 0) {_tmp2.push(_tmp4); continue;};
_tmp2 = void 0;
break;
if (_.Array.isArray(v[0]) && v[0].length >= 1) {
let _tmp1: (NamedAlternative) | undefined;
_tmp1 = toNamedAlternative(v[0][0]);
if (_tmp1 !== void 0) {
let _tmp2: (Array<_val>) | undefined;
let _tmp3: (Array<NamedAlternative>) | undefined;
_tmp2 = v[0].slice(1);
{
_tmp3 = [];
for (const _tmp4 of _tmp2) {
let _tmp5: (NamedAlternative) | undefined;
_tmp5 = toNamedAlternative(_tmp4);
if (_tmp5 !== void 0) {_tmp3.push(_tmp5); continue;};
_tmp3 = void 0;
break;
};
if (_tmp3 !== void 0) {result = {"_variant": "or", "pattern": _tmp1, "patterns": _tmp3};};
};
if (_tmp2 !== void 0) {result = {"_variant": "or", "patterns": _tmp2};};
};
};
};
};
if (result === void 0) {
let _tmp5: (Alternative) | undefined;
_tmp5 = toAlternative(v);
if (_tmp5 !== void 0) {result = {"_variant": "Alternative", "value": _tmp5};};
let _tmp6: (Alternative) | undefined;
_tmp6 = toAlternative(v);
if (_tmp6 !== void 0) {result = {"_variant": "Alternative", "value": _tmp6};};
};
return result;
}
export function fromDefinition(_v: Definition): _val {
switch (_v._variant) {
case "or": {return _.Record($or, [_v["patterns"].map(v => fromNamedAlternative(v))]);};
case "or": {
return _.Record(
$or,
[
[
fromNamedAlternative(_v["pattern"]),
... _v["patterns"].map(v => fromNamedAlternative(v))
]
]
);
};
case "Alternative": {return fromAlternative(_v.value);};
};
}
@ -386,35 +406,49 @@ export function toAlternative(v: _val): undefined | Alternative {
let _tmp0: (null) | undefined;
_tmp0 = _.is(v.label, $and) ? null : void 0;
if (_tmp0 !== void 0) {
if (_.Array.isArray(v[0])) {
let _tmp1: (Array<_val>) | undefined;
let _tmp2: (Array<NamedPattern>) | undefined;
_tmp1 = v[0];
{
_tmp2 = [];
for (const _tmp3 of _tmp1) {
let _tmp4: (NamedPattern) | undefined;
_tmp4 = toNamedPattern(_tmp3);
if (_tmp4 !== void 0) {_tmp2.push(_tmp4); continue;};
_tmp2 = void 0;
break;
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 (_tmp2 !== void 0) {result = {"_variant": "and", "patterns": _tmp2};};
};
};
};
};
if (result === void 0) {
let _tmp5: (Pattern) | undefined;
_tmp5 = toPattern(v);
if (_tmp5 !== void 0) {result = {"_variant": "Pattern", "value": _tmp5};};
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, [_v["patterns"].map(v => fromNamedPattern(v))]);};
case "and": {
return _.Record(
$and,
[
[
fromNamedPattern(_v["pattern"]),
... _v["patterns"].map(v => fromNamedPattern(v))
]
]
);
};
case "Pattern": {return fromPattern(_v.value);};
};
}

View File

@ -83,7 +83,7 @@ export function parseSchema(toplevelTokens: Array<Input>,
if (definitions.has(name)) {
throw new SchemaSyntaxError(preserves`Duplicate definition: ${clause}`, pos);
}
definitions.set(name, parseDefinition(name, clause.slice(2)));
definitions.set(name, parseDefinition(name, pos, clause.slice(2)));
} else if (clause.length === 2 && is(clause[0], M.$version)) {
version = M.asVersion(peel(clause[1]));
} else if (clause.length === 2 && is(clause[0], M.$pointer)) {
@ -125,7 +125,7 @@ function namedMustBeSimple(p: Position | null): never {
throw new SchemaSyntaxError('Named patterns must be Simple patterns', p);
}
function parseDefinition(name: symbol, body: Array<Input>): Definition {
function parseDefinition(name: symbol, pos: Position | null, body: Array<Input>): Definition {
let nextAnonymousAlternativeNumber = 0;
function alternativeName([input, p]: readonly [Array<Input>, Alternative])
: M.NamedAlternative
@ -183,14 +183,22 @@ function parseDefinition(name: symbol, body: Array<Input>): Definition {
// 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(body,
return parseOp(pos,
body,
M.ORSYM,
p => [p, parseOp(p,
p => [p, parseOp(pos,
p,
M.ANDSYM,
p => [p, parsePattern(name, p)] as const,
ps => M.Alternative.and(ps.map(patternName)),
(p0, ps) => M.Alternative.and({
pattern: patternName(p0),
patterns: ps.map(patternName)
}),
p => M.Alternative.Pattern(p[1]))] as const,
ps => M.Definition.or(ps.map(alternativeName)),
(p0, ps) => M.Definition.or({
pattern: alternativeName(p0),
patterns: ps.map(alternativeName)
}),
p => M.Definition.Alternative(p[1]));
}
@ -340,14 +348,19 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
() => M.Pattern.CompoundPattern(parseCompound(body[0])));
}
function parseOp<Each, Combined>(body: Array<Input>,
function parseOp<Each, Combined>(pos: Position | null,
body: Array<Input>,
op: Input,
each: (p: Array<Input>) => Each,
combineN: (ps: Array<Each>) => Combined,
combineN: (p0: Each, ps: Array<Each>) => Combined,
finish1: (p: Each) => Combined): Combined
{
const pieces = splitBy(body, op).map(each);
return (pieces.length === 1) ? finish1(pieces[0]) : combineN(pieces);
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 {

View File

@ -17,18 +17,13 @@ Definitions = { symbol: Definition ...:... }.
; Pattern / Pattern / ...
; and the empty pattern is <or []>
;
; TODO: refactor to be
; <or [@pattern NamedAlternative @patterns NamedAlternative ...]> / Alternative .
; Similarly for <and...>
;
Definition = <or [@patterns NamedAlternative ...]> / Alternative .
Definition = <or [@pattern NamedAlternative @patterns NamedAlternative ...]> / Alternative .
NamedAlternative = [@variantLabel string @alternative Alternative].
; Pattern & Pattern & ...
; and the universal pattern, "any", is <and []>
Alternative = <and [@patterns NamedPattern ...]> / Pattern .
Alternative = <and [@pattern NamedPattern @patterns NamedPattern ...]> / Pattern .
Pattern = SimplePattern / CompoundPattern .