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 pspotThe 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 %1See 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.smlWhen you compile the program, the compiler will issue the following message:
Warning: %1.sml is an external routineYou 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.smlthe 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:
[calc] [sum]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:
&include pspot.incNote 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
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 ... &return &routine curdir &rem Get current directory &rem ... &returnTo compile pspot.r into an object library file, execute the following command:
COMPSML pspot R pspotNote 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 plottoolYou can use LIBSML to list the object routines which are in a library:
LIBSML pspot LISTYou can also delete a routine:
LIBSML pspot DELETE curdiror copy a routine to a different library:
LIBSML pspot COPY common curdir
To link the pspot routines, run LINKSML and answer the dialog prompts as follows:
LINKSML 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 pspotIf 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)!
[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".