Arboricx bundle format 1.1
We don't need SHA verification or Merkle dags in our transport bundle. Content stores can handle both bundle and term verification and hashing.
This commit is contained in:
@@ -1,80 +1,113 @@
|
||||
import { strictEqual, ok } from "node:assert";
|
||||
import { describe, it } from "node:test";
|
||||
import { apply, isLeaf, isStem, isFork } from "../src/tree.js";
|
||||
import { reduce } from "../src/cli.js";
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { strictEqual, ok } from 'node:assert';
|
||||
import { describe, it } from 'node:test';
|
||||
import {
|
||||
findLib,
|
||||
init,
|
||||
free,
|
||||
leaf,
|
||||
stem,
|
||||
fork,
|
||||
app,
|
||||
reduce,
|
||||
toBool,
|
||||
toString,
|
||||
toNumber,
|
||||
loadBundleDefault,
|
||||
ofString,
|
||||
ofNumber,
|
||||
} from '../src/lib.js';
|
||||
|
||||
describe("tree — basic types", () => {
|
||||
it("Leaf is empty array", () => {
|
||||
ok(isLeaf([]));
|
||||
ok(!isStem([]));
|
||||
ok(!isFork([]));
|
||||
const libPath = findLib();
|
||||
|
||||
describe('tree construction', () => {
|
||||
it('leaf returns a positive index', () => {
|
||||
const ctx = init(libPath);
|
||||
try {
|
||||
const idx = leaf(ctx);
|
||||
ok(idx > 0);
|
||||
} finally {
|
||||
free(ctx);
|
||||
}
|
||||
});
|
||||
|
||||
it("Stem is single-element array", () => {
|
||||
ok(isStem([[]]));
|
||||
ok(!isLeaf([[]]));
|
||||
it('stem wraps a child', () => {
|
||||
const ctx = init(libPath);
|
||||
try {
|
||||
const l = leaf(ctx);
|
||||
const s = stem(ctx, l);
|
||||
ok(s > 0);
|
||||
ok(s !== l);
|
||||
} finally {
|
||||
free(ctx);
|
||||
}
|
||||
});
|
||||
|
||||
it("Fork is two-element array", () => {
|
||||
ok(isFork([[], []]));
|
||||
ok(!isLeaf([[], []]));
|
||||
it('fork combines left and right', () => {
|
||||
const ctx = init(libPath);
|
||||
try {
|
||||
const a = leaf(ctx);
|
||||
const b = leaf(ctx);
|
||||
const f = fork(ctx, a, b);
|
||||
ok(f > 0);
|
||||
ok(f !== a && f !== b);
|
||||
} finally {
|
||||
free(ctx);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("tree — apply rules", () => {
|
||||
// Leaf = [], Stem = [child], Fork = [right, left]
|
||||
|
||||
it("apply(Leaf, b) = Stem(b)", () => {
|
||||
const b = []; // Leaf
|
||||
const result = apply([], b);
|
||||
ok(isStem(result), "Stem(b) should be a Stem");
|
||||
strictEqual(result[0], b);
|
||||
describe('reduction — booleans', () => {
|
||||
it('true.arboricx reduces to boolean true', () => {
|
||||
const ctx = init(libPath);
|
||||
try {
|
||||
const bundle = readFileSync('../../test/fixtures/true.arboricx');
|
||||
const root = loadBundleDefault(ctx, bundle);
|
||||
const result = reduce(ctx, root, 1_000_000n);
|
||||
strictEqual(toBool(ctx, result), true);
|
||||
} finally {
|
||||
free(ctx);
|
||||
}
|
||||
});
|
||||
|
||||
it("apply(Stem(a), b) = Fork(a, b)", () => {
|
||||
const a = []; // Leaf
|
||||
const b = []; // Leaf
|
||||
const result = apply([a], b);
|
||||
ok(isFork(result), "Fork(a, b) should be a Fork");
|
||||
// Fork = [right, left] = [b, a]
|
||||
strictEqual(result[0], b);
|
||||
strictEqual(result[1], a);
|
||||
});
|
||||
|
||||
it("apply(Fork(Leaf, a), _) = a", () => {
|
||||
// Fork(Leaf, a) = [a, Leaf]
|
||||
const a = []; // Leaf
|
||||
const result = apply([a, []], []);
|
||||
strictEqual(result, a);
|
||||
ok(isLeaf(result));
|
||||
it('false.arboricx reduces to boolean false', () => {
|
||||
const ctx = init(libPath);
|
||||
try {
|
||||
const bundle = readFileSync('../../test/fixtures/false.arboricx');
|
||||
const root = loadBundleDefault(ctx, bundle);
|
||||
const result = reduce(ctx, root, 1_000_000n);
|
||||
strictEqual(toBool(ctx, result), false);
|
||||
} finally {
|
||||
free(ctx);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("tree — reduction", () => {
|
||||
it("reduces Leaf to Leaf", () => {
|
||||
const result = reduce([], 100);
|
||||
ok(isLeaf(result));
|
||||
});
|
||||
|
||||
it("reduces Stem Leaf to Stem Leaf", () => {
|
||||
const result = reduce([[]], 100);
|
||||
ok(isStem(result));
|
||||
ok(isLeaf(result[0]));
|
||||
});
|
||||
|
||||
it("reduces Fork Leaf Leaf to Fork Leaf Leaf", () => {
|
||||
const result = reduce([[], []], 100);
|
||||
ok(isFork(result));
|
||||
ok(isLeaf(result[0]));
|
||||
ok(isLeaf(result[1]));
|
||||
});
|
||||
|
||||
it("S combinator applied to Leaf reduces", () => {
|
||||
// S = t (t (t t)) t = Fork (Fork (Fork Leaf Leaf) Leaf) Leaf
|
||||
// In array form: [[[], []], [], []]
|
||||
const s = [[], [[[], []], []]];
|
||||
const leaf = [];
|
||||
const result = reduce([s, leaf], 100);
|
||||
ok(Array.isArray(result), "S Leaf should reduce to an array");
|
||||
describe('reduction — id', () => {
|
||||
it('id applied to string returns the string', () => {
|
||||
const ctx = init(libPath);
|
||||
try {
|
||||
const bundle = readFileSync('../../test/fixtures/id.arboricx');
|
||||
const idRoot = loadBundleDefault(ctx, bundle);
|
||||
const arg = ofString(ctx, 'hello');
|
||||
const applied = app(ctx, idRoot, arg);
|
||||
const result = reduce(ctx, applied, 1_000_000n);
|
||||
strictEqual(toString(ctx, result), 'hello');
|
||||
} finally {
|
||||
free(ctx);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('reduction — numbers', () => {
|
||||
it('ofNumber round-trips through toNumber', () => {
|
||||
const ctx = init(libPath);
|
||||
try {
|
||||
const num = ofNumber(ctx, 42);
|
||||
strictEqual(toNumber(ctx, num), 42);
|
||||
} finally {
|
||||
free(ctx);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user