reorganize directory and file structure

This commit is contained in:
Joeri Exelmans 2025-05-07 13:44:49 +02:00
parent 1d826ea8d4
commit 48390b8556
99 changed files with 1155 additions and 1629 deletions

22
lib/structures/dict.js Normal file
View file

@ -0,0 +1,22 @@
import { RBTreeWrapper } from "../util/rbtree_wrapper.js";
import { newProduct } from "./product.js";
import { newLeft, newRight } from "./sum.js";
export const emptyDict = compareFn => RBTreeWrapper.new((x, y) => compareFn(x)(y));
export const has = dict => key => dict.tree.get(key) === true;
export const set = dict => key => value => new RBTreeWrapper(dict.tree.remove(key).insert(key, value));
export const remove = dict => key => new RBTreeWrapper(dict.tree.remove(key));
export const length = dict => dict.tree.length;
export const first = dict => dict.tree.begin;
export const last = dict => dict.tree.end;
export const read = iter => {
if (iter !== undefined && iter.valid) {
return newRight(newProduct(newProduct(iter.key)(iter.value))(iter.clone().next()));
}
else {
return newLeft(unit);
}
};

View file

@ -0,0 +1,20 @@
import { makeTypeParser } from "../parser/type_parser.js";
import { makeTypeConstructor } from "../meta/type_constructor.js";
import { emptyDict, first, has, last, length, read, remove, set } from "./dict.js";
const dictIterator = makeTypeConstructor('DictIterator__d9d175b6bfd1283f00851a99787d0499')(2);
const mkType = makeTypeParser({
extraInfixOperators: [['|=>|', dictIterator]],
});
export const ModuleDict = [
{ i: emptyDict , t: mkType("∀a,b: (a -> a -> Int) -> (a => b)") },
{ i: has , t: mkType("∀a,b: (a => b) -> a -> Bool")},
{ i: set , t: mkType("∀a,b: (a => b) -> a -> b -> (a => b)")},
{ i: remove , t: mkType("∀a,b: (a => b) -> a -> (a => b)")},
{ i: length , t: mkType("∀a,b: (a => b) -> Int")},
{ i: first , t: mkType("∀a,b: (a => b) -> (a |=>| b)")},
{ i: last , t: mkType("∀a,b: (a => b) -> (a |=>| b)")},
{ i: read , t: mkType("∀a,b: (a |=>| b) -> (Unit + ((a*b) * (a |=>| b)))")},
];

37
lib/structures/enum.js Normal file
View file

@ -0,0 +1,37 @@
import { capitalizeFirstLetter } from "../util/util.js";
import { newProduct as newProduct, getLeft } from "./product.js";
import { newLeft, newRight, match } from "./sum.js";
const eatParameters = (numParams, result) => {
if (numParams === 0) {
return result;
}
else return () => eatParameters(numParams-1, result);
}
export const makeMatchFn = variants => {
if (variants.length === 0) {
return undefined;
}
const [_, ...remainingVariants] = variants;
return sum => handler => {
return match(sum)
(leftValue => eatParameters(remainingVariants.length, handler(leftValue)))
(rightValue => makeMatchFn(remainingVariants)(rightValue));
};
};
export const makeConstructors = variants => {
if (variants.length === 0) {
return [];
}
const [variant, ...remainingVariants] = variants;
const name = getLeft(variant);
const ctorName = `new${capitalizeFirstLetter(name)}`;
const constructor = { [ctorName]: val => newLeft(val) }[ctorName];
return [
constructor,
...makeConstructors(remainingVariants).map(ctor =>
({[ctor.name]: val => newRight(ctor(val))}[ctor.name])),
];
}

View file

