preserves/implementations/javascript/packages/schema/src/host.ts

84 lines
3.0 KiB
TypeScript

import { compare } from '@preserves/core';
import * as M from './meta';
import * as H from './gen/host';
export * from './gen/host';
export function definitionType<V>(p: M.Definition<V>): H.Definition {
switch (p._variant) {
case 'or': return H.Definition.union([p.pattern0, p.pattern1, ... p.patternN].map(p =>
H.Variant({ label: Symbol.for(p.variantLabel), type: patternType(p.pattern) })));
case 'and':
return H.Definition.Simple(productType([p.pattern0, p.pattern1, ... p.patternN]));
case 'Pattern':
return H.Definition.Simple(patternType(p.value));
}
}
export function patternType<V>(p: M.Pattern<V>): H.Simple {
switch (p._variant) {
case 'SimplePattern':
return H.Simple.Field(fieldType(p.value));
case 'CompoundPattern':
return productType([M.NamedPattern.anonymous(p)]);
}
}
export function fieldType<V>(p: M.SimplePattern<V>): H.Field {
switch (p._variant) {
case 'any': return H.Field.any();
case 'atom': return H.Field.AtomKind(p.atomKind);
case 'embedded': return H.Field.embedded();
case 'lit': return H.Field.unit();
case 'seqof': return H.Field.array(fieldType(p.pattern));
case 'setof': return H.Field.set(fieldType(p.pattern));
case 'dictof': return H.Field.map({ key: fieldType(p.key), value: fieldType(p.value) });
case 'Ref': return H.Field.ref(p.value);
}
}
export function productType<V>(ps: M.NamedPattern<V>[]): H.Simple {
const gathered: H.NamedField[] = [];
ps.forEach(p => gather(p, gathered));
if (gathered.length === 0) return H.Simple.Field(H.Field.unit());
return H.Simple.Record(H.Record(gathered));
}
function promote<V>(p: M.NamedSimplePattern<V>): M.NamedPattern<V> {
if (p._variant === 'named') return p;
return M.NamedPattern.anonymous(M.Pattern.SimplePattern(p.value));
}
function gather<V>(p: M.NamedPattern<V>, into: H.NamedField[]) {
switch (p._variant) {
case 'named': {
const t = fieldType(p.value.pattern);
if (t._variant !== 'unit') into.push(H.NamedField({ name: p.value.name, type: t }));
break;
}
case 'anonymous': {
if (p.value._variant === 'SimplePattern') return;
const q = p.value.value;
switch (q._variant) {
case 'rec':
gather(q.label, into);
gather(q.fields, into);
break;
case 'tuple':
q.patterns.forEach(p => gather(p, into));
break;
case 'tuplePrefix':
q.fixed.forEach(p => gather(p, into));
gather(promote(q.variable), into);
break;
case 'dict': {
const items = Array.from(q.entries.entries()).sort((a, b) => compare(a[0], b[0]));
items.forEach(([_key, p]) => gather(promote(p), into));
break;
}
}
}
}
}