An embeddable Rexx engine written in Aether, consumable as a C library and an Aether binary library. RexxJS is the dialect reference; spreadsheet and sae supply integration examples. The default library has no fs/net/os/extern capability grants. Applications supply effects through explicit host requests; there is no JavaScript runtime dependency.
This is a working implementation slice, not a complete port. It has typed values, 71 builtins with limited profiles, registered application functions, labeled routines with CALL/RETURN, comparisons, IF/ELSE, SELECT and DO loops that suspend and resume across host calls.
Current references are RexxJS 8317bd1 and spreadsheet c19848d
(vendoring b3d5e4e). Strict differential fixtures match both reference
engines. The separate update audit identifies variable-led ADDRESS clauses as an implementation gap;
assigned CALL expressions also need a reference-semantics decision. Its
unsupported boundary cases are not counted as passing parity. See
the reference report for the concrete gaps.
Targets Aether 0.841.0; requires a C compiler and make. Tests require Python 3; differential tests and inventory generation also need Node.js and a RexxJS checkout. No npm install is required.
make # self-contained library and C counter host
make aether-lib # shared-runtime variant for Aether consumers
make test # std.spec, binary linkage, ctypes, host harness
make repl # console session; = expression, :help, :begin / :run
make parity REXXJS=../RexxJS # strict comparison with the selected checkout
make reference-audit # characterize standalone and vendored targets
make binary-test # both Aether binary-library variants
make containment # runtime escape probes and compiler guards
make memcheck # requires Valgrind
make inventory check-inventory # function/dependency analysisInstall and select the release with ae version install 0.841.0 and
ae version use 0.841.0, then confirm both ae and aetherc in ae version.
Alternatively, an official release extracted to
build/toolchains/aether-0.841.0 is selected automatically by Make; it sets
AETHER_HOME to keep that compiler and stdlib together. AE=/path/to/ae
overrides the executable; an extracted installation also needs its matching
AETHER_HOME=/path/to/release.
Check ae version before testing; installed and development executables can
differ. Use make -B when switching compilers to rebuild libraries and
consumers together. Make keeps its Aether cache under
build/cache.
Validation results cover Linux, both library variants and containment. Aether integration status records open transport concerns and resolved toolchain regressions. The Makefile has a macOS library/rpath branch; macOS and Windows remain untested. Static/object packaging is a follow-up.
make repl starts a Python console around the native Aether engine. Variables
persist without replaying earlier entries:
rexx> LET total = 10
rexx> = total * 2
20
rexx> :show total
10
Use :begin / :run for multiline Rexx, :reset to clear session state and
:quit to exit. The default console mounts no effect services and vetoes
application commands. repl.md describes ownership, error recovery,
cancellation and session behavior.
The optional calculator demo adds a native Aether console,
an explicitly imported Rexx CALC function, and a TinyWeb HTTPS endpoint using
pure-Aether TLS 1.3. It uses the same released compiler and stdlib as the core
engine, including EOF-aware terminal input.
The calculator script explicitly imports its API before calling it:
/* @seeks http.post https://localhost:8443/calculate */
/* @purpose Remote calculator operations */
/* @seeks http.get /static/calculator.rexx */
REQUIRE "/static/calculator.rexx"
SAY CALC('add', '0.1', '0.2')REQUIRE "/static/calculator.rexx" AS remote instead exports remote_CALC, matching
RexxJS's prefix semantics. Permission approval and module import are separate.
See importing the calculator API
for alias rules, URL resolution and separate GET/POST permissions.
The function is defined in the served Rexx module.
make calculator-test
python3 tools/test_calculator_tls.pyThe terminal runner writes build/calculator/tls-report.json and logs. Offline
specs cover protocol validation, endpoint policy, enclosing sandbox restrictions,
native EOF/session behavior and TLS keypair validation. Live TLS checks require
socket permission; this workspace cannot perform them. Deployment templates
under deploy/calculator are not installed or provisioned automatically.
For local testing without SSL, start the Aether server in one terminal:
make calculator
CALC_HTTP=1 ./build/calculator/calculator-serviceIn a second terminal, run the native REPL and the local test script:
CALC_ENDPOINT=http://localhost:8080/calculate \
CALC_PROGRAM=examples/calculator/calculator-local.rexx ./build/calculator/calculator-consoleThe native console asks separately for the exact calculator POST and module GET.
After an explicit y or yes for each of the POST and module GET requests, the script prints 0.3; then try
SAY CALC('mul', '6', '7'). No certificates,
CA file or Python are needed. Plain HTTP is restricted to localhost and
127.0.0.1; HTTPS remains required for remote endpoints.
Public GCP URL: to be announced. In this mode the TinyWeb calculator runs
continuously on a GCP VM. Your local Aether console runs the test Rexx script
and sends CALC requests to that service. The client path uses the compiled
RexxAE library and Aether TLS 1.3. It requires no Python or local server.
Once the URL is announced, replace calculator.example.com:8443 below with
its hostname and explicit port. The CA file shown is the usual Debian/Ubuntu
trust bundle; use an absolute path to the appropriate CA bundle on your OS.
make build/calculator/calculator-console
calc_endpoint='https://calculator.example.com:8443/calculate'
# Give the test script the exact endpoint the host will approve.
printf '/* @seeks http.post %s */\n' "$calc_endpoint" > build/calculator/gcp-calculator.rexx
tail -n +2 examples/calculator/calculator.rexx >> build/calculator/gcp-calculator.rexx
CALC_ENDPOINT="$calc_endpoint" \
CALC_CA=/etc/ssl/certs/ca-certificates.crt \
CALC_PROGRAM=build/calculator/gcp-calculator.rexx ./build/calculator/calculator-consoleThe script requests HTTP access in its first-line declaration. The host checks
that declaration against CALC_ENDPOINT and asks for session approval before
execution. The prompt labels the script's purpose as self-claimed and
unverified; trust in a parent domain or sibling subdomain is never inherited.
After an explicit y or yes for each of the POST and module GET requests, REQUIRE "/static/calculator.rexx" loads CALC for that exact endpoint.
The startup calculation should print 0.3. The native REPL
then waits for input; try:
SAY CALC('mul', '6', '7')
42
LET fraction = CALC('div', '1', '8')
SAY fraction
0.125
:quit
Each CALC call reaches the GCP service. Variables remain local to the Rexx
session. See GCP client and service setup
for the deployment model and endpoint configuration.
Enter, denial or EOF prevents the script from running. Piped input cannot
approve requests; automation must explicitly set CALC_APPROVE_ENDPOINT to
exactly CALC_ENDPOINT and CALC_APPROVE_MODULE_URL to the complete module URL.
See permission approval
for the prompt and the host policy contract.
Include rexxae.h and link librexxae.
examples/counter.c demonstrates ADDRESS commands.
examples/host.py is a Python/ctypes host harness running
host.rexx:
python3 examples/host.py build/librexxae.soIt registers three functions, passes arrays/objects, accepts typed replies, executes nested CALL/RETURN and SELECT/DO and asserts the resulting host-call sequence. It does not replicate a spreadsheet.
Create a context, register function names and arities, optionally seed typed
variables, then call aether_rexxae_step(ctx, instruction_budget):
| Status | Host action |
|---|---|
REXXAE_SAY |
Read text, display/capture it, then step again. |
REXXAE_COMMAND |
Read address/text; reply with RC, RESULT and ERRORTEXT. |
REXXAE_FUNCTION |
Read function name/JSON arguments; reply with JSON or an error. |
REXXAE_RUNNABLE |
The instruction budget was used; schedule another step. |
REXXAE_DONE |
Read the exit code, then destroy the context. |
REXXAE_ERROR |
Read the error and source line, then destroy the context. |
Use aether_rexxae_register_function, aether_rexxae_function,
aether_rexxae_arguments_json and aether_rexxae_reply_value for application
functions. Names are case-insensitive; implemented builtins cannot be replaced.
Host functions currently take positional arguments.
Typed arguments/replies use JSON numbers, strings, booleans, null, arrays and objects. Variable getters/setters use the same representation. Convenience argument getters expose JSON type codes and Rexx display text. JSON excludes nonfinite numbers: those remain internal numeric values and cannot be exported as typed host arguments.
Pending events repeat until answered. Expression results are memoized until an instruction completes, so resumption does not repeat an earlier host effect. Malformed JSON replies leave the call pending for correction; an accepted application error terminates the script. Registry/variable mutation is rejected while a request is pending.
Inputs are copied. Getter payloads are borrowed; copy them before retaining across another context call. A context belongs to one host thread; concurrent use is not established. Destruction releases pending expressions and loop state. This ABI is experimental.
An Aether application imports the compiled library through its catalog:
import std.string
import rexxae
main() {
ctx = rexxae.rexxae_create("LET n = 6 * 7\nSAY n")
defer rexxae.rexxae_destroy(ctx)
status = rexxae.rexxae_step(ctx, 100)
if status == 1 {
println(string.copy(rexxae.rexxae_text(ctx)))
}
}
After make aether-lib, compile with:
ae build consumer.ae --lib /path/to/RexxAE/build/aether -o consumerbuild/aether/librexxae.so uses --shared-runtime, allowing an Aether host
and its libraries to share one runtime. The self-contained build/librexxae.so
can also be imported; it is the default C/ctypes artifact. Both use the same
engine and API. Aether's importer supplies native link/rpath wiring.
test_binary_import.ae tests both variants using
import rexxae, with no engine source import. Source-import std.spec tests
are a separate layer.
Supported now:
- Assignments, arithmetic, signs, parentheses and concatenation.
- Script-derived compound tails, nested property assignment, uppercase unset symbols, zero-based array reads and literal host property access.
- PARSE VAR word/literal-delimiter templates and explicit PARSE VALUE expressions WITH templates, including suspended host input.
- Typed numbers, strings, booleans, arrays, objects and JSON/host nulls.
- 60 scalar functions plus JSON_PARSE, JSON_STRINGIFY, JSON_GET, JSON_KEYS, ARRAY_GET, ARRAY_LENGTH, ARRAY_PUSH, JOIN, TYPEOF, lazy IF and ARG.
- Positional/nested calls and recognized named scalar arguments.
- Labeled routines with optional PROCEDURE/EXPOSE, CALL (including dynamic targets), RETURN, RESULT, ARG() and simple ARG/PARSE ARG argument binding. Routine calls preserve pending expressions across host requests and cooperative yields.
- Comparisons, IF/THEN/ELSE, conditional DO blocks, and SELECT/WHEN/OTHERWISE with nested branches and first-match execution.
- DO ranges/BY, repeat, WHILE, UNTIL, FOREVER and array/object OVER; innermost LEAVE/ITERATE, with existing loop variables restored as in RexxJS.
- REQUIRE with a quoted reference and optional literal AS prefix, dispatched to an explicitly mounted resolver; the calculator supplies a relative HTTP resolver.
- SAY, ADDRESS/quoted commands, integer EXIT, clause separators, comments, CRLF and doubled quotes. Keywords/functions ignore case; variables retain it.
Still needed: positional PARSE templates, classic stem defaults, interpolation, double-quoted escapes, INTERPRET, language error handlers, general module resolution and many remaining functions. Missing symbols return their derived uppercase names; ARRAY_GET/JSON_GET missing properties still error rather than returning JS undefined. Nested writes through arrays are diagnosed as unsupported. JSON_GET supports direct object keys, not full paths. Named typed/host arguments are incomplete. Text functions remain ASCII-only except UTF-16-aware LENGTH; numeric coercion/formatting has tested profiles, not full JS equivalence.
Bare null resolves to the unset symbol NULL; typed null comes
from JSON_PARSE or a host. Seven compatibility differences are pinned in
known_differences.json, not counted as parity.
For shared syntax, assign composite values to variables before function
calls and use double-quoted JSON object keys. Copy RESULT into another variable before
RETURN; the reference parses RETURN RESULT as a function call. Assign compound
function arithmetic before WHEN for shared condition syntax. See
routines.md for CALL forms, PROCEDURE scopes and
argument-list behavior. select.md explains SELECT forms,
including the reference's THEN DO marker and closing END behavior.
parse-and-compounds.md records PARSE input
quirks, explicit WITH behavior and owned values versus reference aliases.
The complete program parses before execution, so syntax errors precede host dispatch. Runtime failures do not roll back earlier application effects. Budgets count instructions, including control flow, not elapsed time or memory; initial parsing is not budgeted.
Containment is an explicit host/guest service boundary. Engine entry points are @pure, its reviewed imports contain no fs/net/os module, and no Rexx syntax reaches native symbols. Hosts mount selected functions, validate typed arguments and veto unmounted ADDRESS environments. The Aether host example combines seal except, sandbox.enforce, trusted service injection and a service-specific file grant. Its escape probes run against both compiled library variants. See containment.md, including the observed std.file library-gate gap and the distinction between this boundary and OS containment of a memory-safety failure.
On the supported core compiler, make test runs 405 native std.spec cases
(including both binary variants), ctypes protocol checks, twelve Python
session/console tests, the C example and Python host harness. make parity
compares 507 programs, including host-call arguments/results, with RexxJS
8317bd1. The dated standalone/vendor audit records the same results; its
additional 28 boundary probes match 16 and remain separate from strict parity.
Seven documented compatibility differences remain separately asserted and
are not counted as parity. The same 125 function fixtures run natively, through ctypes and on RexxJS. Valgrind covers
the C example and the native spec executables. Eleven compiler
rejection probes and the pinned guest/import surfaces protect the containment
contract. The optional calculator's 29 native specs and 30 Python console tests
also pass using the same release. Module execution passes Valgrind.
Live HTTP/TLS verification remains pending: this workspace forbids sockets;
run the terminal integration runner above to collect that evidence.
See design.md, compatibility-baseline.md, function-policy.md and the generated function inventory. The inventory covers 386 core functions, 119 converter helpers and 42 optional manifests. Documentation index links the usage guides, contracts and archived investigations. AGENTS.md records contributor and testing rules.