Redo bijection checking without using gentype

This commit is contained in:
Tony Garnock-Jones 2021-05-24 12:03:04 +02:00
parent 0db223ede8
commit 46d76dfca7
1 changed files with 24 additions and 23 deletions

View File

@ -1,5 +1,3 @@
import { typeFor, typeForIntersection } from './gentype';
import { ANY_TYPE, SimpleType } from './type';
import * as M from './meta'; import * as M from './meta';
export function checkSchema(schema: M.Schema): ( export function checkSchema(schema: M.Schema): (
@ -14,6 +12,11 @@ export function checkSchema(schema: M.Schema): (
} }
} }
enum ValueAvailability {
AVAILABLE,
NOT_AVAILABLE,
};
class Checker { class Checker {
problems: Array<string> = []; problems: Array<string> = [];
@ -40,49 +43,48 @@ class Checker {
this.recordProblem(context, `duplicate variant label`); this.recordProblem(context, `duplicate variant label`);
} }
labels.add(variantLabel); labels.add(variantLabel);
this.checkPattern(new Set(), pattern, context, typeFor(_ref => ANY_TYPE, pattern)); this.checkPattern(new Set(), pattern, context, ValueAvailability.AVAILABLE);
}); });
break; break;
} }
case 'and': { case 'and': {
const ps = [def.pattern0, def.pattern1, ... def.patternN]; const ps = [def.pattern0, def.pattern1, ... def.patternN];
const scope = new Set<string>(); const scope = new Set<string>();
ps.forEach((p) => this.checkNamedPattern( ps.forEach((p) => this.checkNamedPattern(scope, p, name.description!));
scope, p, name.description!, typeForIntersection(_ref => ANY_TYPE, ps)));
break; break;
} }
case 'Pattern': case 'Pattern':
this.checkPattern( this.checkPattern(
new Set(), def.value, name.description!, typeFor(_ref => ANY_TYPE, def.value)); new Set(), def.value, name.description!, ValueAvailability.AVAILABLE);
break; break;
} }
} }
checkNamedPattern(scope: Set<string>, p: M.NamedPattern, context: string, t: SimpleType): void { checkNamedPattern(scope: Set<string>, p: M.NamedPattern, context: string): void
{
switch (p._variant) { switch (p._variant) {
case 'named': { case 'named': {
const key = p.value.name.description!;
if (t.kind !== 'record' || !t.fields.has(key)) {
throw new Error(
`Internal error: cannot step ${JSON.stringify(t)} by ${JSON.stringify(key)}`);
}
this.checkBinding(scope, p.value.name, context); this.checkBinding(scope, p.value.name, context);
this.checkPattern(scope, this.checkPattern(scope,
M.Pattern.SimplePattern(p.value.pattern), M.Pattern.SimplePattern(p.value.pattern),
`${JSON.stringify(p.value.name.description!)} of ${context}`, `${JSON.stringify(p.value.name.description!)} of ${context}`,
t.fields.get(key)!); ValueAvailability.AVAILABLE);
break; break;
} }
case 'anonymous': case 'anonymous':
this.checkPattern(scope, p.value, context, t); this.checkPattern(scope, p.value, context, ValueAvailability.NOT_AVAILABLE);
break; break;
} }
} }
checkPattern(scope: Set<string>, p: M.Pattern, context: string, t: SimpleType): void { checkPattern(scope: Set<string>,
p: M.Pattern,
context: string,
availability: ValueAvailability): void
{
switch (p._variant) { switch (p._variant) {
case 'SimplePattern': case 'SimplePattern':
if (p.value._variant !== 'lit' && (t.kind === 'record' || t.kind === 'unit')) { if (p.value._variant !== 'lit' && availability === ValueAvailability.NOT_AVAILABLE) {
this.recordProblem(context, 'cannot recover serialization of non-literal pattern'); this.recordProblem(context, 'cannot recover serialization of non-literal pattern');
} }
break; break;
@ -90,26 +92,25 @@ class Checker {
((p: M.CompoundPattern): void => { ((p: M.CompoundPattern): void => {
switch (p._variant) { switch (p._variant) {
case 'rec': case 'rec':
this.checkNamedPattern(scope, p.label, `label of ${context}`, t); this.checkNamedPattern(scope, p.label, `label of ${context}`);
this.checkNamedPattern(scope, p.fields, `fields of ${context}`, t); this.checkNamedPattern(scope, p.fields, `fields of ${context}`);
break; break;
case 'tuple': case 'tuple':
p.patterns.forEach((pp, i) => p.patterns.forEach((pp, i) =>
this.checkNamedPattern(scope, pp, `item ${i} of ${context}`, t)); this.checkNamedPattern(scope, pp, `item ${i} of ${context}`));
break; break;
case 'tuple*': case 'tuple*':
p.fixed.forEach((pp, i) => p.fixed.forEach((pp, i) =>
this.checkNamedPattern(scope, pp, `item ${i} of ${context}`, t)); this.checkNamedPattern(scope, pp, `item ${i} of ${context}`));
this.checkNamedPattern( this.checkNamedPattern(
scope, M.promoteNamedSimplePattern(p.variable), `tail of ${context}`, t); scope, M.promoteNamedSimplePattern(p.variable), `tail of ${context}`);
break; break;
case 'dict': case 'dict':
p.entries.forEach((np, key) => p.entries.forEach((np, key) =>
this.checkNamedPattern( this.checkNamedPattern(
scope, scope,
M.promoteNamedSimplePattern(M.addNameIfAbsent(np, key)), M.promoteNamedSimplePattern(M.addNameIfAbsent(np, key)),
`entry ${key.asPreservesText()} in dictionary in ${context}`, `entry ${key.asPreservesText()} in dictionary in ${context}`));
t));
break; break;
} }
})(p.value); })(p.value);