@ -0,0 +1,20 @@
import { Bottom } from "../primitives/primitive_types.js";
import { getRight } from "./product.js";
import { sumType } from "./type_constructors.js";
// 'variants' is an array of (name: string, type: Type) pairs.
// e.g., the list of variants:
// [ { l: "price" , r: Int },
// { l: "prices" , r: [Int] },
// { l: "not_found", r: Unit } ]
// results in the type:
// (Int | ([Int] | (Unit | ⊥)))
export const enumType = variants => {
if (variants.length === 0) {
return Bottom; // empty enum is equal to Bottom-type (cannot be instantiated)
}
const [variant, ...rest] = variants;
const variantType = getRight(variant);
return sumType(() => variantType)(() => enumType(rest));
};

16
lib/structures/list.js Normal file
View file

@ -0,0 +1,16 @@
// 'normal' implementation
export const emptyList = [];
// const emptyListType = makeGeneric(a => lsType(() => a));
export const get = ls => i => ls[i];
export const put = ls => i => elem => ls.with(Number(i), elem);
export const push = ls => elem => ls.concat([elem]);
export const pop = ls => ls.pop();
export const map = ls => fn => ls.map(elem => fn(elem));
export const length = ls => ls.length;
export const fold = ls => callback => initial => {
let acc = initial;
for (let i=0; i<ls.length; i++) {
acc = callback(acc)(ls[i]);
}
return acc;
}

View file

@ -0,0 +1,15 @@
import { getDefaultTypeParser }from "../parser/type_parser.js";
import { emptyList, fold, get, length, map, pop, push, put } from "./list.js";
const mkType = getDefaultTypeParser();
export const ModuleList = [
{ i: emptyList, t: mkType("∀a: [a]")},
{ i: get , t: mkType("∀a: [a] -> Int -> a")},
{ i: put , t: mkType("∀a: [a] -> Int -> a -> [a]")},
{ i: push , t: mkType("∀a: [a] -> a -> [a]")},
{ i: pop , t: mkType("∀a: [a] -> a")},
{ i: map , t: mkType("∀a: [a] -> (a -> b) -> [b]")},
{ i: length , t: mkType("∀a: [a] -> Int")},
{ i: fold , t: mkType("∀a: [a] -> (b -> a -> b) -> b -> b")},
];

View file

@ -0,0 +1,9 @@
// Product-type (also called: pair, tuple)
// A Product-type always has only two fields, called "left" and "right".
// Product-types of more fields (called Structs) can be constructed by nesting Product-types.
// In JS, all products are encoded in the same way:
export const newProduct = l => r => ({l, r});
export const getLeft = product => product.l;
export const getRight = product => product.r;

View file

@ -0,0 +1,10 @@
import { getDefaultTypeParser } from "../parser/type_parser.js";
import { newProduct, getLeft, getRight } from "./product.js";
const mkType = getDefaultTypeParser();
export const ModuleProduct = [
{ i: newProduct, t: mkType("∀a,b: a -> b -> (a * b)") },
{ i: getLeft , t: mkType("∀a,b: (a * b) -> a" ) },
{ i: getRight , t: mkType("∀a,b: (a * b) -> b" ) },
];

29
lib/structures/set.js Normal file
View file

@ -0,0 +1,29 @@
import { newRight } from "./sum.js";
import { newProduct } from "./product.js";
import { unit } from "../primitives/unit.js";
import { RBTreeWrapper } from "../util/rbtree_wrapper.js";
// (a -> a -> Int) -> Set(a)
export const emptySet = compareFn => RBTreeWrapper.new((x, y) => compareFn(x)(y));
export const has = set => key => set.tree.get(key) === true;
export const add = set => key => set.tree.get(key) === true ? set : new RBTreeWrapper(set.tree.insert(key, true));
export const remove = set => key => new RBTreeWrapper(set.tree.remove(key));
export const length = set => set.tree.length;
export const first = set => set.tree.begin;
export const last = set => set.tree.end;
// test if iterator is 'done', and if not, get element and advance iterator.
export const read = iter => {
if (iter !== undefined && iter.valid) {
return newRight(newProduct(iter.key)(iter.clone().next()));
}
else {
return newLeft(unit);
}
};
export const forEach = set => fn => {
set.tree.forEach(key => { fn(key); });
};

