127 lines
4.5 KiB
TypeScript
127 lines
4.5 KiB
TypeScript
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);
|
|
}
|
|
}
|