The SML Compiler

"What the heck, at least it compiles!"

Anyone who works with Host ARC/INFO's AML most likely knows the incredible mess that large applications can create. One application that I revised for a client comprised nearly two hundred AML and menu files scattered through over a dozen highly convoluted directories. I also found a fair number of files floating around that were not addressed by any routine. The only way to trace program calls was to print everything out and use colored pencils[1]. Agony!

That is one reason why I am extremely fond of the SML compiler and linker. Routines can be grouped into routine files, routine files can be grouped into object library files, and all required objects (and ONLY the required objects) are automatically linked to generate the final application[2], with a nice report summarizing program calls. Ecstasy!

Routine Files

Routine files (default extension .r) are typically collections of SML routines, though they may also contain text files and popup files[3]. Each routine in a routine file begins with the &routine directive, which names each routine[4] and optionally how it is to be processed by the compiler:

   &routine pspot
The default option, S, compresses the code, removing &rem statements (except the first lines of a popup file), blank lines, and leading spaces. In situations where a blank response is required (such as the TABLES ADD command), you can set a blank variable and use it as the response, for example:

   &sv 1
See the Online Help's &routine entry for an exhausting discussion of options.

Each routine is self-contained; it cannot address a label present in another routine. While it may be inconvenient to add an "error" label to each routine that contains a &read statement, it does help keep the logic structured: one way in, one way out.

A routine may use &run or &r to call any other internal routine (i.e. a routine linked to the application) or to call an external routine (typically a temporary file generated on the fly). It is possible to run an external routine specified by an SML variable:

   &r %1.sml
When you compile the program, the compiler will issue the following message:

   Warning: %1.sml is an external routine
You cannot, however, call an internal routine in this fashion save in conjunction with &wild, which is discussed below. Neither can you call an internal routine from an external routine.

[TIP: If you generate temporary files to call with &run, prefix their names with "t$" so that the compiler assumes them to be such; if, on the other hand, you also prefix the name with a variable containing the path:

   &value 1 WKSP
   &r %1t$run.sml
the program will compile ok, but you will receive an "external routine" warning message. Do not begin internal routines with "t$" or they will not link.]

In addition to the structured programming directives discussed in the previous article, there are other directives which may be used in a routine file: &define, &delim, &include, and &wild.

&define allows the programmer to create an alias for a frequently used string; the extremely useful &var option allows the creation of named variables. Note that previously defined aliases may be applied to &define statements (here is an excellent example from the Online Help):

   &define pop 10 &var
   &define sum 12 &var
   &define calc "&cv [sum] [pop] + [sum]"
   &define print_sum "&type ""There are [sum] people"" "
The compiler, when processing named variables, will automatically place "%" in front of the variable number where appropriate, and will leave it off where it is not appropriate. Using the above example, the statements:

would expand to:

   &cv 12 %10 + %12
   &type "There are %12 people"
There are some situations where "%" will not automatically be placed in front of the variable number, even though you may wish to address its contents, for example:

   &run idcalc %[editcov]
   CALC $ID = %[temp]
   SHOW ARC %[arcno] VERTEX %[v] [vx] [vy]
   MOVE %[x1] %[y1] %[x2] %[y2]
Note that named arguments to the &r directive do not require a leading percent:

   &r idcalc [editcov]
By default, a string alias or named variable is delimited by "[" and "]". This may be changed using the &delim directive, e.g. for usage lines:

   &delim < >
   &type "Usage: &r PSPOT [cover] [radius/item] {outline}"
   &delim [ ]
If different routines share the same named variables, you can use the &include directive to load a file containing definitions:

Note that each routine using that set of definitions must issue the &include statement before it begins referring to them (it's very easy to forget &include statements when building new routines within an application). The only directives acted upon in an include file are &delim, &define, and &wild, and only take effect in the routine which loads it[5].

Finally, &wild defines a list of allowable internal routines (up to 200) for a wildcard &run or POPUP directive, for example,

   &wild clean build createla
   &wild nodeerro labelerr
   &run %1

The Compiler

COMPSML and LINKSML are standalone applications; they may be run either from the ARC prompt or the DOS prompt.

COMPSML has options similar to those of the &routine directive. The "R" option processes the file according to its &routine headers, storing the results in an object library file. The "N" option expands routines into standard SML files (more about this in the next article).

Let's assume that we have a routine file, pspot.r[6], that contains two routines:

   &routine pspot

   &rem Plot point coverage as shaded circles

   &rem ...


   &routine curdir

   &rem Get current directory

   &rem ...

To compile pspot.r into an object library file, execute the following command:

   COMPSML pspot R pspot
Note that two files are created: pspot.lml and pspot.lmx. The LML file is the actual object library file; the LMX file is an index file to speed up file access.

If the object library already exists, COMPSML updates it rather than starting over from scratch. For example, you may wish to group the routines with other ARCPLOT routines residing in the library PLOTTOOL:

   COMPSML pspot R plottool
You can use LIBSML to list the object routines which are in a library:

   LIBSML pspot LIST
You can also delete a routine:

   LIBSML pspot DELETE curdir
or copy a routine to a different library:

   LIBSML pspot COPY common curdir

The Linker

LINKSML links together all of the routines it can find which are required to build the final application, then displays a report of which objects in the specified library files are used, which ones are not used, and how the ones that are used relate to each other. External and duplicate routines are also listed. If a routine cannot be found, it is assumed to be external. If duplicate routines exist, the first one encountered is linked.

To link the pspot routines, run LINKSML and answer the dialog prompts as follows:


   CML file:pspot
   Main routine:pspot
   Report file:
   LML file:pspot
   LML file:
The first prompt specifies the name of the CML file that will be created. The next prompt specifies the main or initial routine to which the other routines will be linked; the main routine must be present in the first LML file to be specified. The next prompt allows you to create a report file; otherwise, the results are displayed on the screen. Subsequent prompts allow you to enter, one at a time, the name of the LML file(s) to be used to create the application. Once all LML files are entered, the dialog is terminated by entering a blank line.

Note again that two files are created: pspot.cml and pspot.cmx. Just as an LMX file acts as an index for an LML file, a CMX file acts as an index for a CML file.

If you update an application frequently, you can create a load file containing the necessary dialog. Create a file called pspot (no extension) and place the above 5 lines of dialog into it. Then run LINKSML as follows:

   LINKSML pspot
If you already have a CURDIR routine in a library named COMMON, you could leave it out of pspot.r and use the following LINKSML dialog:

   CML file:pspot
   Main routine:pspot
   Report file:
   LML file:pspot
   LML file:common
   LML file:
Note that if common.lml is in a different directory, the path to it should also be specified.

Finally, copy the CM* files to the UTOOL directory:

COPY *.CM* ..
and you're all ready to go (until you discover your first bug)!

Next: "It's not a bug, it's a feature!"

[1]Watch files are useful for isolating bugs, but are a nightmare for tracing program calls.

[2]Unfortunately, unlike AML, SML routines that run in different ARC/INFO modules must be compiled into separate applications. However, if a satellite process requires only a few lines of code, they may be generated on the fly (see Numbers and Strings for an example).

[3]It doesn't seem to work, though, for WIN menu files and dialog files.

[4]Routine names must follow the 8.3 filename convention. The default extension, .sml, need not be given.

[5]CONVSML may be used to convert an old SML to a routine file with named variables. See the "SML User's Guide" for details.

[6]The actual program is available at this Web site under "Arcplot Utilities".

Return to ArcTips page