View file

@ -0,0 +1,20 @@
import { makeTypeParser } from "../parser/type_parser.js";
import { makeTypeConstructor } from "../meta/type_constructor.js";
import { emptySet, has, add, remove, length, first, read, last } from "./set.js";
const setIterator = makeTypeConstructor('SetIterator__f6b0ddd78ed41c58e5a442f2681da011')(1);
const mkType = makeTypeParser({
extraBracketOperators: [['<', ['>', setIterator]]],
});
export const ModuleSet = [
{ i: emptySet , t: mkType("∀a: (a -> a -> Int) -> {a}") },
{ i: has , t: mkType("∀a: {a} -> a -> Bool")},
{ i: add , t: mkType("∀a: {a} -> a -> {a}")},
{ i: remove , t: mkType("∀a: {a} -> a -> {a}")},
{ i: length , t: mkType("∀a: {a} -> Int")},
{ i: first , t: mkType("∀a: {a} -> <a>")},
{ i: last , t: mkType("∀a: {a} -> <a>")},
{ i: read , t: mkType("∀a: <a> -> (Unit + (a * <a>))")},
];

33
lib/structures/struct.js Normal file
View file

@ -0,0 +1,33 @@
import { unit } from "../primitives/unit.js";
import { capitalizeFirstLetter } from "../util/util.js";
import { newProduct, getLeft, getRight } from "./product.js";
export const makeConstructor = nParams => {
const internal = (nParams, ret) => {
if (nParams === 0) {
const result = ret(unit);
return result;
}
return nextParam => {
const wrappedName = 'wrapped_' + ret.name;
const newRet = {
[wrappedName]: inner => newProduct(nextParam)(ret(inner)),
}[wrappedName];
return internal(nParams-1, newRet);
}
};
const id = x => x;
return internal(nParams, id);
};
export const makeGetters = fieldNames => {
if (fieldNames.length === 0) {
return [];
}
const [fieldName, ...rest] = fieldNames;
const getterName = `get${capitalizeFirstLetter(fieldName)}`;
return [
{ [getterName]: obj => getLeft(obj) }[getterName],
...makeGetters(rest).map(getter => ({[getter.name]: obj => getter(getRight(obj))}[getter.name])),
];
};

View file

@ -0,0 +1,61 @@
import { getDefaultTypeParser } from "../parser/type_parser.js";
import { newDynamic } from "../primitives/dynamic.js";
import { Type, Unit } from "../primitives/primitive_types.js";
import { zip } from "../util/util.js";
import { map } from "./list.js";
import { getLeft, getRight } from "./product.js";
import { makeConstructor, makeGetters } from "./struct.js";
import { fnType, prodType } from "./type_constructors.js";
// 'fields' is an array of (name: string, type: Type) pairs.
// e.g.:
// [{l: "x", r: Double}, {l: "y", r: Double}]
// results in the type (Double × (Double × Unit))
export const structType = fieldTypes => {
if (fieldTypes.length === 0) {
return Unit;
}
const [fieldType, ...rest] = fieldTypes;
return prodType(_ => fieldType)(_ => structType(rest));
};
export const makeConstructorType = fieldTypes => {
if (fieldTypes.length === 0) {
return structType(fieldTypes);
}
const [fieldType, ...rest] = fieldTypes;
return fnType(_ => fieldType)(_ => makeConstructorType(rest));
};
export const makeGettersTypes = fieldTypes => {
const type = structType(fieldTypes);
return fieldTypes.map(fieldType => {
return fnType(_ => type)(_ => fieldType);
});
};
export const makeModuleStruct = fields => {
const fieldNames = map(fields)(getLeft);
const fieldTypes = map(fields)(getRight);
const type = structType(fieldTypes);
const ctor = makeConstructor(fields.length);
const ctorType = makeConstructorType(fieldTypes);
const getterTypes = makeGettersTypes(fieldTypes);
const getters = makeGetters(fieldNames);
const module = [
{i: type, t: Type},
{i: ctor, t: ctorType},
...zip(getters, getterTypes)
.map(([getter, getterType]) => newDynamic(getter)(getterType)),
];
return module;
};
const mkType = getDefaultTypeParser();
export const ModuleStruct = [
{i: structType, t: mkType("[String*Type] -> Type")},
{i: makeModuleStruct, t: mkType("[String*Type] -> [Dynamic]")},
];

