Rexx

An under-appreciated scripting language that is actually faintly prophetic.

Rexx is a scripting language that doesn't get a lot of respect from the mainstream of development because of its unhip origins in the bowels of IBM.  This is too bad, really, because it's actually a sweet, simple little language that presaged a lot of other stuff that later became normal.  In this little article I'll be providing an overview of Rexx so that, perhaps, some of its finer qualities can be appreciated.

History

Rexx is the creation of Mike Cowlishaw, a man with an interesting history.  He's known, of course, for Rexx, but, less well-known, he is arguably the inventor of syntax highlighting and code folding in text editors.  He's quite a bright and innovative chap, in other words.  (On a personal note, one of his innovations, a generalized decimal library, is my current learning project for Mercury.)

 

Rexx (Restructured Extended Executor) was a project begun in 1979 as a replacement for IBM's venerable (and execrable) EXEC/EXEC 2 scripting languages.  It was chiefly designed as a "glue language" (and thus can be considered a precursor to Tcl or its ilk).  It was first publicly described in 1981 and, in view of the public reaction to it, was released as a formal product shortly afterward in 1982.  Since that time it has been ported or rewritten by IBM for each of its platforms (including my own first exposure to it in OS/2) and has been ported or rewritten by third parties for a bewildering variety of other platforms including, but not limited to, PC/MS-DOS, various Unix implementations (including Unix-alikes like Coherent and Linux), the Amiga, the JVM and even some (larger) embedded systems.  There's even an ANSI standard for Rexx (ANSI X3.274–1996 "Information Technology – Programming Language REXX").  Not bad for a language nobody has ever heard of.

So what's Rexx look like?

Let's take a look at a few samples from Rosetta Code, shall we?

100 Doors

1
2
3
4
5
6
7
8
9
do inc = 1 to 100
  do d = inc to 100 by inc
    door.d = \door.d
  end
end
say "The open doors after 100 passes:"
do i = 1 to 100
  if door.i = 1 then say i
end

Here we see some classic Rexxisms like say for output and the generalized do…end loop structure.  Some of the other versions of this program at the linked-to site also show off the parse statement that gives the language much of its power.  (Trust me.  Parse is very powerful.)

 

One bizarre feature of the language illustrated here is compound variables.  Rexx has no array types.  Arrays are simulated with compound variables in a stem.tail style of construction.  Make the tail part a number (like a.1, a.2 and a.3) and you have yourself an array.  Make it a variable and the power shines through better as with door.d or door.i above.  Wikipedia has more on this.

Bitwise Operations

Everything in Rexx is stored and operated upon as strings (at least at the notional level; the details may vary by implementation as they do in Tcl).  This doesn't stop it from being able to do bitwise operations.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/*REXX program displays dates of last Fridays of each month for any year*/
parse arg yyyy
do j=1 for 12
  say lastDOW('Friday',j,yyyy)
  end  /*j*/
exit                                   /*stick a fork in it, we're done.*/
/*┌────────────────────────────────────────────────────────────────────┐
  │ lastDOW:  procedure to return the date of the  last day-of-week of │
  │           any particular month  of any particular year.            │
  │                                                                    │
  │ The  day-of-week  must be specified (it can be in any case,        │
  │ (lower-/mixed-/upper-case)  as an English name of the spelled day  │
  │ of the week,   with a minimum length that causes no ambiguity.     │
  │ I.E.:   W  for Wednesday,   Sa  for Saturday,   Su  for Sunday ... │
  │                                                                    │
  │ The month can be specified as an integer   1 -> 12                 │
  │    1=January     2=February     3=March     ...     12=December    │
  │ or the English  name  of the month,  with a minimum length that    │
  │ causes no ambiguity.    I.E.:  Jun  for June,   D  for December.   │
  │ If omitted  [or an asterisk(*)],  the current month is used.       │
  │                                                                    │
  │ The year is specified as an integer or just the last two digits    │
  │ (two digit years are assumed to be in the current century,  and    │
  │ there is no windowing for a two-digit year).                       │
  │ If omitted  [or an asterisk(*)],  the current year is used.        │
  │ Years < 100   must be specified with  (at least 2)  leading zeroes.│
  │                                                                    │
  │ Method used: find the "day number" of the 1st of the next month,   │
  │ then subtract one  (this gives the "day number" of the last day of │
  │ the month,  bypassing the leapday mess).   The last day-of-week is │
  │ then obtained straightforwardly,   or  via subtraction.            │
  └────────────────────────────────────────────────────────────────────┘*/
