Miniproc documentation

version 3.05, 11-SEP-2000 Introduction Obtaining a copy Building instructions Command line usage Command file usage Comment lines Variables Pointer Variables Array variables Hashtable variables Indicating string literals Reserved and special variables Scope of variables and macros Command pass through Functions [ RPN operations ] - Math, String, and Logical Operations Macros If structures Switch structures Switch implemented with arrays Loop repeat counters <<>> and {{}} embedded substitution tags Translation to C Common programming errors Version specific incompatibilities Design choices in Miniproc Limits Copyright Reporting bugs, getting more information f$evaluate,f$<- Math, String, and Logical Operations Example miniproc script (testfile.mpc)

Introduction

Miniproc is a tiny preprocessor for use in formatting HTML or other documents, and performing other similar tasks. It may be used to embed preprocessing information for any language into the source code, so that platform specific versions of the final code result after the presource code is processed. (Like the C preprocessor, but it will work with any language.) Miniproc scripts are case insensitive in all locations (variable names, function names, macro names). In order to keep Miniproc very small the script syntax is extremely rigid. Although this can make Miniproc scripts a bit ugly, it also eliminates many common coding problems (for instance, incorrectly nested if/then/else constructs, or use of = for ==, or operator precedence problems). That isn't to say it isn't possible to miscode a Miniproc script, just that miscoded scripts will usually exit with an error message - which is better than going on to do the wrong thing. It is called "miniproc" because it is a mini processor, and because the term "miniproc" appears not to be in general use at the time it is written (less than 10 hits in AltaVista on 22-NOV-1997.) table of contents

Obtaining a copy

To obtain source, documentation, and binaries for several platforms visit the Miniproc home page at: http://seqaxp.bio.caltech.edu/www/miniproc.html table of contents

Building instructions

