From dbd6c3cf5359d44333add6c8dfda60940ee2aaaf Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Fri, 21 May 2021 17:28:18 +0200 Subject: [PATCH] Repair checker --- .../javascript/packages/schema/src/checker.ts | 80 ++++++++++++------- 1 file changed, 51 insertions(+), 29 deletions(-) diff --git a/implementations/javascript/packages/schema/src/checker.ts b/implementations/javascript/packages/schema/src/checker.ts index b4b31ad..1b0d618 100644 --- a/implementations/javascript/packages/schema/src/checker.ts +++ b/implementations/javascript/packages/schema/src/checker.ts @@ -1,5 +1,5 @@ -import { typeFor } from './gentype'; -import { ANY_TYPE } from './type'; +import { typeFor, typeForIntersection } from './gentype'; +import { ANY_TYPE, FieldType, SimpleType } from './type'; import * as M from './meta'; export function checkSchema(schema: M.Schema): M.Schema { @@ -32,36 +32,41 @@ class Checker { switch (def._variant) { case 'or': [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; case 'and': { + const ps = [def.pattern0, def.pattern1, ... def.patternN]; const scope = new Set(); - [def.pattern0, def.pattern1, ... def.patternN].forEach((p) => - this.checkNamedPattern(scope, p, name.description!)); + ps.forEach((p) => this.checkNamedPattern( + scope, p, name.description!, typeForIntersection(_ref => ANY_TYPE, ps))); break; } 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; } } - checkNamedPattern(scope: Set, p: M.NamedPattern, context: string): void { + checkNamedPattern(scope: Set, p: M.NamedPattern, context: string, t: SimpleType): void { switch (p._variant) { case 'named': this.checkBinding(scope, p.value.name, context); this.checkPattern(scope, 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; case 'anonymous': - this.checkPattern(scope, p.value, context); + this.checkPattern(scope, p.value, context, t); break; } } - checkPattern(scope: Set, p: M.Pattern, context: string): void { - const t = typeFor((_ref) => ANY_TYPE, p); + checkPattern(scope: Set, p: M.Pattern, context: string, t: SimpleType): void { switch (p._variant) { case 'SimplePattern': if (p.value._variant !== 'lit' && (t.kind === 'record' || t.kind === 'unit')) { @@ -72,58 +77,75 @@ class Checker { ((p: M.CompoundPattern): void => { switch (p._variant) { case 'rec': - this.checkNamedPattern(scope, p.label, `label of ${context}`); - this.checkNamedPattern(scope, p.fields, `fields of ${context}`); + this.checkNamedPattern(scope, p.label, `label of ${context}`, t); + this.checkNamedPattern(scope, p.fields, `fields of ${context}`, t); break; case 'tuple': p.patterns.forEach((pp, i) => - this.checkNamedPattern(scope, pp, `item ${i} of ${context}`)); + this.checkNamedPattern(scope, pp, `item ${i} of ${context}`, t)); break; case 'tuple*': if (p.variable._variant === 'named') { this.checkBinding(scope, p.variable.value.name, context); this.checkPattern(scope, 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 { if (t.kind !== 'array') { 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) => - this.checkNamedPattern(scope, pp, `item ${i} of ${context}`)); + this.checkNamedPattern(scope, pp, `item ${i} of ${context}`, t)); break; case 'setof': if (t.kind !== '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; case 'dictof': if (t.kind !== '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; case 'dict': p.entries.forEach((np, key) => this.checkNamedPattern( scope, M.promoteNamedSimplePattern(M.addNameIfAbsent(np, key)), - `entry ${key.asPreservesText()} in dictionary in ${context}`)); + `entry ${key.asPreservesText()} in dictionary in ${context}`, + t)); break; } })(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)!; +}