Repair checker
This commit is contained in:
parent
2bde06f509
commit
dbd6c3cf53
|
@ -1,5 +1,5 @@
|
||||||
import { typeFor } from './gentype';
|
import { typeFor, typeForIntersection } from './gentype';
|
||||||
import { ANY_TYPE } from './type';
|
import { ANY_TYPE, FieldType, SimpleType } from './type';
|
||||||
import * as M from './meta';
|
import * as M from './meta';
|
||||||
|
|
||||||
export function checkSchema(schema: M.Schema): M.Schema {
|
export function checkSchema(schema: M.Schema): M.Schema {
|
||||||
|
@ -32,36 +32,41 @@ class Checker {
|
||||||
switch (def._variant) {
|
switch (def._variant) {
|
||||||
case 'or':
|
case 'or':
|
||||||
[def.pattern0, def.pattern1, ... def.patternN].forEach(({ variantLabel, pattern }) =>
|
[def.pattern0, def.pattern1, ... def.patternN].forEach(({ variantLabel, pattern }) =>
|
||||||
this.checkPattern(new Set(), pattern, `variant ${variantLabel} of ${name.description!}`));
|
this.checkPattern(new Set(),
|
||||||
|
pattern,
|
||||||
|
`variant ${variantLabel} of ${name.description!}`,
|
||||||
|
typeFor(_ref => ANY_TYPE, pattern)));
|
||||||
break;
|
break;
|
||||||
case 'and': {
|
case 'and': {
|
||||||
|
const ps = [def.pattern0, def.pattern1, ... def.patternN];
|
||||||
const scope = new Set<string>();
|
const scope = new Set<string>();
|
||||||
[def.pattern0, def.pattern1, ... def.patternN].forEach((p) =>
|
ps.forEach((p) => this.checkNamedPattern(
|
||||||
this.checkNamedPattern(scope, p, name.description!));
|
scope, p, name.description!, typeForIntersection(_ref => ANY_TYPE, ps)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'Pattern':
|
case 'Pattern':
|
||||||
this.checkPattern(new Set(), def.value, name.description!);
|
this.checkPattern(
|
||||||
|
new Set(), def.value, name.description!, typeFor(_ref => ANY_TYPE, def.value));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkNamedPattern(scope: Set<string>, p: M.NamedPattern, context: string): void {
|
checkNamedPattern(scope: Set<string>, p: M.NamedPattern, context: string, t: SimpleType): void {
|
||||||
switch (p._variant) {
|
switch (p._variant) {
|
||||||
case 'named':
|
case 'named':
|
||||||
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),
|
||||||
`${p.value.name.description!} of ${context}`);
|
`${p.value.name.description!} of ${context}`,
|
||||||
|
stepType(t, p.value.name.description!));
|
||||||
break;
|
break;
|
||||||
case 'anonymous':
|
case 'anonymous':
|
||||||
this.checkPattern(scope, p.value, context);
|
this.checkPattern(scope, p.value, context, t);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkPattern(scope: Set<string>, p: M.Pattern, context: string): void {
|
checkPattern(scope: Set<string>, p: M.Pattern, context: string, t: SimpleType): void {
|
||||||
const t = typeFor((_ref) => ANY_TYPE, p);
|
|
||||||
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' && (t.kind === 'record' || t.kind === 'unit')) {
|
||||||
|
@ -72,58 +77,75 @@ 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}`);
|
this.checkNamedPattern(scope, p.label, `label of ${context}`, t);
|
||||||
this.checkNamedPattern(scope, p.fields, `fields of ${context}`);
|
this.checkNamedPattern(scope, p.fields, `fields of ${context}`, t);
|
||||||
break;
|
break;
|
||||||
case 'tuple':
|
case 'tuple':
|
||||||
p.patterns.forEach((pp, i) =>
|
p.patterns.forEach((pp, i) =>
|
||||||
this.checkNamedPattern(scope, pp, `item ${i} of ${context}`));
|
this.checkNamedPattern(scope, pp, `item ${i} of ${context}`, t));
|
||||||
break;
|
break;
|
||||||
case 'tuple*':
|
case 'tuple*':
|
||||||
if (p.variable._variant === 'named') {
|
if (p.variable._variant === 'named') {
|
||||||
this.checkBinding(scope, p.variable.value.name, context);
|
this.checkBinding(scope, p.variable.value.name, context);
|
||||||
this.checkPattern(scope,
|
this.checkPattern(scope,
|
||||||
M.Pattern.SimplePattern(p.variable.value.pattern),
|
M.Pattern.SimplePattern(p.variable.value.pattern),
|
||||||
`${JSON.stringify(p.variable.value.name.description!)} of ${context}`);
|
`${JSON.stringify(p.variable.value.name.description!)} of ${context}`,
|
||||||
|
stepType(t, p.variable.value.name.description!));
|
||||||
} else {
|
} else {
|
||||||
if (t.kind !== 'array') {
|
if (t.kind !== 'array') {
|
||||||
this.recordProblem(context, 'unable to reconstruct tail of tuple* pattern');
|
this.recordProblem(context, 'unable to reconstruct tail of tuple* pattern');
|
||||||
|
} else {
|
||||||
|
this.checkPattern(scope,
|
||||||
|
M.Pattern.SimplePattern(p.variable.value),
|
||||||
|
`variable-length portion of ${context}`,
|
||||||
|
t.type);
|
||||||
}
|
}
|
||||||
this.checkPattern(scope,
|
|
||||||
M.Pattern.SimplePattern(p.variable.value),
|
|
||||||
`variable-length portion of ${context}`);
|
|
||||||
}
|
}
|
||||||
p.fixed.forEach((pp, i) =>
|
p.fixed.forEach((pp, i) =>
|
||||||
this.checkNamedPattern(scope, pp, `item ${i} of ${context}`));
|
this.checkNamedPattern(scope, pp, `item ${i} of ${context}`, t));
|
||||||
break;
|
break;
|
||||||
case 'setof':
|
case 'setof':
|
||||||
if (t.kind !== 'set') {
|
if (t.kind !== 'set') {
|
||||||
this.recordProblem(context, 'unable to reconstruct set');
|
this.recordProblem(context, 'unable to reconstruct set');
|
||||||
|
} else {
|
||||||
|
this.checkPattern(scope,
|
||||||
|
M.Pattern.SimplePattern(p.pattern),
|
||||||
|
`set in ${context}`,
|
||||||
|
t.type);
|
||||||
}
|
}
|
||||||
this.checkPattern(scope,
|
|
||||||
M.Pattern.SimplePattern(p.pattern),
|
|
||||||
`set in ${context}`);
|
|
||||||
break;
|
break;
|
||||||
case 'dictof':
|
case 'dictof':
|
||||||
if (t.kind !== 'dictionary') {
|
if (t.kind !== 'dictionary') {
|
||||||
this.recordProblem(context, 'unable to reconstruct dictionary');
|
this.recordProblem(context, 'unable to reconstruct dictionary');
|
||||||
|
} else {
|
||||||
|
this.checkPattern(scope,
|
||||||
|
M.Pattern.SimplePattern(p.key),
|
||||||
|
`key in dictionary in ${context}`,
|
||||||
|
t.key);
|
||||||
|
this.checkPattern(scope,
|
||||||
|
M.Pattern.SimplePattern(p.value),
|
||||||
|
`value in dictionary in ${context}`,
|
||||||
|
t.value);
|
||||||
}
|
}
|
||||||
this.checkPattern(scope,
|
|
||||||
M.Pattern.SimplePattern(p.key),
|
|
||||||
`key in dictionary in ${context}`);
|
|
||||||
this.checkPattern(scope,
|
|
||||||
M.Pattern.SimplePattern(p.value),
|
|
||||||
`value in dictionary in ${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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stepType(t: SimpleType, key: string): FieldType {
|
||||||
|
if (t.kind !== 'record' || !t.fields.has(key)) {
|
||||||
|
throw new Error(
|
||||||
|
`Internal error: cannot step ${JSON.stringify(t)} by ${JSON.stringify(key)}`);
|
||||||
|
}
|
||||||
|
return t.fields.get(key)!;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue