SML PROGRAMMING

Numbers and Strings

SML programs typically store temporary data by means of variables, which are named as integer numbers from -20 to 9999 (variables may be assigned alphanumeric names using the SML compiler directive &define, which will be discussed under The SML Compiler. As was mentioned in the previous article, the SML processor—upon reading a line from an SML program—subsitutes the contents of variables for variable references (indicated by "%") and the resulting text is evaluated. Thus if the SML processor encountered the line:

   &type "The sum is %21 acres."
and variable 21 at that time contained the value "40.25", the SML processor would "see" that line as:

   &type "The sum is 40.25 acres."
and proceed to execute that command. SML variables are stored as character strings, which may be numeric or nonnumeric. Numeric strings (if resulting from a calculation) are formatted by the SML processor according to the FORTRAN fixed decimal format 17.5, i.e. up to 11 digits before the decimal point and five digits after. Nonnumeric strings are limited to 80 characters.

It is important to note that there are two types of variables: local and global. Local variables, introduced in version 3.5, are only accessible within a routine, whereas global variables may be accessed by all routines.

Local variables are numbered -20 through -1. When one routine calls another, the slate will be wiped clean for that routine; upon return to the parent routine, its local variables will be restored. The following example makes use of the &lv (or &listvar) directive to list variable contents: TEST1.SML

&sv -1 10
&sv -2 20
&run test2
&lv -2 -1
&return
TEST2.SML

&sv -1 11
&sv -2 12
&lv -2 -1
&return
When TEST1 is executed, the output is as follows:

VAR.  VALUE
%-002 12
%-001 11
VAR.  VALUE
%-002 20
%-001 10
The chief advantage of local variables is to provide scratch space for routines and to help prevent variable collision, which will be discussed under Debugging.

Global variables may in turn be categorized as memory and extended.

Memory variables, numbers 1 through 50, are memory resident and are useful as short term variables, especially those involved in frequent or iterated calculations, since run time is a bit faster. Memory variables are global in that they are accessible from all routines running within a particular SML processor. Those variables are not globally accessible, however, from other SML processors. Thus if an SML program running under ARC were to call a routine running under ARCEDIT, the routine running under ARCEDIT would not have access to the memory variables of the parent routine; rather, it would have a "clean slate". The ARC memory variables are not eradicated, though: they will be available once again after termination of ARCEDIT.

Extended variables, numbered 51 through 9999, are globally accessible from all SML processors invoked in a PC ARC/INFO session; their values are maintained by default in the file %WKSP%t$extsml.var (see discussion of DOS environment variables below), which is created at the beginning of the ARC session and deleted upon quitting.

Variable 0 is a dummy variable, and is generally used as an argument to the SHOW command when not all returned values are desired:

   SHOW STATISTICS 0 0 0 -11 0

Saving Variables

Global variables may be saved via the &save directive, which saves a range of variables to an SML program which in turn may be executed upon restart.

Question: Why cannot local variables be saved using the &save directive (even though the directive permits it)? Answer: Because after the resulting SML is executed, the parent routine's local variables will be restored. Any information to be saved must be assigned to global variables or sent to a temporary file:

&rem **** save local variables

&value -20 WKSP
&openw %-20t$sav.lis
&write "%-1"
&write "%-2"
&write "%-3"
&write "%-4"
&closew
...

&rem **** restore local variables

&open %-20t$sav.lis error
&read -1 error
&read -2 error
&read -3 error
&read -4 error
&close

Assigning Values

The basic SML directives for assigning values to SML variables within the body of an SML program are &sv, &cv, &extract, &length, and &value (SML commands for user input and file I/O will be discussed under Retrieving Data). The &sv (or &setvar) directive allows assignment of literal expressions to SML variables, whereas the &cv (or &calcvar) directive assigns a formatted numeric value calculated from the expression. For example, in the following lines:

   &sv 11 4.00000001
   &cv 12 5.00000001
   &sv 13 "%11 * %12"
   &cv 14 %11 * %12
variable 11 will be assigned a value of "4.00000001", variable 12 will be assigned a value of "5", variable 13 will be assigned a value of "4.00000001 * 5", and variable 14 will be assigned a value of "20". The &cv directive supports complex arithmetic statements with multiple levels of parentheses (I have yet to discover its limits); see the SML command reference entry &calcvar for an exhaustive list of operators and functions.

[TIP: The SML Developer's Toolkit has an SML program which uses Newton's method to calculate square roots. Forget it. Just use the exponentiation operator (e.g. 2 ** 0.5), which is quite as accurate (and far quicker).][1]

The &extract directive is very useful to extract tokens from a string, i.e. substrings separated by a delimiter such as a space or a backslash. For example, in the following lines:

   &sv 11 "SML is Your friend."
   &extract 12 11 3
   &extract 13 11 3 L
variable 12 will be assigned the value "YOUR" and variable 13 the value "Your"; by default, &extract converts substrings to upper case: the "L" option preserves the case of the substring. If the delimiter is anything other than a space, the extracted string will retain the delimiter at the end. This is a mixed blessing because on one hand, you know you have reached the final token if the delimiter is missing:

   &rem **** Variable 51 contains full path and name of coverage ******
   &sv 1 0
   &label loop
      &cv 1 %1 + 1
      &extract 11 51 %1 # \
      &goback loop &if &cn %11 \
   &label end
In this example, if variable 51 contains "C:\HOME\PROJECT1\TOPO", the final value of variable 11 will be "TOPO". On the other hand, in all other cases you'll most likely want to get rid of the delimiter—that is where &length, which returns the length of a string, comes in handy:

   &sv 11 "Archaic|Pueblo I|Late Pueblo III"
   &extract 12 11 2 L |
   &length 13 "%12"
   &cv 13 %13 - 1
   &value 12 12 1 %13
The &value directive has three different functions: 1) to assign the contents of an SML variable, 2) to assign a substring of a variable (as above), and 3) to assign the contents of a DOS environment variable (see below). Note that SML is structured in such a way as to allow pointers, i.e. variables which refer to other variables. For example, in the following lines:

   &cv %49 12 / 3
   &value 11 %49
