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

64
lib/versioning/slot.js Normal file
View file

@ -0,0 +1,64 @@
import { add, emptySet, forEach } from "../../structures/set.js";
import { deepEqual } from "../util/util.js";
import {inspect} from "node:util";
import { compareSlots } from "../compare/versioning.js";
// UUID -> Value<a> -> Slot<a>
export const newSlot = uuid => value => ({
overwrites: uuid,
value,
depth: 1,
[inspect.custom]: (depth, options, inspect) => `newSlot(${inspect(uuid)}, ${inspect(value)})`,
});
// Slot<a> -> Value<a> -> Slot<a>
export const overwrite = slot => value => ({
overwrites: slot,
value,
depth: slot.depth + 1,
// [inspect.custom]: (depth, options, inspect) => `overwrite(${inspect(slot)}, ${inspect(value)})`,
});
const findLCA = slotA => slotB => {
if (deepEqual(slotA, slotB)) {
return slotA;
}
if (slotA.depth > slotB.depth) {
return findLCA(slotA.overwrites)(slotB)
}
else {
return findLCA(slotB.overwrites)(slotA)
}
};
// Slot<a> -> Slot<a> -> MergeResult<a>
export const merge = compareElems => slotA => slotB => {
const lca = findLCA(slotA)(slotB);
if (lca === undefined) {
throw new Error("Could not find LCA");
}
if (deepEqual(lca, slotA)) {
return add(emptySet(compareSlots(compareElems)))(slotB);
// return new Set([slotB]); // B is successor of A -> fast-forward
}
if (deepEqual(lca, slotB)) {
return add(emptySet(compareSlots(compareElems)))(slotA);
// return new Set([slotA]); // A is successor of B -> fast-forward
}
return add(add(emptySet(compareSlots(compareElems)))(slotA))(slotB);
// return new Set([slotA, slotB]);
};
// MergeResult<a> -> MergeResult<a> -> MergeResult<a>
export const merge2 = compareElems => mA => mB => {
let result = emptySet(compareSlots(compareElems));
forEach(mA)(slotOfA => {
forEach(mB)(slotOfB => {
const merged = merge(compareElems)(slotOfA)(slotOfB);
forEach(merged)(merged => {
result = add(result)(merged);
});
});
});
return result;
};

27
lib/versioning/types.js Normal file
View file

@ -0,0 +1,27 @@
import { Dynamic } from "../primitives/dynamic.js";
import { Int } from "../primitives/types.js";
import { enumType } from "../structures/enum.js";
import { newProduct } from "../structures/product.js";
import { structType } from "../structures/struct.js";
import { prodType } from "../structures/types.js";
import { prettyT } from "../util/pretty.js";
const Slot = structType([
newProduct("value")(() => Value),
newProduct("depth")(() => Int),
newProduct("overwrites")(() => Slot),
]);
// A Value is either:
// - a literal, without any dependencies.
// - read from a slot. the Value then has a read-dependency on that slot.
// - a transformation of another Value, by a function. the function is also a Value.
const variants = [
newProduct("literal", () => Dynamic),
newProduct("read", () => Slot),
newProduct("transformation", () => prodType(Value, Dynamic)),
];
const Value = enumType(variants);
// console.log(prettyT(Slot));
// console.log(prettyT(Value));

91
lib/versioning/value.js Normal file
View file

@ -0,0 +1,91 @@
import { fnType } from "../structures/types.js";
import { deepEqual } from "../util/util.js";
import { inspect } from "node:util";
// a -> Value<a>
export const newLiteral = val => ({
kind: "literal",
out: val,
[inspect.custom]: (depth, options, inspect) => `newLiteral(${inspect(val)})`,
});
// Slot<a> -> Value<a>
export const read = slot => ({
kind: "read",
slot,
out: slot.value.out,
[inspect.custom]: (depth, options, inspect) => `read(${inspect(slot)})`,
});
// Value<a> -> Value<a -> b> -> Value<b>
export const transform = input => fn => {
const output = fn.out(input.out);
const _inspect = (depth, options, inspect) => `transform(${inspect(input)}, ${inspect(fn)})`;
if (input.kind === "literal") {
// optimization: sandwich everything together
return {
kind: "literal",
out: output,
// [inspect.custom]: _inspect,
};
}
else {
return {
kind: "transformation",
in: input,
fn,
out: output,
// [inspect.custom]: _inspect,
};
}
};
// Value<a> -> Set<Slot<Top>>
export const getReadDependencies = value => {
if (value.kind === "literal") {
return new Set();
}
else if (value.kind === "read") {
return new Set([value.slot]);
}
else if (value.kind === "transformation") {
return new Set([
...getReadDependencies(value.in),
...getReadDependencies(value.fn),
]);
}
};
// for debugging
export const verifyValue = (value, indent = 0) => {
let success = true;
const printIndent = (...args) => {
console.log(" ".repeat(indent * 2), ...args);
};
const compare = (a, b, kind) => {
if (typeof a === 'function' && typeof b === 'function') {
printIndent("note: cannot compare functions", `(${kind})`);
}
else if (deepEqual(a, b)) {
printIndent(`ok (${kind})`);
}
else {
printIndent(`bad (${kind})`);
success = false;
}
};
if (value.kind === "literal") {
compare(1, 1, "literal");
}
else if (value.kind === "read") {
compare(value.out, value.slot.value.out, "read");
}
else if (value.kind === "transformation") {
compare(value.fn.out(value.in.out),
value.out, "transformation");
success &= verifyValue(value.in, indent + 1);
success &= verifyValue(value.fn, indent + 1);
}
return success;
};