AREXX USER'S REFERENCE MANUAL PART 1 TABLE OF CONTENTS INTRODUCTION....................................................1 1. Organization of this Document...............................1 1 Using this Manual.........................................2 2 Typographic Conventions...................................2 2. Future Directions...........................................2 CHAPTER 1. WHAT IS AREXX?.......................................3 1. Language Features...........................................3 2. ARexx on the Amiga..........................................4 3. Further Information.........................................4 CHAPTER 2. GETTING ACQUAINTED...................................5 1. Installing ARexx............................................5 1 ARexx and Workbench.......................................5 2 Installation..............................................5 3 Starting the Resident Process.............................6 4 Naming Conventions........................................6 5 The REXX: Directory.......................................6 2. Program Examples............................................7 CHAPTER 3. ELEMENTS OF THE LANGUAGE............................11 1. Format.....................................................11 2. Tokens.....................................................11 1 Comment Tokens...........................................11 2 Symbol Tokens............................................11 3 String Tokens............................................12 4 Operators................................................12 5 Special Character Tokens.................................13 3. Clauses....................................................14 1 Null Clauses.............................................14 2 Label Clauses............................................14 3 Assignment Clauses.......................................14 4 Instruction Clauses......................................15 5 Command Clauses..........................................15 6 Clause Classification....................................15 4. Expressions................................................16 1 Symbol Resolution........................................16 2 Order of Evaluation......................................16 5. Numbers and Numeric Precision..............................17 1 Boolean Values...........................................17 2 Numeric Precision........................................17 6. Operators..................................................18 1 Arithmetic Operators.....................................18 2 Concatenation Operators..................................20 3 Comparison Operators.....................................20 4 Logical (Boolean) Operators..............................21 7. Stems and Compound Symbols.................................21 i CHAPTER 3. ELEMENTS OF THE LANGUAGE (CONT).....................11 8. The Execution Environment..................................22 1 The External Environment.................................22 2 The Internal Environment.................................22 3 Input and Output.........................................23 4 Resource Tracking........................................24 CHAPTER 4. INSTRUCTIONS........................................25 1. ADDRESS....................................................25 2. ARG........................................................25 3. BREAK......................................................26 4. CALL.......................................................26 5. DO.........................................................27 6. DROP.......................................................28 7. ECHO.......................................................28 8. ELSE.......................................................28 9. END........................................................29 10. EXIT......................................................29 11. IF........................................................29 12. INTERPRET.................................................30 13. ITERATE...................................................30 14. LEAVE.....................................................31 15. NOP.......................................................31 16. NUMERIC...................................................31 17. OPTIONS...................................................32 18. OTHERWISE.................................................32 19. PARSE.....................................................33 1 Input Sources...........................................33 2 Templates...............................................34 20. PROCEDURE.................................................35 21. PULL......................................................35 22. PUSH......................................................36 23. QUEUE.....................................................37 24. RETURN....................................................37 25. SAY.......................................................38 26. SELECT....................................................38 27. SHELL.....................................................38 28. SIGNAL....................................................38 29. THEN......................................................39 30. TRACE.....................................................40 31. UPPER.....................................................40 32. WHEN......................................................41 CHAPTER 5. COMMANDS............................................43 1. Command Clauses............................................43 2. The Host Address...........................................44 3. The Command Interface......................................44 4. Using Commands in Macro Programs...........................45 5. Using ARexx with Command Shells............................45 6. Command Inhibition.........................................46 ii CHAPTER 6. FUNCTIONS...........................................47 1. Syntax and Search Order....................................47 1 Search Order.............................................47 2 Internal Functions.......................................48 3 Built-In Functions.......................................49 4 External Function Libraries..............................49 5 Function Hosts...........................................50 2. The Built-In Function Library..............................50 1 ABBREV().................................................51 2 ABS()....................................................51 3 ADDLIB().................................................51 4 ADDRESS()................................................51 5 ARG()....................................................52 6 B2C()....................................................52 7 BITAND().................................................52 8 BITCHG().................................................52 9 BITCLR().................................................53 10 BITCOMP()...............................................53 11 BITOR().................................................53 12 BITSET()................................................53 13 BITTST()................................................53 14 BITXOR()................................................54 15 C2B()...................................................54 16 C2D()...................................................54 17 C2X()...................................................54 18 CENTER() OR CENTRE()....................................55 19 CLOSE().................................................55 20 COMPRESS()..............................................55 21 COMPARE()...............................................55 22 COPIES()................................................55 23 D2C()...................................................56 24 DATATYPE()..............................................56 25 DELSTR()................................................56 26 DELWORD()...............................................57 27 EOF()...................................................57 28 ERRORTEXT().............................................57 29 EXISTS()................................................57 30 EXPORT()................................................57 31 FREESPACE().............................................58 32 GETCLIP()...............................................58 33 GETSPACE()..............................................58 34 HASH()..................................................58 35 IMPORT()................................................59 36 INDEX().................................................59 37 INSERT()................................................59 38 LASTPOS()...............................................59 39 LEFT()..................................................60 40 LENGTH()................................................60 iii 41 MAX()...................................................60 42 MIN()...................................................60 43 OPEN()..................................................60 44 OVERLAY()...............................................61 45 POS()...................................................61 46 PRAGMA()................................................61 47 RANDOM()................................................62 48 RANDU().................................................62 49 READCH()................................................62 50 READLN()................................................63 51 REMLIB()................................................63 52 REVERSE()...............................................63 53 RIGHT().................................................63 54 SEEK()..................................................63 55 SETCLIP()...............................................64 56 SHOW()..................................................64 57 SIGN()..................................................64 58 SPACE().................................................64 59 STORAGE()...............................................65 60 STRIP().................................................65 61 SUBSTR()................................................65 62 SUBWORD()...............................................66 63 SYMBOL()................................................66 64 TIME()..................................................66 65 TRACE().................................................67 66 TRANSLATE().............................................67 67 TRIM()..................................................67 68 UPPER().................................................67 69 VALUE().................................................68 70 VERIFY()................................................68 71 WORD()..................................................68 72 WORDINDEX().............................................68 73 WORDLENGTH()............................................68 74 WORDS().................................................69 75 WRITECH()...............................................69 76 WRITELN()...............................................69 77 X2C()...................................................69 78 XRANGE()................................................69 CHAPTER 7. TRACING AND INTERRUPTS..............................71 1. Tracing Options............................................71 2. Display Formatting.........................................72 1 Tracing Output...........................................72 2 Command Inhibition.......................................73 3. Interactive Tracing........................................73 1 Error Processing.........................................74 2 The External Tracing Flag................................74 4. Interrupts.................................................74 iv CHAPTER I. PARSING AND TEMPLATES...............................77 1. Template Structure.........................................77 1 Template Objects.........................................78 2 The Scanning Process.....................................78 2. Templates in Action........................................79 1 Parsing by Tokenization..................................79 2 Pattern Parsing..........................................80 3 Positional Markers.......................................80 4 Multiple Templates.......................................80 CHAPTER 9. THE RESIDENT PROCESS................................83 1. Command Utilities..........................................83 1 HI.......................................................83 2 RX.......................................................84 3 RXSET....................................................84 4 RXC......................................................84 5 TCC......................................................84 6 TCO......................................................84 7 TE.......................................................84 8 TS.......................................................84 2. Resource Management........................................85 1 The Global Tracing Console...............................85 2 The Library List.........................................85 3 The Clip List............................................86 CHAPTER 10. INTERFACING TO AREXX...............................89 1. Basic Structers............................................90 2. Designing a Command Interface..............................91 1 Receiving Command Messages...............................92 2 Result Fields............................................92 3 Multiple Host Processes..................................92 3. Invoking ARexx Programs....................................93 1 Message Packets..........................................93 2 Command Invocations......................................94 3 Function Invocations.....................................95 4 Search Order.............................................95 5 Extension Fields.........................................96 6 Interpreting the Result Fields...........................97 4. Communicating with the Resident Process....................97 1 Command (Action) Codes...................................97 2 Modifier Flags...........................................99 3 Result Fields...........................................100 5. External Function Libraries...............................100 1 Design Considerations...................................100 2 Calling Convention......................................101 3 Parameter Conversion....................................101 4 Returned Values.........................................101 6. Direct Manipulation of Data Structures....................102 v APPENDIX A. ERROR MESSAGES....................................103 APPENDIX B. LIMITS AND COMPATIBILITY..........................109 1 Limits.....................................................109 2 Compatibility..............................................109 APPENDIX C. THE AREXX SYSTEMS LIBRARY.........................111 1 Functional Groups..........................................111 2 Library Functions..........................................113 APPENDIX D. THE AREXX SUPPORT LIBRARY.........................127 1 ALLOCMEM()...............................................127 2 CLOSEPORT()..............................................127 3 FREEMEM()................................................128 4 GETARG().................................................128 5 GETPKT().................................................128 6 OPENPORT()...............................................128 7 REPLY()..................................................129 8 SHOWDIR()................................................129 9 SHOWLIST()...............................................129 10 STATEF()................................................130 11 WAITPKT()...............................................130 APPENDIX E. DISTRIBUTION FILES................................131 1 Directories................................................131 1 The :C Directory.........................................131 2 The :INCLUDE Directory...................................131 3 The :LIBS Directory......................................132 4 The :REXX Directory......................................132 5 The :TOOLS Directory.....................................132 6 Miscellaneous Files......................................132 2 Listings of Header Files...................................133 1 storage.h................................................133 2 rxslib.h.................................................139 3 rexxio.h.................................................142 4 errors.h.................................................144 GLOSSARY......................................................147 INDEX.........................................................151 vi INTRODUCTION Welcome to ARexx,an implementation of the REXX language for the Amiga computer. ARexx is a powerful programming took,but one which by virtue of its clean syntax and sparse vocabulary is also easy to learn and easy to use. 1 ORGANIZATION OF THIS DOCUMENT This document will attempt to fill the roles of User's Manual,Language Reference,and Programmer's Guide. The chapters that follow have been organized to provide a gently introduction to the language. Chapter 1,What is ARexx?,gives an overview of the ARexx language and its implementation of the Amiga. Chapter 2,Getting Acquainted,tells how to install ARexx on your Amiga and presents several example programs to illustrate the features of the language. Chapter 3,Elements of the Language,introduces the language structure and syntax. Chapter 4,Instructions,describes the action statements of ARexx. Chapter 5,Commands,describes the program statements used to communicate with external programs. Chapter 6,Functions,explains how functions are called and documents the Built- In Function library. Chapter 7,Tracing and Interrupts,describes the source level debugging features useful for developing and testing programs. Chapter 8,Parsing and Templates,describes the instructions used to extract words or fields from strings. Chapter 9,The Resident Process,describes the capabilities of the global communications and resources manager. Chapter 10,Interfacing to ARexx,describes how to design and implement an interface between ARexx and an external program. Appendix A,Error Messages,lists the error messages issued by the interpreter. Appendix B,Limits and Compatibility,discusses the compatibility of ARexx with the language standard. Appendix C,The ARexx Systems Library,documents the functions of ARexx systems library. Appendix D,The Support Library,documents the library of Amiga specific functions. Appendix E,Distribution Files,lists the files on the distribution disk. Finally,a Glossary and an Index are provided. 1 USING THIS MANUAL If you are new to the REXX language,or perhaps to programming itself,you should review chapters 1 through 4 and then play with ARexx by running some of the sample programs given in chapter 2. Further examples are available in the :rexx directory of the distribution disk. If you are already familiar with REXX you may wish to skip directly to chapter 5,which begins to present some of the system-dependent features of this implementation. A summary of the compatibility of ARexx with the language definition is contained in Appendix B. TYPOGRAPHIC CONVENTIONS Describing a language is sometimes difficult because of the multiple and changing contexts involved. To help clarify the presentation here,a simply typographic convention has been adopted throughout the document. All of the terms and words specific to the REXX language,as well as the program examples and computer input and output,have been set in typewriter font like this. This should help to distinguish the language keywords and examples from the surrounding text. 2 FUTURE DIRECTIONS ARexx,like most software products,will probably envolve somewhat over the next few years as new features are added,old bugs are removed,and market imperatives become more apparant. While the core language will probably undergo few modifications,many capabilities will be added to the function libraries supported by ARexx. Your comments and suggestions for improvements to ARexx are most welcome. The author sincerely hopes that other software developers will consider using ARexx with their products. The advantages of having a rich variety of software products sharing a common user interface and a common procedural interface cannot be overstated. This is the underlying promise of the Amiga's multitasking capability,and that which most sets it apart from other inexpensive computers. Example Programs. One of the best ways to learn a computer language is to study examples written by more experienced programmers. The ARexx distribution disk includes a few example programs in the :rexx directory,and more programs will be added in future releases. If you have written REXX language program(for any computer)that you think would be of interest to a more general audience,please send it to the author for consideration. Programs should be of interest either in terms of their specific funtionality or as an example of programming technique. Each program submitted should include an author credit and a few lines of commentary on its intended fuction. 2 ARexx is a high-level language useful for prototyping,software integration,and general programming tasks. It is an implementation of the REXX language described by M.F. Cowlishaw in The REXX Language:A Practical Approach to Programming(Prentice-Hall,1985),and follow the language definition closely. ARexx is particularly well suited as a command language. Command programs, sometimes called "scripts" or "macros",are widely used to extend the predefined commands of an operating system or to customize an applications program. As a programming language,ARexx can be useful to a wide cross section of users. For the novice programmer,ARexx is an easy-to-learn yet powerful language that serves as a good introduction to programming techniques. Its source-level debugging facilities will help take some of the mystery out of how programs work(or don't work,as is more frequently the case.) For the more sophisticated user,ARexx provides the means to build fully integrated software packages,combining different applications programs into an environment tailored to their needs. A common command language among applications that support ARexx will bring uniformity to procedural interfaces, much as the Amiga's Intuition provides uniformity in the graphical interface. Finally,for the software developer,ARexx offers a straightforward way to build fully programmable applications programs. Developers can concentrate their efforts on making the basic operations of their programs fast and efficient,and let the end user add the frills and custom features. 1-1 LANGUAGE FEATURES Some of the important features of the language are: TYPELESS DATA. Data are treated as typeless character strings. Variables do not have to be declared before being used,and all operations dynamically check the validity of the operands. COMMAND INTERFACE. ARexx programs can issue commands to external programs that provide a suitable command interface. Any software package that implements the command interface is then fully programmable using ARexx,and can be extended and customized by the end user. TRACING AND DEBUGGING. ARexx includes source-level debugging facilities that allow the programmer to see the step-by-step actions of a program as it runs, thereby reducing the time required to develop and test programs. An internal interrupt system permits special handling of errors that would otherwise cause the program to terminate. 3 INTERPRETED EXECUTION. ARexx programs are run by an interpreter,so separate compilation and linking steps are not required. This makes it especially useful for prototyping and as a learning tool. FUNCTION LIBRARIES. External function libraries can be used to extend the capabilities of the language or as bridges to other programs. Libraries also allow ARexx programs to be used as "test drivers" for software development and testing. AUTOMATIC RESOURCE MANAGEMENT. Internal memory allocation related to the creation and destruction of strings and other data structures is handled automatically. 1-2 AREXX ON THE AMIGA ARexx was designed to run on the Amiga,and makes use of many of the features of its multitasking operating system. ARexx programs run as separate tasks and may communicate with each other or with external programs. The interpreter follows the design guidelines expected of well-behaved programs in a multitasking environment: specifcally,it uses as little memory as possible and is careful to reutrn resources to the operating system when they are no longer needed. Memory requirements were minimized by implementing the entire ARexx system as a shared library,so that only one copy of the program code must be loaded. 1-3 FURTHER INFORMATION The aforementioned book by M.F. Cowlishaw is highly recommended to those interested in further information about REXX. It presents an interesting discussion of the design and development of the language. 4 CHAPTER 2 GETTING ACQUAINTED This chapter explains how to install ARexx on your Amiga computer and shows some example programs. 2-1 INSTALLING AREXX ARexx requires an Amiga computer with at least 256k of memory,and will operate under V1.1 or V1.2 of the operating system. It uses the double-precision math library called "mathieeedoubbas.library" that is supplied with the Amiga WorkBench disk,so make sure that this file is present in your LIBS: directory. The distribution disk includes the language system,some example programs,and a set of the INCLUDE files required for integrating ARexx with other software packages. The distribution files are listed in Appendix E. AREXX AND WORKBENCH ARexx can be installed and loaded from within the icon-based environment provided by the Amiga WorkBench. However,it is a primarily a text-oriented language system and requires a good text editor and file management environment to be most effective. Unless you purchased ARexx as part of an applications package that includes an integrated editor,you'll probably find it useful to become familiar with the Commmand Line Interface (CLI)environment on the Amiga. INSTALLATION The ARexx language system consists of a shared library,a resident program,and several command utilities. All of the required files are contained in the :c and :libs directories of the distribution disk. ARexx may be installed on any of the system disks with which it will be used,but first check the :c and :libs directores of each disk to make sure that there are no naming conflicts. The following steps will then install ARexx on the system disk,provided that two disk drives are available: 1. Activate a CLI window. 2. Copy the ARexx :libs directory to the system LIBS: directory with the command "copy df1:libs to libs:". 3. Copy the ARexx :c directory to the system C: directory with the command "copy df1:C to c:". SINGLE DRIVE SYSTEMS. Installing software in a single-drive system can be very confusing,so an installation utility has been provided with the ARexx distribution disk. It copies the :c and :libs directories of the distribution disk into memory,and then prompts the user to insert each disk that is to receive the files. Follow these steps to run the installation utility: 5 1. Activate a CLI window. 2. Insert the distribution disk into drive 0 and type "df0:rxinstall". 3. At the program prompt,insert the system disk on which ARexx is to be installed into drive 0. 4. Repeat step 3 as required. STARTING THE RESIDENT PROCESS ARexx programs are launched by a background program called the resident process. It can be started by issuing the command rexxmast and must be active before any ARexx programs can be run. The rexxmast program briefly displays a small window to announce itself,and then disappears into the background to await your next request. If you will be using ARexx frequently,you can place the rexxmast command in the "startup-sequence" file that resides in the system S: directory. This will start the resident process automatically when you reboot the computer. After the resident process has been loaded,ARexx programs can be run from the CLI by typing the command rx followed by the program name and any arguments. For example,the sample program calc.rexx,which evaluates an expression,could be run by typeing "rx :rexx/calc 1+1." You may not need to start up the resident process if you are using a software package that starts it automatically. Applications that use ARexx can test whether the resident process is active by checking for a public message port named "REXX." If the port hasn't been opened,the program can issue the rexxmast command directly. The resident process can be closed using the command rxc;it will then exit as soon as the last ARexx program finishes execution. Unless you are very short on memory space,there is usually no reason to close ARexx,as it simply waits in the background for the next program to run. NAMING CONVENTIONS ARexx programs can be named anything,but adopting a simple naming convention will make managing the programs much easier. Programs to be run from the CLI are usually given the file extention .rexx to distinguish them from programs written in other languages. Programs written as "macros" or "scripts" for a particular applications program should be given a file extension specific to that program. For example,a macro written for a communications program called "MyComm" might be named "download.myc". ARexx uses this file extention when it searches for a program file to be executed. THE REXX: DIRECTORY You can designate one directory as the system-wide source for ARexx programs by defining a REXX: "device" with the assign command. This directory should reside on a volume that is usually mounted,such as SYS: or a hard disk. For example, the command "assign rexx: sys:rexx" defines the REXX: device as the :rexx directory on the system disk. Once defined,the REXX: device is searched after the current directory when looking for an ARexx program. 6 2-2 PROGRAM EXAMPLES Before introducing the structure and syntax of the language,let's look at a few examples of ARexx programs. Readers familiar with other high-level programming languages should find many points of similarity between ARexx and other languages. In the examples that follow,new terms are highlighted in the text as they are introduced,and will be convered in depth in the next few chapters. These short programs can be created using any text editor and then run from the Command Line Interface (CLI),or may simply be read as samples of the language. If the examples are to be run,first complete the installation procedures outlined in the previous section,and then start the ARexx resident process. Example programs can then be run by entering,for example,"rx age" at the CLI prompt. We'll begin with a "Hello,World" program that simply displays a message on the console screen. /* A simple program */ say 'Hello,World' This program consists of a comment line that describes the program and an instruction that displays text on the console. For historical reasons, ARexx programs begin with a comment line;the initial "/*" says "I'm an ARexx program" to the interpreter when it searches for a program. Instructions are language statements that denote a certain action to be performed,and always start with a symbol,in this case the word say. Symbols are translated to uppercase when the program is run,so the symbol say here is equivalent to SAY. Following say is an example of a string,which is a series of characters surrounded by quotes ('). Double quotes (") could also have been used to define the string. In the next program we'll display a prompt for input and then read some information from the user. /* Calculate age in days */ say 'Please enter your age' pull age say 'You are about' age*365 'days old' This program uses the pull instruction to read a line of input into a variable called age,which is then used with a say instuction. Variables are symbols that may be assigned a value. The words following say form an expression in which strings are joined and an arithmetic calculation is performed. Note that the variable age did not have to be declared as a number;instead,its value was checked when it was actually used in the expression. To see what would happen if age wasn't a number,try rerunning the program with a non-numeric entry for the age. The resulting error message shows the line number and type of error that occurred,after which the program ends. 7 The next program introduces the do instruction,which allows program statements to be executed repeatedly. It also illustrates the exponentiation operator, which is used to raise a number to an integral power. /* Calculate some squares and cubes */ do i = 1 to 10 /* 10 interations */ say i i**2 i**3 /* calculations */ end say ' all done ' The do instruction causes the statements between the do and end instructions to be executed 10 times. The variable i is the index variable for the loop,and is incremented by 1 for each iteration. The number following the symbol to is the limit for the do instruction,and could have been a full expression rather than just the constant 10. Note that the statements within the loop have been indented. This is not required by the language,but it makes the program more readable and is therefore good programming practice. The subject of the next example is the if instruction,a often-used control statement that allows statements to be conditionally executed. The numbers from 1 to 10 are classified as even or odd by dividing them by 2 and then checking the remainder. /* Even or odd? */ do i = 1 to 10 if i//2 = 0 then type = 'even' else type = 'odd' say i 'is' type end This example intoduces the // arithmetic operator,which calculates the remainder after a division operation. The if instruction tests whether the remainder is 0 and executes the then branch if it is,thereby setting the variable type to "even." If the remainder was not 0,the alternative else branch is executed and type is set to "odd." The next example introduces the concept of a function,which is a group of statements that can be executed by mentioning the function name in a suitable context. Functions are an important part of most programming languages,as they allow large,complex programs to be built from smaller modules. Functions are specified in an expression as a name followed by an open parenthesis. One or more expressions called arguments may follow the parenthesis;these are used to pass information to the function for processing. 8 /* Defining and calling a function */ do i = 1 to 5 say i square(i) /* call square */ end exit /* all done */ square: /* function name */ arg x /* get the "argument" */ return x**2 /* square it and return*/ The function square is defined in the lines followed the label square: up through the return instruction. Two new instructions are introduced here: arg retrieves the value of the argument string,and return passes the functon's result back to the point where the function was called. One final example will suffice for now. A new instruction called trace is used here to activate the tracing features of ARexx. /* Demonstrate "results" tracing */ trace results sum=0;sumsq=0; do i = 1 to 5 sum = sum + i sumsq = sumsq + i**2 end say 'sum=' sum 'sumsq=' sumsq When this program is run,the console displays the source lines as they are excuted,and shows the final results of expressions. This makes it easy to tell what the program is really doing,and helps reduce the time required to develop and test a new program. One minor point is illustrated here: the third line shows two distinct statements separated by a semicolon (;). The semicolon is an example of a special character,characters that have particular meanings within ARexx programs. The following chapters will present further information on the language statements illustrated here and will introduce others that have not been shown. Take heart,though;ARexx is a "small" language and there are relatively few words and rules to learn. 9 THERE WAS NO PAGE 10 ELEMENTS OF THE LANGUAGE This chapter introduces the rules and concepts that make up the REXX language. The intent is not to present a formalized definition,but rather to convey a practical understanding of how the language elements "fit together" to form programs. 3-1 FORMAT ARexx programs are compossed of ASCII characters and may be created using any text editor. No special formatting of the program statements is required or imposed on the programmer. 3-2 TOKENS The smallest distinct entities or "words" of the language are called tokens. A token may be series of characters,as in the symbol MyName,or just a single character like the "+" operator. Tokens can be categorized into comments, symbols,strings,operators,and special characters. Each of these groups are described below. COMMENT TOKENS Any group of characters beginning with the sequence "/*" and ending with "*/" defines a comment token. Comments may be placed anywhere in a program and cost little in terms of execution speed,since they are stripped(removed)when the program is first scanned by the interpreter. Comments may be "nested" within one another,but each "/*" must have a matching "*/" in the program. Examples: /* Your basic comment */ /* a /* nested! */ comment */ SYMBOL TOKENS Any group of the characters a-z,A-Z,0-9,and .!?$_defines a symbol token. Symbols are translated to uppercase as the program is scanned by the interpreter,so the symbol MyName is equivalent to MYNAME. Four types of symbols are recognized: Fixed symbols begin with a digit (0-9) or a period(.). Simple symbols do not begin with a digit,and do not contain any periods. Stem symbols have exactly one period at the end of the symbol name. Compound symbols include one or more periods in the interior of the name. Stems and compound symbols have special properties that make them useful for building arrays and lists. 11 SYMBOLS VALUES. The value used for a fixed symbol is always the symbol name itself(as translated to uppercase.) Simple,stem,and compound symbols are called variables and may be assigned a value;the value used for an uninitialized variable is just the variable name itself. Examples: 123.45 /*a fixed symbol */ MyName /*same as MyName */ a. /*a stem symbol */ a.1.Index /*a compound symbol */ STRING TOKENS A group of characters beginning and ending with a quote (')or double quote(") delimiter defines a string token. The delimiter character itself may be included within the string by a double-delimiter sequence (" or ""). The number of characters in the string is called its length,and a string of length zero is called a null string. A string is treated as a literal in an expression;that is,its value is just the string itself. Strings followed immediately by an "X" or "B" character that is not part of a longer symbol are classified as hex or binary strings,respectively,and must be composed of hexadecimal digits(0-9,A-F) or binary digits(0,1). Blanks are permitted at byte boundaries for added readability. Hex and binary strings are convenient for specifying non-ASCII characters and for machine-specific information like addresses in a program. They are converted immediately to the "packed" internal form. Examples: "Now is the time" /*a simple example */ "" /*a null string */ 'Can't you see??' /*Can't you see?? */ '4A 3B CO'X /*a hex string */ '00110111'b /*binary for '7' */ OPERATORS The characters +-*/=><*| may be combined in the sequences shown in Table 3.1 to form operator tokens. Operator sequences may include leading,trailing,and embedded blanks,all of which are removed when the program is scanned. In addition to the above characters,the blank character as a concatenation operator if it follows a symbol or string and is not adjacent to an operator or special character. Each operator has an associated priority that determines the order in which operations will be performed in an expression. Operators with higher priorities before those with lower priorities. 12 TABLE 3.1 OPERATOR SEQUENCES SEQUENCE PRIORITY OPERATOR DEFINITION 8 Logical NOT + 8 Prefix Conversion - 8 Prefix Negation ** 7 Exponentiation * 6 Multiplication / 6 Division % 6 Integer Division // 6 Remainder + 5 Addition - 5 Subtration || 4 Concatenation (blank) 4 Blank Concatenation == 3 Exact Equality ~== 3 Exact Inequality = 3 Equality ~= 3 Inequality > 3 Greater Than >=,~< 3 Greater Than or Equal To < 3 Less Than <=,~> 3 Less Than or Equal To & 2 Logical AND | 1 Logical Inclusive OR ^,&& 1 Logical Exclusive OR SPECIAL CHARACTER TOKENS The characters :():,are each treated as a separate special character token and have particular meanings within an ARexx program. Blanks adjacent to these special characters are removed,except for those preceding an open parenthesis or following a close parenthesis. COLON (:). A colon,if preceded by a symbol token,defines a label within the program. Lavels are locations in the program to which control may be transferred under various conditions. OPENING AND CLOSING PARENTHESES (()). Parentheses are used in expressions to group operators and operands into subexpressions,in order to override the normal operator primorities. An open parenthesis also serves to identify a function call within an expression;a symbol or string followed immediately by an open parenthesis defines a function name. Parentheses must always be balanced within a statement. SEMICOLON (;). The semicolon acts as a program statement terminator. Several statements may be placed on a single source line if separated by semicolons. 13 COMMA (,). A comma token acs as the continuation character for statements that must be entered on several source lines. It is also used to separate the argument expressions in a function call. 3-3 CLAUSES Tokens are grouped together to form clauses,the smallest language unit that can be executed as a statement. Every clause in ARexx can be classified as either a null,label,assignment,instruction,or command clause. The classification process is very simple,since no more than two tokens are required to classify any clause. Assignment,instruction,and command clauses are jointly termed statements. CLAUSE CONTINUATION. The end of a source line normally acts as the implicit end of a clause. A clause can be continued on the next source line by ending the line with a comma (,). The commas is then removed,and the next line is considered as a continuation of the clause. There is no limit to the number of continuations that may occur. String and comment tokens are automatically continued if a line end before the closing delimiter has been found,and the "newline" character is not considered to be part of the token. MULTIPLE CLAUSES. Several clauses can be placed on a single line by separating them with semicolons(;). NULL CLAUSES Lines consisting only of blanks or comments are called null clauses. They have no function in the execution of a program,except to aid its readability and to increment the source line count. Null clauses may appear anywhere in a program. Example: /* perform annuity calculations */ LABEL CLAUSES A symbol followed immediately by a colon defines a label clause. A label acts as a placemarker in the program,but no action occurs with the "execution" of a label. The colon is considered as an implicit clause terminator,so each label stands as a separate clause. Label clauses may appear anywhere in a program. Examples: start: /* begin execution */ syntax: /* error processing */ ASSIGNMENT CLAUSES Assignments are identified by a variable symbol followed by an "=" operator. In this context the operator's normal definition(an equality comparison)is overridden,and it becomes an assignment operator. The tokens to the right of the "=" are evaluated as an expression,and the result is assigned to(becomes the value of)the variable symbol. 14 Examples: when= 'Now is the time' answ= 3.14 * fact(5) INSTRUCTION CLAUSES Instructions begin with certain keyword symbols,each of which denotes a particular action to be performed. Instruction keywords are recognized as such only at the beginning of a clause,and may otherwise be used freely as symbols (although such use may become confusing at times.) The ARexx instructions are described in detail in Chapter 4. Examples: drop a b c /* reset variables */ say 'please' /* a polite program */ if j > 5 then leave; /* several instructions */ COMMAND CLAUSES Commands are any ARexx expression that can't be classified as one of the preceding types of clauses. The expression is evaluated and the result is issued as a command to an external host,which might be the native operating system or an application program. Commands are discussed in Chapter 5,and the details of the host command interface are given in Chapter 10. Examples: 'delete' 'myfile' /* a DOS command */ 'jump' current+10 /* an editor command? */ CLAUSE CLASSIFICATION The process by which program lines are divided into clauses and then classified is important in understanding the operation of an ARexx program. The language interpreter splits the program source into groups of clauses as the program is read,using the end of each line as a clause separator and applying the continuation rule as required. These groups of one or more clauses are then tokenized,and each clause is classified into one of the above types. Note that seemingly small syntactic differences may completely change the semantic content of a statement. For example, SAY 'Hello, Bill' is an instruction clause and will display "Hello, Bill" on the console,but ""SAY 'Hello, Bill' is a command clause,and will issue "SAY Hello, Bill" as a command to an external program. The presence of the leading null string changes the classification from an instruction clause to a command clause. 15 3-4 EXPRESSIONS Expression evaluation is an important part of ARexx programs,since most statements include at least one expression. Expressions are composed of strings,symbols,operators,and parentheses. Strings are used as literals in an expression;their value in an operation is just the string itself. Fixed symbols are also literals(remember that symbols are always translated to uppercase,) but variable symbols may have an assigned value. Operator tokens represent the predefined operations of ARexx;each operator has an associated priority that determines the order in which operations will be performed. Parentheses may be used to alter the normal order of evaluation in the expression,or to identify function calls. A symbol or string followed immediately by an open parenthesis defines the function name,and the tokens between the opening and(final)closing parenthesis form the argument list for the function. For example,the expression "J 'fractorial is' fact(J)" is composed of a symbol J,a blank operator,the string 'factorial is',another blank,the symbol fact,an open parenthesis,the symbol J again,and a closing parenthesis. FACT is a function name and (J) is its argument list,in this case the single expression J. SYMBOL RESOLUTION Before the evaluation of an expression can proceed,the interpreter must obtain a value for each symbol in the expression. For fixed symbols the value is just the symbol name itself,but variable symbols must be looked up in the current symbol table. In the example above,the expression after symbol resolution would be "3 'factorial is' FACT(3)," assuming that the symbol J had the value 3. Suppose that the example above had been "FACT(J) 'is' J 'factorial'." Would the second occurrence of symbol J still resolve to 3 in this case? In general, function calls may have "side effects" that include altering the values of variables,so the value of J might have been changed by the call to FACT. In order to avoid ambiguities in the values assigned to symbols during the resolution process, ARexx guarantees a strict left-to-right resolution order. Symbol resolution proceeds irrespective of operator priority or parenthetical grouping;if a function call is found,the resolution is suspended while the function is evaluated. Note that it is possible for the same symbol to have more than one value in an expression. ORDER OF EVALUATION After all symbol values have been resolved,the expression is evaluated based on operator priority and subexpression grouping. Operators of higher priority are evaluated first. ARexx does not guarantee an order of evaluation among operators of equal priority,and does not employ a "fast path" evaluation of boolean operators. For example,in the expression (1 = 2) & (FACT(3) = 6) the call to the FACT function will be made,although it is clear that the final result will be 0,since the first term of the AND operation is 0. 16 3-5 NUMBERS AND NUMERIC PRECISION An important class of operands are those representing numbers. Numbers consist of the characters 0-9,.+-,and blanks;an e or E may follow a number to indicate exponential notation,in which case it must be followed by a (signed) integer). Both string tokens and symbol tokens may be used to specify numbers. Since the language is typeless,variables do not have to be declared as "numeric" before being used in an arithmetic operation. Instead,each value string is examined when it is used to verify that it represents a number. The following examples are all valid numbers: 33 " 12.3 " 0.321e12 ' + 15.' Note that leading and trailing blanks are permitted,and that blanks may be embedded between a "+" or "-" sign and the number body(but not within the body.) BOOLEAN VALUES The numbers 0 and 1 are used to represent the boolean values False and True, respectively. The use of a value other than 0 or 1 when a boolean operand is expected will generate an error. Any number equivalent to 0 or 1,for example "0.000" or "0.1E1," is also acceptable as a boolean value. NUMERIC PRECISION ARexx allows the basic precision used for arithmetic calculations to be modified while a program is executing. The number of significant figures used in arithmetic operations is determined by the Numeric Digits environment variable, and may be modified using the NUMERIC instruction. The number of decimal places used for a result depends on the operation performed and the number of decimal places in the operands. Unlike many languages,ARexx preserves trailing zeroes to indicate the precision of the result. If the total number of digits required to express a value exceeds the current Numeric Digits setting,the number is formatted in exponential notation. Two such formats are provided: In SCIENTIFIC notation,the exponent is adjusted so that a single digit is placed to the left of the decimal point. in ENGINEERING notation,the number is scaled so that the exponent is a multiple of 3 and the digits to the left of the decimal point range from 1 to 999. The numeric precision and format can be set using the NUMERIC instruction. 17 3-6 OPERATORS Operators can be grouped into four categories: Arithmetic operators require one or two numeric operands,and produce a numeric result. Concatenation operators join two strings into a single string. Comparison operators require two operands,and produce a boolean(0 or 1) result. Logical Operators require one or two boolean operands,and produce a boolean result. ARITHMETIC OPERATORS The aritmetic operators are listed in Table 3.2 below. Note the inclusion of the integer division(%)and remainder(//)operators,along with the usual arithmetic operations. The result of an arithmetic operation is always foratted based on the current Numeric Digits setting,and will never have leading or trailing blanks. TABLE 3.2 ARITHMETIC OPERATORS SEQUENCE PRIORITY OPERATION + 8 Prefix Conversion - 8 Prefix Negation ** 7 Exponentiation * 6 Multiplication / 6 Division % 6 Integer Division // 6 Remainder + 5 Addition - 5 Subtraction PREFIX CONVERSION(+). This unary operator converts the operand to and internal numeric form and formats the result based on the current Numeric Digits settings. This causes any leading and trailing blanks to be removed,and may result in a loss of precision. Examples: ' 3.12 ' ==> 3.12 1.5001 ==> 1.500 /* If digits = 3 */ PREFIX NEGATION(-). This unary operator negates the operand. The result is formatted based on the current Numeric Digits setting. 18 Examples: -' 3.12 ' ==> -3.12 1.5E2 ==>-150 EXPONENTIATION(**). The left operand is raised to the power specified by the right operand,which must be an integer. The number of decimal places for the result is the product of the exponent and the number of decimal places in the base. Examples: 2**3 ==>8 3**-1 ==>.333333333 0.5**3 ==>0.125 MULTIPLICATION(*). The product of two numbers is computed. The number of decimal places for the result is the sum of the decimal places of the operands. Examples: 12 * 3 ==>36 1.5 * 1.50 ==>2.250 DIVISION(/). The quotient of two numbers is computed. The number of decimal places for the result depends on the current setting of the numeric DIGITS variable;the nuber is formatted to the maximum precision required. Examples: 6 / 3 ==>2 8 / 3 ==>2.66666667 INTEGER DIVISION(%). The quotient of two numbers is computed,and the integer part of the quotient is used as the result. Examples: 5 % 3 ==>1 -8 % 3 ==>-2 REMAINDER(//). The result is the remainder after the two operands are divided. The remainder for "a//b" is calculated as "a-(a%b)*b." If both operands are positive integers,this operation yields the usual "modulo" result. 19 Examples: 5 // 3 ==>2 -5 // 3 ==>-2 5.1 // 0.2 ==>0.1 ADDITION(+). The sum of two numbers is computed. The number of decimal places for the result is the larger of the decimal places of the operands. Examples: 12 + 3 ==>15 3.1 + 4.05 ==>7.15 SUBTRATION(-). The difference of two numbers is computed. As in the case of addition,the number of decimals places for the result is the larger of the decimal places of the operands. Examples: 12 - 3 ==>9 5.55 - 1.55 ==>4.00 CONCATENATION OPERATORS ARexx defines two concatenation operators,both of which require two operands. The first,identified by the operator sequence "||",joins two strings into a single string with no intervening blank. The second concatenation operation is identified by the blank operator,and joins the two operand strings with one intervening blank. An implicit concatenation operator is recognized when a symbol and a string are directly abutted in an expression. Concatenation by abuttal uses the "||" operator,and behaves exactly as though the operator had been provided explicitly. Examples: 'why me,' || 'Mom?' ==>why me,Mom? 'good' 'times' ==>good times one'two'three ==>ONEtwoTHREE COMPARISON OPERATORS Comparisons are performed in one of three modes,and always result in a boolean value(0 or 1.) Exact comparisons proceed character-by-character,including any leading blanks that may be present. String comparisons ignore leading blanks,and pad the shorter string with blanks if necessary. 20 Numeric comparisons first convert the operands to an internal numeric form using the current Numeric Digits setting,and then perform a standard arithmetic comparison. Except for the exact equality and exact inequality operators,all comparison operators dynamically determine whether a string of numeric comparison is to be performed. A numeric comparison is performed if both operands are valid numbers otherwise,the operands are compared as strings. TABLE 3.3 COMPARISON OPERATORS SEQUENCE PRIORITY OPERATION MODE == 3 Exact Equality Exact ~== 3 Exact Inequality Exact = 3 Equality String/Numeric ~= 3 Inequality String/Numeric > 3 Greater Than String/Numeric >=,~< 3 Greater Than or Equal String/Numeric < 3 Less Than String/Numeric <=,~> 3 Less Than or Equal String/Numeric LOGICAL (BOOLEAN) OPERATORS ARexx defines the four logical operations NOT,AND,OR,and Exclusive OR,all of which require boolean operands and produce a boolean result. Boolean operands must have values of either 0(False)or 1(True.) An attempt to perform a logical operation on a non-boolean operand will generate an error. TABLE 3.4 LOGICAL OPERATORS SEQUENCE PRIORITY OPERATION ~ 8 NOT(Inversion) & 2 AND | 1 OR ^,&& 1 Exclusive OR 3-7 STEMS AND COMPOUND SYMBOLS Stems and compound symbols have special properties that allow for some interesting and unusual programming. A compound symbol can be regarded as having the structure stem.n1.n2.n3...nk where the leading name is a stem symbol and each node n1...nk is a fixed or simple symbol. Whenever a compound symbol appears in a program,its name is expanded by replacing each node with its current value as a (simple) symbol. The value string may consist of any characters,including embedded blanks,and is not converted to uppercase. The result of the expansion is a new name that is used in place of the compound symbol. For example if j has the value of 3 and k has the value 7,then the compound symbol a.j.k will expand to A.3.7. Stem symbols provide a way to initialize a whole class of compound symbols. When an assignment is made to a stem symbol,it assigns that value to all possible compound symbols derived from the stem. Thus,the value of a compound symbol depends on the prior assignments made to itself or its associated stem. 21 Compound symbols can be regarded as a form of "associative" or "content- addressable" memory. For example,suppose that you needed to store and retrieve a set of names and telephone numbers. The conventional approach would be to set up two arrays NAME and NUMBER,each indexed by an integer running from one to the number of entries. A number would be "looked up" by scanning the name array until the given name was found,say in NAME.12,and then retrieving NUMBER.12. With compound symbols,the symbol NAME could hold the name to be looked-up,and NUMBER.NAME would then expand to NUMBER.Bill(for example),which be the corresponding number. Of course,compound symbols can also be used as conventional indexed arrays,with the added convenience that only a single assignment(to the stem)is required to initialize the entire array. 3-8 THE EXECUTION ENVIRONMENT The ARexx interpreter provides a uniform environment by running each program as a separate task(actually,as a DOS process)in the Amiga's multitasking operating system. This allows for a flexible interface between an external host program and the interpreter,as the host can either proceed concurrently with its operations or can simply wait for the interpreted program to finish. THE EXTERNAL ENVIRONMENT The external environment of a program includes its task(process)structure, input and output streams,and current directory. When each ARexx task is created,it inherits the input and output streams and current directory from its client,the external program that invoked the ARexx program. The current directory is used as the starting point in a search for a program or data file. EXTERNAL PROGRAMS. The external environment usually includes one or more external programs with which the ARexx program may communicate. Any program that supports a suitable interface can receive commands from ARexx programs. The command interface is discussed in Chapter 5. THE INTERNAL ENVIRONMENT The internal environment of an ARexx program consists of a static global structure and one or more storage environments. The global data values are fixed at a time the program is invoked,and include the argument strings,program source code,and static data strings. The storage environment includes the symbol table used for variable values,the numeric options,trace options,and host address strings. While the global environment is unique,there may be many storage environments during the course of the program execution. Each time an internal function is called a new storage environment is activated and initialized. The initial values for most fields are inherited from the previous environment,but values may be changed afterwards without affecting the caller's environment. The new environment persists until control returns from the function. 22 ARGUMENT STRINGS. A program may receive one or more argument strings when it is first invoked. These arguments persist for the duration of the program and are never altered. The number of arguments a program receives depends in part on the mode of invocation. ARexx programs invoked as commands normally have only one argument string,although the "command tokenization" option may provide more than one. A program invoked as a function can have any number of arguments if called as an internal function,but external functions are limited to a maximum of 15 arguments. The argument strings can be retrieved using either the ARG instruction or the ARG() Built-In function. ARG() can also return the total number of arguments,or the status(as "exists" or "omitted")of a particular argument. THE SYMBOL TABLE. Every storage environment includes a symbol table to store the value strings that have been assigned to variables. This symbol table is organized as a two-level stores entries for simple and stem symbols,and the secondary level is used for compound symbols. All of the compound symbols associated with a particular stem are stored in one tree,with the root of the tree held by the entry for the stem. Symbols are not entered into the table until an assignment is made to the symbol. Once created,entries at the primary level are never removed,even if the symbol subsequently becomes uninitialized. Secondary trees are released whenever an assignment is made to the stem associated with the tree. For the most part ARexx programmers need not be concerned with the details of storage environments except to understand what values are saved when a function is called. Applications developers who need to manipulate environment values should refer to the structure definitions in the INCLUDE files provided on the ARexx distribution disk. INPUT AND OUTPUT Most computer programs require some means of communicating with the outside world,either to accept input data or to pass along results. The REXX language includes only a minimal specification of input and output (I/O)operations, leaving the choice of additional functionality to the language implementor. This is in keeping with the design of many computer languages. For instance,the "C" language has no statements dedicated to I/O,but instead relies on a standardized set of I/O functions. ARexx extends the I/O facilities fo REXX by providing Built-In functions to manipulate external files. Files are referenced by a logical name associated with the file when it is first opened. The initial input and output streams are given the names STDIN and STDOUT. ARexx maintains a list of all of the files opened by a program and automatically closes them when the program finishes. There is no limit to the number of files that may be open simultaneously. 23 RESOURCE TRACKING ARexx provides complete tracking for all of the dynamically-allocated resources that it uses to execute a program. These resources include memory space,DOS files and related structures,and the message port structures supported by ARexx. The tracking system was designed to allow a program to "bail out" at any point(perhaps due to an execution error)without leaving any hanging resources. It is possible to go outside of the interpreter's resource tracking net by making calls directly to the Amiga's operating system from within an ARexx program. In these cases it is the programer's responsibility to track and return all of the allocated resources. ARexx provides a special interrupt facility so that a program can retain control after an execution error,perform the required cleanup,and then make an orderly exit. Chapter 7 has information on the ARexx interrupt system. 24 CHAPTER 4 INSTRUCTIONS Instruction clauses are identified by an initial keyword symbol that is not followed by a colon(:)or an equals(=)operator. Each instruction signifies a specific action,and may be followed by one or more subkeywords,expressions,or other instruction-specific information. Instruction keywords and subkeywords are recognized only in this specific context,and are therefore not "reserved words" in the usual sense of the term. Keywords may be used freely as variables or function names,although such usage may become confusing at times. In the descriptions that follow,keywords are shown in uppercase and optional parts of the instruction are enclosed in brackets. Alternative selections are separated by a vertical bar(|),and required alternative are enclosed in braces ({}). 4-1 ADDRESS Usage: ADDRESS [Symbol|string|VALUE] [expression]] This instruction specifies a host address for commands issued by the interpreter. A host address is the name associated with an external program to which commands can be sent;external hosts are described in Chapter 5. ARexx maintains two host addresses:a "current" and a "previous"address is lost,and the "current" and a "previous" value. Whenever a new host address is supplied, the "previous" address is lost,and the "current" address becomes the "previous" one. These host addresses are part of a program's storage environment and are preserved across internal function calls. The current address can be retrieved with the Built-In function ADDRESS(). There are four distinct forms for the ADDRESS instruction: ADDRESS {string | symbol} expression. The expression is evaluated and the result is issued to the host specified by the string or symbol,which is taken as a literal. No changes are made to the current or previous address strings. This provides a convenient way to issue a single command to an external host without disturbing the current host addresses. The return code from the command is treated as it would be from a command clause. ADDRESS {string | symbol}. The string or symbol,taken as a literal,specifies the new host address. The current host address becomes the previous address. ADDRESS [VALUE] expression. The result of the expression specifies the new host address,and the current address becomes the previous address. The VALUE keyword may be omitted if the first token of the expression is not a symbol or string. ADDRESS. This form interchanges the current and previous hosts. Repeated execution will therefore "toggle" between the two host addresses. Examples: address edit /* set an new host address */ address edit 'top' /* move to the top */ address VALUE edit n /* compute a new host address */ address /* swap current and previous */ 25 Usage: ARG [template] [,template...] ARG is shorthand form for the PARSE UPPER ARG instruction. It retrieves one or more of the argument strings available to the program,and assigns values to the variables in the template. The number of argument strings available depends on the whether the program was invoked as a command or a function. Command invocations normally have only one argument string,but functions may have up to 15. The argument strings are not altered by the ARG instruction. The structure and processing of templates is described briefly with the PARSE instruction,and in greater depth in Chapter 8. Example: arg first,second /* fetch arguments */ 4-3 BREAK Usuage: BREAK The BREAK instruction is used to exit from the range of a DO instruction or from within an INTERPRETed string,and is valid only in these contexts. If used within a DO statement,BREAK exits from the innermost DO statement containing the BREAK. This contrasts with the otherwise similar LEAVE instruction,which exits only from an interative DO. Example: do /* begin block */ if i>3 then break /* all done? */ a = a + 1 y.a = name end /* end block */ 4-4 CALL Usage: CALL {symbol | string} [expression] [,expression,...] The CALL instruction is used to invoke an internal or external function. The function name is specified by the symbol or string token,which is taken as a literal. Any expressions that follow are evaluated and become the arguments to the called function. The value returned by the function is assigned to the special variable RESULT. It is not an error if a result string is not returned; in this case the variable RESULT is DROPed(becomes uninitialized.) The linkage to the function is established dynamically at the time of the call. ARexx follows a specific search order in attempting to locate the called function;this process is described in Chapter 6. Example: call center name,length+4,'+' 26 Usage: DO [var=exp] [To exp] [BY exp]] [FOR exp] [FOREVER] [WHILE exp | UNTIL exp] The DO instruction begins a group of instructions to be executed as a block. The range of the DO instruction includes all statements up to and including an eventual END instruction. There are two basic forms of the instruction: The DO keyword by itself defines a block of instructions to be executed once. If any iteration specifiers follow the DO keyword,the block of instructions is executed repeatedly until a termination condition occurs. An interative DO instruction is sometimes called a "loop",since the interpreter "loops back" to perform the instruction repeatedly. The various parts of the DO instruction are described below. Initializer expression. An initializer expression of the form "variable= expression" defines the index variable of the loop. The expression is evaluated when the DO range is first activated,and the result is assigned to the index variable. On subsequent iterations an expression of the form "variable = variable + increment" is evaluated,where the increment is the result of the BY expression. If specified, the initializer expression must precede any of the other subkeywords. BY expression. The expression following a BY symbol defines the increment to be added to the index variable in each subsequent iteration. The expression must yield a numeric result,which may be positive or negative and need not be an integer. The default increment is 1. TO expression. The result of the TO expression specifies the upper(or lower) limit for the index variable. At each iteration the index variable is compared to the TO result. If the increment(BY result)is positive and the variable is greater than the limit,the DO instruction terminates and control passes to the statement following the END instruction. Similarly,the loop terminates if the increment is negative and the index variable is less than the limit. FOR expression. The FOR expression must yield a positive whole number when evaluated,and specifies the maximum number of iterations to be performed. The loop terminates when this limit is reached irrespective of the value of the index variable. FOREVER. The FOREVER keyword can be used if an iterative DO instruction is required but no index variable is necessary. Presumably the loop will be terminated by a LEAVE or BREAK instruction contained within the loop body. WHILE expression. The WHILE expression is evaluated at the beginning of each iteration and must result in a boolean value. The iteration proceeds if the result is 1;otherwise,the loop terminates. 27 UNTIL expression. The UNTIL expression is evaluated at the end of each iteration and must result in a boolean value. The instruction continues with the next iteration if the result is 0,and terminates otherwise. The initializer,BY,TO,and FOR expressions are evaluated only when the instruction is first activated,so the increment and limits are fixed throughout the execution. Note that a limit need not be supplied;for example,the instruction "DO i=1" will simply count away forever. Note also that only one of the WHILE or UNTIL keywords can be specified. Example: do i=1 to limit for 5 while time <50 y.1=i*time end 4-6 DROP Usage: DROP variable [variable...] The specified variable symbols are reset to their uninitialized state,in which the value of the variable is the variable name itself. It is not an error to DROP a variable that is already uninitialized. DROPping a stem symbol is equivalent to DROPping the values of all possible compound symbols derived from that stem. Example: a=123 /* assign a value */ drop a b /* drop some */ say a b /* ==>A B */ 4-7 ECHO Usage: ECHO [expression] The ECHO instruction is a synonym for the SAY instruction. It displays the expression result on the console. Example: echo "You don't SAY!" 4-8 ELSE Usage: ELSE [;] [conditional statement] The ELSE instruction provides the alternative conditional branch for an IF statement. It is valid only within the range of an IF instruction,and must follow the conditional statement of the THEN branch. If the THEN branch wasn't executed,the statement following the ELSE clause is performed. Binding. ELSE clauses always bind to the nearest(preceding)IF statement. It may be necessary to provide "dummy" ELSE clauses for the inner IF ranges of a compound IF statement in order to allow alternative branches for the outer IF statements. In this case it is not sufficient to follow the else with a semicolon or a null clause. Instead,the NOP(no-operation)instruction can be used for this purpose. 28 Example: if 1 > 2 then say 'really?' else say 'I thought so' 4-9 END Usage: END [variable] The END instruction terminates the range of a DO or SELECT instruction. If the optional variable symbol is supplied,it is compared to the index variable of the DO statement(which must therefore be iterative). An error is generated if the symbols do not match,so this provides a simple mechanism for matching the DO and END statements. Example: do i=1 to 5 /* index variable is I */ say i end i /* end "I" loop */ 4-10 EXIT Usage: EXIT [expression] The EXIT instruction terminates the execution of a program,and is valid anywhere within a program. The evaluated expression is passed back to the caller as the function or command result. Results Processing. The processing of the EXIT result depends on whether a result string was requested by the calling program,and whether the current invocation resulted from a command or function call. If a result string was requested,the expression result is copied to a block of allocated memory and a pointer to the block is returned as the secondary result of the call. If the caller did not request a result string,and the program was invoked as a command,then an attempt is made to convert the expression result to an integer. This value is then returned as the primary result,with 0 as the secondary result. This allows the EXIT expression to be interpreted as a "return code" by the caller. Refer to Chapter 10 for further information on the data structures used to return the result string. Examples: exit /* no result needed */ exit 12 /* an error return? */ 4-11 IF Usage: IF expression [THEN] [;] [conditional statement] The IF instruction is used in conjunction with THEN and ELSE instruction to conditionally execute a statement. The result of the expression must be a boolean value. If the result is 1 (True),the statement following the THEN symbol is executed;otherwise,control passes to the next statement(which might be an ELSE clause.) The THEN keyword need not immediately follow the IF expression,but may appear as a separate clause. The instruction 29 is actually analyzed as "IF expression; THEN; statement;." In essence,the IF statement begins a syntactic range and establishes the test condition that determines whether subsequent THEN or ELSE clauses will be performed. Any valid statement may follow the THEN symbol;in particular,a "DO; ... END;" group allows a series of statements to be performed conditionally. Examples: if result < 0 then exit /* all done? */ 4-12 INTERPRET Usage: INTERPRET expression The expression is evaluated and the result is executed as one or more program statements. The statements are considered as a group,as though surrounded by a "DO; ...;END" combination. Any statements can be included in the INTERPRETed source,including DO or SELECT instruction. An INTERPRET instruction activates a control range when it is executed,which serves as a "fence" for LEAVE and ITERATE instructions. These instructions can therefore be used only with DO-loops defined within the INTERPRET. The BREAK instuction can be used to terminate the processing of INTERPRETed statements. While it is not an error to include label clauses within the interpreted string,only those labels defined in the original source code are searched during a transfer of control. The INTERPRET instruction can be used to solve programming problems in interesting and novel ways. Programs can be constructed dynamically and then executed using this instruction,or program fragments may be passed as arguments to functions,which then INTERPRET them. Example: inst = 'say' /* an instruction */ interpret inst hello /* ..."say HELLO" */ 4-13 ITERATE Usage: ITERATE [variable] The ITERATE instruction terminates the current iteration of a DO instruction and begins the next iteration. Effectively,control passes to the END statement and then(depending on the outcome of the UNTIL expression)back to the DO statement. The instruction normally acts on the innermost iterative DO range containing the instruction. An error results if the LEAVE instruction is not contained within an iterative DO instruction. The optional variable symbol specifies which DO range is to be exited,in the event that several nested ranges exist. The variable is taken as a literal and must match the index variable of a currently active DO instruction. An error results if no such matching DO instruction is found. 30 Example: do i=1 to 3 if i=j then iterate i end 4-14 LEAVE Usage:LEAVE [variable] LEAVE forces an immediate exit from the iterative DO range containing the instruction. An error results if the LEAVE instruction is not contained within an iterative DO instruction. The optional variable symbol specifies which DO range is to be exited,in the event that several nested ranges exist. The variable is taken as a literal and must match the index variable of a currently active DO instruction. An error results if no such matching DO instruction is found. Example: do i=1 to limit if i > 5 then leave /* maximum iterations */ end 4-15 NOP Usage: NOP The NOP or "no-operation" instruction does just that:nothing. It is provided to control the binding of ELSE clauses in compound IF statements. Example: if i=j then /* first (outer) IF */ if j=k then a=o /* inner IF */ else nop /* binds to inner IF */ else a=a+1 /* binds to outer IF */ 4-16 NUMERIC Usage: NUMERIC {DIGITS | FUZZ} expression or: NUMERIC FORM {SCIENTIFIC | ENGINEERING} This instruction sets options relating to the numeric precision and format. The valid forms of the NUMERIC instruction are: NUMERIC DIGITS expression. Specifies the number of digits of precision for arithmetic calculations. The expression must evaluate to a positive whole number. NUMERIC FUZZ expression. Specifies the number of digits to be ignored in numeric comparison operations. This must be a positive whole number that is less than the current DIGITS setting. NUMERIC FORM SCIENTIFIC. Specifies that numbers that require exponential notation be expressed in SCIENTIFIC notation. The exponent is adjusted so that the mantissa (for non-zero) numbers) is between 1 and 10. This is the default format. 31 NUMERIC FORM ENGINEERING. Selects ENGINEERING format for numbers that require exponential notation. ENGINEERING format normalizes a number so that its exponent is a multiple of three and the mantissa(if not 0)is between 1 and 1000. The numberic options are preserved when an internal function is called. Examples: numeric digits 12 /* precision */ numeric form scientific /* format */ 4-17 OPTIONS Usage: OPTIONS [FAILAT expression] or: OPTIONS [PROMPT expression] or: OPTIONS [RESULTS] The OPTIONS instruction is used to set various internal defaults. The FAILAT expression sets the limit at or above which command return codes will be signalled as errors,and must evaluate to an integer value. The PROMPT expression provides a string to be used as the prompt with the PULL (or PARSE PULL)instruction. The RESULTS keyword indicates that the interpreter should request a result string when it issues commands to an external host. The internal options controlled by this instruction are preserved across function calls,so an OPTIONS instruction can be issued within an internal function without affecting the callers environment. If no keyword is specified with the OPTIONS instuction,all controlled options revert to their default settings. Example: options failat 10 options prompt "Yes Boss?" options results 4-18 OTHERWISE Usage: OTHERWISE [;] [conditional statement] This instruction is valid only within the range of a SELECT instruction,and must follow the "WHEN ... THEN" statements. If none of the preceding WHEN clauses have succeeded,the statement following the OTHERWISE instruction is executed. An OTHERWISE is not mandatory within a SELECT range. However,an error will result if the OTHERWISE clause is omitted and none of the WHEN instructions succeed. Example: select when i=1 then say 'one' when i=2 then say 'two' otherwise say 'other' end 32 4-19 PARSE Usage: PARSE [UPPER] inputsorce [template] [,template...] The PARSE instruction provides a mechanism to extract one or more substrings from a string and assign them to variables. The input string can come from a variety of sources,including argument strings,an expression,or from the console. The template provides both the variables to be given values and the way to determine the value strings. The template may be omitted if the instruction is intended only to create the input string. The different options of the instruction are described below. INPUT SOURCES The sources for the input strings are specified by the keyword symbols listed below. When multiple templates are supplied,each template receives a new input string, although for some source options the new string will be identical to the previous one. The input source string is copied before being parsed,so the original strings are never altered by the parsing process. UPPER. This optional keyword may be used with any of the input sources,and specifies that the input string is to be translated to uppercase before being parsed. It must be the first token following PARSE. ARG. This input option retrieves the argument strings supplied when the program was invoked. Command invocations normally have only a single argument string, but functions may have up to 15 argument strings. Multiple templates may be given to retrieve successive argument strings. EXTERNAL. The input strings is read from the console. If multiple templates are supplied,each template will read a new string. This source option is the same as PULL. NUMERIC. The current numeric options are placed in a string in the order DIGITS,FUZZ,and FORM,separated by a single space. PULL. Reads a string from the input console. If multiple templates are supplied,each template will read a new string. SOURCE. The "source" string for the program is retrieved. This string is formatted as "{COMMAND | FUNCTION} {0 | 1} called resolved ext host." The first token indicates whether the program was invoked as a command or as a function. The second token is a boolean flag indicating whether a result string was requested by the caller. The called token is the name used to invoke this program,while the resolved token is the final resolved name of the program. The ext token is the file extension to be used for searching(the default is "REXX"). Finally,the host token is the initial host address for commands. VALUE expression WITH. The input string is the result of the supplied expression. The WITH keyword is required to separate the expression from the template. The expression result may be parsed repeatedly by using multiple templates,but the expression is not reevaluated. VAR variable. The value of the specified variable is used as the input string. When multiple templates are provided,each template uses the current value of the variable. 33 This value may change if the variable is included as an assignment target in any of the templates. VERSION. The current configuation of the ARexx interpreter is supplied in the form "ARexx version cpu mpu video freq". The version toekn is the release level of the interpreter,formatted as V1.0. The cpu token indicates the processor currently running the program,and will be one of the values 68000,68010,or 68020. The mpu token will be either NONE or 68881 depending on whether a math coprocessor is available on the system. The video token will indicate either NTSC or PAL,and the freq token gives the clock(line)frequency as either 60HZ or 50 HZ. TEMPLATES Parsing is controlled by a template,which may consist of symbols,strings, operators,and parentheses. During the parsing operation the input string is split into substrings that are assigned to the variable symbols in the template. The process continues until all of the variables in the template have been assigned a value;if the input string is "used up",any remaining variables are given null values. Templates are described in depth in Chapter 8,so only a simplified description is presented here. The goal of the parsing operation is to associate a "current" and "next" position with each variable symbol in the template. The substring between these positions is then assigned as the value to the variable. There are three basic methods used to determine the value strings. PARSING BY TOKENIZATION. When a variable in the template is followed immediately by another variable,the value string is determined by breaking the input string into words separated by blanks. Each word is assigned to a variable in the template. Values determined by tokenization will never have leading or trailing blanks. Normally the last variable in the template receives the untokenized remainder of the input string,since it is not followed by a symbol. A "placeholder" symbol,signified by a period(.),may be used to force tokenization. Placeholders behave like variables in the template except that they are never actually assigned a value. Example: /* Numeric string is: "9 0 SCIENTIFIC" */ parse numberic digits fuzz form . say digits /* =>9 */ say fuzz /* =>0 */ say from /*=> SCEIENTIFIC */ PARSING BY POSITION. If the fields in the input string have known positions, value strings can be specified by absolute or relative positions. Relative positions are indicated by a number preceded by a "+" or "-" operator. Each positional marker updates the scan position in the string. The value assigned to a variable is the string from the current position up to,but not including, the next position in the string. 34 Example: /* assume argument is "1234567890" */ parse arg 1 a 3 b +2 1 c say a b c /* ==> 12 34 1234567890 */ PARSING WITH PATTERNS. Fields in the input string separated by specific characters or strings can be parsed using a pattern,which is matched against the input string. A pattern is specified in the template as a string token,or alternatively as a symbol enclosed in parentheses. The position in the parse string matched by the pattern determines the value strings. The pattern is removed from the input string when a match is found;this is the only parsing operation that modifies the input string. Example: check = 'one,two,three' parse var check a ',' b ',' c say a b c /* ==> one two three */ 4-20 PROCEDURE Usage:PROCEDURE [EXPOSE variable [variable...]] The PROCEDURE instruction is used within an internal function to create a new symbol table. This protects the symbols defined in the caller's environment from being altered by the execution of the function. PROCEDURE is usually the first statement within the function,although it is valid anywhere withing the function body. It is an error to execute two PROCEDURE statements within the function. EXPOSING VARIABLES. The EXPOSE subkeyword provides a selective mechanism for accessing the caller's symbol table,and for passing global variables to a function. The variables following the EXPOSE keyword are taken to refer to symbols in the caller's table. Any subsequent changes made to these variables will be reflected in the caller's environment. The variables in the EXPOSE list may include stems or compound symbols,in which case the ordering of the variables becomes significant. The EXPOSE list is processed from left to right,and compound symbols are expanded based on the values in effect in the new generation. For example,suppose that the value of the symbol J in the previous gneration is 123,and that J is unitialized in the new generation. Then PROCEDURE EXPOSE J A.J will expose J and A.123,whereas PROCEDURE EXPOSE A.J J will expose A. J. and J. Exposing a stem has the effect of exposing all possible compound symbols derived from that stem. Example: fact: procedure /* a recursive function */ arg i if i <=1 then return 1 else return i*fact(i-1) 35 Usage:PULL [template] [,template...] This is a shorthand form of the PARSE UPPER PULL instruction. It reads a string from the input console,translates it to uppercase,and parses it using the template. Multiple strings can be ready by supplying additional templates. The instruction will read from the console even if no template is given. Templates are described briefly with the PARSE instruction and in greater depth in Chapter 8. Example: pull first last. /* read names */ 4-22 PUSH Usage: PUSH [expression] The PUSH instruction is used to prepare a stream of data to be ready by a command shell or other program. It appends a "newline" to the result of the expression and then stacks or "pushes" it into the STDIN stream. Stacked lines are placed in the stream in "last-in, first-out" order,and are then available to be ready just as though they had been entered interactively. For example, after issuing the instructions push line 1 push line 2 push line 3 the steam would be read in the order "line 3," "line 2" and "line 1." There are several restrictions governing the use of the PUSH instruction and its alter ego QUEUE. These instructions use a special I/O mechanism to accomplish their task,and as a result can be used only with an interactive (stream-model) I/O device like a console or pipe. The stream must be managed by with a DOS handler that supports the special ACTION_STACK (for PUSH) or ACTION_QUEUE (for QUEUE) command. PUSH allows the STDIN stream to be used as a private scratchpad to prepare data for subsequent processing. For example,several files could be concatenated with delimiters between them by simply reading the input files,PUSHing the lines into the stream,and inserting a delimiter where required. Once the stacked lines are exhausted,the stream reverts to its normal source of data. Example: /* Stack commands for compile and link*/ push "blink c.o+main.o library amiga.lib to myprog" push "cc main" 36 4-23 QUEUE Usage:QUEUE [expression] The QUEUE instruction is used to prepare a stream of data to be read by a command shell or other program. It is very similar to the preceding PUSH instruction,and differs only that the data lines are placed in the STDIN stream in "first-in,first-out" order. In this case the instruction queue line 1 queue line 2 queue line 3 would be read in the order "line 1," "line 2," and "line 3." The QUEUEd lines always precede all interactivly-entered lines,and always follow any PUSHed (stacked)lines. The same restriction noted with the use of the PUSH instruction apply to the QUEUE instruction. The queueing mechanism uses the ACTION_QUEUE command,so the DOS handler associated with the STDIN stream must support this command. In most cases the choice of whether to use PUSH or QUEUE is just a matter of convenience or personal preference. Each of them provides a "scratch pad" facility similar to that provided by an I/O pipe,but useful within one program or task rather than just for interprocess communications. Example: /* Queue commands for compile and link */ queue "cc main" queue "blink c.o+main.o library amiga.lib to myprog" 4-24 RETURN RETURN is used to leave a function and return control to the point of the previous function invocation. The evaluated expression is returned as the function result. If an expression is not supplied,an error may result in the caller's environment. Functions called from within an expression must return a result string,and will generate an error if no result is available. Function invoked by the CALL instruction need not return a result. A RETURN issued from the base environment of a program is not an error,and is equivalent to an EXIT instruction. Refer to the EXIT instruction for a description of how result strings are passed back to an external caller. Example: return 6*7 /*the answer */ 37 4-25 SAY Usage:SAY [expression] The result of the evaluated expression is written to the output console,with a "newline" character appended. If the expression is omitted,a null string is sent to the console. Example: say 'The anwer is ' value 4-26 SELECT Usage:SELECT This instruction begins a group of instructions containing one or more WHEN clauses and possibly a single OTHERWISE clause,each followed by a conditional statement. Only one of the conditional statements within the SELECT group will be executed. Each WHEN statement is executed in succession until one succeeds;if none succeeds,the OTHERWISE statement is executed. The SELECT range must be terminated by an eventual END statement. Example: select when i=1 then say 'one' when i=2 then say 'two' otherwise say 'other' end 4-27 SHELL Usage:SHELL [symbol | string] [expression] The SHELL instruction is a synonym for the ADDRESS instruction. Example: shell edit /* set host to 'EDIT' */ 4-28 SIGNAL Usage: SIGNAL {ON |OFF} condition or: SIGNAL [VALUE] expression There are two forms of the SIGNAL instruction. The first form illustrated controls the state of the internal interrupt flags. Interrupts allow a program to detect and retain control when certain errors occur,and are discussed in Chapter 7. In this form SIGNAL must be followed by one of the keywords ON or OFF and one of the condition keywords listed below. The interrupt flag specified by the condition symbol is then set to the indicated state. The valid signal conditions are: BREAK_C A "control-C" break was detected. BREAK_D A "control-D" break was detected. BREAK_E A "control-E" break was detected. 38 BREAK_F A "control-F" break was detected. ERROR A Host command returned a non-zero code. HALT An external HALT request was detected. IOERR An error was detected by the I/O system. NOVALUE An uninitialized variable was used. SYNTAX A syntax or execution error was detected. The condition keywords are interpreted as labels to which control will transferred if the selected condition occurs. For example,if the ERROR interrupt is enabled and a command returns a non-zero code,the interpreter will transfer control to the label ERROR:. The condition label must of course be defined in the program;otherwise,an immediate SYNTAX error results and the program exits. In the second form of the instruction,the tokens following SIGNAL are evaluated as an expression. An immediate interrupt is generated that transfers control to the label specified by the expression result. The instruction thus acts as a "computed goto." INTERRUPTS. Whenever an interrupt occurs,all currently active control ranges (IF,DO,SELECT,INTERPRET,or interactive TRACE) are dismantled before the transfer of control. Thus,the transfer cannot be used to jump into the range of a DO-loop or other control structure. Only the control structures in the current environment are affected by a SIGNAL condition,so it is safe to SIGNAL from within an internal function without affecting the state of the caller's environment. SPECIAL VARIABLES. The special variable SIGL is set to the current line number whenever a transfer of control occurs. The program can inspect SIGL to determine which line was being executed before the transfer. If an ERROR or SYNTAX condition causes an interrupt,the special variable RC is set to the error code that triggered the interrupt. For the ERROR condition,this code is usually an error secerity level. The SYNTAX condition will always indicate an ARexx error code. Examples: signal on error /* enable interrupt */ signal off syntax /* disable SYNTAX */ signal start /* goto START */ 4-29 THEN Usage:THEN[;] [conditional statement] The THEN instruction must be the next statement following an IF or WHEN instruction,and is valid only in that context. It tests whether the preceding expression evaluated to 1(True),in which case the conditional statement following the THEN is performed. If the expression result was a 0(False),the conditional statement is skipped. 39 Example: if i=j then say 'equal' else say 'not equal' 4-30 TRACE Usage:TRACE [symbol|string|[[VALUE] expression]] The TRACE instruction is used to set the internal tracing mode. If a symbol or string is supplied,it is taken as a literal. Otherwise,the tokens following the VALUE keyword are evaluated as an expression. The VALUE keyword can be omitted if the expression doesn't start with a symbol or string token. In either case the result string is converted to uppercase and checked first for one of the "alphabetic" options. The valid alphabetic options are ALL, COMMANDS,ERRORS,INTERMEDIATES,LABELS,RESULTS,and SCAN. These can be spelled out in full or shortened to the initial character,and are described in Chapter 7. If the result doesn't match any of these options,the interpreter attempts to convert it to an integer. A conversion failure here will be reported as an error. PREFIX CHARACTERS. Two special symbol characters may precede any of the alphabetic keywords. The "?" character interactive tracing,and the "!" character controls command inhibition. These characters act as "toggles" to alternatively select and de-select the respective modes. Any number of prefix characters may precede an alphabetic option. Interactive tracing and command inhibition are described in Chapter 7. NUMERIC OPTION. If the specified trace option is a negative whole number,it is accepted as a trace suppression count. The suppression count is the number of clauses(that would otherwise be traced)to be passed over before resuming the tracing display. Suppression counts are ignored execept during interactive tracing. Examples: trace ?r /* interactive RESULTS */ trace off trace -20 /* skip 20 clauses */ 4-31 UPPER Usage:UPPER variable [variable...] The values of the variables in the list are converted to uppercase. It is not an error to include an uninitialized variable in the list,but it will be trapped if the NOVALUE interrupt has been enabled. The TRANSLATE() or UPPER() Built-In functions could also be used to convert variables to uppercase,but the instruction form is more concise(and faster) if several variables are being converted. 40 Example: when='Now is the time' upper when say when /* NOW IS THE TIME */ 4-32 WHEN Usage:WHEN expression [THEN [;] [conditional statement]] The WHEN instruction is similar to the IF instruction,but is valid only within a SELECT range. Each WHEN expression is evaluated in turn and must result in a boolean value. If the result is a 1,the conditional statement is executed and control passes to the END statement that terminates the SELECT. As in the case of the IF instruction,the THEN need not be part of the same clause. Example: select; when i1 say abbrev('almost','alm',4) ==>0 say abbrev('any','') ==>1 ABS() Usage:ABS(number) Returns the absolute value of the number argument,which must be numeric. Examples: say abs(-5.35) ==>5.35 say abs(10) ==>10 ADDLIB() Usage ADDLIB(name,priority,[offset,version]) Adds a function library or a function host to the Library List maintained by the resident process. The name argument specifies either the name of a function library or the public message port associated with a function host. The name is case-sensitive,and any libraries thus declared should reside in the system LIBS: directory. The priority argument specifies the search priority and must be an integer between 100 and -100,inclusive. The offset and version arguments apply only to libraries. The offset is the integer offset to the library's "query" entry point,and the version is an integer specifying the minimum acceptable release level of the library. The function returns a boolean result that indicates whether the operation was successful. Note that if a library is specified,it is not actually opened at this time;similarly,no check is performed as to whether a specified function host port has been opened yet. Example: say addlib("rexxsupport.library",0,-30,0)==>1 call addlib "EtherNet",-20 /* a gateway */ 51 ADDRESS() Usage:ADDRESS() Returns the current host address string. The host address is the message port to which commands will be sent. The SHOW()function can be used to check whether the required external host is actually available. See Also:SHOW() Example: say address() ==>REXX ARG() Usage:ARG([number],['Exists' | 'Omitted']) ARG()returns the number of arguments supplied to the current environment. If the number parameter alone is supplied,the corresponding argument string is returned. If a number and one of the keywords Exists or Omitted is given,the boolean return indicates the status of the corresponding argument. Note that the existence or omission test does not indicate whether the string has a null value,but only whether a string was supplied. Examples: /* Assume arguments were: ('one,,10) */ say arg() ==>3 say arg(1) ==>one say arg(2,'0') ==>1 B2C() Usage:B2C(string) Converts a string of binary digits(0,1)into the corresponding(packed)character representation. The conversion is the same as though the argument string had been specified as a literal binary string(e.g. '1010'B). Blanks are permitted in the string,but only at byte boundaries. This function is particularly useful for creating strings that are to be used as bit masks. See also:X2C() Examples: say b2c('00110011') ==>3 say b2c('01100001') ==>a BITAND() Usage:BITAND(string1,string2,[pad]) The argument strings are logically ANDed together,with the length of the result being the longer of the two operand strings. If a pad character is supplied,the shorter string is padded on the right;otherwise,the operation terminates at the end of the shorter string and the remainder of the longer string is appended to the result. Example: bitand('0313'x,'FFF0'x) ==>'0310'x 52 BITCHG() Usage:BITCHG(string,bit) Changes the state of the specified bit in the argument string. Bit numbers are defined such that bit 0 is the low-order bit of the rightmost byte of the string. Example: bitchg('0313'x,4) ==>'0303'x BITCLR() Usage:BITCLR(string,bit) Clears(sets to zero)the specified bit in the argument string. Bit numbers are defined such that bit 0 is the low-order bit of the rightmost byte of the string. Example: bitclr('0313'x,4) ==>'0303'x BITCOMP() Usage:BITCOMP(string1,string2,[pad]) Compares the argument strings bit-by-bit,starting at bit number 0. The returned value is the bit number of the first bit in which the strings differ,or -1 if the strings are identical. Examples: bitcomp('7F'x,'FF'x) ==>7 bitcomp('FF'x,'FF'x) ==>-1 BITOR() Usage:BITOR(string1,string2,[pad]) The argument strings are logically ORed together,with the length of the result being the longer of the two operand strings. If a pad character is supplied,the shorter string is padded on the right;otherwise,the operation terminates at the end of the shorter string and the remainder of the longer string is appended to the result. Example: bitor('0313'x,'003F'x) ==>'033F'x BITSET() Usage:BITSET(string,bit) Sets the specified bit in the argument string is 1. Bit numbers are defined such that bit 0 is the low-order bit of the rightmost byte of the string. Example: bitset('0313'x,2) ==>'0317'x BITTST() Usage:BITTST(string,bit) The boolean return indicates the state of the specified bit in the argument string. 53 Bit numbers are defined such that bit 0 is the low-order bit of the rightmost byte to the string. Example: bittst('0313'x,4) ==>1 BITXOR() Usage:BITAND(string1,string2,[pad]) The argument strings are logically exclusively-ORed together,with the length of the result being the longer of the two operand strings. If a pad character is supplied,the shorter string is padded on the right;otherwise,the operation terminates at the end of the shorter string and the remainder of the longer string is appended to the result. Example: bitxor('0313'x,'001F'x) ==>'030C'x C2B() Usage:C2B(string) Converts the character string into the equivalent string of binary digits. See Also:C2X() Example: say c2b('abc') ==>011000010110001001100011 C2D() Usage:C2D(string,[n]) Converts the string argument from its character representation to the corresponding decimal number,expressed as ASCII digits(0-9). If n is supplied, the character string is considered to be a number expressed in n bytes. The string is truncated or padded with nulls on the left as required,and the sign bit is extended for the conversion. Examples: say c2d('0020'x) ==>32 say c2d('FFFF') ==>1 say c2d('FF0100',x,2) ==>256 C2X() Usage:C2X(string) Converts the string argument from its character representation to the corresponding hexadecimal number,expressed as the ACSII characters 0-9 and A-F. See Also:C2B() Example: say c2x('abc') ==>616263 54 CENTER() or CENTRE() Usage:CENTER(string,length,[pad])or CENTRE(string,length,[pad]) Centers the string argument in a string with the specified length. If the length is longer than that of the string,pad characters or blanks are added as necessary. Examples: say center('abc',6) ==>' abc ' say center('abc',6,'+') ==>'+abc++' say center('123456',3) ==>'234' CLOSE() Usage:CLOSE(file) Closes the file specified by the given logical name. The returned value is a boolean success flag,and will be 1 unless the specified file was not open. Example: say close('input') ==>1 COMPRESS() Usage:COMPRESS(string,[list]) If the list argument is omitted,the function removes leading,trailing,or embedded blank characters from the string argument. If the optional list is supplied,it specifies the characters to be removed from the string. Examples: say compress (' why not ') ==>whynot say compress ('++12-34-+','+-') ==>1234 COMPARE() Usage:COMPARE(string1,string2,[pad]) Compares two strings and returns the index of the first position in which they differ,or 0 if the strings are identical. The shorter string is padded as required using the supplied character or blanks. Examples: say compare('abcde','abcce') ==>4 say compare('abcde',abcde') ==>0 say compare('abc++','abc+-','+') ==>5 COPIES() Usage:COPIES(string,number) Creates a new string by concatenating the specified number of copies of the original. The number argument may be zero,in which case the null string is returned. 55 Example: say copies('abc',3) ==>abcabcabc D2C() Usage:D2C(number) Creates a string whose value is the binary(packed)representation of the given decimal number. Example: d2c(31) ==>'1F'x DATATYPE() Usage:DATATYPE9string,[option]) If the option parameter is not specified,DATATYPE()tests whether the string parameter is a valid number and returns either NUM or CHAR. If an option keyword is given,the boolean return indicates whether the string satisfied the requested test. The following option keywords are recognized: Table 6.1 DATATYPE()Options KEYWORD CHARACTERS ACCEPTED Alphanumeric Alphabetics (A-Z,a-z) or Numerics (0-9) Binary Binary Digits String Lowercase Lowercase Alphabetics (a-z) Mixed Mixed Upper/Lowercase Numeric Valid Numbers Symbol Valid REXX Symbols Upper Uppercase Alphabetics (A-Z) Whole Integer Numbers X Hex Digits String Examples: say datatype('123') ==>NUM say datatype('1a f2','x') ==>1 say datatype('aBcde','L') ==>0 DELSTR() Usage:DELSTR(string,n,[length]) Deletes the substring of the string argument beginning with the nth character for the specified length in characters. The default length is the remaining length of the string. Example: say delstr('123456',2,3) ==>156 56 DELWORD() Usage:DELWORD(string,n,[length]) Deletes the substring of the string argument beginning with the nth word for the specified length in words. The default length is the remaining length of the string. The deleted string includes any trailing blanks following the last word. Examples: say delword('Tell me a story',2,2)==>'Tell story' say delword('one two three',3) ==>'one two ' EOF() Usage:EOF(file) Checks the specified logical file name and returns the boolean value 1(True)if the end-of-file has been reached,and 0(False)otherwise. Example: say eof(infile) ==>1 ERRORTEXT() Usage:ERRORTEXT(n) Returns the error message associated with the specified ARexx code. The null string is returned if the number is not a valid error code. Example: say errortext(41) ==>Invalid expression EXISTS() Usage:EXISTS(filename) Tests whether an external file of the given filename exists. The name string may include device and directory specifications. Example: say exists('df0:c/ed') ==>1 EXPORT() Usage:EXPORT(address,[string],[length],[pad]) Copies data from the (optional) string into a previously-allocated memory area, which must be specified as a 4-byte address. The length parameter specifies the maximum number of characters to be copied; the default is the length of the string. If the specified length is longer than the string,the remaining area is filled with the pad character or nulls('00'x). The returned value is the number of characters copied. Caution is advised in using this function. Any area of memory can be overwritten,possibly causing a system crash. Task switching is forbidden while the copy is being done,so system performance may be degraded if long strings are copied. See Also:IMPORT(),STORAGE() 57 Example: count = export('0004 0000'x,'The answer') FREESPACE() Usage:FREESPACE(address,length) Returns a block of memory of the given length to the interpreter's internal pool. The address argument must be a 4-byte string obtained by a prior call to GETSPACE(),the internal allocator. It is not always necessary to release internally-allocated memory,since it will be released to the system when the program terminates. However,if a very large block has been allocated,returning it to the pool may avoid memory space problems. The return value is a boolean success flag. See Also:GETSPACE() Example: say freespace('00042000'x,32) ==>1 GETCLIP() Usage:GETCLIP(name) Searches the Clip List for an entry matching the supplied name parameter,and returns the associated value string. The name-matching is case-sensitive,and the null string is returned if the name cannot be found. The usage and maintenance of Clip List entries is described in the Chapter 9. See Also:SETCLIP() Example: /* Assume 'numbers' contains 'PI=3.14159' */ say getclip('numbers') ==>PI=3.14159 GETSPACE() Usage:GETSPACE(length) Allocates a block of memory of the specified length from the interpreter's internal pool. The returned value is the 4-byte address of the allocated block, which is not cleared or otherwise initialized. Internal memory is automatically returned to the system when the ARexx program terminates,so this function should not be used to allocate memory for use by external programs. The Support Library(described in Appendix D)includes the function ALLOCMEM()which to allocate memory from the system free list. See Also:FREESPACE() Example: say c2x(getspace(32)) ==>'0003BF40'x HASH() Usage:HASH(string) Returns the hash attribute of a string as a decimal number,and updates the internal hash value of the string. 58 Example: say hash('1') ==>49 IMPORT() Usage:IMPORT(address,[length]) Creates a string by copying data from the specified 4-byte address. If the length parameter is not supplied,the copy terminates when a null byte is found. See Also:EXPORT() Example: extval = import('0004 0000'x,8) INDEX() Usage:INDEX(string,pattern,[start]) Searches for the first occurrence of the pattern argument in the string argument,beginning at the specified start position. The default start position is 1. The returned value is the index of the matched pattern,or 0 if the pattern was not found. Examples: say index("123456","23") ==>2 say index("123456","77") ==>0 say index("123123","23",3) ==>5 INSERT() Usage:INSERT(new,old,[start],[length],[pad]) Inserts the new string into the old string after the specified start position. The default starting position is 0. The new string is truncated or padded to the specified length as required,using the supplied pad character or blanks. If the start position is beyond the end of the old string,the old string is padded on the right. Examples: say insert('ab,'12345') ==>ab12345 say insert('123','++',3,5,'-') ==>++-123-- LASTPOS() Usage:LASTPOS(pattern,string,[start]) Searches backwards for the first occurrence of the pattern argument in the string argument,beginning at the specified start position. The default starting position is the end of the string. The returned value is the index of the matched pattern,or 0 if the pattern was not found. 59 Examples: say lastpos("123234","2") ==>4 say lastpos("123234","5") ==>0 say lastpos("123234","2",3) ==>2 LEFT() Usage:LEFT(string,length,[pad]) Returns the leftmost substring in the given string argument with the specified length. If the substring is shorter than the requested length,it is padded on the left with the supplied pad character or blanks. Examples: say left('123456',3) ==>123 say left('123456',8,'+') ==>123456++ LENGTH() Usage:LENGTH(string) Returns the length of the string. Example: say length('three') ==>5 MAX() Usage:MAX(number,number[,number,...]) Returns the maximum of the supplied arguments,all of which must be numeric. At least two parameters must be supplied. Example: say max(2.1,3,-1_ ==>3 MIN() Usage:MIN(number,number[,number,...]) Returns the minimum of the supplied arguments,all of which must be numeric. At least two parameters must be supplied. Example: say min(2.1,3,-1) ==>-1 OPEN() Usage:OPEN(file,filename,['Append' | 'Read' | 'Write']) Opens an external file for the specified operation. The file argument defines the logical name by which the file will be referenced. The filename is the external name of the file,and may include device and directory specifications. The function returns a boolean value that indicates whether the operation was successful. There is no limit to the number of files that can be open 60 simultaneusly,and all open files are closed automatically when the program exits. See Also:CLOSE(),READ(),WRITE() Examples: say open('MyCon','CON:160/50/320/100/MyCon/cds') ==>1 say open('outfile','ram:temp','W') ==>1 OVERLAY() Usage:OVERLAY(new,old,[start],[length],[pad]) Overlays the new string onto the old string beginning at the specified start position,which must be positive. The default starting position is 1. The new string is truncated or padded to the specified length as required,using the supplied pad character or blanks. If the start position is beyond the end of the old string,the old string is padded on the right. Examples: say overlay('bb,'abcd') ==>bbcd say overlay('4','123',5,5,'-') ==>123--4---- POS() Usage:POS(pattern,string,[start]) Searches for the first occurrence of the pattern argument in the string argument,beginning at the position specified by the start argument. The default starting position is 1. The returned value is the index of the matched string,or 0 if the pattern wasn't found. Examples: say pos('23','123234') ==>2 say pos('77','123234') ==>0 say pos('23','123234',3) ==>4 PRAGMA() Usage:PRAGMA(option,[value]) This function allows a program to change various attributes relating to the system environment within which the program executes. The option argument is a keyword that specifies an environmental attribute;the currently implemented options are Directory and Priority. The value argument supplies the new attribute value to be installed. The value returned by the function depends on the attribute selected. Some attributes return the previous value installed,while others may simply set a boolean success flag. The currently defined option keywords are listed below. DIRECTORY. Specifies a new "current" directory. The current directory is used as the "root" for filenames that do not explicitly include a device specification. The return value is a boolean success flag. PRIORITY. Specifies a new task priority. The priority value must be an integer in the range -128 to 127,but the practical range is much more limited. ARexx programs should never be run at a priority higher than that of the resident process,which currently runs at priority 4. The returned value is the previous priority level. 61 Examples: say pragma('priority',-5) ==>0 call pragma 'Directory','df0:system' RANDOM() Usage:RANDOM([min],[max],[seed]) Returns a pseudorandom integer in the interval specified by the min and max arguments. The default minimum value is 0 and the default maximum value is 999. The interval max-min must be less than or equal to 1000. If a greater range of random integers is required,the values from the RANDU()function can be suitable scaled and translated. The seed argument can be supplied to initialize the internal state of the random number generator. See Also:RANDU() Example: thisroll = random(1,6) /* might be 1 */ nextroll = random(1,6) /* snake eyes? */ RANDU() Usage:RANDU([seed]) Returns a uniformly-distributed pseudorandom number between 0 and 1. The number of digits of precision in the result is always equal to the current Numeric Digits setting. With the choice of suitable scaling and translation values, RANDU()can be used to generate pseudorandom numbers on an arbitrary interval. The optional seed argument is used to initialize the internal state of the random number generator. See Also:RANDOM() Example: firsttry = randu() /* 0.371902021? */ numeric digits 3 tryagain = randu() /* 0.873? */ READCH() Usage:READCH(file,length) Reads the specified number of characters from the given logical file into a string. The length of the returned string is the actual number of characters read,and may be less than the requested length if,for example,the end-of-file was reached. See Also:READLN() Example: instring = readch('input',10) 62 READLN() Usage:READLN(file) Reads characters from the given logical file into a string until a "newline" character is found. The returned string does not include the "newline". See Also:READCH() Examples: instring = readln('MyFile') REMLIB() Usage:REMLIB(name) Removes an entry with the given name from the Library List maintained by the resident process. The boolean return is 1 if the entry was found and successfully removed. Note that this function does not make a distinction between function libraries and function hosts,but simply removes a named entry. See Also:ADDLIB() Example: say remlib('MyLibrary.library')==>1 REVERSE() Usage:REVERSE(string) Reverses the sequence of characters in the string. Example: say reverse('?ton yhw') ==>why not? RIGHT() Usage:RIGHT(string,length,[pad]) Returns the rightmost substring in the given string argument with the specified length. If the substring is shorter than the requested length,it is padded on the left with the supplied pad character or blanks. Examples: say right('123456',4) ==>3456 say right('123456',8,'+') ==>++123456 SEEK() Usage:SEEK(file,offset,['Begin' | 'Current' | 'End']) Moves to a new position in the given logical file,specified as an offset from an anchor position. The default anchor is Current. The returned value is the new position relative to the start of the file. Examples: say seek('input',10,'B') ==>10 say seek('input',O,'E') ==>356 /* file length */ 63 SETCLIP() Usage:SETCLIP(name,[value]) Adds a name-value pair to the Clip List maintained by the resident process. If an entry of the same name already exists,its value is updated to the supplied value string. Entries may be removed by specifying a null value. The function returns a boolean value that indicates whether the operation was successful. Examples: say setclip('path','df0:s') ==>1 say setclip('path') ==>1 SHOW() Usage:SHOW(option,[name],[pad]) Returns the names in the resource list specified by the option argument,or tests to see whether an entry with the specified name is available. The currently implemented options keywords are Clip,Files,Libraries,and Ports, which are described below. Clip. Examines the names in the Clip List. Files. Examines the names of the currently open logical file names. Libraries. Examines the names in the Library List,which are either function libraries or function hosts. Ports. Examine the names in the system Ports List. If the name argument is omitted,the function returns a string with the resource names separated by a blank space or the pad character,if one was supplied. If the name argument is given,the returned boolean value indicates whether the name was found in the resource list. The name entries are case-sensitive. SIGN() Usage:SIGN(number) Returns 1 if the number argument is positive or zero,and -1 if number is negative. The argument must be numeric. Examples: say sign(12) ==>1 say sign(-33) ==>-1 SPACE() Usage:SPACE(string,n,[pad]) Reformats the string argument so that there are n spaces(blank characters) between each pair of words. If the pad character is specified,it is used instead of blanks as the separator character. Specifying n as 0 will remove all blanks from the string. Examples: say space('Now is the time',3) ==>'Now is the time' say space('Now is the time',0) ==>'Nowisthetime' say space('1 2 3',1,'+') ==>'1+2+3' 64 STORAGE() Usage:STORAGE([address],[string],[length],[pad]) Calling STORAGE()with no arguments returns the available system memory. If the address argument is given,it must be a 4-byte string,and the function copies data from the(optional)string into the indicated memory area. The length parameter specifies the maximum number of bytes to be copied,and defaults to the length of the string. If the specified length is longer than the string,the remaining area is filled with the pad character or nulls('00'x.) The returned value is the previous contents of the memory area. This can be used in a subsequent call to restore the original contents. Caution is advised in using this function. Any area of memory can be overwritten,possibly causing a system crash. Task switching is forbidden while the copy is being done,so system performance may be degraded if long strings are copied. See Also:EXPORT() Examples: say storage() ==>248400 oldval = storage('0004 000'x,'The answer') call storage '0004 0000'x,,32,'+' STRIP() Usage:STRIP(string,[{'B' | 'L' | 'T'}],[pad]) If neither of the optional parameters is supplied,the function removes both leading and trailing blanks from the string argument. The second argument specifies whether Leading,Trailing,or Both(leading and trailing)characters are to be removed. The optional pad(or unpad,perhaps)argument selects the character to be removed. Examples: say strip(' say what? ') ==>'say what?' say strip(' say what? ','L') ==>'say what? ' say strip('++123+++','B','+') ==>'123' SUBSTR() Usage:SUBSTR(string,start,[length],[pad]) Returns the substring of the string argument beginning at the specified start position for the specified length. The starting position must be positive,and the default length is the remaining length of the string. If the substring is shorter than the requested length,it is padded on the left with the blanks or the specified pad character. Examples: say substr('23456',4,2) ==>45 say substr('myname',3,6,'=') ==>name== 65 SUBWORD() Usage:SUBWORD(string,n,[length]) Returns the substring of the string argument beginning with the nth word for the specified length in words. The default length is the remaining length of the string. The returned string will never have leading or trailing blanks. Example: say subword('Now is the time ',2,2) ==>is the SYMBOL() Usage:SYMBOL(name) Tests whether the name argument is a valid REXX symbol. If the name is not a valid symbol,the function returns the string BAD. Otherwise,the returned string is LIT if the symbol is uninitialized and VAR if it has been assigned a value. Examples: say symbol('J') ==>VAR say symbol('x') ==>LIT say symbol('++') -->BAD TIME() Usage:TIME(option) Returns the current system time or controls the internal elapsed time counter. The valid option keywords are listed below. Table 6.2 TIME()Options OPTION KEYWORD DESCRIPTION Elapsed Elapsed time in seconds. Hours Current time in hours since midnight Minutes Current time in minutes since midnight Reset Reset the elapsed time clock Seconds Current time in seconds since midnight If no option is specified,the function returns the current system time in the form HH:MM:SS. Examples: /* Suppose that the time is 1:02 AM ... */ say time('Hours') ==>1 say time('m') ==>62 say time('S') ==>3720 call time 'R' /* reset timer */ say time('E') ==>.020 66 TRACE() Usage:TRACE(option) Sets the tracing mode to that specified by the option keyword,which must be one of the valid alphabetic or prefix options. The tracing options are described in Chapter 7. The TRACE()function will alter the tracing mode even during interactive tracing,when TRACE instructions in the source program are ignored. The returned value is the mode in effect before the function call;this allows the previous trace mode to be restored later. Example: /* Assume tracing mode is ?AL */ say trace('Results') ==>?A TRANSLATE() Usage:TRANSLATE(string,[output],[input],[pad]) This function constructs a translation table and uses it to replace selected characters in the argument string. If only the string argument is given,it is translated to uppercase. If an input table is supplied,it modifies the translation table so that characters in the argument string that occur in the input table are replaced with the corresponding character in the output table. Characters beyond the end of the output table are replaced with the specified pad character or a blank. Note that the result string is always of the same length as the original string. The input and output tables may be of any length. Examples: say translate("abcde","123","cbade","+") ==>321++ say translate("low") ==>LOW say translate("0110","10","01") ==>1001 TRIM() Usage:TRIM(string) Removes trailing blanks from the string argument. Example: say length(trim(' abc ')) ==>4 UPPER() Usage:UPPER(string) Translates the strip to uppercase. The action of this function is equivalent to that of TRANSLATE(string),but it is slightly faster for short strings. Example: say upper('One Fine Day') ==>ONE FINE DAY 67 VALUE() Usage:VALUE(name) Returns the value of the symbol represented by the name argument. Example: /* Assume that J has the value of 12 */ say value('j') ==>12 VERIFY() Usage:VERIFY(string,list,['Match']) If the Match argument is omitted,the function returns the index of the first character in the string argument which is not contained in the list argument,or 0 if all of the characters are in the list. If the Match keyword is supplied, the function returns the index of the first character which is in the list,or 0 if none of the characters are. Examples: say verify('123456','0123456789') ==>0 say verify('123a56','0123456789') ==>4 say verify('123a45','abcdefghij','m') ==>4 WORD() Usage:WORD(string,n) Returns the nth word in the string argument,or the null string if there are fewer than n words. Example: say word('Now is the time ',2) ==>is WORDINDEX() Usage:WORINDEX(string,n) Returns the starting position of the nth word in the argument string,or 0 if there are fewer than n words. Example: say wordindex('Now is the time ',3) ==>8 WORDLENGTH() Usage:WORDLENGTH(string,n) Returns the length of the nth word in the string argument. Example: say wordlength('one two three',3) ==>5 68 WORDS() Usage:WORDS(string) Returns the number of words in the string argument. Example: say words("You don't say!") ==>3 WRITECH() Usage:WRITECH(file,string) Writes the string argument to the given logical file. The returned value is the actual number of characters written. Example: say writech('output','Testing') ==>7 WRITELN() Usage:WRITELN(file,string) Writes the string argument to the given logical file with a "newline" appended. The returned value is the actual number of characters written. Example: say writeln('output','Testing') ==>8 X2C() Usage:X2C(string) Converts a string of hex digits into the(packed)character representation. Blank characters are permitted in the argument string at byte boundaries. Examples: say x2c('12ab') ==>'12ab'x say x2c('12 ab') ==>'12ab'x XRANGE() Usage:XRANGE([start],[end]) Generates a string consisting of all characters numerically between the specified start and end values. The default start character is '00'x,and the default end character is 'FF'x. Only the first character of the start and end arguments is significant. Examples: say xrange() ==>'00010203 ... FDFEFF'x say xrange('a','f') ==>'abcdef' say xrange(,'10'x) ==>'00010203040506070809010'x 69 NOW LOAD PART 2 end.