Rexx PARSE: Pattern-Based String Extraction Without Regex
Rexx's PARSE extracts structured data using literal delimiters, positional patterns, and variable placeholders — no regex. One statement handles logs, fixed-width records, arguments, and stems. Trade-offs: no alternation, whitespace-sensitive templates.
27 Aug 2026, 16:21 UTC

The Problem: Extracting Structure from Messy Text
You have a log line like 2024-03-15 14:22:01 [WARN] Connection timeout (host=db-primary, port=5432) and need the timestamp, level, host, and port. In most languages you reach for a regex. In Rexx, you use PARSE — a built-in pattern matcher that splits strings by literal delimiters, positional columns, or whitespace, all in a single statement with no compilation step.
How PARSE Works: Templates as Micro-Specs
A PARSE template mixes three elements:
- Literal strings act as delimiters — they match exactly and are discarded.
- Variable names receive the substring between the previous and next delimiter.
- Positional patterns (absolute column numbers or relative offsets) carve fixed-width fields.
The placeholder period . discards a field. PARSE VAR source template parses a variable; PARSE ARG parses subroutine arguments; PARSE PULL reads from the external data queue or stdin.
Worked Example: Parsing a Structured Log Line
Given the log line above, this template extracts the four pieces:
line = '2024-03-15 14:22:01 [WARN] Connection timeout (host=db-primary, port=5432)'
parse var line timestamp ' [' level '] ' . ' (host=' host ',' port=' port ')' .
say timestamp --> 2024-03-15 14:22:01
say level --> WARN
say host --> db-primary
say port --> 5432
Breakdown:
timestamptakes everything up to the literal[.leveltakes everything up to]..discardsConnection timeout.(host=is a literal delimiter;hostcaptures until, port=.portcaptures until the closing).- Final
.discards any trailing text.
No regex, no loops, no SUBSTR/INDEX arithmetic. The template reads like a spec.
Fixed-Width Records: Positional Patterns
Mainframe-style fixed-width records map naturally to absolute column numbers:
record = '00123ABCDEPTX '
parse var record 1 id 6 name 11 dept 15 .
say id --> 00123
say name --> ABCDEPT
say dept --> X
Columns are 1-based. 1 id 6 means "start at column 1, put into id, stop before column 6". Relative offsets work too: parse var line (pos) word1 word2 starts at the column stored in pos.
Stem Variables for Variable-Length Fields
When the number of whitespace-delimited words isn't known upfront, a stem (compound variable) collects them all:
parse var 'one two three four' words.*
do i = 1 to words.0
say words.i
end
/* outputs four lines: one, two, three, four */
words.0 holds the count; words.1 through words.n hold each word.
Trade-offs and Limitations
- No alternation or optional groups. If a field might be delimited by
,or;, you need multiplePARSEstatements or pre-process withTRANSLATE. - Whitespace sensitivity.
word1 word2splits on any run of spaces/tabs;'word1 word2'(with a literal space in the template) requires exactly one space in the source. - Variable substitution at parse time.
parse var src (start) (len) .uses the current values ofstartandlen; changing them later does not re-parse. - Cross-implementation differences. NetRexx uses Java String semantics (Unicode, no EBCDIC) and
PARSE SOURCEreturns different tokens. Test delimiter behavior when porting. - Limited debugging.
TRACE ?Rshows parse results; no static template validator exists.
Quick Verification Checklist
Run these one-liners in Regina Rexx or ooRexx to confirm behavior:
/* Basic delimiter parse */
parse var 'Smith, John (42)' . name '(' age ')' .; say name age
/* Fixed-width */
parse var '00123ABCDEPTX' 1 id 6 name 11 dept 15 .; say id name dept
/* ARG parsing */
call test 'alpha' 'beta gamma'; exit
test: parse arg a b c; say a b c
/* Stem fill */
parse var 'one two three' words.*; do i=1 to words.0; say words.i; end
/* Case handling */
parse upper var 'HeLLo' a b; say a b
When to Reach for PARSE
Use PARSE when:
- Input follows a predictable delimiter or column structure (logs, config lines, fixed-width records).
- You want a single-pass, O(n) extractor with no regex compilation overhead — useful in hot batch loops.
- The parsing logic should stay readable and maintainable by non-regex experts.
Reach for regex when you need alternation, capture groups, lookahead, or validation of complex formats (email addresses, nested brackets). Rexx implementations often provide regex via external libraries (e.g., rxregex for Regina), but PARSE covers a surprising amount of day-to-day extraction without them.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.