.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 is an argument separator, used to separate is a command terminator, used to indicate the end of a command, and to separate multiple commands on a line is a character used to delimit a string is a string of characters which is ignored by the syntax analyzer is a continuation character, used to indicate that the next line should be appended to the current line before processing is the end of a line is a containing decimal digits, with optional leading sign, which is interpreted as an integer is a character used to separate a command or argument keyword from the which follows it is an argument keyword followed by a list of values is a sequence of zero of more all of the same , separated by is a character used to separate items in a is a recognized as an entry in some table is a character which serves only to terminate a token, and otherwise is ignored by the syntax analyzer is a character defined as punctuation by its appearance in the table of punctuation characters is the character used as a string delimiter for those strings requiring delimiters is a which contains no blanks, and otherwise follows (approximately) the fortran rules for real constants is a which follows the fortran 77 rules for character constants, except that a containing no or characters need not be surrounded by is a character string not containing any characters (except within a quoted ), and surrounded by characters is one of , , , or [ ] 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 ::= ' ' (blank) ::= '@' (commercial at) ::= ' (apostrophe) ::= ',' or ':' (comma, colon) ::= '=' or '(' (equals, left parenthesis) ::= '/' or ')' (slant, right parenthesis) ::= ';' (semicolon) ::= '&' (ampersand) .END LITERAL .HL 2 Tokens Tokens are, for the purpose of SCANLIB, all significant syntactical items other than punctuation. In this sense, and are considered non-significant. Tokens may be , , , or . .HL 3 Integers A token is an if it occurs in a context where an is expected, and contains only decimal digits, and optionally a leading sign ('+' or '-'). Blanks (i.e. ) are not permitted within an . An 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 number if it occurs in a context where a is expected, and follows (approximately) the Fortran rules for real constants. A may not contain any blanks (i.e. ). The general form is: .SK .I3 ::= [][...][.[...]][E[]...] .SK where is a sign ('+' or '-') and is a digit. The following are examples of valid : .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 if it occurs in a context where a is expected, and is found in an appropriate section of a table of . follows the same formation rules as . .HL 4 Lookup Rules The rules for considering a to be "found" (as a name) are: .SK .LIST 0,"-" .LE;only the first characters are significant, the rest are ignored. .LE;if the is shorter than , only the number of characters actually present are compared to the table. .END LIST .SK Thus, if a table contains an entry 'ABCDEF', and 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 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 if it occurs in a context where a is expected. A is a sequence of any non- characters, or a sequence of any characters enclosed in and containing an even number of . 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 has a length attribute , which may be zero to an implementor-defined maximum (). .HL 3 Use of Quotes Any which contains or or or must be enclosed in . Any other may be, but need never be, enclosed in . The presence or absence of enclosing never affects the of a . .HL 2 Lists A is a sequence of zero or more , all of the same , separated by , and followed by or . SCANLIB uses two types of : 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 ::= 1,2,,4 ::= 1,2,0,4 ::= 1,2,4 .END LITERAL .SK is equivalent to if it is a positional list, but is equivalent to 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 (which may be disallowed by the implementor on a command-by-command basis), followed by zero or more , each of which is an followed by a . The various items are separated by appropriate punctuation, as follows: .SK .LITERAL ::= [[][[]]...] or ::= [[][[]]...] where is a command name is an initial list is a of the form [[]] is an argument keyword name which is recognized for use within a command headed by is normally inserted by the command reader at , and need not be specified explicitly by the user unless another 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 . .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 character (=) is followed by (/) 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 is allowed, the following is equivalent: .SK .I3 [[][]]...[[][]]... .SK That is, any punctuation may be preceded or followed by any combination of characters (nominally spaces) and strings. A is any sequence of characters enclosed in characters (nominally '@'), or beginning with and continuing to . .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 character (nominally '_&') at any position where or or is allowed, and then continuing the command on the next input line. The 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 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 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 are attached to the PLOT command. Similar 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 . 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 ---- ------- ----- 50 size of command table 100 size of keyword argument table 200 size of name table 10 size of table of punctuation characters 6 maximum number of characters in a command, keyword, or name 72 maximum number of characters in any token, including quoted strings 12 maximum number of entries allowed in any list 25 total number of integer lists 25 total number of name lists 25 total number of real lists 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 = *(++) + + 1 characters = 5*(+) + + 3 words .END LITERAL .SK .TP 5 .LITERAL = ** characters = + + *(+++) words .END LITERAL .SK using the default values for these parameters, the sizes are thus: .SK .TP 4 .LITERAL = 2,111 characters = 763 words = 21,600 characters = 1350 words .END LITERAL .SK Note that this is dominated by the product .SK .I3 * * .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 may impact the entry of and as well, since they are also limited by 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 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 command names KWDTBL keyword argument names NAMTBL names allowed in name lists PUNTBL punctuation characters CARGTP list type codes for initial lists CNARGS max number of entries in initial lists CLSTDX index of list where results are to be stored from initial list KWSTRT index into KWDTBL of first keyword allowed for each command KWSTOP index into KWDTBL of last keyword allowed for each command KARGTP list type codes for keyword argument lists KNARGS maximum number of entries in kwd arg lists KLSTDX index of list where results are to be stored from keyword argument list NMSTRT index into NAMTBL of first name allowed for each keyword NMSTOP index into NAMTBL of last name allowed for each keyword PNCODE 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 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* string arrays. .SK List type codes to be entered in CARGTP and KARGTP are: .SK .TP 4 .LITERAL 1 for non-positional, -1 for positional 2 for non-positional, -2 for positional 3 for non-positional, -3 for positional 4 for 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 , 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 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 character which is not part of a or quoted string. It then appends the portion of the card image to the left of the (or to the left of column 73 if no 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 character was found. If a 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 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 character is found outside a or quoted string, or .LE;column 72 has been scanned without finding . .END LIST .LE;If was found, indicate that the line is not complete, and set the copy limit to one position before , 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 ). 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 ) - 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 . The strength of punctuation is: .SK .I3 < < < < < .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 . 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 . 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 . 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 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 , , or , 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 . This will usually cause the remaining text to be treated as an invalid keyword argument field, up to the next , 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 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 or , 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 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) = 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 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 , RETURN WITH SCNTRM POINTING AT FIRST NON-NOISE CHAR AFTER CLOSING OR AT . 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 and . If CMSKIP is called with the scan pointer pointing to a character which is not or , it simply returns the output scan pointer equal to the input scan pointer. .SK CMSKIP first skips zero or more characters, using a WHILE loop. Then, while it sees an opening , it scans to the next (closing) , then skips zero or more characters. All this is repeated while the scan is less than EOL. This algorithm results in skipping .SK .LITERAL zero or more , followed by zero or more ( followed by zero or more ) .END LITERAL .SK which is equivalent to any combination of or . .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 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 or 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 = / FILE= / DEVICE= / LABEL= & / DENSITY= OUTAPE = / FILE= / DEVICE= / LABEL= & / DENSITY= / PASSWORD= PRINT = > START = STOP = PLOT = > / MIN = > & MAX = > / LABEL= / GRID= FILES = 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 = , 2 = , 3 = , 4 = .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 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 --------- ------- ------ 50 9 100 19 200 30 6 3 (since all identifiers are unique in the first three characters) 72 ? (see below) 12 5 25 6 25 8 25 2 25 4 .END LITERAL .SK The value needed for 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 -- 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