Host ABI definition and ergonomics in TC
This commit is contained in:
@@ -4,6 +4,8 @@ This document describes how to build a minimal host-language shell that can exec
|
||||
|
||||
The intended reader is an implementation agent building a first prototype in a host language such as PHP. The same approach should generalize to any language with a small Tree Calculus evaluator.
|
||||
|
||||
See also: [`docs/host-abi.md`](./host-abi.md) for the precise host-facing ABI value tags and typed runner contract.
|
||||
|
||||
## Goal
|
||||
|
||||
Build a tiny host program that can:
|
||||
@@ -14,7 +16,8 @@ Build a tiny host program that can:
|
||||
4. Read an application `.arboricx` bundle from disk.
|
||||
5. Convert host inputs into canonical Tree Calculus values.
|
||||
6. Apply the kernel to the application bundle and arguments.
|
||||
7. Decode the result back into host values.
|
||||
7. Unwrap a standardized host ABI result.
|
||||
8. Decode the host ABI payload back into host values.
|
||||
|
||||
A concrete target example:
|
||||
|
||||
@@ -29,13 +32,19 @@ The host should be able to call that bundle with the host string `"james"` and r
|
||||
hello james
|
||||
```
|
||||
|
||||
Conceptually the host evaluates:
|
||||
With the Host ABI layer, the preferred conceptual call is:
|
||||
|
||||
```tricu
|
||||
runArboricxArgs <applicationBundleBytes> ["james"]
|
||||
runArboricxToString <applicationBundleBytes> ["james"]
|
||||
```
|
||||
|
||||
where `runArboricxArgs` comes from the self-hosted Arboricx runtime kernel.
|
||||
This returns:
|
||||
|
||||
```tricu
|
||||
ok (hostString "hello james") rest
|
||||
```
|
||||
|
||||
where `runArboricxToString` comes from the self-hosted Arboricx runtime kernel.
|
||||
|
||||
## Architectural overview
|
||||
|
||||
@@ -43,7 +52,7 @@ There are two Arboricx bundles involved:
|
||||
|
||||
1. **Kernel bundle**
|
||||
- Contains the self-hosted Arboricx parser/executor written in tricu.
|
||||
- Exposes ergonomic runtime entrypoints such as `runArboricxArgs`.
|
||||
- Exposes ergonomic runtime entrypoints such as `runArboricxArgs` and Host ABI entrypoints such as `runArboricxToString`.
|
||||
- This can be hardcoded as a Tree Calculus value in the host, or loaded by a minimal host-side Arboricx parser.
|
||||
|
||||
2. **Application bundle**
|
||||
@@ -149,7 +158,9 @@ This logic is exactly what the tricu self-hosted kernel does, so the hardcoded-k
|
||||
|
||||
The ergonomic runtime API currently lives in `lib/arboricx.tri`.
|
||||
|
||||
Primary entrypoints:
|
||||
### Raw execution entrypoints
|
||||
|
||||
These return raw application results inside the existing `ok` / `err` result protocol:
|
||||
|
||||
```tricu
|
||||
readArboricxExecutableByName nameBytes bundleBytes
|
||||
@@ -160,52 +171,70 @@ runArboricxArgsByName nameBytes bundleBytes args
|
||||
runArboricxArgs bundleBytes args
|
||||
```
|
||||
|
||||
Recommended host entrypoint:
|
||||
|
||||
```tricu
|
||||
runArboricxArgs
|
||||
```
|
||||
|
||||
It accepts:
|
||||
`runArboricxArgs` accepts:
|
||||
|
||||
1. Raw application bundle bytes as a Tree Calculus byte list.
|
||||
2. A Tree Calculus list of arguments.
|
||||
|
||||
It returns a result-wrapped value.
|
||||
|
||||
For named exports, use:
|
||||
|
||||
```tricu
|
||||
runArboricxArgsByName
|
||||
```
|
||||
|
||||
It accepts:
|
||||
For named exports, use `runArboricxArgsByName`, which accepts:
|
||||
|
||||
1. Export name as bytes.
|
||||
2. Application bundle bytes as bytes.
|
||||
3. Argument list.
|
||||
|
||||
### Applying the kernel in the host evaluator
|
||||
### Host ABI typed entrypoints
|
||||
|
||||
If the host has the Tree Calculus value for `runArboricxArgs`, call it by constructing nested application trees.
|
||||
For host-language ports, prefer the Host ABI typed runners. These wrap successful outputs in a tagged host ABI value so every host can decode the same envelope shape.
|
||||
|
||||
Default export variants:
|
||||
|
||||
```tricu
|
||||
runArboricxToTree bundleBytes args
|
||||
runArboricxToString bundleBytes args
|
||||
runArboricxToNumber bundleBytes args
|
||||
runArboricxToBool bundleBytes args
|
||||
runArboricxToList bundleBytes args
|
||||
runArboricxToBytes bundleBytes args
|
||||
```
|
||||
|
||||
Named export variants:
|
||||
|
||||
```tricu
|
||||
runArboricxByNameToTree nameBytes bundleBytes args
|
||||
runArboricxByNameToString nameBytes bundleBytes args
|
||||
runArboricxByNameToNumber nameBytes bundleBytes args
|
||||
runArboricxByNameToBool nameBytes bundleBytes args
|
||||
runArboricxByNameToList nameBytes bundleBytes args
|
||||
runArboricxByNameToBytes nameBytes bundleBytes args
|
||||
```
|
||||
|
||||
Recommended first host entrypoint for the `append "hello "` example:
|
||||
|
||||
```tricu
|
||||
runArboricxToString
|
||||
```
|
||||
|
||||
## Applying the kernel in the host evaluator
|
||||
|
||||
If the host has the Tree Calculus value for `runArboricxToString`, call it by constructing nested application trees.
|
||||
|
||||
In Tree Calculus application form:
|
||||
|
||||
```text
|
||||
((runArboricxArgs bundleBytesTree) argsTree)
|
||||
((runArboricxToString bundleBytesTree) argsTree)
|
||||
```
|
||||
|
||||
Structurally, if `app(f, x)` constructs `Fork(f, x)`, then:
|
||||
|
||||
```php
|
||||
$expr = app(app($kernelRunArboricxArgs, $bundleBytesTree), $argsTree);
|
||||
$expr = app(app($kernelRunArboricxToString, $bundleBytesTree), $argsTree);
|
||||
$result = normalize($expr);
|
||||
```
|
||||
|
||||
For named export execution:
|
||||
|
||||
```text
|
||||
(((runArboricxArgsByName nameBytesTree) bundleBytesTree) argsTree)
|
||||
(((runArboricxByNameToString nameBytesTree) bundleBytesTree) argsTree)
|
||||
```
|
||||
|
||||
Structurally:
|
||||
@@ -213,7 +242,7 @@ Structurally:
|
||||
```php
|
||||
$expr = app(
|
||||
app(
|
||||
app($kernelRunArboricxArgsByName, $nameBytesTree),
|
||||
app($kernelRunArboricxByNameToString, $nameBytesTree),
|
||||
$bundleBytesTree
|
||||
),
|
||||
$argsTree
|
||||
@@ -221,24 +250,73 @@ $expr = app(
|
||||
$result = normalize($expr);
|
||||
```
|
||||
|
||||
## Result convention
|
||||
## Result convention and Host ABI envelope
|
||||
|
||||
The runtime API returns results using the tricu `ok` / `err` convention from `lib/binary.tri`:
|
||||
All runtime APIs return the existing tricu `ok` / `err` convention from `lib/binary.tri`:
|
||||
|
||||
```tricu
|
||||
ok value rest = pair true (pair value rest)
|
||||
err code rest = pair false (pair code rest)
|
||||
```
|
||||
|
||||
The host should unwrap this result before decoding the final value.
|
||||
The host should always unwrap this outer result first.
|
||||
|
||||
Expected success shape:
|
||||
### Raw runners
|
||||
|
||||
Raw runners such as `runArboricxArgs` return:
|
||||
|
||||
```tricu
|
||||
ok value rest
|
||||
ok rawApplicationValue rest
|
||||
```
|
||||
|
||||
For typical execution, `value` is the application result. `rest` is usually not important to the host shell unless debugging parser behavior.
|
||||
The host must know how to interpret `rawApplicationValue`.
|
||||
|
||||
### Host ABI typed runners
|
||||
|
||||
Typed runners such as `runArboricxToString` return:
|
||||
|
||||
```tricu
|
||||
ok hostAbiValue rest
|
||||
```
|
||||
|
||||
A host ABI value has shape:
|
||||
|
||||
```tricu
|
||||
pair tag payload
|
||||
```
|
||||
|
||||
The payload is still the canonical/raw Tree Calculus representation for that type.
|
||||
|
||||
Initial tags are specified in [`docs/host-abi.md`](./host-abi.md):
|
||||
|
||||
```tricu
|
||||
hostTreeTag = 0
|
||||
hostStringTag = 1
|
||||
hostNumberTag = 2
|
||||
hostBoolTag = 3
|
||||
hostListTag = 4
|
||||
hostBytesTag = 5
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```tricu
|
||||
runArboricxToString bundleBytes ["james"]
|
||||
```
|
||||
|
||||
returns:
|
||||
|
||||
```tricu
|
||||
ok (hostString "hello james") rest
|
||||
```
|
||||
|
||||
which is structurally:
|
||||
|
||||
```tricu
|
||||
ok (pair hostStringTag "hello james") rest
|
||||
```
|
||||
|
||||
### Error shape
|
||||
|
||||
Expected error shape:
|
||||
|
||||
@@ -250,8 +328,11 @@ The error code is a Tree Calculus number. Error constants are defined in:
|
||||
|
||||
- `lib/binary.tri`
|
||||
- `lib/arboricx-common.tri`
|
||||
- `lib/arboricx.tri` for Host ABI codec errors, currently `errHostCodecFailed = 14`
|
||||
|
||||
A prototype host can simply report the numeric error code and optionally dump a compact representation of `rest`.
|
||||
Typed runners return `errHostCodecFailed` if the application result cannot be interpreted as the requested type.
|
||||
|
||||
A prototype host can report the numeric error code and optionally dump a compact representation of `rest`.
|
||||
|
||||
## Example execution flow
|
||||
|
||||
@@ -268,7 +349,7 @@ Host flow:
|
||||
1. Load kernel entrypoint tree:
|
||||
|
||||
```php
|
||||
$runArboricxArgs = loadHardcodedKernelEntrypoint('runArboricxArgs');
|
||||
$runArboricxToString = loadHardcodedKernelEntrypoint('runArboricxToString');
|
||||
```
|
||||
|
||||
2. Read application bundle bytes:
|
||||
@@ -293,7 +374,7 @@ Host flow:
|
||||
5. Build application expression:
|
||||
|
||||
```php
|
||||
$expr = app(app($runArboricxArgs, $bundleBytesTree), $args);
|
||||
$expr = app(app($runArboricxToString, $bundleBytesTree), $args);
|
||||
```
|
||||
|
||||
6. Evaluate:
|
||||
@@ -305,19 +386,26 @@ Host flow:
|
||||
7. Unwrap `ok` result:
|
||||
|
||||
```php
|
||||
[$ok, $value, $rest] = unwrapResult($result);
|
||||
[$ok, $hostValue, $rest] = unwrapResult($result);
|
||||
if (!$ok) { throw new RuntimeException('Arboricx error'); }
|
||||
```
|
||||
|
||||
8. Decode the value:
|
||||
8. Unwrap Host ABI envelope:
|
||||
|
||||
```php
|
||||
echo decodeString($value); // hello james
|
||||
[$tag, $payload] = unwrapHostValue($hostValue);
|
||||
if ($tag !== HOST_STRING_TAG) { throw new RuntimeException('Expected string'); }
|
||||
```
|
||||
|
||||
9. Decode the payload:
|
||||
|
||||
```php
|
||||
echo decodeString($payload); // hello james
|
||||
```
|
||||
|
||||
## What the kernel does internally
|
||||
|
||||
`runArboricxArgs` performs the following steps inside Tree Calculus:
|
||||
`runArboricxToString` performs the following steps inside Tree Calculus:
|
||||
|
||||
1. Parse and validate the raw Arboricx bundle bytes.
|
||||
2. Parse the manifest.
|
||||
@@ -328,9 +416,12 @@ Host flow:
|
||||
4. Read the nodes section.
|
||||
5. Reconstruct the selected root tree from the Merkle DAG.
|
||||
6. Apply each host-provided argument in order.
|
||||
7. Return `ok result rest` or an `err`.
|
||||
7. Validate that the raw result is string-like.
|
||||
8. Return `ok (hostString result) rest`, or an `err`.
|
||||
|
||||
`runArboricxArgsByName` is identical except that it selects a named export.
|
||||
`runArboricxByNameToString` is identical except that it selects a named export.
|
||||
|
||||
Other typed runners follow the same pattern for their requested output type.
|
||||
|
||||
## Tests proving the expected behavior
|
||||
|
||||
@@ -342,12 +433,18 @@ Important cases:
|
||||
- `readArboricxExecutableByName: selects named export`
|
||||
- `runArboricx: applies host-provided argument to default export`
|
||||
- `runArboricxArgs: applies host-provided argument list in order`
|
||||
- `host ABI: constructors expose tag and payload`
|
||||
- `runArboricxToTree: wraps raw result as hostTree`
|
||||
- `runArboricxToString: wraps string result as hostString`
|
||||
- `runArboricxToNumber: wraps number result as hostNumber`
|
||||
- `runArboricxToBool: rejects non-bool result`
|
||||
|
||||
These tests demonstrate the host-shell contract:
|
||||
|
||||
- application bundle bytes are supplied as a Tree Calculus byte list,
|
||||
- host arguments are supplied as canonical Tree Calculus values,
|
||||
- execution returns a result-wrapped Tree Calculus value.
|
||||
- execution returns an outer result-wrapped value,
|
||||
- Host ABI typed runners return a tagged ABI envelope inside `ok`.
|
||||
|
||||
## Minimal PHP prototype checklist
|
||||
|
||||
@@ -357,13 +454,14 @@ A PHP prototype should implement:
|
||||
- [ ] Application helper: `app($f, $x) = Fork($f, $x)`.
|
||||
- [ ] Normal-order Tree Calculus reducer.
|
||||
- [ ] Fuel/step limit for debugging.
|
||||
- [ ] Hardcoded kernel entrypoint tree for `runArboricxArgs`.
|
||||
- [ ] Hardcoded kernel entrypoint tree for `runArboricxToString` for the first string-output prototype.
|
||||
- [ ] Encode application bundle file bytes into a Tree Calculus byte list.
|
||||
- [ ] Encode host argument values into Tree Calculus values.
|
||||
- [ ] Build expression: `((runArboricxArgs bundleBytes) args)`.
|
||||
- [ ] Build expression: `((runArboricxToString bundleBytes) args)`.
|
||||
- [ ] Normalize expression.
|
||||
- [ ] Unwrap `ok` / `err` result.
|
||||
- [ ] Decode result value into host type.
|
||||
- [ ] Unwrap outer `ok` / `err` result.
|
||||
- [ ] Unwrap Host ABI `pair tag payload` envelope.
|
||||
- [ ] Decode payload according to tag.
|
||||
|
||||
For exact codec details, reference the Haskell implementation in `src/Research.hs` and the existing JS runtime if available.
|
||||
|
||||
@@ -371,14 +469,15 @@ For exact codec details, reference the Haskell implementation in `src/Research.h
|
||||
|
||||
For the first PHP implementation:
|
||||
|
||||
1. Hardcode only the `runArboricxArgs` kernel entrypoint as a Tree Calculus value.
|
||||
1. Hardcode only the `runArboricxToString` kernel entrypoint as a Tree Calculus value.
|
||||
2. Do not implement host-side Arboricx parsing yet.
|
||||
3. Implement only enough codecs for:
|
||||
- bytes,
|
||||
- strings,
|
||||
- lists,
|
||||
- result unwrapping.
|
||||
- result unwrapping,
|
||||
- Host ABI envelope unwrapping.
|
||||
4. Use one test fixture: an Arboricx bundle whose root is `append "hello "`.
|
||||
5. Assert that calling it with `"james"` returns `"hello james"`.
|
||||
5. Assert that calling it with `"james"` returns an outer `ok`, then a `hostString`, then payload `"hello james"`.
|
||||
|
||||
Once that works, add named export support via `runArboricxArgsByName` and expand codecs as needed.
|
||||
Once that works, add named export support via `runArboricxByNameToString` and expand Host ABI tags/codecs as needed.
|
||||
|
||||
Reference in New Issue
Block a user