import { refPosition } from "../reader"; import * as M from "../meta"; import { ModuleContext } from "./context"; import { ANY_TYPE, AtomicType, CollectionType, FieldMap, SimpleType, Type } from "./type"; export function typeForDefinition(mod: ModuleContext, d: M.Definition): Type { switch (d._variant) { case 'or': return Type.union( new Map([d.pattern, ... d.patterns].map(a => [a.variantLabel, typeFor(mod, a.pattern)]))); case 'and': return typeForIntersection(mod, [d.pattern, ... d.patterns]); case 'Pattern': return typeFor(mod, d.value); } } export function typeForIntersection(mod: ModuleContext, ps: M.NamedPattern[]): SimpleType { const fs = new Map(); ps.forEach(p => gatherFields(fs, mod, p)); return fs.size > 0 ? Type.record(fs) : Type.unit(); } export function setType(mod: ModuleContext, p: M.SimplePattern): CollectionType { return Type.set(simpleType(mod, p)); } export function dictionaryType(mod: ModuleContext, kp: M.SimplePattern, vp: M.SimplePattern): CollectionType { return Type.dictionary(simpleType(mod, kp), simpleType(mod, vp)); } export function typeFor(mod: ModuleContext, p: M.Pattern): SimpleType { if (p._variant === 'SimplePattern') { return simpleType(mod, p.value); } else { switch (p.value._variant) { case 'setof': return setType(mod, p.value.pattern); case 'dictof': return dictionaryType(mod, p.value.key, p.value.value); default: { const arrayType = M.simpleArray(p.value); if (arrayType === void 0) { const fs = new Map(); compoundFields(fs, mod, p.value); return fs.size > 0 ? Type.record(fs) : Type.unit(); } else { return Type.array(simpleType(mod, arrayType)); } } } } } export function simpleType(mod: ModuleContext, 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 M.lookup(refPosition(p.value), p.value, mod.env, (_p) => Type.ref(p.value.name.description!), (modId, modPath,_p) => { mod.imports.add([modId, modPath]); return Type.ref(`${modId}.${p.value.name.description!}`); }); default: ((_p: never) => {})(p); throw new Error("Unreachable"); } } function compoundFields(fs: FieldMap, mod: ModuleContext, p: M.CompoundPattern): void { switch (p._variant) { case 'rec': gatherFields(fs, mod, p.label); gatherFields(fs, mod, p.fields); break; case 'tuple': p.patterns.forEach(pp => gatherFields(fs, mod, pp)); break; case 'tuple*': { p.fixed.forEach(pp => gatherFields(fs, mod, pp)); const n = p.variable; if (n._variant === 'named') { fs.set(n.value.name.description!, Type.array(simpleType(mod, n.value.pattern))); } break; } case 'setof': case 'dictof': break; case 'dict': p.entries.forEach((n, k) => gatherFields(fs, mod, M.promoteNamedSimplePattern(M.addNameIfAbsent(n, k)))); break; default: ((_p: never) => {})(p); throw new Error("Unreachable"); } } function gatherFields(fs: FieldMap, mod: ModuleContext, n: M.NamedPattern): void { if (n._variant === 'named') { fs.set(n.value.name.description!, simpleType(mod, n.value.pattern)); } else if (n.value._variant === 'CompoundPattern') { compoundFields(fs, mod, n.value.value); } }