.TITLE IMPLEMENTOR'S GUIDE TO SCANLIB       366-CM-454(B)
.LM 0 .RM 70 .NF .NJ .PS 59,70
.SEND TOC .LAYOUT 1,3
.SEND TOC .RM 70
.SEND TOC .DISPLAY NUMBER RL
.SEND TOC .CENTER ;Table of Contents
.SEND TOC .SK
.STYLE HEADERS 6
-
.SK 10
.CENTER ;Implementor's Guide to the
.CENTER ;Specializable Command Analyzer Library
.CENTER ;(SCANLIB)
.SK 2
.CENTER ;Section 366 Computing Memorandum
.CENTER ;Number 454
.SK 2
.CENTER ;Revision B
.SK 5
.CENTER ;James W. Brown
.SK 18
.CENTER ;Jet Propulsion Laboratory
.CENTER ;California Institute of Technology
.CENTER ;Pasadena, California 91109
.PG .F .J
.PG .HL 1 INTRODUCTION
.HL 2 Definition of Some Terms
In order to provide some reference points, a few key terms are
first defined:
.SK 
An application program, or application, is a medium to large
program, or system of programs, written for use by a group of
users other than the person(s) who wrote it.  It is not usually
(although it may be) considered part of an operating system.
.SK
An implementor is a programmer who writes an application
program.
.SK
A user is a person who prepares run-time inputs (commands) for
an application, and executes the application program.
.HL 2 Purpose
The specializable command analyzer library (SCANLIB) is
designed to provide a portable, easy-to-use implementation
of a free-format command syntax for application programs.  By
using SCANLIB, an implementor can provide for users a convenient
command syntax with little more design and implementation effort
than would be required for a simple,
but well-designed, fixed-format command structure.
.SK
Because SCANLIB is highly portable, it can be used on a variety
of hardware, thus reducing conversion efforts for long-lived
application programs, and reducing learning effort for users of
new applications.
.HL 2 Portability
SCANLIB is written with the intent of maximizing portability.  It
is written in the SFTRAN3 language, for which a portable processor
exists, and uses mostly features of ANSI 1966 Fortran, with some
extensions which are part of Fortran 77 and which are
available in most current Fortran compilers.  In particular, see
1.3.4 below.
.HL 3 Character Representation and Operations
Character items are represented as arrays of characters,
holding one character per array element.  The
only operations done on characters
are comparison for equality or inequality, and copy.
.SK
SCANLIB contains a small table of characters which have
special significance as punctuation.  These can easily be
changed by the implementor to avoid any special problems
which some punctuation characters may cause on some types
of hardware, or to be consistent with related features of other
languages (such as the command language) on the system being used.
.SK
SCANLIB is independent of character codes and collating
sequences.  It is case-insensitive, i.e. upper and lower case
letters are treated as equal within the table lookup routine.  The
case conversion routine &i&s dependent on the character code used,
and may need to be modified for a non-ASCII machine. 
.HL 3 Input/Output
The SCANLIB routines do not perform any input (unless
CMREAD is used).  The command
stream to be analyzed is provided by an implementor-written
calling routine.  Output is limited to diagnostic messages,
which are produced by a single, small subroutine using
standard Fortran WRITE statements to an implementor-selectable
Fortran unit.  If CMREAD is used for command
input, it also echoes the input to the same output unit.
.HL 3 Extensions to 1966 Fortran
The following features of Fortran 77 which are not part
of ANSI 1966 Fortran are used by SCANLIB:
.SK .LITERAL
   - CHARACTER data type
   - IMPLICIT (i.e., IMPLICIT INTEGER (A-Z))
   - PARAMETER (for array dimensions and named constants)
   - Apostrophe instead of nH for "Hollerith" data and FORMATs
   - Implied DO loops in DATA statements
   - Array names without subscripts in DATA statements
.END LITERAL .SK
Usages of these features are limited to the specification
portion at the beginning of the various routines, where they
can easily be found and modified if necessary.  The usage
of implicit and parameter is limited to the "INCLUDE" text in
SCANLIB.PARAMS.
.HL 3 SFTRAN3 "INCLUDE"
SCANLIB uses the "INCLUDE" feature in SFTRAN3, which is slightly
awkward in
the portable implementation.  It is recommended that this feature
be installed in some form in any SFTRAN3 implementation.  If it is
not available, its function can be obtained by using an equivalent
(but non-portable)
capability in some Fortran compilers, or by use of a text editor.
.HL 2 Use
SCANLIB is intended to provide a convenient way for a user to
enter run-time options, parameter values, and limited amounts
of data into an application program.  In order to incorporate
this capability into a program, the implementor must perform
the following tasks:
.SK
.LIST 0,"-"
.LE;analyze the needs of the application for run-time inputs.
.LE;design an appropriate command language to provide these
    inputs, within the syntax rules of SCANLIB.
.LE;determine how the run-time information will be represented
    internally.
.LE;give careful consideration to defaults, so the user will
    not have to enter an excessive amount of input to perform
    the most common tasks, and so the program will do
    something reasonable if the user forgets to enter
    some commands or options.
.LE;prepare tables of command keywords, argument keywords,
    and value keywords, and corresponding tables of argument
    types, maximum list lengths, etc.  Arrange these tables
    according to the rules detailed below, and encode the
    tables in a BLOCK DATA subprogram.
.LE;if the standard table and list sizes provided by SCANLIB
    are not appropriate, change the appropriate parameters
    and recompile the dozen or so subroutines in SCANLIB.
.LE;compile the BLOCK DATA program containing the language
    definition tables.
.LE;write a set of subroutines to:
.SK
.LIST 0
.LE;set defaults
.LE;read inputs and call the SCANLIB scanner
.LE;analyze the results provided by SCANLIB, and set the
    appropriate run-time values.
.END LIST
.LE;link the above with the rest of the application, and test.
.END LIST
.HL 2 Limitations
As stated above, SCANLIB is intended to provide options and
values to application programs.  It is not intended as a
general-purpose high-level language.  Therefore it lacks
some capabilities sometimes available in command languages,
such as statement labels, control-flow instructions,
declaration and use of variables, arithmetic, etc.  However,
some such capabilities can be provided by clever definition
of punctuation and line-at-a-time semantic analysis.  Such
stretching of the intent of the language is not generally
recommended.
.SK
SCANLIB is simply a syntax analyzer -- it has no knowledge
of the semantics of the language it is processing.  Semantic
analysis is the responsibility of the implementor, but is
usually straightforward in the kind of application considered
here.
.HL 2 Revision History
The original version of SCANLIB (30 Jan 80) was written for an
extended 1966 Fortran environment (Univac Fortran V).  Revision
A encompasses conversion to standard Fortran 77, using the
CHARACTER*1 data type in place of the former one-character-per-word
INTEGER implementation.  Revision B adds a "help" processor, removes
case-sensitivity, and
makes a few modifications for greater flexibility.
.PG .HL 1 GENERAL SYNTAX
This section defines the general syntax which is supported
by SCANLIB.  In reading the examples, the reader should keep
in mind that every syntactical item used by SCANLIB is a table
entry which can be changed by the implementor.  The examples
use the punctuation convention which is contained in the sample
tables delivered with SCANLIB.  It is recommended that these not
be changed without good reason, in order to minimize confusion
for users moving from one application to another.
.HL 2 Meta-language
The following notation is used to describe the syntax of any
language supported by a SCANLIB implementation.  It is not
a part of that language.  The definitions that follow are informal:
.SK
.LITERAL
 <argsep>  is an argument separator, used to separate <keyword
           argument lists>
 <cmterm>  is a command terminator, used to indicate the end of
           a command, and to separate multiple commands on a line
 <coment>  is a character used to delimit a <comment> string
 <comment> is a string of characters which is ignored by the
           syntax analyzer
 <contin>  is a continuation character, used to indicate
           that the next line should be appended to the
           current line before processing
 <eol>     is the end of a line
 <integer> is a <token> containing decimal digits, with optional
           leading sign, which is interpreted as an integer
 <keysep>  is a character used to separate a command or argument
           keyword from the <list> which follows it
 <keyword argument list> is an argument keyword followed by
           a list of values
 <list>    is a sequence of zero of more <tokens> all of the
           same <type>, separated by <lstsep>
 <lstsep>  is a character used to separate items in a <list>
 <name>    is a <token> recognized as an entry in some table
 <noise>   is a character which serves only to terminate a token,
           and otherwise is ignored by the syntax analyzer
 <punct>   is a character defined as punctuation by its
           appearance in the table of punctuation characters
 <quote>   is the character used as a string delimiter for
           those strings requiring delimiters
 <real>    is a <token> which contains no blanks, and otherwise
           follows (approximately) the fortran rules for real constants
 <string>  is a <token> which follows the fortran 77 rules
           for character constants, except that a <string>
           containing no <punct> or <quote> characters
           need not be surrounded by <quotes>
 <token>   is a character string not containing any <punct>
           characters (except within a quoted <string>),
           and surrounded by <punct> characters
 <type>    is one of <integer>, <real>, <name>, or <string>
 [ ]       square brackets are used to indicate that the
           enclosed syntactical items are optional
 < >       angle brackets are used to denote syntactical items
 ...       ellipsis is used to indicate that the preceding
           syntactical item or group of items may be repeated
           zero or more times, subject to maximum list lengths
           set by the implementor
 ::=       means "is defined to be"
.END LITERAL
.HL 2 Punctuation
Punctuation characters are used by SCANLIB as the primary means
of interpreting the syntax.  There are several types of
punctuation, each of which may be represented by one or more
characters.  Any printable characters representable on the
hardware may be used as punctuation, but this usage makes
them unavailable for any other use, except inside quoted
strings.  Any character may be used for at most one type of
punctuation.
.SK
.TP 10
The standard punctuation delivered with SCANLIB is:
.SK .LITERAL
   <noise>  ::= ' '          (blank)
   <coment> ::= '@'          (commercial at)
   <quote>  ::=  '           (apostrophe)
   <lstsep> ::= ',' or ':'   (comma, colon)
   <keysep> ::= '=' or '('   (equals, left parenthesis)
   <argsep> ::= '/' or ')'   (slant, right parenthesis)
   <cmterm> ::= ';'          (semicolon)
   <contin> ::= '&'          (ampersand)
.END LITERAL
.HL 2 Tokens
Tokens are, for the purpose of SCANLIB, all significant
syntactical items other than punctuation.  In this sense,
<comments> and <noise> are considered non-significant.  Tokens
may be <integers>, <reals>, <names>, or <strings>.
.HL 3 Integers
A token is an <integer> if it occurs in a context where an
<integer> is expected, and contains only decimal digits, and
optionally a leading sign ('+' or '-').  Blanks (i.e. <noise>) are
not permitted
within an <integer>.  An <integer> may consist of only a sign,
in which case it has the value zero.  A negative zero cannot
be entered (i.e. '-0' has
the value (+) zero).  The largest magnitude which can be
processed is hardware-dependent.  SCANLIB provides
protection against integer overflow by limiting integers to 
absolute values less than 2 000 000 000.  This machine-dependent
parameter is contained in subroutine CMINTG.
.HL 3 Real Numbers
A token is a <real> number if it occurs in a context where a
<real> is expected, and follows (approximately) the Fortran rules for
real constants.  A <real> may not contain any blanks
(i.e. <noise>).  The general form is:
.SK .I3
   <real> ::= [<S>][<D>...][.[<D>...]][E[<S>]<D>...]
.SK
where <S> is a sign ('+' or '-') and <D> is a digit.  The
following are examples of valid <reals>:
.SK .LITERAL
   3
   +4
   -6789
   3.
   3.3
   .3
   3E1
   3E-1
   3.E4
   -3.4E-5
   '-3.4E-5'
   .2E2
   E5
.END LITERAL .SK
Note that the decimal point is optional, and if not present
is assumed to follow the last character before the exponent.  Note
also that an exponent with no mantissa is valid, and has the
value ZERO.  A null token is also valid in a postional list,
and has
value zero.  If the 'E' is present, it must be followed by a
valid integer.  SCANLIB does not provide protection
against overflow or underflow when evaluating real tokens.
.HL 3 Names
A token is a <name> if it occurs in a context where a
<name> is expected, and is found in an appropriate
section of a table of <names>.  <name> follows the
same formation rules as <string>.
.HL 4 Lookup Rules
The rules for considering
a <token> to be "found" (as a name) are:
.SK
.LIST 0,"-"
.LE;only the first <namlen> characters are significant,
    the rest are ignored.
.LE;if the <token> is shorter than <namlen>, only
    the number of characters actually present are
    compared to the table.
