dope2/lib/structures/enum.js

75 lines
2.3 KiB
JavaScript

import { compareStrings } from "../compare/primitives.js";
import { unit } from "../primitives/unit.js";
import { capitalizeFirstLetter } from "../util/util.js";
const eatParameters = (numParams, result) => {
if (numParams === 0) {
return result;
}
else return () => eatParameters(numParams-1, result);
}
export const makeMatchFn = variantNames => {
if (variantNames.length === 0) {
return _ => {
throw new Error("Bottom!");
};
}
return enumm => {
return matchFn(enumm, variantNames);
}
};
const matchFn = (enumm, variantNames) => {
const [variantName, ...remaining] = variantNames;
return handler => {
if (enumm.variant === variantName) {
return eatParameters(
remaining.length,
handler(enumm.value));
}
else {
return matchFn(enumm, remaining);
}
};
};
export const symbolElse = Symbol('else');
export const makeNiceMatchFn = (variantNames) => handlers => enumm => {
const incomplete = variantNames.filter(({l: variant}) => !handlers.hasOwnProperty(variant)).length > 0;
if (incomplete && !handlers.hasOwnProperty(symbolElse)) {
throw new Error("if not every variant is handled, must have 'else' handler");
}
if (!incomplete && handlers.hasOwnProperty(symbolElse)) {
console.warn("'else' handler will never be invoked because every variant is already handled");
}
for (const [variant, handler] of Object.entries(handlers)) {
if (enumm.variant === variant) {
return handler(enumm.value);
}
}
return handlers[symbolElse](unit);
}
export const makeConstructors = variantNames => {
if (new Set(variantNames).size !== variantNames.length) {
throw new Error("precondition failed: non-unique variant names");
}
return variantNames.map(variantName => {
const ctorName = `new${capitalizeFirstLetter(variantName)}`;
const ctor = { [ctorName]: value => ({variant: variantName, value}) }[ctorName];
return ctor;
})
};
export const makeEnumCompareFn = variantCompares => {
return enumA => enumB => {
const cmp = compareStrings(enumA.variant)(enumB.variant);
if (cmp !== 0) {
return cmp;
}
const getCompareFn = variantCompares.find(({l: variantName}) => variantName === enumA.variant).r;
return getCompareFn()(enumA.value)(enumB.value);
}
};