#!/usr/bin/env php [arg ...] * php run.php inspect * php run.php repl * * The "run" command: * 1. Reads the .arboricx bundle as raw bytes * 2. Encodes bundle bytes as a Tree Calculus byte list * 3. Encodes each host argument (string or number) * 4. Calls runArboricxToString via the hardcoded kernel * 5. Unwraps ok/err, then Host ABI envelope * 6. Decodes the string payload * * This is a minimal host shell — it does NOT parse Arboricx bundles itself. * The kernel handles bundle parsing internally. */ 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, getRunArboricxToString}; // ── Commands ───────────────────────────────────────────────────────────────── function debugTime(string $label): void { static $start = null; static $last = null; $now = microtime(true); if ($start === null) { $start = $now; $last = $now; } fwrite(STDERR, sprintf("[%.3fs +%.3fs] %s\n", $now - $start, $now - $last, $label)); $last = $now; } 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); } $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"); exit(1); } $bundleTree = ofBytes($bundleBytes); debugTime('encoded bundle bytes'); $argTrees = []; foreach ($args as $arg) { $argTrees[] = encodeArg($arg); } $argsTree = ofList($argTrees); debugTime('encoded args'); $expr = app(app($kernel, $bundleTree), $argsTree); debugTime('built expression'); fwrite(STDERR, "Reducing kernel application...\n"); $result = reduce($expr, 1_000_000); debugTime('reduced kernel application'); [$kind, $value, $rest] = unwrapResult($result); debugTime('unwrapped result'); if ($kind === 'error') { fwrite(STDERR, "Error detail: $rest\n"); exit(1); } if ($kind === 'err') { [$ok2, $code] = Arboricx\toNumber($value); $codeStr = $ok2 ? (string)$code : formatTree($value); fwrite(STDERR, "Arboricx error code: $codeStr\n"); fwrite(STDERR, "Rest: " . formatTree($rest) . "\n"); exit(1); } [$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"); fwrite(STDERR, "Raw tag: $tag, payload: " . formatTree($payload) . "\n"); exit(1); } } /** * 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); } 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); } $bytes = strlen($bundleBytes); echo "Bundle: $bundlePath\n"; echo "Size: $bytes bytes\n"; // Run with no arguments to see the default export $kernel = getRunArboricxToString(); if ($kernel === null) { fwrite(STDERR, "Warning: kernel not configured, skipping execution\n"); return; } $bundleTree = ofBytes($bundleBytes); $emptyArgs = ofList([]); $result = reduce(app(app($kernel, $bundleTree), $emptyArgs), 10_000); [$kind, $value, $rest] = unwrapResult($result); if ($kind === 'ok') { echo "\nResult (ok):\n"; try { [$tag, $payload] = unwrapHostValue($value); $decoded = decodeHostPayload($tag, $payload); echo " Tag: $tag (type: " . $decoded['type'] . ")\n"; echo " Value: " . $decoded['value'] . "\n"; } catch (\Throwable $e) { echo " Raw: " . formatTree($value) . "\n"; } } else { echo "\nResult (err):\n"; [$ok, $code] = Arboricx\toNumber($value); echo " Code: " . ($ok ? (string)$code : formatTree($value)) . "\n"; } } function cmdRepl(): void { echo "Arboricx PHP REPL\n"; echo "Commands:\n"; echo " run [args...] — Run a bundle\n"; echo " inspect — 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 [args...]\n"); break; } $cmdRun($parts[1], array_slice($parts, 2)); break; case 'inspect': if (!isset($parts[1])) { fwrite(STDERR, "Usage: inspect \n"); break; } cmdInspect($parts[1]); break; case 'help': echo "Commands:\n"; echo " run [args...] — Run a bundle\n"; echo " inspect — Inspect a bundle\n"; echo " exit — Exit\n"; break; default: fwrite(STDERR, "Unknown command: $cmd\n"); } } } // ── Main ───────────────────────────────────────────────────────────────────── $argv = $_SERVER['argv'] ?? []; $argc = $_SERVER['argc'] ?? 0; if ($argc < 2) { echo "Arboricx PHP Host Shell\n"; echo "\nUsage:\n"; echo " php run.php run [args...]\n"; echo " php run.php inspect \n"; echo " php run.php repl\n"; exit(0); } $command = $argv[1]; switch ($command) { case 'run': if ($argc < 3) { fwrite(STDERR, "Usage: php run.php run [args...]\n"); exit(1); } cmdRun($argv[2], array_slice($argv, 3)); break; case 'inspect': if ($argc < 3) { fwrite(STDERR, "Usage: php run.php inspect \n"); exit(1); } cmdInspect($argv[2]); break; case 'repl': cmdRepl(); break; default: echo "Unknown command: $command\n"; echo "Usage: php run.php run|inspect|repl ...\n"; exit(1); }