.END LIST .SK
Thus, if a table contains an entry 'ABCDEF', and <namlen>
is 6, then both 'ABC' and 'ABCDEFGH' will match this entry
and be considered "found".  Note that this has the
consequences that
.SK .LIST 0,"-"
.LE;names may be abbreviated by the user to the shortest initial
    substring that is unique within a specific table.
.LE;the user may append extra non-significant characters
    to a name to aid readability, if a table contains
    abbreviations which are the initial <namlen> characters
    of corresponding extended words.
.LE;no entry in a table should be an initial substring
    of another entry in the same table.
.END LIST .SK
These rules are encoded in subroutine CMLKUP, and may be
changed there if desired.  To avoid user confusion, such
changes are not generally recommended.
.HL 3 Strings
A token is a <string> if it occurs in a context where a
<string> is expected.  A <string> is a sequence of any
non-<punct> characters, or a sequence of any characters
enclosed in <quotes> and containing an even number of
<quotes>.  In the latter case, the rules are the same as
for Fortran 77 character constants (pairs of consecutive
quotes, excluding the quotes which delimit the string, are
interpreted as single quotes).  A <string> has a
length attribute <stleng>, which may be zero to an
implementor-defined maximum (<toknsz>).
.HL 3 Use of Quotes
Any <token> which contains <punct> or <quote> or <noise> or <coment>
must be enclosed in <quotes>.  Any other <token> may be,
but need never be, enclosed in <quotes>.  The presence or
absence of enclosing <quotes> never affects the <type> of
a <token>.
.HL 2 Lists
A <list> is a sequence of zero or more <tokens>, all of the
same <type>, separated by <lstsep>, and followed by <argsep>
or <cmterm>.  SCANLIB uses two types of
<list>:  positional and non-positional.  The only difference
between the two is that positional lists may contain null items,
while null items in non-positional lists are ignored.  For
example, in the lists:
.SK .LITERAL
   <lista> ::= 1,2,,4
   <listb> ::= 1,2,0,4
   <listc> ::= 1,2,4
.END LITERAL .SK
<lista> is equivalent to <listb> if it is a positional list,
but <lista> is equivalent to <listc> if it is a non-positional
list.  Note that in the first case, the null token is given
a value (zero), while in the second case it is ignored.  This
difference shows up in the way the lists are stored by SCANLIB
for return to the implementor-provided semantic analyzer.  It
is up to the latter to process these lists in the
appropriate way to ensure positional sensitivity or the
lack of it.
.HL 2 Commands
The largest independent syntactic grouping is the command.  A
command is a command keyword followed by a <list> (which may be
disallowed by the implementor on a command-by-command basis),
followed by zero or more <keyword argument lists>, each of
which is an <argument keyword> followed by a <list>.  The various
items are separated by appropriate punctuation, as follows:
.SK .LITERAL
   <command> ::=
     <c-name>[<keysep>[<i-list>][<argsep>[<k-list>]]...]<cmterm>
 or
   <command> ::=
     <c-name>[<noise>[<i-list>][<argsep>[<k-list>]]...]<cmterm>

 where
   <c-name> is a command name
   <i-list> is an initial list
   <k-list> is a <keyword argument list> of the form
      <k-name>[<keysep>[<list>]]
   <k-name> is an argument keyword name which is recognized
      for use within a command headed by <c-name>
   <cmterm> is normally inserted by the command reader at
      <eol>, and need not be specified explicitly by the user
      unless another <command> follows on the same line.
.END LITERAL
.HL 3 Command Keyword
The first token in a command must be the command keyword.  This
keyword provides context for the rest of the command, if any.  A
command keyword with no arguments is a valid command.  If the
processor does not recognize the first token as a valid command,
the rest of the command is ignored, up to the next <cmterm>.
.HL 3 Initial List
A command may or may not allow an initial list.  If the initial
list is allowed, it need not be always present.  If it is not
allowed, but is present, it will be interpreted as a (probably
invalid) argument keyword.  Examples of commands with initial lists
only are:
.SK .TP 5 .LITERAL
   OPTIONS= 4,8,12
   INTAPE = ABC123
   LIMITS = 3.5, 80
   START  = 79:235:12:30:00
   STOP   = 79:235:15::/
.END LITERAL
.HL 3 Keyword Argument Lists
A command may allow zero or more keyword argument lists.  A
keyword argument list looks like a command with an initial
list, for example:
.SK .LITERAL
   PROCESS1 = OPTIONS = 4,8,12 / INTAPE = ABC123 / LIMITS = 3.5,80
   OUTAPE = / FILE=2 / LABEL=ANSI / DEVICE=9TRACK / DENSITY=1600
   OUTAPE = / (FILE=2) (DEV=9TRACK) (LAB=ANSI) (DENS=1600)
.END LITERAL .SK
Note that in the second example the <keysep> character (=)
is followed by <argsep> (/) to indicate that the allowed
initial list is not present.  in the first example the
command process1 does not allow an initial list, so this is
not required (although it is always allowed).
.HL 3 Comments
As a general rule, wherever any <punct> is
allowed, the following is equivalent:
.SK .I3
   [[<noise>][<comment>]]...<punct>[[<noise>][<comment>]]...
.SK
That is, any punctuation may be preceded or followed by
any combination of <noise> characters (nominally spaces) and
<comment> strings.  A <comment> is any sequence of characters
enclosed in <coment> characters (nominally '@'),
or beginning with <coment> and continuing to <eol>.
.HL 3 Continuation
SCANLIB can be operated in a mode which allows line
continuation.  This is accomplished by using subroutine CMCONT to collect
input line images into logical line images before passing to
CMSCAN for processing.  Continuation is indicated by entering
a <contin> character (nominally '_&') at any position where
<noise> or <punct> or <comment> is allowed, and then continuing
the command on the next input line.  The <contin> character
must occur within input card image positions 1-72, and the
resulting total logical
line must not exceed an implementor-defined maximum number of
characters.  Anything following <contin> on the same
input line image is ignored, and is not included in the character count.
.HL 3 Examples
Examples of the various command forms are:
.SK .LITERAL
   INTAPE = ABC123 / FILE = 3 / LABEL = STD
   SWITCH = ON
   LIMITS = 1.2, 3.0
   OPTIONS = ALPHA, DELTA, THIS, THAT
   SELECT= 4,8,17,2,27 @ THIS IS A COMMENT @
   CMD1=YES; CMD2=NO @ TWO COMMANDS ON A LINE
   SETA= @NO INITIAL LIST@ OPT=1/LIMIT=33.5/NAME=JOE/LABEL='#47A'
   SETB= OPT(1,2,3) LIMIT(55E2) LABEL('AB''CD') NAME()
   SETC= @INITIAL LIST OMITTED@ /OPT=5
   LONG = @ THIS IS A LONG COMMAND @ 111, 222, 333/ &
          KEY1 = 'STRING NUMBER ONE' , 'STRING NUMBER TWO' &
         /KEY2 = @NULL@
   PRINT MYFILE/COPIES=2
.END LITERAL
.PG .HL 1 IMPLEMENTING A COMMAND PROCESSOR
.HL 2 Designing a Command Language
Careful attention to language design is necessary if the
resulting language is to be acceptable to the users.  The
syntax supported by SCANLIB was designed with this in mind,
but a language is more than just syntax.  Important
additional considerations include such things as:
.SK .LIST 0,"-"
.LE;careful choice of names for commands, keywords,
    and list item names
.LE;proper tradeoffs between few commands with
    many options and many commands with few options
.LE;context sensitivity, i.e. whether the meaning of a
    keyword depends on the command in which it appears,
    and whether the meaning of a name in a list depends
    on the argument keyword and/or the command in which it appears
.LE;choice of defaults, so that the user may enter the
    minimum amount of information to get the
    application to perform its most commonly used functions
.LE;minimizing interdependence of commands, so that they
    can be entered in any order
.LE;provision for cancelling, overriding, or re-entering
    previously entered commands, especially by interactive users.
.END LIST .SK
SCANLIB places some restrictions in these areas -- some intentionally,
and others to facilitate a robust and flexible implementation.  The
implementor should be aware of these throughout the design
process.  Some of the important restrictions are:
.SK .LIST 0,"-"
.LE;once CMSCAN has control, it will scan and process to the
    end of the (logical) line.
.LE;semantic analysis can occur either after each line
    has been scanned, or after the entire input stream
    has been scanned.  This depends on the implementor,
    and impacts the way in which command overrides are
    handled.
.LE;commands are scanned from left to right, with no backing
    up or looking ahead.
.LE;CMSCAN puts values in lists.  It may overwrite them
    if the same command is entered more than once, or
    if the same keyword appears more than once on
    the same command, but it will not erase any.
.END LIST
.HL 3 Naming Considerations
The choice of names for commands, argument keywords, and
list items is of great importance to the user.  Good choices
make learning and remembering easier, and facilitate interpretation
of results.  The implementor must also be aware
of the search rules used by subroutine CMLKUP:
.SK .LIST 0,"-"
.LE;the number of characters in a token which are actually
    used in a table lookup is the smaller of <namlen> and the
    number of characters present in the token.  see the
    implications of this above under "names".
.LE;tables are searched sequentially, and the first match
    found (if any) is the one used.  If a user enters a
    token which is an initial substring of an entry in the
    corresponding table, the first such entry will be used.  A
    warning is given if the abbreviation is not unique.  This
    implies that commonly used names should be closer to the beginning
    of their table than obscure ones which the user may not know
    about.
.END LIST
.HL 3 Use of Positional Lists
Positional lists are notoriously difficult for users to use except
in the following cases:
.SK .LIST 0,"-"
.LE;lists containing three or fewer entries
.LE;lists where the positionality is widely standardized
    in common usage (such as yy:ddd:hh:mm:ss)
.LE;lists where all entries relate to the same thing,
    and where their positions in the list relate to the
    positions of entries in other, parallel lists.
.END LIST .SK
For example, if we wish to plot four channels of
data, specifying limits for each channel, we might use
a command structure such as:
.SK .I3
   PLOT=23,77,291,3031/MIN=0.,0.,-20.,300./MAX=10.,100.,0.,400.
.SK .I1
 or
.SK .LITERAL
   OPTION  = PLOT
   CHANNEL = 23,   77,  291,  3031
   MIN     = 0.,   0.,  -20,  300.
   MAX     =10., 100.,   0.,  400.
.END LITERAL .SK
Otherwise, positional lists should not be used.
.HL 3 List Context
The above examples illustrate one of the issues of list context
as well.  In the first example, the MIN and MAX <keyword
argument lists> are attached to the PLOT command.  Similar
<keyword argument lists> could also be used in another command
and refer to something different.  In the second example, MIN
and MAX are command keywords, and have no other context, so
they can have only one meaning.  For this reason, it is
desirable to list all the requirements of the application for
command inputs before selecting among the many possible ways
in which they may be stated.
.HL 3 Defaults
The careful selection of defaults is one of the major factors
influencing ease of use of a command language.  When all the
possible run-time inputs for the application have been listed,
examine each one carefully to determine whether it is meaningful
for it to have a default value.  For example, in an application
which reads and writes tapes, it is probably not meaningful
to have a default for the input tape reel number, but it may be
meaningful to default the output tape reel number to 'SCRTCH'.  In
the plotting examples given above, the default for all MIN items
might be zero.  If
several possible default values suggest themselves for the same
parameter, try to select the one which most users would want most
of the time, or consider the possibility that the default for one
parameter may be dependent on what the user enters for other
parameters.  For example, in a utility program which includes
such functions as copying and dumping tapes, the default may be
to dump without copying if no output tape is specified, or to
copy without dumping if one is.  This latter kind of defaulting
must be encoded in the semantic analyzer, while the simpler kinds
discussed above are best handled by tables which can be changed
easily.
.HL 2 Building the Processor
Once the language design is complete, you should have prepared
lists of:
.SK .LIST 0,"-"
.LE;command keywords
.LE;argument keywords allowed with each command
.LE;names which can appear as list items with each keyword
.LE;type and maximum length of the initial list (if any)
    for each command
