90 lines
3.2 KiB
TypeScript
90 lines
3.2 KiB
TypeScript
import { anglebrackets, braces, Item, keyvalue, opseq, seq } from "./block";
|
|
|
|
export type Type =
|
|
| { kind: 'union', variants: VariantMap } // zero: never
|
|
| SimpleType
|
|
|
|
export type SimpleType = AtomicType | CompoundType
|
|
export type FieldType = AtomicType | CollectionType;
|
|
|
|
export type AtomicType =
|
|
| { kind: 'unit' }
|
|
| { kind: 'ref', typeName: string } // also for base types
|
|
|
|
export type CompoundType =
|
|
| CollectionType
|
|
| { kind: 'record', fields: FieldMap }
|
|
|
|
export type CollectionType =
|
|
| { kind: 'array', type: AtomicType }
|
|
| { kind: 'set', type: AtomicType }
|
|
| { kind: 'dictionary', key: AtomicType, value: AtomicType }
|
|
|
|
export type VariantMap = Map<string, SimpleType>;
|
|
export type FieldMap = Map<string, FieldType>;
|
|
|
|
export namespace Type {
|
|
export const union = (variants: VariantMap): Type => ({ kind: 'union', variants });
|
|
export const unit = (): AtomicType => ({ kind: 'unit' });
|
|
export const ref = (typeName: string): AtomicType => ({ kind: 'ref', typeName });
|
|
export const record = (fields: FieldMap): CompoundType => ({ kind: 'record', fields });
|
|
export const array = (type: AtomicType): CollectionType => ({ kind: 'array', type });
|
|
export const set = (type: AtomicType): CollectionType => ({ kind: 'set', type });
|
|
export const dictionary = (key: AtomicType, value: AtomicType): CollectionType => (
|
|
{ kind: 'dictionary', key, value });
|
|
}
|
|
|
|
export const ANY_TYPE: AtomicType = Type.ref('_val');
|
|
|
|
export function variantInitFor(variantName: string | undefined) : Item[] {
|
|
return variantName === void 0 ? [] : [variantFor(variantName)];
|
|
}
|
|
|
|
export function variantFor(variantName: string): Item {
|
|
return keyvalue('_variant', JSON.stringify(variantName));
|
|
}
|
|
|
|
export function renderVariant([variantName, t]: [string, SimpleType]): Item {
|
|
let fields: Item[];
|
|
switch (t.kind) {
|
|
case 'unit':
|
|
fields = [];
|
|
break;
|
|
case 'ref':
|
|
case 'set':
|
|
case 'dictionary':
|
|
case 'array':
|
|
fields = [keyvalue('value', renderType(t))];
|
|
break;
|
|
case 'record':
|
|
fields = Array.from(t.fields).map(([nn, tt]) => keyvalue(nn, renderType(tt)));
|
|
break;
|
|
default:
|
|
((_: never) => {})(t);
|
|
throw new Error("Unreachable");
|
|
}
|
|
return braces(variantFor(variantName), ... fields);
|
|
}
|
|
|
|
export function renderType(t: Type): Item {
|
|
switch (t.kind) {
|
|
case 'union': return opseq('never', ' | ', ...
|
|
Array.from(t.variants).flatMap(renderVariant));
|
|
case 'unit': return 'null';
|
|
case 'ref': return t.typeName;
|
|
case 'set': return seq('_.KeyedSet', anglebrackets(
|
|
renderType(t.type),
|
|
'_embedded'));
|
|
case 'dictionary': return seq('_.KeyedDictionary', anglebrackets(
|
|
renderType(t.key),
|
|
renderType(t.value),
|
|
'_embedded'));
|
|
case 'array': return seq('Array', anglebrackets(renderType(t.type)));
|
|
case 'record': return braces(... Array.from(t.fields).map(([nn, tt]) =>
|
|
keyvalue(nn, renderType(tt))));
|
|
default:
|
|
((_: never) => {})(t);
|
|
throw new Error("Unreachable");
|
|
}
|
|
}
|