Advanced Statements
Advanced Statements
Advanced language constructs for precision control, parsing, debugging, and program structure.
NUMERIC Statement - Precision Control
Control arithmetic precision and formatting following classic Rexx standards.
Basic Syntax
NUMERIC DIGITS value -- Set decimal precision (1-999999999)
NUMERIC FUZZ value -- Set comparison tolerance (0 to digits-1)
NUMERIC FORM format -- Set format (SCIENTIFIC or ENGINEERING)
Examples
-- Set decimal precision
NUMERIC DIGITS 15
SAY "High precision calculation"
LET result = 1.0 / 3.0
-- Set fuzz for comparison tolerance
NUMERIC FUZZ 2
SAY "Tolerant comparisons"
-- Set number format
NUMERIC FORM SCIENTIFIC
SAY "Scientific notation enabled"
-- Using variables
LET precision = "20"
NUMERIC DIGITS precision
SAY "Dynamic precision setting"
Supported Settings
- DIGITS - Decimal precision (default: 9)
- FUZZ - Comparison tolerance (default: 0)
- FORM - Number format: SCIENTIFIC or ENGINEERING (default: SCIENTIFIC)
PARSE Statement - Advanced String Parsing
Powerful string parsing with templates, following classic Rexx patterns.
Basic Syntax
PARSE VAR variable WITH template -- Parse from variable
PARSE VALUE expression WITH template -- Parse from expression
PARSE ARG variable WITH template -- Parse from arguments
Template Features
- Variables - Capture parsed segments
- Quoted delimiters - Custom separators like
",","-","@" - Space parsing - Automatic word separation
- Remaining text - Last variable gets all remaining content
Examples
Space-delimited parsing:
LET fullName = "John Doe Smith"
PARSE VAR fullName WITH firstName lastName middleName
-- firstName="John", lastName="Doe", middleName="Smith"
Custom delimiter parsing:
LET date = "2024-12-25"
PARSE VAR date WITH year "-" month "-" day
-- year="2024", month="12", day="25"
LET email = "user@example.com"
PARSE VAR email WITH username "@" domain
-- username="user", domain="example.com"
VALUE source (expressions):
LET csvData = "apple,banana,cherry"
PARSE VALUE csvData WITH fruit1 "," fruit2 "," fruit3
-- fruit1="apple", fruit2="banana", fruit3="cherry"
Remaining text handling:
LET sentence = "The quick brown fox jumps over"
PARSE VAR sentence WITH word1 word2 remaining
-- word1="The", word2="quick", remaining="brown fox jumps over"
Stack Operations - PUSH/PULL/QUEUE
Classic Rexx stack operations for managing data structures with LIFO and FIFO patterns.
Basic Operations
RETRY_ON_STALE - Handling Stale DOM Elements
The RETRY_ON_STALE feature is used in browser automation scripts to handle stale DOM elements. It automatically retries operations on elements that may have become stale due to page updates or dynamic content loading.
Usage
RETRY_ON_STALE
This directive can be placed before operations that interact with the DOM to ensure they are retried if the target elements are not immediately available.
PUSH value -- Add to top of stack (LIFO)
PULL variable -- Remove from top into variable
QUEUE value -- Add to bottom of stack (FIFO)
Stack Functions
STACK_SIZE -- Get number of items
STACK_PEEK -- Look at top item without removing
STACK_PUSH value -- Add item, return new size
STACK_PULL -- Remove and return top item
STACK_QUEUE value -- Add to bottom, return new size
STACK_CLEAR -- Empty stack, return cleared count
Examples
LIFO operations (Last In, First Out):
PUSH "first item"
PUSH "second item"
PUSH "third item"
PULL item1 -- Gets "third item"
PULL item2 -- Gets "second item"
PULL item3 -- Gets "first item"
FIFO operations (First In, First Out):
QUEUE "task1"
QUEUE "task2"
QUEUE "task3"
PULL next_task -- Gets "task1"
Using variables:
LET urgent = "priority task"
PUSH urgent
PULL processing
-- Stack functions for advanced operations
LET stack_size = STACK_SIZE
LET top_item = STACK_PEEK
LET pushed_count = STACK_PUSH value="new item"
LET pulled_item = STACK_PULL
LET cleared_count = STACK_CLEAR
Subroutines - CALL/RETURN
Classic Rexx subroutine support for modular programming with parameter passing and return values.
Basic Syntax
CALL subroutine_name -- Call with no arguments
CALL subroutine_name arg1 -- Call with one argument
CALL subroutine_name arg1 arg2 -- Call with multiple arguments (space-separated)
RETURN -- Return to caller
RETURN value -- Return with value
External Script Calling
RexxJS supports calling external REXX scripts as subroutines using the same CALL syntax:
CALL "./path/to/script.rexx" -- Call external script (quoted, relative path)
CALL "../other/script.rexx" a b -- Call with parameters (space-separated)
LET result = CALL external.rexx data_array -- Call with native objects
Native Object Preservation
CALL operations preserve JavaScript native object types, distinguishing RexxJS from traditional POSIX parameter passing:
- JavaScript arrays remain as arrays (not converted to strings)
- JavaScript objects remain as objects with all properties
- Numbers remain as numeric types (not converted to strings)
- Booleans and other native types are preserved
-- Main script
LET myArray = [10, 20, 30, "test"]
CALL process_array.rexx myArray
-- In process_array.rexx:
PARSE ARG received_param
SAY "Type: " || DATATYPE(received_param) -- "ARRAY"
SAY "Length: " || LENGTH(received_param) -- 4
SAY "First: " || ARRAY_GET(received_param, 1) -- 10 (1-based REXX indexing)
CALL vs ADDRESS System Parameter Passing
RexxJS distinguishes between two parameter passing approaches:
| Operation | Parameter Handling | Use Case |
|---|---|---|
CALL script.rexx |
Native objects preserved | Script-to-script communication |
ADDRESS system 'rexx script.rexx' |
POSIX string conversion | System command compatibility |
-- Native object preservation (CALL)
LET data = [1, 2, 3]
CALL processor.rexx data -- Array passed as JavaScript array
-- POSIX string conversion (ADDRESS system)
LET data = [1, 2, 3]
ADDRESS system 'rexx processor.rexx ' || data -- Array converted to "1,2,3"
Variable Isolation
External scripts called via CALL have complete variable isolation:
- Each called script has its own variable space
- No variable sharing between calling and called scripts
- Changes to variables in called scripts don’t affect the caller
-- Main script
LET name = "main"
CALL sub.rexx
SAY name -- Still "main"
-- In sub.rexx:
LET name = "subroutine" -- Does not affect main script's 'name'
RETURN
Pass-by-Reference Semantics for Objects and Arrays
Objects and arrays passed to external scripts via CALL support true pass-by-reference semantics, meaning mutations made in the called script are visible to the caller:
-- Main script
LET myArray = [10, 20, 30]
LET myObject = {"name": "original", "value": 42}
SAY "Before: " || ARRAY_GET(myArray, 1) -- 10
SAY "Before: " || ARRAY_GET(myObject, "name") -- "original"
CALL modifier.rexx myArray, myObject
SAY "After: " || ARRAY_GET(myArray, 1) -- 999 (modified!)
SAY "After: " || ARRAY_GET(myObject, "name") -- "CHANGED" (modified!)
-- In modifier.rexx:
PARSE ARG arr, obj
-- Modify array elements (mutations visible to caller)
LET result1 = ARRAY_SET(arr, 0, 999) -- Change first element
LET result2 = ARRAY_SET(arr, 2, "new") -- Change third element
-- Modify object properties (mutations visible to caller)
LET result3 = ARRAY_SET(obj, "name", "CHANGED")
LET result4 = ARRAY_SET(obj, "newProp", "added")
RETURN
Key Benefits:
- Efficient data sharing: Large objects/arrays aren’t copied, just referenced
- True object mutation: Changes persist across script boundaries
- JavaScript-native behavior: Objects behave as they would in regular JavaScript
- Performance: No serialization/deserialization overhead
Use Cases:
- Data processing pipelines that modify shared data structures
- Configuration objects that need updates from multiple scripts
- Large datasets that need incremental modifications
- Collaborative data manipulation across script boundaries
Subroutine Structure
-- Main program
CALL my_subroutine "param1", "param2"
LET result = result -- Access return value
EXIT
-- Subroutine definition
my_subroutine:
LET param1 = ARG.1 -- First argument (1-based)
LET param2 = ARG.2 -- Second argument
LET count = ARG.0 -- Argument count
-- subroutine code here
RETURN result_value
Argument Access
- ARG.0 - Number of arguments passed
- ARG.1 - First argument (1-based indexing)
- ARG.2 - Second argument
- ARG.n - nth argument
Examples
Simple subroutine call:
CALL calculate_tax 100, 0.08
LET tax_amount = result
EXIT
calculate_tax:
LET amount = ARG.1 -- First argument
LET rate = ARG.2 -- Second argument
LET arg_count = ARG.0 -- Number of arguments
LET result = amount * rate
RETURN result -- Return value
Nested subroutine calls:
CALL process_order "12345"
EXIT
process_order:
LET order_id = ARG.1
CALL validate_order order_id
CALL calculate_shipping order_id
LET status = "processed"
RETURN status
validate_order:
LET id = ARG.1
LET valid = "yes"
RETURN
calculate_shipping:
LET id = ARG.1
LET shipping = "standard"
RETURN
TRACE Statement - Execution Debugging
Enable execution tracing and debugging with multiple modes for different levels of detail.
Basic Syntax
TRACE A -- All instructions (most verbose)
TRACE R -- Results of assignments and functions
TRACE I -- Intermediate (assignments, functions, calls)
TRACE O -- Output operations only (SAY statements)
TRACE NORMAL -- Basic execution tracing
TRACE OFF -- Disable tracing (default)
TRACE Mode Details
- A (All) - Traces every instruction with full details and results
- R (Results) - Traces instructions that produce results (assignments, functions)
- I (Intermediate) - Traces assignments, function calls, and subroutine calls
- O (Output) - Traces only output operations like SAY statements
- NORMAL - Basic instruction flow and subroutine call tracing
- OFF - Disables all tracing (default state)
Examples
Debug a calculation:
TRACE A
LET value = 100
LET tax_rate = 0.08
LET total = value * tax_rate
TRACE OFF
Monitor subroutine calls:
TRACE I
CALL calculate_shipping "priority"
CALL format_results
TRACE NORMAL
Track only assignments:
TRACE R
LET processed_data = TRANSFORM input="raw data"
LET final_result = VALIDATE data=processed_data
Trace Output Format
[HH:MM:SS.sss] MODE:TYPE message => result
[14:32:15.123] A:I ASSIGNMENT
[14:32:15.124] A:A LET value = "100" => 100
[14:32:15.125] I:C CALL calculate_shipping (1 args)
Accessing Trace Output
-- Trace output is captured and can be accessed via interpreter
-- getTraceOutput() returns formatted trace lines
-- clearTraceOutput() clears the trace buffer
Best Practices
NUMERIC Usage
- Set appropriate precision for your calculations
- Use FUZZ for floating-point comparisons
- Reset to defaults when precision is no longer needed
PARSE Usage
- Use descriptive variable names for parsed components
- Handle cases where input might not match expected format
- Consider using WORDS/WORD functions for simple word extraction
Stack Usage
- Use PUSH/PULL for LIFO operations (undo stacks, recursive data)
- Use QUEUE/PULL for FIFO operations (task queues, processing pipelines)
- Check STACK_SIZE before PULL operations to avoid errors
Subroutine Design
- Use descriptive subroutine names
- Document expected parameters with comments
- Always use EXIT before subroutine definitions in main program
- Handle variable argument counts with ARG.0
TRACE Usage
- Use TRACE sparingly in production code
- Start with TRACE I for general debugging
- Use TRACE A for detailed step-by-step analysis
- Always turn off tracing when debugging is complete
Argument Styles for CALL
- External script calls use space-separated positional arguments only.
- Commas between arguments are not supported.
- External script names must be quoted and use a relative prefix (“./” or “../”). This keeps scripts portable across Node and browsers without bundlers.
- Examples:
CALL "./path/to/script.rexx" arg1 arg2- With expressions:
CALL "./path/to/script.rexx" COPY(myArray) COPY(myObject)
COPY() and Cross-Script Semantics
- Strings and other primitives are passed by value.
- Arrays/objects are passed by reference by default.
- Use
COPY(value)to deep copy before CALL if you do not want the callee to mutate your original.
See also:
- Basic Syntax for fundamental language constructs and pipe operator (
|>) - Control Flow for IF/DO/SELECT statements
- String Functions for parsing alternatives
- Error Handling for SIGNAL ON ERROR patterns