.LE;type and maximum length of each keyword argument list
.LE;whether each list is positional or non-positional
.END LIST .SK
Arrange the command list in alphabetical order.  If two or more
commands have a common initial substring, either select alternate
words that do not, or reorder the list so that the most
commonly used words appear first.
.SK
If argument keywords are to be context-sensitive (i.e. to
have different results depending on which command they appear in),
then they must be replicated for each distinct context.  For example,
if the 'REELNUM' keyword may appear in both an 'INPUT' and 'OUTPUT'
command, the 'REELNUM' entry must appear twice.  Organize all the
keywords by command, and group separately those which are
context independent.  Consider redefining some or all of the
latter as commands rather than argument keywords.  Allow several
spare slots for each command, as new argument keywords are likely
to be needed later.  Within each command, order the
list of keywords in the same way as discussed above
for the command list.
.SK
Finally, collect all names which may appear in lists of type
<name>.  These should be organized into sub-lists, by
keyword.  The order of the names within each sub-list should
be such as to minimize
initial substring conflicts, and to simplify later semantic
analysis.  During command processing, each name will be translated
to an integer index which is simply its position in the name
table.  Names which have related meaning or function should
generally be assigned consecutive table locations.
.SK
Note that keyword arguments may have context restrictions, so
that they may appear only in certain commands.  These restrictions
are enforced by SCANLIB.  Names in lists, likewise,
may be restricted to specific keywords.
.SK
While it is permissible for the same name to be used as a
command name, an argument keyword, and a name list item, this
should be minimized because it may result in confusing
diagnostics when syntax errors occur.
.HL 3 List and Table Sizing
Once all the lists are complete, and sufficient spares have been
allowed for, the sizing parameters may be determined.  These are:
.SK .TP 16
.LITERAL
     Name    Default   Usage
     ----    -------   -----
   <cmtbsz>     50     size of command table
   <kwtbsz>    100     size of keyword argument table
   <nmtbsz>    200     size of name table
   <pntbsz>     10     size of table of punctuation characters
   <namlen>      6     maximum number of characters in a
                       command, keyword, or name
   <toknsz>     72     maximum number of characters in any
                       token, including quoted strings
   <lstlen>     12     maximum number of entries allowed
                       in any list
   <nilsts>     25     total number of integer lists
   <nnlsts>     25     total number of name    lists
   <nrlsts>     25     total number of real    lists
   <nslsts>     25     total number of string  lists
.END LITERAL .SK
If any of these parameters is too small, or significantly too large,
to meet the needs of your application, you may change the
corresponding parameter in SCANLIB.PARAMS, and recompile all
the SCANLIB routines.  Storage requirements in two pairs of COMMON
blocks are affected by these parameters.  The requirements are:
.SK .TP 4 .LITERAL
  <size of CMTABC> =
    <NAMLEN>*(<CMTBSZ>+<KWTBSZ>+<NMTBSZ>) + <PNTBSZ> + 1 characters
  <size of CMTABL> =
    5*(<CMTBSZ>+<KWTBSZ>) + <PNTBSZ> + 3                 words
.END LITERAL
.SK .TP 5 .LITERAL
  <size of CMLISC> =
    <LSTLEN>*<NSLSTS>*<TOKNSZ>                           characters
  <size of CMLIST> =
    <CMTBSZ> + <KWTBSZ>
      + <LSTLEN>*(<NILSTS>+<NNLSTS>+<NRLSTS>+<NSLSTS>)   words
.END LITERAL .SK
using the default values for these parameters, the sizes are thus:
.SK .TP 4 .LITERAL
     <size of CMTABC> =  2,111 characters
     <size of CMTABL> =    763 words
     <size of CMLISC> = 21,600 characters
     <size of CMLIST> =   1350 words
.END LITERAL .SK
Note that this is dominated by the product
.SK .I3
   <LSTLEN> * <NSLSTS> * <TOKNSZ>
.SK
which amounts to 21,600 characters using the values
supplied.  This may not be important if the command processing
is done at the beginning of an application execution and then
either overlayed by the main processing, or paged out of the
working set in a virtual memory machine.  However, it is
probably desirable to reduce it in many cases.  This can be achieved
by minimizing the use of string lists, the maximum length
of strings, and the maximum length of lists.  For example,
changing to a maximum of 6 string lists, maximum list
length of 8, and maximum string length of 12 characters
reduces the above product from 21,600 to 576.  Note however that
reducing <toknsz> may impact the entry of <integers> and
<reals> as well, since they are also limited by <toknsz> while
being scanned.
.HL 3 Table Creation
The information collected above, which will define the command
language, is encoded into tables in COMMON/CMTABL/ by means
of DATA statements in a BLOCK DATA subprogram.  Character
strings are entered one character per array element.  This form of
entry is rather tedious, but only has to be done once, and
is straightforward.  Alternatively, the EQUIVALENCED forms
(variable names end with 'TBE' instead of 'TBL') may be
used to enter each item as a single string.  For example, to
enter the command name
'INPUT' into position 23 of the command table, assuming <namlen>
is 6, one of the following statements can be used:
.SK .I7
      DATA (CMDTBL(I,23),I=1,NAMLEN)/'I','N','P','U','T',' '/
.SK .I1
 or
.SK .I7
      DATA CMDTBE(23) / 'INPUT ' /
.SK
The tables to be entered, and their contents, are as follows:
.SK .TP 6 .LITERAL
   Table  #Entries  Content
   ------ --------  -------
   CMDTBL <cmtbsz>  command names
   KWDTBL <kwtbsz>  keyword argument names
   NAMTBL <nmtbsz>  names allowed in name lists
   PUNTBL <pntbsz>  punctuation characters
   CARGTP <cmtbsz>  list type codes for initial lists
   CNARGS <cmtbsz>  max number of entries in initial lists
   CLSTDX <cmtbsz>  index of list where results are to be
                    stored from initial list
   KWSTRT <cmtbsz>  index into KWDTBL of first keyword allowed
                    for each command
   KWSTOP <cmtbsz>  index into KWDTBL of last keyword allowed
                    for each command
   KARGTP <kwtbsz>  list type codes for keyword argument lists
   KNARGS <kwtbsz>  maximum number of entries in kwd arg lists
   KLSTDX <kwtbsz>  index of list where results are to be
                    stored from keyword argument list
   NMSTRT <kwtbsz>  index into NAMTBL of first name allowed
                    for each keyword
   NMSTOP <kwtbsz>  index into NAMTBL of last name allowed
                    for each keyword
   PNCODE <pntbsz>  punctuation type codes corresponding to PUNTBL
.END LITERAL .SK
The first three tables contain character strings and are
two-dimensional.  The first index runs from 1 to <namlen>
and spans the characters in a given entry.  The second
index runs from 1 to _#Entries, and spans the various
entries.  The alternate, EQUIVALENCEd, forms are CMDTBE, KWDTBE, and
NAMTBE, respectively.  These are one-dimensional CHARACTER*<namlen>
string arrays.
.SK
List type codes to be entered in CARGTP and KARGTP are:
.SK .TP 4 .LITERAL
   1 for <integer> non-positional,  -1 for positional
   2 for <real>    non-positional,  -2 for positional
   3 for <name>    non-positional,  -3 for positional
   4 for <string>  non-positional,  -4 for positional
.END LITERAL .SK
Index values to be entered in CLSTDX and KLSTDX are indexes
to lists in COMMON/CMLIST/ where list values are to be
stored.  For example, if CLSTDX(3) is set to 5 and CARGTP(3)
is set to 2, then the command at CMDTBL(*,3) expects a non-positional
initial list of type <real>, which, if entered, will be
stored in RLIST(*,5).
.SK
A sample version of the required BLOCK DATA program is
contained in SCANLIB.CMTABLBD.  This may be used as a
guide or as a source file for text editing.
.HL 3 Setting Defaults
There are several ways to set defaults, once their values have
been decided.  The various ways have various consequences which
need to be considered.  The subroutine CMINIT should be called
before processing any commands, to clear all the lists in
/CMLIST/.  This call may be followed by a call to an
implementor-written routine which places default values in
appropriate places in the various lists.  Alternatively, /CMLIST/
can be initialized with a BLOCK DATA subprogram containing
default values, and the call to CMINIT omitted.  In
either case, if the user then
enters commands which provide other values, these list
positions will be overwritten.  The values used for the
defaults should be obtained from DATA statements, coded either
directly in the default-setting routine, or in a BLOCK DATA
program for a COMMON block read by that routine.
.SK
Another approach is to apply defaults during the semantic
analysis phase.  This approach is required if some defaults
depend on what the user has entered for other options (see
the tape utility example above).  To facilitate this type
of analysis, there are two tables, CMDFND and KWDFND, which
have entries set to 1 if corresponding commands or keywords
have been processed in the input stream, and 0 if not.
.SK
A mixture of these two types of default setting is also
possible, and is in general required if sophisticated
defaulting schemes are to be supported.
.HL 3 Writing a Command Reader
The implementor has several choices of how commands are
read and presented to the SCANLIB routines.  The simplest
way is to use subroutine CMREAD, which reads card images
from Fortran unit CMUNIT (in COMMON/CMTABL/),
echoes them to Fortran unit ERUNIT
(also in COMMON/CMTABL/), collects continuation lines, and
prepares a logical line image using subroutine CMCONT.  If
slightly different behavior is required, the implementor
can modify CMREAD to suit the needs of the application
environment.  If line continuation is not to be supported,
card images can be read directly with Fortran (or equivalent)
input (e.g. READ(5,FMT) card, with FMT being FORMAT(80A1)) and
the card image passed
directly to CMSCAN for processing.  Note that however the
input line image is prepared, it is necessary to ensure that
variable EOL in /CMTABL/ points to one position
beyond the
last significant character position in the line image, and
that the position pointed to contain a <cmterm> character.
.HL 3 Writing a Semantic Analyzer
The semantic analyzer serves several purposes:
.SK .LIST 0,"-"
.LE;perform global analysis of all command input
    to check validity, consistency, and completeness
.LE;supply defaults not otherwise supplied
.LE;translate results (if necessary) from the position and
    representation produced by SCANLIB to the position
    and representation needed by the application.
.END LIST .SK
Just how these functions are done depends entirely on the
application.  If the application is designed to be driven
directly by the lists produced by SCANLIB, then this function
may not be needed at all, or may be limited to checking that
all required inputs have been specified, and that all values
are valid.  If SCANLIB is being used to fit a command language
onto an existing or separately designed application, the
translation required may be extensive.  The example in Appendix
A shows a simple intermediate case.
.SK
The semantic analyzer may be called after each line is
processed by CMSCAN, may be called after all input has been
processed, or may be called after some special command (e.g.
'END' or 'RESET') has been processed, as indicated by the setting
of the corresponding entry in CMDFND.
.HL 3 Compiling
The routines of SCANLIB use SFTRAN3 "INCLUDE" statements
to bring in the PARAMETERS and COMMON blocks used.  If the
sizing parameters are to be modified, all the routines must be
recompiled through SFTRAN3 and Fortran.  Likewise, the
semantic analyzer, and probably the default setter, require
access to these, and so should include the SFTRAN3 statements:
.SK .LITERAL
  INCLUDE (SCANLIB.PARAMS)
  INCLUDE (SCANLIB.CMLIST)
  INCLUDE (SCANLIB.CMTABL)
.END LITERAL .SK
The contents of these modules must be made available to the
SFTRAN3 processor by whatever method is appropriate on the
specific system being used.  PARAMS
contains PARAMETER statements that define the sizes
of the various lists and tables used by SCANLIB.  It also
contains the statement
.SK .I7
       IMPLICIT INTEGER (A - Z)
