Mumps/MDH Toolkit
The Mumps Programmer's Guide
Version 9.0

Kevin C. O'Kane, Ph.D.
Computer Science Department
University of Northern Iowa
Cedar Falls, IA 50614
okane@cs.uni.edu
http://www.cs.uni.edu/~okane
July 3, 2007

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.

  1. 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

  2. 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
Preface
Introduction to Mumps
Programming Basics Language Specification
Data Types and Values
Variables
Global Arrays
Writing Programs
Example Programs
Operators Commands Builtin Functions
Builtin Variables
  1. $horolog (Abbreviation $h)
  2. $io (Abbreviation: $i)
  3. $job (Abbreviation $j)
  4. $test (Abbreviation $t)
  5. $storage (Abbreviation $s)
  6. $x
  7. $y
Installing the Compiler
  1. Other Software Needed
  2. MS Windows Install
    1. MS Visual C++ Binary Install
    2. MS Visual C++ Full Build Details
    3. Windows XP Apache Web Server Interface
  3. Linux Install
    1. Linux/Cygwin Quick Install
    2. 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
  1. Program Structure
  2. 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

  1. 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.

  2. 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.

  3. 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.

  4. 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 (^).

  5. 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.

  6. 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.

  7. 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.

  8. 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",!
    

  9. 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 (/).

  10. 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):

set i="hello world" set i="hello",j="world" ; multiple arguments to the "set" set:a=10 i="hello world" ; only executed if "a=10" is true goto label1 gotto:b<a label1 ; executes only if "a<b" is true goto label1:c>d ; executes only if "c>d" goto lab1:c>d,lab2:d<e ; multiple post-conditionals quit:a=b set i="hello" ; argumentless "quit" followed by 2 blanks goto:a=10 lab1:c>d,lab2:d<e ; multiple post-conditionals

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 i<j write "yes",! ; yes 4 if i<j,k>j write "yes",! ; yes 5 if i<j&k>j write "yes",! ; does not write 6 if i<j&(k>j) 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.

  1. 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).

  2. 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.

  3. 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

  4. 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.

  5. 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.

  6. 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.

  7. 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

  1. 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

  2. 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

  3. 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

  4. 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

  5. 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.

  6. 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

  7. The indirection operator: @

    The indirection operator permits direct interpretation of string expressions. For example:

    set a="2+2" write @a,!

    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:

set I=10*5

same as above with command word abbreviation:

s I=10*5

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

  1. 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.

  2. 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:

    1. InputLengthException
    2. 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:

    1. 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",!

  3. 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

  4. continue (no abbreviation)

    See quit

  5. 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

  6. 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:

      1. 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 "$$".

      2. 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 .

      3. 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&gt;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&gt;0 do write " continuing" . write !,i . if i&gt;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&gt;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&gt;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&gt;0 do write " continuing" . write !,i . if i&gt;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&gt;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

  7. 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:

    else set i=10

    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.

  8. 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

  9. 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

  10. 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.

  11. 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:

    halt:I=3

  12. 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:

    hang:I=J 2*K

  13. 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.

  14. 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:

    I=0&J<0

    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:

    (I=0)&(J<0)

    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

  15. 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.

  16. 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:

    kill

    The second form appears as a list of references (note: indirection for the names is permitted):

    kill A,B,^G(1,2,33)

    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:

    kill (A,B(1,1),K)

    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.

  17. 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

  18. 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).

  19. 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

  20. 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