string = '\n' /* two characters, \ and an n, though not a newline */ string = "\n" /* two characters, \ and an n, though not a newline */ string = "0A"X /* newline character code [hex] */ string = "1010"B /* newline character code [binary] */ string = "Newline" "0A"X "here" /* embedded newline in string */ string = 'Jon ''Maddog'' Orwant' /* literal single quotes */ string = "Jon ""Maddog"" Orwant" /* literal double quotes */ string = "Jon 'Maddog' Orwant" /* embedded literal single quotes */ string = 'Jon "Maddog" Orwant' /* embedded literal double quotes */ /* ----------------------------- */ /* HERE documents not supported, but multi-line string allowed */ a = "This is a multiline string that is not a HERE document" , "but consists of a series of concatenated strings" , "each on its own line courtesy of the 'comma' which, when" , "it appears as the last, space-separated character on a" , "line, acts as a line continuation character" /* ----------------------------- */ /* Pseudo implementation of a HERE document */ signal HEREDOC /* Line 1 ... Line 2 ... Line 3 */ HEREDOC: a = NULL do i = SIGL + 1 line = SOURCELINE(i) if line = "*/" then leave a = a||NEWLINE||line end |
/* ------------------------------------------------------------------ */ /* * REXX offers string manipulation built-in functions [BIF's] many */ /* being equivalent to Perl offerings. However, all REXX BIF's */ /* return copies of the transformed string; original is unaltered. */ /* Therefore this type of usage is illegal: */ /* */ /* SUBSTR(string, offset, count) = newstring */ /* */ /* Instead, variable storing original must be reassigned with the */ /* altered copy */ /* */ /* * REXX implements PARSE instruction which provides a faster means */ /* of: */ /* - tokenising strings [from several sources: string, file, stack] */ /* - assigning tokens to variables */ /* - initialisng and swapping variables, multi-line assignments */ /* */ /* Examples of both approaches shown wherever applicable */ /* ------------------------------------------------------------------ */ string = "a value" /* ----------------------------- */ offset = 3 ; count = 9 ; padchar = 'X' parse var string =(offset) v v = SUBSTR(string, offset) /* "value " */ parse var string =(offset) v +(count) v = SUBSTR(string, offset, count) /* "value " */ v = SUBSTR(string, offset, count, padchar) /* "valueXXXX" */ /* ----------------------------- */ offset = 2 ; count = 2 ; padchar = '*' ; newstr = "Z" v = INSERT(newstr, string, offset, count, padchar) /* "a Z*value" */ v = OVERLAY(newstr, string, offset, count, padchar) /* "aZ*alue" */ /* ----------------------------- */ /* *** Unfinished *** - UNPACK */ /* ----------------------------- */ /* PARSE VAR instruction equivalent, but more efficient, than SUBSTR */ string = "This is what you have" slen = LENGTH(string) parse var string =1 first +1 first = SUBSTR(string, 1, 1) /* "T" */ parse var string =6 start +2 start = SUBSTR(string, 6, 2) /* "is" */ parse var string =14 rest rest = SUBSTR(string, 14) /* "you have" */ parse var string =(slen) last +1 last = SUBSTR(string, slen, 1) /* "e" */ parse var string =(slen) -3 end end = SUBSTR(string, slen - 3) /* "have" */ parse var string =(slen) -7 piece +3 piece = SUBSTR(string, slen - 7, 3) /* "you" */ /* Display contents of string */ say string /* Change "is" to "wasn't" : This wasn't what you have */ string = CHANGEWORD("is", string, "wasn't") /* Replace last 12 characters : This wasn't wondrous */ newstr = "ondrous" ; slen = LENGTH(string) ; nlen = LENGTH(newstr) /* 1 - slow */ string = OVERLAY(newstr, string, slen - 11) string = DELSTR(string, LASTPOS(newstr, string) + nlen) /* 2 - faster */ string = LEFT(string, slen - 12) || newstr /* 3 - fastest */ sparse = slen - 12 parse var string string +(sparse) string = string || newstr /* delete first character : his wasn't wondrous */ parse var string =2 string string = DELSTR(string, 1, 1) string = RIGHT(string, slen - 1) /* Return last 15 characters : wasn't wondrous */ slen = LENGTH(string) parse var string =(slen) -14 string +15 string = SUBSTR(string, slen - 14, 15) string = RIGHT(string, 15) /* Delete last 10 characters : wasn' */ slen = LENGTH(string) ; sparse = slen - 10 parse var string string +(sparse) string = DELSTR(string, slen - 9, 10) string = LEFT(string, slen - 10) /* *** Unfinished *** */ |
/* ------------------------------------------------------------------ */ /* REXX Boolean values are strictly: */ /* */ /* 1 - TRUE */ /* 0 - FALSE */ /* */ /* All other values force an syntax error if used in a Boolean */ /* context; Boolean expression can be forced via a comparision */ /* operation [see example below] */ /* */ /* REXX does not support conditional structures other than the 'IF' */ /* and 'SELECT' instructions; there is no ternary operator, nor a */ /* conditional assignment expression. This can, however, be mimiced */ /* via function; examples below use an 'iif' function implementation */ /* that, rather crudely, supports this type of operation */ /* */ /* iif(CONDITION, TRUE_VALUE, FALSE_VALUE) */ /* */ /* It is also worth mentioning that the WORD BIF can also be used for */ /* performing conditional assignment. It can be used where alternate */ /* values can be placed in the same string, and relies on: */ /* */ /* * The fact that in REXX all data are strings */ /* * The values of FALSE and TRUE being exactly 0, and 1, respectively*/ /* */ /* See example at end of this section */ /* ------------------------------------------------------------------ */ condition = TRUE ; b = 'B' ; c = 'C' ; x = TRUE ; y = 'Y' /* Use 'b' if 'condition' is TRUE, else return 'c' */ a = iif(condition, b, c) /* Use 'b' if 'b' is TRUE, else 'c' */ a = iif(, b, c) /* Set 'x' to 'y' unless 'x' is already TRUE */ x = iif(, \x, y) /* As above; Boolean expression forced in case 'x' non-Boolean */ x = iif(, \(x == TRUE), y) /* ----------- */ /* Use 'b' if 'b' is defined, else 'c' */ a = iif(SYMBOL('b') == "VAR", b, c) bar = "ANOTHER VALUE" foo = iif(SYMBOL('bar') \= "VAR", bar, "DEFAULT VALUE") exit 0 /* ----------- */ iif : procedure expose (globals) if ARG(1, 'E') then cond = ARG(1) ; else cond = ARG(2) if cond == TRUE then return ARG(2) ; else return ARG(3) /* ----------------------------- */ condition = TRUE ; alternatives = "B C" /* condition: FALSE -> 'B' returned condition: TRUE -> 'C' returned */ WORD(alternatives, condition + 1) |
/* ------------------------------------------------------------------ */ /* No multiple-assignment support, but PARSE VALUE instruction may be */ /* used to perform: */ /* */ /* * Multiple variable initialisation */ /* * Multiple variable assignment [even swap values without temps] */ /* ------------------------------------------------------------------ */ parse value 1 2 with VAR1 VAR2 parse value VAR1 VAR2 with VAR2 VAR1 /* ----------------------------- */ a = 1 ; b = 2 temp = a ; a = b ; b = temp /* ----------------------------- */ parse value 57 72 103 with alpha beta production parse value beta production alpha with alpha beta production |
/* ------------------------------------------------------------------ */ /* REXX is a typeless language: all data are strings. This means: */ /* */ /* * REXX has no notion of objects, or aggregate types like arrays */ /* * It does not support 'primitive' types, those usually mapped to */ /* hardware registers */ /* */ /* In order to support mathematical operations, however, strings in */ /* Base 10 format [containing 0-9, leading + or -, a decimal point, */ /* exponent indicator 'E' and exponent] are recognised as 'numeric' */ /* strings in such contexts [whilst hex and binary strings are not]. */ /* */ /* The benefit of this approach: */ /* */ /* * Simplifies interpreter implementation on new platforms */ /* * Implicit support for arbitrary precision arithmetic */ /* * Language kept simple - no declarations, casting or conversions */ /* */ /* A set of conversion BIF's is supplied to facilitate the conversion */ /* of strings to / from various numeric representations, though it is */ /* understood that this is not a type conversion, but a 'form' */ /* conversion, one that may facilitate data printing or storage: */ /* */ /* * C2D / D2C [Character to Decimal / vice versa] */ /* * C2X / X2C [Character to Hex / vice versa] */ /* * X2B / B2X [Hex to Binary / vice versa] */ /* ------------------------------------------------------------------ */ char = 'A' /* or: char = '41'X [ASCII] */ num = C2D(char) char = D2C(num) /* ----------------------------- */ char = 'e' say "Number" C2D(char) "is" char /* Number 101 is e */ /* ----------------------------- */ string = "ABCDE" ascii = C2X(string) /* ascii [hex]: 4142434445 */ string = X2C(ascii) /* string: ABCDE */ /* ----------------------------- */ /* Contents: 73616D706C65 */ ascii_character_numbers = C2X("sample") /* Output will now be: 73 61 6D 70 6C 65 */ out = "" ; acn = ascii_character_numbers do while acn <> NULL parse var acn token +2 acn out = out token end say STRIP(out) /* Output will now be: sample */ out = X2C(ascii_character_numbers) say out /* ----------------------------- */ hal = "HAL" ; ibm = "" do while hal <> NULL parse var hal token +1 hal ibm = ibm||D2C(C2D(token) + 1) end /* Output will now be: IBM */ say ibm |
/* ------------------------------------------------------------------ */ /* The task of reversing strings is easily and efficiently performed */ /* via the REVERSE BIF. Implementation of a palindome-checking routine*/ /* is probably best accomplished via its use since it involves a */ /* single function call, thus incurs minimal calling overhead. Since */ /* REXX is typically used as an interpreted language, it often becomes*/ /* a significant issue. Performance comparision of the following two */ /* 'isPalindrome' functions should clearly reveal it's impact. */ /* */ /* isPalindrome : procedure */ /* i = 1 ; j = LENGTH(ARG(1)) */ /* do until i >= j */ /* if SUBSTR(ARG(1),i,1) \= SUBSTR(ARG(1),j,1) ; then return FALSE*/ /* i = i + 1 ; j = j - 1 */ /* end */ /* return TRUE */ /* */ /* isPalindrome : procedure */ /* return REVERSE(ARG(1)) == ARG(1) */ /* */ /* The task of reversing words within a string can quite easily be */ /* accomplished in several ways: */ /* */ /* * PARSE instruction together with the stack operations PUSH and */ /* PARSE PULL [stack and queue structures are native to REXX, and */ /* are used for many diverse tasks including interprocess comms] */ /* */ /* * Word-oriented BIF's ['reverseWords' is a recursive function that */ /* uses two of these: DELWORD and WORD. Anyone familiar with LISP or*/ /* Scheme will note how they are being used like 'car' and cdr'] */ /* */ /* reverseWords : procedure */ /* if ARG(1) == "" then ; return "" */ /* return STRIP(reverseWords(DELWORD(ARG(1), 1, 1)) WORD(ARG(1), 1))*/ /* ------------------------------------------------------------------ */ string = "A horse is a horse, of course, of course !" /* Reverse string using REXX BIF */ revbytes = REVERSE(string) /* ----------------------------- */ /* Tokenise 'string', and place each token on stack */ do while string <> NULL parse var string token string push token end /* Build 'revwords' by extracting tokens from stack */ revwords = "" do while QUEUED() > 0 parse pull token revwords = revwords token end /* ----------------------------- */ string = 'Yoda said, "can you see this?"' /* Reverse the word order in a string [custom function - see header] */ revwords = reverseWords(string) say revwords /* ----------------------------- */ word = "reviver" /* Check whether string is palindrome [custom function - see header] */ is_palindrome = isPalindrome(word) |