.SK
and thus must appear before any other non-comment statements
in each program unit in which it is used.  All variables of
type other than INTEGER must thus be explicitly declared.
CMLIST contains the definition of COMMON/CMLIST/, which contains
the result lists.  CMTABL contains the definition of
COMMON/CMTABL/, which contains the language definition
tables and some constants which the user may wish to
modify, such as CMUNIT (the Fortran unit number for CMREAD
to use to read input), ERUNIT (the Fortran unit for writing
diagnostic messages), and EOL (the end-of-line pointer,
which may be modified at run time if line continuation
is used).
.HL 3 Linking
Copies of the compiled SCANLIB object modules
should be placed in a library which can be accessed by the
linking program.  For Univac 1100 applications, this is a
"@PREPped" program file, for IBM, a partitioned data set,
for VAX/VMS, an object library,
etc.  This library should be specified to the linker as
appropriate for the type of equipment being used.  All external
names in SCANLIB begin with the characters 'CM'.  If
conflicts occur with other libraries, there may be
renaming commands in the linker which will prevent the
need to rename all the SCANLIB routines in the source
code.
.HL 4 COMMON Blocks
Most linkers do not automatically include BLOCK DATA
subprograms, so it is necessary to explicitly request
inclusion of CMTABLBD, and any other BLOCK DATA used
by the application.
.HL 4 Overlaying
SCANLIB routines are designed not to cause problems in
an overlay environment.  Called routines do not retain
any information between calls.  All dynamic information
is passed through calling sequences, except for EOL
when line continuation is used.  With this exception,
/CMTABL/ and /CMTABC/ are read-only, and may be overlaid when SCANLIB
routines are not being used, provided that CMTABLBD
resides in the same overlay segment as the rest of the
SCANLIB routines.  COMMON /CMLIST/ and /CMLISC/ must not be overlaid
from the time they are cleared using CMINIT to the time at
which the semantic analyzer or the driven application
is finished using them.
.PG .HL 1 MAINTENANCE AND MODIFICATION
This chapter is for the use of anyone wishing to modify the
SCANLIB routines or understand their internal workings.  It
is not necessary for most implementors to be familiar with
this material, except for section 4.3 "Result Lists".
.HL 2 Functional Overview
.HL 3 Reading Input
Reading the input stream may be done by the implementor directly,
or it may be done by subroutines CMREAD and CMCONT.  CMREAD reads
input card images of up to 80 characters from Fortran unit CMUNIT,
echoes the input on Fortran unit ERUNIT, as defined in
COMMON/CMTABL/, and calls CMCONT to combine the input card
images into logical line images.  CMREAD returns to the calling
program with a logical line image and an indication of whether
an end of file was encountered on unit CMUNIT, either before or in
the middle of a logical line.  The latter case is reported as
an error.
.SK
CMCONT is called by CMREAD, or may be called by the implementor's
input routine.  Given a card image and the index of the last
character position previously used in the line image, it
scans the card image for a <contin> character which is not
part of a <comment> or quoted string.  It then appends the
portion of the card image to the left of the <contin> (or to
the left of column 73 if no <contin> is found) onto the
existing line image.  If the resulting line would be longer
than the maximum allowed by the calling routine, an error
message is issued.  CMCONT returns the index of the last
line position filled, and an indication of whether a <contin>
character was found.  If a <contin> character was found,
CMCONT is called again with the next input image, and
this is repeated until a non-continued image is processed.
.HL 3 Command Line Scanning
The highest level subroutine which does the actual input
parsing is CMSCAN, which takes a line image as input, and
produces appropriate results in the lists in COMMON/CMLIST/.  CMSCAN
scans the line from position 1 to position EOL-1, performing
the following:
.SK .LIST 0
.LE;pick up a token
.LE;look up the token in the command table
.LE;if the token is a valid command, then process it,
    otherwise issue a diagnostic and skip to the
    next command on the line, if any.
.END LIST .SK
Command processing involves processing the initial list,
if any, and then processing any keyword argument lists.  The
processing of the initial list involves picking up the
list type code, maximum list length, and result list index
from the appropriate tables, and calling the appropriate
subroutine to process the list.  These are:
.SK .TP 4 .LITERAL
   CMLSTI for integer lists
   CMLSTR for real lists
   CMLSTN for name lists
   CMLSTS for string lists
.END LITERAL .SK
Each routine is given control of the line scanning, and
returns from zero to the maximum allowed number of items
to the appropriate list.  Each also returns a type code
for the punctuation which terminated the scanning of the
list, with the corresponding scan pointer.
.SK
Keyword argument scanning is quite similar to command and
initial list scanning, except that different tables are
used, and zero or more keyword argument lists may be scanned.
.SK
CMSCAN returns when all commands on a line have been processed.
.HL 2 Tables
The SCANLIB routines are driven by tables in COMMON /CMTABL/
and /CMTABC/.  These
tables include all the commands, keywords, and names recognized
in an application command language, plus indicators of the type
and length of the list allowed with each command and keyword
argument, the keywords allowed with each command, and indexes to
the result lists which receive the results of the processing.  The
punctuation characters and their meanings are also encoded in these
tables.  /CMTABL/ also contains the Fortran unit number used for
printing diagnostic messages, and the index of the end of the
line image to be processed.
.SK
With the exception of the EOL index, which may be modified if
line continuation is being used, all these tables are read-only.
.HL 2 Result Lists
Results of the scanning are placed in lists in COMMON /CMLIST/
and /CMLISC/.  The
distinction between a list and a table as used in this document
is that tables are read-only, while lists contain results.  There
are two kinds of lists:
.SK .LIST 0,"-"
.LE;CMDFND and KWDFND contain indicators of whether or not
    each command or keyword argument was found (1 if found, 0 if not)
.LE;ILIST, NLIST, RLIST, SLIST contain the values extracted
    from integer, name, real, and string lists, respectively,
    and STLENG contains the lengths of the strings in SLIST.
.END LIST .SK
The index of CMDFND or KWDFND corresponds to the second index
of CMDTBL or KWDTBL, respectively.  The last index of the
other lists corresponds to entries in CLSTDX or KLSTDX.  The
next-to-last index corresponds to the order in which individual
list items were entered by the user.  For example, if the user
entered
.SK .I3
   ALPHA = 3,5,9,7
.SK
and the command 'ALPHA' is defined to have an initial list of
type <integer> at CLSTDX index of 23, then the result will be:
.SK .TP 3 .LITERAL
   ILIST(1,23) = 3
   ILIST(2,23) = 5
   ILIST(3,23) = 9
   ILIST(4,23) = 7
   ILIST(K,23) = 0 for K=5,LSTLEN
.END LITERAL .SK
If the user enters
.SK .I3
   ALPHA = 3,5,9,7;  ALPHA = 2,4
.SK
the result will be:
.SK .TP 3 .LITERAL
   ILIST(1,23) = 2
   ILIST(2,23) = 4
   ILIST(3,23) = 9
   ILIST(4,23) = 7
   ILIST(K,23) = 0 for K=5,LSTLEN
.END LITERAL .SK
The difference between positional and non-positional lists
is illustrated as follows.  If the user enters
.SK .I3
   ALPHA = 2,4,,6
.SK
then ILIST will look like
.SK .TP 6 .LITERAL
     Positional           Non-positional
   ---------------        ---------------
   ILIST(1,23) = 2        ILIST(1,23) = 2
   ILIST(2,23) = 4        ILIST(2,23) = 4
   ILIST(3,23) = 0        ILIST(3,23) = 6
   ILIST(4,23) = 6        ILIST(4,23) = 0
.END LITERAL
.HL 2 General Variable Definitions
In order to facilitate maintenance, the same variable names
are used for the same things throughout SCANLIB, to a
considerable extent.  Variables which are used in more than
one place are described here.  Note that these are not in
COMMON, and are not actually global, but are either local
copies in each subroutine, or are passed through calling
sequences.
.SK .TP 6 .LITERAL
   Name(Dim)     Type     Use
   ---------     ----     ---
   LINE(?)       CHAR*1   line image being scanned
   TOKEN(TOKNSZ) CHAR*1   token being picked up or processed
   ICHAR         INTEGER  index to a character within TOKEN
   ISTAT         INTEGER  status code -- 0=ok, 1=error
   LINDEX        INTEGER  list index spanning the entries in
                          a given list
   NCHARS        INTEGER  number of characters in TOKEN
   SCAN          INTEGER  current scan pointer
   SCANPT        INTEGER  scan pointer at subroutine input --
                          points to character at which to
                          begin scanning
   SCNTRM        INTEGER  scan pointer at subroutine output --
                          points to punctuation character
                          which stopped scan
   TRMTYP        INTEGER  punctuation type code of character
                          which last stopped a scan
.END LITERAL .SK
The variables marked CHAR*1 are implemented as INTEGERs in
the version of SCANLIB intended for pre-Fortran-77 use, and as
CHARACTER*1 variables in the Fortran 77 version.
.PAGE
.HL 2 Subroutines
All variables are type INTEGER, unless specified otherwise,
except that CARD, LINE, and TOKEN are CHARACTER*1 in the
Fortran 77 version.
.HL 3 CHRCAT
.LITERAL
     SUBROUTINE CHRCAT(INLENG,INSTRG,OUTLEN,  OUTSTR)

     CONVERT AN ARRAY OF CHARACTERS TO A CHARACTER STRING

     INPUTS:  INLENG - INTEGER - LENGTH OF INPUT ARRAY
              INSTRG - CHAR*1(INLENG) - CHARACTER ARRAY
              OUTLEN - INTEGER - LENGTH OF OUTPUT STRING

     OUTPUT:  OUTSTR - CHAR*(OUTLEN) - OUTPUT CHARACTER STRING
.END LITERAL .SK
CHRCAT converts a CHARACTER*1 array into a single character string
variable.  The number of characters copied from the input array to
the output string is the lesser of INLENG and OUTLEN.  If OUTLEN is
greater than INLENG, the output string is padded on the right with
blanks.
.HL 3 CMCASE
.LITERAL
     SUBROUTINE CMCASE(INCHAR,  OUCHAR)

     IF INPUT CHARACTER IS A LOWER CASE LETTER, CHANGE IT TO
     UPPER CASE.   OTHERWISE, OUTPUT = INPUT
     NOTE:  THIS ROUTINE IS VALID ONLY ON AN ASCII MACHINE.

     INPUT:  INCHAR - CHAR*1 - CHARACTER TO BE UPPER-CASED
     OUTPUT: OUCHAR - CHAR*1 - OUTPUT CHARACTER
.END LITERAL .SK
CMCASE converts one character to upper case.  If the input character
is anything other than a lower case letter, it is copied unchanged to
the output.  This routine is machine-dependent, and may need to be
modified for a machine with an internal character set other than ASCII.
.PAGE
.HL 3 CMCONT
.SK .LITERAL
     SUBROUTINE CMCONT(CARD,LAST,MXLINE,  COMPLT,LINE,NEWLST)

     COMBINE INPUT CARD IMAGES INTO A SINGLE LINE IMAGE,
     REMOVING CONTINUATION CHARACTERS.

     INPUTS--
       CARD(1:72) - FIRST 72 COLUMNS OF CARD IMAGE - CHAR*1(72)
       LAST       - LAST POSITION USED IN LINE FROM PREVIOUS CALL.
                    ZERO IF THIS IS START OF NEW LINE.
       MXLINE     - MAXIMUM NUMBER OF CHARACTERS ALLOWED IN LINE

     OUTPUTS--
       COMPLT     - 1 = LINE IS COMPLETE, 0 = MORE INPUT NEEDED
       LINE(1:MXLINE) - RESULT OF COMBINING INPUTS - CHAR*1(MXLINE)
       NEWLST     - LAST CHARACTER POSITION FILLED IN LINE()
.END LITERAL .SK
CMCONT performs the following functions:
.SK
.LIST 0
.LE;Scan card using CMPOSL until either
.SK
.LIST 0
.LE;A <contin> character is found outside a <comment>
    or quoted string, or
.LE;column 72 has been scanned without finding <contin>.
.END LIST
.LE;If <contin> was found, indicate that the line is not
    complete, and set the copy limit to one position
    before <contin>, so it will not be copied.
.LE;Append characters from CARD onto LINE, starting at
    CARD position 1 and continuing to position 72 or
    the copy limit from (2) above, whichever is less,
    but in no case copying more characters than LINE
    can hold.  Return the index into LINE of the last character
    copied.  NEWLST can never exceed MXLINE.
.LE;If there were too many characters for LINE to hold,
    issue an error message.  If more continuation card images
    follow, each will result in an error message.
.END LIST
.PAGE
.HL 3 CMERMS
.LITERAL
     SUBROUTINE CMERMS(ERNUM,TOKEN,NCHARS)

     PRINT ERROR MESSAGE

     INPUTS--
       ERNUM   - ERROR NUMBER
       TOKEN() - TEXT TO BE PRINTED AFTER MESSAGE TEXT - CHAR*1()
       NCHARS  - NUMBER OF CHARACTERS IN TOKEN
.END LITERAL .SK
CMERMS is the centralized error message printing routine.  It
prints a message composed of text keyed to the input error
number, followed by the characters in token.  If TOKEN is
null, the text 'NULL ITEM' is substituted.  Messages are
printed on the Fortran unit number contained in ERNUM, which
is normally obtained from ERUNIT in COMMON/CMTABL/.
.HL 3 CMHELP
.LITERAL
     SUBROUTINE CMHELP(TOKEN,NCHARS)

     PRINT HELP TEXT (ON UNIT ERUNIT) FOR THE COMMAND IN TOKEN

     INPUTS:  TOKEN() - CHAR*1 - COMMAND TO BE HELPED
     .        NCHARS  - NUMBER OF CHARACTERS IN TOKEN
     OUTPUTS: NONE  -- TEXT PRINTED ON UNIT ERUNIT
