76 lines
2.4 KiB
JavaScript
76 lines
2.4 KiB
JavaScript
import { SymbolT, Type } from "../primitives/types.js";
|
|
import { makeTypeConstructor } from "../type_constructor.js";
|
|
import { Module, String } from "./list.js";
|
|
import { prodType, fnType, lsType } from "./types.js";
|
|
|
|
function capitalizeFirstLetter(val) {
|
|
return String(val).charAt(0).toUpperCase() + String(val).slice(1);
|
|
}
|
|
|
|
export const createStruct = (typeVars, symbol, fields) => {
|
|
const makeConstructor = (remainingFields, obj={}) => {
|
|
if (remainingFields.length===0) {
|
|
return obj;
|
|
}
|
|
const {left: fieldName} = remainingFields[remainingFields.length-1];
|
|
return v => makeConstructor(
|
|
remainingFields.slice(0,-1),
|
|
Object.assign({[fieldName]: v}, obj));
|
|
};
|
|
const constructor = makeConstructor(fields);
|
|
|
|
const type = makeTypeConstructor(symbol)(typeVars.size);
|
|
const types = [ type ];
|
|
const recordFnType = inType => outType => {
|
|
const fnT = fnType(inType)(outType);
|
|
types.push(fnT);
|
|
return fnT;
|
|
}
|
|
|
|
const makeConstructorType = (remainingFields, type) => {
|
|
if (remainingFields.length===0) {
|
|
return type;
|
|
}
|
|
const {right: fieldType} = remainingFields[remainingFields.length-1];
|
|
return recordFnType(makeConstructorType(remainingFields.slice(0,-1)))(fieldType);
|
|
};
|
|
const constructorType = makeConstructorType(fields);
|
|
|
|
const functions = [
|
|
["constructor", constructor, constructorType],
|
|
...fields.map(({left: fieldName, right: fieldType}) => {
|
|
const getterName = 'get'+capitalizeFirstLetter(fieldName);
|
|
const getter = {
|
|
// stupid trick to give the JS-function a computed name.
|
|
// only important for debugging, so it says [Function: getAge] instead of [Function (anonymous)]:
|
|
[getterName]: obj => obj[fieldName],
|
|
}[getterName];
|
|
if (typeVars.has(fieldType)) {
|
|
// getterFnType = recordFnType(type)(fieldType)
|
|
}
|
|
const getterFnType = recordFnType(type)(fieldType);
|
|
return [fieldName, getter, getterFnType];
|
|
}),
|
|
];
|
|
|
|
const module = {l:[
|
|
{i: type, t: Type},
|
|
|
|
...functions.flatMap(([_, getter, getterFnType]) => [
|
|
{i: getter , t: getterFnType},
|
|
]),
|
|
|
|
...types.map(type => ({i: type, t: Type})),
|
|
]};
|
|
|
|
return {
|
|
module,
|
|
constructor,
|
|
functions: Object.fromEntries(functions),
|
|
};
|
|
};
|
|
|
|
export const createNominalADTModuleFnType =
|
|
fnType(SymbolT)
|
|
(fnType(lsType(prodType(String)(Type)))
|
|
(Module));
|