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)
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
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
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
(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
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
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
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
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
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
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
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
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
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 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
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 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 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 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 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 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 [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
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
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 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 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 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 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 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 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$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
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.
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
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
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 ]
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 ]
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 ]
(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 ]
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 ]
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 ]
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 ]
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 ]
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 ]
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 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 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 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 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
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 (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
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 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
#__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 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
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
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
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
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:
- {{}} in command lines
- f$macro_create
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 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
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
There is a length limit of 32768 characters
which applies to:
command lines
fully substituted command lines
pass through lines
fully substituted pass through lines
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
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.
#__ [ 1 2 .+.] output
which should have been
#__ [ 1 2 .+. ] output
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.
#__ a(1, 2, 3) = 4
miniproc, fatal error, unknown macro passed to mpcf_do_macro a(1,
should have been
#__ a(1,2,3) = 4
Assumed substitution level does not match real one.
#__ <<<<variable>>>>
Fails to substitute completely because subs is only 1.
Or, the inverse error, it isn't supposed to substitute
all the way, but does, because subs is 10.
Invalid file numbers. These are the only ones
which may be used:
f$in 10-19, 10 is the default
f$out 0 - 9, 0 is the default
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 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
For more information, or to report bugs, contact:
mathog@caltech.edu
table of contents
#__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