#!/usr/bin/env php [args...] * php run.php inspect */ require __DIR__ . '/src/ffi.php'; use function Arboricx\{ctx_init, ctx_free, loadBundleDefault, ofNumber, ofString, app, reduce, toString, toBool, toNumber}; // ── Locate libarboricx.so ────────────────────────────────────────────────── function findLib(): string { $env = getenv('ARBORICX_LIB'); if ($env !== false && file_exists($env)) { return $env; } $paths = [ __DIR__ . '/../../zig/zig-out/lib/libarboricx.so', '/usr/local/lib/libarboricx.so', '/usr/lib/libarboricx.so', './libarboricx.so', ]; foreach ($paths as $p) { if (file_exists($p)) { return $p; } } fwrite(STDERR, "Error: libarboricx.so not found.\nSet ARBORICX_LIB to its full path.\n"); exit(1); } // ── Decode helpers ───────────────────────────────────────────────────────── function decode(\FFI\CData $ctx, int $root): string { // Bool first: false is Leaf, which is also a valid empty string/list. try { return toBool($ctx, $root) ? 'true' : 'false'; } catch (\Throwable $e) { try { return toString($ctx, $root); } catch (\Throwable $e2) { try { return (string) toNumber($ctx, $root); } catch (\Throwable $e3) { throw new \RuntimeException('could not decode result'); } } } } function decodeType(\FFI\CData $ctx, int $root): string { try { toBool($ctx, $root); return 'bool'; } catch (\Throwable $e) { try { toString($ctx, $root); return 'string'; } catch (\Throwable $e2) { try { toNumber($ctx, $root); return 'number'; } catch (\Throwable $e3) { return 'unknown (raw tree)'; } } } } // ── Commands ───────────────────────────────────────────────────────────────── function readBundle(string $path): string { 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); } return $bytes; } function cmdRun(string $libPath, string $bundlePath, array $args): void { $ctx = ctx_init($libPath); try { $term = loadBundleDefault($ctx, readBundle($bundlePath)); foreach ($args as $arg) { $argTree = preg_match('/^\d+$/', $arg) ? ofNumber($ctx, (int)$arg) : ofString($ctx, $arg); $term = app($ctx, $term, $argTree); } $result = reduce($ctx, $term, 1_000_000_000); echo decode($ctx, $result) . "\n"; } finally { ctx_free($ctx); } } function cmdInspect(string $libPath, string $bundlePath): void { $ctx = ctx_init($libPath); try { $bundle = readBundle($bundlePath); echo "Bundle: $bundlePath\nSize: " . strlen($bundle) . " bytes\n\nResult:\n"; $term = loadBundleDefault($ctx, $bundle); $result = reduce($ctx, $term, 1_000_000_000); $type = decodeType($ctx, $result); try { $value = decode($ctx, $result); } catch (\RuntimeException $e) { $value = '(raw tree)'; } echo " Type: $type\n Value: $value\n"; } finally { ctx_free($ctx); } } // ── Main ───────────────────────────────────────────────────────────────────── $argv = $_SERVER['argv'] ?? []; $argc = $_SERVER['argc'] ?? 0; if ($argc < 2) { echo "Arboricx PHP Host Shell (via libarboricx C ABI)\n\nUsage:\n"; echo " php run.php run [args...]\n"; echo " php run.php inspect \n"; exit(0); } $libPath = findLib(); $command = $argv[1]; switch ($command) { case 'run': if ($argc < 3) { fwrite(STDERR, "Usage: php run.php run [args...]\n"); exit(1); } cmdRun($libPath, $argv[2], array_slice($argv, 3)); break; case 'inspect': if ($argc < 3) { fwrite(STDERR, "Usage: php run.php inspect \n"); exit(1); } cmdInspect($libPath, $argv[2]); break; default: fwrite(STDERR, "Unknown command: $command\nUsage: php run.php run|inspect ...\n"); exit(1); }