.END LITERAL .SK
CMHELP is not called by any SCANLIB routines, but may be called
by the implementor's semantic analysis routine if desired
(typically in response to a command of the form HELP <cmdname>).  The
input token must be a valid command name (or abbreviation) from
CMDTBL.  The CMHELP routine displays information from the various
tables related to the specified command.  Output is to ERUNIT.
.PAGE
.HL 3 CMINIT
.I5
     SUBROUTINE CMINIT
.SK
CMINIT has no arguments in the calling sequence.  Its function
is to clear to zeroes all lists in COMMON/CMLIST/.  The lists
of strings in SLIST are cleared by setting their lengths to
zero in STLENG.
.HL 3 CMINTG
.LITERAL
     SUBROUTINE CMINTG(TOKEN,NCHARS,  INTVAL,ISTAT)

     CONVERT CHARACTER STRING TO INTEGER (SIGN OPTIONAL)

     INPUTS--
       TOKEN() - TOKEN TO BE CONVERTED - CHAR*1()
       NCHARS  - NUMBER OF CHARACTERS IN TOKEN

     OUTPUTS--
       INTVAL  - INTEGER VALUE CONVERTED FROM TOKEN
       ISTAT   - RESULT CODE = 0 IF OK
                             = 1 IF ILLEGAL CHARACTER
.END LITERAL .SK
CMINTG converts an integer token to an integer variable.  It first
checks for a leading sign, and then checks the remaining characters to
make sure they are decimal digits.  ISTAT is set to one if a non-digit
is found.  If the absolute value represented by TOKEN exceeds 
2,000,000,000, the output (INTVAL) is set to zero, to protect
against integer overflow on a 32-bit machine.  A token
with a zero value is returned as zero, never as "minus zero".
.PAGE
.HL 3 CMLKUP
.LITERAL
     SUBROUTINE CMLKUP(TOKEN,NCHARS,TABLE,START,STOP,  INDEX)

     LOOK UP TOKEN IN THE SECTION OF TABLE BOUNDED BY START,STOP

     INPUTS--
       TOKEN() - CHARACTERS TO BE LOOKED UP - CHAR*1()
       NCHARS  - NUMBER OF CHARACTERS IN TOKEN
       TABLE(,)- TABLE OF CHARACTER STRINGS (TYPE <NAME>) - CHAR*1
       START   - INDEX OF FIRST TABLE ENTRY TO CHECK
       STOP    - INDEX OF LAST  TABLE ENTRY TO CHECK

     OUTPUT--
       INDEX   - INDEX OF TABLE ENTRY WHICH MATCHES TOKEN IF FOUND
                 (ZERO IF NOT FOUND)
.END LITERAL .SK
CMLKUP is the table lookup subroutine for SCANLIB.  This is
the routine that must be modified if the abbreviation or
uniqueness rules are to be changed.  CMLKUP uses variable
NMAX to control the number of characters to be matched
between TOKEN and TABLE.  NMAX is set to the smaller of
NCHARS, the number of characters in TOKEN, and NAMLEN, the
number of characters in the table entries.  If TOKEN is null,
processing is bypassed and INDEX is returned as zero.  The
characters in TOKEN
are converted to upper case before being compared with TABLE.
.SK
Abbreviations
could be disallowed by inserting the following functions
at the beginning of CMLKUP:
.SK .LIST 0
.LE;Create a temporary string TOKEN2 containing NAMLEN
    blank characters.
.LE;Copy the smaller of NCHARS and NAMLEN characters from
    TOKEN to TOKEN2.
.LE;Use all NAMLEN characters of TOKEN2 for the
    table lookup, i.e. set NMAX to NAMLEN, and use
    TOKEN2 in place of TOKEN.
.END LIST .SK
CMLKUP searches the table sequentially from START to STOP.  This
allows the calling routine to specify that only a subset of
a table is eligible for searching.  The search continues until
a match is found or the specified part of the table has
been exhausted.  A "WHILE" loop is used to search the
table entries from START to STOP.  This
loop may be executed zero times, so that
a null table (START > STOP) can be
specified.  No token will match in a null table.  If more than
one match is found, a warning message is issued, which includes
the value in TABLE that will be used.
.PAGE
.HL 3 CMLSTI
.LITERAL
     SUBROUTINE CMLSTI(MAXLST,LINE,SCANPT,  SCNTRM,TRMTYP,ILIST)

     SCAN A COMMAND ARGUMENT INTEGER LIST

     INPUTS--
       MAXLST - IABS(MAXLST) IS MAXIMUM NUMBER OF ITEMS ALLOWED IN
                THE LIST.  THE LIST IS POSITIONAL IF MAXLST LT 0
       LINE() - LINE OF CHARACTERS TO BE SCANNED - CHAR*1()
       SCANPT - INDEX OF CHARACTER IN LINE() TO START SCANNING

     OUTPUTS--
       SCNTRM - INDEX OF CHARACTER IN LINE() WHICH STOPPED SCAN
       TRMTYP - PUNCTUATION TYPE CODE FOR LINE(SCNTRM)
       ILIST()- DESTINATION FOR LIST VALUES
.END LITERAL .SK
CMLSTI is the routine which scans integer lists.  It scans
until it reaches a punctuation character
"stronger" than <lstsep>.  The strength of punctuation is:
.SK .I3
   <noise> < <coment> < <lstsep> < <keysep> < <argsep> < <cmterm>
.SK
If the list is positional (MAXLST < 0), tokens are
collected using CMPOSL, which allows null tokens.  Otherwise
CMTOKN is used, which ignores null tokens.  As long as the number
of tokens found does not exceed the maximum number of list
entries allowed, tokens are converted to integers by CMINTG
and placed in ILIST.  Error messages are issued if CMINTG
finds invalid integer tokens.  If more tokens are found than
are allowed, error messages are issued and the excess tokens
are ignored.
.PAGE
.HL 3 CMLSTN
.LITERAL
     SUBROUTINE CMLSTN(MAXLST,LINE,SCANPT,KWDNDX,
    *                  SCNTRM,TRMTYP,NLIST)

     SCAN A COMMAND ARGUMENT NAME LIST

     INPUTS--
       MAXLST - IABS(MAXLST) IS MAXIMUM NUMBER OF ITEMS ALLOWED IN
                THE LIST.  THE LIST IS POSITIONAL IF MAXLST LT 0
       LINE() - LINE OF CHARACTERS TO BE SCANNED - CHAR*1()
       SCANPT - INDEX OF CHARACTER IN LINE() TO START SCANNING
       KWDNDX - INDEX OF KEYWORD TO PROVIDE SEARCH CONTEXT
                (IF ZERO, SEARCH WHOLE NAME TABLE)

     OUTPUTS--
       SCNTRM - INDEX OF CHARACTER IN LINE() WHICH STOPPED SCAN
       TRMTYP - PUNCTUATION TYPE CODE FOR LINE(SCNTRM)
       NLIST()- DESTINATION FOR LIST VALUES (NAME INDEXES)
.END LITERAL .SK
CMLSTN is the routine which processes lists of type <name>.  It
functions exactly the same as CMLSTI, except that the
conversion from TOKEN to integer value is done by calling
CMLKUP with NAMTBL as the table argument, instead of calling
CMINTG.  START and STOP indexes to limit the portion of NAMTBL
searched are provided from the keyword context implied by KWDNDX.
.PAGE
.HL 3 CMLSTR
.LITERAL
     SUBROUTINE CMLSTR(MAXLST,LINE,SCANPT,  SCNTRM,TRMTYP,RLIST)

     SCAN A COMMAND ARGUMENT REAL LIST

     INPUTS--
       MAXLST - IABS(MAXLST) IS MAXIMUM NUMBER OF ITEMS ALLOWED IN
                THE LIST.  THE LIST IS POSITIONAL IF MAXLST LT 0
       LINE() - LINE OF CHARACTERS TO BE SCANNED - CHAR*1()
       SCANPT - INDEX OF CHARACTER IN LINE() TO START SCANNING

     OUTPUTS--
       SCNTRM - INDEX OF CHARACTER IN LINE() WHICH STOPPED SCAN
       TRMTYP - PUNCTUATION TYPE CODE FOR LINE(SCNTRM)
       RLIST()- (TYPE REAL) DESTINATION FOR LIST VALUES
.END LITERAL .SK
CMLSTR is the routine which processes lists of type <real>.  It
functions exactly the same as CMLSTI, except that the
conversion from TOKEN to real value is done by calling
CMREAL instead of CMINTG.
.HL 3 CMLSTS
.LITERAL
     SUBROUTINE CMLSTS(MAXLST,LINE,SCANPT,
    *                  SCNTRM,TRMTYP,SLIST,STRLEN)

     SCAN A COMMAND ARGUMENT STRING LIST

     INPUTS--
       MAXLST - IABS(MAXLST) IS MAXIMUM NUMBER OF ITEMS ALLOWED IN
                THE LIST.  THE LIST IS POSITIONAL IF MAXLST LT 0
       LINE() - LINE OF CHARACTERS TO BE SCANNED - CHAR*1()
       SCANPT - INDEX OF CHARACTER IN LINE() TO START SCANNING

     OUTPUTS--
       SCNTRM - INDEX OF CHARACTER IN LINE() WHICH STOPPED SCAN
       TRMTYP - PUNCTUATION TYPE CODE FOR LINE(SCNTRM)
       SLIST(,)- (TYPE CHAR*1) DESTINATION FOR LIST VALUES
       STRLEN()- LENGTHS OF STRINGS
.END LITERAL .SK
CMLSTS is the routine which processes lists of type
<string>.  It functions the same as CMLSTI, except that
no conversion from TOKEN to string is required, since
both are character strings with the same representation.  CMLSTS
simply copies TOKEN to SLIST, and puts the number of characters
in STRLEN.
.PAGE
.HL 3 CMPOSL
.LITERAL
     SUBROUTINE CMPOSL(LINE,SCANPT,  SCNTRM,TOKEN,NCHARS,TRMTYP)

     SCAN COMMAND LINE AND PICK UP NEXT TOKEN.  THIS ROUTINE IS USED
     FOR POSITIONAL LISTS, SO A TOKEN MAY BE NULL.  TOKEN IS DEFINED
     AS A STRING OF NON-PUNCTUATION CHARACTERS BETWEEN 2 PUNCTUATION
     CHARACTERS, WITH <NOISE> CHARACTERS AND COMMENT STRINGS REMOVED.
     A QUOTED STRING IS RETURNED WITHOUT DELIMITING QUOTES.
     INTERIOR QUOTES ARE RETURNED ONE-FOR-TWO.

     INPUTS--
       LINE() - INPUT LINE - CHAR*1()
       SCANPT - INDEX TO LINE OF FIRST CHARACTER TO SCAN

     OUTPUTS--
       SCNTRM - INDEX TO LINE OF CHARACTER WHICH STOPPED SCAN
       TOKEN()- CHARACTERS COMPRISING THE TOKEN - CHAR*1(NCHARS)
       NCHARS - NUMBER OF CHARACTERS IN TOKEN
       TRMTYP - PUNCTUATION TYPE CODE OF LINE(SCNTRM)
.END LITERAL .SK
CMPOSL is the basic token scanning routine.  Beginning at
the character of LINE indicated by SCANPT, it scans until
it finds a punctuation character, and returns the characters
(if any) before that punctuation as a token.  It also
returns the punctuation type code of the character which
stopped the scan.  Noise characters and comment strings at
the beginning and end of the scan are ignored.
.SK
The scanning process begins by calling CMSKIP to skip any
leading noise or comments.  When CMSKIP returns, the scan
pointer points to one of:
.SK .LIST 0,"-"
.LE;QUOTE
.LE;other punctuation
.LE;first character of a token
.END LIST .SK
If the character is punctuation, other than QUOTE, no further
processing is needed, and a null token is returned.  If the
character is a QUOTE, the scan pointer is advanced to the
next character, and CMPOSL is placed in string mode.  If
CMPOSL is not in string mode, token scanning proceeds by
copying characters from line to token until a punctuation
character is reached.  If a QUOTE is seen, an error message is
issued, and the QUOTE is ignored.
.SK
The key local variable used with the string mode is EVENQ,
which indicates whether an even number of QUOTE characters
has been processed before the current character.  EVENQ is
set to .FALSE. when the opening quote of the string is
processed.  Subsequently, any non-QUOTE character seen when
EVENQ is false is processed as part of the string.  When a
QUOTE character is seen, then:
.SK .LIST 0,"-"
.LE;If EVENQ is false, i.e. the last QUOTE seen was an odd
    one, the current QUOTE is an even one.  Either it is the
    closing QUOTE of the string, or it will be followed
    immediately by another QUOTE, indicating that one QUOTE
    should be entered in the TOKEN then.  Nothing is done
    with this even QUOTE, except to set EVENQ to .TRUE.