Miniproc is completely contained in miniproc.c. It is an ANSI C program, and compiles cleanly on OpenVMS and Irix with the pickiest ANSI C compiler settings. Just compile it in ANSI C mode, link it (if that's a separate step on your OS), and run it. Here are some typical build commands for different operating systems: gcc (various platforms) AND IRIX (SGI compiler): % gcc -ansi -pedantic \ -I /usr/include -I /usr/include/sys \ -o miniproc miniproc.c -lm gcc (some older versions on Linux): % gcc -ansi -pedantic -DS_IFDIR=0040000 \ -o miniproc miniproc.c -lm Tru64 (Compaq C): % cc -std1 -o miniproc miniproc.c -lm OpenVMS (Compaq C) $ cc/standard=ansi89/prefix=all- /warn=(enable=all,disable=(TRUNCINTASN,DOLLARID)) miniproc.c $ link miniproc.obj To test it issue a command like: $ miniproc newtest.mpc failonly=1 Where the "failonly=1" causes the file to run in a mode which logs errors but is not very verbose. The results appear in a file "test.txt". table of contents

Command line usage

(This varies a bit with operating system, add quotes slashes, etc. as required to pass the double quotes seen here): miniproc input.mpc int1=123 s1="a string" s2=&123 That is, the first parameter is the name of the first input file to open. If that isn't provided, the program prompts for one. Subsequent parameters are pairs of "variablename"="variablecontents", they are equivalent to input file commands like: #__ intvar=123 #__ stringvar="this is a string" #__ alsostring=&123 #__ adouble=1.234 #__ an_rpn_operator=.multiply. Variable names are case insensitive. Variables may be defined on the command line, but neither macros nor functions may be invoked. When the first argument on the command line after the miniproc command is: help, HELP, -help, -HELP, -h, -H, or ? the program outputs the following miniproc Version 3.04 - 04-JAN-2000 For more information read the file miniproc.doc and then exits immediately with exit status as "success". table of contents

Command file usage

Miniproc scripts or command files consist of 2 classes of lines: 1. Pass through. A line is read from the current input file, substitutions are performed, and the line is written to the current default output file. Pass through lines may not be continued. The substitution tags are <<>> or {{}}, where the contents of the variable named is inserted into that position in the line. These lines may not begin with either the altprefix string or "#__", which are the command line indicators. If altprefix is set to an empty string, then there will be no pass through lines. 2. Commands. These begin with the altprefix string or "#__" and may be continued by placing a final "-" as the last character on the line, with the final line lacking this character. There is only one command per command line. The maximum length of a command line is normally 32768 characters, but it may be adjusted at compile time up or down. There are exactly 7 types of command line: #__! this is a comment Comment lines #__var1=var2 Variable Assignment #__macrnoname parameter parameter Macro invocations #__f$whatever parameter parameter Function invocations #__if/elseif/else/endif test If structures #__"#__command" Command pass through The seventh type is a command line which has either zero length or is composed only of tabs and spaces. These are provided so that scripts may be made more legible - they are ignored during script execution. Commands are processed in this manner: 1. a line is read in 2. any comments on the line are removed 3. if the line ends in a continuation character the next line is processed as before and appended to it 4. leading and trailing white space is removed (Leading white space is in the region following #__ or the altprefix string. If the altprefix string is empty then leading white space begins with the first character on the line.) 5. substitutions are performed 6. the command line is passed to the command line interpreter Multiple spaces and tabs between tokens are reduced to a single space. The assignment operator does not require white space around the "=" symbol, but all other tokens on command lines must be separated by white space. table of contents

Comment lines

As with any scripting or programming language, it is important to be able to add comments. Two different types of comment lines are allowed, and they can be mixed together in various combinations. Note that comments are removed very early during command processing, so that they do not appear in TRACE statements. Comments may not be continued from line to line with a terminal "-" character, but they may be interspersed with continued lines. Here is the first form #__! text The "!" must follow the command line prefix immediately - no intervening characters are allowed. When this is encountered, the entire line is not processed further. Comment lines may be inserted inside continued commands, and when so placed, do NOT need to have an end of line continuation line themselves. #__ if a - #__! comment embeded in a continued command line #__ [ 1 2 .+_. ] Trailing comments may also be placed at the ends of command lines. In this format the last character in the line must be "!", or if the line is to be continued, the second to last character must be "!". All text forward until the next encountered "!" is a comment. For this reason, trailing comments may not include other "!" characters. Examples: #__ [ a b c .append_. ] aresult ! trailing comments! #__ [ a ! this is a comment about a !- #__ b ! this is a comment about b !- #__ c ! this is a comment about c !- #__ .append_. ] aresult ! this is a comment aresult ! Note that none of the lines in this somewhat contrived example are trailing comments: #__ [ a "!- #__abc!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"- #__ '!!!!!!!!!!' .append_. ] aresult It is only possible to place comments on command lines. table of contents

Variables

There are an unlimited number of variables. Variables are created when they first appear to the left of "=", or when they are the result of a [ ] reverse Polish calculation. (There are also some predefined variables, see Reserved and special variables.) Variables are deleted when they go out of scope. ( See Scope of variables and macros.) Variables can hold integers, double precision real numbers, strings, hashtables, or reverse Polish operators. For most variables once the type is set, it cannot be changed. For certain special predefined variables, it can be changed. Pointer variables reference other variables. The redirection operators "*" and "#" allow the underlying variables to be referenced through pointers. Multiple levels of redirection can be obtained by prepending as many redirection symbols as needed. Variable names must begin with a letter, or a colon (:) followed by a letter if it is a local variable. . This is currently not Variable names may NOT start with any of these characters: 1234567890+-[]@;,.! It is strongly suggested that all variables begin with one of the 26 letters Variable status may be modified by the use of variable flags prepended to the variable name at the time it is created and preceded and separated by underscores. Currently these flags are: CREATE create a variable or override an existing variable. ANY allow the variable being created to have any type, otherwise the type of an override variable must match the preceding instance. INT,STRING,DBL,HASHTABLE,RPN Data type of variable PTR1...PTR8 Pointer type of variable (if none of PTR types are specified the variable holds the actual data.) PTR1 variables point to data variables, PTR2 variables point to PTR1 variables that point to data variables, and so forth. . Delimiter that means "after this is the name of the variable even if parts of it match reserved keys". Examples: #__ bvar=1 If bvar doesn't exist create it, set it to 1 #__ _CREATE_avar=1 Create or re-create avar, set it to 1 #__ _CREATE_ANY_avar='string' Override avar and allow it's type to change. Without "ANY" this would have generated a type mismatch error. #__ _CREATE_ANY_STRING_avar='string' Override avar and allow it's type to change. Here explicitly set the type to STRING which must match the assignment. #__ _CREATE_ANY_STRING_PTR1_spvar=@avar Declare spvar to be a PTR1 referencing a string variable avar. #__ _CREATE_ANY_int_ivar(40,10,30)=1 Declare ivar to be an integer array and set position (40,10,30) to 1. The active cell in the array is set to (1,1,1). #__ _CREATE_int_ivar(10,30)=2 Declare ivar to be an integer array and set position (40,10,30) to be 2. Make the active cell (1,1). If this declaration follows the preceding one the current ivar definition will remain in effect until ivar is released with .free. or by going out of scope. At that point the previous ivar definition will go back into effect. #__ _CREATE_STRING_.string_string="foo" Create a variable "string_string" and assign it the value "foo". If the "." was omitted an error would result due to confusion between part of the variable name and the reserved keyword "string". #__name="string" put the string literal into name (without the outermost set of quotes. If it doesn't exist, create it. The type is determined by the value it will hold. Double quotes mark off a region from the first pair on the line to the last. So that: ="foobar" "foobar" "boo" will store the string: foobar" "foobar" "boo This is different from most other languages! Even though Miniproc is written in C, \t,\n and so forth have no special meaning in strings. #__ name=&string put the string literal into name #__ name='string' " #__ name="" reset name value to an empty string #__ name='' " #__ name=& " #__ name=name2 copy contents of name2 into name #__ name=&12 put the string "12" into name #__ count=12 put the integer 12 into count #__ name2=&count name2 contains "count" #__ pointme1=&name2 pointme1 contains "name2" #__ pointme2=&count pointme2 contains "count" #__ name3=*pointme1 name3 takes on the value of name2 = "count" #__ count2=*pointme2 count2 takes on the value of count #__ count2=**pointme1 count2 takes on the value of count #__ double=1.2 a double precision value #__ anoper=.multiply_3. an RPN operator Some array operations #__ a(10)='amacro' create string array "a" with 10 members, the 10th set to pointme1 and all others to 0. #__ a(5) = **list(1,2,(j(1,k,3))) assign to element 5 of string array a the value from two redirects starting at the cell in string array "list" specified by the complex expression shown. (For demonstration purposes only - code this complex would be very hard to maintain.) #__ *a(10) runs the macro "amacro" Redirection can also be used on the left side, even in combination with global variable overrides and arrays: #__ **pointme1 = 0 sets count to 0 #__ _CREATE_*pointme1 = 'foo' override variable name2 assigned value 'foo' #__ _CREATE_*a(6) = 'foo' Starting with the 6th element of string array "a" redirect once, then create an override variable with the value found and assign to it the value 'foo'. Strict type matching is enforced. For instance, this will trigger an error, because count is an integer variable, and double is a double precision variable. #__count=double Variables may also be created with the _CREATE_ notation within an RPN operation. When an array is created within an RPN operation the values of the array are all set to the default (0, 0.0, or empty string, as appropriate) but no values are loaded onto the stack UNLESS a pointer to the array is created at the same time, in which case that pointer may be loaded. In this example the array foo(10) is created, all values are set to 0, but nothing is loaded onthe stack. #__ [ _CREATE_ANY_INT_foo(10) ] In this example the same array is created and initialized. Additionally, a pointer to that array is loaded onto the stack. This pointer may be used immediately with .store. or other pointer based stack operator. #__ [ @_CREATE_ANY_INT_foo(10) ] table of contents

Pointer Variables

Pointers reference other variables and the "*" and "#" operators operate in slightly different ways to extract a value from a pointer. They may be used interchangeably when a pointer references a scalar variable. However, the meaning of #__ b = *a(1,2) is "a is an array of pointers, find the (1,2) element, which is a pointer and return the value it points to." Conversely: #__ _CREATE_INT_iarray(10,10)=1 #__ a = @iarray #__ b = #a(1,2) means "a is a pointer to an array of values. Follow the pointer to that array, and return the (1,2) value from the array". What happens literally is that "#a" is resolved to "iarray", and then the value is retrieved from "iarray(1,2)". The "#" operator may be combined with the "@" operator within macros to allow pointers to be passed and then referenced as pointers with an override array cell specified. For instance: #__ do_some_macro @iarray and in do_some_macro #__ [ @#P1(1,2) 3 .(load). ] will be exactly equivalent to: #__ [ @iarray(1,2) 3 .(load). ] A particular cell may specified in the macro call like: #__ do_some_macro @iarray(1,3) in which case these are equivalent: #__ [ P1 3 .(load). ] #__ [ @iarray(1,3) 3 .(load). ] When P1 has this value: #__ [ @#P1(1,2) 3 .(load). ] will override the (1,3) designation, causing values to be loaded from the array starting at cell (1,2). Pointers that have been passed into macro may be retained in a local variable and then accessed using the @# form. Example #__ @_CREATE_INT_PTR1_:lptr=P1 #__ [ @#:lptr(1,2) 3 .(load). ] table of contents

Array variables

Miniproc supprts two types of arrays. The first type, "pseudoarrays" is deprecated and you should not use them. A series of variables may be named "array[1]", "array[2]" and so forth. These are actually separate variables, and the "[1]" part is part of the NAME, not an array index. C and related languages use square brackets for array indices, and since the names shown above are valid, programs may "sort of" run. The "true" arrays which Miniproc supports use () to indicate array indices. These arrays may contain integer, double, or string values. An array holds multiple instances of one type of value in a single variable. Only one cell of each array is "active" at a time and this special cell is called the active cell of the array. The specialized array RPN operators may be used to manipulate arrays and the f$macro_map function to map array dimensions onto the repeat counts for macros. There may be up to 3 dimensions per array, and indices within each dimension of size N lie between 1 and N. The dimensions and active cell can be specified directly with the (a,b,c) notation or set via certain RPN operator functions. If (a,b,c) is used, there may be no spaces between the parentheses. Here are some valid examples of this notation: r(3), r(2,4), r(2,3,4), r(a,3,4), r(a(b(c(2))),*a,b(2)) Redirect operators may be combined with parenthesis notation as in #__ a = **b(2,3) The precedence of the redirect operator is lower than that of the parenthesis notations, so the meaning of **b(2,3) is *(*(b(2,3)) and not (**b)(2,3). Examples: Create a 3 x 4 x 5 integer array, and set the highest element to 1 (all others will be set to zero), then set a few other elements. Whenever an array is created the active cell is the first cell in the array (1,1,1). The active cell may only be changed with RPN operators but may be overridden with the parenthesis notation. #__! explicit and implicit creation of an array of size (3,4,5) #__! with active cell (1,1,1). You would specify one or the #__! other - not both! #__! #__ _CREATE_INT_iarray(3,4,5)=1 !explict ! #__ iarray(3,4,5)=1 !implict ! #__! #__! assigment to the cells shown, the active cell is still at #__! (1,1,1) after these operations #__! #__ iarray(1,2,2)=1 #__ iarray(2,2,2)=2 #__ iarray(3,2,2)=3 #__ iarray(iarray(1,2,2),iarray(2,2,2),3)=iarray(3,2,2)) #__ [ 1 ] iarray(1,1,1) #__ iarray=3 ! use the active cell, set at (1,1,1) ! #__! #__! in the next command the active array is changed to (3,3,3) #__! and the subsequent save from the stack loads the value 5 #__! into that cell. #__! #__ [ @iarray 3 3 3 .(). 5 ] iarray ! loads 5 into (3,3,3) ! Arrays may also be created by expanding an existing variable using RPN functions. This method is deprecated and should not be used in new code. Use the _CREATE_ method instead. When this is done the original value of the variable is lost. #__ iarray=1 ! create two integer variables ! #__ jarray=2 Convert these to same sized arrays. The (1,1,1) cell is active, all values are set to 0. (Real initializes to 0.0 values, and string to empty strings.) #__ [ @jarray @iarray 3 4 5 .(dim)_. ] #__"active cell of iarray has value {{iarray}};" active cell of iarray has value 0 Now make another cell active in jarray, store a value in it, then transfer it to iarray. #__ [ @jarray 1 2 3 .(). ] #__ jarray = 5 #__ iarray = jarray #__"active cell of iarray has value {{iarray}}" active cell of iarray has value 5 At this point jarray(1,2,3) has value 5, as does iarray(1,1,1). table of contents

Hashtable variables

Miniproc supports hashtable variables. A hashtable variable consists of a collection of pairs of keys (a string) and values (an integer greater than zero.) The purpose of hashtable variables is to speed matching operations. For instance, imagine a list containing several thousand words, and a program which needs to find a single word in that list. A linear search of an array or long string would require several thousand operations but a retrieval operation from a hashtable variable would only require a few operations. The value which is returned may be used subsequently as an index into an array to retrieve matching values. A single hashtable may contain multiple pairs having the same key (although the speed of access will suffer if there are too many of these since all will be stored in the same hashtable bucket.) Keys are case sensitive. If key lookup is to be case insensitive convert all key strings (consistently!) to upper or lower case before using them. Note also that the hash algorithm works best when the keys contain plain text with no particular pattern. In particular, a series of keys like "key1" "key2" "key3" etc. "key1000" will fall into just a few of the available buckets. Hashtables are never used in normal assignment operations. Instead they are always manipulated through a special set of RPN hashtable operators: .h-table. create a hash table .h-retable. resize a hash table .h-insert. insert key/value pairs into the table .h-find. finds pairs which match the key supplied exactly and has a value .ge. to the query value supplied. Leaves the value on the stack or zero if no pair was found. .h-delete. like .h_find. except that it deletes the pair if one is found and leaves the value from that pair on the stack, or zero if no pair was found. .h-space. lists the number of buckets in the tables specified. .h-entries. lists the number of entries in the tables specified. .h-unload. stores all key/value pairs from a set of tables into a pair of arrays. Examples: Create a 200 entry hashtable, enter a few values into it, look one up, then delete one of the entries and fail to delete two others. #__ [ 'table' 200 .h-table. ] #__ [ 'key1' 100 'table' .h-insert. ] #__ [ 'key2' 300 'table' .h-insert. ] #__ [ 'key3' 1000 'table' .h-insert. ] #__ [ 'key3' 2000 'table' .h-insert. ] #__ [ 'key3' 3000 'table' .h-insert. ] #__ [ 'key4' 'key5' 'key6' 1 .h-insert_. ] !3 keys, all use one value ! #__ [ 'key2' 0 'table' .h-find. ] a #__ [ 'KEY2' 0 'table' .h-find. ] b #__! a = 300, b= 0 #__ [ 'key3' 2001 'table' .h-delete. ] c #__ [ 'key3' 4001 'table' .h-delete. ] d #__ [ 'KEY3' 1000 'table' .h-delete. ] e #__! c = 3000, d = e = 0, the pair ('key3',3000) has been removed #__ [ 'key3' 'key1' 'key2' -1 ] !deletes ALL key3, key1 and key2 entries ! table of contents

Indicating string literals

There are three ways to indicate "this is a string literal": external quotation = enclose it in double quotes The string extends from the first double quote to the LAST. Any double quotes within will be part of the string literal. Only one string contained in external quotes may be used per line. internal quotatation = enclose it in single quotes The string extends from the first single quote to the single quote. If two single quotes in a row, not counting the opening quote, they will be reduced to a single quote within the string. Multiple strings delimited by internal quotes may be used on each line. tagged string = precede with "&" When used in an assignment statement the "&" indicates a string literal extending from the first character after the "&" to the end of the line. When used for command pass through, the "&" indicates that the entire line is to be passed through to the output. When used in other locations, the token beginning with "&" and extending to the next tab, space, or end of line is a string. Externally quoted strings are best used for command passthrough and simple string assignments. The advantage of this form is that everything in it goes out verbatim, with no possible chance of modification. One such string may be used in any other command, but be very careful not to use two such strings in one command or they will fuse with undesired and unpredictable consequences. Examples of good usage: #__"#__ some comand line" #__a = "some "string"!" In the following example, there is only a single externally quoted string (begining with "some" and ending with "string") , but it would be easy to misinterpret this as consisting of two such strings. #__[ "some text" a "another string" .append_. ] Internally quoted strings are similar to strings in most other languages. Here are some examples: #__A = 'this is a string' #__B = 'this isn''t illegal' #__C = '''' #__[ A B C '''this is ok too''' .showstack_. ] 3: S: 'this is ok too' 2: S: ' 1: S: this isn't illegal 0: S: this is a string Tagged strings are often most useful for working around the quoting system of the command line shell, but they are also useful in command passthrough and for marking simple tokens as strings. Examples: #__&#__ this is a passthrough command #__A = &all of this goes into A #__ [ &word & &. .showstack_. ] 2: S: . 1: S: ZERO LENGTH string 0: S: word table of contents

Reserved and special variables

Some variables are reserved and have special meanings and uses. These all have PROGRAM or MINIPROC scope, and may not be removed with a .free. command. Variables which are "special" can change type. Special variables may not have values explicitly assigned to them by a miniproc script. For instance: P1=1 is illegal. The reserved and special variables are: STATUS Integer returned by macros, functions, and command files. 0 = failed, anything else = ok. Functions usually return 1 for ok. To set STATUS on macro exit use f$macro_return or f$macro_break. To set STATUS on input file exit use f$exit or f$break. RESULT The special variable used by the RPN function to return the results of a calculation. It may have any type but usually double or string. altprefix String. The "#__" string always indicates the beginning of a command line. The altprefix variable specifies a string which also indicates the presence of a command line. Example: #__ altprefix="$" $! the next command and all others can use "$" OR "#__" If you use alternate prefixes in command files you should always begin with an altprefix definition, as you cannot know that another routine might not call the miniproc script, and if the calling script uses a different altprefix, or none at all, the called script will fail. Macros and called scripts keep track of altprefix changes and reset the value correctly when they exit. convertwidth Integer Sets the maximum size of an output string produced by .d->s. or .i->s, and defaults to a value of 32. This is adequate for conversions where only a single number is output, but inadequate for a conversion embedded in a long text string. For the latter case, the convertwidth variable must be adjusted upwards so that the entire formatted string will fit in the output. Scripts will be more memory efficient if this variable is kept as small as possible for every conversion. macrosubs Integer Similar to subs, but controls replacements while macros are recording. The default level is 0 - no replacements while macros are recording. It is important to note that line continuation resolution occurs during macro execution, so that if a a substituted variable is split across two lines it will not be substituted during recording no matter what macrosubs is set to. MC1,2,3 MC1,2,3MAX Integers. These hold macro repeat count information. See f$macro_repeat for more information. P0 Integer The number of special variables passed into a macro. P1-P9 Special variables (any type) Used to pass parameters into Macros. If the corresponding parameter is not supplied attempting to access it will cause a fatal program error. These variables only exist within a macro until another macro is called, after which attempts to access them will generate a fatal error. safety Integer Set on command line ONLY to restrict actions taken by possibly hostile input files. Bit map. Default is 0. 1 use only string to the right of /\]>: in file names, disabling paths (excluding the file name passed from the command line) 2 disables f$in 4 disables f$out (all output to stdout) 8 disables f$file_info 16 disables f$dir_list 32 disables f$rename 64 disables f$remove 128 disables f$command subs Integer The number of levels of <<>> or {{}} substitution to perform. The default is 1, so if a new <<>> is created, it will not be substituted. If the value is set higher than 1, then after the first full pass through a line a second or third pass will be made. Set it to something very large and it will go until it cannot find any more <<>>. If N is set to 0 it will not do any <<>> substitutions. trace Integer. Set the trace level, for debugging. This is a bit mask, any bit set causes that operation to be logged to stdout. (But the integer must be specified in decimal syntax!) 1 Log command lines 2 Log noncommand lines 4 Log variable creation 8 Log variable setting 16 Log macro invocation 32 Log function invocation 64 Log output lines (to stderr) 128 Log results of substitution passes 512 Log variable deletion Default is 0, nothing is logged. It is not possible to log command line actions! table of contents

Scope of variables and macros

Variables and macros have a defined scope within which they exist. "Local" objects have a scope which is strictly limited to within the module where they were created. The initial scope of a "global" object, where "global" here means "not local", is defined by the time and place of its creation. When any variable or macro goes out of scope, it is deleted automatically. Variables and macros with "local" scope have names which begin with a colon, as in ":var". They may not be promoted to a higher scope, and may only be referenced within the module (macro or file) where they were defined. Note that "var1" and ":var1" are different variables, and ":var1", ":macro1" in one module are never the same as in another. "Local" variables may not be set from the command line. Global variables may be overridden by redefinitions in macros or input files by assigning a value to it when the name is prepended by an "_CREATE_". This signal tells the interpreter to create another instance of the variable, and to continue using it until it is released, either by going out of scope, or with an explicity .free. command. The special variables (RESULT and P1-P9) may not be overridden. After the global has been overridden subsequent references to it at the same or lower scope refer to the overridden variable, not the original. Overridden global variables may not be promoted. They should not be overridden within the body of a loop construct. To prevent runaway conditions a global may only be overridden 10 times. (This protection will be removed in coming versions of Miniproc.) Note that if a variable holds an array that #__ _CREATE_array(3,5)="foo" creates a new override global variable "array" dimensioned as 3,5, and that element holds foo. It does not override the single cell of the existing array variable!. The primary purpose of global variable override is to allow parameter passing within nested macros without regard for prior definitions of variables. For instance, in the following example, the global override on variable "i" allows the two macros to reuse that variable name without interfering with, or interference from, the prior definition. Redefinition is total - variables may be converted to another type or to an array. When the macros exit the redefined global "i" goes out of scope and is deleted, which reasserts the prior usage for that variable. #__ i="foo" #__ j=0 #__ macro outer(10) #__ _CREATE_i(10)=0 #__ f$macro_body #__ i(MC1) = MC1 #__ macro inner MC1 #__ [ i(P1) j .+. ] j #__ endmacro inner #__ endmacro outer #__ " i is {{i}} j is {{j}}" Would emit "i is foo j is 55" Global value override may be combined with redirection like this: #__ var1="foo" #__ var2="var1" #__ "{{var1}}" ! emits "foo" ! #__ _CREATE_*var2=10 ! overrides var1 and sets it to 10 ! #__ "{{var1}}" ! emits "10" ! #__ ! the following comands are not allowed ! #__ _CREATE__var2=10 ! override an override ! #__ *_var2=10 ! redirect an override ! There are four "global" scope classes, PROGRAM, MINIPROC, FILE, and MACRO. Each input file, and each macro which is executing has attached to it a list of active objects. When execution of that file or macro terminates all of those objects are deleted. Before this happens, a program may promote a "global", but NOT a "local" variable, to a higher scope. For instance, a MACRO variable might be promoted to the scope of the file which called it, or the first file, or it might just be promoted to the level above it via the .lifetime. or .changescope. RPN commands. As a miniproc script executes the scope levels become nested as indicated in the following diagram: [ PROGRAM [ MINIPROC [ FILE1 [ MACRO1 [ MACRO2 [ FILE2 ...]]]]]] If Miniproc is used as a standalone program, then the PROGRAM and MINIPROC classes are functionally equivalent. If Miniproc is embedded in a program, then the MINIPROC scope refers to all variables and macros created within a single miniproc run within that program, and PROGRAM refers to variables and macros which are carried over to subsequent runs. MACRO scope variables and macros may be promoted to PROGRAM, MINIPROC, or a FILE scope, but not to a higher MACRO scope. FILE scope variables and macros may be promoted to PROGRAM, MINIPROC, or a higher FILE scope, but not to a higher MACRO scope. MINIPROC scope variables and macros may be promoted to PROGRAM scope. PROGRAM scope variables and macros may not be promoted. More details on automatic scoping: Global variable or macro "name": Visible in all input files and macros called directly or indirectly from the level at which it was declared (or promoted to). Deleted when program execution goes "above" that level. Internal name = "name" Local variable or macro declared in command file "input.mpc", but not inside a macro: Visible only in that command file Deleted when "input.mpc" exits. Internal name = "^input.mpc^name" Local variable or macro declared in command file "input.mpc", inside a macro "amacro": Visible only in that macro inside that command file Deleted when that macro exits. Internal name = "^^input.mpc^amacro^name" (If you are familiar with DCL from OpenVMS, the scoping rules for "global" versus "local" are somewhat similar to those for = vs ==, except that there are multiple "global" types.) It is a very bad idea to refer to local variables indirectly through global variables. That is: #__aglobal=&*:var In one module #__whatever=aglobal later, in another module if there is not a local variable ":var" in the new module the reference will cause a fatal error. If there is a local variable ":var" it will be referenced instead of the original, which is probably not the intended use. It is ok to pass the values of local variables into a macro because the value is copied into the special Pn variables. As in: #__somemacro :localvar but local variables should not be passed by reference (by name) for the same reason as described above. The general idea with scopes is that the structure of a miniproc script is "onion-like", with "global" variables defined at each level available to the routines within, but not above. Sometimes though a routine will need to export "global" variables or macros outward several levels. Here is the simplest example of that - when using miniproc as a preprocessor, for instance, for Fortran, which doesn't have its own portable preprocessor. A file like "template_for.mpc" might have near the front some lines like: #__ f$in "platfrom.mpc" #__ if NONPORTABLE sgi code for sgi #__ elseif NONPORTABLE [ hpux du solaris aix .or_. ] code for these OS's #__ elseif NONPORTABLE [ wnt vms .or_. ] code for these OS's #__ endif The command file "platform.mpc" initializes the OS variables which are then used for subsequent conditional processing. So it needs to pass these back out, or "promote" them. Here is an example of a script which does so. #__! platform.mpc #__! create and set variables for the assorted platforms #__! #__ ifnot PLATFORMDEFINED f$test platform Fatal error, the variable "platform" is not defined. Supported platforms are: aix du hpux sgi solaris wnt vms Add something like this platform=vms to the command line. #__ f$break 0 BANG #__ endif PLATFORMDEFINED #__! #__! This macro creates a variable with the name passed in P1 #__! and promotes it to FILE scope #__! #__ f$macro_record test_symbol deck #__ [ P1 platform .ccompare. - #__ P1 .store. - #__ FILE .lifetime. ] #__ f$macro_return #__ deck #__! #__! test the allowed OS's #__! #__ test_symbol &aix #__ test_symbol &du #__ test_symbol &hpux #__ test_symbol &sgi #__ test_symbol &solaris #__ test_symbol &wnt #__ test_symbol &vms #__ ifnot SOMEPLATFORM [ aix du hpux sgi solaris wnt vms .or_. ] Platform is <<platform>>, which is not one of these supported platforms: aix du hpux sgi solaris wnt vms. #__ f$break 0 BANG #__ endif SOMEPLATFORM #__ f$exit table of contents

Command pass through

Command pass through is a special shorthand for handling lines that would normally be interpreted as commands. Without the shorthand one of these three forms must be used: #__var="#__some command line <<name>>" #__f$write var 0 or #__var="#__some command line <<name>>" <<var>> or #__f$write "#__some command line <<name>>" 0 But the first two forms take two lines and require the creation of a temporary variable and the third form still requires an f$write and filenumber on each line. The shorthand forms are: #__"#__some command line <<name>>" #__&#__some command line <<name>> It's also possible to use internal quotes like this: #__'#__some command line <<name>>' The preceding two forms are easier to use since any internal single quotes in the preceding line would need special treatment. If the reserved variable altprefix is set to an empty string then all lines are interpreted as command lines. In this instance, command pass through may be used to send text lines to the default output file. These are equivalent: #__! next line goes to output This line goes to output var = <<var>> and #__altprefix="" ! next line goes to output, this one doesn't "This line goes to output var = <<var>>" table of contents

Functions

f$out f$in f$read f$write f$copy f$exit,f$break f$date f$pause f$file_info f$dir_list f$type f$rename f$remove f$command f$stack_dump [ RPN operations ] - Math, String, and Logical Operations f$evaluate,f$<- Math, String, and Logical Operations Functions cause certain actions to take place and most change the value of one or more variables. All set the variable STATUS (uppercase) when they return. Because some of these can present security problems in some environments most may be irreversibly disabled by setting the appropriate bit in the reserved variable safety when Miniproc is invoked. table of contents

f$out

#__f$out filename [filenumber] [disposition]] filename: string variable or immediate quoted string filenumber: integer variable or immediate integer value disposition: string variable or immediate quoted string Example: #__f$out 'outfile.txt' 0 'append' Opens the file specified by filename for output. With no other parameters, it redirects the primary output stream (filenumber 0) to the new file. Filenumbers must be in the range 0-9, inclusive. Disposition may be either 'new' or 'append'. The default is 'new' which means that the output file opened. On most operating systems this will destroy any previous versions but if file versions are used it will instead create a new version. To use disposition you must include a filenumber. f$out automatically closes open files when a filenumber is reused. If filename is an empty string, it closes that filenumber. Whenever they are not explicitly redirected to a file with an f$out command file number 0 will correspond to the C "stdout" device and file number 2 will correspond to the C "stderr" device. table of contents Functions

f$in

#__f$in filename [filenumber] filename: string variable or immediate quoted string filenumber: integer variable or immediate integer value Examples: #__f$in "commandfile.mpc" #__f$in stringvar 12 #__f$in stringvar unitnumber Opens the file "filename" for input. With no other parameters, it redirects the primary input stream (filenumber 10) to the new file. Filenumbers may be in the range 10-19, inclusive. The primary input stream may be redirected up to 10 levels deep with f$in commands. When a redirected stream executes f$exit or f$break that input file is closed and the input stream continues from the previous file. Filenumbers 11-19 are automatically closed if reused. This does not generate a warning or error. If filename is an empty string, the file is closed without opening another file. Filenumber 10 may only be closed via f$exit or f$break. If no input file has been set with f$in f$read on unit 10 will be from the C "stdin" device. table of contents Functions

f$read

#__f$read string filenumber [characters] string: string variable filenumber: integer variable or immediate integer value characters: integer variable or immediate integer value Examples: #__f$read stringvar 12 #__f$read stringvar unitnumber 100 Read a record from a file indicated by "filenumber" into the string variable "string". When f$read encounters an input line larger than the string variable's allocated storage space it will not overflow into surrounding memory but will truncate the data to fit. If the optional "characters" parameter is supplied then this truncation is considered normal, otherwise STATUS indicates that it is an error. The possible outcomes and corresponding status values are: STATUS character condition 0 ignored end of file (no more data) 0 ignored other read errors 0 none truncated read, any type 1 ignored input line fits in buffer 2 positive truncated read, no end of line 3 positive truncated read with end of line Be sure to test the STATUS variable after each read as the program will not otherwise stop when it encounters an end of file or error condition. Note also that if f$in has been used to direct input from a terminal to a file number then certain input from that terminal will cause a 0 STATUS value, yet subsequent input may still be valid. For instance, on Unix entering a ^D or on OpenVMS a ^Z will cause the STATUS to be 0. By combining f$read and .append. operations an arbitrarily long line may be read and manipulated. Be aware though that substitution operators <<>> only work on strings up to the maximum length command string, and consequently ultra long lines should not be expanded within a command line. See Limits. Note that it is very dangerous to read from filenumber 10 (the command stream) since any mistakes will corrupt the logic of the script it contains. table of contents Functions

f$write

#__f$write string filenumber string: string variable or immediate string value filenumber: integer variable or immediate integer value Examples: #__f$write "some text" 0 #__f$write stringvar 0 Write the contents of the string or string variable into the file indicated by filenumber. f$write returns 1 for normal operation, 0 for any error. The end of line character(s) written by f$write is platform specific. table of contents Functions

f$copy

#__f$copy infilenumber outfilenumber [lines] [skip] infilenumber: integer variable or immediate integer value outfilenumber: integer variable or immediate integer value line: integer variable or immediate integer value skip: integer variable or immediate integer value Examples: #__f$copy 11 2 ! copy all lines from 11 to 2 ! #__f$copy 11 2 5 ! ditto, but only move 5 lines ! #__f$copy 11 2 5 3 ! ditto, but after skipping the first 3! Copies the text from an open input file to an open output file. Substitutions are performed if the variable subs has a positive value, but no other command line processing takes place. The lines may not be bigger than the maximum command line size. On exit the variable RESULT holds the number of lines transferred. The values of STATUS on function exit are: 0 No lines were transferred 1 All indicated lines transferred without hitting the end of the input file. (There may be more lines in it.) 2 Line transfer was terminated by the end of the input file. If the number of lines isn't specified, this will be the status. The end of line character(s) written by f$copy is platform specific. table of contents Functions

f$exit,f$break

#__f$exit [status] [bang] #__f$break [status] [bang] status: integer variable or immediate integer value bang: any characters other than a comment Examples: #__f$exit #__f$break 21 #__f$break intvar now Close the current input file and return status. If the input stream has been redirected with f$in this will return control to the previous input stream and set the value of the special variable STATUS to status. When all input streams are closed the program exits and status is returned to the operating system as the program exit status. Status is an integer and can take any value, zero or greater, when exiting from redirected input files. However, when using f$exit from the "top" input file to return to the operating system status should only be either 0 (failure) or 1 (success.) This is because status values returned by programs typically have some meaning and may result in uninteded consequences. For instance, on OpenVMS, if status is 26 the operating system will emit this error message after the program exits: %SYSTEM-E-EXQUOTA, process quota exceeded No such error actually took place but miniproc, by returning a status code of 26, told the operating system that it had. Status defaults to 1 (success or true) if it is not specified. If the second parameter is present it causes an immediate exit from the entire program, passing the status value to the operating system. The second parameter may be anything other than a comment string. Either f$exit or f$break may be used for this function anywhere in a miniproc script. Use f$exit to exit unconditionally from an input script. f$exit checks for dangling bits from if/elseif/else structures, indicating bad command file syntax. As a consequence, it may not be used conditionally. Use f$break to exit from within an if/elseif/else structure. f$break does not check for dangling if/elseif/else structures on exit. f$break may not be used outside of such a structure. Except when executing an unconditional program exit, neither of these may be used within a macro. table of contents Functions

f$date

#__f$date Example: #__f$date f$date sets the following variables with values corresponding to the time at which it is invoked. day the day (Sun - Sat) (string) month the month (Jan - Dec) (string) dd the date (1-31) integer mm the month (1-12) " wday day of the week (1-7) " yday day of the year (1-365) " yyyy the year (4 digit) " hour the hour (0-23) " minute the minute (0-59) " second the second (0-59) " unixtime store time in Unix format table of contents Functions

f$pause

#__f$pause Example: #__f$date seconds microseconds f$pause causes a script to hibernate for the indicated number of seconds and microseconds. This function may not be available on some operating systems. table of contents Functions

f$file_info

#__f$file_info filename filename: string variable or immediate string value Examples: #__f$file_info "file.txt" #__f$file_info stringvar f$file_info cchecks the status of the indicated file and sets the following variables: file_exists 1=true, 0=false file_size In bytes. The size may not be exact on some operating systems and for some types of files. file_modified The time the file was last modified in Unix time format. file_isdir 1=true, 0=false table of contents Functions

f$dir_list

#__f$dir_list directory varname directory: string variable or immediate string value filenumber: string variable Examples: #__f$dir_list "/" rootlist #__f$dir_list thedir dirlist All the files found in the directory will be stored as an array of strings in varname. Any data previously in varname will be lost. If varname is already an array it will be redimensioned and the active cell set to (1,0,0). After this call STATUS will be set as follows: 0 directory could not be opened (probably isn't a directory) 1 did a listing, array was created 2 directory was empty, no array was created. table of contents Functions

f$type

#__f$type string string: the name of any variable. Examples: #__f$type intvar Returns the type of the variable named in the immediate string value. STATUS Meaning 0 not defined 1 integer 2 string 3 macro 4 zero length string 5 RPN operator 6 double 7 hashtable Add to these: 0 data 100 ptr1 200 ptr2 300 ptr3 Example, a ptr3 to a macro would have type 303. The special variables P1-P9 will return a type of 4 when they have not been set on a macro call. The following will also return a STATUS of 0, the first because it will try to look up "string" rather than string, and the second because variable redirection is not enabled for this function. #__f$type "string" #__f$type *a See also the .pcmp. function, which may be used to compare types (but not values, except for pointers) within RPN statements. table of contents Functions

f$rename

#__f$rename was willbe was: string variable or immediate string value willbe: string variable or immediate string value Examples: #__f$rename 'new.txt' 'old.txt' #__f$rename oldsvar newsvar f$rename will change the name of the file specified by was to the name specified by willbe. If the file cannot be renamed a fatal error will occur. This function may be blocked from the command line by setting the appropriate bit in the reserved variable safety. table of contents Functions

f$remove

#__f$remove filename filename: string variable or immediate string value Examples: #__f$remove 'killme.txt' #__f$remove hold_filename f$remove will delete the file specified by filename. If the file cannot be removed a fatal error will occur. This function may be blocked from the command line by setting the appropriate bit in the reserved variable safety. table of contents Functions

f$command

#__f$command string string: string variable or immediate string value Examples: #__f$command 'ls killme* > listing.txt' #__f$command an_os_command_string f$command will pass the string to the operating system as a shell level command in a subprocess. The integer status value returned by the operating system will be stored in STATUS. The value in STATUS for a valid operation on some operating systems may not correspond to the logical equivalent of TRUE in miniproc. Consequently logic involving the valid execution of f$command cannot be implemented like: #__ if LABEL f$command "os_specific command" Instead, use something like this: #__ f$command "os_specific command" #__ if LABEL [ STATUS 1234 .eq. ] f$command is extremely nonportable and can present a severe security hazard in some environments even when invoked from a nonprivileged account. This function may be blocked from the command line by setting the appropriate bit in the reserved variable safety. table of contents Functions

f$stack_dump

#__f$command string (No parameters) Examples: #__f$stack_dump f$stack_dump causes a dump of the file and macro call stack to be sent to the error or debug device (usually the terminal.) It is also called automatically if any run time errors are encountered. The format is: Type Line Name MACRO 3 FROMSECOND FILE 30 second.mpc MACRO 13 LEVEL3 MACRO 1 LEVEL2 MACRO 1 LEVEL1 FILE 39 oops.mpc Type is either MACRO, FILE, or COMMAND. (COMMAND will only be seen when this routine handles a fatal command line argument which causes an error.) Line is the line number in the Macro or File that called the next level. In COMMAND mode it is the number of the parameter which caused the fatal error. Line numbers in a Macro begin with the "1" being the first line after the #__ macro(0) name or f$macro_create line. Name is the name of the Macro or File. In command mode the field is blank. If the example shown had been the result of a run time error the top line would indicate the location of that error. table of contents Functions

Reverse Polish Notation operations

Summary of RPN operators by type Summary of RPN operators by name Details of RPN operators math operators string operators conversion operators stack operators array operators hashtable operators miscellaneous operators bitwise operators - limitations The RPN operations provide a simple way to perform most common math and string operations. The general syntax is: #__ [ operands arguments .operator_count. ] var1 var2 ... varN The only space in the above lines which may be removed is the one before the "[". If an RPN statement ends with ".... .operator.]" the terminal "]" will not be recognized and a fatal error will result. The number of arguments for each RPN operator is fixed and lies in the range 1 to 3. The arguments may be of different types. For each RPN operator there are default, minimum, and maximum number of operands. All operands have the same type. If no _count field is present the default number of operands is used. If the _count field is present that number of operands are processed, unless that number is outside the allowed range for that operator and/or exceeds the stack size. RPN operators that replace a single stack entry with multiple stack entries generally are allowed only one operand. Those that change the value of a stack entry (replace themselves with a single result) are generally allowed as many operands as there are stack slots. Here are three examples of an operator showing the different _count field forms. The numbers of operands for each RPN operator are indicated in the tables below. Here are some examples demonstrating the _count field: .+. add the default number of operands (two in this case) .+_4. add the previous four operands .+_. add all operands on the stack, however many that is RPN calculations may also be used as the conditional argument of an if/elseif/else statement, as in this example: #__ if label [ var1 var2 var3 .eq_. ] All variables are equal #__ elseifnot label [ var1 var2 var3 .+_. ] Here if variables add to zero #__ endif label More than one operation may be performed within a single use of the RPN machine. The RPN machine is stack based - operands and arguments are pushed onto the stack until an operator is encountered. Then that operation is performed, and the results, if any, left on the stack. Subsequent operators may act on these results. After the closing RPN bracket variables that follow may be loaded with the values remaining on the stack from this set of operations. However, each set of RPN operations begins by clearing the stack, so that it isn't possible to retrieve previous results unless they have been explicitly stored into a variable. Here is a simple example: #__ [ 'stay' 1 2 .+. 3 .*. 'fred' .uppercase. ] thestring theresult #__ [ ] theresult2 After the first line executes, the integer variable "theresult" will hold the value 9, and the string variable "thestring" will hold "FRED". The special variable RESULT always also assumes the last value left on the stack, so it too is set to "FRED". The 'stay' value was also left on the stack. The second line clears the RPN stack, erasing 'stay', and then exits. A fatal error will result because no values are available to load into theresult2. The types and numbers of operands on the stack are checked, and if they do not match what the operator expects, a fatal error results. For instance, these will fail: #__ [ 2 'fred' .+. ] ! mismatched operator types ! #__ [ 2 1 .+_6. ] ! specified more operands than are present ! #__ [ 2 .+. ] ! not enough operands present ! Some operators have one or more arguments, in addition to their operands. For instance: #__ [ &fred 'sally' "frank" 7 .head_. ] Results in the value "fredsal", corresponding to the first 7 characters of all stack operands. As with operands, the type and number of arguments is also checked. Be aware though that if the types of the operands and arguments are and the same and one or more arguments are omitted an equivalent number of operands will be interpreted as the missing arguments. This will give a valid result - but not the intended result. For instance, #__ [ 'fred' 'sally' ':' ',' .append_. ] ! creates "fred,sally,:" ! #__ [ 'fred' 'sally' ':' .append_. ] ! creates "fred:spot" ! Within the RPN machine all math operations are performed as double precision real numbers. When an integer value is loaded into the stack it is converted to this type. When values are unloaded from the stack, floating point numbers may be stored back into preexisting integer variables (in which case they are truncated to the nearest integer) or into floating point variables. If a new variable is automatically created by receiving the result of an RPN calculation which has a real value, then that variable will also be real even if the value stored in that real was integer. Example: #__ anint = 5 #__ adouble = 7.1 #__ [ 10 anint adouble ] anint adouble adouble2 Result, anint holds the integer value 7, adouble holds the real value 5.0, adouble2 holds 10.0. The _CREATE_ syntax for creating variables that is discussed in the section on Variables may be used to chang All RPN operations also set the STATUS variable.

RPN operators by type

The following table contains a summary of the RPN operators sorted into groups by type. Operand and Argument type may be double (D), string (S), any (A), or pointer to VAR (P). Operators work on min# to max# operands and may also have 1 to 3 arguments. When max# is MS (MAXSTACK) it means that operands may fill the RPN stack all the way to its maximum capacity. Operands #Arguments min Type Action Name class max power math 1 MS D X:D raise to exponent X modulo math 1 MS D X:I take modulo X add math 2 MS D 0 add + math 2 MS D 0 add subtract math 2 MS D 0 subtract - math 2 MS D 0 subract multiply math 2 MS D 0 multiply * math 2 MS D 0 multiple divide math 2 MS D 0 divide / math 2 MS D 0 divide sin math 1 MS D 0 sine cos math 1 MS D 0 cosine tan math 1 MS D 0 tangent asin math 1 MS D 0 arcsine acos math 1 MS D 0 arccosine atan math 1 MS D 0 arctangent expe math 1 MS D 0 exponent base e exp10 math 1 MS D 0 exponent base 10 loge math 1 MS D 0 natural logarithm log10 math 1 MS D 0 base 10 logarithm deg->rad math 1 MS D 0 degrees to radians rad->deg math 1 MS D 0 radians to degrees scale math 1 MS D X:D X multiplies operands offset math 1 MS D X:D X added to operands bitfield math 1 MS D 0 reformat unsigned integer bitfield so that it will store as an integer. b-and math 2 MS D 0 binary and b-or math 2 MS D 0 binary or test-b-xor math 1 MS D X:D binary xor X with operands test-b-and math 2 MS D X:D binary and test Operands become X AND operand test-b-or math 2 MS D X:D binary or test Operands become X OR operand eq logic 1 MS D X:D equal to X ne logic 1 MS D X:D not equal to X ge logic 1 MS D X:D greater than or equal to X gt logic 1 MS D X:D greater than X lt logic 1 MS D X:D less than X le logic 1 MS D X:D less than or equal to X xor logic 2 MS D 0 exclusive or not logic 1 MS D 0 logical negation and logic 2 MS D 0 logical and or logic 2 MS D 0 logical or nand logic 2 MS D 0 logical not and nor logic 2 MS D 0 logical not or test-and logic 1 MS D X:D logical and test test-or logic 1 MS D X:D logical or test test-nand logic 1 MS D X:D logical not and test test-nor logic 1 MS D X:D logical not or test test-xor logic 1 MS D X:D logical xor test pcmp logic 1 MS A X:A compares the types of arguments columns string 1 1 S L:Darray Extract columns from string as indicated by array head string 1 MS S L:D extract L characters from front of catenated string(s) tail string 1 MS S L:D extract L characters from back of catenated string(s) segment string 1 MS S B:D L:D extract L characters from position B in catenated string(s) append string 2 MS S X:S catenate strings separated X uppercase string 1 MS S 0 convert string(s) to upper case lowercase string 1 MS S 0 convert string(s) to lower case shortest string 1 MS S 0 return shortest string longest string 1 MS S 0 return longest string eliminate string 1 MS S X:S eliminate chars in X from string(s) retain string 1 MS S X:S retain chars in X in string(s) element string 1 MS S X:S I:D return element I of string(s) delimited by X. locate string 1 MS S Q:S find position of Q in string(s) compare string 1 MS S Q:S case sensitive compare Q to string(s) ccompare string 1 MS S Q:S case insensitive compare Q to string(s) minlength string 1 MS S L:D remove strings from stack smaller than L maxlength string 1 MS S L:D remove strings from stack larger than L length string 1 MS S 0 length of string(s) lexhigh string 2 MS S 0 lexically highest value from strings lexlow string 2 MS S 0 lexically lowest value from strings resize string 1 MS S L:D named variables resized to L insert string 1 MS S T:S C:D Insert S into string before column T justify string 1 MS S T:S Justify string with mode T overwrite string 1 MS S T:S C1:D C2:D Overwrite columns C1 to C2 with S pad string 1 MS S L:D Right pad or truncate string to size L resize string 1 MS S L:D named variables resized to L storage string 1 MS S 0 show available storage for variables substitute string 1 MS S L:D do L rounds of substitution edit string 1 MS S T:S apply T edit operation to string(s) d->s convert 1 MS D F:S write double to string using format F i->s convert 1 MS D F:S write integer to string using format F i->c convert 1 MS D convert integer to character, store in string c->i convert 1 MS S convert first character in string to integer s->d convert 1 MS S F:S read double from string using F s->i convert 1 MS S F:S read integer from string using F array stack 1 MS S N:D load var[1] to var[N] onto stack delete stack 1 MS A 0 remove indicated range in stack duplicate stack 1 MS A 0 duplicate the range of stackentries elements stack 1 MS S X:S N:D expand N elements from X delimited string(s) onto stack. load stack 1 MS A 0 push a variable(s)'s values onto stack rot->down stack 2 MS A 0 rotate entries down over stack range rot->up stack 2 MS A 0 rotate entries up over stack range showstack stack 0 MS A 0 print range of RPN stack skip stack 1 MS A 0 skip range or RPN stack operations stacksize stack 1 MS A 0 number of entries is saved onstack stackholds stack 1 MS A 0 maximum capacity of the stack swap stack 1 MS A X:D swap up to X stack entries over range indicated in operator. () array 1 MS P X:D Y:D Z:D make cell (X,Y,Z) active in the named array(s) (cro) array 1 MS P C:P R:P O:P Center Rotate Offset. 2D and 3D geometric transformations. (cross) array 1 MS PD X:PD vector cross product of reference vector to one or more operand vectors which are then overwritten with the result. (dim) array 1 MS P X:D Y:D Z:D convert variable(s) to array(s) with dimensions(X,Y,Z) (dot) array 1 MS PD X:PD dot product of reference vector to one or more operand vectors. (showdim) array 1 1 P 0 put the dimensions on the RPN stack (showcell) array 1 1 P 0 put the indices for the active cell on the RPN stack (scale) array 1 MS P X:D multiply all array cells by X (offset) array 1 MS P X:D add X to all array cells (sort) array 1 MS P B:D E:D Sort a 1D array and change the order to match on one or more other arrays. (subdim) array 1 MS P X:D Y:D Z:D specify that array is an assembly of smaller arrays of indicated dimensions. (unit) array 1 MS PD convert vectors to unit vectors h-delete hashtable 1 MS S V:D T:S find a pair (key string/V) in table T, leaving V on the stack, then removing the pair from the table. h-find hashtable 1 MS S V:D T:S find a pair (key string/V) in table T, leaving V on the stack h-insert hashtable 1 MS S V:D T:S insert a pair (key string/V) into table T h-retable hashtable 1 MS S B:D resizes hashtable(s) to B buckets h-space hashtable 1 MS S shows number of buckets in hashtable(s) h-table hashtable 1 MS S B:D creates hashtable(s) with B buckets constant misc 1 MS S 0 save integer value of predefined constant(s) on the stack debug misc 1 MS A 0 print string(s) free misc 1 MS S 0 delete the named variable(s) getenv misc 1 MS S 0 return values of environmental variables lifetime misc 1 MS S N:D promote the last N variables or macros created to the liftetime specified by the operand. changescope misc 1 MS PV N:S promote the variables indicated by the pointers to the scope specified by the operand. rstore misc 1 MS A X:S store stack value into variable X starting with the value in the range which is closest to the start of the stack. store misc 1 MS A X:S store stack value into variable X starting with the value in the range which is closest to the start of the stack. showscope misc 0 MS A 0 print lifetimes of extant variables setdepth misc 1 MS P X:D apply depth X to an RPN operator

RPN operators by name

The following table contains a summary of the RPN operators. Operand and Argument type may be double (D), string (S), or any (A). Operators work on min# to max# operands and may also have 1 to 3 arguments. When max# is MS (MAXSTACK) it means that operands may fill the RPN stack all the way to its maximum capacity. Operands #Arguments min Type Action Name class max + math 2 MS D 0 add - math 2 MS D 0 subract * math 2 MS D 0 multiple / math 2 MS D 0 divide () array 1 MS P X:D Y:D Z:D make cell (X,Y,Z) active in the named array(s) (cro) array 1 MS P C:P R:P O:P Center Rotate Offset. 2D and 3D geometric transformations. (cross) array 1 MS PD X:PD vector cross product of reference vector to one or more operand vectors which are then overwritten with the result. (dim) array 1 MS P X:D Y:D Z:D convert variable(s) to array(s) with dimensions(X,Y,Z) (dot) array 1 MS PD X:PD dot product of reference vector to one or more operand vectors. (offset) array 1 MS PD X:D add X to all array cells (scale) array 1 MS P X:D multiply all array cells by X (showcell) array 1 1 P 0 put the indices for the active cell on the RPN stack (showdim) array 1 1 P 0 put the dimensions on the RPN stack (sort) array 1 MS PA B:D E:D Sort a 1D array and change the order to match on one or more other arrays. (subdim) array 1 MS P X:D Y:D Z:D specify that array is an assembly of smaller arrays of indicated dimensions. (unit) array 1 MS PD convert vectors to unit vectors acos math 1 MS D 0 arccosine add math 2 MS D 0 add and logic 2 MS D 0 logical and append string 2 MS S X:S catenate strings separated X asin math 1 MS D 0 arcsine array stack 1 MS S N:D load var[1] to var[N] onto stack atan math 1 MS D 0 arctangent bitfield math 1 MS D 0 reformat unsigned integer bitfield so that it will store as an integer. b-and math 2 MS D 0 binary and b-or math 2 MS D 0 binary or b-not math 1 MS D 0 binary not b-xor math 1 MS D X:D binary xor X with operands c->i convert 1 MS S convert first character in string to integer ccompare string 1 MS S Q:S case insensitive compare Q to string(s) changescope misc 1 MS PV N:S promote the variables indicated by the pointers to the scope specified by the operand. columns string 1 1 S L:Darray Extract columns from string as indicated by array compare string 1 MS S Q:S case sensitive compare Q to string(s) constant misc 1 MS S 0 save integer value of predefined constant(s) on the stack cos math 1 MS D 0 cosine d->s convert 1 MS D F:S write double to string using format F debug misc 1 MS A 0 print string(s) deg->rad math 1 MS D 0 degrees to radians delete stack 1 MS A 0 remove indicated range in stack divide math 2 MS D 0 divide duplicate stack 1 MS A 0 duplicate the range of stackentries edit string 1 MS S T:S apply T edit operation to string(s) element string 1 MS S X:S I:D return element I of string(s) delimited by X. elements stack 1 MS S X:S N:D expand N elements from X delimited string(s) onto stack. eliminate string 1 MS S X:S eliminate chars in X from string(s) eq logic 2 MS D 0 equal to expe math 1 MS D 0 exponent base e exp10 math 1 MS D 0 exponent base 10 free misc 1 MS S 0 delete the named variable(s) ge logic 2 MS D 0 greater than or equal to getenv misc 1 MS S 0 return values of environmental variables gt logic 2 MS D 0 greater than h-delete hashtable 1 MS S V:D T:S find a pair (key string/V) in table T, leaving V on the stack, then removing the pair from the table. h-find hashtable 1 MS S V:D T:S find a pair (key string/V) in table T, leaving V on the stack h-insert hashtable 1 MS S V:D T:S insert a pair (key string/V) into table T h-retable hashtable 1 MS S B:D resizes hashtable(s) to B buckets h-space hashtable 1 MS S shows number of buckets in hashtable(s) h-table hashtable 1 MS S B:D creates hashtable(s) with B buckets head string 1 MS S L:D extract L characters from front of catenated string(s) i->c convert 1 MS D convert integer to character, store in string i->s convert 1 MS D F:S write integer to string using format F insert string 1 MS S T:S C:D Insert S into string before column T justify string 1 MS S T:S Justify string with mode T le logic 2 MS D 0 less than or equal to length string 1 MS S 0 length of string(s) lexhigh string 2 MS S 0 lexically highest value from strings lexlow string 2 MS S 0 lexically lowest value from strings lifetime misc 1 MS S N:D promote the last N variables or macros created to the liftetime specified by the operand. load stack 1 MS A 0 push a variable(s)'s values onto stack locate string 1 MS S Q:S find position of Q in string(s) loge math 1 MS D 0 natural logarithm log10 math 1 MS D 0 base 10 logarithm longest string 1 MS S 0 return longest string lowercase string 1 MS S 0 convert string(s) to lower case lt logic 2 MS D 0 less than maxlength string 1 MS S L:D remove strings from stack larger than L minlength string 1 MS S L:D remove strings from stack smaller than L modulo math 2 MS D 0 take modulo (of integer) multiply math 2 MS D 0 multiply nand logic 2 MS D 0 logical not and ne logic 2 MS D 0 not equal to nor logic 2 MS D 0 logical not or not logic 1 MS D 0 logical negation offset math 1 MS D X:D X added to operands or logic 2 MS D 0 logical or overwrite string 1 MS S T:S C1:D C2:D Overwrite columns C1 to C2 with S pad string 1 MS S L:D Right pad or truncate string to size L pcmp logic 1 MS A X:A compares the types of arguments power math 2 MS D 0 raise to exponent rad->deg math 1 MS D 0 radians to degrees resize string 1 MS S L:D named variables resized to L retain string 1 MS S X:S retain chars in X in string(s) rot->up stack 2 MS A 0 rotate entries up over stack range rot->down stack 2 MS A 0 rotate entries down over stack range rstore misc 1 MS A X:S store stack value into variable X starting with the value in the range which is closest to the start of the stack. s->d convert 1 MS S F:S read double from string using F s->i convert 1 MS S F:S read integer from string using F scale math 1 MS D X:D X multiplies operands segment string 1 MS S B:D L:D extract L characters from position B in catenated string(s) setdepth misc 1 MS P X:D apply depth X to an RPN operator shortest string 1 MS S 0 return shortest string showscope misc 0 MS A 0 print lifetimes of extant variables showstack stack 0 MS A 0 print range of RPN stack sin math 1 MS D 0 sine skip stack 1 MS A 0 skip range or RPN stack operations stacksize stack 1 MS A 0 number of entries is saved onstack stackholds stack 1 MS A 0 maximum capacity of the stack storage string 1 MS S 0 show available storage for variables store misc 1 MS A X:S store stack value into variable X starting with the value in the range which is closest to the start of the stack. stringdel string 1 MS S Q:S find and eliminate string Q substitute string 1 MS S L:D do L rounds of substitution subtract math 2 MS D 0 subtract swap stack 1 MS A X:D swap up to X stack entries over range indicated in operator. tail string 1 MS S L:D extract L characters from back of catenated string(s) test-and logic 2 MS D X:D logical and test test-b-xor math 1 MS D X:D binary xor X with operands test-b-and math 2 MS D X:D binary and test Operands become X AND operand test-b-or math 2 MS D X:D binary or test Operands become X OR operand test-or logic 2 MS D X:D logical or test test-nand logic 2 MS D X:D logical not and test test-nor logic 2 MS D X:D logical not or test test-xor logic 1 MS D X:D logical xor test tan math 1 MS D 0 tangent uppercase string 1 MS S 0 convert string(s) to upper case xor logic 2 MS D 0 exclusive or

Details of RPN operators

math operators

Arithmetic operators: These have 2 or more operands and no arguments. The result is operand1 OPERATOR operand2 OPERATOR etc. and replaces all operands. operators Example Equivalent +,add [ 4 5 .+. ] [ 9 ] +,add [ 1 2 3 4 5 .+_. ] [ 15 ] +,add [ .1 .2 .3 4 .+_. ] [ 4.6 ] -,subtract [ 4 5 .-. ] [ -1 ] -,subtract [ 10 1 2 3 4 .-_. ] [ 0 ] *,multiply [ 3 4 5 .*. ] [ 60 ] /,divide [ 4 5 ./. ] [ .8 ] [ 4 5 8 ./. ] [ .1 ] These have one argument and 1 or more operands. Each operand is replaced by operand OPERATOR argument. scale [ 4 5 2 .scale_. ] [ 8 10 ] offset [ 4 5 10 .offset_. ] [ 14 15 ] power [ 2 3 4 2 .power_. ] [ 4 9 16 ] modulo [ 3 15 27 10 .modulo_. ] [ 3 5 7 ] Bitwise operators: (be aware of possible limitations) when specifying values. These take 2 or more operands and no arguments and they reduce the set of operands to a single result. operators Example Equivalent b-or [ 1 2 4 .b-or_. ] [ 7 ] b-and [ 1 2 4 .b-and_. ] [ 0 ] Test-Bitwise operators. These take one argument and one or more operands. The test is argument LOGIC operand and replaces the operand. test-b-or [ 1 2 4 .test-b-or_. ] [ 5 6 ] test-b-and [ 1 2 7 .test-b-and_. ] [ 1 2 ] test-b-xor [ 15 8 2 4 7 .b_xor_. ] [ 8 15 5 3 ] Bit related operator: The bit operators described above work on an un unsigned integer "bitfield" that is stored on the RPN stack a DOUBLE value. Many of the resulting values cannot be stored directly to an integer because the most significant bit of the unsigned int value is set, so that a store to int would cause an overflow. Yet it is very convenient to be able to handle these values as integers. The .bitfield. operator tests double values and if they are within the valid range for an unsigned int converts them to a format which will give the equivalent bit pattern when stored as an int. (If the value tested is outside of the valid unsigned int range a fatal error will result.) This should only be used ONCE on a given value, as values > 2^31 will be converted to negative doubles, and the second attempt to convert the negative number will generate the fatal error. Example: [ 1 4294967295. 2147483649. .bitfield_ ] stores as "hex" values: 00000001,FFFFFFFF,F0000001 and bitfield converts to float values: 1.0, -1.0, -2147483647 which when stored as integers have the bit patterns: 00000001,FFFFFFFF,F0000001 If .bitfield. was not applied the first value would convert to an int successfully, but the second (and third) would trigger a conversion error. Double precision numeric operators: (In all cases the result is a double precision real value.) deg->rad degrees to radians [ 180 .deg->rad. ] [ 3.14159 ] rad->deg radians to degrees [ 3.14159 rad->deg ] [ 179.999848 ] sin, cos, tan Trigonometric functions, arguments in radians. [ 1.5708 .sin. ] [ 1 ] [ 1.5708 .cos. ] [ -3.673205103e-06 ] (due to rounding) [ .7854 .tan. ] [ 1.000003673 ] asin, acos, atan Trigonometric functions, results in radians. [ 0.0 .asin. ] [ 0.0 ] [ 0.0 .acos. ] [ 1.570796327 ] [ 1.0 .atan. ] [ 0.7853981634 ] expe, exp10 Standard exponential functions [ 1.0 .expe. ] [ 2.718281828 ] [ 1.0 .exp10. ] [ 10.0 ] loge, log10 Standard logarithmic functions. [ 2.718281828 .loge ] [ 1.0 ] [ 0.1 .log10. ] [ -1.0 ] table of contents [ RPN operations ]

Logic operators

Logical values are 0 = false, 1 = true. not [ 9 0 -5 0 .not_. ] [ 0 1 0 1 ] xor is the logical exclusive or. For more than two operands, the logic is: "true if (not all true AND not all false)" xor [ 0 0 1 .xor_. ] [ 1 ] [ 1 2 3 .xor_. ] [ 0 ] For the following, if there are more than two operands, the logic applied is: [ a b c .operator_.] = [ a .operator. b .operator. c ] All require at least two operands. and [ 0 1 2 .and. ] [ 0 ] [ 2 1 3 .and_. ] [ 1 ] [ 0 1 2 .and_. ] [ 0 ] or [ 0 0 9 .or_. ] [ 1 ] [ 0 0 0 .or_. ] [ 0 ] nand [ 2 1 3 .nand_. ] [ 0 ] [ 0 1 3 .nand_. ] [ 1 ] nor [ 0 0 9 .nor_. ] [ 0 ] [ 0 0 0 .nor_. ] [ 1 ] Each of the following has one argument and 1 or more operands. The argument is tested against each of the operands utilizing the logic shown, and the result replaces that operand. test-and [ 0 1 2 .test-and. ] [ 0 0 ] [ 2 1 3 .test-and_. ] [ 1 1 ] [ 0 1 2 .test-and_. ] [ 0 1 ] test-or [ 0 0 9 .test-or_. ] [ 1 1 ] [ 0 0 0 .test-or_. ] [ 0 0 ] test-nand [ 2 1 3 .test-nand_. ] [ 0 0 ] [ 0 1 3 .test-nand_. ] [ 1 0 ] test-nor [ 0 0 9 .test-nor_. ] [ 0 0 ] [ 0 0 0 .test-nor_. ] [ 1 1 ] test-xor [ 0 1 9 .test-or_. ] [ 1 0 ] [ 0 1 0 .test-or_. ] [ 0 1 ] Numeric comparison logic Minimum of two numeric operands Logical numerical result (0 = false, 1 = true) For more than two operands, the logic is: [ a b c .operator_.] = [ a b .operator. a c .operator. .and. ] eq [ 2 1 3 .eq_. ] [ 0 ] [ 2 2 2 .eq_. ] [ 1 ] [ 2.1 2.09 .eq_. ] [ 0 ] neq [ 2 1 3 .neq_. ] [ 1 ] [ 2 1 2 .neq_. ] [ 0 ] ge [ 2 1 3 .ge_. ] [ 0 ] [ 2 1 2 .ge_. ] [ 1 ] le [ 2 1 3 .le_. ] [ 0 ] [ 2 3 2 .le_. ] [ 1 ] gt [ 2 1 3 .gt_. ] [ 0 ] [ 2 1 0 .gt_. ] [ 1 ] lt [ 2 1 3 .lt_. ] [ 0 ] [ 2 3 4 .lt_. ] [ 1 ] Type comparison logic Logical numerical result (0 = false, 1 = true) Minimum of one operands The .pcmp. operator compares one argument with a series of operands. If the types match, the operands are replaced on the stack with true, if they do not match, a "false goes in instead. Pointers match if they reference the same atomic type (DBL, for instance) and have the same pointer level (1, for instance), AND, the target variable is the same (or they are both NULL pointers). Examples: Argument Operand T 'string' 'string' T 'string' 'boo' F 'string' 1.0 F ptr1->int1 ptr2->int1 F prt1->int1 ptr1->int2 T ptr1->int1 @int1 [ 'string' 1.0 an_rpn_oper_var 2.0 ] [ 0 1 0 ] [ _CREATE_INT_inull ] _CREATE_INT_int1=1 iptr1=@int1 iptr2=@iptr1 [ @int1 iptr1 iptr2 inull @int1 ] [ 1 1 0 0 ] table of contents [ RPN operations ]

String operators

append [ 'fred' 'jane' 'mary' ',' .append_. ] [ 'fred,jane,mary' ] Append the indicated operands separated by the supplied delimiter string (which may be an empty string.) ccompare [ 'foo' 'this' 'This' 'this' .ccompare_. ] [ 0 1 1 ] Result = 1 if argument matches an operand, ignoring case. 0 otherwise. columns [ '12345' @limits .columns. ] [ '1' '234' ' ' ] A 2xN array of integers contains N begin/end ranges. Each range is extracted from the string and left on the stack. When the pairs are (1,1),(2,4),(10,12) the results are as in this example. The output string is padded with spaces if the range extends past the end of the string. Columns are counted from 1. compare [ 'foo' 'this' 'This' 'this' .compare_. ] [ 0 1 0 ] Result = 1 if argument exactly matches an operand, 0 otherwise. edit [ ',A,B,.C,' 'compress' ',.' .edit. ] [ &,A,B,C, ] [ ',A,B,.C,' 'collapse' ',.' .edit. ] [ &ABC ] [ ',A,B,.C,' 'classify' '.,' .edit. ] [ &.A.B.C. ] [ ',A,B,.C,' 'trim' ',.' .edit. ] [ &A,B,.C ] [ ',A,B,.C,' 'translate' '|,A' .edit. ] [ &|||B|.C| ] [ 'ABC' 'separate' ',' .edit. ] [ 'A,B,C' ] Edit operations similar to DCL f$edit, except that the list of characters to act on must be explicitly supplied. Compress: reduce runs of the specified characters to one instance. Collapse: remove all characters in the list Classify: Like compress, except that a run composed of characters in the list is replaced with a single instance of the first character in the list. Trim: Like Collapse, but only acts on the ends of the string Translate: Replaces any instance of a character in the list with the first character in the list. Separate: Separates each character by an instance of the edit string. Use with .elements. to break individual characters out onto the stack. element [ 'A,B,C' 1 ',' .element. ] [ 'A'] [ 'A,B,C' 'W,YZ' ',,,' 2 ',' .element_. ] [ 'B' 'YZ' '' ] [ 'A,B,C' 'W,YZ' 10 ',' .element_. ] [ '' '' ] Select an element from the string operands, delimiter determined by one argument, the number of the element by the other. When no element matches, STATUS is set to false and a zero length string is entered onto the stack. elements [ 'A,B,C' 5 ',' .elements. ] [ &A &B &C & &] [ 'A,B,C' 1 ',' .elements_. ] [ &A ] Expands up to N elements in the operand string and leaves them all on the stack. Operates ONLY on the single preceding string, .elements,.elements_., and .elements_3. are all equivalent. Delimiter determined by one argument, the number of the elements to extract by the other. When elements are missing, STATUS is set to false and as many zero length strings as are required are entered onto the stack. eliminate [ 'First' 'Tried' 'ir' .eliminate_. ] [ 'Fst' 'Ted' ] Eliminate from operands any characters that are in argument. head [ 'fred' 'sally' 'frank' 7 .head_. ] [ 'fredsal' ] [ 'fred' 'sally' 'frank' 100 .head_. ] [ 'fredsallyfrank' ] Extract argument characters starting from the first character and working backwards, looking only at the indicated operands. insert [ '123' '1234' 'AB' 3 .insert_. ] [ '12AB3' '12AB34' ] Insert the argument into each operand string BEFORE the column indicated. The valid range for the column is 1 through LENGTH+1, with the extremes inserting the string before, and after the string. justify [ ' 123' '123 ' 'LEFT' .justify_. ] [ '123 ' '123 ' ] [ ' 123' '123 ' 'RIGHT' .justify_. ] [ ' 123' ' 123' ] [ ' 123' '123 ' 'CENTER' .justify_. ] [ ' 123 ' ' 123 ' ] Justify the operands as specified by the argument. The valid arguments are LEFT, RIGHT, and CENTER. length [ & &this &the .length_. ] [ 0 4 3 ] Replace each string operand with its length. lexhigh [ 'ABC' 'abc' 'abc2' .lexhigh. ] [ 'abc2' ] lexlow [ 'ABC' 'abc' 'abc2' .lexlow. ] [ 'ABC' ] Compare operands for lexical value pairwise and find the highest/lowest. If in one pair the lengths are not equal, and they are identical up to the length of the shorter of the two, the longer one has the higher lexical value. locate [ '56' '1234' '34abc' '34' .locate_. ] [ 0 3 1 ] Find the position of the argument ("34") in each of the string operands. Returns zero if it isn't found. Search is case sensitive. longest [ 'A' 'ab' '1234' .shortest_. ] [ '1234' ] Select the longest of the indicated operands. lowercase [ 'Fred' .lowercase. ] [ 'fred' ] Change the case in the indicated operands. maxlength [ '1' '12' '123' 2 .maxlength_. ] [ '1' '12' ] Remove strings from the stack if they are longer than the size indicated. minlength [ '1' '12' '123' 2 .minlength_. ] [ '12' '123' ] Remove strings from the stack if they are smaller than the size indicated. overwrite [ '123' '1234' 'ABC' 3 4 .overwrite_. ] [ '12ABC' '12ABC' ] [ '123' '1234' 'A' 3 4 .overwrite_. ] [ '12A' '12A' ] [ '123' '1234' '' 3 4 .overwrite_. ] [ '12' '12' ] Overwrite the indicated column range of each operand with the argument string. The operand may be larger or smaller than the range of columns, and the resulting string will expand or contract as needed. If the argument is an empty string the columns shown will be deleted. pad [ '12' '123' '12345' 3 .pad_. ] [ '12 ' '123' '123' ] Forces the string to the width specified. If the string is shorter than the width it is right padded with spaces. If it is longer, it is truncated. retain [ 'First' 'Tried' 'FTir' .retain_. ] [ 'Fir' 'Tri' ] Retain in operands only characters that are in argument. resize [ @var1 @var2 1000 .resize_. ] none Pointers indicate variables where string memory area is increased/decreased to the size provided in the argument. If the string is truncated by the resize a terminating character is applied in the final remaining position, and STATUS is set to 0. The argument may not be smaller than 1, which is a null string (a single terminating character.) The pointer remains on the stack after the operation. segment [ 'first' 'second' 'third' 'fourth' 4 10 .segment_3. ] [ 'ondthirdfo' ] Start from the 3rd operand down into the stack (4 and 10 are arguments), beginning at the 4th character found, scan up through the remaining operands and extract a total of 10 characters. shortest [ 'A' 'ab' '1234' .shortest_. ] [ &A ] Select the shortest of the indicated operands. storage [ @var1 @var2 .resize_. ] [ 100 1000 ] Pointers indicate variables whose string memory area is checked, and the allocated memory size is stored in the stack. The example shown says that 100 charcters will fit in var1, 1000 in var2. Note that this is NOT the length of the string, use the .length. operator to find that value, rather it is one more than the maximum string length (because a '\0' must be present on the end of any string. substitute [ "a <<foo>> fly" 1 .substitute. ] [ "a foo_contents fly" ] [ "a <<foo>> fly" subs .substitute. ] [ "a foo_contents fly" ] or [ "a {{foo}} fly" 1 .substitute. ] [ "a foo_contents fly" ] [ "a {{foo}} fly" subs .substitute. ] [ "a foo_contents fly" ] Causes the <<>> or {{}} substitution operators to be applied to the string variable(s) in the stack. The single integer parameter is the substitution level, to use the current default, use the form shown in the second example, which references the special variable "subs", containing this default. Note that the example shown here would only work in a Macro. If read directly from a file and executed the substitution would take place before the RPN operation was invoked. stringdel [ &First &Tried &ir .stringdel_. ] [ &Fst &Tried ] Eliminate from operands the substring matching the argument. Search is case sensitive. tail [ &fred &sally &frank 7 .tail_. ] [ &lyfrank ] [ &fred &sally &frank 100 .tail_. ] [ &fredsallyfrank ] Extract argument characters starting from the final character and working forward, looking only at the indicated operands. uppercase [ &Fred .lowercase. ] [ &FRED ] Change the case in the indicated operands. table of contents [ RPN operations ]

Array operators

(cro) [ @array @center @rotate @offset .(cro). ] "cro" stands for Center, Rotate, Offset. It performs 2D and 3D geometric operations on a vector or vector(s) of corresponding size stored in 'array'. The operations are: extract vector from array vector - center rotate * vector vector + offset The other three strings are the names of the array holding the data for the corresponding operation. Center and offset are both N x 1 and rotate is of size N x N, where N is 2 or 3. At least one of center, rotate, and offset must be the name of an existing array, the others may be empty strings, which means "skip that operation". Here is an example where the rotation matrix is used to swap the (X,Y) values in the vector held in matrix: rotate(2,2)=0.0 rotate(1,2)=1.0 rotate(2,1)=1.0 rotate(1,1)=0.0 matrix(2,1)=5.0 matrix(1,1)=3.0 [ @matrix & @rotate & ] Note that while rotate may be used simply for rotation, by setting its values appropriately scaling, projection, and axis swapping may also be achieved. For instance, setting rotate(1,1)=10, rotate(2,2)=.01, and the others to zero would stretch X values by a factor of 10 and compress Y values by a factor of 100. If the matrix array were, for instance, of size (10,20) and subdim had not been explicitly set, then the preceding operations would have treated matrix as 20 separate vectors and would have modified each independently. If the active cell was not the first cell in the array then the modifications would have begun with the active cell. If the array had been subdimensioned then it would have stepped along each axis by the amounts set by the subdim values. For instance, if dim=(5,5,5) and subdim=(5,1,1) it would do all 25 (1,*,*) vectors. If subdim has been left at (0,0,0) it would only have done the 5 (1,1,*) vectors. (KNOWN BUG: WILL NOT WORK WITH THE FORM "@vector3(1,3)", it only respects the active array setting.) (cross) [ @vector3 @vector2 @reference .(cross)_. ] Calcultes the cross products reference X vector3 and reference X vector2 and leaves those values in vector3 and vector2. Ie, the target vectors are replaced with the result. The reference vector is not modified. All vectors must be of type DOUBLE and size 3. If the vector is part of a larger array whose first dimension is 3 then the active cell is used. (KNOWN BUG: WILL NOT WORK WITH THE FORM "@vector3(1,3)", it only respects the active array setting.) (dim) [ @array 3 4 5 .(dim). ] Converts a regular variable (integer, string, float) into an array with dimensions (3,4,5). Sets the active cell to (1,1,1). Initializes all cell values in the array to: integer = 0 float = 0.0 string = '' (empty string (dot) [ @vector3 @vector2 @vector1 .(dot)_. ] Leaves on the stack the dot products of vector3 with vector1 and vector2 with vector1, as type double. If the vector is part of a larger array the active cell is used (KNOWN BUG: WILL NOT WORK WITH THE FORM "@vector3(1,3)", it only respects the active array setting.) () [ @array 1 2 3 .(). 5 .store. ] Makes the (1,2,3) cell active, then put the value 5 into it. (showdim) [ @array .showdim. ] [ 3 4 5 ] Extracts the dimensions of the array and leaves them on the RPN stack. (showcell) [ @array .showcell. ] [ 1 2 3 ] Extracts the indices which define the active cell and leaves them on the RPN stack. (scale) [ @array 0.5 .scale. ] Multiplies all cells in an array of double precision real numbers by 0.5. (scale) may only be applied to this type of array. (offset) [ @array 1000 .scale. ] Adds 1000 to all cells in an array of double precision real numbers. (offset) may only be applied to this type of array. (sort) [ @b(1) @c(1) @a(1) begin end .(sort)_. ] Sorts array 'a' into ascending order beginning with the cell shown. This array must be one dimensional. The cell must be explicitly specified as shown or the default cell set with an explicit .(). command. The starting cell need not be the first cell of the array. If array "a" holds strings then the columns "begin" to "end" only are considered in the comparison, ie, "ABCDE" when begin=2 and end=4 would only compare "BCD" with the similar positions in other strings. If 'a' has been subdimensioned only that much of it is sorted, beginning with the cell specified by the explicit () syntax. For instance, to sort cells 20 through 49 on 'a' first do either one of these: [ @a 30 0 0 .(subdim). @a 1 0 0 .(). @a 1 10000 .(sort). ] [ @a 30 0 0 .(subdim). @a(20) 1 10000 .(sort). ] After the first array is sorted any subsequent arrays will be compatibly reordered along their "slow" (highest) dimension. For instance, if "a" in the first example had 100 cells, and "c" was dimensioned (5,20,100) then the 100 (5x20) subarrays within "c" would be reordered to match the new order in "a". It is a fatal error if the largest dimension in one of the secondary arrays does not match the one dimension of the primary array. The sort algorithm used is "combsort". Consequently the order of same valued cells in the rearranged primary array will be arbitrary. (subdim) [ @array 3 0 0 .(subdim). ] Indicates that the array is to be treated as an assembly of smaller arrays of the indicated dimensions. This is used by (cro),(load) and store. For instance, if the array is of size (10,10) and the starting cell is (5,5) and subdim is set to (4,0) (or 4,1) then the cells will be accessed in the order: (5,5),(9,5),(5,6),(9,6) etc. If subdim is set to (8,2): (5,5), (5,7), (5,9). If subdim had never been set, or is reset to (0,0) then the cells are accessed sequentially. Access past the end of the array is an error in all functions. Subdim indices must be nonnegative. (unit) [ @array1 @array2 .(unit)_. ] Reduces all operand vectors to unit vectors. The pointers to the vectors remain on the stack. Work on subdimensioned arrays is as with (cro). If any vectors are encountered which have zero length a fatal error will result. (KNOWN BUG: WILL NOT WORK WITH THE FORM "@vector3(1,3)", it only respects the active array setting.) table of contents [ RPN operations ]

Hashtable operators

h-delete [ 'key1' 'key2' 'key3' query @table .h-delete_. ] Searches for and deletes the first key found with the same key string and a value .ge. to the query. If such a pair is found it is deleted and its value left on the stack. If none is found a value of zero is left on the stack. If query is negative then all pairs matching the key string are deleted and the value of the last one is left on the stack. h-find [ 'key1' 'key2' 'key3' query @table .h-delete_. ] For each key listed searches for the first pair with the same key string and a value .ge. to the query. (A negative query will match any pair with the same key without regard to its value.) If such a pair is found its value is left on the stack. If no such pair is found a value of zero is left on the stack. h-insert [ 'key1' 'key2' 'key3' value @table .h-insert_. ] Inserts one or more key/value (string/positive integer) pair(s) into a table. Keys need not be unique. If multiple keys are used in one operation all will have the same value assigned. If subsequent operations are to be case insensitive convert all keys to upper or lower case. h-retable [ @table1 @table2 400 .h-table_. ] Resizes one or more (here, two) hashtables to the number of buckets shown. Data within the hashtable is unaffected by the operation. Leaves the number of data pairs within each table on the stack. h-space [ @table @table2 .h-space_. ] Leaves the corresponding number of buckets for each table on the stack. h-entries [ @table @table2 .h-entries_. ] Leaves the number of entries for each table on the stack. h-unload [ @table @table2 @values @keys .h-unload_. ] Extracts the value/key pairs from one or more tables and saves them into the indicated arrays. These arrays must be of at least the size shown by the sum of the h-entries values for the tables. The values array must be of type integer, the keys array of type string. h-table [ @table1 @table2 200 .h-table_. ] Creates one or more (here, two) empty hashtables with the names and sizes shown. table of contents [ RPN operations ]

Miscellaneous operators

changescope [ @var @macro 'FILE' .changescope. ] [ ] Promote the variables and/or macros declared at, or promoted to, the current scope to the FILE scope. Promotions are described below. Promotion to PROGRAM only differs from promotion to MINIPROC when miniproc is used as an embedded command processor. Attempting to promote a variable when it is out of the scope where it was declared is a fatal error. Changescope is generally preferable to .lifetime. because the variables to modify are specified directly, rather than by "most recent N". From To Result MACRO FILE scope of active file MACRO UPFILE \ scope of file above active file, FILE UPFILE / or uppermost active file MACRO TOPFILE scope of uppermost active file FILE TOPFILE scope of uppermost active file MACRO MINIPROC scope of miniproc instance FILE MINIPROC scope of miniproc instance MACRO PROGRAM \ FILE PROGRAM + scope of program (for embedded Miniproc) MINIPROC PROGRAM / FILE FILE no change in scope constant [ 'command_trace' .constant. ] [ 1 ] Look up the name of one of the predefined constants and place the value on the stick. Case is ignored. Valid for these constant names only: Bit values for trace: TRACE_COMMAND 0x0001 command lines TRACE_INPUT 0x0002 input lines TRACE_VAR_ADD 0x0004 variable add TRACE_VAR_SET 0x0008 variable set TRACE_MACRO 0x0010 macro calls TRACE_FUNC 0x0020 f$ calls TRACE_OUTPUT 0x0040 output lines TRACE_SUBS 0x0080 every substitution TRACE_FULLNAME 0x0100 variable fullname TRACE_VAR_DEL 0x0200 variable deletion TRACE_HASH 0x0400 hashtable operations TRACE_PTRREF 0x0800 uses of pointers TRACE_FILE 0x1004 f$in/f$out operations Bit values for sets in complex find (NOT IMPLEMENTED YET): LETTERS 0x0001 NUMERIC 0x0002 ALPHANUMERIC 0x0200 VISIBLE 0x0004 CONTROL 0x0008 WHITESPACE 0x0010 PUNCTUATION 0x0020 UPPER 0x0040 LOWER 0x0080 HEX 0x0100 The length of the input/substitution buffer: MAXINLINE 32768 debug [ string .debug. ] NONE Used to send messages to the debug device (which differs from platform to platfrom and between standalone and embedded miniproc. All other IO is to files. free [ &:local &global .free_. ] NONE Deletes the named variables, whether "local" or "global". Special variables may not be deleted. getenv [ &USER .getenv. ] [ &USER'S_NAME ] Replaces each operand with the contents of the matching environmental variable. STATUS is always true, but is 1 if the variable exists, 2 if not (in which case an empty string goes into the stack at that position.) getenv may not be present on all operating systems. lifetime [ &file 4 .lifetime. ] [ [ Promote the last 4 variables and/or macros declared at, or promoted to, the current scope to the FILE scope. Promotions are described below. Promotion to PROGRAM only differs from promotion to MINIPROC when miniproc is used as an embedded command processor. See also .changescope., which is usually the preferred command for this type of operation. From To Result MACRO FILE scope of active file MACRO UPFILE \ scope of file above active file, FILE UPFILE / or uppermost active file MACRO TOPFILE scope of uppermost active file FILE TOPFILE scope of uppermost active file MACRO MINIPROC scope of miniproc instance FILE MINIPROC scope of miniproc instance MACRO PROGRAM \ FILE PROGRAM + scope of program (for embedded Miniproc) MINIPROC PROGRAM / FILE FILE no change in scope setdepth [ @rpnoper 4 .setdepth. ] rpnoper = .add_4. Sets the depth of the RPN operator indicated by the pointer. Used in conjuction with .stacksize. to adjust depth of RPN operators before use, for instance, if rpnoper = .add., then [ 1 2 3 4 5 6 7 &rpnoper .stacksize. 3 .subtract. rpnoper ] is equivalent to [ 1 2 3 4 5 6 7 .add_5. ] showscope [ .showscope. ] NONE Sends a summary of the scopes of extant variables to the primary output file. Useful primarily for debugging .lifetime. and garbage collection interactions. table of contents [ RPN operations ]

String/Numeric conversions

Minimum of one operand of the appropriate type for the conversion. Result specific to conversion type. Note 1. The single argument, if present, is an ANSI C formatting string. Note 2. As with ANSI C, there is nothing to prevent you from applying the wrong format string to a given type conversion. This will not usually crash a script, but the results will often be incorrect. Note 3. The size of the output string created by the .d->s. and .i->s. is set by the convertwidth special variable, which has a default value of 32. If the resultant string will be bigger than that, this variable must be adjusted upwards. Convert a number as a double precision real to a string. d->s [-2 1234.1 "%e" .d->s_. ] [ &-2.000000e+00 &1.234100e+03 ] Convert a number as an integer to a string. Numbers are stored on the stack as double precision real numbers, before the format is applied this number is first converted to an integer. i->s [-2 1234.1 "%d" .i->s_. ] [ &-2 &1234 ] Use i->c and c->i to convert between integer values and character values. That is, to convert integer 10 to a linefeed character use i->c. Example where convertwidth had to be adjusted before the string was written. #__ cstring="This is too long for the standard convertwidth. [%d]" #__ [ cstring .length. 8 .+. ] convertwidth #__ [ 123 cstring .i->s_. ] #__ convertwidth=32 Convert a string to a double precision real. s->d [ "-2.0" "1234.1" "%le" .s->d_. ] [ -2.0 1234.1 ] Convert a string to an integer. The integer is then further converted to a double precision real before being stored on the stack. s->i [ "-2.2" "1234.1" "%le" .s->i_. ] [ &-2 &1234 ] [ "1F" "%x" .s->i_. ] [ 31 ] [ "17" "%o" .s->i_. ] [ 15 ] Method to insert Unix "new line" or other control characters into a string: [ 'foo' 'boo' 'blue' 10 .i->c. .append_. ] string string contains foo(LF)boo(LF)blue. Warning, it may not be safe to print this string on some platforms! There is no supported method for inserting the string terminator character into a string. Using the wrong format type will often crash miniproc with an access violation. Here's one command line that did just that: [ 10 '%s' .i->s. ] table of contents [ RPN operations ]

Stack operators

delete [ &1 &2 &3 &4 &5 1 .swap_3. ] [ &1 &2 ] Removes the range of operands indicated by the operator _ extension from the stack. duplicate [ 2 &this &1 .duplicate_2. ] [ 2 &this &1 &this &1 ] Duplicate argument operands, starting from the operand indicated by the operator's _ extension. endsk See "skip" load [ @var1 @a(1) .load_2. ] [ var1 a(1) ] Load the values from the variables indicated by the pointers onto the stack, replacing the pointer with the value. This is usually only used when the name gets onto the stack as the result of some preceding RPN operation. Arrays are accessed from the active cell of the cell specified with () notation., To load multiple array cells use .(load). instead. (load) [ @name 3 .(load). ] [ name(1) name(2) name(3) ] [ @name(5) 3 .(load). ] [ name(5) name(6) name(7) ] [ @name 3 0 0 .(subdim). - @name(5) 3 .(load). ] [ name(5) name(8) name(11) ] [ @name 2 0 0 .(). - @name 3 .(load). ] [ name(2) name(5) name(8) ] Loads N elemnts form an array onto the stack. This is analogous to the .load. operator, but it loads multiple cells in one call. Note that .(load). puts values onto the stack in the opposite order from how .store. saved them. Use .rstore. to save values that will later be restored with .(load). That is, [ ... @name .store_5. @name 5 .(load). ] would reverse the order of the last 5 entries on the stack. Like .store. and .(cro). the subdim values of the array affect the order in which its cells are accessed. maxlength [ '1' '12' '123' 2 .maxlength_. ] [ '1' '12' ] Remove strings from the stack if they are longer than the size indicated. minlength [ '1' '12' '123' 2 .minlength_. ] [ '12' '123' ] Remove strings from the stack if they are smaller than the size indicated. rot->down [ &1 &2 &3 &4 &5 .rot->down_. ] [ &5 &1 &2 &3 &4 ] Rotate stack operands in range shown down by one position. Bottom rotates up to fill top of stack. rot->up [ &1 &2 &3 &4 &5 .rot->up_. ] [ &2 &3 &4 &5 &1 ] Rotate stack operands in range shown up by one position. Top rotates down to fill bottom of stack. rstore [ var1 var2 @name .rstore_2. ] [ var1 var2 ] name(1) name(2) [ var3 @name .rstore. ] name = var3 [ ... @_CREATE_STRING_name(5,3) .rstore_. ] create name(5,3) and store values into it sequentially from name(1,1) name(5,10)=0 [ ... @name(2,2) .rstore_. ] 'name' already exists begin storing at name(2,2) [ @name 2 0 0 .(subdim). - ... @name(2,2) .rstore_. ] Store into (2,2), (4,2), (2,4), (4,4), (4,6), etc. [ @name 1 5 0 .(). - ... @name .rstore_. ] Store into (1,5), (3,5), (5,5), (1,7), etc. Given the range of stack values specified, rstore takes values off the stack in the opposite direction from store, but saves them into the array starting at the same cell position. Values are assigned to the scalar or array variable, which is created if it does not yet exist. Like .(load). and .(cro). .rstore. is affected by subdim values in arrays and also respects () override of the active cell in an array. Note that if the size of the new array is specified with () notation then the active cell cannot be specified and defaults to the first cell in the array. For this reason these are not equivalent: [ 1 @_CREATE_INT_name(10) .rstore_. ] != name(10)=1 The right side would leave the value in the 10th cell, the left side in the first cell. showstack [ 1 &this .showstack_. ] NONE Sends a summary of the stack contents to the primary output file. Useful primarily for debugging RPN calculations. skip [ 'A' 0 0 .if. 'B' .endsk. 'C' .endsk. 100 .head_. ] [ 'ABC' ] [ 'A' 1 1 .if. 'B' .endsk. 'C' .endsk. 100 .head_. ] [ 'AC' ] [ 'A' 1 2 .if. 'B' .endsk. 'C' .endsk. 100 .head_. ] [ 'A' ] Conditionally skips rpn operations up to the Nth .endsk. encountered. If the operand is TRUE the operations are skipped. The one argument N must be an integer greater to or equal than 1. When .endsk. is not acting as a terminator for a skip it is ignored. stackholds [ .stackholds. ] [ 1024 ] Puts the total capacity of the RPN stack onto the stack. Note that it is not safe to fill the stack all the way to the top since some of the entries there are are used by .swap. and other operations. stacksize [ &a 2 &b .stacksize. ] [ &a 2 &b 3 ] Puts the number of stack variables present, not counting itself, onto the stack. store [ var1 var2 @name .store_2. ] [ var1 var2 ] name(2) name(1) [ var3 @name .store. ] name = var3 [ ... @_CREATE_STRING_name(5,3) .store_. ] create name(5,3) and store values into it sequentially from name(1,1) name(5,10)=0 [ ... @name(2,2) .store_. ] 'name' already exists begin storing at name(2,2) [ @name 2 0 0 .(subdim). - ... @name(2,2) .store_. ] Store into (2,2), (4,2), (2,4), (4,4), (4,6), etc. [ @name 1 5 0 .(). - ... @name .store_. ] Store into (1,5), (3,5), (5,5), (1,7), etc. Given the range of stack values specified, rstore takes values off the stack in the opposite direction from store, but saves them into the array starting at the same cell position. Values are assigned to the scalar or array variable, which is created if it does not yet exist. Like .(load). and .(cro). .store. is affected by subdim values in arrays and also respects () override of the active cell in an array. Note that if the size of the new array is specified with () notation then the active cell cannot be specified and defaults to the first cell in the array. For this reason these are not equivalent: [ 1 @_CREATE_INT_name(10) .store_. ] != name(10)=1 The right side would leave the value in the 10th cell, the left side in the first cell. swap [ &1 &2 &3 &4 &5 &6 1 .swap_. ] [ &6 &2 &3 &4 &5 &1 ] [1] [ &1 &2 &3 &4 &5 &6 2 .swap_. ] [ &6 &5 &3 &4 &2 &1 ] [ &1 &2 &3 &4 &5 &6 3 .swap_. ] [ &6 &5 &4 &3 &2 &1 ] [ &1 &2 &3 &4 &5 &6 2 .swap. ] [ &1 &2 &3 &4 &6 &5 ] [ &1 &2 &3 &4 &5 &6 2 .swap_4. ] [ &1 &2 &6 &4 &5 &3 ] [ &1 &2 &3 &4 &5 &6 1 .swap_4. & .append_. ] string [ &1 &2 &3 &4 &5 &6 .stacksize. .swap_. ] [ &6 &5 &4 &3 &2 &1] [ &1 &2 &3 &4 &5 &6 1 .swap_1. ] [ &1 &2 &3 &4 &5 &6 ] Swap the variables on the stack from the top to the bottom. The operand is the maximum number of variables to move. The range is from the top of the stack down to the variable indicated by the the .swap. operator _ extension. The default swap range is two. If the operand is at least as large as half the range, the order of the range is reversed. A range of 1 variable is possible, and legal, but has no effect on the order of the operands in the stack (see last example.) The last example shows a safe way to swap all variables on the stack without knowing the size of the stack, as the stack will never be a billion variables in size. table of contents [ RPN operations ]

bitwise operators - limitations

The Double floating numeric value on the RPN stack is converted to an unsigned Integer, operated on, and converted back to a Double. Some platforms may not be able to operate on one or more of the highest bits due to conversion problems! On 32 bit platforms Integer values of '80000000'x (2147483648 decimal) or greater must be specified using Double variables because the integer value range only extends to '7FFFFFFF'. The result of such an operation can only safely be saved in a Double variable. To save the bit pattern into an integer use the .bitfield. operator ONCE on the data, then store into an integer variable or array. Conversions are: less than zero = zero more than max unsigned integer = max unsigned integer float (128.4) = int (128) int (128) = float (128.0) table of contents [ RPN operations ]

Macros

f$macro_record f$macro_create f$macro_break, macro_break f$macro_return, macro_return f$macro_continue, macro_continue f$macro_repeat f$macro_prototype, macro_prototype f$macro_body, macro_body f$macro_map macro, endmacro Macros contain a series of program lines, command or pass through, which may be played back some time after they are recorded. They may be recorded exactly one time. If a macro is created with the macro/endmacro construct it will usually execute as soon as it is defined. Macros created with f$macro_record or f$macro_create only execute later when they are explicitly invoked. Macro names must start with a letter, and may not be the same as a variable name. Macros are invoked by name. If the name doesn't correspond to a known macro it is assumed to be a string variable, with the value of that variable being the macro's name. Examples: foobar Execute the macro named foobar string Execute the macro named in the string variable *string Execute the macro pointed to by the string variable. Macros accept up to 9 parameters which appear as the variables P1 through P9 inside the Macro. These are passed by value. (To pass a variable by reference precede it with an @). The variable P0 supplies the number of parameters which were passed. #__name 'foo' &boo name2 1 count The preceding line says execute the macro "name", and pass it the string literals foo and boo (which may be the names of other variables), the value of name2 and count, and the integer value 1. Parameters P1 - P9 are special variables, and may contain strings, integer or floating point numbers, or RPN operators. They may not contain a macro, but may contain the name of a macro. The P variables are temporary globals which are reset every time a new macro is invoked, so if a macro needs to refer to those values later it must save them somewhere, for instance, in a local variable. P1-P9 only exist within a macro until it calls another macro. After that point P0 is set 0 and P1-P9 are undefined. Attempts to access P1-P9 in the normal manner will generate a fatal error. The MACRO command starts recording a macro "in situ", the ENDMACRO command stops the recording, and if appropriate, runs that macro immediately. That is, if two macros are defined this way, one following an IF and the other the corresponding ELSE, then both will be recorded, but only the appropriate one will execute, depending upon the IF/ELSE logic. Macros recorded this way are very similar to those recorded with F$MACRO_RECORD or F$MACRO_CREATE except that: MACRO statements may be nested, whereas only one F$MACRO_RECORD or F$MACRO_CREATE may be active at any given time. MACRO statements like: #__ MACRO name(0) will record the macro "name" with repeat counts all set to zero. Macros do not execute when all repeats are zero so this format may be used in some instances instead of F$MACRO_RECORD. However, the macro "name" must either have its repeat counts reset with F$MACRO_MAP or F$MACRO_REPEAT or it must be invoked with explicit repeat counts ( name(3) ) or it will not execute when invoked later. MACRO statements may not be used to create a macro with a "local" name (beginning with a ":"). To see why consider the following illegal program. #__ macro outer #__ macro :inner #__ "foo" #__ endmacro :inner #__ endmacro outer which is equivalent to #__ macro outer #__ :inner #__ endmacro outer Both "outer" and ":inner" would be recorded at the same file/macro level, but ":inner" refers to one level down, inside "outer", so ":inner" would not be found. If a local macro is really required then it must be created from within the macro which holds it via F$MACRO_RECORD or F$MACRO_CREATE. That way it will be "local" in the correct context. Here is a legal example: #__ macro outer #__ f$macro_record :inner deck #__ "foo" #__ deck #__ f$macro_body #__ :inner #__ endmacro outer When recorded with F$MACRO_RETURN or F$MACRO_CREATE a macro requires an F$MACRO_RETURN statement at the bottom of the code. Macros recorded with MACRO/ENDMACRO automatically insert an "F$MACRO_RETURN 1". However this may be overridden by explicitly placing a different F$MACRO_RETURN statement immediately before the ENDMACRO statement. Control of execution within a macro by f$macro_break, f$macro_return, f$macro_body, and f$macro_return works the same for all macros, no matter how they are recorded. table of contents Macros

f$macro_record

#__f$macro_record name [deck] Create and begin recording a macro. When a macro is recording it goes in verbatim. Substitutions are performed if the special variable macrosubs is is set to a positive value. However, no other processing of any kind is performed. Only one macro may be recording at a time. The name is a literal string, the only way to change it during execution is by <<var>> substitution. deck is also a string literal. Deck terminates the macro when it appears on a line like #__deck. If deck is not supplied it defaults to "f$macro_end". It is a fatal error to try to rerecord a macro, so if there is any chance that a file will be reexecuted during a single run, protect the macros as you would C header files, like this: #__ifnot f$test macroname #__f$macro_record macroname deck ...(macro contents)... #__deck #__endif a table of contents Macros

f$macro_create

#__f$macro_create name Create and begin recording a macro. Differs from f$macro_record in that rather than passively storing the commands as they are read in from a file, all output is redirected to the macro, so that it may be constructed under program control. This also allows macros to be constructed on the fly, without having to write them to a file and then read them back in. (On most systems this offers a huge speed advantage.) Note that the special variable subs controls substitutions, and that macrosubs is ignored. Recording terminates when f$macro_end executes. Example of usage: #__f$macro_create foobar "#__! this macro doesn't do much" #__! now do some hairy calculation in a macro #__! that stores 3 command lines in A,B, and C. #__! normally it would just write them there, but #__! this is an illustration #__ hairy_macro <<A>> <<B>> <<C>> " it also writes this one line" "#__f$macro_return 1" #__f$macro_end It is a fatal error to try to rerecord a macro so if there is any chance that a file will be reexecuted during a single run, protect the macros as you would C header files, like this: #__ifnot f$test macroname #__f$macro_record macroname deck ...(macro contents)... #__deck #__endif a table of contents Macros

f$macro_break, macro_break, f$macro_return, macro_return, f$macro_continue, macro_continue

#__ f$macro_break status (these two do the same thing) #__ macro_break status #__ f$macro_return status (these two do the same thing) #__ macro_return status #__ f$macro_continue (these two do the same thing) #__ macro_continue The final command in any macro MUST be an f$macro_return. Following the execution of an f$macro_return statement, the macro's counters are incremented, and if the counter limits have not been reached, another iteration of the macro is performed. The function also checks syntax for dangling if/elseif/else/endif constructs. Use f$macro_break to immediately terminate a macro and return to the calling script or macro. No further iterations of the macro will be performed, no matter what the setting of the iteration counters. f$macro_break is only used within conditional statements, and it does not check syntax for incomplete if structures. Use f$macro_continue to immediately terminate an iteration of a macro but allow it to continue on to further iterations. f$macro_continue is only used within conditional statements, and it does not check syntax for incomplete if structures. This function does not take an argument as the macro does not terminate when it is executed. Macros return a status value in the integer variable STATUS. Status defaults to 1 (true) unless it is explicitly set. table of contents Macros

f$macro_repeat

#__f$macro_repeat name [int1 [int2 [int3]]]] Defines up to 3 repeat counters that are initiated each time the named macro is executed. These are named MC1, MC2, and MC3, with corresponding range limits of MC1MAX, MC2MAX, and MC3MAX. These are readonly integer variables. (Actually, you can rewrite their values, but they are reset on each repeat through the macro without regard to your actions.) The default setting for f$macro_repeat is that the macro executes once. #__f$macro_repeat foobar 3 2 Means that the macro command #__foobar would execute 6 times, and while it did so the counter MC1 would count from 1 to 3, and for each of those, the counter MC2 would count from 1 to 2. #__f$macro_repeat foobar 0 Disables the macro foobar. The next instance of #__foobar would be skipped, without even touching the STATUS variable. table of contents Macros

f$macro_prototype, macro_prototype

For macros used as simple loop constructs comprehensive parameter checking is usually not required, but when Macros accept a long parameter list and are used like a function in C or Fortran program validation of these parameters is a very good idea. To simplify that process use the f$macro_prototype or macro_prototype function. These create local variables (only!) and assign to them the values of the corresponding P* variable. Example: macro testparams(0) macro_prototype _CREATE_INT_:a - _CREATE_PTR1_DBL_:b - _CREATE_PTR1_STRING_:c macro_body ! commands using :a,:b,:c endmacro testparams and later testparams(1) 1 1.0 @stringvar If the number or types of the parameters did not match the macro_prototype function would stop the program with a fatal error. The macro_prototype function should execute only one time for each macro, so it should be placed above the macro_body statement. Note that while P0 through P9 are defined in the macro before the macro_prototype statement after it P1 will hold a value of 0 and any attempt to utilize P1 through P9 will cause a fatal error. table of contents Macros

f$macro_body, macro_body

#__ f$macro_body (these do the same thing) #__ macro_body This command is used within a macro to delineate a "header" region, which is only executed on the first cycle, and a "body" region, which is executed on each cycle. F$macro_body eliminates the need for an explicit test of MC1,MC2,MC3 to detect the condition "first cycle in this macro." Example: #__ a="outside" a is {{a}} #__ macro ncount(0) #__ _CREATE_a=P1 #__ macro_body {{a}} #__ [ a 1 .+. ] a #__ macro_return 1 #__ endmacro ncount a is {{a}} Start at 5 #__ ncount(2,2) 5 Start at 15 #__ ncount(2,2) 15 Would output: a is outside Start at 5 5 6 7 8 Start at 15 15 16 17 18 a is outside The initialization of the overridden global variable a only occurs once, not on each cycle of the macro "ncount". table of contents Macros

f$macro_map

#__f$macro_map This command maps the size of an array onto the repeat values for a macro. It is provided as a convenience so that the dimensions needn't be extracted from the array and then applied separately with f$macro_repeat. The dimensions of a single array may be mapped onto multiple macros in one operation. Example: An array "bigR" exists and has dimensions (3,4,5). A macro "sumarray" exists which looks like this: #__! a macro which sums all elements of an array #__f$macro_record sumarray deck #__ :a=0 #__ f$macro_body #__ [ P1 MC1 MC2 MC3 .(). :a .+. ] :a #__ f$macro_return :a #__ deck Then to use sumarray on bigR do: #__ f$macro_map bigR sumarray #__ sumarray bigR table of contents Macros

macro, endmacro

#__macro name(a,b,c) #__endmacro name Create and begin recording a macro "name". When a macro is recording it goes in verbatim. Substitutions are performed if the special variable macrosubs is is set to a positive value. However, no other processing of any kind is performed. Macros may be nested up to 100 levels deep. The name is either a literal string or the contents of a string variable. As with macros, macro names must be unique if they are global, and unique within the Macro or File if they are local. Example: #__ i = 3 #__ MACRO outer(i) ! begin recording ! #__ MACRO inner(MC1) ! begin recording ! #__ "{{MC1}}" #__ ENDMACRO inner ! done, do NOT execute now ! #__ ENDMACRO outer ! done, execute it ! Would emit: 1 1 2 1 2 3 Macros defined with a "macro" statemen return a status of 1 automatically, but may be induced to return a different status value if an explicit f$macro_return statment is placed immediately before ENDMACRO. In the following code the first conditional block will always execute. #__ MACRO increment #__ [ i 1 .+. ] i #__ ENDMACRO increment #__ ! increment runs ONCE because no repeat count was specified #__ i = 5 #__ if TEST increment(i) #__ ! some code which always executes #__ else TEST #__ ! some code which never executes #__ endif TEST table of contents Macros

If structures

#__ifnot label test #__if label test #__elseif label test #__elseifnot label test #__else label #__endif label If, elseif, else structure. Label is an arbitrary immediate string, case insensitive. If a variable is to be used for the label it must be substituted all the way to a value, ie #__if <<alabel>> test The function of the label is to allow detection of overlapping if and switch structures at run time. The "not" forms invert the logic of the test. Test type interpretation int 0 = false, anything else is true string Zero length string is false, anything else is true *string As for string, but indirect reference macro Check STATUS returned, 0 is false, anything else true. Note that a macro which has been set to loop zero times returns a status of 1 when invoked, so if used in a test in this state it will always be true. function Check STATUS, false if 0, true if not. [ RPN operations ] Check STATUS, if 0, this is a fatal error. Oterwise, check the value of RESULT (which is set implicitly on RPN machine exit to be the most recent value on the stack). hashtable, RPN operator, PTR1,PTR2,PTR3 Fatal error, these do not have a logical value. Examples: #__ intvar = 0 #__ dblvar = 0.0 #__ stringvar = & #__! #__ if label intvar not here, as intvar is not nonzero #__ elseif label dblvar not here, as dblvar is not nonzero #__ elseif label stringvar not here, as stringvar is a zero length string #__ elseif label f$type nonexistant not here, as the type of an undeclared variable is 0 #__ elseif label somemacro arg1 arg2 maybe here, depends on the return status of the macro somemacro #__ elseif label [ intvar 1 .lt. ] here, because the calculation returns a STATUS of 1 (no error, otherwise there would have been a "fatal error" program exit) and a RESULT of 1.0 (not zero, so true.) #__ endif label table of contents

Switch structures

#__SWITCH label testvar #__case label test1 test2 test3 etc. #__default label #__ENDSWITCH label Each SWITCH structure must contain at least one CASE statement and may contain a single final DEFAULT statement. Once the block of code following a CASE statement executes program control transfers to the line after the matching ENDSWITCH. There is no "break;" as there are in many other languages switch/case constructs. Multiple CASE lines may not follow each other to "fall through" into a single block of code. If a block of code is placed between SWITCH and the first CASE statement it will execute before, and in addition to, any other blocks which are selected by the case/default logic. Label is an arbitrary immediate string, case insensitive. If a variable is to be used for the label it must be substituted all the way to a value, ie #__if <<alabel>> test The function of the label is to allow detection of overlapping switch and if structures at run time. testvar is either an integer or string variable. If the latter the value to test is the integer representation of the first character of the string, or zero if the string is empty. test1, test2, test3, etc. are a list of immediate integer and/or quoted string values. If testvar matches any of the integer values, or any character in the string, then the code within the case executes. Example: #__ SWITCH label var block 0 #__ case label 'abcdefghijklmnopqrstuvwxyz' 10 20 30 block 1 #__ case label 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 40 '' block 2 #__ case label '!@#$%^&*()' 40 '' block 3 #__ default block 4 #__ ENDSWITCH label Value of var Blocks which execute 10 0,1 "Foo" 0,2 '' 0,3 0 0,3 "!" 0,3 ":" 0,4 table of contents

Switch implemented with arrays

By combining string arrays and macros it is possible to construct the equivalent of a Fortran computed goto. This method will be faster than the SWITCH construct if there are enough cases to consider. Here is an example of how to do this. First set up the macros and map them into the array. This only needs to be done once. #__ f$macro_record case1 deck #__ ! code goes here, this will be the default ! #__ deck #__ f$macro_record case2 deck #__ ! code goes here ! #__ deck #__ f$macro_record case3 deck #__ ! code goes here ! #__ deck #__ swarray(100)="" ! create a large array ! #__ macro setswarray(100) ! set all cells to the default ! #__ swarray(MC1)="case1" #__ endmacro setswarray #__ swarray(75)=case3 ! set those cells that aren't default ! #__ swarray(64)=case3 #__ swarray(32)=case3 #__ swarray(27)=case2 #__ swarray(10)=case2 Let choice be an integer that picks the case. If it isn't within the array's range (in this case, 1 to 100) Miniproc will issue a fatal error and stop in a controlled manner. After the above code has executed once the following line may be executed one or more times in a variety of places in your code to implement the switch action. #__ *swarray(choice) ! a LOT of logic for one line ! table of contents

Loop repeat counters

The following commands all cause a macro "name" to repeat a*b*c times. The final format first sets the repeat counters to a',b',c' and then runs the macro. f$macro_record name(a,b,c) f$macro_repeat name a b c f$macro_map array name macro name(a,b,c) name(a',b',c') ! override and use values on command line ! name ! uses a',b',c' like preceding line ! There is no way to set an infinite loop condition since the counter limits are finite. However, if the code includes something like this: #__foobar(2000000000,2000000000,2000000000) that is effectively the same thing as an infinite loop, since the macro will take 8 x 10^27 cycles to complete. Typical loop structures can be implemented within a macro without much difficulty. For instance: do 100 times #__MACRO name(100) ...(operations)... #__ENDMACRO do while variable is true #__MACRO dowhile(2000000000,2000000000,2000000000) #__ IF a variable ...(operations)... #__ ELSE a #__ f$macro_break 1 #__ ENDIF a #__ENDMACRO dowhile do until variable is true #__MACRO dountil(2000000000,2000000000,2000000000) #__ IF a variable #__ f$macro_break 1 #__ ELSE a ...(operations)... #__ ENDIF a #__ENDMACRO dountil and so forth. table of contents

<<>> and {{}} embedded substitution tags

The <<>> and {{}} substitution tags are the only miniproc operations that can be mixed with other characters in an output line. Two types of tags are supplied so that one or the other may be used in any given environment. For instance, <<>> will be mangled by any program operating on HTML, so use {{}} for that application. Do not mix the two types of substitution tags within a single line as the result is not specified and will likely not be what you want. <<>> and {{}} substitutions are done before ANYTHING else on each line. See above for the action of "subs", which controls how many times the line is processed to remove <<>> or {{}}. The * operator does not work inside <<>> or {{}}, that is <<*name>> will not resolve to whatever name points to. This is not an error, it will leave <<*name>> as is on the output line. <<name>> Insert the string variable text. <<name>> Insert the integer variable into text. Typical usage might be: #__whichstory=&weapon #__whichpocket="right coat" #__killer="Robert" #__! then much, much later... #__! the next three lines have some single or double #__! substitutions and then go right to the output "I have an invitation to dinner," said <<killer>> as he gripped the <<<<whichstory>>>> in his <<whichpocket>> pocket ever more tightly. See testfile.mpc for an example miniproc script. table of contents

Translation to C

A Miniproc to C translator is planned but has not yet been implemented. The goal is to be able to convert Miniproc scripts which need faster performance to C code, compile it, and then run the resulting program with no further debugging needed. Certain features of the interpreted language are incompatible with such a translation, and must not be used in any scripts which may ever be translated. This is because they are examples of self modifying code which is useful in an interpreted language but nearly impossible to handle in a compiled one. These are: For optimal translation variables should be restricted to a single type even when they are overridden. This can be accomplished with the variable allocation qualifiers S,I,F,R. Additionally, if a script is to be compiled the allocation qualifiers A and P must be used where appropriate. See Variables for more information. table of contents

Version specific incompatibilities

Version 2. Eliminated f$evaluate and f$<-, introduced the [] RPN machine. Version 3. Garbage collection introduced along with a much more complex "global" scope scheme for variables and macros. Scripts for previous versions may fail when run with Version 3 because expected variables and macros may have been deleted when they went out of scope. It should be relatively easy to find these problems by just running the scripts - missing macros and variables will generate warnings. Version 3.05. Redefined eq,lt,le,gt,ge,modulo, and power so that all take a single argument which is applied via the operation shown to the argument, with the result replacing the argument. Redefined and,or,nand,nor so that the result is (operand OPERATOR operand OPERATOR etc.), previously the logic was more similar to test-and, except that the first operand acted like an argument. These changes only affect code where more than two operands were compared (now one operand, and one argument.) table of contents

Design choices in Miniproc

Miniproc does things very differently from most modern scripting languages. Believe it, or not, most of that is the result of the design goal of eliminating common types of programming errors. Miniproc syntax is case insensitive, eliminating the problems present in case sensitive languagues of "foo" being used where "Foo" was intended. Braces {} are not used to constrain logical or functional units and all parts of IF and SWITCH statements are explicitly labeled. Consequently all flow control statements are self documenting and unterminated flow conditions can generate errors referring to the label of the offending structure. This eliminates many problems seen with, for instance, complex C preprocessor #ifdef,#else statements where it is often difficult to determine which #if the #else applies to. For similar reasons there are no loop structures other than the macro. There are neither goto nor pointer statements. Admittedly, redirects are a sort of pointer, but since variables are referenced by name, the worst an invalid redirect can do is cause a script to exit with a fatal error. Arrays always check bounds before attempting to access a cell. In short, only an interpreter bug will allow a miniproc script to touch memory it should not have access to. The RPN syntax was chosen with a different goal in mind - simplicity of implementation and, eventually, speed of execution. Unfortunately complex series of operations in RPN syntax can be quite difficult to understand, and so are probably more prone to error than is the equivalent statement in a nonRPN syntax. table of contents

Limits

There is a length limit of 32768 characters which applies to: This limit does not apply anywhere else in Miniproc. In particular, f$read and f$write can handle arbitrarily long strings, as can the RPN operators. This limit may be adjusted at program compilation by defining MAXINLINE to be some other value. table of contents

Common programming errors

Here are some of the programming errors which are easy to make when writing miniproc scripts. Most of these will generate a fatal error when the script runs. Missing spaces in RPN calculations. Extra spaces in (a,b,c) parenthesis notation used for array or macro repeat specfication. Since the absence of a space triggers an error shortly after the command line string is reduced to tokens (very early in the translation) the error messages can be very confusing. In the following instance the error message arises because the transator sees no "=" after the first token, sees that "a(1," isn't a miniproc function, and then assumes that the programmer is trying to invoke a macro "a(1," with some parameters, but fails when that macro doesn't exist. Assumed substitution level does not match real one. Invalid file numbers. These are the only ones which may be used: When altprefix has been set to "" (null string) there will be no passthrough lines without quoting, and blank lines will generate a fatal error as they become empty commands. Example: #__ foo = 1 #__ #__ "one blank line in output before this one" #__ altprefix="" foo = 2 "won't get here, because preceding blank line is" "an illegal empty command, not a blank line to" "the default output. See next line for blank output" "" Never mix <<>> and {{}} on a line. The result of this construct is not specified and will likely not be what you want. For instance, don't try any of the following: "<<{{var}}>>" "<<{{var>>}}" The next form will usually work but is still a bad idea: "<<var>> {{var2}}" It isn't possible to use a macro to check the values of P0 through P9 for another macro. This is because these variables are global and are set with new values at each macro call, destroying any previous values. Moreover, the previous values are not restored when the called macro exits. Use the macro_prototype construct to automatically store parameters in local variables so that they will survive a call to an internal macro. Typographical errors such as .h_space. instead of .h-space. result in an error message about .h. being an unknown operator. Attempts to create different macros using if statements. macro/endmacro are essentially just a space saver to avoid having to predeclare a macro with f$macro_record or f$macro_create. Whenever a macro/endmacro is encountered it is immediately recorded, so that this sort of code will NOT work: #__ if LABEL A #__ macro foo #__ "foo" #__ endmacro foo #__ else LABEL #__ macro foo #__ "not foo" #__ endmacro foo #__ endif LABEL As the program passes through this code it will record the first foo macro WHETHER OR NOT that logical block is executed in the normal sense. Consequently when it gets to the second definition of the macro it will fail because it cannot re-record a macro. The desired program logic can be obtained by using instead an f$macro_record structure. When operand and argument types are the same, omitting one or more arguments will result in valid, but incorrect code. Example: #__ [ &fred &sally &spot &, .append_. ] ! "fred,sally,spot" ! #__ [ &fred &sally &spot .append_. ] ! "fredspotsally" ! It is not possible to use if or case constructs to avoid command line substitution. Miniproc processes lines by first removing comments, then assembling continued lines into one, then performaing substitions, and finally acting on the line to see if it is a command or output. So for instance if pointer has not been defined the following construct will cause a fatal error even if signal is FALSE. The error occurs during the substitution phase of processing before the if/endif logic can have an effect. #__ if VALIDPOINTER signal #__ & this will blow up {{*pointer}} #__ endif VALIDPOINTER table of contents

Copyright

Copyright 1997, 1998, 1999, 2000 by David Mathog and the California Instititute of Technology. This software may be used freely, but may not be redistributed. You may modify this sofware for your own use, but you may not incorporate any part of the original code into any other piece of software which will then be distributed (whether free or commercial) unless prior written consent is obtained. table of contents

Reporting bugs, getting more information

For more information, or to report bugs, contact: mathog@caltech.edu table of contents

f$evaluate,f$<- Operations

#__f$evaluate result op operand operand operand ... #__f$<- op operand operand operand ... These were available in versions of Miniproc lower than 2.0. They have been removed and the newer reverse Polish notation implemented. If either of these functions is encountered in a script it will trigger an error message and an immediate exit. table of contents Functions