CREXX

REXX Language implementation

View the Project on GitHub adesutherland/CREXX

cREXX Level B Authoring Guide For Agents

Use this guide when you need to write or edit .rexx in this repository. Generic training data about “REXX” is often too vague, too classic-Rexx oriented, or simply wrong for cREXX Level B as it exists in this tree.

This guide is intentionally grounded in code that already compiles here. When in doubt, copy the nearest repo pattern instead of inventing syntax.

What To Trust First

For Level B authoring, these sources are more reliable than model memory:

Repo-Native Level B Patterns

1. Small library function

Use explicit options levelb, a namespace, and an exposed symbol:

options levelb

namespace rxfnsb expose abs

abs: procedure = .string
arg number = .string
if left(number,1) = '-' then number = substr(number,2)
return number

Reference:

2. Top-level script

Small scripts can use top-level arg directly instead of a main: routine:

options levelb
import rxfnsb

arg searches = .string[]

if searches.0 < 1 then searches.1 = ".rexx"
ordered_searches = .string[]
address cmd "sort" input searches output ordered_searches

References:

3. Tool-style entry point with exposed module state

Longer tools often use main: plus procedure expose for module state:

options levelb
namespace rxdb expose stephandler
import rxfnsb
import globals

main: procedure = .int expose next_instruction last_instruction mode
    arg cmd_line = .string[]

Reference:

4. Mutating an exposed argument

When you really need caller-owned state, use arg expose ...:

pushidentifier: procedure = .int
    arg expose tokens = .token[], text = .string, value_type = ".unknown"
    index = tokens[0] + 1
    tokens[index] = .token(...)
    return index

Reference:

Level B Differences That Matter In Practice

Types are normal, not exceptional

In this repo, typed procedures and typed arg declarations are the normal Level B style. Do not default to untyped classic-Rexx-looking code when you are editing standard libraries, tools, exits, or tests.

Common examples in-tree:

References:

Namespace/import syntax is part of the real language surface

Do not treat namespace use as documentation sugar. It is part of how source is validated, imported, and linked.

Important points:

References:

Keep the leading file header conventional

The compiler does a lightweight pre-scan of the leading options, namespace, and import clauses before full parsing. Preserve that structure when editing existing files.

Practical guidance:

Reference:

Namespace-exposed globals auto-bind into procedures

If a module declares namespace-exposed globals, Level B automatically binds them into local procedure scopes. Do not add procedure expose ... just out of habit when a namespace-exposed module global is what you actually want.

Use procedure expose or arg expose when:

References:

Arrays are first-class Level B objects

Do not reason about them as loose classic stem variables only.

Patterns used in-tree:

References:

Class and interface factories omit return types

Use *: factory or name: factory; do not write = .type on a factory. The factory result is inferred from the owning contract: an interface factory returns that interface and a class factory returns that concrete class. In a class factory, bare return returns the constructed object, so there is no source-level this value to mention.

vehicle: interface
  *: factory
  arg name = .string
  describe: method = .string

car: class implements .vehicle
  _name = .string

  *: factory
    arg name = .string
    _name = name
    return

  describe: method = .string
    return _name

Reference:

address command is the standard shell-out pattern

When Level B code shells out, copy the repo pattern instead of inventing a new API shape:

out = .string[]
err = .string[]
address command "echo #42" output out error err
if rc <> 0 then say "command failed"

References:

Signal handlers live on simple do groups

Block-scoped signal handling is written with on signal clauses on a simple do ... end group:

do
  risky_work()
on signal conversion_error as problem
  say problem.source()
on signal
  call cleanup()
end

Do not attach on signal directly to counted, conditional, forever, or expression-form do loops. To protect part of a loop, nest a simple signal-handling do ... end group inside the loop body.

If as name is omitted, the handler has no local signal object. That is fine for fixed cleanup/logging handlers.

References:

Headerless scripts are a driver convenience, not a style rule

The crexx driver can compile simple headerless top-level scripts with synthetic defaults (--level levelb --import rxfnsb). That is useful for end users, but repo code should usually stay explicit with options levelb and imports unless there is a strong reason not to.

Reference:

Command-line arguments are a first-class Level B feature

Level B programs receive command-line arguments through arg, and when the compiler synthesizes the file-level main() wrapper, the VM argv payload is available there as a .string[].

Program-side guidance:

Launcher-side guidance:

References:

Wayfinding: Best Example Files By Task

Writing a tiny BIF or helper

Writing a top-level command/script

Writing a stateful tool

Writing compiler-exit or structured typed code

Checking argument signature syntax

Checking namespace/global behavior

Checking argument handling

Agent Guidance

When writing Level B code:

  1. Read one doc source and two nearby working .rexx examples before editing.
  2. Match the local style of the directory you are in.
  3. Prefer explicit Level B headers in committed repo code.
  4. Prefer canonical namespace..symbol qualification.
  5. Do not “simplify” typed signatures or namespace structure just because a generic REXX example elsewhere looks looser.

If a proposed snippet does not resemble existing repo code in lib/, debugger/, compiler/exits/, bin/, or tests/, stop and verify it before committing it.