.LE;If EVENQ is true, the immediately preceding character
    must have been a QUOTE, so the current QUOTE is copied
    to TOKEN, and EVENQ is set to .FALSE.
.END LIST .SK
When a non-QUOTE character is seen when EVENQ is true, it means
that the immediately preceding character was the terminating
QUOTE of the string.  This is so because the first non-QUOTE
character seen when EVENQ is true will force termination of
the scan.  If the character is <punct>, <noise>, or <coment>,
this is the normal termination of the string.  CMSKIP is then
called to skip any trailing noise or comments.  If the
character after the terminating QUOTE is not one of the above,
an error message is issued, the string is terminated, the scan
is backed up to point to the closing QUOTE, and the TRMTYP code
is set to <argsep>.  This will usually cause the remaining
text to be treated as an invalid keyword argument field, up
to the next <argsep>, and it will be ignored, with appropriate
error messages being issued.
.SK
If a quoted string is still open when the scan reaches EOL,
an error message is issued, and TRMTYP is set to <cmterm> to
cause command scanning to terminate.
.SK
When scanning has stopped, for whatever reason, CMSKIP is called
to skip over any trailing noise or comments.  If the scan stopped
on anything other than <noise> or <coment>, CMSKIP does nothing
except set SCNTRM=SCAN.  The final step is then to set TRMTYP
to the code corresponding to the terminating punctuation.
.PAGE
.HL 3 CMREAD
.LITERAL
     SUBROUTINE CMREAD(MXLINE,  LINE,EOF)

     READ INPUT CARD IMAGES AND PREPARE INPUT FOR CMSCAN

     INPUT--
       MXLINE - MAX NUMBER OF CHARACTERS ALLOWED IN A LOGICAL LINE

     OUTPUTS--
       LINE() - COMPLETED LINE - CHAR*1(MXLINE)
       EOF    - = 0 IF NO EOF ON UNIT CMUNIT,
                = 1 IF EOF AND NO PARTIAL LINE HAS BEEN READ,
                = 2 IF EOF BEFORE EXPECTED CONTINUATION CARD
.END LITERAL .SK
CMREAD is an optional routine which may be used as is, or as
a model for producing an input routine which uses CMCONT to
support line continuation.  It reads card images from Fortran
unit CMUNIT, echoes them on unit ERUNIT, and calls CMCONT to
build a line image.  This is repeated until a card image which
does not contain a <contin> character has been processed.
.SK
The return code eof indicates one of the following outcomes:
.SK .LIST 0,"-"
.LE;normal completion with a new line available
.LE;end of file on unit CMUNIT, no partial line read
.LE;end of file on unit CMUNIT, after starting but
    before completing a line.
.END LIST .SK
CMCONT takes care of preventing line overflow, and issuing
error messages if the input line is too long.
.PAGE
.HL 3 CMREAL
.LITERAL
     SUBROUTINE CMREAL(TOKEN,NCHARS,  REAVAL,ISTAT)

     CONVERT CHARACTER STRING TO REAL VALUE.
     SIGN, DECIMAL POINT, EXPONENT OPTIONAL

     INPUTS--
       TOKEN() - TOKEN TO BE CONVERTED - CHAR*1()
       NCHARS  - NUMBER OF CHARACTERS IN TOKEN

     OUTPUTS--
       REAVAL  - REAL VALUE CONVERTED FROM TOKEN
       ISTAT   - RESULT CODE - = 0 IF OK
                               = 1 IF ERROR
.END LITERAL .SK
CMREAL converts a real token to a real variable.  It first
checks for a leading sign, and encodes it and advances the
scan if one is found.  It then scans for the three remaining
optional parts of the token:  integer part, fractional part,
and exponent.  These are combined at the end into a single
DOUBLE PRECISION quantity, and then converted to REAL.
.SK
The variables NLEFT and NRIGHT encode the number of digits
found to the left and right of the decimal point.  LSTART
and RSTART point to their starting positions, and LEFT and
RIGHT hold the converted integer values.
.SK
After sign processing, the token is scanned until a non-digit
is found.  The digits processed (if any) are passed to CMINTG,
which returns the value in LEFT.
.SK
If there are characters remaining in TOKEN, the next character
is checked to see if it is a decimal point.  If so, scanning
of the fractional part begins.  TOKEN is scanned until a
non-digit is found, and the resulting digits (if any) are
sent to CMINTG, which returns the value in RIGHT.
.SK
If there are characters remaining in TOKEN, the next character
is checked to see if it is 'E'.  If so, the rest of TOKEN is
sent to CMINTG, which returns a signed exponent value in
EXPVAL.  If the characters after the 'E' do not form a valid
integer token, an error indication is returned from CMINTG.
.SK
Finally, LEFT, RIGHT, EXPVAL, and SIGN are combined to form
the result.  No checking is done to prevent arithmetic
overflow or underflow or exponent overflow.
.PAGE
.HL 3 CMSCAN
.LITERAL
     SUBROUTINE CMSCAN(LINE)

     THIS IS THE TOP LEVEL ROUTINE IN THE SCANLIB COMMAND PROCESSOR
     LIBRARY.  IT PARSES THE COMMAND KEYWORD, LOOKS UP THE COMMAND,
     AND PARSES THE ARGUMENT LIST(S).

     INPUT -- LINE() - LINE OF CHARACTERS TO BE SCANNED - CHAR*1()
                     -    LINE(EOL) = <CMTERM>

     OUTPUT -- NONE IN CALLING SEQUENCE
               RESULTS STORED IN LISTS IN /CMLIST/
.END LITERAL .SK
CMSCAN is the main subroutine which performs the command
line scanning and analysis.  For each command on the input
line it does the following:
.SK .LIST 0,"-"
.LE;call CMTOKN to pick up the first non-null token,
    which should be the command name,
.LE;if a token was found, call CMLKUP to look it up
    in the command table,
.LE;if the command was found, set the corresponding
    entry in CMDFND, and process the command, otherwise
    issue an error message and skip to the end of the
    command using CMTOKN, to avoid confusion with <cmterm>
    characters which may be inside quoted strings or comments.
.END LIST .SK
Command processing consists of two steps:
.SK .LIST 0,"-"
.LE;if an initial list is allowed, process it,
.LE;if any keyword argument lists are present, process them.
.END LIST .SK
Initial list processing is done as follows:
.SK .LIST 0,"-"
.LE;pick up the list type, maximum list length, and result
    list index from the appropriate tables in /CMTABL/,
.LE;according to the list type, call the appropriate one
    of CMLSTI, CMLSTR, CMLSTN, or CMLSTR to scan and
    process the list into the appropriate result list.
.END LIST .SK
Keyword argument list processing is analogous to processing
the command and initial list:
.SK .LIST 0,"-"
.LE;call CMTOKN to pick up the first token in the
    keyword argument, which should be the argument keyword,
.LE;if a non-null token was found, call CMLKUP to look it
    up in the portion of the keyword table allocated to the
    command currently being processed,
.LE;if the keyword was found, set the corresponding entry in
    KWDFND, and pick up the corresponding
    list type, maximum list length, and result list
    index from the appropriate tables in /CMTABL/,
.LE;according to the list type, call the appropriate one
    of CMLSTI, CMLSTR, CMLSTN, or CMLSTS to scan and
    process the list into the appropriate result list,
.LE;if the keyword was not found, issue an error message
    and call CMTOKN to advance the scan to the next
    keyword argument or the end of the command.
.END LIST .SK
At each step, the called subroutines return the scan pointer
(SCNTRM) pointing to the punctuation character which stopped
the scan.  Immediately after each call, CMSCAN sets the scan
to SCNTRM+1 so the next scanning operation will start with
a new character.
.SK
Note that because CMTOKN is used to pick up commands
and keywords, null commands and null keyword arguments
are ignored.  This means that it is always safe to
over-punctuate the input (except with positional lists).
.PAGE
.HL 3 CMSKIP
.LITERAL
     SUBROUTINE CMSKIP(LINE,SCANPT,  SCNTRM)

     SKIP OVER NOISE AND COMMENT FIELD(S), IF ANY, I.E. --
     IF FIRST NON-NOISE CHAR AT OR AFTER LINE(SCANPT) IS <COMENT>,
     RETURN WITH SCNTRM POINTING AT FIRST NON-NOISE CHAR AFTER
     CLOSING <COMENT> OR AT <EOL>.  ELSE SCNTRM=SCANPT.

     INPUTS--
       LINE() - COMMAND LINE - CHAR*1()
       SCANPT - STARTING SCAN POINTER

     OUTPUT--
       SCNTRM - ENDING SCAN POINTER
.END LITERAL .SK
CMSKIP is the routine which processes noise and comments.  Its
function is limited to advancing the scan pointer over any
combination of <noise> and <comments>.  If CMSKIP is called
with the scan pointer pointing to a character which is not
<noise> or <coment>, it simply returns the output scan
pointer equal to the input scan pointer.
.SK
CMSKIP first skips zero or more <noise> characters, using a
WHILE loop.  Then, while it sees an opening <coment>, it
scans to the next (closing) <coment>, then skips zero or
more <noise> characters.  All this is repeated while the scan
is less than EOL.  This algorithm results in skipping
.SK .LITERAL
   zero or more <noise>, followed by
   zero or more ( <comment> followed by zero or more <noise> )
.END LITERAL .SK
which is equivalent to any combination of <noise> or <comment>.
.PAGE
.HL 3 CMTOKN
.LITERAL
     SUBROUTINE CMTOKN(LINE,SCANPT,  SCNTRM,TOKEN,NCHARS,TRMTYP)

     SCAN FOR A TOKEN.  A TOKEN IN THIS ROUTINE IS A NON-NULL
     STRING OF NON-PUNCTUATION CHARACTERS, NOT INCLUDING
     COMMENT STRINGS.  THIS ROUTINE SCANS UNTIL A TOKEN IS FOUND,
     OR UNTIL A TERMINATOR OF LEVEL <ARGSEP> OR HIGHER IS REACHED.
     SEE ALSO SUBROUTINE CMPOSL.

     INPUTS--
       LINE() - INPUT LINE - CHAR*1()
       SCANPT - INDEX TO LINE OF FIRST CHARACTER TO SCAN

     OUTPUTS--
       SCNTRM - INDEX TO LINE() OF CHARACTER WHICH STOPPED SCAN
       TOKEN()- CHARACTERS COMPRISING THE TOKEN - CHAR*1(NCHARS)
       NCHARS - NUMBER OF CHARACTERS IN TOKEN
       TRMTYP - PUNCTUATION TYPE CODE OF LINE(SCNTRM)
.END LITERAL .SK
The function of CMTOKN is basically the same as that of
CMPOSL, except that null tokens are skipped.  CMTOKN
simply calls CMPOSL until it returns a non-null token,
or until a punctuation of strength <argsep> or <cmterm>
is reached.
.HL 3 SCANTABLE
SCANTABLE is a main program which, when linked with your version of
CMTABLBD, will display your tables in a format which facilitates
making modifications.  Printed output goes to the standard Fortran
output using WRITE (*, fmt) statements. 
.APPENDIX APPENDIX A - EXAMPLE
.PG .HL 1 APPENDIX A - EXAMPLE
This example is intended to illustrate some of the techniques
of using SCANLIB, without getting too involved in all the
complications posed by very sophisticated or
specialized language designs which are possible.
.SK
This example is for a simple utility application as alluded
to in the text.  The application will process tapes containing
a mixture of four record types -- designated AA, AB, BA, and
BB.  It can select any or all of these types for processing,
and can do any or all of:
.SK .LIST 0,"-"
.LE;copy selected records to an output tape
.LE;print the contents of selected records
.LE;plot up to four "channels" of data, indicated by
    channel numbers, from a single record type.
.END LIST .SK
In addition, it can select data by an associated time tag,
so that data outside some specified time interval can be
ignored.  Tapes may have more than one file, and processing
may begin with any file, and continue for any number of files.
.HL 2 Language Definition
Commands will be needed to specify input and output tapes,
including tape reel numbers, file numbers, device type
(7 or 9 track), recording density, labelling, and owner
identification or password for enabling writing on the
output tape.  Additional commands will be needed for
specifying the various options:  copying, printing,
and plotting, for selecting record types and time
interval, and for specifying various characteristics
of the plots.  Note that we will not address the issues
of how any of these things are accomplished, but will
merely use SCANLIB to set up a machine independent way
for a user to specify what is wanted.
.SK
Reasonable defaults would seem to be:
.SK .LIST 0,"-"
.LE;Copying is implied by specifying an output
    tape.  If no output tape is specified, printing
    will be done by default.
