Factor out common constructor-building logic in interpreter

This commit is contained in:
Tony Garnock-Jones 2024-03-27 14:47:14 +01:00
parent dc0ddf95dd
commit 1bd4a3cdb4
1 changed files with 60 additions and 65 deletions

View File

@ -78,6 +78,22 @@ export type Unparseable<V extends Embeddable> = TopParsed<V>;
export type Unparser<V extends Embeddable> = (v: Parsed<V>) => Value<V>;
export type UnparserCompound<V extends Embeddable> = (v: Bindings<V>) => Value<V>;
function attachSchema<V extends Embeddable>(
schema: M.Schema<V>,
name: symbol,
f: (input: any) => Parsed<V>,
variant?: symbol,
): SingleConstructor<V> {
const g = f as SingleConstructor<V>;
g.schema = () => ({
schema: fromJS(schema),
imports: {}, // TODO
definitionName: name,
variant,
});
return g;
}
export class SchemaInterpreter<V extends Embeddable> {
activeModule: M.ModulePath = [];
unparserCache: { [key: string]: [Unparser<V>] } = {};
@ -135,83 +151,62 @@ export class SchemaInterpreter<V extends Embeddable> {
return result;
}
buildConstructor(
modulePath: M.ModulePath,
name: symbol,
schema: M.Schema<V>,
ty: H.Simple,
variant?: symbol,
): SingleConstructor<V> {
const flatName = M.formatModulePath([
... modulePath, name, ... (variant === void 0 ? [] : [variant])]);
const mkBase = (variant === void 0)
? () => ({})
: () => ({ _variant: variant.description! });
switch (ty._variant) {
case 'Field': {
const tmp =
ty.value._variant === 'unit'
? { [flatName]: () => this.makeTop(modulePath, name, mkBase()) }
: (variant === void 0
? { [flatName]: (value: any) => value }
: { [flatName]: (value: any) => this.makeTop(
modulePath, name, { ... mkBase(), value }) });
return attachSchema(schema, name, tmp[flatName], variant);
}
case 'Record': {
const rec = ty.value;
if (rec.fields.length > 1) {
const tmp = { [flatName]: (fields: Bindings<V>) =>
this.makeTop(modulePath, name, { ... mkBase(), ... fields }) };
return attachSchema(schema, name, tmp[flatName], variant);
} else {
const tmp = { [flatName]: (field: Parsed<V>) =>
this.makeTop(modulePath, name, {
... mkBase(),
[M.jsId(rec.fields[0].name.description!)]: field,
}) };
return attachSchema(schema, name, tmp[flatName], variant);
}
}
}
}
definitionConstructor(
modulePath: M.ModulePath,
name: symbol,
): DefinitionConstructors<V> {
return this._lookup(modulePath, name, (definition, schema): DefinitionConstructors<V> => {
function attachSchema(
f: (input: any) => Parsed<V>,
variant?: symbol,
): SingleConstructor<V> {
const g = f as SingleConstructor<V>;
g.schema = () => ({
schema: fromJS(schema),
imports: {}, // TODO
definitionName: name,
variant,
});
return g;
}
const ty = H.definitionType(definition);
if (ty._variant === 'union') {
const multiple: MultipleConstructors<V> = {};
ty.variants.forEach(v => {
const _variant = v.label.description!;
const flatName = M.formatModulePath([... modulePath, name, v.label]);
switch (v.type._variant) {
case 'Field': {
const tmp =
v.type.value._variant === 'unit'
? { [flatName]: () => this.makeTop(modulePath, name, { _variant }) }
: { [flatName]: (value: any) => this.makeTop(modulePath, name, { _variant, value }) };
multiple[M.jsId(_variant)] = attachSchema(tmp[flatName], v.label);
break;
}
case 'Record': {
const rec = v.type.value;
if (rec.fields.length > 1) {
const tmp = { [flatName]: (fields: Bindings<V>) =>
this.makeTop(modulePath, name, { _variant, ... fields }) };
multiple[M.jsId(_variant)] = attachSchema(tmp[flatName], v.label);
} else {
const tmp = { [flatName]: (field: Parsed<V>) =>
this.makeTop(modulePath, name, {
_variant,
[M.jsId(rec.fields[0].name.description!)]: field,
}) };
multiple[M.jsId(_variant)] = attachSchema(tmp[flatName], v.label);
}
break;
}
}
multiple[M.jsId(v.label.description!)] = this.buildConstructor(
modulePath, name, schema, v.type, v.label);
});
return multiple;
} else {
const flatName = M.formatModulePath([... modulePath, name]);
switch (ty.value._variant) {
case 'Field': {
const tmp =
ty.value.value._variant === 'unit'
? { [flatName]: () => this.makeTop(modulePath, name, {}) }
: { [flatName]: (value: any) => value };
return attachSchema(tmp[flatName]);
}
case 'Record': {
const rec = ty.value.value;
if (rec.fields.length > 1) {
const tmp = { [flatName]: (fields: Bindings<V>) =>
this.makeTop(modulePath, name, fields) };
return attachSchema(tmp[flatName]);
} else {
const tmp = { [flatName]: (field: Parsed<V>) =>
this.makeTop(modulePath, name, {
[M.jsId(rec.fields[0].name.description!)]: field,
}) };
return attachSchema(tmp[flatName]);
}
}
}
return this.buildConstructor(modulePath, name, schema, ty.value);
}
});
}