11
lib/structures/sum.js Normal file
View file

@ -0,0 +1,11 @@
// Sum-type (also called: tagged union, disjoint union, variant type)
// A Sum-type always has only two variants, called "left" and "right".
// Sum-types of more variants (called Enums) can be constructed by nesting Sum-types.
export const newLeft = left => ({t: "L", v: left });
export const newRight = right => ({t: "R", v: right});
export const match = sum => leftHandler => rightHandler =>
sum.t === "L"
? leftHandler(sum.v)
: rightHandler(sum.v);

View file

@ -0,0 +1,10 @@
import { getDefaultTypeParser }from "../parser/type_parser.js";
import { match, newLeft, newRight } from "./sum.js";
const mkType = getDefaultTypeParser();
export const ModuleSum = [
{ i: newLeft , t: mkType("∀a,b: a -> (a + b)") },
{ i: newRight , t: mkType("∀a,b: b -> (a + b)") },
{ i: match , t: mkType("∀a,b,c: (a + b) -> (a -> c) -> (b -> c) -> c") },
];

View file

@ -0,0 +1,17 @@
// to break up dependency cycles, type constructors are defined in their own JS module
import { makeTypeConstructor } from "../meta/type_constructor.js";
export const symbolFunction = "Function__c2433e31fa574a2cb3b6b5d62ac9d4b2";
export const symbolSum = "Sum__89b731efa6344ea0b6a8663a45cf3ea8";
export const symbolProduct = "Product__89351ecdedfb4b05b2a5a6cc0c383e12";
export const symbolList = "List__daa8de8a9047435e96034ec64f2da3a1";
export const symbolSet = "Set__8fef2c1873df4327ac31bd61d2ecf7e0";
export const symbolDict = "Dict__d7158547322549ac9f7f8176aec123dd";
export const fnType = makeTypeConstructor(symbolFunction)(2);
export const sumType = makeTypeConstructor(symbolSum)(2);
export const prodType = makeTypeConstructor(symbolProduct)(2);
export const lsType = makeTypeConstructor(symbolList)(1);
export const setType = makeTypeConstructor(symbolSet)(1);
export const dictType = makeTypeConstructor(symbolDict)(2);

View file

@ -0,0 +1,23 @@
import { getDefaultTypeParser } from "../parser/type_parser.js";
import { SymbolT } from "../primitives/primitive_types.js";
import { dictType, fnType, lsType, prodType, setType, sumType, symbolDict, symbolFunction, symbolList, symbolProduct, symbolSet, symbolSum } from "./type_constructors.js";
const mkType = getDefaultTypeParser();
export const ModuleStructuralSymbols = [
{ i: symbolSet , t: SymbolT },
{ i: symbolList , t: SymbolT },
{ i: symbolProduct , t: SymbolT },
{ i: symbolSum , t: SymbolT },
{ i: symbolDict , t: SymbolT },
{ i: symbolFunction , t: SymbolT },
];
export const ModuleTypeConstructors = [
{ i: setType , t: mkType("Type -> Type") },
{ i: lsType , t: mkType("Type -> Type") },
{ i: prodType , t: mkType("Type -> Type -> Type") },
{ i: sumType , t: mkType("Type -> Type -> Type") },
{ i: dictType , t: mkType("Type -> Type -> Type") },
{ i: fnType , t: mkType("Type -> Type -> Type") },
];