.LE;If record types are not specified, all will be processed.
.LE;If times are not specified, all will be processed.
.LE;Tapes will be 9 track, 1600 bpi density, unlabelled
    unless specified otherwise.
.LE;If plotting is requested, printing will not be done
    unless explicitly requested.
.LE;Plotting will never be done unless explicitly requested.
.END LIST .SK
This appears to be enough for now.  We will have to develop
a few more defaults as the details of the language emerge.
.SK
We now try to pick some specific keywords and options.  There
are many ways to do this.  For this illustration, we will
lean towards few commands with many options.  The opposite
approach could be used with roughly equal ease.
.SK
Let us try the following set of commands:
.SK .LITERAL
   INTAPE = <REEL#> / FILE=<I> / DEVICE=<N> / LABEL=<N> &
                    / DENSITY=<N>
   OUTAPE = <REEL#> / FILE=<I> / DEVICE=<N> / LABEL=<N> &
                    / DENSITY=<N> / PASSWORD=<S>
   PRINT = <LIST OF <N>>
   START = <YY:DDD:HH:MM:SS>
   STOP  = <YY:DDD:HH:MM:SS>
   PLOT = <LIST OF <I>> / MIN = <LIST OF <R>> &
              MAX = <LIST OF <R>> / LABEL=<S> / GRID=<N>
   FILES = <I>
   END
   RESET
.END LITERAL .SK
where most of the options are as alluded to in the text.  The
print command will take a list of record type names:
.SK .I3
   AA, AB, BA, BB, A*, B*, *A, *B, OR **
.SK
where * will imply both A and B, i.e. A* means the same as
AA,AB.  (This would be more useful in practice if there
were more possibilities, such as AA,AB,AC,AD...).
.SK
The label option on the PLOT command will specify a character
string to be used as a plot label.  The default will be
the null string.  The FILES command will specify the number
of files to process.  The default will be one.  The FILE
option on the INTAPE and OUTAPE command will specify the
starting file number, and will default to one.  The lists in
the PLOT command will be positional, as discussed in the text.
.TP 14 .SK
The list of commands is:
.SK .LITERAL
    # COMMAND  CNARGS CARGTP CLSTDX KEYWORDS
    - -------  ------ ------ ------ --------
    1 END         0      -      -      -
    2 FILES       1      1      1      -
    3 INTAPE      1      4      1   DENSITY,DEVICE,FILE,LABEL
    4 OUTAPE      1      4      2   DENSITY,DEVICE,FILE,LABEL,
                                    PASSWORD
    5 PLOT       -4      1      2   GRID,LABEL,MAX,MIN
    6 PRINT       4      3      1      -
    7 RESET       0      -      -      -
    8 START      -5      1      3      -
    9 STOP       -5      1      4      -
.END LITERAL .SK
This table is constructed simply by following these steps:
.SK .LIST 0
.LE;Arrange the commands in some order (alphabetic is usually
    best).
.LE;Determine the maximum number of items allowed in the
    initial list for each command.  If the list is positional,
    negate this number.
.LE;Determine the argument type for the initial list --
.BR
        1 = <integer>, 2 = <real>, 3 = <name>, 4 = <string>
.LE;List the keywords allowed with each command.
.LE;Assign list indexes sequentially by argument type, i.e.
    assign ascending indexes for all lists with CARGTP=1,
    starting with 1, then for all lists with CARGTP=2,
    starting again from 1, etc.
.END LIST .SK
The next step is to construct the keyword table.  Note which of
the keywords specified above are context-sensitive.  In this case
they all are, so we need a separate copy for each context in which
they may appear.  We then form the following tables:
.SK .TP 21 .LITERAL
    # KEYWORD  KNARGS KARGTP KLSTDX ALLOWED NAMES
    - -------  ------ ------ ------ -------------
    1 DENSITY     1      3      2   800, 1600, 6250
    2 DEVICE      1      3      3   7TRACK, 9TRACK
    3 FILE        1      1      5     -
    4 LABEL       1      3      4   ANSI, IBM, NONE
    5 SPARE       -      -      -     -
    6 SPARE       -      -      -     -
    7 DENSITY     1      3      5   800, 1600, 6250
    8 DEVICE      1      3      6   7TRACK, 9TRACK
    9 FILE        1      1      6     -
   10 LABEL       1      3      7   ANSI, IBM, NONE
   11 PASSWORD    1      4      3     -
   12 SPARE       -      -      -     -
   13 SPARE       -      -      -     -
   14 GRID        1      3      8   COARSE, FINE, NONE
   15 LABEL       1      4      4     -
   16 MAX        -4      2      1     -
   17 MIN        -4      2      2     -
   18 SPARE       -      -      -     -
   19 SPARE       -      -      -     -
.END LITERAL .SK
This table is constructed the same way as the command table.  The
list indexes are assigned beginning where they left off in the
command table, since there is no distinction between initial
lists and keyword argument lists when they are stored.  The list
indexes may be assigned arbitrarily, as long as they are not
used more than once for the same <type> of list.  Spaces may
be left for spares, but this is usually not necessary, since
the ordering is usually not important.
.SK
Once the keyword table is set up, the start and stop pointers
into it can be read off easily:
.SK .TP 11 .LITERAL
    COMMAND # KWSTRT KWSTOP
    --------- ------ ------
        1        1      0   (STOP < START means null table)
        2        1      0
        3        1      4
        4        7     11
        5       14     17
        6        1      0
        7        1      0
        8        1      0
        9        1      0
.END LITERAL .SK
The name table can be set up in any order that is convenient.  It
should be kept in mind that semantic analysis will be simplified
if related items are in consecutive order.  Example:
.SK .TP 18 .LITERAL
     # NAME        # NAME
     - ----        - ----
     1 800        17 AA
     2 1600       18 BA
     3 6250       19 AB
     4 SPARE      20 BB
     5 SPARE      21 A*
     6 7TRACK     22 B*
     7 9TRACK     23 *A
     8 SPARE      24 *B
     9 SPARE      25 **
    10 ANSI       26 SPARE
    11 IBM        27 SPARE
    12 NONE       28 SPARE
    13 COARSE     29 SPARE
    14 FINE       30 SPARE
    15 SPARE
    16 SPARE
.END LITERAL .SK
Again, we have left a few spares at the end of each group to
facilitate later enhancements.  Unless storage requirments are
a severe problem, this is always advisable.  If you eventually
run out of spares, it is a relatively simple task to rearrange
the tables, but the corresponding values of indexes tend to
get built into the semantic analyzer or the driven application,
so modification may be more difficult.
.SK
The final step is to set up the start and stop pointers into the
name table.  Note that name entries can be used for more than one
context.  It is the list indexes that keep the usages distinct.
.SK .TP 21 .LITERAL
   Keyword # NMSTRT NMSTOP
   --------- ------ ------
       1        1      3
       2        6      7
       3        1      0
       4       10     12
       5        -      -
       6        -      -
       7        1      3
       8        6      7
       9        1      0
      10       10     12
      11        1      0
      12        -      -
      13        -      -
      14       12     14
      15        1      0
      16        1      0
      17        1      0
      18        -      -
      19        -      -
.END LITERAL
.HL 2 Sizing
The minimum values required for the various sizing parameters
are:
.SK .TP 13 .LITERAL
   Parameter  Default  Needed
   ---------  -------  ------
   <cmtbsz>      50       9
   <kwtbsz>     100      19
   <nmtbsz>     200      30
   <namlen>       6       3 (since all identifiers are unique
                             in the first three characters)
   <toknsz>      72       ? (see below)
   <lstlen>      12       5
   <nilsts>      25       6
   <nnlsts>      25       8
   <nrlsts>      25       2
   <nslsts>      25       4
.END LITERAL .SK
The value needed for <toknsz> is the largest of what will be
needed for tape reel numbers (usually 6), the longest keyword
("password" needs 8), the length needed to hold a password
string (8 should be plenty for any system), and the number
of characters to be allowed for the plot label string.  The
last is, of course, going to be the largest.  At least 40
characters should be allowed, probably more.
.SK
What we see from this exercise is that we could save a
considerable amount of storage by changing these parameters
and recompiling SCANLIB for this application.  On the other
hand, the command language we have designed here is a relatively
small one relative to what is common in major applications, so
the standard values will probably be about right most of the
time, and changing them might not be worth the trouble for
applications which can be expected to grow, as the utility
program we have illustrated would be certain to do in real life.
.HL 2 CMTABL
The tables we have generated above can be encoded into the
CMTABL COMMON by means of a BLOCK DATA program and the
appropriate DATA statements.  Portions of that are
illustrated here:
.SK .LITERAL
      BLOCK DATA
INCLUDE (SCANLIB.PARAMS)
INCLUDE (SCANLIB.CMTABL)
C
      DATA (CMDTBL(I,1),I=1,NAMLEN) / 'E','N','D' /
      DATA (CMDTBL(I,2),I=1,NAMLEN) / 'F','I','L' /
      DATA (CMDTBL(I,3),I=1,NAMLEN) / 'I','N','T' /
      ...
      DATA (KWDTBL(I,1),I=1,NAMLEN) / 'D','E','N' /
      DATA (KWDTBL(I,2),I=1,NAMLEN) / 'D','E','V' /
      DATA (KWDTBL(I,3),I=1,NAMLEN) / 'F','I','L' /
      ...
      DATA (NAMTBL(I,1),I=1,NAMLEN) / '8','0','0' /
      DATA (NAMTBL(I,2),I=1,NAMLEN) / '1','6','0' /
      DATA (NAMTBL(I,3),I=1,NAMLEN) / '6','2','5' /
      ...
      DATA CARGTP(1) / 0 / , CNARGS(1) / 0 / , CLSTDX(1) / 0 /
      DATA CARGTP(2) / 1 / , CNARGS(2) / 1 / , CLSTDX(2) / 1 /
      ...
      DATA KARGTP(1) / 3 / , KNARGS(1) / 1 / , KLSTDX(1) / 2 /
      ...
      DATA KWSTRT(1) / 1 / , KWSTOP(1) / 0 /
      DATA KWSTRT(2) / 1 / , KWSTOP(2) / 0 /
      DATA KWSTRT(3) / 1 / , KWSTOP(3) / 4 /
      DATA KWSTRT(4) / 7 / , KWSTOP(4) /11 /
      ...
      DATA NMSTRT(1) / 1 / , NMSTOP(1) / 3 /
      DATA NMSTRT(2) / 6 / , NMSTOP(2) / 7 /
      ...
      END
.END LITERAL
.PAGE
.HL 2 Command Reader
Since we have no special I/O requirements, we can use CMREAD
for command input.  We will need input line continuation
because some of the commands may be long.  The command
processing routine can be something like:
.SK .LITERAL
      SUBROUTINE COMAND(  CODE)
C
C     OUTPUT - CODE = 0 IF OK, = 1 IF ERROR
C
INCLUDE (SCANLIB.PARAMS)
INCLUDE (SCANLIB.CMLIST)
INCLUDE (SCANLIB.CMTABL)
      INTEGER CODE
      INTEGER MXLINE, EOF
      CHARACTER*1 LINE(400), CHELP(3), CRESET(3), CEND(3)
      LOGICAL HELP, RESET, FINISH
      DATA MXLINE / 400 /
      DATA CHELP  / 'H' , 'E' , 'L' /
      DATA CRESET / 'R' , 'E' , 'S' /
      DATA CEND   / 'E' , 'N' , 'D' /
C
C     FIND INDEXES OF 'HELP', 'RESET', AND 'END' COMMANDS --
      NCHARS = 3
      ISTART = 1
      CALL CMLKUP (CHELP,NCHARS,CMDTBL,ISTART,CMTBSZ,   IHELP)
      CALL CMLKUP  (CEND,NCHARS,CMDTBL,ISTART,CMTBSZ,    IEND)
      CALL CMLKUP(CRESET,NCHARS,CMDTBL,ISTART,CMTBSZ,  IRESET)
C
      DO UNTIL (.NOT.RESET)
         CALL CMINIT
         DO (SET STARTING DEFAULTS)
         CODE = 0
         DO UNTIL (FINISH)
            CALL CMREAD(MXLINE,  LINE,EOF)
            IF (EOF.GT.0) THEN
               FINISH = .TRUE.
               IF (EOF.GT.1) CODE = 1
            ELSE
               CALL CMSCAN(LINE)
               HELP   = CMDFND(IHELP) .GT.0
               FINISH = CMDFND(IEND  ).GT.0
               RESET  = CMDFND(IRESET).GT.0
               IF (HELP) THEN
                  DO (HELP PROCESSING)
               ENDIF
            ENDIF
         END UNTIL
      END UNTIL
      IF (CODE.EQ.0) CALL SEMANT(  CODE)
      RETURN
