CREXX

REXX Language implementation

View the Project on GitHub adesutherland/CREXX

crexxsaa host integration

crexxsaa is the initial CREXX compatibility API for C hosts that want a REXXSAA-shaped entry point without taking on the full historical REXXSAA ABI. It is deliberately small: the API is a stable facade over the current CREXX rxvml, ADDRESS, sandbox, and exposed-variable contracts.

The first supported host model is command-environment execution:

This is not a full RexxStart() or RexxVariablePool() clone. Future adapter entry points may be added where real integrations need them, but the internal runtime model remains the modern CREXX ADDRESS model.

Writing hosted source

crexxsaa compiles source exactly as supplied. It does not add OPTIONS LEVELB, an ADDRESS clause, imports, or host-specific boilerplate.

Hosted source should therefore declare the language level and command environment it needs:

options levelb
address the
'emsg CREXX_PROFILE_HOSTED'

The script owns its language mode and its command routing. crexxsaa owns only compile, cache, load, and run orchestration.

Minimal host flow

crexxsaa_context *ctx = NULL;

crexxsaa_create(location, library_rxbin, &ctx);
crexxsaa_set_compiler(ctx, rxc_path, rxas_path, import_dir);
crexxsaa_register_address_environment(ctx, "THE", the_callback, userdata);
crexxsaa_set_address_environment(ctx, "THE");
crexxsaa_run_source(ctx, profile_path, "THE", 0, argc, argv, &program_rc);
crexxsaa_destroy(ctx);

The cache namespace argument, "THE" in this example, is part of the cache identity. Different hosts can compile the same source path without sharing a cache bucket accidentally.

ADDRESS callbacks

A host registers an environment with crexxsaa_register_address_environment(). When CREXX executes a command in that environment, the callback receives a crexxsaa_address_request containing the environment name, command text, and active context.

The callback fills a crexxsaa_address_response:

The callback should return zero for a successfully handled dispatch. Non-zero callback return values indicate host-side failure in the bridge itself.

Variable access during callbacks

crexxsaa exposes simple name-based helpers for active ADDRESS callbacks:

These helpers are valid only while a native ADDRESS callback is active. They resolve variables in this order:

  1. Direct scalar ADDRESS ... EXPOSE name bindings.
  2. Direct stem/array ADDRESS ... EXPOSE name[] bindings.
  3. The active ADDRESS ... SANDBOX pool object.
  4. The request’s standard sandbox fallback.

The host gets one API, while the CREXX script chooses whether a command uses direct exposure or sandbox storage.

Compound names such as FILENAME.1 are mapped to exposed stems by splitting at the first dot and using the remaining text as the stem key. Names are matched case-insensitively at this facade layer to fit legacy host expectations. The standard CREXX sandbox already normalises keys internally.

For compatibility with command processors that treat a scalar result as a one-item stem, an exposed scalar also has a limited compound view:

This rule is applied only when no exposed stem with the same base name exists. Real EXPOSE name[] stems keep their normal stem semantics.

The variable facade is not a general variable-pool enumerator and does not provide arbitrary access to unexposed CREXX locals.

Source cache

crexxsaa_run_source() compiles source through rxc and rxas, then stores the resulting .rxbin in a disposable cache. Normal source edits, CREXX rebuilds, compiler path changes, and library rebuilds cause a recompile without manual cache clearing.

Cache location rules:

Runtime cache controls:

The trace currently emits events such as miss, hit, stale, refresh, and disabled.

Compiler path controls:

Cache maintenance tool

The CREXX build/install includes a crexxsaa maintenance binary in the normal bin directory. It is a troubleshooting tool for the compiled-script cache, not a script runner.

Common commands:

crexxsaa --location
crexxsaa --list
crexxsaa --clear
crexxsaa --cache-dir /tmp/crexxsaa-cache --list
crexxsaa --cache-dir /tmp/crexxsaa-cache --clear --list

Default invocation with no arguments prints the cache location and lists cache entries. --location alone prints only the resolved cache directory.

Example list output:

cache: /Users/adrian/Library/Caches/crexx/crexxsaa
source: /path/to/profile.the
  bucket: 0379ad70148bf7ca
  rxbin: /Users/adrian/Library/Caches/crexx/crexxsaa/v1/0379ad70148bf7ca/b0ec3f1ffcc51e4c.rxbin
  rxbin_size: 1216
  source_hash: 9100aa6921615883
  config_hash: 5af0757a39c4eba1

Technical cache layout

The cache schema is versioned. Current entries live under v1:

<cache-root>/v1/<source-key>/
  manifest
  <object-hash>.rxbin

source-key is an FNV-1a 64-bit hash rendered as 16 hex characters. It includes the host namespace and canonical source path when available.

object-hash is also an FNV-1a 64-bit hash rendered as 16 hex characters. It includes the source content hash and compiler/library configuration hash.

Manifest fields:

version=1
source_path=/absolute/or/supplied/source/path
source_hash=<16-hex-content-hash>
source_size=<bytes>
source_mtime=<mtime>
config_hash=<16-hex-config-hash>
rxbin=<absolute/cache/path/to/object.rxbin>

The content hash, not only the timestamp, decides whether source changed. Size and mtime are recorded for diagnostics and human inspection.

The configuration hash includes:

Compile and update sequence:

Invalidation:

The cache is disposable. Deleting it should affect performance only, not program semantics. The current implementation uses atomic file replacement for compiled objects and manifests, but it is not a full cross-process locking protocol. For troubleshooting, clear the cache while the host application is idle.