if variable 49 contains the value "101", variables 101 and 11 both will be assigned the value "4". Pointers are useful for manipulating primitive arrays, or lists of values.

&value also allows the substitution of one string into another:

&sv -1 123
&sv -2 abcdefghi
&value -2 -1 1 3 4 6
In the above example, variable -2 will be assigned the value "abc123ghi".

The SHOW command in the ALLOCATE, ARCEDIT, ARCPLOT, and ROUTE modules allows you to assign a variety of session-related values to SML variables. For example, if in ARCPLOT a project area is to be divided into mapsheets, you can create a polygon coverage MAPSHEET which may be reselected by the item NAME to determine the mapextent for the plot. The problem: if you use MAPE POLY MAPSHEET, the resulting mapextent is 10% larger than what you want. The solution: a bit of arithmetic.

   &rem **** variable 51 contains the name of the mapsheet ************
   RES MAPSHEET POLY NAME = '%51'
   MAPE POLY MAPSHEET
   SHOW MAPE 11 12 13 14
   &cv 11 %11 + ( ( %13 - %11 ) / 22 )
   &cv 12 %12 + ( ( %14 - %12 ) / 22 )
   &cv 13 %13 - ( ( %13 - %11 ) / 22 )
   &cv 14 %14 - ( ( %14 - %12 ) / 22 )
   &sv 15 "%11 %12 %13 %14"
   &type "%15"
   MAPE %15
The lines involving variable 15 are redundant, but they highlight an important point: should there be any possibility that an expression for &sv or &type contains spaces, enclose it in quotes. If your program types out incomplete text, check your &type statements; if you get the error message "Could not format value", check your &sv statements.

Embedded Calculations

Although it can be easy to use up all 20 local variables, the number of "scratch" variables in a routine may be reduced by using embedded calculations wherever feasible. Any expression that would be used in the &cv directive may be substituted for a variable reference by enclosing it in "%<" and ">". For example, the lpos function is useful in conjunction with &value to extract the file name from a file specification:

&value -2 -1 %<lpos \ %-1 + 1>
and to extract the path from a file specification:

&value -2 -1 1 %<lpos \ %-1>

DOS Environment Variables

DOS environment variables reside in memory[2], and are set in DOS using the SET command, typically in autoexec.bat. These variables are accessed by DOS and many applications to indicate such parameters as default file locations, search paths, and modes of operation. An important DOS variable is PATH, which lists the default directories for DOS or other programs to search for a file or command. Typically, the variable is set using the PATH command, for example:

   PATH C:\WINDOWS;C:\;C:\DOS;C:\ARCEXE\CMD;C:\DBASE
DOS variables may be utilized directly within a DOS batch program by bracketing the variable reference with percent characters. For example, you could create a batch program utool.bat (residing in a PATH directory) as follows:

   @ECHO OFF
   A %ARC%\UTOOL
   :DO
   IF "%1-" == "-" GOTO END
   CD %1
   SHIFT
   GOTO DO
   :END
Assuming that autoexec.bat contains the following line:

   SET ARC=C:\ARCEXE
if you were to type "UTOOL" you would end up in C:\ARCEXE\UTOOL, or if you were to type "UTOOL SOURCE" you would end up in the directory C:\ARCEXE\UTOOL\SOURCE. (Further discussion of batch programming is beyond the scope of this article.)

[TIP: A.exe is a handy little utility supplied with PC ARC/INFO which allows you to jump to a combined drive/directory specification; you do not need to be at the ARC prompt to use it. Note, however, that the application is 16 bit and does not support long directory names.]

Finally, the contents of DOS variables may be assigned to SML variables using the &value directive. The DOS variable WKSP, which is set at the beginning of the ARC session to establish a unique location for system files, also provides a convenient location to store temporary SML programs created by a parent program, as in the following example:

   COPYINFO impacted.pat area_a
   ADDITEM area_a area_a acres 8 12 f 4
   &value 51 WKSP
   &openw %51t$calc.sml
   &write "SEL area_a"
   &write "RES area < 0"
   &write "PURGE"
   &write "Y"
   &write "CALC acres = area / 4047"
   &write "Q"
   &closew
   TABLES %51t$calc.sml
   & DEL %51t$calc.sml
Note that a backslash is not required after "%51" because %WKSP%, in an ARC session, ends in a backslash. The "& " at the final line is equivalent to &sys, indicating that a system or DOS command is to be executed.

Next: Retrieving Data


[1]SML will not exponentiate negative numbers, even if the statement is otherwise legitimate (e.g. -2 ** 3).

[2]DOS environment variables reside in the portion of memory allocated as "environment": in Windows 95, this space should be dynamically allocated (see Installing PC A/I 3.5.1 in Windows 95 for details).


Return to ArcTips page