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; export type FieldMap = Map; 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"); } }