branching and very basic merging of slots
This commit is contained in:
parent
614e6c0fdb
commit
3978f7f835
32 changed files with 684 additions and 420 deletions
96
versioning/slot.js
Normal file
96
versioning/slot.js
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import { add, emptySet, forEach } from "../structures/set.js";
|
||||
import { deepEqual } from "../util/util.js";
|
||||
import {inspect} from "node:util";
|
||||
|
||||
// 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)})`,
|
||||
});
|
||||
|
||||
export const compareSlots = compareElems => slotA => slotB => {
|
||||
if (slotA.depth < slotB.depth) {
|
||||
return -1;
|
||||
}
|
||||
if (slotB.depth < slotA.depth) {
|
||||
return 1;
|
||||
}
|
||||
return compareValues(compareElems)(slotA.value)(slotB.value);
|
||||
};
|
||||
|
||||
export const compareValues = compareElems => valA => valB => {
|
||||
if (valA.kind < valB.kind) {
|
||||
return -1;
|
||||
}
|
||||
if (valB.kind < valA.kind) {
|
||||
return 1;
|
||||
}
|
||||
if (valA.kind === "literal") {
|
||||
return compareElems(valA.out)(valB.out);
|
||||
}
|
||||
if (valA.kind === "read") {
|
||||
return compareSlots(compareElems)(valA.slot)(valB.slot);
|
||||
}
|
||||
if (valA.kind === "transformation") {
|
||||
const cmpIn = compareValues(compareElems)(valA.in)(valB.in);
|
||||
if (cmpIn !== 0) {
|
||||
return cmpIn;
|
||||
}
|
||||
return compareValues(compareElems)(valA.fn)(valB.fn);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 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;
|
||||
};
|
||||
95
versioning/value.js
Normal file
95
versioning/value.js
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import { deepEqual } from "../util/util.js";
|
||||
import {inspect} from "node:util";
|
||||
|
||||
// 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.
|
||||
|
||||
// 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<Any>>
|
||||
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;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue