CREXX

REXX Language implementation

View the Project on GitHub adesutherland/CREXX

Statements

ADDRESS

ADDRESS sends commands or function requests to a named external environment. It is implemented through the current compiler-exit and VM environment protocol.

Basic command form:

address system "echo hello"

Command output and error streams can be captured:

address command "echo #42" output out error err
say out

ADDRESS host-variable anchors such as :name and ${name} are compiler auto-expose syntax. Their command meaning belongs to the selected environment handler; the VM carries binding values and write-back updates.

The current native registration API is environment based:

rxvml_address_register_callback_environment(ctx, name, id,
    command_cb, function_cb, userdata);

The old command-only callback registration form is retired.

ARG

See Procedures and Arguments Section

CALL

CALL routine [ parameter ] [, [ parameter ] … ]

DO/END

DO [ repetitor ] [ conditional ] ; [ clauses ]

expr ::= DO ; [ clauses ] END

END [ symbol ] ;

repetitor : = symbol = expri [ TO exprt ] [ BY exprb ] [ FOR exprf ]

conditional : = WHILE exprw UNTIL expru

The DO/END statement is the command employed to iterate and group multiple statements into a singular block. This instruction consists of multiple clauses.

When DO ... END appears where an expression is expected, it is parsed as a block expression. In that form the body must yield a value using LEAVE WITH expr.

Simple DO ... END groups may also carry block-scoped signal handlers using ON SIGNAL clauses. The handler clauses are valid only on a simple DO group, not on counted, conditional, forever, or expression-form DO. To protect code inside a loop, nest a simple signal-handling DO ... END group inside the loop body.

EXIT

EXIT [ expr ] ;

Causes the Rexx program to cease execution and, optionally, returns the expression expr to the calling program.

IF/THEN/ELSE

IF expr [;] THEN [;] statement

[ ELSE [;] statement ]

This provides the standard conditional statement structure.

ITERATE

ITERATE [ symbol ] ;

The ITERATE instruction will execute the innermost, active loop in which the ITERATE instruction is situated repeatedly. If a symbol is specified, it will execute the innermost, active loop having the symbol as the control variable repeatedly.

LEAVE

LEAVE [ symbol ] ;

LEAVE WITH expr ;

This statement terminates the innermost, active loop. If symbol is specified, it terminates the innermost, active loop having symbol as control variable.

LEAVE WITH expr is distinct from loop-control LEAVE. It exits the innermost enclosing expression-form DO ... END block and returns the value of expr to the parent expression.

NOP

NOP ;

The NOP instruction is the “null operation” directive; it executes without performing any operation.

OPTIONS

OPTIONS expr ;

The OPTIONS instruction is used to set various interpreter-specific options. See Language Level and Options Section

PARSE

PARSE [ option ] [ CASELESS ] type [ template ] ;

Current implementation status:

PROCEDURE

See Procedures and Arguments Section.

SAY

SAY [ expr ] ;

Evaluates the expression expr and prints the resulting string onto the standard output stream.

SELECT/WHEN/OTHERWISE

SELECT [expression] [;] WHEN expression [, expression …] [;] THEN [;] instruction [;] [WHEN expression [, expression …] [;] THEN [;] instruction [;]] … [OTHERWISE [;] [instruction] [;] …] END [;]

The SELECT statement allows you to conditionally evaluate multiple expressions and execute corresponding instructions based on the first expression that evaluates to true (1).

There are two styles of the SELECT statement in cREXX:

  1. Classic SELECT: Does not include an initial expression after the SELECT keyword. Each WHEN expression is evaluated as a standalone boolean condition.
  2. C-Style SELECT (SWITCH): Includes an initial expression after the SELECT keyword. The expression is evaluated once, and its result is implicitly compared for equality (=) against each WHEN expression.

If a WHEN condition is met, its associated THEN instruction is executed, and control exits the SELECT block. If no WHEN condition is met, the OTHERWISE block (if present) is executed. If no WHEN condition is met and an OTHERWISE block is absent, the SELECT statement acts as a NOP (null operation) and does nothing.

SIGNAL

Signals are Level B error/condition objects implementing .signal. Rexx-created signals can be raised with a signal object or with the compact named forms:

signal .signal("error", "message")
signal error
signal error "message"

Procedure-scoped handlers are installed with SIGNAL ON and removed with SIGNAL OFF:

signal on conversion_error call handle_conversion
signal on error, syntax call handle_problem
signal off conversion_error

handle_conversion: procedure = .signalaction
  arg problem = .signal
  say problem.source()
  return .signalaction.skip()

The handler procedure receives one .signal argument and returns a .signalaction: .signalaction.skip(), .signalaction.retry(), or .signalaction.fail().

Block-scoped handlers use ON SIGNAL clauses on a simple DO ... END group:

do
  risky_work()
on signal conversion_error as problem
  say problem.source()
on signal error, syntax
  call cleanup()
on signal
  call log_unhandled_signal()
end

The statements before the first ON SIGNAL clause are the protected body. Normal completion skips the handlers. A handler that completes normally leaves the DO block. ON SIGNAL with no names catches all maskable signals. AS name binds the current .signal object; if AS is omitted, no signal object is available to that handler.

Only a simple DO ... END group can carry ON SIGNAL clauses. Counted, conditional, forever, and expression-form DO loops do not carry handlers directly. To protect code inside a loop, put a simple signal-handling DO ... END group inside the loop body.

TRACE

TRACE enables or disables VM breakpoint-backed tracing for the current call frame and procedures called from it.

The initial supported forms are:

trace off
trace normal
trace rexx
trace asm

TRACE NORMAL and TRACE REXX currently both trace authored Rexx clauses and skip the runtime/debugger internals by default. TRACE ASM traces VM/RXAS instruction information and includes source text where metadata is available. TRACE OFF disables breakpoint tracing and resets the trace runtime state.

TRACE is implemented as a certified compiler exit. It requires normal compiler exit loading; compiling with exits disabled rejects the statement rather than treating it as an implicit command.

Procedures and Arguments

Function Arguments

Arguments can be passed to a procedure by reference or by value. When an argument is passed by reference, the procedure can modify the original variable that was passed to it. When an argument is passed by value, a copy of the variable is passed to the procedure, and any changes made to the copy do not affect the original variable.

The user-visible rules are:

By example:

ARG a1 = 0, a2 = .int, expose a3 = .aclass, ?a4 = .aclass, a5 = .string[]

Examples:

bump: procedure = .int
  arg value = .int
  value = value + 1
  return value

x = 10
say bump(x)
say x           /* still 10 */
bumpref: procedure = .void
  arg expose value = .int
  value = value + 1
  return

x = 10
call bumpref(x)
say x           /* now 11 */

Ellipsis (…)

The last arguments declaration can be an ellipsis (‘…’), this is used to show that 0 or more arguments can be provided. For example:

ARG a1 = 0, a2 = .int, … = .string

Pseudo Array arg allows access to the ‘…’ arguments. Also see the Arrays section.

The type of this Pseudo is the type of the ‘…’ argument

arg() Operator

The compatibility arg() operator is designed to provide some compatibility with classic REXX; by example:

Implicit Main Procedure

In the event that a module file contains instructions preceding a PROCEDURE instruction, an implicit procedure named main() is automatically generated within the namespace of the module file. The arguments for this procedure can be accessed through the pseudo array arg or arg() operator. This implicit main() case is the compatibility bridge that maps classic arg(n) access onto command-line arguments when no explicit signature is present. The return type of the implicitly defined main() procedure is automatically set to either int or void.