import * as M from "./meta"; import { ANY_TYPE, FieldType, FieldMap, SimpleType, Type } from "./type"; export type RefResolver = (ref: M.Ref) => FieldType; export function typeForDefinition(resolver: RefResolver, d: M.Definition): Type { switch (d._variant) { case 'or': return Type.union( new Map([d.pattern0, d.pattern1, ... d.patternN].map(a => [a.variantLabel, typeFor(resolver, a.pattern)]))); case 'and': return typeForIntersection(resolver, [d.pattern0, d.pattern1, ... d.patternN]); case 'Pattern': return typeFor(resolver, d.value); } } export function typeForIntersection(resolver: RefResolver, ps: M.NamedPattern[]): SimpleType { const fs = new Map(); ps.forEach(p => gatherFields(fs, resolver, p)); return fs.size > 0 ? Type.record(fs) : Type.unit(); } export function typeFor(resolver: RefResolver, p: M.Pattern): SimpleType { if (p._variant === 'SimplePattern') { return simpleType(resolver, p.value); } else { return typeForIntersection(resolver, [M.NamedPattern.anonymous(p)]); } } export function simpleType(resolver: RefResolver, p: M.SimplePattern): FieldType { switch (p._variant) { case 'any': return ANY_TYPE; case 'atom': switch (p.atomKind._variant) { case 'Boolean': return Type.ref(`boolean`); case 'Float': return Type.ref(`number`); case 'Double': return Type.ref(`number`); case 'SignedInteger': return Type.ref(`number`); case 'String': return Type.ref(`string`); case 'ByteString': return Type.ref(`_.Bytes`); case 'Symbol': return Type.ref(`symbol`); } case 'embedded': return Type.ref(`_embedded`); case 'lit': return Type.unit(); case 'seqof': return Type.array(simpleType(resolver, p.pattern)); case 'setof': return Type.set(simpleType(resolver, p.pattern)); case 'dictof': return Type.dictionary(simpleType(resolver, p.key), simpleType(resolver, p.value)); case 'Ref': return resolver(p.value); default: ((_p: never) => {})(p); throw new Error("Unreachable"); } } function compoundFields(fs: FieldMap, resolver: RefResolver, p: M.CompoundPattern): void { switch (p._variant) { case 'rec': gatherFields(fs, resolver, p.label); gatherFields(fs, resolver, p.fields); break; case 'tuple': p.patterns.forEach(pp => gatherFields(fs, resolver, pp)); break; case 'tuple*': p.fixed.forEach(pp => gatherFields(fs, resolver, pp)); gatherFields(fs, resolver, M.promoteNamedSimplePattern(p.variable)); break; case 'dict': p.entries.forEach((n, k) => gatherFields(fs, resolver, M.promoteNamedSimplePattern(M.addNameIfAbsent(n, k)))); break; default: ((_p: never) => {})(p); throw new Error("Unreachable"); } } function gatherFields(fs: FieldMap, resolver: RefResolver, n: M.NamedPattern): void { if (n._variant === 'named') { const t = simpleType(resolver, n.value.pattern); if (t.kind !== 'unit') { fs.set(n.value.name.description!, t); } } else if (n.value._variant === 'CompoundPattern') { compoundFields(fs, resolver, n.value.value); } }