.END LITERAL
.PAGE
.LITERAL
      PROCEDURE (SET STARTING DEFAULTS)
C
C     TO ILLUSTRATE HOW THE LISTS ARE SET UP, WE HARD-CODE
C     THESE HERE.  IT WOULD BE BETTER TO USE A TABLE LOOKUP
C     AS IS DONE ABOVE TO FIND THE END AND RESET INDEXES.
C
C     'IN-LINE' COMMENTS ARE USED HERE TO CONSERVE SPACE.
C     BECAUSE OF LACK OF PORTABILITY, THEY SHOULD NOT BE
C     USED IN ACTUAL APPLICATION CODE.
C
      ILIST(1,1) = 1    @ FILES = 1
      ILIST(1,3) = 0    @ START = 00:000:00:00:00
      ILIST(2,3) = 0
      ILIST(3,3) = 0
      ILIST(4,3) = 0
      ILIST(5,3) = 0
      ILIST(1,4) = 99   @ STOP  = 99:366:23:59:59
      ILIST(2,4) = 366
      ILIST(3,4) = 23
      ILIST(4,4) = 59
      ILIST(5,4) = 59
      ILIST(1,5) = 1    @ INTAPE /FILE = 1
      ILIST(1,6) = 1    @ OUTAPE /FILE = 1
      RLIST(1,1) = 0.0  @ PLOT /MIN = 0., 0., 0., 0.
      RLIST(2,1) = 0.0
      RLIST(3,1) = 0.0
      RLIST(4,1) = 0.0
      RLIST(1,2) = 1.E9 @ PLOT /MAX = 1.E9, 1.E9, 1.E9, 1.E9
      RLIST(2,2) = 1.E9
      RLIST(3,2) = 1.E9
      RLIST(4,2) = 1.E9
      NLIST(1,2) = 2    @ INTAPE /DENSITY = 1600
      NLIST(1,3) = 7    @ INTAPE /DEVICE  = 9TRACK
      NLIST(1,4) = 12   @ INTAPE /LABEL   = NONE
      NLIST(1,5) = 2    @ OUTAPE /DENSITY = 1600
      NLIST(1,6) = 7    @ OUTAPE /DEVICE  = 9TRACK
      NLIST(1,7) = 12   @ OUTAPE /LABEL   = NONE
      NLIST(1,8) = 12   @ PLOT /GRID = NONE
      END PROCEDURE
.END LITERAL
.PAGE
.LITERAL
      PROCEDURE (HELP PROCESSING)
C
C     THIS PROCEDURE ILLUSTRATES THE USE OF THE OPTIONAL
C     HELP SUBROUTINE.
C
C     PARAMETER (MAXCMD = highest entry used in CMDTBL)
C
C     LENGTH OF ARGUMENT SPECIFIED ON HELP COMMAND --
C
      LENHLP = STLENG(1,CLSTDX(IHELP))
C
      IF (LENHLP.LE.0) THEN
C        NAKED HELP --
         WRITE (ERUNIT,2000) (CMDTBL(I,J),I=1,NAMLEN),J=1,MAXCMD)
 2000    FORMAT(' COMMANDS AVAILABLE:'/12(1X,9(1X,6A1,:,','),:/))
C        ............. Put value of NAMLEN here --^
         WRITE (ERUNIT,2002)
 2002    FORMAT(' For help on a command, enter HELP cmd-name')
      ELSE
C        HELP <cmdname> --
         CALL CMHELP(SLIST(1,1,CLSTDX(IHELP)),LENHLP)
      ENDIF
      END PROCEDURE
C
      END PROGRAM
.END LITERAL
.PAGE
.HL 2 Semantic Analyzer
We assume that the application has defined the following
interfaces for its internal control:
.SK .LITERAL
   INREEL    CHAR*6   INPUT  TAPE REEL NUMBER
   OUREEL    CHAR*6   OUTPUT TAPE REEL NUMBER
   INFILE    INTEGER  INPUT  TAPE FILE NUMBER
   OUFILE    INTEGER  OUTPUT TAPE FILE NUMBER
   INDEV     INTEGER  INPUT  TAPE DEVICE: 0 = 7TRACK, 1 = 9TRACK
   OUDEV     INTEGER  OUTPUT TAPE DEVICE: 0 = 7TRACK, 1 = 9TRACK
   INLABL    INTEGER  INPUT  TAPE LABEL SPEC: 0=ANSI,1=IBM,2=NONE
   OULABL    INTEGER  OUTPUT TAPE LABEL SPEC: 0=ANSI,1=IBM,2=NONE
   INDENS    INTEGER  INPUT  TAPE DENSITY: 1=800,2=1600,3=6250
   OUDENS    INTEGER  OUTPUT TAPE DENSITY: 1=800,2=1600,3=6250
   PASSWD    CHAR*8   OUTPUT TAPE PASSWORD
   PRENAB(2,2) LOGICAL PRINT ENABLE FLAGS: AA=1,1, AB=1,2, ...
   STARTM    INTEGER  START TIME (SECONDS FROM 50:001:00:00:00)
   STOPTM    INTEGER  STOP  TIME (SECONDS FROM 50:001:00:00:00)
   PLCHNL(4) INTEGER  CHANNEL NUMBERS TO BE PLOTTED
   PLMAX(4)  REAL     UPPER LIMIT VALUES FOR PLOTTING
   PLMIN(4)  REAL     LOWER LIMIT VALUES FOR PLOTTING
   PLABEL    CHAR*40  PLOT LABEL
   PLGRID    INTEGER  GRID OPTION: 0=NONE,1=COARSE,2=FINE
   NFILES    INTEGER  NUMBER OF FILES TO PROCESS
   PLTENB    LOGICAL  PLOT ENABLE (TRUE IF PLOT COMMAND SEEN)
   OUTENB    LOGICAL  OUTPUT ENABLE (TRUE IF OUTAPE COMMAND SEEN)
.END LITERAL .SK
We assume that these variables are declared in a COMMON block,
the text of which is in APPLIC.COMMON, which can be accessed by
the SFTRAN "INCLUDE".  The semantic analyzer routine could be:
.PAGE
.LITERAL
      SUBROUTINE SEMANT(  CODE)
C
C     PERFORM POST-PROCESSING OF INPUT COMMANDS TO SET UP
C     APPLICATION CONTROL COMMON.
C
C     OUTPUT - CODE (INTEGER) = 0 IF OK, = 1 IF ERROR
C
C     CALLS TIMCON, WHICH IS AN IMPLEMENTOR-WRITTEN
C     ROUTINE WHICH CONVERTS AN INPUT ARRAY OF 5
C     INTEGERS INTERPRETED AS YY, DDD, HH, MM, SS TO
C     SECONDS PAST 50:001:00:00:00, INCLUDING ERROR CHECKING.
C
INCLUDE (SCANLIB.PARAMS)
INCLUDE (SCANLIB.CMLIST)
INCLUDE (APPLIC.COMMON)
C
      INTEGER CODE
      LOGICAL PRDEF
C
      CALL CHRCAT(STLENG(1,1),SLIST(1,1,1),6,  INREEL)
      CALL CHRCAT(STLENG(1,2),SLIST(1,1,2),6,  OUREEL)
C     (REEL NUMBER VALIDITY CHECKING MAY BE DONE HERE)
      INFILE = ILIST(1,5)
      OUFILE = ILIST(1,6)
      INDEV  = NLIST(1,3) - 6
      OUDEV  = NLIST(1,6) - 6
C     (DEVICE CODE VALIDITY CHECKING MAY BE DONE HERE)
      INLABL = NLIST(1,4) - 10
      OULABL = NLIST(1,7) - 10
C     (LABEL CODE VALIDITY CHECKING EXAMPLE:)
      IF (INLABL.LT.0 .OR. INLABL.GT.2) THEN
         WRITE(6,1000)
 1000    FORMAT(' INVALID INPUT TAPE LABEL CODE.',
     *   '  ASSUMING UNLABELLED TAPE.')
         INLABL = 0
      ENDIF
C     (DITTO FOR OULABL)
      INDENS = NLIST(1,2)
      OUDENS = NLIST(1,5)
C     (CHECK)
      CALL CHRCAT(STLENG(1,3),SLIST(1,1,3),8,  PASSWD)
      CALL TIMCON(ILIST(1,3),  STARTM)
      CALL TIMCON(ILIST(1,4),  STOPTM)
      DO FOR I=1,4
         PLCHNL(I) = ILIST(I,2)
         PLMAX(I)  = RLIST(I,1)
         PLMIN(I)  = RLIST(I,2)
      END FOR
.END LITERAL
.PAGE
.LITERAL
      CALL CHRCAT(STLENG(1,4),SLIST(1,1,4),40,  PLABEL)
      PLGRID = ILIST(1,8) - 12
      NFILES = ILIST(1,1)
      IF (NFILES.LE.0) THEN
         WRITE(6,2000)
 2000    FORMAT(' INVALID NUMBER OF FILES.  DEFAULTING TO 1.')
         NFILES = 1
      ENDIF
      PLTENB = CMDFND(5).GT.0
      OUTENB = CMDFND(4).GT.0
C
C     AT THIS POINT EVERYTHING IS FINISHED EXCEPT FOR SETTING UP
C     THE PRENAB ARRAY.  THIS IS A CASE OF FANCIER DEFAULTING.
C
      IF (CMDFND(6).EQ.0) THEN
         PRDEF = .NOT. (PLTENB .OR. OUTENB)
         DO FOR J=1,2
            DO FOR I=1,2
               PRENAB(I,J) = PRDEF
            END FOR
         END FOR
      ELSE
         DO FOR ITEM=1,4
            NAMPRT = ILIST(ITEM,1) - 16
            DO (SET UP PRINT CODE)
         END FOR
      ENDIF
      RETURN
.END LITERAL
.PAGE
.LITERAL
      PROCEDURE (SET UP PRINT CODE)
         DO CASE (NAMPRT,9)
         CASE 1  @ AA
            PRENAB(1,1) = .TRUE.
         CASE 2  @ BA
            PRENAB(2,1) = .TRUE.
         CASE 3  @ AB
            PRENAB(1,2) = .TRUE.
         CASE 4  @ BB
            PRENAB(2,2) = .TRUE.
         CASE 5  @ A*
            DO FOR J=1,2
               PRENAB(1,J) = .TRUE.
            END FOR
         CASE 6  @ B*
            DO FOR J=1,2
               PRENAB(2,J) = .TRUE.
            END FOR
         CASE 7  @ *A
            DO FOR I=1,2
               PRENAB(I,1) = .TRUE.
            END FOR
         CASE 8  @ *B
            DO FOR I=1,2
               PRENAB(I,2) = .TRUE.
            END FOR
         CASE 9  @ **
            DO FOR J=1,2
               DO FOR I=1,2
                  PRENAB(I,J) = .TRUE.
               END FOR
            END FOR
         CASE OTHER
            WRITE(6,3000)
 3000       FORMAT(' INVALID RECORD TYPE CODE IN PRINT COMMAND.')
         END CASE
      END PROCEDURE
.END LITERAL
.PAGE
The previous approach does not extend very nicely to a larger
than 2x2 situation.  In realistic situations, a table-driven
approach is probably easiest to generalize.  Such an approach
might define tables FROM(), TO(), and BY(), indexed by NAMPRT,
which would permit all the cases to be handled by:
.SK .LITERAL
      PROCEDURE (SET UP PRINT CODE)
C
C     AT TOP OF SEMANT, PUT:
C
C     LOGICAL PREN2(4)
C     EQUIVALENCE (PREN2(1),PRENAB(1,1))
C
      IF (NAMPRT.GT.0 .AND. NAMPRT.LE.9) THEN
         DO FOR I = FROM(NAMPRT),TO(NAMPRT),BY(NAMPRT)
            PREN2(I) = .TRUE.
         END FOR
      ELSE
         WRITE(6,3000)
 3000    FORMAT(' INVALID RECORD TYPE CODE IN PRINT COMMAND.')
      ENDIF
      END PROCEDURE
C
      END
.END LITERAL .SK
The tables would contain, for this example:
.SK .LITERAL
   NAMPRT FROM  TO  BY
   ------ ----  --  --
      1     1    1   1
      2     2    2   1
      3     3    3   1
      4     4    4   1
      5     1    3   2
      6     2    4   2
      7     1    2   1
      8     3    4   1
      9     1    4   1
.END LITERAL