lastdow: procedure; arg dow .,mm .,yy .              /*DOW = day of week*/
parse arg a.1,a.2,a.3                                /*orig args, errmsg*/
if mm=='' | mm=='*' then mm=left(date('U'),2)        /*use default month*/
if yy=='' | yy=='*' then yy=left(date('S'),4)        /*use default year */
if length(yy)==2 then yy=left(date('S'),2)yy         /*append century.  */
/*Note mandatory leading blank in strings below.*/
$=" Monday TUesday Wednesday THursday Friday SAturday SUnday"
!=" JAnuary February MARch APril MAY JUNe JULy AUgust September",
  " October November December"
upper $ !                                            /*uppercase strings*/
if dow==''                 then call .er "wasn't specified",1
if arg()>3                 then call .er 'arguments specified',4

do j=1 for 3                                       /*any plural args ?*/
  if words(arg(j))>1       then call .er 'is illegal:',j
  end

dw=pos(' 'dow,$)                                     /*find  day-of-week*/
if dw==0                   then call .er 'is invalid:',1
if dw\==lastpos(' 'dow,$)  then call .er 'is ambigious:',1

if datatype(mm,'month') then                         /*if MM is alpha...*/
  do
    m=pos(' 'mm,!)                                     /*maybe its good...*/
    if m==0                  then call .er 'is invalid:',1
    if m\==lastpos(' 'mm,!)  then call .er 'is ambigious:',2
    mm=wordpos(word(substr(!,m),1),!)-1                /*now, use true Mon*/
    end

if \datatype(mm,'W')       then call .er "isn't an integer:",2
if \datatype(yy,'W')       then call .er "isn't an integer:",3
if mm<1 | mm>12            then call .er "isn't in range 1──►12:",2
if yy=0                    then call .er "can't be 0 (zero):",3
if yy<0                    then call .er "can't be negative:",3
if yy>9999                 then call .er "can't be > 9999:",3

tdow=wordpos(word(substr($,dw),1),$)-1               /*target DOW, 0->6*/
/*day# of last dom.*/
_=date('B',right(yy+(mm=12),4)right(mm//12+1,2,0)"01",'S')-1
?=_//7                                               /*calc. DOW,  0->6*/
if ?\==tdow then _=_-?-7+tdow+7*(?>tdow)             /*not DOW?  Adjust.*/
return date('weekday',_,"B") date(,_,'B')            /*return the answer*/

.er: arg ,_;say; say '***error!*** (in LASTDOW)';say /*tell error,  and */
say word('day-of-week month year excess',arg(2)) arg(1) a._
say; exit 13                                       /*... then exit.   */

Here we see a function (lastdow) and a procedure (.er) defined and we see the syntactic oddity (by modern standards) that lastDOW and lastdow are interchangeable tokens.  Note also variables like _, ?, ! and $.  It is instructive to note the different uses of the do...end loop construct as well.  A single, generalized loop construct is used in Rexx that covers what other languages use several loop types to do.  You can read more about this on Wikipedia.

Other cool stuff

Rexx can do limited metaprogramming and has the very powerful interpret instruction for runtime evaluation.

Why should I care?

Well, no particular reason really.

 

Rexx is a simple, small, elegant, powerful procedural scripting "glue" language.  It predates many languages used in that role today (including Tcl, Python, Ruby and Lua) and is, yet, arguably superior to most of them in terms of just getting in and getting the job done with a minimum of interference.  (In my personal opinion, in fact, the only one of the four languages named that isn't clearly inferior to Rexx in the "glue" language role is Lua.)

 

Rexx is competently crafted, available on almost any platform from the biggest of the big iron to many embedded environments.  It is unpretentious yet subtly elegant anyway.  You could easily do worse than using it (and, indeed, probably do).

 

If none of that matters to you, of course, you shouldn't care.

So where do I go from here?

Well, if Rexx looks interesting to you, rexx.org has a decent implementation of the Rexx language available for download (not to mention a large variety of packages for it).  There's a pure-source tutorial available at the Rexx Language Association's web site.

Bahman Movaqar
Bahman Movaqar says:
Jan 09, 2013 05:59 AM

Good choice. I'd always wondered what is this T-Rex language that I know absolutely nothing about? :-)
However, to make the article more “ttmrichter'ish” style, I'd say put in some more shorter and commented examples, specially covering the `\variable' syntax, functions/procedures and their input/output mechanisms.
Thanks for the intro.