|
Except for the exceptions noted in the Appendices,
this document is Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Kevin C. O'Kane, Ph.D.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.1 or any
later version published by the Free Software Foundation; with the Invariant
Sections being: Page 1, with the Front-Cover Texts being: Page 1, and with
the Back-Cover Texts being: no Back-Cover Texts. A copy of the license
is included in the section entitled "GNU Free Documentation License".
The following students have contributed to this project:
| Matthew J. Lockner
| Michael D. Janssen
| Chris Johnson
| Jacob Good
| Madhur S. Mitra
|
The software is distributed under one
of several licenses (please see each source code
module for specific copyright and license details applicable to that
module). In general, the compiler itself is distributed under the
GNU GPL license and the run-time support routines are distributed
under the GNU LGPL.
- GNU General Public License
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- GNU Lesser General Public License
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Full texts of the licenses appear at the end of this document.
Compiled Mumps programs may call upon the Perl Compatible Regular Expression
Library which, in some cases, is distributed with the Mumps Compiler.
The license and copyright statement for PCRE appears in Appendix H.
|
Contents
Language Specification
Data Types and Values
Variables
Global Arrays
Writing Programs
Example Programs
Operators
Commands
Builtin Functions
- $ascii(e1) or $ascii(e1,i2)
- $char(i1) or $char(i1,i2) or $char(i1,i2,...)
- $get(i1) or $get(i1,i2)
- $data(vn)
- $extract(e,i2) or $extract(e1,i2,i3)
- $find(e1,e2) or $find(e1,e2,i3)
- $justify(e1,i2) or $justify(e1,i2,i3)
- $len(e1) or $len(e1,e2)
- $name(vn,[e1])
- $order(vn[,d])
- $piece(e1,e2,i3) or $piece(e1,e2,i3,i4)
- $qlength(e1)
- $qsubscript(e1,e2)
- $query(e1[,c])
- $random(i1)
- $reverse(i1)
- $select(t1:e1,t2:e2,...tn:en)
- $text(L1) or $text(L1+/-I2) or $text(I1)
- $translate(S1,S2) or $translate(S1,S2,S3)
- $view
- Special Text Functions
-
$zzAvg(arg)
-
$zzBMGSearch(arg,arg2)
-
$zzCentroid(arg1,arg2)
-
$zzCount(gbl)
-
Stream Input Functions
-
$zzScan
-
$zzScanAlnum
-
Similarity Functions
- $zzCosine(arg1,arg2)
- $zzSim1(arg1,arg2)
- $zzDice(arg1,arg2)
- $zzJaccard(arg1,arg2)
-
$zzIDF(arg1,arg2)
-
$zzDocCorrelate(arg1,arg2,arg3,arg4)
-
$zzMax(arg)
-
$zzMin(arg)
-
$zzMultiply(arg1,arg2,arg3)
- Regular Expression Matching
-
$zPerlMatch()
-
$zReplace()
-
$zzTermCorrelate(a1,a3)
-
$zzTranspose(arg1,arg2)
-
Shred functions
-
$zShred(a1,a2)
-
$zShredQuery(a1,a2)
-
$zSmithWaterman(arg1,arg2,arg3,argi4,arg5,arg6,arg7)
-
$zzSum(gbl)
-
Word Functions
-
$zStopInit(arg)
-
$zStopLookup(arg)
-
$zSynInit(arg)
-
$zSynLookup(arg)
- Math Functions
-
$zacos(arg)
-
$zasin(arg)
-
$zatan(arg)
-
$zcos(arg)
-
$zexp(arg)
-
$zlog(arg)
-
$zlog10(arg)
-
$zpow(arg1,arg2)
-
$zsin(arg)
-
$ztan(arg)
- $zab(arg) Absolute value
- $zb(arg) Remove extraneous blanks.
- $zcd[(filename)] Dump global arrays
- $zchdir[(directory_path)] Change directory
- $zcl[(filename)] Restore global arrays
- $zdate Date in printable format
- $zd1 Date in Linux time
- $zd2(InternalDate) Convert Linux time to printable time
- $zd3(Year,Month,Day) Gregorian time to Julian
- $zd4(Year,DayOfYear) Julian to Gregorian time
- $zd5(Year, Month, Day) Julian time
- $zd6 Hour, minute, second
- $zd7 Linux time to printable time
- $zd8 Linux time to printable time
- $zd9 Linux time to printable time
- $zf(arg) Test if file exists
- $zflush Flush native file system globals
- $zg Size of global arrays
- $zh(arg) Encode string as HTML parameter
- $zhit Calculate Btree cache hit ratio.
- $zm(global) Next global array row.
- $zlower(string) Convert to lower case..
- $zn(arg1,[arg2]) Word normalize
- $zp(arg1,arg2) Pad to the right with blanks
- $zr(arg) Square root
- $zs(arg) Execute shell command
- $zseek(arg) Move to file address
- $zsqr(arg) Square an argument
- $zstem(arg) Return word stem
- $zsystem(arg) Execute shell command
- $ztell Tell file position
- $zu(exp) Is expression numeric?
- $zwi(arg) Initialize parse buffer
- $zwn Return next token
- $zwp Return next parsed token
- $zws Initialize parse buffer
Builtin Variables
-
$horolog (Abbreviation $h)
-
$io (Abbreviation: $i)
-
$job (Abbreviation $j)
-
$test (Abbreviation $t)
-
$storage (Abbreviation $s)
-
$x
-
$y
Installing the Compiler
-
Other Software Needed
-
MS Windows Install
- MS Visual C++ Binary Install
- MS Visual C++ Full Build Details
- Windows XP Apache Web Server Interface
-
Linux Install
-
Linux/Cygwin Quick Install
-
Linux/Cygwin Full Installation
Compiling Programs
Global Arrays
Using Mumps with a Web Server
Uncontrolled Program Termination
Program Formats and Error Messages
Compiler Implementation Overview
Writing Programs, Functions and Calling Conventions
-
Program Structure
-
Main Programs and Functions
Hybrid Mumps and C++ Programs
Implementation Notes
Relational Algebra for Global Arrays
Programming Examples
How To Write Web Server CGI Mumps Scripts
Technical Notes
Index
Appendix B - GNU GPL/LGPL and Documentation Licenses
Appendix D - Mumps 95 Pattern Matching
Appendix E - Using Perl Regular Expressions
Appendix G - Manual Pages
Appendix H - PCRE License
Appendix I - MAC OS/X Installation Instructions
| |
Preface
The purpose of this document is to present an overview of the Mumps
Compiler implementation of the Mumps language. This package consists of
source code for compilers and support libraries for various operating systems
for a dialect of the Mumps language.
Mumps is a simple, easy to learn string and hierarchical
data base scripting language originally developed for
medical computing applications but is now used
in many other settings.
Related documentation is available at:
MDH/C++ Manual
Information Storage & Retrieval in Mumps
IDF Searching of Genomic Data Bases
Introduction to Mumps
Mumps (also referred to as 'M') is a general purpose programming language
that supports a native hierarchical data base facility. It is supported by a
large user community (mainly biomedical), and a diversified installed application
software base. The language originated in the mid-60's at the Massachusetts General
Hospital and it became widely used in both clinical and commercial settings. A
dwindling number of implementations exist for the language. There were ANSI, ISO (ISO/IEC 11756:1992)
and DOD approved standards for Mumps although these have mainly lapsed in
recent years.
As originally conceived, Mumps differed from other mini-computer based languages of the
late 1960's by providing: 1) an easily manipulated hierarchical (multi-dimensional) data base
that was well suited to representing medical records; 2) flexible string handling support; and
(3) multiple concurrent tasks in limited memory on very small machines. Syntactically, Mumps is
based on an earlier language named JOSS and has an appearance that is similar to early versions of
Basic that were also based on JOSS.
This software package includes both a compiler and interpreter for Mumps
The Mumps Compiler is a translator that converts Mumps
to C++. The compiler implements much of the most recent Mumps standard (see the manual). Mumps programs
are translated to standard C++ programs and subsequently compiled to binary executables. The compiler distribution
contains the compiler source code, the manual, the run-time functions source code, all
written in C/C++, and examples, written in Mumps.
The compiler works under Linux and Cygwin (for Windows) with the 'gcc/g++' compilers and
Microsoft Visual C++ 2005 under Windows.
The Mumps Interpreter is a compiled Mumps program that permits direct execution (interpretation)
of Mumps programs. It is available under Linux, Cygwin and Microsoft Windows.
The MDH (Multi-Dimensional and Hierarchical Data Base Toolkit) is a Linux-based, open sourced,
toolkit of portable software that supports Mumps-compatible, very fast, flexible, multi-dimensional and hierarchical
storage, retrieval and manipulation of data bases ranging in size up to 256 terabytes. The package is
written in C and C++ and is available under the GNU GPL/LGPL licenses in source code form. You must
install the Mumps Compiler in order to use the MDH.
Programming Basics
-
The Mumps programs described in this document can be run in either of two
ways: either as interpreted code using the Mumps Interpreter
or as binary executables resulting from application of the Mumps
Compiler. Binary programs run faster that interpreted programs but
the difference can be small if the programs are dominantly
input/output jobs. The Mumps Interpreter is created
by compiling the program "mumps.mps" provided
with the distribution. For WinXP based systems, where
a C/C++ compiler is not normally available,
the interpreter may be s simpler and easier approach to
developing applications. All programs that execute with the interpreter
can be compiled by the compiler.
-
How to Run a Program.
Programs written in Mumps normally have the extension ".mps"
when used with the compiler. Programs written
for the interpreter, however, may have any extension but ".mps" is
preferred. MDH programs written in C++ must have the ".cpp" extension.
When you compile a Mumps program, a C++ translation of your program
is made and resides on the disk with the same name but with the .cpp extension.
The C++ translation is then compiled and linked with run-time libraries
to build an executable binary. On MS Windows, the binary will
have the same name as your original program but with the .exe extension.
On Linux, the binary will have the extension .cgi and the same name as
your original program. Depending on which system you are using,
there will be other, intermediate files generated by the Mumps and C++ compilers.
These are not important.
You may compile a program either by using the built in script "mumpsc" or by
manually performing each of the steps at the command prompt.
To compile a Mumps program using the script, type:
mumpsc myprog.mps
This will translate your Mumps program to C++, run the C++ compiler
on the result, and link the output of the C++ compiler with the
standard Mumps libraries.
To do the above steps individually, type the following sequence:
mumps2c myprog.mps
g++ -O3 -o myprog.cgi myprog.cpp -lmpscpp -lmumps -lmpsglobal_native -lpcre
The first command ("mumps2c") translates the Mumps program to C++. The output
will be named "myprog.cpp". The second command ("g++") runs the C++ compiler
on "myprog.cpp" and links the result with the standard Mumps libraries
(using the native gloabl arrays). You may wish to use this sequence if your
Mumps programs has embedded C++ code that calls on special libraries that
are not included in the "mumsc" script, for example, PostgreSQL.
Also, use the above "g++" command to compile a C++ program generated by
the Mumps Compiler that you may have edited (for example, to insert debugging
information). You may not use the "mumpsc" script to compile a ".cpp"
program generated by the Mumps Compiler. The "mumpsc" script may only be
used to compile Mumps source programs (".mps" extensions) and MDH
programs (".cpp" extensions). When "mumpsc" sees you compiling a
program with the ".cpp" extension, it sets certain switches that
are not appropriate for a ".cpp" program generated by the Mumps Compiler.
To compile an MDH/C++ program using the script, type:
mumpsc myprog.cpp
To compile a Mumps program with Embedded SQL commands, you must use the long
form:
mumps2c sql.mps
ecpg sql.cpp
g++ sql.c -lmpscpp -lmumps -lmpsglobal_native -lpcre -lecpg -lpq
Where "sql.mps" is your Mumps Program with Embedded SQL and "ecpg"
is a pre-processor for Embedded SQL supplied by the PostgreSQL
package.
In this case, the Mumps Compiler translates your Mumps program to C++.
The "ecpg" program replaces the Embedded SQL commands with PostgreSQL
function calls and generates a program named "sql.c".
Finally, the C++ compiler compiles and links the result.
Note the extra link libraries on the "g++" command. The
default output of "ecpg" is a ".c" file although, in reality,
the contents of this file are C++. The GNU C++ compiler
does not seem to have a problem with the ".c" extension on
a C++ file. This may not be so on all versions
so renaming of this module may be required in somecases.
To compile a C++ program that has been created by the compiler, you must use the following
command (the mumpsc command will cause duplicate routines to be inserted):
g++ myprog.cpp -lmpscpp -lmumps -lmpsglobal_native -lpcre
To interpret a program, type:
mumps myprog.mps
The Mumps Interpreter runs your program from source code. Note: the interpreter is subject to several
restrictions and should not be used for production work. It is intended
for debugging and quick, interactive viewing of global arrays.
-
Generally speaking, in most cases you will receive syntax error messages from
which will identify the error and the line number
in the original Mumps program containing the error. When using the compiler,
in some cases, an error may be detected by
the C++ compiler. If you get C++ error messages, the line number
on the error message will refer to the line number in the C++ translation
of your Mumps program. To translate this to a line number in your Mumps program,
look into the generated .cpp file at the line number given in the C++ error message and then back
track to the nearest prior commented Mumps source line - this is the
line in your Mumps programs that caused the problem.
For example, if you get a message from the C++ compiler saying that
you have an error at line 1234 in the C++ module, open the
C++ file and move to line 1234. At that location you will
see something like:
/*=================================================================================*
svPtr->LineNumber=4; // write "the sum is: ",total,!
/*=================================================================================*/
if (svPtr->out_file[svPtr->io]==NULL) ErrorMessage("Write to input file",svPtr->LineNumber);
svPtr->hor[svPtr->io]+=fprintf(svPtr->out_file[svPtr->io],"%s","the sum is: ");
if (sym_(SYMGET,(unsigned char *) "total",(unsigned char *) tmp0,svPtr)==NULL)
VariableNotFound(svPtr->LineNumber);
svPtr->hor[svPtr->io]+=fprintf(svPtr->out_file[svPtr->io],"%s",tmp0);
fprintf(svPtr->out_file[svPtr->io],"\n"); svPtr->hor[svPtr->io]=0; svPtr->ver[svPtr->io]++;
|
Notice that each original line of Mumps code and its line number in the
original Mumps file appear in a comment prior to the C++ translation.
To find the line of Mumps code that caused the C++ error, look for the line of Mumps
code next preceding the line which the C++ complier flagged as being in error.
Generally speaking, you may receive C++ error messages if you reference non-existent
labels or subroutines, or incorrectly specify indented do blocks
(see below). It is not necessary to know C++ to do this.
Also, you may see ^M (control-M) characters in the code, especially
if you are viewing a MS WinXP file with a Linux editor. These
are visible due the differences bewteen the operating systems. Under
WinXP, each line ends in a a set of carriage-return and a line-feed characters.
Under Linux, each line ends in a line-feed character only. The control-M's
you see are the carriage-returns. They are harmless.
-
Mumps has two basic classes of variables: local and global. Local variables
reside in memory and exist during program execution (like normal variables)
while global variables reside on disk and continue to exist after
the program has terminated. Generally speaking, global and local variable names
can be used interchangeably except that all globals begin with a circumflex (^).
-
Unlike other languages which employ many data types,
Mumps has basically one data type - string. Strings that contain numbers,
however, can have arithmetic operations performed on them. Neither
global nor local variables need to
be declared - they will created as needed (note: an extension in the Mumps
Compiler permits scalar variables to be pre-declared in order to improve performance).
Variables can be destroyed by the KILL command.
-
All Mumps statements begin with a keyword. A keyword can be fully spelled out or, in
many cases, abbreviated. The common keywords are:
set read write if else halt hang use open close do for quit break
After a keyword there is one blank followed by (in most cases) an "argument".
An argument may not have any embedded blanks unless they are within quotes ("...").
Blanks are delimeters in Mumps and their use is restricted.
More than one command may be on the same line if there is one or more blanks
following the previous commands argument. Two or more blanks are required
if the previous command had no argument.
For example:
set a="abc" write a,!
quit:a=b write a,!
In the first line, both an assignment and write commands are on the same line.
In the second line, the quit has no argument so there are two blanks separating
it from the write
The figure :a=b in the quit is not an argument: it is called a post-conditional.
Post-conditionals are expressions that are evaluated before command execution.
If true, the command is executed. If false, the command is not executed. Most
commands may have post-conditionals attached to the command word.
-
Mumps programs that are to compiled begin with the command zmain which has no arguments and may not
be followed by any other text on the line.
Interpreted programs do not require zmain.
In this guide, zmain will normally appear
in each program. The interpreter ignores this line.
-
There is no precedence. All expressions are evaluated strictly left to
right unless you use parens. This can cause problems if you are
not careful. The expression:
a+b-c*d/e means: ((((a+b)-c)*d)/e)
Note that:
if a=b&c=d write "hello",!
means:
if (((a=b)&c)=d) write "hello",!
which is probably not what you wanted. You probably wanted:
if (a=b)&(c=d) write "hello",!
|
-
The main operators are +, -, *, /, \, #, **, _, >, <, [, ], ', ?, and @. See below for a description of the operators.
Note that there is an integer division operator (\) as well as a floating point division
operator (/).
-
Mumps has many functions for string processing. These are covered in the manual
and you should study them. Mumps also permits indirect execution, that is,
your program can create and execute code dynamically.
Language Specification
Introduction
The purpose of this section is to provide you with an introduction to
the Mumps language in general and the Mumps compiler. The Mumps language
originated in the mid-60's at the Massachusetts General Hospital. The acronym
stands for "Massachusetts General Hospital Utility Multi-Programming System".
It is a language which is similar in some respects to BASIC but it contains
many additional features not found in BASIC, or for that matter, in most
other languages. In its full form, Mumps is an interpretive language. In
fact, parts of the language specification require that it can never fully
become a "compiled" language such as FORTRAN, COBOL or PL/I. In the compiler,
some features, therefore, mainly those involving run time interpretation
and symbol table binding, are removed. See above for details.
Among the features which make Mumps attractive for both bio-medical
and general scientific applications are:
Hierarchical data base facility. Mumps data sets are not only
organized along traditional sequential and direct access methods, but also
as hierarchical trees whose data nodes are addressed as path descriptions
in a manner which is easy for a programmer to master in a relatively short
time.
Flexible and powerful string manipulation facilities. Mumps built-in
string manipulation operators and functions provide programmer's with access
to efficient means to effect complex string manipulation and pattern matching
operations.
Transportability to widely different systems Mumps presently
runs under a large number of operating systems on many machine architectures.
These systems range in size from small home micro-computers to the largest
central time sharing systems. Through efforts that have taken place by
the Mumps Development Committee over the years, a well organized language
definition has been written and formally published. This standard provides
for a far tighter specification for system performance and linguistic definition
than is normally the case. As a result, programs written under a Mumps
system can be moved with relatively little effort from one system to another.
Full numeric data handling facilities Mumps provides, in addition
to string handling facilities, a full range of fixed and floating point
computational facilities.
Data Types and Values
Basically, Mumps has only one data type: string, although it does allow
integer and floating point computations as well as logical expressions.
The values in a string may be any ASCII code from 32 to 126
(decimal) inclusive.
The Mumps Compiler does not permit usage of the
ASCII zero character (<NULL>). Ordinarily, strings will contain ASCII
printable characters. String constants are enclosed in double quote marks
("). A double quote mark can be included with two adjacent
quote marks (""). A constant containing only numerics (and, optionally,
plus, minus and decimal point), need not be enclosed by quotes (although
doing so has no effect). The following are examples of valid Mumps character
string constants:
|
"THE SEAS DIVIDE AND MANY A TIDE"
"123.45"
"BRIDGET O'SHAUNESSEY? YOU'RE NOT MAKING THAT UP?"
"""THE TIME HAS COME,"" THE WALRUS SAID"
|
When a string is being used as a number (e.g., in addition), the numeric
portion must be 20 characters or less in length. Numeric constants are
restricted to integer or decimal values (positive or negative).
If a string begins with a number but ends with
non-numeric characters, only the numeric leading portion will participate
in operations requiring numeric operands (e.g., add, subtract, etc.); the
trailing non-numeric portion is lost. On the other hand, if a string begins
with non-numeric characters, its value will be interpreted as 0. The following
are examples:
|
1+2 will be evaluated as 3.
"ABC"+2 will be evaluated as 2.
"1AB"+2 will be evaluated as 3.
"AA1"+2 will be evaluated as 2.
"1"+"2" will be evaluated as 3.
|
Only one plus or minus sign is permitted at the beginning of
a string or the value will be interpreted as 0.
Although "string" is the basic data type, Mumps converts strings internally
to floating point values (double) for calculations. Consequently, numbers are of
approximately 15 digit precision.
Internally, integers are stored as C++ language "long" and floating point
numbers as "double."
Logical values in Mumps are special cases of the numerics. A numeric
value of zero is interpreted as false while a non-zero value is interpreted
as true. Logical operators yield either zero or one and their results can
be treated like any other numeric. Similarly, the numeric result of any
numeric operator can be used as a logical operand. The results of string
operators are interpreted either as zero (leading characters non-numeric)
or some value (leading characters numeric). Strings and the results of
string operations can therefore participate as the operands of logical
operators.
Variables
Variables are named in Mumps in much the same manner they are named
in other languages. A Mumps variable name must begin with a letter (A through
Z) or percent sign (%) and may be followed by either letters or numbers.
Both upper and lower case letters may be used for variables names.
Mumps, in effect has only one data type so any variable name may be any
value. All Mumps variables are varying length strings. The maximum
string length permitted is determined when the compiler is
installed. This number is usaullyat least 1024.
In Mumps there are no data declaration statements. Variables come into
existence through the set or the read command and
pass from existence through the kill command.
In the Mumps, there are two kinds of arrays: internal arrays and global
arrays. The following pertains to internal arrays: arrays are not dimensioned.
A name used as an array variable may also, at the same time, be used as
a scalar. Array values are created by assignment or appearance in a read
statement. If you create an element of an array, let us say element 10,
it does not mean that Mumps has created any other elements: that is, it
does not imply that there exist elements 1 through 9. You may specifically
create these or not. Array indices may be positive or negative numbers
or character strings. Arrays in Mumps may have multiple dimensions. The
following are some examples of arrays:
|
set a(1,2,3)="ARRAY"
read test(22)
write test(22)
set i=10 set a(i)=10
set a("TEST")=100
set i="TESTING" set a(i)=1001
set a("MUMPS","USERS'","GROUP")="MUG"
|
Global Arrays
Global arrays may be viewed either as multi-dimensional matrices or
as tree structured hierarchies.
As matrices, data may be stored not only at fully
subscripted matrix elements but also at other levels. For
example, given a three dimensional matrix mat1, you could
initialize it as follows:
|
for i=0:1:100 do
. for j=0:1:100 do
.. for k=0:1:100 do
... set ^mat1(i,j,k)=0
|
In this example, all the
elements of a three dimensional matrix of 100 rows, 100 columns and
100 planes are initialized to zero.
Unlike other programming
languages, however, there are additional nodes of the matrix which
could have been initialized such as indicated by the following example:
|
for i=0:1:100 do
. set ^mat(i)=1
. for j=0:1:100 do
.. set ^mat(i,j)=j
.. for k=0:1:100 do
... set ^mat1(i,j,k)=0
|
In effect, this means
that mat1 can also be a single dimensional vector, a two
dimensional matrix and a three dimensional matrix simultaneously.
Furthermore, not all elements of a matrix need exist. That is, the matrix can be sparse.
For example:
|
for i=0:10:100 do
. for j=0:10:100 do
.. for k=0:10:100 do
... set ^mat1(i,j,k)=0
|
In the above, only index values 0, 10, 20, 30, 40, 50, 60, 70, 80, and 90
are used to create each of the dimensions of the array and only those elements of the
matrix are created. The omitted elements do not exist.
Global arrays are unique to Mumps. As a programmer, you will work with
them as though they were arrays. The system, however, interprets them as
tree path descriptions for the system's external data files. A global array
is distinguished by beginning with the circumflex character (^). The remainder
of the specification is the same as an internal array. global arrays are
not dimensioned and they may appear anywhere an ordinary variable may appear
(except in certain forms of the "kill" command). A typical global array
specification consists of the array name followed by some number of indices
(indices may be constants, variables [including internal or global arrays]
or expressions of string, numeric or mixed type). For example:
|
set ^a(1,43,5,99)="TEST"
set ^ship("1ST FLEET","BOSTON","FLAG")="CONSTITUTION"
set ^captian(^ship("1ST FLEET","BOSTON","FLAG"))="JONES"
set ^home(^captain(^ship("1ST FLEET","BOSTON","FLAG")))="PORTSMOUTH"
write ^ship("1ST FLEET","BOSTON","FLAG")
... CONSTITUTION
write ^captain("CONSTITUTION")
... JONES
write ^home("JONES")
... PORTSMOUTH
write ^home(^captain("CONSTITUTION"))
... PORTSMOUTH
write ^home(^captain(^ship("1ST FLEET","BOSTON","FLAG")))
... PORTSMOUTH
|
The system files are viewed as trees. Each global array name ("A", "SHIP",
"CAPTAIN", and "HOME" in the above) is the root of a tree. The indices
are thought of as path descriptions to leaves. For example, out of the
root "^a" there may be many branches, the above specifies to take the branch
labeled "1" (note: this does not mean the "first" branch out of the node
- it means the branch with label "1"). At the second level the specification
says to take the branch labeled "43" (note: this does not imply that branches
1 through 42 necessarily exist). The path description is followed (or,
possibly, created if the global array specification appears on the left
hand side of an assignment statement or in a read command) to a final
node. The value at the node is either retrieved or a new value stored depending
upon the context in which the global array specification was used. The
indices of global arrays may be numeric or character strings. The second
sequence of examples above illustrates this usage.
Both string and character indices may be mixed in the same path description.
A value may be stored at any position in the tree. For example:
|
set ^A(1,43,5)=22
set ^A(1,43)="TEST MIDDLE LEVEL"
|
Mumps arrays are not pre-declared
and they are sparse. That is, only those elements which you explicitly create
actually exist. For example, if you create element ^A(10), it does not
necessarily mean that elements ^A(1) through ^A(9) exist.
Global arrays are often interpreted as trees where each successive index describes the path through
a multi-way tree. At each node, data can be stored (or not). The path from
the root to a node is given by the sequence of indices of an array
reference. The data base can store many trees, each distinguished by
their array name.
The Mumps global array facility is due to the early uses of Mumps in medical data
bases which are basically hierarchical in nature.
The Mumps global arrays were a solution to the problem of how to
represent the tree-like structure of patient data in a simple
and easily manipulated data structure.
For example, consider a basic patient record. At the top level is the
patient's id node at which is stored the patient's name. At
the second level, are nodes for demographic (address, gender, phone number,
etc.) data and the main entry node for clinical data. Clinical data
is organized by diagnostic or problem category and each problem or
diagnostic code is divided into episodes of the problem organized by
onset date. For a given problem and onset, the data are divided
by category (medications, lab tests, orders, notes, etc.) which
are further subdivided by, for example, in the case of lab tests,
test, date, time and result.
For example:
Here, the tree is named patient which is also the name of the global array
(notice that global arrays always have a circumflex (^) preceding their name).
The Mumps code to populate the above might look like:
set ^patient("123-45-6789")="Jones, John, J"
set ^patient("123-45-6789","Demographics","Street")="123 Elm St"
set ^patient("123-45-6789","Demographics","City")="Anytown"
set ^patient("123-45-6789","Demographics","State")="IA"
set ^patient("123-45-6789","Demographics","ZIP")="50613"
set ^patient("123-45-6789","Dx",789.00,"6/23/2005")="Dr Smith"
set ^patient("123-45-6789","Dx",789.00,"6/23/2005","lab","HCT","6/23/2005","10:45",45.2)=""
set ^patient("123-45-6789","Dx",789.00,"6/23/2005","lab","HCT","6/23/2005","20:45",43.2)=""
set ^patient("123-45-6789","Dx",789.00,"6/23/2005","lab","HCT","6/24/2005","21:10",44.2)=""
set ^patient("123-45-6789","Dx",789.00,"6/23/2005","lab","HCT","6/25/2005","14:10",44.2)=""
|
Notice that the empty string can be stored at a node. In these cases, the actual data
(the lab test result) is the value of the final index. Also note that each intermediate node
need not be created. The nodes representing ""Demographics", "lab", "Dx", HCT, and others
are not explicityly created. Their creation is implicit in constructing the longer
paths of which they are intermediates.
Writing Programs
Mumps was originally conceived as a language to be executed by
interpreters. Consequently, the syntax and structure of
commands tend to be line oriented and simple to parse.
Each command in Mumps begins with a key word. The keyword may
be abbreviated, in many cases to a single letter
(this was to save storage and time on the early interpreters
but has no effect on efficiency in the Mumps Compiler).
A command is followed optionally be a post-conditional expression
and then, in most cases by one or more arguments.
The blank character is a terminator in Mumps so the placement of blanks is
important. A blank is used to separate a command keyword from
its arguments and to terminate the list of arguments. If
a command has no arguments and there are others commands following
on the line, the command (or, the command and its post-conditional)
are followed by at least two blanks. Blanks may not be embedded
in expressions except in quoted fields.
A post-conditional is an expression that will be evaluated
prior to executing the arguments of a command. If the expression
is true, the arguments will be executed. If the expression
is false, the arguments will not be executed and control will
move to the next command. Some commands, such as the if and
the else may not be post-conditionalized. Other
commands, such as the do and the goto may not
only be post-conditionalized at the command level, but also
at the argument level. Most commands are only post-conditionalized
at the command level. Here are some valid commands with and
without post-conditionals (";" causes the remainder of the line to be
interpreted as a comment):
The basic assignment statement in Mumps is the set
statement. It may have one or more arguments. The left hand
side is evaluated and the result is assigned to the variable
on the left hand side. Expressions in Mumps are evaluated
strictly left-to right without precedence. If you want
a different ordering of the evaluation, you must
use parentheses. This is true in any Mumps expression in any
Mumps command. It is a common source of error, especially
in if commands with compound predicates.
Variable in Mumps, as noted above, can be local, that is,
present only during the execution of the program, or global,
which is to say, disk resident and persistent from
one execution of the program to the next. Both local
and global variables may be either scalar or arrays
although most local variables tend to be scalar and
most global variables tend to be arrays in practice.
A variable, either global or local, is created by
appearing on the right hand side of a set statement (or
as an argument in a read statement).
The Mumps symbol table is dynamic and variables
are created as needed. They may also be destroyed
(kill).
Expressions may involve both local and global variables,
operators (see below) and constants. Constants may be
either numeric or string. String constants are enclosed
in double quits (use two immediately adjacent double
quotes to create a single double quote in a quoted
string). Strings are converted to numerics as needed
and back again. The basic internal data type is string.
For example (in each of these, the item written
is followed by a <new-line> character due to
the "!"):
|
set i=2 write i,! ; writes 2
set i=2*3 write i,! ; writes 6
set i="2"*3 write i,! ; writes 6
set i=" 2"*3 write i,! ; writes 6
set i="""hello""" write i,! ; writes "hello"
set ^a="test" write ^a,! ; writes test
set a="test" write a,! ; writes test
set ^a(1)="tst" write ^a(1),! ; write tst
set a(1)="tst" write a(1),! ; write tst
|
The last four examples use global and local scalars, respectively.
The input/output commands are read and write.
They may each use formatting codes. The formatting codes
are:
|
! - new line (!! two new-lines, etc.)
# - new page
?x - advance to column "x" (new-line if needed)
|
The read command may contain out put codes
and string constants along with the variable
names to be read. This permits prompts to
be embedded in the read (note: this only
applies when reading from the console). For example:
read !,"enter name:",?20,x
This will cause the prompt "enter name:" to be written at the
beginning of a new line (!) and then the cursor will
tab to column 20 and await the user's input which will
be stored in variable "x".
Examples of the write command:
|
write "hello world",!
set i="hello",j="world" write i," ",j,!
set i="hello",j="world" write i,!,j,!
write 1,?10,2,?20,3,?30,4,!!
|
The first example above writes "hello world" followed by
a new-line. The second does the same.
The third writes "hello" and "world" on separate lines.
The fourth writes 1, 2, 3, and 4 in columns 10, 20,30
and 40, respectively followed by two new-lines.
The argumented form of theif statement Mumps tests one or more
expressions for true and does or does not execute the remainder
of the line depending upon the result. A value is
considered to be "true" if it is non-zero and "false"
if zero. If there are multiple arguments, each
argument must be true. Note that the if
command has as its scope the entire remainder of the
line on which it appears, not just the next command.
As a side effect, the if statement sets the
system built-in variable $test to "true".
And's ("&") and or's ("|") may be used in expressions
along with not's ("'"). The relational operators
are cited below.
Examples:
|
1 set i=1,j=2,k=3
2 if i=1 write "yes",! ; yes
3 if ij write "yes",! ; yes
5 if ij write "yes",! ; does not write
6 if ij) write "yes",! ; yes
7 if i write "yes",! ; yes
8 if 'i write "yes",! ; does not write
9 if '(i=0) write "yes",! ; yes
10 if i=0|(j=2) write "yes",! ; yes
|
Note the multiple arguments on line 4 above. This effectively constitutes
an "and" operation. Also note line 5. Due to strict left-to-right,
non-precedence parse, the expression is interpreted as though
it had been written "(((i<j)&k)>j)". The "i<j" is true (1); "1&k"
is true (1) but "1>j" is false so the expression is false.
The expression on line 6 is probably what was wanted.
It is very important to include parentheses in compound expressions
to express precedence.
The argumentless form of the if statement (which is followed by two
or more blanks) tests the current value of $test
and does or does not execute the remainder of the line depending
upon if $test is true (1) or false (0).
For example:
|
set i=1
if i=1 write "yes",!
if write "yes again",!
|
The above will write "yes" and "yes again" because the first if
sets $test to true.
A negative side effect of this is that $test can be reset to
false in the scope of the first if:
|
set i=1
if i>1 write "yes",! if i>2 write "yes yes",!
if write "yes again",!
|
The above will write "yes" only. The second if on the
second line sets $test to false.
For compiled code, $test is restored to its
previous value upon exit from a block:
|
set i=1
if i=1 do
. if i=0 write 123,!
else write 987,!
|
The false if expression sets $test only
within the block. The previous value is
restored on exit.
Note: $test is not restore from block exit in
the interpreter.
The principle iterative command is the for command.
It has the syntax of a local, scalar index variable followed
by an equals sign followed by one or more arguments.
The arguments may either be specific values the
index variable should have or a range specification.
A range specification is either two numbers separated
by a colon or three numbers separated by two colons.
In both cases, the first number is the initial value
for the index variable, and the second is the increment
(or decrement if negative). The third number, if
present, is the limit value. If no third number is specified,
there is no upper limit and some other mechanism must terminate
the loop. For example:
|
for i=1:1:10 write i,! ; 1,2,...9,10
for i=1,3,5,7 write i,! ; 1,3,5,7
for i=10:-1:0 write i,! ; 10,9,...2,1,0
for i=1:1 write i,! ; 1,2,3 ...
for i=1:1:10,100,200 write i,! ; 1,2,...9,10,100,200
for i=10,20,30:1:40 write i,! ; 10,20,30,31,...40
|
An argumentless for loops forever until stopped by
another command. The quit command will
terminate the nearest command on the current line:
|
for i=1:1 write i,! quit:i>10 ; 1,2,...9,10
|
If there are multiple for commands, the quit
terminates the nearest:
|
for i=1:1:10 for j=1:1 write j,! quit:j>5
|
the above will write 1 through 5 a total of 10 times.
The for is often used with the argumentless form
of the do command. The argumentless form of the
do assumes there will be a block of code immediately
following the current command line. The block will be executed.
For example:
|
for i=1:1:10 do
. write i
. write " ",i*i,!
|
The above will write the numbers 1 through 10 followed
by the square of the number.
In the compiled code, when you enter a block by means of an argumentless
do, the value of $test is stored
and restored on normal exit from the block.
If you exit the block by means of a goto,
the value of $test is not restored.
$test is not restored under any circumstances in the interpreter.
In the context of a block, the quit command
means to quit the block, not the for.
For example:
|
for i=1:1:10 do
. write i
. if i>5 write ! quit
. write " ",i*i,!
|
The above will write 5 lines with numbers and their
squares and 5 lines with just the numbers (6 through 10).
The quit terminates the block but not the for.
A break (this is a non-standard feature of the
Mumps Compiler) may be used to exit a block:
|
for i=1:1:10 do
. write i
. if i>5 write ! break
. write " ",i*i,!
|
The above will only write 5 lines. The break
terminates the block and the for. A break
may only be used in a block. Alternatively, the
quit could be used as follows:
|
for i=1:1:10 do write ! quit:i>4
. write i
. if i>4 quit
. write " ",i*i
|
The above will write 5 lines, 4 of which have the number and its square.
Note the two blanks after the argumentless do.
Another form would be:
|
set i=1
for do
. write i," ",i*i,!
. set i=i+1
. if i>10 break
|
The above will write 10 lines, each with two numbers.
Blocks may be nested but the break only applies to the
block in which it appears:
|
set i=1
for do
. write i," "
. set j=1
. for do
.. write j," "
.. set j=j+1
.. if j>10 break
. write !
. set i=i+1
. if i>10 break
|
The above will write ten lines, each with 11 numbers
(1 number for the outer loop and 10 numbers due to the inner
loop).
The do command is also used to invoke
subfunctions. See the discussion and examples
below under the do command.
For input/output, Mumps uses a scheme based on unit number.
The Mumps compiler permits 10 unit numbers.
By default, your program begins using unit 5, the console
screen and keyboard (stdin and stdout in
Linux terminology). You may open files and associate
them with unit numbers through the open
command and disassociate files from unit numbers
with the close command. It is important that
you close files that you have used for output
as this will insure that the buffers have been
written to disk. The use command
tells the system which I/O unit to use. This
unit remains in effect until changed.
For example, the following copies the contents of
file "aaa.dat" to file "bbb.dat" which is
created if it did not previously exist:
|
zmain
open 1:"aaa.dat,old"
open 2:"bbb.dat,new"
write "copying ...",!
for do
. use 1
. read rec
. if '$test break
. use 2
. write rec,!
close 1,2
use 5
write "done",!
|
Each read command sets $test to true
is successful. On end of file, $test is
set to false and the loop terminates.
the ",old" and ",new" markers indicate if the
file is being opened for input (",old") or output
(",new").
Example Programs
Mumps is a very simple but powerful string manipulation and data base
scripting language. It is similar in structure to early forms of
BASIC and can generally be learned in a few hours. The following
are a series of examples of Mumps programs and explanations
of the code.
-
Program to sum the first 100 numbers:
|
zmain
set total=0
for i=1:1:100 set total=total+i
write "the sum is: ",total,!
|
Notes:
- A program can be written using a text editor such as
vi, emacs or some other editor that does not embed
special characters in the text (as some word processor
programs do).
-
Lines of Mumps code may begin with
a blank, a plus sign (+), a pound sign (#), a character, or a letter.
-
If a line begins with a pound sign, the remainder of the
line is taken as a comment and not processed by
the compiler.
-
If a line begins with a plus sign, the remainder
of the line is taken to be C++ code and is passed
unchanged to the C++ compler.
-
If the line begins
with a letter, the word beginning with the letter
up to the first blank on the line is taken as a label.
There must be at least on blank following a label.
-
If the line begins with a character
or one or more blanks, the line has no label. The
code for the line begins at the first non-blank character.
-
Each line has one or more commands, each beginning with
a keyword. Most commands have operands which
are separated by exactly one blank from the
command word. In cases where the command has no
operands, the command is either the final
item on a line or it is seprated from the next command
by two or more blanks. Operands to commands may
not have embedded blanks except in
quoted fields.
-
Every program has at least one zmain command that identifies the
main program. The zmain must be on a line by itself.
-
In the example above, the set introduces the assignment
command. The variable "total" is created (first use)
and assigned the value zero.
-
The for command is the iterative command and can have 3 arguments: starting value, increment and limit. It can also have
other forms (see below). The scope of the for command shown is the current line.
-
The write command writes variables, strings, numbers, etc.
Each element is separated from the others by a comma.
The "!" means new line. You may also use something of the form ?15 to mean tab
to position 15 (a numeric constant is required).
-
Program to read a file of numbers and sum them.
|
zmain
set sum=0
for do
. read val
. if '$test break
. set sum=sum+val
write "sum is ",sum,!
|
-
The program reads numbers until it receives an end-of-file.
The loop cotrol is a the for command
with no arguments. Note there are 2 blanks
after the word for. These are REQUIRED as there is no argument.
A for with no arguments loops until a quit
(single line context) or a break (multiple line do
context).
-
The input command in Mumps is read.
It reads from the current I/O device (see below, by default, this is stdin).
Mumps reads a line at a time thus multiple values should, in this
case, be on separate lines (note: there are, however, functions to extract separate
values from a line - see below).
-
The do command is used to invoke a section of code as a subroutine.
It often is followed by an argument giving the
name of the entry point of the code module being
invoked.
However, you use the do without an argument if you want to invoke a block of code
contained on a group of lines immediately following in the code.
The block of lines must must each begin with one or more "." characters to express the
level of depth and in order to tell the compiler the extent of the block.
You may have blocks within blocks. Nested blocks have more
leading "." characters. A block ends when the indent (".") level reduces.
In the example above, the block of code invoked by the do
command is the three lines immediately following the do, each
having one ". " preceding the code on the line.
-
Although the for has single line scope, the do effectively
extends the scope to subsequent lines.
The break, in this case, is a non-standard extension
that exits both the do and its controlling for.
-
A break is not permitted
in a single line for - use quit. A quit on a single line for
terminates a for. HOWEVER, a quit in a do
block means "continue" - that is, skip the remainmder of the block and return to the
command following the controlling do. This often results in the block being re-invoked.
-
The builtin variable "$test" indicates if something worked or not.
If the previous read succeeded (i.e., did not achieve end-of-file),
"$test" is true, false otherwise.
The single quote "'" is the "not" sign so the expression in
the if command means "not $test" which will be true if the read
failed (end of input).
-
Some Mumps commands such a for and if
have a scope that extends through the end of the line on which they
appear. This can cause some unusual consequences.
For example, if the previous program could have also been written with multiple
commands following on the same line as the for as:
|
zmain
set sum=0
for read val if '$test quit set sum=sum+val
write "sum is ",sum,!
|
it would not work. This is because of the if command. If the
if expression is not true, the remainder of the line is not
executed. Thus, if the read were successful, the expression in
the if would be dalse and the remainder of
the line, including the set
statement, would not execute.
There is a way around this sort of problem:
|
zmain
set sum=0
for read val quit:'$test set sum=sum+val
write "sum is ",sum,!
|
Here the "not $test" is applied as a postconditional (note the ":") to the
quit command. When a postconditional is applied, the
command is only effective if the postconditional is
true. the quit command on a one line for statement
means to terminate the loop.
Because the quit has no argument (a postconditional is not an argument),
at least two blanks are required before the next command.
Most commands may have postconditionals.
Overall, you should probably use the do and a dotted indet block.
-
Write a program to read a line of text and separate the words.
|
zmain
read line
set line=$zb(line) // replaces multiple blanks by one blank
for i=1:1 do
. set word=$piece(line," ",i) // separates the string delimited by " "
. if word="" break
. write word,!
when given the input:
How much wood would a wood chuck chuck if a wood chuck would chuck wood?
produces:
How
much
wood
would
a
wood
chuck
chuck
if
a
wood
chuck
would
chuck
wood?
|
Notes:
-
The function $zb() returns a string from which multiple blanks have been
replaced with single blanks. Builtin functions in Mumps begin with a
dollar sign ($). Functions whose names begin with a "z" are extensions
from the legacy language standard. A complete list of these functions
can be found in the manual.
-
The for of the for command in the example initializes "i" to 1 (the first
constant after the equals sign) and increments it by 1 (second constant)
after each iteration. There is no upper limit.
-
The builtin function $piece(source,pattern,start[,end])
returns a string from "source" delimited by "pattern"
The string returned follows the "start"-1 instance of the pattern
and ends at the "end" instance of pattern if "end" is present,
"start" otherwise. An empty string is returned if the function
cannot extract the requested piece of the source string.
For example:
|
set a="abc.def.ghi.jkl"
write $piece(a,".",1) -> abc
write $piece(a,".",2) -> def
write $piece(a,".",1,2) -> abc.def
|
-
Write a program to read two numbers and a numeric operator,
perform the operation and report the result. That is,
use indirection (interpretation).
|
zmain
read first
read second
read op
if first'?.N write "operand 1 must be numeric",! halt
if second'?.N write "operand 2 must be numeric",! halt
if $length(operator)'=1 write "operator must be a single character",! halt
if $find("+-*/#",op)=0 write "unknown operator",! halt
set str=first_op_second
write str,"=",@str,!
input:
10
2
*
output:
10*2=20
|
Notes:
-
The expression first'?.N is an example of the pattern match operator (?).
It means "first not-pattern .N" or, the expression is true if the contents
of first are not consistent with the pattern ".N". The figure ".N" means "some number of numerics"
("." means any number - including zero, "N" means numeric). See the manual for
other examples and pattern matching codes.
-
The halt command terminates the program.
-
The $length() function returns the length of a string.
The $find() function (see manual) returns an integer that is zero (false) if
the second string is not found in the first string. It returns a
positive integer if it is found. See the manual for a description
of what the integer means.
In the case of the example, the function is used to determine if the
operator is one of the allowed operators.
-
The "_" operator means concatenate two strings together.
-
The "@" is the indirection or interpretation operator. It causes the
expression following it to be evaluated and executed. Interpretation
is slow but handy.
In the example, the string "str" conrtains an expression composed of
the values read in from the keyboard with the operator between them
(an arithmetic expression).
When the "@" operator is applied to a string containing
a valid Mumps expression, the system evaluates the expression
(at run-time) and returns a string containing an answer.
Some legal forms for indirection:
|
write @"2+3",! // writes 5
set ^a(1)="3*3"
write @^a(1),! // writes 9
set ^a(9)="4+4"
set i="8+1"
write @^a(@i),! // writes 8
|
Indirection should be used sparingly since it is inefficient.
-
You can trap errors from the interpreter with:
|
zmain
set x="1/0"
try NoMessages set i=@x
catch InterpreterException write "error",!
halt
|
-
The statement:
write @(first_op_second),!
also works.
-
Write a program that reads in an expression and then applies it to each line a file:
|
zmain
read "enter expression: ",exp
open 1:"input.dat,old"
if '$test write "file open error",! halt
for do
. use 1
. read line
. if @exp use 5 write line,!
example input expression:
$find(line,"abc")
|
Notes:
-
The program will, by interpretation, apply the expression to
weach line of input. If the expression is "true", the
input line will be printed.
-
The "open" statement opens an input file. Open files are referred to
by unit numbers that you pick (1 thru 4, normally). The ",old" part means
the file exists. If you are creating a file, use ",new".
If the file open fails, $test is false.
-
The unit number for your keyboard/screen is 5. It is the
only unit number that you can both read and write too.
A common error is to try to write to an input file or
read from an output file.
When you do a read or write, Mumps uses the current unit number, usually
5. If you want another, use the use command. After a unit number
is specified, it remains in effect until changed.
-
Write a program to read a file and build a dictionary of the words
found in the file and count the total number times each word occurs.
Input is fom stdin.
|
zmain
for do
. read line
. if '$t break
. set line=$zb(line) // replaces multiple blanks by one blank
. for i=1:1 do
.. set word=$piece(line," ",i) // separates the string delimited by " "
.. if word="" break
.. if '$data(^dict(word)) set ^dict(word)=1
.. else set ^dict(word)=^dict(word)+1 // caution - "else" isn't what it seems
set word=""
for do
. set word=$order(^dict(word))
. if word="" break
. write word," ",^dict(word),!
|
-
The $piece() function returns the ith piece of the first string delimited by the second.
-
The $data() function is true if the global array node exists. It is false otherwise.
-
The (i>$order() function returns the next ascending value of the final index of the
global array argument or the empty string if there are no more.
Indices are retrieved in collating sequence order (ASCII).
If the value of the argument is the empty string, the function
returns the value of the first index, if one exists.
On sucessive iterations, the value to the function is the current index
value which results in retrieving the next index value, if one exists.
- The else command (notice the two blanks after it - it has no
arguments) is not really connected to the if statement. In Mumps,
else checks the value of $test and executes the remainder of the
line if $test is false. No preceding if
is required - the else is purely related tio $test.
One consequence of this is that if you have an if statement
that executes (its expression is true), and an else on the following
line, if the line executed as a result of the if causes $test
to become false, the subsequenct else will execute!
Note also, an if command may also have no arguments (followed
by at least two blanks). If this is the case, it tests $test and executes the
remainder of the line it is on if $test is true.
-
Notice the double nested loops expressed by the level indent dots.
The outer loop reads input while the inner loop processes each line. When the inner
loop terminates, the outer loop interates.
-
The program reads a line from standard input, removes extraneous blanks,
the extracts successive words from the line (until the string returned
from $piece() is empty). Each word extracted is checked
against the data base. If the word is not in the global array as an index
($data() returns non-zero), intialize the global array element for this word as zero;
otherwise, increment the value stored in the
global array - the count of the number of times this work has occurred.
When the input is exhausted, the program prints the words (indices) and the count
stored at each one.
-
Write a program to produce an indented display of the indices of a global array tree.
Assume the tree has no more than 4 levels of depth.
Notes:
-
The program is written as a set of four nested for loops.
-
The outer loop iterates the first index; the first nested
loop iterates, the second index, and so forth.
-
Should the global array have fewer than four levels of
indices, the program willl still work. If there are no
indecies as a given level three, for example, the third
nested loop will immediately return an empty string and the
loop will terminate. The fourth nested loop will not be attempted.
-
Notice that prior to each loop, the index variable is set to
the initial value of empty string and that the value returned
by $order() is tested for empty string. The first
value presented to $order() is empty and the last value returned
is empty.
|
zmain
for i=1:1:5 for j=1:1:5 for k=1:1:5 for l=1:1:5 set ^tree(i,j,k,l)=i
for lev1="":$order(^tree(lev1)):"" do
. write lev1,!
. for lev2="":$order(^tree(lev1,lev2)):"" do
.. write ?5,lev2,!
.. for lev3="":$order(^tree(lev1,lev2,lev3)):"" do
... write ?10,lev3,!
... for lev4="":$order(^tree(lev1,lev2,lev3,lev4)):"" do
.... write ?15,lev4,!
Both programs produce output such as:
1
1
1
1
2
3
4
5
2
1
2
3
4
5
3
1
2
3
4
5
4
1
2
3
4
5
5
1
2
3
4
5
2
1
1
2
3
4
5
2
1
2
3
4
5
3
1
2
3
4
5
4
1
2
3
4
5
5
1
2
5
.
.
.
|
In the example, the for loops initialize, increment and terminate the
loops automatically. Each loop initializes its loop variable with
the empty string, immediately runs the incrementing function, and tests the
results for empty string. If the empty string is returned, the loop terminates.
Otherwise, the loop body is executed.
Notice that after initialization, the increment is done immediately
thus providing the true initial value for the loop variable.
This occurs only when a function replaces the increment position.
Normally, in iterative loops, the loop is executed once with the initial value prior
to adding the increment.
There are other extended forms fo the for command. See the manual.
Operators
-
Arithmetic unary operators: + -
The arithmetic unary operators are: + and -. The plus operator (+) has
no effect other than to force the expression to its right to be interpreted
as numeric. The minus operator forces numeric interpretation and negates
the result. For example:
|
set I="123 ELM STREET"
write +I yields 123
write -I yields -123
|
-
Arithmetic binary operators: + - * / \ **
The addition (+), subtraction (-), multiplication (*) and exponentiation
(**) operators perform in the normal manner. Operands are given a numeric
interpretation if necessary. Operands may be either expressions, constants,
variables or array references. Results are computed in floating point if
appropriate. Mumps has two division operators: full division (/) and integer
division (\). Full division give results which may have fractional parts.
Integer division truncates the answer to an integer.
The modulo operator (#) gives the left operand modulo the right operand.
The following are examples:
|
2+3 yields 5
2.31+1 yields 3.31
3-5 yields -2
7/4 yields 1.75
7\4 yields 1
11#3 yields 2 (please see compiler notes)
3**2 yields 9
|
-
Arithmetic relational operators: > <
The greater than (>) and less than (<) relational operators compare
numbers. If the operands are not numbers, they are given a numeric interpretation.
The result is either zero for FALSE or one for TRUE. For example:
|
1>2 yields 0
2>1 yields 1
1<2 yields 1
2<1 yields 0
|
Both operators may be negated producing not greater than ('>) and not
less than ('<) (note: the single quote mark is the negating operator).
There is no "less than or equals" or "greater than or equals" operators
as such. For example:
|
1'>2 yields 1
2'>1 yields 0
1'<2 yields 0
2'<1 yields 1
|
-
String binary operator: _
The only binary string operator is concatenation (_) represented by
an underscore character. The following are examples:
|
"ABC"_"XYZ" yields "ABCXYZ"
"ABC"_123 yields "ABC123"
123_456 yields 123456
|
-
String relational operators: = [ ] ]] ?
The equals relational operator (= and '=) tests for equality as in the following
example:
|
if "ABC"="ABC" write "EQUALS"
if "ABC"'="ABC" write "EQUALS"
|
We would expect that "EQUALS" would be written to the terminal in the
first case and not in the second. The
not-equals operator is formed by the single quote mark and the equals sign.
The equals and not-equals operator may be used with strings or numbers.
The contains operator ([ and '[) determines if the right hand operand is contained
in the left hand argument. For example:
|
set A="NOW IS THE TIME"
if A["THE" write "YES"
if A'["THE" write "YES"
|
The word "YES" would by printed on the terminal for the first
example and not printed for the second.
The follows and sorts after operators (] '] ]] and ']]) are used to test if the
left hand operand follows
the right hand operand in the collating sequence. For example:
|
set A="ABC"
if A]"AAA" write "YES"
if A]]"AAA" write "YES"
if A']"AAA" write "YES"
if A']]"AAA" write "YES"
|
The word "YES" would be printed at the terminal for the first two exmples and
not printed for the second two.
The pattern matching operator (? and '?) is used to determine if a string conforms
to a certain pattern.
Pattern match operations are converted to Perl Compatible Regular
Expressions and are executed by functions in the PCRE
library which must be present.
You may access the PCRE directly, using Perl
expression format with the ^perlmatch(string,pattern)
function discussed in Appendix E.
The Mumps patterns are:
|
A for the entire upper and lower case alphabet.
C for the 33 control characters.
E for any of the 128 ASCII characters.
L for the 26 lower case letters.
N for the numerics
P for the 33 punctuation characters.
U for the 26 upper case characters.
A literal string.
|
A pattern code is made up of one or more of the above, each preceded
by a count specifier. The count specifier indicates how many of the named
item must be present. Alternatively, an indefinite specifier - a decimal
point - may be used to indicate any count (including zero). For example:
|
set A="123-45-6789"
if A?3N1"-"2N1"-"4N write "OK"
if A'?3N1"-"2N1"-"4N write "OK"
set A="JONES, J. L."
if A?.A1",".A write "OK"
if A'?.A1",".A write "OK"
|
Extensions to the pattern syntax suggested by the Mumps 95 standard,
including support for alternation, are supported as described in
Appendix D.
-
Logical operators: &, ! '
The logical operators AND (&), OR (!) and NOT (') may be applied
in the usual manner. The user should note, however, that since Mumps has
strict left-to-right precedence, the results can sometimes be odd:
|
1&1 yields 1
2&1 yields 1
1&0 yields 0
1&0<1 yields 1
1&(0<1) yields 1
1!1 yields 1
1!0 yields 1
0!0 yields 0
2!0 yields 1
|
The "NOT" operator may be used in conjunction with other operators to
form compound operators. The resulting compound operators are:
|
'< not less than
'> not greater than
'= not equal
'[ not contains
'] not follows
'? not pattern
|
-
The indirection operator: @
The indirection operator permits direct interpretation of string
expressions. For example:
will result in the value "4" being written to the current output device.
Commands
Each statement in Mumps begins with a unique command word. Often the
command word is abbreviated to a single character. The single character
abbreviations are unique for all commands except those which begin with
the letter "Z". For commands not beginning with the letter "Z", Mumps does
not check the spelling of the command word if more than one character of
the spelling is given. Generally speaking, only the first letter is used
to determine the command. Thus "write", "W", and "WRIGHT" all have the
same meaning.
The format of a command normally consists the command word or letter
followed (optionally) by a post-conditional, followed by exactly one blank
followed by the arguments to the command. Most commands can have multiple
arguments. Multiple arguments are delimited by commas. If a line is to
have more than one command, the first command is delimited by exactly one
blank and the next command word or letter follows immediately. Blanks are
very significant in Mumps.
As noted above, most commands may be "post-conditionalized". A post-conditional
is a logical expression which is used to determine if the command (and
all its arguments) should be executed. It is like a small "if" statement.
A post-conditional appears as a colon followed by an expression. If the
expression evaluates to 0 (false), the command (or argument) is not executed. If the
expression evaluates non-zero, the command or argument is executed.
The following are examples of the above:
an ordinary assignment statement:
same as above with command word abbreviation:
an assignment statement with multiple arguments:
|
s I=10*5,J=5,K=I+J ;(K will equal 55)
|
an assignment statement post-conditionalized:
|
s:I=10 J=0 ;(set J to zero if I equals 10)
|
a multiple command line:
|
s I=10*5 s j=5 s S=I+J ;(same as above)
|
Table of Commands
-
break (abbreviation: b)
Mumps usage is non-standard.
It may be used to exit an indented block. It may not be used
in any other context.
For example:
|
for i=1:1:10 Do
. write i,!
. if i>5 break
|
The above will print 1 through 6.
See Notes on break and quit for
further details.
-
catch (abbreviation: ca)
A catch command immediately follows a try command. If one
of the commands on the try command line fails,
the catch is entered if the cause of the failure is the
exception noted in the argument to the catch command.
The permitted arguments are:
- InputLengthException
- InterpreterException
The try command may be followed by one or
more optional control words. If no control
words are present, the command MUST be followed by
at least two blanks.
The optional control words are:
-
NoMessages
Used when invoking the interpreter to suppress interpreter
error message generation. If NoMessages is not present and
there is an error detected during interpretation, the
interpreter will generate specific error messages to
stdout.
Example:
|
try read a
catch InputLengthException write "line too long",! halt
write a
set a="2+2"
try NoMessages write @a,!
catch InterpreterException write "expression error",!
|
-
close (abbreviation: c
The close command closes a unit number and makes it available for other
uses. It also frees the system buffers for other uses. The argument must
evaluate to a number which corresponds to an open unit. In Mumps this must
be in the range of 1 to 4 (other values will terminate your program). An
output file must be closed explicitly. Failure to do so may result in loss
of some or all of the file. The close command may be post-conditionalized
and it may have multiple arguments.
|
close 4
close I
close:K=J 1,2
|
-
continue (no abbreviation)
See quit
-
declare (no abbreviation)
The non-standard declare command can be used to improve speed by
declaring Mumps variables as permanent variables in the
C++ environment. A variable so declared, does not need
to be looked-up in teh run-time
symbol table each time it is used. This
can significantly improve speed of execution.
Only unsubscriped Mumps variables may be declareed.
They may not be killed. Variables must be
declared within the main program or
a subroutine (i.e., not globally) and
they have the scope of the entire routine
in which they are declared.
Declared variables may be passed to
subroutines but a received parameter
may not be declared in the scope of the receiving subroutine.
Examples:
|
zmain
declare i,j,k
for i=1:1:10 do
. for j=1:1:10 do
.. for k=1:1:10 do
... set ^a(i,j,k)=0
|
-
do (abbreviation: d)
-
The do command with an argument causes the program to branch to the label specified
by the argument and continue execution beginning at that label. Execution proceeds until
a quit command is encountered at which time execution
returns to the invoking do.
If an end of source file is encountered, it is interpreted as
a halt command.
- Interpreter only: The do may invoke
disk resident, uncompiled, Mumps routines.
-
The do command may be post-conditionalized and its arguments may be post-conditionalized.
-
The argumentless do causes the code on the immediately following
line to be executed. This line must be the beginning of a block with a
level of dotted indent greater than that of the line containing the do.
This block is terminated by a quit, a break or by encountering a line with
a lower level of dotted indent. An argumentless do must be followed
by two blanks (unless it is the last command on a line). It may be Post-Conditionalized.
-
Within an argumentless do block, a quit will return to the line
containing the original do and resume execution of that line.
A break will exit the block but resumes execution with
the line following the block.
-
See the discussion on the effect of break
and quit on blocks invoked by do commands.
-
do commands with arguments may be post-conditionalized
at the argument level.
-
do with arguments:
Arguments may have three forms:
- Names of labels local to the current routine, with or
without parameters:
|
zmain
write "hello "
do www
write !
halt
www write "world"
quit
---------------------------
zmain
write "hello "
do www("world")
write !
halt
www(a)
write a
quit
---------------------------
zmain
write "hello "
write $$www("world"),!
write !
halt
www(a)
quit a
|
Each of the above writes "hello world" by executing a local subroutine.
Note that invoking the routine as a function requires the "$$" while
invoking it with a do does not include the "$$".
- Names of subroutines linked to the current executable module such as
the following:
|
^www()
write "world"
quit
zmain
write "hello "
do ^www
write !
halt
---------------------------
^www(a)
write a
quit
zmain
write "hello "
do ^www("world")
write !
halt
---------------------------
^www(a)
quit a
zmain
write "hello "
write $$^www("world")
write !
halt
---------------------------
|
Note the empty open/close parentheses in the first example.
These are required at present. For a thorough explanation of the meaning of
"$$" and "^" before function labels, see the section on
Functions .
- Name of other binary executables. These are other Mumps programs
(although, the could be written in other languages if the conform
to the calling conventions) and compiled to binary
executables. All are assumed to have the ".cgi" extension:
|
zmain
write "hello "
do ^pgm1
write !
halt
where pgm1 is pgm1.cgi compiled from the following:
zmain
write "world"
halt
|
Programs invoked in this manner may not, at present,
have parameters nor may they return values.
They do, however, have all the variables and
values from the calling program's symbol table
available and any new variables the called program may create or any changes
to existing variables will be reflected in the calling program's symbol
table upon return. This method is relatively slow and should be
used sparingly. It creates a separate shell, stores the current
symbol table on disk (in a file whose name is the same as the
calling program's process id), and invokes the program. The
called program, upon invocation, loads the symbol table,
executes, dumps the symbol table back to disk and terminates.
The shell created terminates and control is returned to
the calling program which loads the revised symbol table.
The embedded interpreter will permit constructions of the form:
do ^ABC
that will cause the file "ABC" to be loaded and interpreter.
This format for compiled code causes the execution
of a separately compiled Mumps function.
If the label following the do command begins
with two circumflexes (^), a separately compiled C++ function is invoked. The
function may be declared in the current file or in a separate file. Functions
from separate files may be pre-compiled and linked. See the section on Functions
If a separately compiled function has no arguments, the open and close parentheses
are not required when invoked by do.
A label of the form xxx^yyy may be used in the compiler
for separately compiled functions. The xxx
is interpreted as a label appearing in the routine yyy.
-
do without arguments:
(see: Notes of break and quit for details)
|
for i=1:1:10 do write " continuing"
. write !,i
. if i>5 quit
. write " ",i
write !,"done",!
writes
1 1 continuing
2 2 continuing
3 3 continuing
4 4 continuing
5 5 continuing
6 continuing
7 continuing
8 continuing
9 continuing
10 continuing
done
------------------------------------------
set i=9
if i>0 do write " continuing"
. write !,i
. if i>5 quit
. write " ",i
write !,"done",!
writes:
9 continuing
done
------------------------------------------
for i=1:1:10 do write " mark " do write " end of line",!
. write i
. if i>5 quit
. write "X"
writes:
1X mark 1X end of line
2X mark 2X end of line
3X mark 3X end of line
4X mark 4X end of line
5X mark 5X end of line
6 mark 6 end of line
7 mark 7 end of line
8 mark 8 end of line
9 mark 9 end of line
10 mark 10 end of line
------------------------------------------
for i=1:1:10 do write " continuing"
. write !,i
. if i>5 break
. write " ",i
write !,"done",!
writes:
1 1 continuing
2 2 continuing
3 3 continuing
4 4 continuing
5 5 continuing
6
------------------------------------------
set i=9
if i>0 do write " continuing"
. write !,i
. if i>5 break
. write " ",i
write !,"done",!
writes:
9
done
------------------------------------------
for i=1:1:10 do write " mark " do write " end of line",!
. write i
. if i>5 break
. write "X"
write !
writes:
1X mark 1X end of line
2X mark 2X end of line
3X mark 3X end of line
4X mark 4X end of line
5X mark 5X end of line
6
|
-
else (abbreviation: e)
The else command tests the value of the system wide built-in variable
$test. If $test (abbreviated as $t) is zero, the remainder of the line
on which the else appears is executed. If $t is not zero, the remainder
of the line is not executed. $test is set, among other ways, by the if
statement. Since else does not take arguments, it must be followed by two
blanks.
For example:
The else does not require a preceding if
statement. It depends solely on the value of $test.
See the discussion below on details
as to how $test may be set.
-
export (abbreviation: ex)
The export command exports the variables
given as arguments to the next outer symbol table:
|
^tst(i,j)
write "in subroutine ",$data(i)," ",$data(j)," ",$data(k),!
set i=10,j=20,k=30
write "in subroutine ",i," ",j," ",k,!
export k
quit
zmain
write "in main",!
set i=1,j=2,k=3
write "before call to subroutine ",i," ",j," ",k,!
do ^tst(i,j)
write "after call to subroutine ",i," ",j," ",k,!
writes:
in main
before call to subroutine 1 2 3
in subroutine 1 1 0
in subroutine 10 20 30
after call to subroutine 1 2 30
|
-
for (abbreviation: f)
Iterative loop command. Examples of the single line scope For:
|
for i=1:1:10 write i,! // prints 1 through 10 on successive lines
for i=10:-1:1 write i,! // prints 10 through 1 on successive lines
for i=1:2:10 write i,! // prints 1, 3, 5, 7, and 9 on successive lines
for i=1:1 write i,! quit:i>100 // no upper limit format
set i=0 for set i=i+1 write i,! quit:i>100 // infinite loop format
for i=1,4,6 write i,! // explicit values of index
for i=2,5,10:1:20,30,100:1:105 write i,! // mixed formats - iterative and explicit
for write "hello",! // write hello infinitely
for i=$order(^a(i)) write i,! // writes all the indices of ^a()
for i=2:$order(^a(i)):8 write i,! // writes indices of ^a() between 2 and 8
|
If the for has no arguments (the "do forever" format), it must be followed by
two blanks.
Mumps Compiler non-standard extensions permit other forms of the for
loop. The first of these, shown in the example below,
permits a function to be executed for each iteration.
In this format, the increment is replaced by a function
call. The loop variable is initialized and, for each iteration, the function is
invoked and its result is copied to the loop variable.
The loop iterates until the value of the loop variable
becomes the third argument or endlessly, if the third
argument is omitted.
The function is invoked immediately after initialization before the first iteration.
Thus, the first value of the variable in the loop is not the initialized value
but the result of the first function execution.
|
zmain
for i=1:1:10 s ^a(i)=i // initialize a global array
e1 for i="":$order(^a(i)):"" write ^a(i),! // write all values of ^a()
write !
kill ^a
for i=1:1:3 for j=1:1:3 for k=1:1:3 set ^a(i,j,k)=i
for a="^a(1)":$query(a):"" do
. if $piece(a,"(",1)'="^a" break // $query() returns all globals
. write a," -> "
. write $qsubscript(a,0)," ",$qsubscript(a,1)," ",$qsubscript(a,2)," ",$qsubscript(a,3),!
write !
for i="":$order(^a(i)):"" do
. for j="":$order(^a(i,j)):"" do
.. for k="":$order(^a(i,j,k)):"" do
... write i," ",j," ",k,!
writes:
1
10
2
3
4
5
6
7
8
9
^a("1","1","1") -> a 1 1 1
^a("1","1","2") -> a 1 1 2
^a("1","1","3") -> a 1 1 3
^a("1","2","1") -> a 1 2 1
^a("1","2","2") -> a 1 2 2
^a("1","2","3") -> a 1 2 3
^a("1","3","1") -> a 1 3 1
^a("1","3","2") -> a 1 3 2
^a("1","3","3") -> a 1 3 3
^a("2","1","1") -> a 2 1 1
^a("2","1","2") -> a 2 1 2
^a("2","1","3") -> a 2 1 3
^a("2","2","1") -> a 2 2 1
^a("2","2","2") -> a 2 2 2
^a("2","2","3") -> a 2 2 3
^a("2","3","1") -> a 2 3 1
^a("2","3","2") -> a 2 3 2
^a("2","3","3") -> a 2 3 3
^a("3","1","1") -> a 3 1 1
^a("3","1","2") -> a 3 1 2
^a("3","1","3") -> a 3 1 3
^a("3","2","1") -> a 3 2 1
^a("3","2","2") -> a 3 2 2
^a("3","2","3") -> a 3 2 3
^a("3","3","1") -> a 3 3 1
^a("3","3","2") -> a 3 3 2
^a("3","3","3") -> a 3 3 3
1 1 1
1 1 2
1 1 3
1 2 1
1 2 2
1 2 3
1 3 1
1 3 2
1 3 3
2 1 1
2 1 2
2 1 3
2 2 1
2 2 2
2 2 3
2 3 1
2 3 2
2 3 3
3 1 1
3 1 2
3 1 3
3 2 1
3 2 2
3 2 3
3 3 1
3 3 2
3 3 3
|
Alternatively, other starting and ending values can be specified:
|
for i="":$order(^a(i)):12 write ^a(i),! // writes values up to but not including 12
for i=2:$order(^a(i)):8 write ^a(i),! // writes values 3 through 7, inclusive
|
Another format is:
|
set i=""
for i=$order(^a(i)) write ^a(i),!
writes all level 1 values of ^a() global.
do $zwi("this is a test string")
for i=$zwn write i,!
writes:
this
is
a
test
string
|
where the loop iterates so long as the value of the
loop variable does not become zero or the empty
string. The Function is invoked before each iteration, including
the first.
The first example iterates through all values fo the global array.
The second example initializes the internal buffer with the string then
extracts each word from the string until there are no more
(returns an empty string).
Note: the order of presentation of the indices is determined by
the collating sequence in the above.
Important Notes on break and quit
-
A quit in a single line for terminates processing of the
for. If there are multiple for commands,
it terminates the nearest:
|
for i=1:1:10 write i,! if i>5 quit
writes 1 through 6 only.
for i=1:1:10 for j=1:1:10 write j,! if j>5 quit
writes 1 through 6 ten times.
for i=1:1:10 write i,! if i>5 break
illegal - generates error message - break not allowed.
|
-
A break may NOT be used in a single line for
command. It may ONLY be used in an indented block that was introduced by a do command.
-
In an indented block, quit and reak have special meanings:
-
A quit ends further processing of the block
in which it appears and returns to the line
containing the invoking do at a point just after the do.
Processing of the line containing the invoking do resumes.
If there are more commands on the line, they are executed.
-
A break ends further processing of the block in which it
appears but does not return the line containing the invoking
do. Instead, execution moves to the line following the block
in which the do appears.
Examples:
|
for i=1:1:10 do write " continuing"
. write !,i
. if i>5 quit
. write " ",i
write !,"done",!
writes
1 1 continuing
2 2 continuing
3 3 continuing
4 4 continuing
5 5 continuing
6 continuing
7 continuing
8 continuing
9 continuing
10 continuing
done
|
In this example, the block is invoked 10 times. After each
invocation, the remainder of the line containing the for
is executed producing the instances of the word "continuing".
Each block invocation prints the value of "i". When
the value of "i" is greater than 5, the block executes
the quit command thus returning to the invoking
line early. When the value if "i" is 5 or less, the
full block is executed and return is made to the invoking
line at block end. When the for command finishes
execution, control is passed to the line following the
for and "done" is printed.
|
set i=9
if i>0 do write " continuing"
. write !,i
. if i>5 quit
. write " ",i
write !,"done",!
writes:
9 continuing
done
|
In this example, the block is entered, the value of "i" is printed
but, because "i" is greater than 5, the quit is executed
and control is returned to the invoking do and the
word "continuing" is printed. Now, the line being completely executed,
control passes to the line following the block and "done"
is printed.
|
for i=1:1:10 do write " mark " do write " end of line",!
. write i
. if i>5 quit
. write "X"
writes:
1X mark 1X end of line
2X mark 2X end of line
3X mark 3X end of line
4X mark 4X end of line
5X mark 5X end of line
6 mark 6 end of line
7 mark 7 end of line
8 mark 8 end of line
9 mark 9 end of line
10 mark 10 end of line
|
In this example, multiple do commands are shown. Note the two blanks
following each. Each do invokes the block following the
line containing the do
On the other hand, the break command terminates the the block in
which it is contained but execution does not return to
the line containing the invoking do but, instead,
continues with the line following the block:
|
for i=1:1:10 do write " continuing"
. write !,i
. if i>5 break
. write " ",i
write !,"done",!
writes:
1 1 continuing
2 2 continuing
3 3 continuing
4 4 continuing
5 5 continuing
6
---------------------------------------
set i=9
if i>0 do write " continuing"
. write !,i
. if i>5 break
. write " ",i
write !,"done",!
writes:
9
done
---------------------------------------
for i=1:1:10 do write " mark " do write " end of line",!
. write i
. if i>5 break
. write "X"
write !
writes:
1X mark 1X end of line
2X mark 2X end of line
3X mark 3X end of line
4X mark 4X end of line
5X mark 5X end of line
6
|
In these examples, execution of the break can be seen to
terminate the current block and move to the line following the block.
-
Note: the contents of $test revert to their former
value when exiting an indented block by means of break or Mb>quit:
|
if 1=1 do
. write "test 1: ",$test,!
. if 1=2 write "wow",!
. else write "not wow",!
. write "test 2: ",$test,!
write "test 3: ",$test,!
writes:
test 1: 1
not wow
test 2: 0
test 3: 1
|
If you exit a block with a goto, the value of $test
is not restored.
see: Unsupported and Non-Standard Features
-
goto (abbreviation: g)
The goto command causes unconditional transfer
of control. In the compiler, local program labels and functions may be
used. Variables are not permitted. Label names must not
be the same as variable names.
Both the command and each argument may be post-conditionalized.
If you exit an indented block with a goto, the
value of $test is not restored to its
pre-block value.
Permitted argument forms:
|
goto label
goto ^function
|
The second example causes the current program to terminate
and control to be passed to a different executable name "function.cgi".
This is accomplished by an "execve()" system function. The symbol table is
dumped prior to transfer and loaded by the target executable.
Control is never returned to the invoking program except if the
invoked program cannot be executed.
-
halt (abbreviation: h)
The halt command terminates execution of the Mumps program and returns
you to the operating system or web server. It may be post-conditionalized.
For example:
-
hang (abbreviation: h)
The hang instruction suspends execution of your program for a specified
period of time (in seconds). It takes as an argument the number of seconds
to wait. It may be post-conditionalized. The hang instruction differs from
the halt instruction only in the argument: a hang without an argument is
a halt instruction. For example:
-
html (abbreviation: html)
The html command causes the remainder of the line to be treated
as html code and sent to the standard output. The command may not be abbreviated.
The remainder of the line is scanned first for embedded Mumps expressions
which are replaced by their results:
&! codes which will be replaced by line feeds (same as the ! in the write statement.
&~expression~ codes whose expression will be evaluated and the
result substituted for the entire code. Thus, assuming the Mumps variable
ptid exists:
|
html < center > Patient &~ptid~ < /center >
|
The value of ptid will be substituted in to the line and the
result will be sent to the standard output.
-
if (abbreviation: i)
The if command permits conditional execution.
The scope of the command is the remainder of the line unless there
is an argumentless do command on the line.
If one of the arguments to the if is false, execution
procedes to the next line. Thus the following is in error:
|
set i=2 if i=3 write "no",! else write "yes",!
|
The else is NOT executed.
Note that expressions are evaluated left to right. This sometimes
causes problems for people used to dealing with other languages. For example,
the expression:
is always false since it is parsed as: (((I=0)&J)<0)
if I is zero, the first expression is true (value of 1); if J is less
than zero, then J is interpreted as true giving, as a result of the AND
operation (&), a value of 1 which is not less than zero - therefore
false. If I is not zero then, regardless of the value of J, the AND operation
results in false (value of zero) which is not less than zero - therefore
false. The expression should have been written as:
The if commnad may be used with no arguments (requires 2 blanks
after command word) or with multiple arguments separated by
commas. If no arguments are given, the current value of $test
is examined and, if true, the remainder of the line containing the
if is executed. If multiple arguments are given, each is
evaluated. If all are true, the remnainder of the line is executed.
If an argument is not true, the remainder of the line including
any unevaluated arguments are not executed and execution
resumes on the next line.
The if command sets $test: if the argument(s) is/are
true, $test is set to true, false otherwise.
An if command with single line scope may initially set
$test to true, but a subsequent command on the same line
may set it to false. Thus, on the following line, $test
may be false even though the expression in the previous if
was true (see example below).
The value of $test is restored upon return from nested blocks except
if those blocks are exited with a goto
command.
See the discussion on the effect of break
and quit on blocks invoked by if commands.
Examples:
|
set i=1,j=2,k=3
if i=1 write "true",! // writes true
if write "true",! // writes true
if i=1,j=2,k=3 write "true",! // writes true
if i=1,j=3,k=3 write "true",! // does not write true
if i=1 do
. write "true",! // writes true
if i=1 if j=3 write "true",! // writes true
write $test,! // writes 1
if i=1 if j=9 write "true",! // does not write true
write $test,! // writes 0
if i=1 do
. write "test 1 ",$test,!
. if j=20 do
.. write "test 2 ",$test,!
. write "test 3 ",$test,!
write "test 4 ",$test,!
writes:
test 1 1
test 3 0
test 4 1
|
-
job (abbreviation: j)
The job command performs a fork(). The global arrays are not closed
prior to the fork.
If you are using the native globals, you must
close them with the ZCLOSE command and reopen them
in either the parent or child. Both processes may not
open the globals concurrently.
If both attempt to open the globals after the
fork, one will pend until the other has closed the globals (zclose).
With the Berkeley DB, both processes may access the globals and
there is no need to close them prior to the job
command.
-
kill (abbreviation: k)
The kill command is used to prune the symbol table and to delete parts
of the global arrays.
There are three forms of the kill command: the first deletes all entries
in the local (i.e., non-global) symbol table; the second deletes specific
elements from the local symbol table or specific elements from the global
arrays; and the third is used to delete all elements from the local symbol
table except for certain named symbols. All forms may be post-conditionalized.
The first form - delete the entire local symbol table - is denoted by the
kill command alone:
The second form appears as a list of references (note: indirection for
the names is permitted):
The above would delete variables A, B and the global array node ^G(1,2,33).
Note: if the global array node ^G(1,2,33) has descendants, they are also
deleted. Also, if a local array node is deleted, any of its descendants
are also deleted.
The final form of the kill may be used to delete all elements from the
local symbol table except for certain protected elements. It has the following
format:
In the above, the local symbol table will
be deleted except for variables A, B(1,1) and K. All other variables will
be lost. Global array nodes may not be used in this form of the kill statement.
Indirection is permitted.
-
lock (abbreviation: l)
The lock command marks a portion of the data
base for exclusive access for an individual user.
A lock with no arguments
frees all prior lock's.
The locks are stored in a file named "Mumps.Locks" which is opened
for exclusive access by the locking/unlocking job. The contents
of the fill may be deleted to remove all locks.
Examples:
|
lock // removes all locks
lock ^a(1) // locks ^a(1) and all children;
lock +^a(1) // increment lock count for ^a(1)
lock -^a(1) // decrement lock count for ^a(1)
lock ^a(1):20 // lock ^a(20) with a 20 second timeout
lock ^a(1),^b(1) // multiple locks
|
-
merge (abbreviation: m)
The merge command copies from one array to another.
At present, only global arrays may participate in the merge
command.
Examples:
|
for i=1:1:9 for j=1:1:9 set ^a(i,j)=i+j
merge ^c=^a // copies all of ^a to ^c
for i=100:1:109 s ^b(i)=i
merge ^b(103)=^a(3) // copies ^a(3) to ^b(103) and children of
// ^a(3) to be children of ^b(103)
merge ^d=^a(3) // creates ^d=^a(3); ^d(1)=^a(3,1),...
|
Note: in code of the form "merge ^x(103)=^a(3)", ^x(103) does not
exist if "^x(103)" did not previously exist and if "^a(3)" does not exist.
In the above examples, there "^a(3)" does not exist (but it does have
descendants).
-
new (abbreviation: n)
The new command creates a new symbol table or namespace and pushes
the old namespace onto a stack. The Mumps compiler implementation of
new differs from the legacy standard Mumps. See also: export
If new is used with no arguments, a new namespace
is created, any previous namespaces are pushed-down and unique variable
names from prior namespaces are instantiated with no values in the new
namespace. Upon exit from
the block in which the new was executed, the old namespace is restored
and the contents of the new namespace are lost. A block is determined to
be exited if you execute a quit from a section of code entered by
a do without arguments or you revert to a lower level of dotted indent.
A call to a separately compiled function results in an implied 'new'.
A call to a local function with parameters also results in an impled 'new'.
A call to a local function without parameters does not result in an implied
'new'. See the examples below.
If you include an
argument to new consisting of a parenthesized list of variable names, a new namespace
will be created that contains instantiated copies, with no values, of
the unique variable names from lower namespaces except those variables
names in the parenthesized list.
If you include one or more variable names as an argument, not
enclosed in parentheses, new copies of these variables will be created.
Upon exit, the old copies will be restored.
At any given level, there may be only one new command.
Examples:
|
zmain
# Example 1 'new'
# 'new' creates a new symbol table and
# pushes down the old table. The new table
# is empty. Upon exit from th 'do' block,
# the old table is restored and contents of
# new symbol table are lost.
set i=1,j=2,k=3
write "before ",i," ",j," ",k,!
do
. new
. set i=100,j=200,k=300
. write "in block ",i," ",j," ",k,!
write "after ",i," ",j," ",k,!
# writes 1 2 3
# Example 2 'new exclusive'
# 'new' creates a new symbol table and
# pushes down the old table. Variable
# 'i' is created in the new table along
# with its contents. Otherwise, the new
# table is empty. Upn block exit, the
# contents of the new table are lost,
# including any changes to 'i' and the
# old table is restored along with the
# old value of 'i'.
set i=1,j=2,k=3
write "before ",i," ",j," ",k,!
do
. new (i)
. set j=200,k=300
. write "in block ",i," ",j," ",k,!
write "after ",i," ",j," ",k,!
# writes:
# before 1 2 3
# in block 1 200 300
# after 1 2 3
# Example 3 'new inclusive'
# 'new' pushes down the old symbol table and
# creates a new symbol table. The new table
# contains all the contents of the old table
# including an instance of 'i' which contains
# the empty string. Since 'i' was in the old
# symbol table, its previous value is not
# visible. Upon exit from the block, the
# old symbol table becomes visible with none
# of the variables changed. No values are copied
# to the old symbol table.
set i=1,j=2,k=3
write "before ",i," ",j," ",k,!
do
. new i
. write "in block ",i," ",j," ",k,!
write "after ",i," ",j," ",k,!
# writes: (value of 'i' in block is the empty string)
# before 1 2 3
# in block 2 3
# after 1 2 3
halt
# Example 4 - implied 'new' with separately compiled functions
# A call to a separately compiled function results in an
# implied 'new' for the function. The old symbol table is
# restored on exit.
^tst(a,b)
write "in subroutine ",$data(i)," ",$data(j)," ",$data(k),!
set i=10,j=20,k=30
write "in subroutine ",i," ",j," ",k,!
quit
zmain
write "in main",!
set i=1,j=2,k=3
write "before call to subroutine ",i," ",j," ",k,!
do ^tst(5,20)
write "after call to subroutine ",i," ",j," ",k,!
# writes:
# in main
# before call to subroutine 1 2 3
# in subroutine 0 0 0
# in subroutine 10 20 30
# after call to subroutine 1 2 3
# Example 5 - implied 'new' with call by reference.
# Same as above except that changes to the arguments
# passed by reference (.i,.j), are returned to the
# calling symbol table.
^tst(i,j)
write "in subroutine ",$data(i)," ",$data(j)," ",$data(k)
set i=10,j=20,k=30
write "in subroutine ",i," ",j," ",k,!
quit
zmain
write "in main",!
set i=1,j=2,k=3
write "before call to subroutine ",i," ",j," ",k,!
do ^tst(.i,.j)
write "after call to subroutine ",i," ",j," ",k,!
# writes:
# in main
# before call to subroutine 1 2 3
# in subroutine 1 1 0
# in subroutine 10 20 30
# after call to subroutine 10 20 3
# Example 6 - implied 'new' on local functions with parameters
# If a local function (one without '^' at the beginning of its
# name) is called, an impled 'new' is performed:
zmain
set i=10
set j=20
set k=30
write "main program: ",i," ",j," ",k,!
do test(i,j,k)
write "main program: ",i," ",j," ",k,!
halt
test(a,b,c)
write "sub-program: ",a," ",b," ",c,!
set a=11
set b=22
set c=33
quit
# writes:
# main program: 10 20 30
# sub-program: 10 20 30
# main program: 10 20 30
# Example 7 - implied 'new' on local functions with call by
# reference: (similar to separately compiled functions above)
zmain
set i=10
set j=20
set k=30
write "main program: ",i," ",j," ",k,!
do test(.i,.j,.k)
write "main program: ",i," ",j," ",k,!
halt
test(a,b,c)
write "sub-program: ",a," ",b," ",c,!
set b=22
set c=33
quit
# writes:
# main program: 10 20 30
# sub-program: 10 20 30
# main program: 10 22 33
# Example 8 - no implied 'new' for local functions without parameters
# A local function called without parameters shares the same symbol
# table as the calling routine. All variables are available and any
# changes are visible upon return:
zmain
set i=10
set j=20
set k=30
write "main program: ",i," ",j," ",k,!
do test
write "main program: ",i," ",j," ",k,!
halt
test
write "sub-program: ",i," ",j," ",k,!
set j=22
set k=33
quit
# writes:
# main program: 10 20 30
# sub-program: 10 20 30
# main program: 10 22 33
|
-
open (abbreviation: o)
The open command opens sequential files and associates them with unit
numbers. Opened files may be read or written. This implementation permits
unit numbers 1 through 4 and 6 through 9 to be used by the programmer. Unit 5 is reserved
for the normal user console (stdin and stdout).
The open command takes a device number (either a number or
an expression which evaluates to a number in the range of 1,2,3,4,6,7,8,9) and a
file name. The file name must either be a variable name or a quoted literal.
The file name consists of a valid file name followed by either ,new ,APPEND
or ,OLD.
If followed by ,new, the program assumes
you are opening the file for output: any previous files with this name
are lost You may only write to this file. If you open the file with the
,OLD option, the program assumes the file exists and opens it for input
only (reads). If you specify ,APPEND, the file is opened for output with
all new data written to the file appearing after the previously existing
data. If an error takes place, $test is set to zero and the remainder of
the command is not inspected. If no error takes place, $test will be one.
The user should |