import * as M from "./meta"; import { ANY_TYPE, AtomicType, CollectionType, FieldMap, SimpleType, Type } from "./type"; export type RefResolver = (ref: M.Ref) => AtomicType; 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 setType(resolver: RefResolver, p: M.SimplePattern): CollectionType { return Type.set(simpleType(resolver, p)); } export function dictionaryType(resolver: RefResolver, kp: M.SimplePattern, vp: M.SimplePattern): CollectionType { return Type.dictionary(simpleType(resolver, kp), simpleType(resolver, vp)); } export function typeFor(resolver: RefResolver, p: M.Pattern): SimpleType { if (p._variant === 'SimplePattern') { return simpleType(resolver, p.value); } else { switch (p.value._variant) { case 'setof': return setType(resolver, p.value.pattern); case 'dictof': return dictionaryType(resolver, p.value.key, p.value.value); default: { const arrayType = M.simpleArray(p.value); if (arrayType === void 0) { const fs = new Map(); compoundFields(fs, resolver, p.value); return fs.size > 0 ? Type.record(fs) : Type.unit(); } else { return Type.array(simpleType(resolver, arrayType)); } } } } } export function simpleType(resolver: RefResolver, p: M.SimplePattern): AtomicType { 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 '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)); const n = p.variable; if (n._variant === 'named') { fs.set(n.value.name.description!, Type.array(simpleType(resolver, n.value.pattern))); } break; } case 'setof': case 'dictof': 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') { fs.set(n.value.name.description!, simpleType(resolver, n.value.pattern)); } else if (n.value._variant === 'CompoundPattern') { compoundFields(fs, resolver, n.value.value); } }