commit 6971e3456ee03418748d47a8dd81312cf0a51aea
parent f122ba64e4dee966709b72c56e4d8a6f104f942b
Author: James Eversole <james@eversole.co>
Date: Sun, 10 May 2026 14:52:24 -0500
PHP host shell cleanup and docs
Diffstat:
4 files changed, 103 insertions(+), 431 deletions(-)
diff --git a/ext/php/run.php b/ext/php/run.php
@@ -9,7 +9,6 @@ declare(strict_types=1);
* Usage:
* php run.php run <bundle.arboricx> <arg> [arg ...]
* php run.php inspect <bundle.arboricx>
- * php run.php repl
*
* The "run" command:
* 1. Reads the .arboricx bundle as raw bytes
@@ -27,71 +26,52 @@ require __DIR__ . '/src/functions.php';
require __DIR__ . '/src/codecs.php';
require __DIR__ . '/src/kernel.php';
-use function Arboricx\{app, apply_, reduce, ofString, ofNumber, ofBytes, ofList,
- formatTree, decodeResult, unwrapResult,
- unwrapHostValue, decodeHostPayload,
- isLeaf, isStem, isFork,
- HOST_STRING_TAG, HOST_NUMBER_TAG, HOST_BOOL_TAG,
- HOST_LIST_TAG, HOST_BYTES_TAG, HOST_TREE_TAG,
+use function Arboricx\{app, reduce, ofString, ofNumber, ofBytes, ofList,
+ formatTree, unwrapResult, unwrapHostValue, decodeHostPayload,
getRunArboricxToString};
// ── Commands ─────────────────────────────────────────────────────────────────
-function debugTime(string $label): void
+function readBundle(string $path): string
{
- static $start = null;
- static $last = null;
- $now = microtime(true);
- if ($start === null) {
- $start = $now;
- $last = $now;
+ if (!file_exists($path)) {
+ fwrite(STDERR, "Error: bundle not found: $path\n");
+ exit(1);
+ }
+ $bytes = file_get_contents($path);
+ if ($bytes === false) {
+ fwrite(STDERR, "Error: could not read bundle: $path\n");
+ exit(1);
}
- fwrite(STDERR, sprintf("[%.3fs +%.3fs] %s\n", $now - $start, $now - $last, $label));
- $last = $now;
+ return $bytes;
}
function cmdRun(string $bundlePath, array $args): void
{
- debugTime('start');
-
- if (!file_exists($bundlePath)) {
- fwrite(STDERR, "Error: bundle file not found: $bundlePath\n");
- exit(1);
- }
- $bundleBytes = file_get_contents($bundlePath);
- debugTime('read bundle bytes');
- if ($bundleBytes === false) {
- fwrite(STDERR, "Error: could not read bundle file: $bundlePath\n");
- exit(1);
- }
+ $bundleBytes = readBundle($bundlePath);
$kernel = getRunArboricxToString();
- debugTime('loaded kernel');
if ($kernel === null) {
- fwrite(STDERR, "Error: runArboricxToString kernel not configured (empty ternary string)\n");
- fwrite(STDERR, "Set KERNEL_RUN_ARBORICX_TO_STRING constant in kernel.php\n");
+ fwrite(STDERR, "Error: runArboricxToString kernel not configured\n");
exit(1);
}
$bundleTree = ofBytes($bundleBytes);
- debugTime('encoded bundle bytes');
$argTrees = [];
foreach ($args as $arg) {
$argTrees[] = encodeArg($arg);
}
$argsTree = ofList($argTrees);
- debugTime('encoded args');
+ // Kernel application: runArboricxToString bundle args
$expr = app(app($kernel, $bundleTree), $argsTree);
- debugTime('built expression');
-
fwrite(STDERR, "Reducing kernel application...\n");
$result = reduce($expr, 1_000_000_000);
- debugTime('reduced kernel application');
+ // The kernel returns an ok/err pair. On ok, the value is a
+ // Host ABI envelope: Fork(tag_number, payload_tree).
[$kind, $value, $rest] = unwrapResult($result);
- debugTime('unwrapped result');
if ($kind === 'error') {
fwrite(STDERR, "Error detail: $rest\n");
exit(1);
@@ -105,11 +85,9 @@ function cmdRun(string $bundlePath, array $args): void
}
[$tag, $payload] = unwrapHostValue($value);
- debugTime('unwrapped host value');
try {
$decoded = decodeHostPayload($tag, $payload);
- debugTime('decoded payload');
echo $decoded['value'] . "\n";
} catch (\Throwable $e) {
fwrite(STDERR, "Host ABI decode error: " . $e->getMessage() . "\n");
@@ -118,34 +96,14 @@ function cmdRun(string $bundlePath, array $args): void
}
}
-/**
- * Encode a command-line argument into a Tree Calculus value.
- *
- * - Numeric strings (digits only) → tree number
- * - Everything else → tree string
- */
function encodeArg(string $arg): int
{
- if (ctype_digit($arg) || ($arg !== '0' && preg_match('/^-?\d+$/', $arg))) {
- return ofNumber((int)$arg);
- }
- return ofString($arg);
+ return ctype_digit($arg) ? ofNumber((int)$arg) : ofString($arg);
}
function cmdInspect(string $bundlePath): void
{
- // Minimal inspection: just read the bundle as bytes and print its size.
- // Full parsing is done by the kernel, not the host shell.
- if (!file_exists($bundlePath)) {
- fwrite(STDERR, "Error: bundle file not found: $bundlePath\n");
- exit(1);
- }
- $bundleBytes = file_get_contents($bundlePath);
- if ($bundleBytes === false) {
- fwrite(STDERR, "Error: could not read bundle file: $bundlePath\n");
- exit(1);
- }
-
+ $bundleBytes = readBundle($bundlePath);
$bytes = strlen($bundleBytes);
echo "Bundle: $bundlePath\n";
echo "Size: $bytes bytes\n";
@@ -179,61 +137,6 @@ function cmdInspect(string $bundlePath): void
}
}
-function cmdRepl(): void
-{
- echo "Arboricx PHP REPL\n";
- echo "Commands:\n";
- echo " run <bundle> [args...] — Run a bundle\n";
- echo " inspect <bundle> — Inspect a bundle\n";
- echo " exit — Exit\n";
- echo "\n";
-
- $kernel = getRunArboricxToString();
- if ($kernel === null) {
- fwrite(STDERR, "Warning: kernel not configured\n");
- }
-
- while (true) {
- $prompt = "arboricx> ";
- fwrite(STDOUT, $prompt);
- $line = trim(fgets(STDIN));
- if ($line === '' || $line === 'exit' || $line === 'quit') {
- break;
- }
-
- $parts = preg_split('/\s+/', $line, 2);
- if (!$parts) {
- continue;
- }
-
- $cmd = $parts[0];
- switch ($cmd) {
- case 'run':
- if (!isset($parts[1])) {
- fwrite(STDERR, "Usage: run <bundle> [args...]\n");
- break;
- }
- $cmdRun($parts[1], array_slice($parts, 2));
- break;
- case 'inspect':
- if (!isset($parts[1])) {
- fwrite(STDERR, "Usage: inspect <bundle>\n");
- break;
- }
- cmdInspect($parts[1]);
- break;
- case 'help':
- echo "Commands:\n";
- echo " run <bundle> [args...] — Run a bundle\n";
- echo " inspect <bundle> — Inspect a bundle\n";
- echo " exit — Exit\n";
- break;
- default:
- fwrite(STDERR, "Unknown command: $cmd\n");
- }
- }
-}
-
// ── Main ─────────────────────────────────────────────────────────────────────
$argv = $_SERVER['argv'] ?? [];
@@ -244,7 +147,6 @@ if ($argc < 2) {
echo "\nUsage:\n";
echo " php run.php run <bundle.arboricx> [args...]\n";
echo " php run.php inspect <bundle.arboricx>\n";
- echo " php run.php repl\n";
exit(0);
}
@@ -264,11 +166,8 @@ switch ($command) {
}
cmdInspect($argv[2]);
break;
- case 'repl':
- cmdRepl();
- break;
default:
echo "Unknown command: $command\n";
- echo "Usage: php run.php run|inspect|repl ...\n";
+ echo "Usage: php run.php run|inspect ...\n";
exit(1);
}
diff --git a/ext/php/src/codecs.php b/ext/php/src/codecs.php
@@ -54,6 +54,9 @@ function toList(int $tree): array
return [true, $rest];
}
+/**
+ * Strings are lists of byte values (each character encoded as a number tree).
+ */
function ofString(string $s): int
{
$bytes = [];
@@ -104,6 +107,12 @@ function unwrapResult(int $tree): array
return $isTrue ? ['ok', $value, $rest] : ['err', $value, $rest];
}
+/**
+ * Host ABI tag constants.
+ *
+ * The kernel wraps successful results in a host value envelope:
+ * Fork(tag_number, payload_tree)
+ */
const HOST_TREE_TAG = 0;
const HOST_STRING_TAG = 1;
const HOST_NUMBER_TAG = 2;
@@ -129,26 +138,6 @@ function isBool(int $tree): bool
return isLeaf($tree) || (isStem($tree) && isLeaf(stemChild($tree)));
}
-function isNumber(int $tree): bool
-{
- [$ok, $_] = toNumber($tree);
- return $ok;
-}
-
-function isList(int $tree): bool
-{
- [$ok, $_] = toList($tree);
- return $ok;
-}
-
-function isString(int $tree): bool
-{
- [$ok, $elements] = toList($tree);
- if (!$ok) return false;
- foreach ($elements as $elem) if (!isNumber($elem)) return false;
- return true;
-}
-
function decodeHostPayload(int $tag, int $payload): array
{
switch ($tag) {
@@ -178,26 +167,4 @@ function decodeHostPayload(int $tag, int $payload): array
}
}
-function decodeResult(int $tree): string
-{
- $tree = reduce($tree);
- if (isLeaf($tree)) return 't';
-
- [$ok, $str] = toString($tree);
- if ($ok && $str !== '') {
- $valid = true;
- for ($i = 0; $i < strlen($str); $i++) {
- $n = ord($str[$i]);
- if ($n < 32 || $n > 126) { $valid = false; break; }
- }
- if ($valid) return '"' . $str . '"';
- }
-
- [$ok, $num] = toNumber($tree);
- if ($ok) return (string)$num;
- [$ok, $xs] = toList($tree);
- if ($ok) return '[' . implode(', ', array_map(fn($x) => decodeResult($x), $xs)) . ']';
-
- return formatTree($tree);
-}
diff --git a/ext/php/src/functions.php b/ext/php/src/functions.php
@@ -5,27 +5,26 @@ declare(strict_types=1);
namespace Arboricx;
/**
- * Arena-backed Tree Calculus representation.
+ * Arena-backed Tree Calculus graph.
*
- * Node refs are ints:
- * 0 = Leaf
+ * Nodes are integers (refs) into four parallel arrays:
+ * tag 0 = Leaf
* tag 1 = Stem(child)
* tag 2 = Fork(left, right)
- * tag 3 = App(function, argument) -- evaluator-internal thunk
- * tag 4 = Ind(target) -- update-in-place indirection
+ * tag 3 = App(function, argument) -- evaluator thunk
+ * tag 4 = Ind(target) -- update-in-place indirection for sharing
+ *
+ * Using ints instead of objects keeps the hot path allocation-free
+ * and lets us do cheap pointer-equality (same ref == same subtree).
*/
-
$GLOBALS['ARB_TAG'] = [0 => 0];
$GLOBALS['ARB_A'] = [0 => 0];
$GLOBALS['ARB_B'] = [0 => 0];
$GLOBALS['ARB_NEXT'] = 1;
-$GLOBALS['ARB_CONS_CACHE'] = [];
function spendFuel(int &$fuel): void
{
- if ($fuel <= 0) {
- throw new \RuntimeException('fuel exhausted');
- }
+ if ($fuel <= 0) throw new \RuntimeException('fuel exhausted');
$fuel--;
}
@@ -38,14 +37,10 @@ function newNode(int $tag, int $a, int $b = 0): int
return $id;
}
-function updateInd(int $node, int $target): void
-{
- if ($node === 0 || $node === $target) return;
- $GLOBALS['ARB_TAG'][$node] = 4;
- $GLOBALS['ARB_A'][$node] = $target;
- $GLOBALS['ARB_B'][$node] = 0;
-}
-
+/**
+ * Chase indirection (tag 4) chains to the real node.
+ * Self-loops are treated as terminal to avoid infinite loops.
+ */
function deref(int $t): int
{
while ($t !== 0 && ($GLOBALS['ARB_TAG'][$t] ?? -1) === 4) {
@@ -56,63 +51,43 @@ function deref(int $t): int
return $t;
}
+/**
+ * Overwrite a node with an indirection to its reduced form.
+ * This short-circuits future reads so shared sub-terms are only
+ * reduced once.
+ */
+function setInd(int $o, int $t): void
+{
+ if ($o !== $t) {
+ $GLOBALS['ARB_TAG'][$o] = 4;
+ $GLOBALS['ARB_A'][$o] = $t;
+ }
+}
+
function leaf(): int { return 0; }
-function stem(int $child): int { return newNode(1, $child); }
-function fork(int $left, int $right): int { return newNode(2, $left, $right); }
-function appNode(int $f, int $x): int { return newNode(3, $f, $x); }
-function tree(): int { return 0; }
+function stem(int $c): int { return newNode(1, $c); }
+function fork(int $l, int $r): int { return newNode(2, $l, $r); }
+function app(int $f, int $x): int { return newNode(3, $f, $x); }
function tag(int $t): int { $t = deref($t); return $GLOBALS['ARB_TAG'][$t] ?? -1; }
-function argA(int $t): int { $t = deref($t); return $GLOBALS['ARB_A'][$t]; }
-function argB(int $t): int { $t = deref($t); return $GLOBALS['ARB_B'][$t]; }
-
function isLeaf(int $t): bool { return deref($t) === 0; }
function isStem(int $t): bool { return tag($t) === 1; }
function isFork(int $t): bool { return tag($t) === 2; }
function isApp(int $t): bool { return tag($t) === 3; }
-function isTree(int $t): bool { $tag = tag($t); return $tag === 0 || $tag === 1 || $tag === 2; }
-function isTerm(int $t): bool { $tag = tag($t); return $tag === 0 || $tag === 1 || $tag === 2 || $tag === 3; }
-
-function stemChild(int $t): int { return argA($t); }
-function forkLeft(int $t): int { return argA($t); }
-function forkRight(int $t): int { return argB($t); }
-function appFunc(int $t): int { return argA($t); }
-function appArg(int $t): int { return argB($t); }
-
-function app(int $f, int $x): int { return appNode($f, $x); }
-
-function newStemFast(int $child, array &$TAG, array &$A, array &$B): int
-{
- $id = $GLOBALS['ARB_NEXT']++;
- $TAG[$id] = 1;
- $A[$id] = $child;
- $B[$id] = 0;
- return $id;
-}
-
-function newForkFast(int $left, int $right, array &$TAG, array &$A, array &$B): int
-{
- $id = $GLOBALS['ARB_NEXT']++;
- $TAG[$id] = 2;
- $A[$id] = $left;
- $B[$id] = $right;
- return $id;
-}
-function newAppFast(int $f, int $x, array &$TAG, array &$A, array &$B): int
-{
- $id = $GLOBALS['ARB_NEXT']++;
- $TAG[$id] = 3;
- $A[$id] = $f;
- $B[$id] = $x;
- return $id;
-}
-
-function apply_(int $a, int $b, int $fuel = 100_000_000_000_000_000): int
-{
- return reduce(app($a, $b), $fuel);
-}
+function stemChild(int $t): int { $t = deref($t); return $GLOBALS['ARB_A'][$t]; }
+function forkLeft(int $t): int { $t = deref($t); return $GLOBALS['ARB_A'][$t]; }
+function forkRight(int $t): int { $t = deref($t); return $GLOBALS['ARB_B'][$t]; }
+function appFunc(int $t): int { $t = deref($t); return $GLOBALS['ARB_A'][$t]; }
+function appArg(int $t): int { $t = deref($t); return $GLOBALS['ARB_B'][$t]; }
+/**
+ * Reduce a term to weak-head normal form (WHNF).
+ *
+ * The evaluator is iterative (no PHP stack growth) and reduces
+ * left-to-right: function first, then argument when needed.
+ * Fuel prevents infinite loops on divergent terms.
+ */
function reduce(int $term, int $fuel = 100_000_000_000_000_000): int
{
return whnf($term, $fuel);
@@ -125,122 +100,71 @@ function whnf(int $term, int &$fuel): int
$B =& $GLOBALS['ARB_B'];
while (true) {
- // deref term
- while ($term !== 0 && $TAG[$term] === 4) {
- $term = $A[$term];
- }
-
- if ($TAG[$term] !== 3) {
- return $term;
- }
+ $term = deref($term);
+ if ($TAG[$term] !== 3) return $term;
$orig = $term;
$f = whnf($A[$term], $fuel);
$x = $B[$term];
- // deref function result
- while ($f !== 0 && $TAG[$f] === 4) {
- $f = $A[$f];
- }
+ $f = deref($f);
$ftag = $TAG[$f];
// apply Leaf b = Stem b
if ($f === 0) {
- $result = newStemFast($x, $TAG, $A, $B);
- if ($orig !== $result) { $TAG[$orig] = 4; $A[$orig] = $result; $B[$orig] = 0; }
- return $result;
+ $r = newNode(1, $x); setInd($orig, $r); return $r;
}
// apply (Stem a) b = Fork a b
if ($ftag === 1) {
- $result = newForkFast($A[$f], $x, $TAG, $A, $B);
- if ($orig !== $result) { $TAG[$orig] = 4; $A[$orig] = $result; $B[$orig] = 0; }
- return $result;
+ $r = newNode(2, $A[$f], $x); setInd($orig, $r); return $r;
}
- if ($ftag !== 2) {
- throw new \RuntimeException('apply: function did not reduce to tree');
- }
+ if ($ftag !== 2) throw new \RuntimeException('apply: function did not reduce to tree');
$left = whnf($A[$f], $fuel);
$right = $B[$f];
-
- // deref left
- while ($left !== 0 && $TAG[$left] === 4) {
- $left = $A[$left];
- }
+ $left = deref($left);
$ltag = $TAG[$left];
// apply (Fork Leaf a) _ = a
if ($left === 0) {
- $term = $right;
- if ($orig !== $term) { $TAG[$orig] = 4; $A[$orig] = $term; $B[$orig] = 0; }
- spendFuel($fuel);
- continue;
+ $term = $right; setInd($orig, $term); spendFuel($fuel); continue;
}
// apply (Fork (Stem a) b) c = (a c) (b c)
if ($ltag === 1) {
- $term = newAppFast(
- newAppFast($A[$left], $x, $TAG, $A, $B),
- newAppFast($right, $x, $TAG, $A, $B),
- $TAG, $A, $B
- );
- if ($orig !== $term) { $TAG[$orig] = 4; $A[$orig] = $term; $B[$orig] = 0; }
- spendFuel($fuel);
- continue;
+ $term = newNode(3, newNode(3, $A[$left], $x), newNode(3, $right, $x));
+ setInd($orig, $term); spendFuel($fuel); continue;
}
- if ($ltag !== 2) {
- throw new \RuntimeException('apply: invalid Fork left child');
- }
+ if ($ltag !== 2) throw new \RuntimeException('apply: invalid Fork left child');
$arg = whnf($x, $fuel);
- while ($arg !== 0 && $TAG[$arg] === 4) {
- $arg = $A[$arg];
- }
+ $arg = deref($arg);
$atag = $TAG[$arg];
// apply (Fork (Fork a b) c) Leaf = a
if ($arg === 0) {
- $term = $A[$left];
- if ($orig !== $term) { $TAG[$orig] = 4; $A[$orig] = $term; $B[$orig] = 0; }
- spendFuel($fuel);
- continue;
+ $term = $A[$left]; setInd($orig, $term); spendFuel($fuel); continue;
}
// apply (Fork (Fork a b) c) (Stem u) = b u
if ($atag === 1) {
- $term = newAppFast($B[$left], $A[$arg], $TAG, $A, $B);
- if ($orig !== $term) { $TAG[$orig] = 4; $A[$orig] = $term; $B[$orig] = 0; }
- spendFuel($fuel);
- continue;
+ $term = newNode(3, $B[$left], $A[$arg]);
+ setInd($orig, $term); spendFuel($fuel); continue;
}
// apply (Fork (Fork a b) c) (Fork u v) = (c u) v
if ($atag === 2) {
- $term = newAppFast(
- newAppFast($right, $A[$arg], $TAG, $A, $B),
- $B[$arg],
- $TAG, $A, $B
- );
- if ($orig !== $term) { $TAG[$orig] = 4; $A[$orig] = $term; $B[$orig] = 0; }
- spendFuel($fuel);
- continue;
+ $term = newNode(3, newNode(3, $right, $A[$arg]), $B[$arg]);
+ setInd($orig, $term); spendFuel($fuel); continue;
}
throw new \RuntimeException('apply: argument did not reduce to tree');
}
}
-function normalize(int $term, int &$fuel): int
-{
- $term = whnf($term, $fuel);
- if (isStem($term)) return stem(normalize(stemChild($term), $fuel));
- if (isFork($term)) return fork(normalize(forkLeft($term), $fuel), normalize(forkRight($term), $fuel));
- return $term;
-}
-
function same_tree(int $a, int $b): bool
{
$a = deref($a); $b = deref($b);
@@ -253,13 +177,6 @@ function same_tree(int $a, int $b): bool
return false;
}
-function typeTag(int $t): string
-{
- return match (tag($t)) {
- 0 => 'leaf', 1 => 'stem', 2 => 'fork', 3 => 'app', 4 => 'ind', default => 'unknown'
- };
-}
-
function formatTree(int $t, int $depth = 0): string
{
$t = deref($t);
diff --git a/ext/php/src/kernel.php b/ext/php/src/kernel.php
@@ -5,160 +5,49 @@ declare(strict_types=1);
namespace Arboricx;
/**
- * kernel.php — Hardcoded tricu kernel entrypoints.
+ * Kernel loader.
*
- * The kernel is the self-hosted Arboricx runtime written in Tree Calculus,
- * compiled to a raw Tree Calculus term. We hardcode it as a **ternary string**
- * and parse it on demand.
+ * The kernel is the self-hosted Arboricx runtime compiled to a raw Tree
+ * Calculus term. It is shipped as a ternary string and parsed on demand.
*
- * Ternary string format (from Research.hs toTernaryString):
- * '0' → Leaf
- * '1' ++ rest → Stem(parse(rest))
- * '2' ++ rest → Fork(parse(rest), parse(rest))
- *
- * The Kernel class holds these constants and exposes them as Tree values.
+ * Ternary string format (matches Research.hs toTernaryString):
+ * '0' → Leaf
+ * '1' rest → Stem(parse(rest))
+ * '2' l r → Fork(parse(l), parse(r))
*/
-use function Arboricx\{leaf, stem, fork, isLeaf, isStem, isFork};
-
-// ── Kernel constants ────────────────────────────────────────────────────────
+use function Arboricx\{leaf, stem, fork};
-// ── File loading ────────────────────────────────────────────────────────────
-
-/**
- * Load a kernel ternary string from a .ternary file.
- *
- * Convention: ext/php/src/kernel_<name>.ternary
- *
- * @param string $name e.g. 'run_arboricx_to_string'
- * @return string The ternary string, or '' if file not found
- */
function loadKernelTernary(string $name): string
{
- // Determine the base directory: go up from this file to ext/php/src/
- $dir = dirname(__FILE__);
- $file = $dir . '/' . $name . '.ternary';
- if (file_exists($file)) {
- $content = file_get_contents($file);
- if ($content !== false) {
- return trim($content);
- }
- }
- return '';
+ $file = __DIR__ . '/' . $name . '.ternary';
+ if (!file_exists($file)) return '';
+ $content = file_get_contents($file);
+ return $content !== false ? trim($content) : '';
}
-// ── Kernel constants (fallbacks — actual values loaded from .ternary files) ─
-
-/**
- * runArboricxToString — the primary Host ABI entrypoint.
- *
- * This is the compiled Tree Calculus term for:
- *
- * runArboricxToString = (bs args :
- * bindResult (runArboricxArgsByName [] bs args)
- * (value rest :
- * wrapHostValue hostString? hostString value rest))
- *
- * Loaded from ext/php/src/kernel_run_arboricx_to_string.ternary
- * if it exists, otherwise falls back to the constant below.
- *
- * @var string
- */
-const KERNEL_RUN_ARBORICX_TO_STRING = '';
-
-/**
- * runArboricxByNameToString — named-export variant.
- *
- * runArboricxByNameToString = (nameBytes bs args : ...)
- *
- * Loaded from ext/php/src/kernel_run_arboricx_by_name_to_string.ternary
- * if it exists, otherwise falls back to the constant below.
- *
- * @var string
- */
-const KERNEL_RUN_ARBORICX_BY_NAME_TO_STRING = '';
-
-// ── Parser ──────────────────────────────────────────────────────────────────
-
-/**
- * Parse a ternary string into a Tree Calculus value.
- *
- * Grammar:
- * term → '0' → Leaf
- * → '1' term → Stem(term)
- * → '2' term term → Fork(term, term)
- *
- * @param string $s The ternary string to parse
- * @param int &$pos Current position (passed by reference)
- * @return int The parsed Tree value node id
- */
function parseTernary(string $s, int &$pos = 0): int
{
if ($pos >= strlen($s)) {
throw new \RuntimeException('parseTernary: unexpected end of string');
}
-
- $char = $s[$pos];
- $pos++;
-
+ $char = $s[$pos++];
return match ($char) {
'0' => leaf(),
'1' => stem(parseTernary($s, $pos)),
'2' => fork(parseTernary($s, $pos), parseTernary($s, $pos)),
- default => throw new \RuntimeException(
- "parseTernary: unexpected char '$char' at position $pos"
- ),
+ default => throw new \RuntimeException("parseTernary: unexpected char '$char' at position $pos"),
};
}
-// ── Kernel accessors ────────────────────────────────────────────────────────
-
-/**
- * Get the runArboricxToString kernel tree.
- *
- * Tries loading from ext/php/src/kernel_run_arboricx_to_string.ternary first,
- * then falls back to the KERNEL_RUN_ARBORICX_TO_STRING constant.
- *
- * Returns the parsed Tree Calculus value, or null if not configured.
- */
function getRunArboricxToString(): ?int
{
- // Try file first
$term = loadKernelTernary('kernel_run_arboricx_to_string');
- if ($term === '') {
- $term = KERNEL_RUN_ARBORICX_TO_STRING;
- }
- if ($term === '') {
- return null;
- }
- $pos = 0;
- $tree = parseTernary($term, $pos);
- if ($pos !== strlen($term)) {
- throw new \RuntimeException('kernel ternary has trailing data: parsed ' . $pos . ' of ' . strlen($term) . ' bytes');
- }
- return $tree;
-}
-
-/**
- * Get the runArboricxByNameToString kernel tree.
- *
- * Tries loading from ext/php/src/kernel_run_arboricx_by_name_to_string.ternary
- * first, then falls back to the KERNEL_RUN_ARBORICX_BY_NAME_TO_STRING constant.
- */
-function getRunArboricxByNameToString(): ?int
-{
- // Try file first
- $term = loadKernelTernary('kernel_run_arboricx_by_name_to_string');
- if ($term === '') {
- $term = KERNEL_RUN_ARBORICX_BY_NAME_TO_STRING;
- }
- if ($term === '') {
- return null;
- }
+ if ($term === '') return null;
$pos = 0;
$tree = parseTernary($term, $pos);
if ($pos !== strlen($term)) {
- throw new \RuntimeException('kernel ternary has trailing data: parsed ' . $pos . ' of ' . strlen($term) . ' bytes');
+ throw new \RuntimeException("kernel ternary has trailing data: parsed $pos of " . strlen($term) . ' bytes');
}
return $tree;
}