Google
 

Trailing-Edge - PDP-10 Archives - decuslib20-01 - decus/20-0002/sail.jfr
Click decus/20-0002/sail.jfr to see without markup as text/plain
There is 1 other file named sail.jfr in the archive. Click here to see a list.
COMMENT    VALID 00294 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00022 00002	Stanford Artificial Intelligence Laboratory		August 1976
C00025 00003	PREFACE HISTORY OF THE LANGUAGE LEARNING ABOUT SAIL CHANGES IN THE LANGUAGE OPERATING SYSTEMS UNIMPLEMENTED CONSTRUCTS ACKNOWLEDGEMENTS
C00032 00004	TABLE OF CONTENTS
C00051 00005	PROGRAMS AND BLOCKS Syntax
C00053 00006	 Semantics DECLARATIONS STATEMENTS BLOCK NAMES
C00057 00007	 EXAMPLES
C00058 00008	ALGOL DECLARATIONS Syntax
C00060 00009
C00062 00010
C00064 00011	 Restrictions
C00066 00012
C00067 00013	 Examples
C00070 00014	 Semantics SCOPE OF DECLARATIONS TYPE QUALIFIERS
C00073 00015	 NUMERIC DECLARATIONS STRING DECLARATIONS ARRAY DECLARATIONS
C00078 00016	 PRELOAD SPECIFICATIONS
C00084 00017	 PROCEDURE DECLARATIONS FORMAL PARAMETERS
C00088 00018	 DEFAULT PARAMETER VALUES FORWARD PROCEDURE DECLARATIONS
C00093 00019	 RECURSIVE PROCEDURES SIMPLE PROCEDURES
C00097 00020	 EXTERNAL PROCEDURES
C00102 00021	 PARAMETRIC PROCEDURES DEFAULTS IN PROCEDURE DECLARATIONS RESTRICTIONS ON PROCEDURE DECLARATIONS ALLOCATION AND DEALLOCATION
C00106 00022	 INITIALIZATION AND REINITIALIZATION SYNONYMS
C00110 00023	 CLEANUP DECLARATIONS REQUIREMENTS
C00114 00024
C00119 00025	 Separately Compiled Procedures
C00124 00026	 FORTRAN PROCEDURES
C00129 00027	 ASSEMBLY LANGUAGE PROCEDURES
C00132 00028	ALGOL STATEMENTS Syntax
C00134 00029
C00136 00030	 Semantics
C00138 00031	 ASSIGNMENT STATEMENTS SWAP ASSIGNMENT CONDITIONAL STATEMENTS
C00142 00032	 AMBIGUITY IN CONDITIONAL STATEMENTS GO TO STATEMENTS
C00145 00033	
C00149 00034	FOR STATEMENTS
C00152 00035	 WHILE STATEMENT DO STATEMENT
C00155 00036	 CASE STATEMENTS RETURN STATEMENT
C00159 00037	 DONE STATEMENT
C00163 00038	 NEXT STATEMENT CONTINUE STATEMENT
C00167 00039	 PROCEDURE STATEMENTS
C00171 00040	 PROCEDURES AS ACTUAL PARAMETERS FORTRAN PROCEDURES
C00176 00041	 NOWSAFE and NOWUNSAFE
C00178 00042	ALGOL EXPRESSIONS Syntax
C00180 00043
C00182 00044
C00184 00045	 Type Conversion
C00186 00046
C00190 00047
C00194 00048	 Semantics CONDITIONAL EXPRESSIONS
C00197 00049	 ASSIGNMENT EXPESSIONS CASE EXPRESSIONS SIMPLE EXPRESSIONS
C00201 00050	 PRECEDENCE OF ALGEBRAIC OPERATORS EXPRESSION EVALUATION RULES  OR
C00205 00051	  AND  NOT <>= RELATIONS
C00210 00052	 MAX MIN +- ADDITION SUBTRACTION LAND LOR XOR EQV LNOT */% MULTIPLICATION DIVISION DIV MOD ASH LSH ROT
C00214 00053	 & CONCATENATION ^ EXPONENTIATION SUBSTRINGS
C00219 00054	  INFINITY SPECIAL LENGTH OPERATOR FUNCTION DESIGNATORS UNARY OPERATORS MEMORY AND LOCATION
C00223 00055	LOCATION
C00225 00056	ASSEMBLY LANGUAGE STATEMENTS Syntax
C00227 00057	 Semantics DECLARATIONS IN CODE BLOCKS
C00230 00058	 PROTECT ACS DECLARATION OPCODES THE <simple addr> FIELD
C00235 00059	 STARTCODE VERSUS QUICKCODE
C00240 00060	 ACCUMULATOR USAGE IN CODE BLOCKS CALLING PROCEDURES FROM INSIDE CODE BLOCKS
C00244 00061	 BEWARE
C00246 00062	INPUT/OUTPUT ROUTINES Execution-time Routines in General SCOPE NOTATIONAL CONVENTIONS
C00250 00063	 SKIP I/O  Channels and Files OPEN
C00254 00064
C00259 00065
C00263 00066	 CLOSE, CLOSIN, CLOSO GETCHAN RELEASE
C00267 00067	 LOOKUP, ENTER RENAME
C00271 00068	 ERENAME Break Characters BREAKSET
C00275 00069
C00279 00070
C00283 00071	 SETBREAK GETBREAK, RELBREAK
C00286 00072	 STDBRK I/O Routines INPUT
C00290 00073	 SCAN
C00293 00074	 SCANC OUT LINOUT
C00297 00075	 SETPL WORDIN WARNING ABOUT DUMP MODE IO ARRYIN
C00301 00076	 WORDOUT ARRYOUT INOUT GETSTS, SETSTS
C00304 00077	 MTAPE USETI, USETO REALIN, INTIN
C00307 00078
C00310 00079	 REALSCAN, INTSCAN TMPIN, TMPOUT
C00314 00080	 AUXCLR, AUXCLV CHNIOR, CHNIOV TTY and PTY Routines TELETYPE I/O ROUTINES BACKUP CLRBUF INCHRS INCHRW INCHSL INCHWL
C00317 00081	 INSTR INSTRL INSTRS IONEOU OUTCHR OUTSTR TTYIN TTYINL TTYINS TTYUP
C00321 00082	 PSEUDO-TELETYPE FUNCTIONS LODED PTYALL PTCHRS PTCHRW PTOCHS PTOCHW PTOCNT PTIFRE PTOSTR PTYGET PTYGTL PTYIN
C00324 00083	 PTYREL PTYSTL PTYSTR Example of TOPS-10 I/O
C00328 00084
C00331 00085	EXECUTION TIME ROUTINES Type Conversion Routines SETFORMAT GETFORMAT
C00334 00086	 CVS CVD CVOS CVO CVE, CVF, CVG
C00337 00087	 CVASC, CVASTR, CVSTR
C00341 00088	 CV6STR, CVSIX, CVXSTR String Manipulation Routines EQU LENGTH
C00344 00089	 LOP SUBSR, SUBST Liberation-from-Sail Routines CODE
C00347 00090	 CALL CALLI USERCON GOGTAB
C00351 00091	 USERERR ERMSBF EDFILE
C00355 00092	 INIACS Byte Manipulation Routines LDB, DPB, etc. POINT
C00358 00093	Other Useful Routines CVFIL FILEINFO ARRINFO
C00361 00094	 ARRBLT ARRTRAN ARRCLR INCONTEXT
C00364 00095	 CHNCDB Numerical Routines SIN COS SIND COSD ASIN ACOS ATAN ATAN2 SINH COSH TANH SQRT
C00368 00096	 RAN LOG EXP TRIGINI OVERFLOW ENTRY POINTS OVERFLOW IMPLEMENTATION
C00373 00097
C00375 00098	PRINT Syntax Semantics DEFAULT FORMATS
C00378 00099	 DESTINATIONS SETPRINT, GETPRINT
C00382 00100	 SIMPLE USE CAVEATS
C00386 00101	 FOR WIZARDS ONLY
C00389 00102
C00390 00103	MACROS AND CONDITIONAL COMPILATION Syntax
C00392 00104
C00394 00105	 Delimiters
C00398 00106	 Macros
C00402 00107	 SCOPE MACRO BODIES DELIMITED STRINGS
C00406 00108	 INTEGER COMPILE TIME EXPRESSIONS STRING COMPILE TIME EXPRESSIONS
C00410 00109	 HYBRID MACRO BODIES
C00413 00110	Macros with Parameters
C00417 00111	 Conditional Compilation
C00421 00112	 Type Determination at Compile Time DECLARATION CHECKTYPE EXPRTYPE
C00425 00113	 BUILTIN LEAPARRAY RESERVED DEFINE CONOK
C00429 00114	Miscelaneous Features COMPILE TIME I/O EVALDEFINE, EVALREDEFINE ASSIGNC NOMAC COMPILERBANNER
C00434 00115	Hints
C00437 00116
C00440 00117	RECORD STRUCTURES Introduction Declaration Syntax Declaration Semantics
C00444 00118
C00448 00119	 Allocation Fields
C00451 00120	 Garbage Collection Internal Representations
C00455 00121	 $CLASS RECRNG HNDLER RECSIZ TYPARR TXTARR
C00457 00122	 Handler Procedures
C00460 00123
C00463 00124	 More about Garbage Collection
C00466 00125
C00469 00126	TENEX ROUTINES Introduction TOPS-10 Style Input/Output
C00472 00127
C00475 00128	 MAGTAPE I/O TENEX Style Input/Output OBTAINING ACCESS
C00479 00129	 DATA TRANSFER RANDOM I/O ERROR HANDLING DIRECT DSK OPERATIONS ASND, RELD
C00483 00130	 BKJFN CFILE CHARIN CHAROUT CHFDB CLOSF
C00486 00131	 CVJFN DELF DELNF DEVST, STDEV
C00489 00132	 DEVTYPE DSKIN, DSKOUT DVCHR ERSTR GDSTS, SDSTS
C00492 00133	 GNJFN GTFDB GTJFN
C00495 00134	 GTJFNL GTSTS, STSTS INDEXFILE
C00498 00135	 JFNS JFNSL MTOPR
C00501 00136	 OPENF OPENFILE
C00504 00137	 RCHPTR, SCHPTR
C00507 00138	 RFBSZ RFPTR, SFPTR RLJFN RNAMF
C00510 00139	 RWDPTR, SWDPTR SETCHAN SETINPUT SINI
C00514 00140	 SIZEF UNDELETE Terminal Handling THE TERMINAL AS A DEVICE
C00518 00141
C00523 00142
C00526 00143	 SETEDIT TERMINAL MODE FUNCTIONS GTTYP, STTYP
C00529 00144	 RFCOC, SFCOC RFMOD, SFMOD STPAR STI DATA TRANSFER
C00532 00145	 INTTY PBTIN SUPPRESSING OUTPUT
C00536 00146
C00539 00147	
C00541 00148	 Utility TENEX System Calls CALL CNDIR DIRST, STDIR
C00544 00149	 GJINF GTAD IDTIM, ODTIM PMAP
C00547 00150	 RDSEG RUNPRG RUNTIM
C00550 00151	LEAP DATA TYPES Introduction
C00554 00152	Syntax
C00556 00153	
C00558 00154	 Semantics ITEM GENESIS SCOPE OF ITEMS DATUMS OF ITEMS
C00563 00155	 ITEMVARS
C00568 00156	 SETS AND LISTS
C00573 00157	 ASSOCIATIONS PROCEDURES IMPLEMENTATION
C00578 00158
C00581 00159	LEAP STATEMENTS Syntax
C00583 00160
C00585 00161	 Restrictions Semantics ASSIGNMENT STATEMENTS
C00589 00162	 PUT REMOVE DELETE
C00594 00163	 MAKE BRACKETED TRIPLE ITEMS
C00598 00164	 ERASE Searching the Associative Store
C00602 00165	 BINDING BOOLEANS
C00607 00166	 DERIVED SETS BRACKETED TRIPLE ITEM RETRIEVAL THE FOREACH STATEMENT
C00611 00167
C00615 00168	 THE LIST MEMBERSHIP <ELEMENT>
C00620 00169	 THE BOOLEAN EXPRESSION <ELEMENT>
C00624 00170	 THE RETRIEVAL TRIPLE <ELEMENT>
C00629 00171
C00633 00172	 THE MATCHING PROCEDURE <ELEMENT>
C00639 00173
C00643 00174
C00644 00175	LEAP EXPRESSIONS Syntax
C00646 00176
C00648 00177	 Semantics ITEM EXPRESSIONS
C00650 00178	 NEW
C00655 00179	 ANY BINDIT TYPES AGAIN SET AND LIST EXPRESSIONS
C00659 00180	 DERIVED SETS BOOLEANS
C00663 00181	 PNAMES PROPS
C00667 00182	BACKTRACKING Introduction Syntax
C00669 00183	 Semantics THE CONTEXT DATA TYPE
C00672 00184	 REMEMBER RESTORE
C00676 00185	 FORGET INCONTEXT CONTEXT ELEMENTS
C00680 00186	PROCESSES Introduction Syntax Semantics STATUS OF A PROCESS
C00683 00187	 SPROUTING A PROCESS
C00687 00188	 SPROUTDEFAULTS MEMORY ACCESSIBLE TO A PROCESS
C00691 00189
C00695 00190	 SPROUT APPLY SPROUTING MATCHING PROCEDURES SCHEDULING
C00701 00191	 POLLING POINTS 
C00706 00192	Process Runtimes TERMINATE SUSPEND RESUME
C00710 00193	 CALLER
C00714 00194	 DDFINT JOIN MYPROC PRISET
C00717 00195	 PSTATUS URSCHD
C00721 00196	EVENTS Syntax Introduction
C00724 00197	 Sail-defined Cause and Interrogate THE CAUSE STATEMENT
C00728 00198	 THE INTERROGATE CONSTRUCT - SIMPLE FORM THE INTERROGATE CONSTRUCT - SET FORM
C00733 00199	 User-defined Cause and Interrogate EVENT TYPE DATA STRUCTURE
C00737 00200	 USER CAUSE PROCEDURES
C00741 00201	 USER INTERROGATE PROCEDURES
C00745 00202
C00746 00203	PROCEDURE VARIABLES Syntax Semantics ASSIGN
C00749 00204	 REFITEM ARGLIST
C00753 00205	 APPLY
C00758 00206
C00761 00207	INTERRUPTS Introduction 
C00766 00208	Interrupt Routines ATI, DTI DFR1IN DFRINT
C00769 00209	 DISABLE, ENABLE INTMAP
C00773 00210	 INTSET INTTBL
C00776 00211	 PSIMAP Immediate Interrupts IN SUAI Sail
C00779 00212	 IN TOPS-10
C00782 00213	 IN TENEX Sail GTRPW RTIW, STIW 
C00785 00214	Clock Interrupts PSIDISMS
C00789 00215	 PSIRUNTM KPSITIME Deferred Interrupts
C00793 00216
C00796 00217	 THE DEFERRED INTERRUPT PROCESS - INTPRO
C00798 00218	LEAP RUNTIMES Types and Type Conversion TYPEIT
C00801 00219	 CVSET CVLIST CVN and CVI MKEVTT 
C00804 00220	Make and Erase Breakpoints BRKERS, BRKMAK, BRKOFF Pname Runtimes CVIS
C00807 00221	 CVSI DELPNAME NEWPNAME Other Useful Runtimes LISTX
C00810 00222	 FIRST, SECOND, THIRD ISTRIPLE LOP COP
C00813 00223	 LENGTH SAMEIV Runtimes for User Cause and Interrogate Procedures SETCP AND SETIP
C00817 00224	 CAUSE1 ASKNTC
C00821 00225	 ANSWER DFCPKT
C00824 00226	BASIC CONSTRUCTS Syntax 
C00827 00227	Semantics VARIABLES DATUMS PROPS
C00832 00228	 RECORD FIELDS IDENTIFIERS
C00837 00229	 ARITHMETIC CONSTANTS STRING CONSTANTS COMMENTS
C00841 00230
C00842 00231	USING SAIL For TOPS-10 Beginners
C00846 00232	 For TENEX Beginners The Complete use of Sail
C00850 00233	 Compiling Sail Programs COMMAND LINE SYNTAX TOPS-10 COMMAND LINE SYNTAX
C00853 00234
C00855 00235	 TENEX SAIL COMMAND LINE SYNTAX
C00857 00236	 COMMAND LINE SEMANTICS
C00862 00237	 SWITCHES
C00866 00238
C00871 00239
C00876 00240
C00880 00241	 Loading Sail Programs Starting Sail Programs STARTING THE PROGRAM IN RPG MODE
C00885 00242	Storage Reallocation with REEnter
C00887 00243	DEBUGGING SAIL PROGRAMS Error Messages
C00891 00244	 ERROR MODES
C00896 00245	 STOPPING RUNAWAY COMPILATIONS EXECUTION TIME ERROR MESSAGES USER ERROR PROCEDURES
C00900 00246	 Debugging
C00905 00247	 SYMBOLS BLOCKS SAIL GENERATED SYMBOLS
C00909 00248	 WARNINGS
C00913 00249	BAIL 
C00919 00250	 COMPILE-TIME ACTION
C00924 00251
C00929 00252	 RUN-TIME ACTION DEBUGGING REQUESTS
C00934 00253	 ARGS BREAK COORD
C00937 00254	 DDT DEFINE HELP SETLEX
C00940 00255	 SHOW TEXT TRACE TRAPS UNBREAK
C00943 00256	 UNTRACE !!GO !!GSTEP !!STEP GOGTAB
C00946 00257	 STRING TYPEOUT BLOCK STRUCTURE BAIL and DDT
C00950 00258	 WARNINGS !!GOTO  !!UP (LEVEL) !!QUERY SETSCOPE
C00955 00259	 RESOURCES USED EXAMPLE
C00957 00260
C00959 00261
C00961 00262	 CURRENT STATUS
C00963 00263
C00967 00264	APPENDICES Characters Stanford (SUAI) Character Set
C00970 00265
C00974 00266
C00975 00267	 Sail Reserved Words
C00978 00268
C00981 00269
C00982 00270	 Sail Predeclared Identifiers
C00985 00271	 SUAI ONLY TOPS-10 ONLY CMU ONLY TYMSHARE ONLY TENEX ONLY
C00987 00272
C00988 00273	 Indices for Interrupts
C00992 00274
C00994 00275
C00996 00276	 Bit Names for Process Constructs SPROUT OPTIONS RESUME OPTIONS
C01000 00277	 CAUSE OPTIONS INTERROGATE OPTIONS
C01003 00278	 Statement Counter System GENERAL DISCUSSION HOW TO GET COUNTERS PROFILE PROGRAM
C01008 00279	 SAMPLE RUN
C01012 00280
C01013 00281	 Array Implementation
C01016 00282
C01018 00283	 String Implementation STRING DESCRIPTORS INEXHAUSTIBLE STRING SPACE
C01022 00284
C01024 00285	 Save/Continue
C01027 00286
C01029 00287	 Procedure Implementation STACK FRAME
C01033 00288	 ACCESSING THINGS ON THE STACK
C01037 00289	 ACTIONS IN THE PROLOGUE FOR NON-SIMPLE PROCEDURES
C01040 00290	 ACTIONS AT THE EPILOGUE FOR NON-SIMPLE PROCEDURES
C01043 00291	 PROCEDURE DESCRIPTORS
C01046 00292	
C01048 00293	REFERENCES
C01052 00294	INDEX
C01092 ENDMK
C;

Stanford Artificial Intelligence Laboratory		August 1976
Memo AIM-289						Sailon 57.4

Computer Science Department
Report No. STAN-CS-76-574


				   SAIL

				edited by

			      John F. Reiser



ABSTRACT

Sail is  a high-level  programming language  for the  PDP-10 computer.  It
includes  an  extended  ALGOL   60  compiler  and   a  companion  set   of
execution-time routines.  In addition to ALGOL, the language features: (1)
flexible linking to hand-coded  machine language algorithms, (2)  complete
access to the PDP-10 I/O facilities, (3) a complete system of compile-time
arithmetic and logic as well as a flexible macro system, (4) a  high-level
debugger,  (5)  records  and  references,  (6)  sets  and  lists,  (7)  an
associative  data  structure,  (8)  independent  processes  (9)  procedure
varaiables, (10) user  modifiable error handling,  (11) backtracking,  and
(12) interrupt facilities.

This manual describes  the Sail language  and the execution-time  routines
for the typical Sail user: a non-novice programmer with some knowledge  of
ALGOL.  It lies somewhere between being a tutorial and a reference manual.

This manual was supported by  the Advanced Research Projects Agency  under
Contract MDA 903-76-C-0206.

The views and  conclusions contained  in this  document are  those of  the
author(s) and should  not be interpreted  as necessarily representing  the
official policies, either  expressed or implied,  of Stanford  University,
ARPA, NSF, or the United States Government.

We thank  Bernard  A. Goldhirsch  and  the Institute  for  Advancement  of
Sailing for their kind  permission to use the  cover design of the  August
1976 issue of SAIL magazine.

Reproduced  in  the   U.S.A.   Available  from   the  National   Technical
Information Service, Springfield, Virginia 22161.

PREFACE HISTORY OF THE LANGUAGE LEARNING ABOUT SAIL CHANGES IN THE LANGUAGE OPERATING SYSTEMS UNIMPLEMENTED CONSTRUCTS ACKNOWLEDGEMENTS



                                  PREFACE



HISTORY OF THE LANGUAGE
The  GOGOL  III compiler,  developed  principally by  Dan  Swinehart  at the
Stanford Artificial  Intelligence Project,  was the  basis for  the non-LEAP
portions  of SAIL.   Robert Sproull  joined Swinehart  in  incorporating the
features of LEAP The first version of the language was released in November,
1969.   SAIL's intermediate  development was  the responsibility  of Russell
Taylor,  Jim  Low,  and Hanan  Samet,  who  introduced  processes, procedure
variables, interrupts,  contexts, matching procedures,  a new  macro system,
and other features.   Most recently John  Reiser, Robert Smith,  and Russell
Taylor  maintained and  extended SAIL.   They added  a  high-level debugger,
conversion to TENEX, a print statement, and records and references.

LEARNING ABOUT SAIL
A novice programmer (or one who is unfamiliar with ALGOL) should  start with
the Sail Tutorial [SmithN].   An experienced programmer with a  knowledge of
ALGOL should be able to use  this Sail manual at once.  Begin  with Appendix
A, Characters; in this manual  the symbol "" designates the  character with
code '030.  For the first reading, a light skim of sections 1, 2, 3,  4, and
8, followed by  a careful perusal of  subsection 21.1 should be  adequate to
familiarize the  new user with  the differences between  ALGOL and  SAIL and
allow him  to start writing  programs in SAIL.   The other sections  of this
manual are relatively self contained, and can be read when one wants to know
about the features they describe.  The exceptions to this rule  are sections
12, 13, and 14.  These describe the basics of the LEAP and are essential for
understanding of the following sections.

Special effort  has gone into  making the index  more comprehensive  than in
previous versions of this manual.  Please use it.

CHANGES IN THE LANGUAGE
There  are no  known incompatibilities  at the  SAIL source  level  with the
language described  in [vanLehn].  PRINT,  BAIL, operation under  TENEX, and
records  are  major additions  to  the language.   Significant  revisions to
[vanLehn] or points  deserving emphasis are marked  by vertical bars  in the
margin.  This paragraph is so marked, as an example.

OPERATING SYSTEMS
Sail runs under  several operating systems.   In this manual  distinction is
drawn between the operating  system at the Stanford  Artificial Intelligence
Laboratory  (SUAI),  the  TOPS-10 operating  system  from  Digital Equipment
Corporation, the TENEX  operating system from  Bolt Beranek and  Newman, and
the TYMSHARE operating system.   The major distinction is between  TENEX and
non-TENEX systems,  although the  differences between  SUAI and  TOPS-10 are
also  significant.   The  TOPS-20 operating  system  from  Digital Equipment
Corporation is the same as TENEX  as far as Sail is concerned.   TENEX users
should substitute "<SAIL>" for "SYS:" wherever the latter appears in  a file
name (except when talking to the LOADER).


UNIMPLEMENTED CONSTRUCTS
The following items are described in the manual as if they existed.   As the
manual goes to press, they are not implemented.

1.  NEW (<contextvariable>).  Creates a new item which has a datum  that is
    a context.

2.  Using a <contextvariable> instead of a list of variables in any  of the
    REMEMBER, FORGET or RESTORE statements.

3.  Using  in the expression n of REMOVE n FROM list.

4.  ANYANYANY searches in Leap  (searches where no constraints at  all are
    placed on the triple returned.)

5.  CHECKED itemvars (the dynamic comparison of the datum type of an item to
    the  datum  type of  the  CHECKED itemvar  to  which the  item  is being
    assigned.) It is currently the user's responsibility to insure  that the
    type of the item agrees with  the type of the itemvar whenever  DATUM is
    used.

ACKNOWLEDGEMENTS
Les Earnest and Robert Smith assisted the editor in PUB wizardry and reading
drafts.

TABLE OF CONTENTS


                     T A B L E   O F   C O N T E N T S



SECTION                                                                 PAGE


1  PROGRAMS AND BLOCKS

   1  Syntax                                                               5
   2  Semantics                                                            6

2  ALGOL DECLARATIONS

   1  Syntax                                                               8
   2  Restrictions                                                        12
   3  Examples                                                            13
   4  Semantics                                                           14
   5  Separately Compiled Procedures                                      25

3  ALGOL STATEMENTS

   1  Syntax                                                              28
   2  Semantics                                                           31

4  ALGOL EXPRESSIONS

   1  Syntax                                                              42
   2  Type Conversion                                                     45
   3  Semantics                                                           48

5  ASSEMBLY LANGUAGE STATEMENTS

   1  Syntax                                                              56
   2  Semantics                                                           57

6  INPUT/OUTPUT ROUTINES

   1  Execution-time Routines in General                                  62
   2  I/O  Channels and Files                                             63
   3  Break Characters                                                    68
   4  I/O Routines                                                        72
   5  TTY and PTY Routines                                                80
   6  Example of TOPS-10 I/O                                              83



7  EXECUTION TIME ROUTINES

   1  Type Conversion Routines                                            85
   2  String Manipulation Routines                                        88
   3  Liberation-from-Sail Routines                                       89
   4  Byte Manipulation Routines                                          92
   5  Other Useful Routines                                               93
   6  Numerical Routines                                                  95

8  PRINT

   1  Syntax                                                              98
   2  Semantics                                                           98

9  MACROS AND CONDITIONAL COMPILATION

   1  Syntax                                                             103
   2  Delimiters                                                         105
   3  Macros                                                             106
   4  Macros with Parameters                                             110
   5  Conditional Compilation                                            111
   6  Type Determination at Compile Time                                 112
   7  Miscelaneous Features                                              114
   8  Hints                                                              115

10 RECORD STRUCTURES

   1  Introduction                                                       117
   2  Declaration Syntax                                                 117
   3  Declaration Semantics                                              117
   4  Allocation                                                         119
   5  Fields                                                             119
   6  Garbage Collection                                                 120
   7  Internal Representations                                           120
   8  Handler Procedures                                                 122
   9  More about Garbage Collection                                      124

11 TENEX ROUTINES

   1  Introduction                                                       126
   2  TOPS-10 Style Input/Output                                         126
   3  TENEX Style Input/Output                                           128
   4  Terminal Handling                                                  140
   5  Utility TENEX System Calls                                         148

12 LEAP DATA TYPES

   1  Introduction                                                       151
   2  Syntax                                                             152
   3  Semantics                                                          154



13 LEAP STATEMENTS

   1  Syntax                                                             159
   2  Restrictions                                                       161
   3  Semantics                                                          161
   4  Searching the Associative Store                                    164

14 LEAP EXPRESSIONS

   1  Syntax                                                             175
   2  Semantics                                                          177

15 BACKTRACKING

   1  Introduction                                                       182
   2  Syntax                                                             182
   3  Semantics                                                          183

16 PROCESSES

   1  Introduction                                                       186
   2  Syntax                                                             186
   3  Semantics                                                          186
   4  Process Runtimes                                                   192

17 EVENTS

   1  Syntax                                                             196
   2  Introduction                                                       196
   3  Sail-defined Cause and Interrogate                                 197
   4  User-defined Cause and Interrogate                                 199

18 PROCEDURE VARIABLES

   1  Syntax                                                             203
   2  Semantics                                                          203

19 INTERRUPTS

   1  Introduction                                                       207
   2  Interrupt Routines                                                 208
   3  Immediate Interrupts                                               211
   4  Clock Interrupts                                                   214
   5  Deferred Interrupts                                                215

20 LEAP RUNTIMES

   1  Types and Type Conversion                                          218
   2  Make and Erase Breakpoints                                         220
   3  Pname Runtimes                                                     220
   4  Other Useful Runtimes                                              221
   5  Runtimes for User Cause and Interrogate Procedures                 223

21 BASIC CONSTRUCTS

   1  Syntax                                                             226
   2  Semantics                                                          227

22 USING SAIL

   1  For TOPS-10 Beginners                                              231
   2  For TENEX Beginners                                                232
   3  The Complete use of Sail                                           232
   4  Compiling Sail Programs                                            233
   5  Loading Sail Programs                                              241
   6  Starting Sail Programs                                             241
   7  Storage Reallocation with REEnter                                  242

23 DEBUGGING SAIL PROGRAMS

   1  Error Messages                                                     243
   2  Debugging                                                          246
   3  BAIL                                                               249

APPENDICES

   A  Characters                                                         264
   B  Sail Reserved Words                                                267
   C  Sail Predeclared Identifiers                                       270
   D  Indices for Interrupts                                             273
   E  Bit Names for Process Constructs                                   276
   F  Statement Counter System                                           278
   G  Array Implementation                                               281
   H  String Implementation                                              283
   I  Save/Continue                                                      285
   J  Procedure Implementation                                           287

REFERENCES                                                               293


INDEX                                                                    294

PROGRAMS AND BLOCKS Syntax


                                 SECTION  1

                            PROGRAMS AND BLOCKS




1.1  Syntax

<program>
    ::= <block>


<block>
    ::= <blockhead> ; <compoundtail>


<blockhead>
    ::= BEGIN <declaration>
    ::= BEGIN <blockname> <declaration>
    ::= <blockhead> ; <declaration>


<compoundtail>
    ::= <statement> END
    ::= <statement> END <blockname>
    ::= <statement> ; <compoundtail>


<compoundstatement>
    ::= BEGIN <compoundtail>
    ::= BEGIN <blockname> <compoundtail>


<statement>
    ::= <block>
    ::= <compoundstatement>
    ::= <requirespecification>
    ::= <assignment>
    ::= <swapstatement>
    ::= <conditionalstatement>
    ::= <ifstatement>
    ::= <gotostatement>
    ::= <forstatement>
    ::= <whilestatement>
    ::= <dostatement>
    ::= <casestatement>
    ::= <printstatement>
    ::= <returnstatement>
    ::= <donestatement>
    ::= <nextstatement>

 Semantics DECLARATIONS STATEMENTS BLOCK NAMES


    ::= <continuestatement>
    ::= <procedurestatement>
    ::= <safetystatement>
    ::= <backtrackingstatement>
    ::= <codeblock>
    ::= <leapstatement>
    ::= <processstatement>
    ::= <eventstatement>
    ::= <stringconstant> <statement>
    ::= <labelidentifier> : <statement>
    ::= <empty>



1.2  Semantics

DECLARATIONS
Sail programs are organized  in the traditional block structure  of ALGOL-60
[Nauer].

Declarations serve  to define the  data types and  dimensions of  simple and
subscripted (array) variables  (arithmetic variables, strings,  sets, lists,
record  pointers, and  items).  They  are also  used to  describe procedures
(subroutines) and record classes,  and to name program labels.

Any  identifier  referred  to  in  a  program  must  be  described  in  some
declaration.  An identifier may only be referenced by statements  within the
scope (see page 14) of its declaration.

STATEMENTS
As in ALGOL, the statement is the fundamental unit of operation in  the Sail
language.  Since a statement within a block or compound statement may itself
be  a  block  or  compound  statement,  the  concept  of  statement  must be
understood recursively.

The  block representing  the  program is  known as  the  "outer block".  All
blocks internal to this one will be referred to as "inner blocks".

BLOCK NAMES
The block name construct is used  to describe the block structure of  a Sail
program to  a symbolic debugging  routine (see page  246).  The name  of the
outer block becomes the title of the binary output file (not necessarily the
file name).  In addition, if a block name is used following an END  then the
compiler compares it  with the block  name which followed  the corresponding
BEGIN.  A mismatch is reported to the user as evidence of a  missing (extra)
BEGIN or END somewhere.

The <stringconstant> <statement> construct  is equivalent in action  to the
<statement> alone; that is, the string constant serves only as a comment.

 EXAMPLES


EXAMPLES

Given:
    S is a statement,
    Sc is a Compound Statement,
    D is a Declaration,
    B is a Block.

Then:

    (Sc)    BEGIN S; S; S; ... ; S END

    (Sc)    BEGIN "SORT" S; S; ... ;S END "SORT"

    (B)     BEGIN D; D; D; ... ; S; S; S; ... ; S END

    (B)     BEGIN "ENTER NEW INFO" D;  D; ... ;
                                         S; ... ;S END



are syntactically valid Sail constructs.

ALGOL DECLARATIONS Syntax


                                 SECTION  2

                             ALGOL DECLARATIONS




2.1  Syntax


<idlist>
    ::= <identifier>
    ::= <identifier> , <idlist>


<declaration>
    ::= <typedeclaration>
    ::= <arraydeclaration>
    ::= <preloadspecification>
    ::= <labeldeclaration>
    ::= <proceduredeclaration>
    ::= <synonymdeclaration>
    ::= <requirespecification>
    ::= <contextdeclaration>
    ::= <leapdeclaration>
    ::= <recordclassdeclaration>
    ::= <protectacs declaration>
    ::= <cleanupdeclaration>
    ::= <typequalifier> <declaration>


<simpletype>
    ::= BOOLEAN
    ::= INTEGER
    ::= REAL
    ::= RECORDPOINTER ( <classidlist> )
    ::= STRING


<typequalifier>
    ::= EXTERNAL
    ::= FORTRAN
    ::= FORWARD
    ::= INTERNAL
    ::= OWN
    ::= RECURSIVE
    ::= SAFE
    ::= SHORT
    ::= SIMPLE



<typedeclaration>
    ::= <simpletype> <idlist>
    ::= <typequalifier> <typedeclaration>


<arraydeclaration>
    ::= <simpletype> ARRAY <arraylist>
    ::= <typequalifier> <array declaration>


<arraylist>
    ::= <arraysegment>
    ::= <arraylist> , <arraysegment>


<arraysegment>
    ::= <idlist> [ <boundpairlist> ]


<boundpairlist>
    ::= <boundpair>
    ::= <boundpairlist> , <boundpair>


<boundpair>
    ::= <lowerbound> : <upperbound>

<lowerbound>
    ::= <algebraicexpression>

<upperbound>
    ::= <algebraicexpression>


<preloadspecification>
    ::= PRELOADWITH <preloadlist>
    ::= PRESETWITH <preloadlist>


<preloadlist>
    ::= <preloadelement>
    ::= <preloadlist> , <preloadelement>


<preloadelement>
    ::= <expression>
    ::= [expression] <expression>


<labeldeclaration>
    ::= LABEL <idlist>



<proceduredeclaration>
    ::= PROCEDURE <identifier> <procedurehead>
        <procedurebody>
    ::= <simpletype> PROCEDURE <identifier>
        <procedurehead> <procedurebody>
    ::= <typequalifier> <proceduredeclaration>


<procedurehead>
    ::= <empty>
    ::= ( <formalparamdecl> )


<procedurebody>
    ::= <empty>
    ::= ; <statement>


<formalparamdecl>
    ::= <formalparameterlist>
    ::= <formalparameterlist> ;
        <formalparamdecl>


<formalparameterlist>
    ::= <formaltype> <idlist>
    ::= <formaltype> <idlist> (
        <defaultvalue> )


<formaltype>
    ::= <simpleformaltype>
    ::= REFERENCE <simpleformaltype>
    ::= VALUE <simpleformaltype>


<simpleformaltype>
    ::= <simpletype>
    ::= <simpletype> ARRAY
    ::= <simpletype> PROCEDURE


<synonymdeclaration>
    ::= LET <synonymlist>


<synonymlist>
    ::= <synonym>
    ::= <synonymlist> , <synonym>

 Restrictions


<synonym>
    ::= <identifier> = <reservedword>


<cleanupdeclaration>
    ::= CLEANUP <procedureidentifierlist>

<requirespecification>
    ::= REQUIRE <requirelist>


<requirelist>
    ::= <requireelement>
    ::= <requirelist> , <requireelement>


<requireelement>
    ::= <constantexpression> <requirespec>
    ::= <procedurename> INITIALIZATION
    ::= <procedurename> INITIALIZATION [
        <phase> ]


<requirespec>
    ::= STRINGSPACE
    ::= SYSTEMPDL
    ::= STRINGPDL
    ::= ITEMSTART
    ::= NEWITEMS
    ::= PNAMES
    ::= LOADMODULE
    ::= LIBRARY
    ::= SOURCEFILE
    ::= SEGMENTFILE
    ::= SEGMENTNAME
    ::= POLLINGINTERVAL
    ::= POLLINGPOINTS
    ::= VERSION
    ::= ERRORMODES
    ::= DELIMITERS
    ::= NULLDELIMITERS
    ::= REPLACEDELIMITERS
    ::= UNSTACKDELIMITERS
    ::= BUCKETS
    ::= MESSAGE
    ::= COMPILERSWITCHES



2.2  Restrictions

For simplicity, the typequalifiers are listed in only one  syntactic class.
Although their  uses are  always valid  when placed  according to  the above
syntax, most of them only have meaning when applied to particular subsets of
these productions:

      SAFE is only meaningful in array declarations.

      INTERNAL/EXTERNAL have no meaning in formal parameter declarations.

      SIMPLE,  FORWARD,  RECURSIVE,  and  FORTRAN  have  meaning  only in
      procedure type specifications.

      SHORT has meaning only when applied to INTEGER or REAL entities.

 Examples


For array declarations  in the outer block  substitute <constantexpression>
for  <algebraicexpression>  in   the  productions  for   <lowerbound>  and
<upperbound>.

A label must be declared in the innermost block in which the statement being
labeled  appears (more  information, page  32).   The  syntax  for procedure
declarations requires semantic embellishment (see page 17) in order  to make
total  sense.   In particular,  a  procedure body  may  be empty  only  in a
restricted class of declarations.



2.3  Examples

Let I, J, K, L, X, Y, and P be identifiers, and let S be a statement.

(<typedeclaration>)
      INTEGER I, J, K
      EXTERNAL REAL X, Y
      INTERNAL STRING K

(<arraydeclaration>)
      INTEGER ARRAY X [0:10, 0:10]
      REAL ARRAY Y [X:P(L)]; Comment illegal
         in outer block unless P is a macro
      STRING ARRAY I [0:IF BIG THEN 30 ELSE 3]

(<labeldeclaration>)
      LABEL L, X, Y

(<procedure declaration>)
      PROCEDURE P; S
      PROCEDURE P (INTEGER I, J;
         REFERENCE REAL X; REAL Y);  S
      INTEGER PROCEDURE P (REAL PROCEDURE L;
         STRING I, J; INTEGER ARRAY K); S
      EXTERNAL PROCEDURE P (REAL X)
      FORWARD INTEGER PROCEDURE X (INTEGER I)

Note that  these sample  declarations are all  given without  the semicolons
which would  normally separate  them from  the surrounding  declarations and
statements.  Here is a sample block  to bring it all together (again,  let S
be any statement, D any declaration, and other identifiers as above):

 Semantics SCOPE OF DECLARATIONS TYPE QUALIFIERS



     BEGIN "SAMPLE BLOCK"
      INTEGER I, J, K;
      REAL X, Y;
      STRING A;
      INTEGER PROCEDURE P (REFERENCE REAL X);
       BEGIN "P"
        D; D; D; ... ;S; ... ; S
       END "P";

      REAL ARRAY DIPHTHONGS [0:10, 1:100];

      S;  S;  S;  S
     END "SAMPLE BLOCK"



2.4  Semantics

SCOPE OF DECLARATIONS
Every  block  automatically introduces  a  new level  of  nomenclature.  Any
identifier declared in  a block's head  is said to  be LOCAL to  that block.
This means that:

   a. The entity represented by  this identifier inside the block  has no
      existence outside the block.

   b. Any entity represented by the same identifier outside the  block is
      completely inaccessible (unless it has been passed  as a parameter)
      inside the block.

An identifier occurring within an  inner block and not declared  within that
block  will  be nonlocal  (global)  to  it;  that  is,  the  identifier will
represent the same entity inside the block and in the block or blocks within
which it is nested, up to and including the level in which the identifier is
declared.

The  Scope  of an  entity  is  the set  of  blocks in  which  the  entity is
represented, using the above rules, by its identifier.  An entity may not be
referenced by any statement outside its scope.

TYPE QUALIFIERS
An array,  variable, or  procedure declared OWN  will behave  as if  it were
declared globally  to the  current procedure;  the OWN  type qualifier  on a
variable, etc. declared in a block not nested inside a procedure declaration
will have no effect.  This means  that in a second call of a  procedure with
OWN locals (or a recursive call)

 NUMERIC DECLARATIONS STRING DECLARATIONS ARRAY DECLARATIONS


the OWN variables will not be reinitialized; they will have the  values that
they had when  the first call of  the procedure finished.   Furthermore, OWN
arrays, etc.  will not be  deallocated upon exiting  the procedure  in which
they are declared.

INTERNAL and  EXTERNAL procedures,  variables, etc.   let one  link programs
that are  loaded together  but were  compiled separately.   See page  25 for
more information.

RECURSIVE, SHORT, FORTRAN, FORWARD, SIMPLE, and SAFE will be explained  when
the data types they modify are discussed.

NUMERIC DECLARATIONS
Identifiers which appear in type declarations with types REAL or INTEGER can
subsequently be used to refer to numeric variables.  An Integer variable may
take on  values from -2^35  to 2^35-1 (-2^26 to 2^26-1 for  SHORT INTEGERS).
A Real variable may take  on positive and negative values from  about 10^-38
to 10^38  with a precision  of 27 bits  (same range for  SHORT REALs  as for
SHORT INTEGERs).  REAL and INTEGER variables (and constants) may be  used in
the  same   arithmetic  expressions;  type   conversions  are   carried  out
automatically (see page 46) when necessary.

The  advantage of  SHORT  reals and  integers  is that  the  conversion from
integer to real is sped by a  factor of 8 if either the integer or  the real
is SHORT.  See page 46 for more information.

The BOOLEAN type is identical to INTEGER.  BOOLEAN and algebraic expressions
are really  equivalent syntactically.  The  syntactic context in  which they
appear determines their meaning. Non-zero integers correspond to TRUE  and 0
corresponds  to  FALSE.   The declarator  BOOLEAN  is  included  for program
clarity.

STRING DECLARATIONS
A  variable  defined  in  a  String  declaration  is  a  two-word descriptor
containing the information necessary to represent a Sail character string.

A String may be thought of as a variable-length, one-dimensional array of 7-
bit ASCII characters.  Its descriptor contains a character count and  a byte
pointer  to  the  first  character (see  page  283).   Strings  originate as
constants  at compile  time (page  229),  as the  result of  a  String INPUT
operation  from some  device (see  page 72),  or from  the  concatenation or
decomposition of already existing strings (see page 53).

When  strings appear   in arithmetic  operations or  vice-versa,  a somewhat
arbitrary conversion is performed to obtain the proper type (by arbitrary we
do not mean to  imply random -- see  page 46).  For this  reason arithmetic,
String,  and  Recordpointer  variables  are  referred  to   as   "algebraic
variables"  and  their  corresponding  expressions  are   called  "algebraic
expressions" (to differentiate them them from the variables  and expressions
of LEAP  -- see page 151).

 PRELOAD SPECIFICATIONS


ARRAY DECLARATIONS
In general, any data  type which is applicable  to a simple variable  may be
applied  in an  Array  declaration to  an  array of  variables.   The entity
represented by the name of an Array, qualified with subscript expressions to
locate  a particular  element (e.g.  A[I, J])  behaves in  every way  like a
simple variable.   Therefore, in the  future we shall  refer to  both simple
variables  and  single   elements  of  Arrays  (subscripted   variables)  as
"variables".  The formal syntax for <variable> can be found on page 227.

For   an Array  which is  not qualified  by the  SAFE attribute,  nor  had a
NOWSAFE  statement done  on it  (NowSafe -  see page  41),  each subscript
will be checked to  ensure that it falls  within the lower and  upper bounds
given for the dimension it specifies.  Subscripts outside the bounds trigger
an  error message  and  job abortion.   The SAFE  declaration  inhibits this
checking, resulting in faster, smaller,  and bolder code.

Arrays which  are allocated at  compile time (OWN  arrays and arrays  in the
outer block) are restricted to 5 or fewer dimensions.  There is no  limit to
the  number  of  dimensions  allowed  for  an  Array  which  is  dynamically
allocated.  However,  the efficiency of  Array references tends  to decrease
for large dimensions.  Avoid large dimensionality.

OWN  Arrays are  available in  part.  They  must be  declared  with constant
bounds, since fixed  storage is allocated.   They are NOT  initialized  when
the program is  started or restarted (except  in preloaded Arrays,  see page
16).  A certain  degree of extra efficiency  is possible in  accessing these
Arrays, since they may be assigned absolute core locations by  the compiler,
eliminating some of  the address arithmetic.   Constant bounds always  add a
little  efficiency, even  in inner  blocks.   Arrays  declared in  the outer
block  must  have constant  bounds,  since  no variable  may  yet  have been
assigned a value.  They are thus automatically made OWN.   For  more details
concerning the internal structure of Arrays see page 246 and page 281.

PRELOAD SPECIFICATIONS
Any OWN arithmetic or String Array may be "pre-loaded" at compile  time with
constant    information    by    preceding    its    declaration    with   a
<preloadspecification>.  This specification  gives the values which  are to
be placed in consecutive  core locations of the Arrays  declared immediately
following the <preloadspecification>.   "Immediately", in this  case, means
all identifiers up to and including one which is followed by boundpairlist
brackets (e.g. in REAL ARRAY X, Y, Z[0:10], W[1:5]; -- preloads X, Y, and Z;
not W).  It is the user's responsibility to guarantee that the proper values
will be obtained under the  subscript mapping, namely: arrays are  stored by
rows;  if A[I, J] is stored  in location 10000, then A[I, J+1] is  stored in
location 10001.

The current   values of non-String  pre-loaded Arrays will  not be   lost by
restarting the program; they will not be re-initialized or re-preloaded. For
preloaded String  Arrays, the  non-constant elements  are set  to NULL  by a
restart.

 PROCEDURE DECLARATIONS FORMAL PARAMETERS


Algebraic  type conversions  will be  performed at  compile-time  to provide
values of the proper types  to pre-loaded Arrays.  All expressions  in these
specifications must be  constant expressions --  that is, they  must contain
only constants and algebraic operators.  The compiler will not allow  you to
fill an Array  beyond its capacity.  You  may, however, provide a  number of
elements less than the total  size of the Array; remaining elements  will be
set to zero or to the null string.

Example:

       PRELOADWITH [5] 0, 3, 4, [4] 6, 2;
       INTEGER ARRAY TABL [1:4, 1:3];

The first five elements of  TABL will be initialized to 0  (bracketed number
is used  as a  repeat argument).   The next two  elements will  be 3  and 4,
followed by four 6's and a 2.  The array will look like this:

                    1  2  3 (second subscript)
                        
                1 | 0  0  0
(first          2 | 0  0  3
   subscript)   3 | 4  6  6
                4 | 6  6  2


PRESETWITH is just like PRELOADWITH  except that an array which  is PRESET
is placed in  the upper segment of  a /H compilation.  This  allows constant
arrays to be in the shared portion of the code.

PROCEDURE DECLARATIONS
If a Procedure is  typed then it  may return  a value (see page 36)   of the
specified  type.   If formal  parameters  are specified  then  they  must be
supplied with actual parameters in a one to one correspondence when they are
called (see page 54 and page 39).

FORMAL PARAMETERS
Formal  parameters,  when   specified,  provide  information  to   the  body
(executable portion) of the Procedure  about the kinds of values  which will
be  provided as  actual parameters  in the  call.  The  type  and complexity
(simple or  Array) are  specified here.  In  addition, the  formal parameter
indicates whether  the value  (VALUE) or address  (REFERENCE) of  the actual
parameter will be  supplied.  If the address  is supplied then  the variable
whose  identifier is  given as  an actual  parameter may  be changed  by the
Procedure.  This is not the case if the value is given.

To pass  a PROCEDURE  by value  has no  readily determined  meaning.  ARRAYs
passed by value (requiring a complete copy operation) are  not  implemented.
Therefore these cases are noted as errors by the compiler.

The proper  use of  actual parameters is  further discussed  on page  39 and
page 54.

 DEFAULT PARAMETER VALUES FORWARD PROCEDURE DECLARATIONS


DEFAULT PARAMETER VALUES
Default values  for trailing  parameters may be  specified by  enclosing the
desired value in parentheses following the parameter declaration.

        PROCEDURE FOO (REAL X; INTEGER I (2);
            STRING S ("FOO"); REAL Y (3.14159) );

If a defaulted parameter is left  out of a procedure call then  the compiler
fills  in the  default automatically.   The following  all compile  the same
code:

        FOO (A+B);
        FOO (A+B, 2, "FOO");
        FOO (A+B, 2, "FOO", 3.14159);

Only  VALUE parameters  may be  defaulted, and  the default  values  must be
constant expressions.  A parameter may not be left out of the middle  of the
parameter list; i.e., FOO (A+B,  , "BAR") won't work.  Finally, it should be
noted  that  the compiled  code  assumes that  all  parameters  are actually
present in the call, so be careful about odd STARTCODE or INTERNAL-EXTERNAL
linkages.  However, APPLY will fill  in default values if not  enough actual
parameters  are  supplied   in  an  interpreted  call.    FORWARD  PROCEDURE
DECLARATIONS
A Procedure's type and parameters must be described before the Procedure may
be  called.   Normally  this is  accomplished  by  specifying  the procedure
declaration in the head of some block containing the call.  If,  however, it
is necessary to have two Procedures, declared in some block head,  which are
both accessible to statements in the compound tail of that block and to each
other, then the  FORWARD construct permits  the definition of  the parameter
information for one of these Procedures in advance of its  declaration.  The
Procedure body must be empty  in a forward procedure declaration.   When the
body  of the  Procedure  described in  the forward  declaration  is actually
declared, the types of the Procedure and of its parameters must be identical
in  both  declarations.  The  declarations  must appear  at  the  same level
(within the same block head).  Example:

    BEGIN "NEED FORWARD"
        FORWARD INTEGER PROCEDURE T1 (INTEGER I);
            COMMENT PARAMS DESCRIBED;
        INTEGER PROCEDURE T2 (INTEGER J);
            RETURN (T1 (J)+3);  COMMENT CALL T1 ;
        INTEGER PROCEDURE T1 (INTEGER I);
            COMMENT ACTUALLY DEFINE T1;
            RETURN (IF I=15 THEN I ELSE T2 (I-1));
                COMMENT CALLS T2;
        ...
        K_T1 (L);  ...  ; L_T2 (K);  ...
    END "NEED FORWARD";

Notice that the forward declaration is required only because BOTH Procedures

 RECURSIVE PROCEDURES SIMPLE PROCEDURES


are  called in  the body  of  the block.   These procedures  should  also be
declared RECURSIVE if recursive entrance is likely.  If only T1  were called
from statements within the block then this example could be implemented as:

     BEGIN "NO FORWARD"
        RECURSIVE INTEGER PROCEDURE T1 (INTEGER I);
        BEGIN
                INTEGER PROCEDURE T2 (J);
                        RETURN (T1 (J)+3);
                RETURN( IF I=15 THEN I
                                ELSE T2 (I-1));
        END "T1";
        ...
        K_T1 (L);
     ...
     END "NO FORWARD";

RECURSIVE PROCEDURES
If  a Procedure  is to  be  entered recursively  then the  compiler  must be
instructed  to provide  code  for allocating  new local  variables  when the
Procedure is called  and deallocating them when  it returns.  Use  the type-
qualifier RECURSIVE in the declaration of any recursive Procedure.

The  compiler  can  produce  much  more  efficient  code  for  non-recursive
Procedures than for  recursive ones.  We feel  that this gain  in efficiency
merits the necessity for declaring Procedures to be recursive.

If a Procedure which has  not been declared recursive is  called recursively
then all its  local variables (and  temporary storage locations  assigned by
the compiler) will behave  as if they were  global to the Procedure  -- they
will not  be reinitialized,  and when  the recursive  call is  complete, the
locals of the calling procedure will reflect the changes made to them during
the recursive call.  Otherwise, no ill effects should be observed.

SIMPLE PROCEDURES
Standard procedures contain a short prologue that sets up some links  on the
stack and a  descriptor that is used  by the storage allocation  system, the
GOTO solver, and some  other  routines.  For most procedures,  this overhead
is insignificant.  However, for small  procedures that just do a  few simple
statements and exit, this overhead  is excessive and unneeded.  To  skip the
prologue,  just include  SIMPLE  in the  attribute list  for  the procedure.
RESTRICTIONS:

 EXTERNAL PROCEDURES


     1.     Simple  procedures  may  not  be  Recursive  and  may  not be
      SPROUTed or APPLYed.

     2.     ARRAY locals must be OWN.

     3.     Set and List  locals must be OWN  (Sets and list are  part of
      Leap, page 151).

     4.     Procedures declared local to a simple procedure must  also be
      of of type SIMPLE, and may not reference any of the   parameters of
      the outer simple procedure.

     5.     One may not GO TO a statement outside the body of  the simple
      procedure.

     6.     RECORDPOINTERs may not be declared or passed as arguments to
      other  procedures, and  the  code must  not cause  the  compiler to
      create RECORDPOINTER temporaries.

EXTERNAL PROCEDURES
A file compiled by Sail  represents either a "main" program or  a collection
of independent procedures to be called by the main program.  The  method for
preparing such  a collection  of Procedures  is described  in page  25.  The
EXTERNAL and FORTRAN type-qualifiers allow description of the types of these
Procedures  and  their   parameters.   An  EXTERNAL  or   FORTRAN  procedure
declaration,  like the  FORWARD declaration,  does not  include  a procedure
body.  Both declarations instead result in requests to the loader to provide
the addresses of  these Procedures to all  statements which call  them. This
means  that an  EXTERNAL Procedure  declaration (or  the declaration  of any
External  identifier)  may  be   placed  within  any  block   head,  thereby
controlling the scope of this External identifier within this program.

Any Sail Procedure which is referenced via these external  declarations must
be an INTERNAL Procedure.  That is, the type-qualifier INTERNAL  must appear
in the actual declaration of the Procedure.  Again, see page 25.

The  type-qualifier FORTRAN  is used  to describe  the type  and name  of an
external Procedure which is to  be called using a Fortran  calling sequence.
Either the old F40 or the new FORTRAN-10 calling sequence can  be generated,
depending on the /A switch (page 237).  All parameters to Fortran Procedures
are by reference.  In fact, the procedure head part of the  declaration need
not be included unless the types expected by the Procedure differ from those
provided by  the actual parameters--the  number of parameters  supplied, and
their  types, are  presumed  correct. Fortran  Procedures  are automatically
External Procedures.   See page 21,  page 39, page  54 for  more information
about Fortran Procedures.  Example:

        FORTRAN PROCEDURE FPF;
        Y_FPF (X, Z);

 PARAMETRIC PROCEDURES DEFAULTS IN PROCEDURE DECLARATIONS RESTRICTIONS ON PROCEDURE DECLARATIONS ALLOCATION AND DEALLOCATION


PARAMETRIC PROCEDURES
The calling conventions for Procedures with Procedures as arguments, and for
the execution of these parametric  Procedures, are described on page  39 and
page 54.  Any  Procedure PP which  is to be used  as a parameter  to another
Procedure  CP  must  not  have any  Procedure  or array  parameters,  or any
parameters  called  by value.   In  other  words, PP  may  only  have simple
reference parameters.   The number of  parameters supplied in  a call  on PP
within CP,  and their  types, will be  presumed correct,  and should  not be
specified in the procedure head.  Example:

    PROCEDURE CP (INTEGER PROCEDURE FP);
        BEGIN INTEGER A, I; REAL X;
        ...
        A_FP (I, X); COMMENT I AND X PASSED BY
                REFERENCE, NO TYPE CONVERSION;
        END "CP";

    INTEGER PROCEDURE PP (REFERENCE INTEGER J;
                 REFERENCE REAL Y);
        BEGIN ...
        END "PP";
     ...
    CP (PP);

DEFAULTS IN PROCEDURE DECLARATIONS
If no VALUE or REFERENCE  qualification appears in the description  then the
following qualifications are assumed:

  VALUE    Integer, String, Real, Recordpointer,
           Set, List variables.
  REFERENCEArrays, Contexts and Procedures.

RESTRICTIONS ON PROCEDURE DECLARATIONS

   1) Fortran  Procedures cannot  handle  String parameters.   Nor  can a
      Fortran Procedure return a string as a result.

   2) Labels may never be passed as arguments to Procedures.

   3) Procedures may not have the type "CONTEXT".

   4) Arrays and Context parameters must always be passed by reference.



ALLOCATION AND DEALLOCATION
All simple  variables (integer, real,  string, boolean, record  pointer) are
allocated at  compile time.  Non-own  simple variables that  are local  to a
recursive procedure  are an  exception to   this and  are allocated  (on the
stack) upon instantiation  of the procedure;  they are deallocated  when the

 INITIALIZATION AND REINITIALIZATION SYNONYMS


instantiation is terminated.    Simple variables which are declared  but not
subsequently referenced are not allocated at all.

All outer-block  and OWN arrays  are allocated at  compile time.   All other
arrays are  allocated when  the block  of their  definition is  entered, and
deallocated when it is exited.

INITIALIZATION AND REINITIALIZATION
Upon allocation, everything is initialized  to 0 or the NULL  string (except
preloaded  arrays,  which  are  initialized to  their  the  values  of their
PRELOAD).   Nothing  is reinitialized  unless  the program  is  restarted by
typing ^C and  REEnter.  This lack  of reinitialization is   noticeable when
one enters a block for the second time, and that block is not the body  of a
recursive procedure.  For example,

    STRING PROCEDURE READIN;
    BEGIN
        INTEGER  CHANNEL, BRTAB;
        IF BRTAB=0 THEN BRTAB _ INIT (CHANNEL);
        RETURN (INPUT (CHANNEL, BRTAB));
    END;

will return a string from  an input operation with every call.   However, on
the first call,  it will do some  initialization of the I/O  channel because
BRTAB is 0 then,  whereas it is not for  any of the other calls.   If READIN
were a  recursive procedure then  CHANNEL and BRTAB  would be  allocated and
hence initialized with every call.

When one REEnters a program, some things are reinitialized and some are not.
Namely, strings and non-preloaded  arrays will be reinitialized,  but simple
variables will not.  Preloaded arrays will not be re-preloaded.

SYNONYMS
The Sail Synonym ("LET") permits one  to declare any identifier to act  as a
reserved word.  The effect  of the reserved word  is not changed; it  may be
used as well  as the new identifier.   Synonyms follow the same  scope rules
that identifiers used for variables, arrays, etc. do.

Since  Sail  permits  one to  declare  almost  any reserved  word  to  be an
identifier   for variables,  procedures,  etc.  (see  about  restrictions on
identifiers,  page  228),  synonyms  are  used  to keep  the  effect  of the
reserved word available.  For example,

 CLEANUP DECLARATIONS REQUIREMENTS



        LET BEG = BEGIN;
        PROCEDURE BEGIN;
          BEG
             .....;

          END;

        ...
        IF OK THEN BEGIN;
        ...

CLEANUP DECLARATIONS
The CLEANUP declaration  requires a list  of procedure names   following the
"CLEANUP" token.  Each procedure specified must be SIMPLE and have no formal
parameters.  The  specified procedures  will be  called at  the exit  of the
block that the  CLEANUP declaration occurs in.   They will be called  in the
order of their appearance  on the list, and  before any of the  variables of
the block are  deallocated.  NOTE: If  the block is  part of a  process (see
about  processes,  page  186)  that is  being  terminated  then  the cleanup
procedures will be called before the terminate is completed.

Cleanup  procedures  are  normally  used  in  connection  with  processes to
"cleanup" a block by terminating  the processes dependent on that  block (it
is an error to leave active a process that depends on an exited block).

REQUIREMENTS
The  user  may,  using  the  REQUIRE  construct,  specify  to  the  compiler
conditions which are required  to be true of the  execution-time environment
of  his  programs.  All  requirements  are legal  at  either  declaration or
statement  level.   The   requirements  fall  into   three  classifications,
described as follows:

Group 1 -- Space requirements --  STRINGSPACE, SYSTEMPDL, etc.

The inclusion of the specification  "REQUIRE 1000 STRINGSPACE"  will ensure
that at least 1000 words of storage will be available for storing  (the text
characters of) Strings when the program is run.  Similar provisions are made
for various  push-down stacks  used by the  execution-time routines  and the
compiled code.  If a parameter is specified twice, or if separately compiled
procedures are loaded (see page 25) then the sum of all  such specifications
will be used.   These parameters could also  be typed to the  loaded program
just before  execution (see page  242), but it  is often more  convenient to
specify  differences from  the standard  sizes in  the source  program.  Use
these specifications only if messages from the running program indicate that
the standard allocations are not sufficient.

Group 2 -- Other files --  LOADMODULE, LIBRARY, SOURCEFILE, etc.

The   inclusion   of  the   specification   REQUIRE   "PROCS1"  LOADMODULE,



"HELIB[1,3]" LIBRARY; would inform the Loader that the file  PROCS1.REL must
be  loaded  and the  library  HELIB.REL[1,3] searched  whenever  the program
containing the  specification is  loaded.  The  parameter for  both features
should be a string constant of  one of the above forms.  The  file extension
.REL is  the only  value permitted,  and is  therefore assumed;  the device,
name, and  ppn may be  specified.  TENEX users  should note that  the LOADER
restricts LOADMODULE  and LIBRARY file  names to 6  characters in  the main
name and 3 characters in the extension.

LOADMODULES (.REL files to  be loaded) may themselves contain  requests for
other LOADMODULES  and LIBRARYs.   LIBRARYs may  only contain  requests for
other  LIBRARYs.  The  LOADER  may do  strange things  with  files requested
twice.

Sail automatically places a request for the library SYS:LIBSAn (<SAIL>LIBSAn
on TENEX) [HLBSAn for /H compilations] in each main program, where n  is the
version number of the current Sail library of runtime routines.

The inclusion of REQUIRE "PREAMB.SAI" SOURCEFILE   will cause  the compiler
to  save the  state of  the  current input  file, then  begin  scanning from
PREAMB.  When PREAMB  is exhausted, Sail  will resume scanning  the original
file   on  the   line   directly  following   the   REQUIRE.   Commonly-used
declarations, particularly  EXTERNAL declarations  for libraries,  are often
put in a separate file which is then REQUIREd.

Restrictions: A SOURCEFILE  request must be  followed by a  semicolon (only
one per  REQUIREment), and must  be the last  text on the  line in  which it
appears.  SOURCEFILE switching must  not be specified from within  a DEFINE
body (see page 106).     SOURCEFILEs may be nested  to a depth of  about 10
levels.

The SEGMENTNAME, SEGMENTFILE specifications are currently  applicable only
to the SUAI "global model"  users of Sail.  They allow specification  of the
name of a  special non-sharable "HISEG",  and the name  of the file  used to
create this HISEG.  These  specifications may, like the  space REQUIREments,
be overridden by using the system REENTER command (see page 242).

Group 3 -- other - INITIALIZATION, VERSION

Before  the execution  of  a program,  Sail runs  through  an initialization
routine.  The user can specify  things that he wants done  at initialization
time by declaring an outer-block Procedure without arguments, then saying

     REQUIRE procedurename INITIALIZATION.

Require-initialization procedures are  run just before the  first executable
statement in  the outer  block of  the program.   They are  run in  order of
ascending phase number, and within each phase in the order the  compiler saw
the REQUIREs.  There are currently three user phases, numbered 0, 1,  and 2.
Phase 1 is the default if no phase is specified.    WARNING: you  should not

 Separately Compiled Procedures


Require  initialization  of a  procedure  which is  declared  inside another
procedure.

REQUIRE n VERSION (n a  non-zero integer) will flag the resultant  .REL file
as version n.  When a program loaded from several such RELfiles  is started,
the Sail allocation code will verify that all specified versions  are equal.
A non-fatal error message is generated if any disagree.  As much as will fit
of the version number is also stored in lh(.JBVER), where .JBVER is location
'137.

For other requirements, check  the index under the specific  condition being
Required.

COMMENT: You have probably noticed  that a great deal of prior  knowledge is
required for  proper understanding  of this  section.  For  more information
about storage  allocation, see  page 242 below.   The form  and use  of .REL
files and libraries are described in [TopHand].



2.5  Separately Compiled Procedures

When a program becomes extremely large it becomes useful to break it up into
several files which can be compiled separately.  This can be done in Sail by
preparing  one file  as  a main  program, and  one  or more  other  files as
programs each of which contains one  or more procedures to be called  by the
main program.  The main program must contain EXTERNAL declarations  for each
of the procedures declared in the other files.  (EXTERNAL  declarations have
no  procedure body.)   The non-main  program files  must have  the following
characteristics:

1)  All procedures to be called  from the main program (or  procedures in
    other files) must be qualified with the INTERNAL attribute  when they
    are   declared.   External   procedure  declarations   with  headings
    identical  to those  of the  actual declarations  must appear  in all
    those programs which call these procedures.

2)  These internal procedures must be uniquely identifiable by  the first
    six characters  of their identifiers.   In general, any  two internal
    procedure names  (or any  other Internal variables  in the  same core
    image)  with  the  same first  six  characters  will  cause incorrect
    linkages when the programs are loaded.

3)  The reserved word ENTRY, followed by a semi-colon, must be  the first
    item in the program (preceding  even the BEGIN for its  outer block).
    No starting address will be issued for a program containing  an Entry
    Specification.  Since no starting  address is present for  this file,
    entry to code  within it may only  be to the procedures  it contains.
    The statements in the outer block, if any, can never be executed.

 FORTRAN PROCEDURES


4)  Should  you  desire  your  separatedly  compiled  procedures   to  be
    collected into a  user library, include  a list of  their identifiers
    between the ENTRY  and the semi-colon  of the Entry  Specification of
    the program containing  those procedure declarations.  The  format of
    libraries is described in [TopHand].  The identifier(s)  appearing in
    the entry list may be any valid identifiers, but usually they will be
    the names of  the procedures contained in  the file.  No  checking is
    done to see if entry identifiers are ever really declared in the body
    of the program.

5)  Any variables (simple or array) which appear in the outer block  of a
    Separately  Compiled  Procedure   program  will  be  global   to  the
    procedures in  this program,  but not available  to the  main program
    (unless  they  are  themselves  connected  to  the  main  program  by
    Internal/External  declarations --  see below).   Non-LEAP  arrays in
    these outer  blocks will  always be  zero when  the program  is first
    loaded, but will  never be cleared as  others are by  restarting your
    program (see reinitialization, page 22).

Any  variable, procedure  or  label may  contain the  attribute  INTERNAL or
EXTERNAL in its declaration (ITEMS may  not -- items are part of  leap, page
151).  The INTERNAL attribute does not affect the storage assignment  of the
entity it represents,  nor does it  have any effect  on the behavior  of the
entity (or  the scope  of its identifier)  in the  file wherein  it appears.
However, its  address and (the  first six characters  of) its name  are made
available to the loader for satisfying External requests.  GOTO  an external
label is for wizards only.

No space is ever allocated for an External declaration.  Instead, a  list of
references to each External identifier  is made by the compiler.   This list
is  passed  to  the  loader  along with  the  first  six  characters  of the
identifier name.  (If there are no references then Sail ignores the External
declaration.)   When a matching  Internal name is found during  loading, the
loader places the associated  address in each of the  instructions mentioned
on the list.  No program inefficiency at all results  from External/Internal
linkages (belay  that --  references to External  arrays are  sometimes less
efficient).

The entity finally represented by an External identifier is  only accessible
within the scope of the External declaration.

FORTRAN PROCEDURES
For  a program  written in  either F40  or FORTRAN-10   to run  in  the Sail
environment, the following restrictions must be observed:

 ASSEMBLY LANGUAGE PROCEDURES


1)    It must be a SUBROUTINE or FUNCTION, not a main program.

2)    It must not execute any  FORTRAN I/O calls.  The UUO  structures of
      the two languages are not compatible.

3)    It must  be declared as  a Fortran Procedure  (see page 40)  in the
      Sail program which calls it.

The type bits required in  the argument addresses for Fortran  arguments are
passed correctly to these routines.

The Sail compiler will not produce a procedure to be called from FORTRAN.

ASSEMBLY LANGUAGE PROCEDURES
The following rules should be observed:

1)    The  ENTRY, INTERNAL,  and EXTERNAL  pseudo-ops should  be  used to
      obtain  linkages  for  procedure  names  and  "global" identifiers;
      remember that only six characters are used for these linkage names.

2)    Accumulators  F (currently  '12), P  (currently '17)  and  SP ('16)
      should be preserved over function calls.  P may be used as  a push-
      down pointer for arithmetic values and return addresses.  SP is the
      string stack pointer.  String  results are returned on  this stack.
      Arithmetic results are returned in AC 1.

3)    Those who  wish to provide  their own UUO  handlers or  to increase
      their core size should read the code.

There  are  no other  known  processors which  will  produce Sail-compatible
programs.

ALGOL STATEMENTS Syntax


                                 SECTION  3

                              ALGOL STATEMENTS




3.1  Syntax


<assignmentstatement>
    ::= <algebraicvariable> _
        <algebraicexpression>


<swapstatement>
    ::= <variable>  <variable>
    ::= <variable> SWAP <variable>


<conditionalstatement>
    ::= <ifstatement>
    ::= <ifstatement> ELSE <statement>


<ifstatement>
    ::= IF <booleanexpression> THEN <statement>

<gotostatement>
    ::= GO TO <labelidentifier>
    ::= GOTO <labelidentifier>
    ::= GO <labelidentifier>

<labelidentifier>
    ::= <identifier>

<forstatement>
    ::= FOR <algebraicvariable> _ <forlist>
        DO <statement>
    ::= NEEDNEXT <forstatement>


<forlist>
    ::= <forlistelement>
    ::= <forlist> , <forlistelement>


<forlistelement>
    ::= <algebraicexpression>
    ::= <algebraicexpression> STEP



        <algebraicexpression> UNTIL
        <algebraicexpression>
    ::= <algebraicexpression> STEP
        <algebraicexpression> WHILE
        <booleanexpression>


<whilestatement>
    ::= WHILE <booleanexpression> DO
        <statement>
    ::= NEEDNEXT <whilestatement>


<dostatement>
    ::= DO <statement> UNTIL
        <booleanexpression>


<casestatement>
    ::= <casestatementhead> <statementlist>
        <casestatementtail>
    ::= <casestatementhead>
        <numberedstatelist>
        <casestatementtail>


<casestatementhead>
    ::= CASE <algebraicexpression> OF BEGIN
    ::= CASE <algebraicexpression> OF BEGIN
        <blockname>


<casestatementtail>
    ::= END
    ::= END <blockname>


<statementlist>
    ::= <statement>
    ::= <statementlist> ; <statement>


<numberedstatelist>
    ::= [ <integerconstant> ] <statement>
    ::=  [ <integerconstant> ]
        <numberedstatelist>
    ::= <numberedstatelist> ; [
        <integerconstant> ] <statement>


<returnstatement>

 Semantics


    ::= RETURN
    ::= RETURN ( <expression> )


<donestatement>
    ::= DONE
    ::= DONE <blockname>


<nextstatement>
    ::= NEXT
    ::= NEXT <blockname>


<continuestatement>
    ::= CONTINUE
    ::= CONTINUE <blockname>


<procedurestatement>
    ::= <procedurecall>


<procedurecall>
    ::= <procedureidentifier>
    ::= <procedureidentifier> (
        <actualparameterlist> )


<actualparameterlist>
    ::= <actualparameter>
    ::= <actualparameterlist> ,
        <actualparameter>


<actualparameter>
    ::= <expression>
    ::= <arrayidentifier>
    ::= <procedureidentifier>


<safetystatement>
    ::= NOWSAFE <idlist>
    ::= NOWUNSAFE <idlist>

 ASSIGNMENT STATEMENTS SWAP ASSIGNMENT CONDITIONAL STATEMENTS


3.2  Semantics

ASSIGNMENT STATEMENTS
The assignment statement causes the value represented by an expression to be
assigned to  the variable appearing  to the left  of the  assignment symbol.
You will see later (page 49) that  one value may be assigned to two  or more
variables through the use of two or more assignment symbols.   The operation
of the assignment statement proceeds in the following order:

   a) The subscript expressions of the left part variable (if any  - Sail
      defines  "variable"  to  include  both  array  elements  and simple
      variables)  are  evaluated  from  left  to  right  (see  Expression
      Evaluation Rules, page 50).

   b) The expression is evaluated.

   c) The value of the expression is assigned to the left  part variable,
      with subscript expressions, if any, having values as  determined in
      step a.

This ordering of operations may usually be disregarded.  However  it becomes
important  when  expression assignments  (page  49) or  function  calls with
reference parameters appear anywhere in the statement.  For example,  in the
statements:

        K_3;
        A[K]_3+(K_1);

A[3] will  receive the  value 4 using  the above  algorithm.  A[1]  will not
change.

Any  algebraic  expression  (REAL,  INTEGER  (BOOLEAN),  or  STRING)  may be
assigned to any variable of algebraic type.  The resultant type will be that
of the left part variable.   The conversion rules for  assignments involving
mixed types are identical to the conversion rules for combining  mixed types
in algebraic expressions (see  page 46).

SWAP ASSIGNMENT
The  operator causes the value of the variable on the left hand side  to be
exchanged with the value of the variable on the right hand side.  Arithmetic
(REALINTEGER)  type  conversions are  made,  if necessary;  any  other type
conversions  are invalid.   Note that  the   operator  may not  be  used in
assignment expressions.

CONDITIONAL STATEMENTS
These statements provide a means whereby the execution of a statement,  or a
series  of statements,  is  dependent on  the  logical value  produced  by a
Boolean expression.

A Boolean expression is an algebraic expression whose use implies that it is

 AMBIGUITY IN CONDITIONAL STATEMENTS GO TO STATEMENTS


to be tested as a logical (truth) value. If the value of the expression is 0
or NULL then the expression  is a FALSE boolean expression, otherwise  it is
TRUE.  See about type conversion, page 46.

IF STATEMENT  - The statement following the operator THEN (the  "THEN part")
is  executed  if  the  logical value  of  the  Boolean  expression  is TRUE;
otherwise, that statement is ignored.

IF ... ELSE STATEMENT  - If the Boolean expression is true, the  "THEN part"
is executed and the statement following the operator ELSE (the  "ELSE part")
is ignored.  If the Boolean expression is FALSE, the "ELSE part" is executed
and the "THEN part" is ignored.

AMBIGUITY IN CONDITIONAL STATEMENTS
The syntax given here for conditional statements does not fully  explain the
correspondences  between  THEN-ELSE pairs  when  conditional  statements are
nested.   An ELSE  will  be understood  to match  the  immediately preceding
unmatched THEN.  Example:

      COMMENT DECIDE WHETHER TO GO TO WORK;

      IF WEEKEND THEN
          IF GIANTSONTV THEN BEGIN
              PHONEEXCUSE ("GRANDMOTHER DIED");
              ENJOY (GAME);
              SUFFER (CONSCIENCEPANGS)
          END
          ELSE IF REALLYSICK THEN BEGIN
              PHONEEXCUSE ("REALLY SICK");
              ENJOY (0);
              SUFFER (AGONY)
          END
          ELSE GO TO WORK;

GO TO STATEMENTS
Each of the three forms of the  Go To statement (GO, GOTO, GO TO)  means the
same  thing --  an unconditional  transfer  is to  be made  to  the "target"
statement labeled by the  label identifier.  The following rules  pertain to
labels:

1)   All label identifiers used in a program must be declared.


2)   The  declaration of  a label  must be  local to  the  block immediately
     surrounding the  statement it identifies  (see exception  below).  Note
     that compound statements  (BEGIN-END pairs containing  no declarations)
     are not blocks.  Therefore the block

            BEGIN "B1"
             INTEGER I, J; LABEL L1;
             ...
             IF BE3 THEN BEGIN "C1"
              ...
              L1:  ...
              ...
             END "C1";
             ...
             GO TO L1
            END "B1"

     is legal.

3)   Rule  2  can  be  violated   if  the  inner  block(s)  have   no  array
     declarations.  E.g.:

       Legal             Illegal

       BEGIN "B1"        BEGIN "B1"
       INTEGER I, J;      INTEGER I, J;
       LABEL L1;         LABEL L1;
       ...               ...
          BEGIN "B2"        BEGIN "B2"
          REAL X;           REAL ARRAY X [1:10];
          ...               ...
          L1: ...           L1: ...
          ...               ...
          END "B2";         END "B2";
       GO TO L1;         GO TO L1;
       END "B1"          END "B1"

4)   No Go  To statement  may specify  a transfer  into a  FOREACH statement
     (FOREACH statements are part of LEAP -- page 151),  or into complicated
     For loops (those with For Lists or which contain a NEXT statement).

Labels will seldom be needed for debugging purposes.  The block name feature
(see page  246) and the  listing feature which  associates with  each source
line  the octal  address of  its corresponding  object code  (see  page 237)
should provide enough information to find things easily.

Many program loops coded with  labels can be alternatively expressed  as For
or  While loops,  augmented by  DONE, NEXT,  and CONTINUE  statements.  This
often  results  in a  source  program whose  organization  is  somewhat more
transparent, and an object program which is more efficient.

FOR STATEMENTS


FOR STATEMENTS
For,  Do  and While  statements   provide  methods for  forming  loops  in a
program.  They allow  the repetitive execution of  a statement zero  or more
times.  These statements will be  described by means of Sail  programs which
are functionally equivalent but which demonstrate better the actual order of
processing.  Refer to these equations for any questions you might have about
what gets evaluated when, and how many times each part is evaluated.

Let  VBL  be  any  algebraic  variable,  AE1,   ...   ,  AE8  any  algebraic
expressions,  BE  a  Boolean  expression, TEMP  a  temporary  location,  S a
statement.  Then the following Sail statements are equivalent.

Using For Statements:

        FOR VBL _ AE1, AE2, AE3 STEP
            AE4 UNTIL AE5, AE6 STEP AE7 WHILE
            BE, AE8 DO S;

Equivalent formulation without For Statements:

        VBL_AE1;
        S;
        VBL_AE2;
        S;

        VBL_AE3; Comment STEP-UNTIL loop;
  LOOP1:  IF (VBL-AE5) * SIGN(AE4)  0 THEN
        BEGIN
            S;
            VBL_VBL+AE4;
            GO TO LOOP1
        END;

        VBL_AE6; Comment STEP-WHILE loop;
  LOOP2:  IF BE THEN BEGIN
            S;
            VBL_VBL+AE7;
            GO TO LOOP2
        END;

        VBL_AE8;
        S;

If AE4 (AE7) is an unsubscripted variable then changing its value within the
loop will cause  the new value  to be used for  the next iteration.   If AE4
(AE7)  is a constant or an expression requiring evaluation of  some operator
then the value used for the step element will remain constant throughout the
execution of the  For Statement.  If  AE5 is an  expression then it  will be
evaluated  before  each  iteration,   so  watch  this  possible   source  of
inefficiency.

 WHILE STATEMENT DO STATEMENT


Now consider the For Statement:

        FOR VBL_AE1 STEP CONST UNTIL AE2 DO S;

where const is  a positive constant.  The  compiler will simplify  this case
to:

        VBL_AE1;
  LOOP3:  IF VBL  AE2 THEN BEGIN
            S;
            VBL_VBL+CONST;
            GO TO LOOP3
        END;

If CONST is negative then the line at LOOP3 would be:

 LOOP3: IF VBL  AE2 THEN BEGIN

The value of VBL when execution of the loop is terminated, whether it  be by
exhaustion  of the  For  list or  by  execution of  a  DONE, NEXT  or  GO TO
statement (see page  37, page 38,  page 32), is  the value last  assigned to
it using the algorithm above.  This value is therefore always well-defined.

The statement S may  contain assignment statements or procedure  calls which
change the value of VBL.  Such a statement behaves the same way it  would if
inserted at the corresponding point in the equivalent loop described above.

WHILE STATEMENT
The statement:

       WHILE  BE DO S;

is equivalent to the statements:

     LOOP:   IF BE THEN BEGIN
               S;
               GO TO LOOP
           END;

DO STATEMENT
The statement:

       DO  S UNTIL BE;

is equivalent to the sequence:

    LOOP:  S;
           IF BE THEN GO TO LOOP;

 CASE STATEMENTS RETURN STATEMENT


CASE STATEMENTS
The statement:

       CASE AE OF BEGIN   S0; S1; S2 ... Sn  END

is functionally equivalent to the statements:

       TEMP_AE;
       IF TEMP<0 THEN ERROR
           ELSE IF TEMP = 0 THEN S0
           ELSE IF TEMP = 1 THEN S1
           ELSE IF TEMP = 2 THEN S2
           ...
           ELSE IF TEMP = n THEN Sn
           ELSE ERROR;

For  applications  of   this  type  the   CASE  statement  form   will  give
significantly more efficient code than the equivalent If statements.  Notice
that dummy statements may be  inserted for those cases which will  not occur
or for which no entries are necessary.  For example,

    CASE AE OF BEGIN  S0; ; ; S3; ; ; S6; END

provides for no actions when AE is 1, 2, 4, 5, or 7.  When AE is 0, 3,  or 6
the  corresponding  statement  will  be  executed.   However,  slightly more
efficient code may  be generated with a  second type of Case  statement that
numbers each of its statement with [n] where n is an integer  constant.  The
above example using this type of Case statement is then:

    CASE AE OF BEGIN [3] S3; [0] S0; [6] S6 END;

All  the statements  must be  numbered,  and the  numbers must  all  be non-
negative integer constant expressions, although they may be in any order.

Multiple case numbers may precede each statement; the statement  is executed
for any one of the numbers specified.  The following two CASE statements are
equivalent:

    CASE AE OF BEGIN [4] [1] S41; [2] [3] S23 END;
    CASE AE OF BEGIN [1] S41; [2] S23;
        [3] S23; [4] S41 END;

Block names (i.e. any string constant)  may be used after the BEGIN  and END
of  a Case  statement  with the  same effect  as  block names  on  blocks or
compound statements.  (See about block names on page 6).

RETURN STATEMENT
This statement is invalid if it appears outside a procedure declaration.  It
provides for  an early return  from a Procedure  execution to  the statement
calling  the  Procedure.   If  no  return  statement  is  executed  then the

 DONE STATEMENT


Procedure will return after  the  last statement representing  the procedure
body is executed (see page 17).

An  untyped Procedure  (see page  39) may  not return  a value.   The return
statement for this kind of Procedure consists merely of the word RETURN.  If
an argument  is given  then it  will cause  the compiler  to issue  an error
message.

A typed Procedure (see page 54) must return a value as it executes  a return
statement.  If no  argument is present an  error message will be  given.  If
the Procedure  has an algebraic  type then any  algebraic expression  may be
returned  as  its value;  type  conversion  will be  performed  in  a manner
described on page 46.

If  no RETURN  statement is  executed in  a typed  Procedure then  the value
returned is undefined.

DONE STATEMENT
The statement  containing only the  word DONE may  be used to  terminate the
execution  of  a FOR,  WHILE,  or DO  (also  FOREACH -  see  page  166) loop
explicitly.  Its operation can most  easily be seen by means of  an example.
The statement

        FOR I_1 STEP 1 UNTIL n DO BEGIN
            S;
            ...
            IF BE THEN DONE;
            ...
        END

is equivalent to the statement

        FOR I_1 STEP 1 UNTIL n DO BEGIN
            S;
            ...
            IF BE THEN GO TO EXIT;
            ...
        END;
    EXIT:

In either case the value of  I is well-defined after the statement  has been
executed (see page 35).

The DONE  statement will  only cause an  escape from  the innermost  loop in
which it appears, unless a  block name follows "DONE".  The block  name must
be the name of a block  or compound statement (a "Loop Block") which  is the
object statement of  some FOR, WHILE, or  DO statement in which  the current
one is nested.  The effect is to terminate all loops out to  (and including)
the Loop Block, continuing with the statement following this outermost loop.
For example:

 NEXT STATEMENT CONTINUE STATEMENT



   WHILE TRUE DO BEGIN "B1"
       ...
       IF OK THEN DO BEGIN "B2"
           ...
           FOR I_1 STEP 1 UNTIL K DO
               IF A[I]=FLAGWORD THEN DONE "B1";
           ...
           END "B2" UNTIL COWSCOMEHOME;
       ...
       END "B1";

Here the block named "B1" is the "loop block".

NEXT STATEMENT
A Next statement is valid only  in a For Statement or a While  Statement (or
Foreach - see  page 166).  Processing of  the loop statement  is temporarily
suspended.  When the NEXT statement  appears in a For  loop, the  next value
is obtained from the For List and assigned to the controlled  variable.  The
termination test is  then made.  If  the termination condition  is satisfied
then control  is passed to  the statement following  the For  Statement.  If
not,  control  is  returned  to  the  inner  statement  following  the  NEXT
statement.  In While and Do loops, the termination condition is  tested.  If
it is satisfied, execution of the loop terminates.  Otherwise it  resumes at
the statement within the loop following the NEXT statement.

Unless a  block name follows  NEXT, the innermost  loop containing  the NEXT
statement  is used  as  the "Loop  Block"  (see page  37).   The terminating
condition for the loop block is  checked.  If the condition is met  then all
inner loops are  terminated (in DONE fashion)  as well.  If  continuation is
indicated then no inner-loop FOR-variable or WHILE-condition will  have been
affected by the NEXT code.

The reserved word NEEDNEXT must  precede  FOR or WHILE in the  "Loop Block",
and must not appear between this block and the NEXT statement.  Example:

   NEEDNEXT WHILE EOF DO BEGIN
       S_INPUT(1,1);
       NEXT;
       Comment check EOF and terminate if TRUE;
       T_INPUT(1,3);
           PROCESSINPUT(S,T);
       END;

CONTINUE STATEMENT
The Continue statement  is valid in only  those contexts valid for  the DONE
statement (see  page 37);  the "Loop Block"  is determined  in the  same way
(i.e., implicitly or by specifying a block name).  All loops out to the Loop
Block are terminated as if DONE had been requested.  Control  is transferred
to  a  point  inside the  loop  containing  the Loop  Block,  but  after all
statements in the loop.  Example:

 PROCEDURE STATEMENTS



          FOR I_1 STEP 1 UNTIL N DO BEGIN
                  ...
                  CONTINUE;
                  ...
          END

is semantically equivalent to:

          FOR I_1 STEP 1 UNTIL N DO BEGIN
                  LABEL CONT;
                  ...
                  GO TO CONT;
                  ...
           CONT:
          END

PROCEDURE STATEMENTS
A Procedure statement is used  to invoke the execution of a   Procedure (see
page  17).   After  execution  of  the  Procedure,  control  returns  to the
statement immediately following  the Procedure statement.   Sail  does allow
you to  use typed  Procedures as procedure  statements.  The  value returned
from the Procedure is simply discarded.

The  actual  parameters  supplied  to  a  Procedure  must  match  the formal
parameters  described  in  the  procedure  declaration,  modulo   Sail  type
conversion.  Thus one may supply an integer expression to a real formal, and
type conversion will be performed as on page 46.

If  an actual  parameter  is passed  by VALUE  then  only the  value  of the
expression is given to the Procedure.  This value may be changed or examined
by the Procedure, but this will  in no way affect any of the  variables used
to evaluate the actual  parameters.  Any algebraic expression may  be passed
by value.  Neither Arrays nor Procedures may be passed by value (use ARRBLT,
page 94, to copy arrays).   See the default declarations for  parameters  in
page 21.

If an actual parameter is passed by REFERENCE then its address is  passed to
the  Procedure.  All  accesses to  the value  of the  parameter made  by the
Procedure are  made indirectly through  this address.  Therefore  any change
the Procedure makes  in a reference parameter  will change the value  of the
variable which was used as  an actual parameter.  This is  sometimes useful.
However, if it  is not intended,  use of this  feature can also  be somewhat
confusing as well as moderately inefficient.  Reference parameters should be
used only where needed.

Variables, constants, Procedures, Arrays, and most expressions may be passed
by reference.  No String expressions (or String constants) may  be reference
parameters.

 PROCEDURES AS ACTUAL PARAMETERS FORTRAN PROCEDURES


If an expression  is passed by reference then its value is first placed in a
temporary location;  a constant passed  by reference is  stored in  a unique
location.   The  address  of  this  location  is  passed  to  the Procedure.
Therefore, any values changed  by the Procedure via reference  parameters of
this form will be inaccessible to the user after the Procedure call.  If the
called program  is an  assembly language routine  which saves  the parameter
address, it is dangerous to pass expressions  to it, since this address will
be used  by the compiler  for other temporary  purposes.  A  warning message
will be printed when expressions are called by reference.

The type of each actual parameter passed by reference must match that of its
corresponding formal parameter, modulo Sail type conversion.   The exception
is reference string  formals, which must  have  string variables  (or string
array elements) passed to them.    If an algebraic type mismatch  occurs the
compiler will create a temporary variable containing the converted value and
pass the address of this  temporary as the parameter, and a  warning message
will be printed.   An exception is made for Fortran calls (see page 40).

PROCEDURES AS ACTUAL PARAMETERS
If an actual parameter to a Procedure PC is the name of a Procedure  PR with
no arguments then one of three things might happen:

    1)If the corresponding  formal parameter requires  a value of  a type
      matching that of  PR (in the loose  sense given above in  page 39),
      the Procedure is evaluated and  its value is sent to  the Procedure
      PC.

   2) If the  formal parameter  of PC requires  a reference  Procedure of
      identical type,  the address of  PR is passed  to PC as  the actual
      parameter.

   3) If  the  formal  parameter  requires  a  reference   variable,  the
      Procedure is evaluated, its  result stored, and its  address passed
      (as with expressions in the previous paragraph) as the parameter.

If  a Procedure  name followed  by actual  parameters appears  as  an actual
parameter  it  is  evaluated   (see  functions,  page  54).   Then   if  the
corresponding  formal  parameter  requires  a  value,  the  result  of  this
evaluation  is passed  as  the actual  parameter.  If  the  formal parameter
requires a reference to a value, it is called as a reference expression.

FORTRAN PROCEDURES
If the Procedure being called is a Fortran Procedure, all  actual parameters
must be of type INTEGER  (BOOLEAN) or REAL.  All such parameters  are passed
by  reference,  since Fortran  will  only  accept that  kind  of  call.  For
convenience, any  constant or expression  used as an  actual parameter  to a
Fortran Procedure is  stored in a temporary  cell whose address is  given as
the reference actual parameter.

It was explained in page 17 that formal parameters need not be described for

 NOWSAFE and NOWUNSAFE


Fortran Procedures.  This allows a program to call a Fortran  Procedure with
varying numbers of arguments.  No type conversion will be performed for such
parameters, of course.  If type conversion is desired, the  formal parameter
declarations should be included  in the Fortran procedure  declaration; Sail
will use them if they are present.

To pass an Array to Fortran, mention the address of its first  element (e.g.
A[0], or B[1, 1]).

NOWSAFE and NOWUNSAFE
The  NOWSAFE and  NOWUNSAFE statements  both take  a list  of  Array names
(names only - no indices) following them.  From a NOWSAFE until the  end of
the  program or  the next  NOWUNSAFE, the  specified arrays  will  not have
bounds checking code emitted for them.  If an array has had a  NOWSAFE done
on it, or has been declared SAFE, NOWUNSAFE will cause bounds checking code
to be  emitted until  the array  is made  safe again  (if ever).   Note that
NOWSAFE and NOWUNSAFE are  compile time statements.  "IF BE  THEN NOWSAFE
..." will not work.

ALGOL EXPRESSIONS Syntax


                                 SECTION  4

                             ALGOL EXPRESSIONS




4.1  Syntax


<expression>
    ::= <simpleexpression>
    ::= <conditionalexpression>
    ::= <assignmentexpression>
    ::= <caseexpression>


<conditionalexpression>
    ::= IF <booleanexpression> THEN
        <expression> ELSE <expression>

<assignmentexpression>
    ::= <variable> _ <expression>

<caseexpression>
    ::= CASE <algebraicexpression> OF (
        <expressionlist> )


<expressionlist>
    ::= <expression>
    ::= <expressionlist> , <expression>


<simpleexpression>
    ::= <algebraicexpression>
    ::= <leapexpression>


<booleanexpression>
    ::= <expression>


<algebraicexpression>
    ::= <disjunctiveexpression>
    ::= <algebraicexpression> 
        <disjunctiveexpression>
    ::= <algebraicexpression> OR
        <disjunctiveexpression>



<disjunctiveexpression>
    ::= <negatedexpression>
    ::= <disjunctiveexpression> 
        <negatedexpression>
    ::= <disjunctiveexpression> AND
        <negatedexpression>


<negatedexpression>
    ::=  <relationalexpression>
    ::= NOT <relationalexpression>
    ::= <relationalexpression>


<relationalexpression>
    ::= <algebraicrelational>
    ::= <leaprelational>


<algebraicrelational>
    ::= <boundedexpression>
    ::= <relationalexpression>
        <relationaloperator>
        <boundedexpression>


<relationaloperator>
    ::= <
    ::= >
    ::= =
    ::= 
    ::= 
    ::= 
    ::= LEQ
    ::= GEQ
    ::= NEQ


<boundedexpression>
    ::= <addingexpression>
    ::= <boundedexpression> MAX
        <addingexpression>
    ::= <boundedexpression> MIN
        <addingexpression>


<addingexpression>
    ::= <term>
    ::= <addingexpression> <addoperator>
        <term>



<addingoperator>
    ::= +
    ::= -
    ::= LAND
    ::= LOR
    ::= EQV
    ::= XOR


<term>
    ::= <factor>
    ::= <term> <multoperator> <factor>


<multoperator>
    ::= *
    ::= /
    ::= %
    ::= LSH
    ::= ASH
    ::= ROT
    ::= MOD
    ::= DIV
    ::= &


<factor>
    ::= <primary>
    ::= <primary> ^ <primary>


<primary>
    ::= <algebraicvariable>
    ::= - <primary>
    ::= LNOT <primary>
    ::= ABS <primary>
    ::= <stringexpression> [ <substringspec> ]
    ::= 
    ::= INF
    ::= <constant>
    ::= <functiondesignator>
    ::= LOCATION  ( <locspecifier> )
    ::= ( <algebraicexpression> )


<stringexpression>
    ::= <algebraicexpression>


<substringspec>

 Type Conversion


    ::= <algebraicexpression> TO
        <algebraicexpression>
    ::= <algebraicexpression> FOR
        <algebraicexpression>


<functiondesignator>
    ::= <procedurecall>


<locspecifier>
    ::= <variable>
    ::= <arrayidentifier>
    ::= <procedureidentifier>
    ::= <labelidentifier>


<algebraicvariable>
    ::= <variable>




4.2  Type Conversion

Sail automatically converts between the data types Integer, Real, String and
Boolean.  The following table  illustrates by description and  example these
conversions.   The  data type  boolean  is identical  to  integer  under the
mapping TRUE0 and FALSE=0.




F |To
r |
o | INTEGER         REAL            STRING
m|
I |               | Left justify  | Make a string
N |               | and raise to  | of 1 character
T |               | appropriate   | with the low
E |               | power.        | 7 bits for its
G |               | 13451.345@3  | ASCII code.
E |               | -678-6.78@2  | 48  "0"
R|||
  |               |               |
R | Take greatest |               | Convert to in-
E | integer.      |               | teger, then to
A | 1.345@2  134 |               | string.
L | -6.71@1  -68 |               | 4.8@1  "0"
  | 2.3@-2  0    |               | 4.899@1  "0"
|||
  | The ASCII code| Convert to in-|
S | for the first | teger then    |
T | character of  | to real.      |
R | string.       |               |
I | "0SUM" 48    | "0SUM" 4.8@1 |
N | NULL  0      | NULL  0      |
G|||

NOTES: The  NULL string is  converted to 0,  but 0 is  converted to  the one
character string with the ASCII code of 0.  If an integer requires more than
27 bits  of precision (2^27  = 134217728) then  some low  order significance
will be lost  in the conversion to  real; otherwise, conversion to  real and
then back  to integer will  result in  the same integer  value.   If  a real
number has magnitude greater than 2^35 - 2^8 (=34359738112)  then conversion
to integer will  produce an invalid result.   UUOFIX does no  error checking
for this case; KIFIX and FIXR will set Overflow and Trap 1.

The default instruction compiled for  a real to integer conversion is  a UUO
which  computes  FLOOR (x),  the greatest  integer  function.   This  can be
changed with the /A switch (page 237) to one of several  other instructions.
For  real  to  integer  conversion  the  choices  are   UUOFIX(opcode  003),
KIFIX(122)  and FIXR(126);  the effect  of each  is shown  in  the following
table.

        real    UUOFIX  KIFIX   FIXR
         1.4     1       1       1
         1.5     1       1       2
         1.6     1       1       2
        -1.4    -2      -1      -1
        -1.5    -2      -1      -1
        -1.6    -2      -1      -2



UUOFIX  is  the default.   In  mathematical  terms, UUOFIX (x)=FLOOR (x)=[x]
where [x] is the traditional notation for the greatest integer less  than or
equal to x.  This UUO  requires execution of 18.125 instructions  (32 memory
references) on the average.   Many FORTRANs use the function  implemented by
KIFIX;   KIFIX (x)=SIGN (x)*FLOOR (ABS (x)).    Many   ALGOLs    use   FIXR;
FIXR (x)=FLOOR (x+0.5).  Note that FIXR (-1.5) is not equal to -FIXR (1.5).

For integer to real conversion the choices are UUOFLOAT(002)  and FLTR(127).
FLTR  rounds  while  UUOFLOAT  (the default)  truncates.   It  only  makes a
difference when the magnitude of the integer being converted is greater than
134217728.   In  such  cases  it  is  always  true  that  UUOFLOAT (i)i and
FLTR (i)i.  UUOFLOAT merely truncates after normalization, while  FLTR adds
+0.5 lsb  and then  truncates.  Most  users will  never see  the difference.
UUOFLOAT takes 18.625 instructions (32 memory references) on the average.

[For integer to  real conversion involving  a SHORT quantity,  FSC ac,233 is
used.  At SUAI  real to integer conversion  involving a SHORT  quantity uses
KAFIX ac,233000; as  this manual went  to press KAFIX  was simulated  by the
system and was very expensive.]

The  binary arithmetic,  logical, and  String operations  which  follow will
accept combinations  of arguments of  any algebraic types.  The type  of the
result  of such  an operation  is  sometimes dependent  on the  type  of its
arguments and sometimes fixed. An  argument may be converted to  a different
algebraic  type  before the  operation  is performed.   The  following table
describes the results of the arithmetic and logical operations given various
combinations of Real and Integer  inputs. ARG1 and ARG2 represent  the types
of  the  actual  arguments.  ARG1' and  ARG2'  represent  the  types  of the
arguments after any necessary conversions have been made.

Beware: automatic  type conversion  can be a  curse as  well as  a blessing.
Study  the conversion  rules carefully;  note that  Sail has  three division
operators, %, DIV, and /.

 Semantics CONDITIONAL EXPRESSIONS



OPERATIONARG1 ARG2 ARG1'ARG2'RESULT

+ -      INT  INT  INT  INT  INT*
* ^ %	 REAL INT  REAL REAL REAL
MAX MIN  INT  REAL REAL REAL REAL
         REAL REAL REAL REAL REAL

LAND LOR INT  INT  INT  INT  INT
EQV XOR  REAL INT  REAL INT  REAL
         INT  REAL INT  REAL INT
         REAL REAL REAL REAL REAL

LSH ROT  INT  INT  INT  INT  INT
ASH      REAL INT  REAL INT  REAL
         INT  REAL INT  INT  INT
         REAL REAL REAL INT  REAL

/        INT  INT  REAL REAL REAL
         REAL INT  REAL REAL REAL
         INT  REAL REAL REAL REAL
         REAL REAL REAL REAL REAL

MOD DIV  INT  INT  INT  INT  INT
         REAL INT  INT  INT  INT
         INT  REAL INT  INT  INT
         REAL REAL INT  INT  INT

* For the operator  ^, ARG2' and RESULT are  REAL unless ARG2 is  a positive
integer constant.



4.3  Semantics

CONDITIONAL EXPRESSIONS
A conditional expression returns one of two possible values depending on the
logical truth value  of the Boolean  expression.  If the  Boolean expression
(BE) is true, the  value of the conditional  expression is the value  of the
expression following the delimiter THEN.  If BE is false, the other value is
used.  If both expressions are of an algebraic type, the precise type of the
entire conditional expression  is that of  the "THEN part".   In particular,
the "ELSE  part" will be  converted to  the type of  the "THEN  part" before
being  returned as  the  value of  the conditional  expression.   Reread and
understand the last sentence.

Unlike  the nested  If  statement problem,  there  can be  no  ambiguity for
conditional  expressions,  since  there  is  an  ELSE  part  in  every  such
expression.  Example:

 ASSIGNMENT EXPESSIONS CASE EXPRESSIONS SIMPLE EXPRESSIONS



   FOURTHDOWN (YARDSTOGO,YARDLINE,
        IF YARDLINE < 70 THEN PUNT ELSE
        IF YARDLINE < 90 THEN FIELDGOAL ELSE
        RUNFORIT)

ASSIGNMENT EXPESSIONS
The somewhat weird syntax for an assignment expression (it is  equivalent to
that for an assignment statement) is nonetheless accurate: the  two function
identically as far as the new value of the left part variable  is concerned.
The difference is that the value of this left part variable is also retained
as the value of the entire expression.  Assuming that the  assignment itself
is legal  (following the  rules given  in page  31 above),  the type  of the
expression  is  that of  the  left  part variable.   This  variable  may now
participate in any surrounding expressions  as if it had been given  its new
value in a separate statement on the previous line.  Only the _  operator is
valid in assignment expressions.  The  operator is valid only  at statement
level.  Example:

        IF (K_K+1) < 30 THEN K_0 ELSE K_K+1;

CASE EXPRESSIONS
The  expression

       CASE AE OF (E0, E1, E2, ... , En)

is equivalent to:

        IF AE=0 THEN E0
            ELSE IF AE=1 THEN E1
            ELSE IF AE=2 THEN E2
            ...
            ELSE IF AE=n THEN En
            ELSE ERROR

The type of the  entire expression is therefore that  of E0.  If any  of the
expressions E1  ...   En cannot be  fit into this  mold an error  message is
issued by  the compiler.   Case expressions differ  from Case  statements in
that one may not use the [n] construct to number the expressions.  Example:

    OUT (TTY, CASE ERRNO OF ("BAD DIRECTORY",
          "IMPROPER DATA MODE",
          "UNKNOWN I/O ERROR",
          ...
          "COMPUTER IN BAD MOOD"));

SIMPLE EXPRESSIONS
Simple expressions are simple only  in that they are not  conditional, case,
or assignment expressions.  There are in fact some exciting  complexities to
be discussed with respect to simple expressions.

 PRECEDENCE OF ALGEBRAIC OPERATORS EXPRESSION EVALUATION RULES  OR


PRECEDENCE OF ALGEBRAIC OPERATORS
The binary  operators in  Sail generally  follow "normal"  precedence rules.
That is, exponentiations are performed before multiplications  or divisions,
which in  turn are  performed before additions  and subtractions,  etc.  The
bounding operators MAX  and MIN are  performed after these  operations.  The
logical connectives  and ,  when they occur, are performed last  ( before
).   The order  of operation  can be  changed by  including  parentheses at
appropriate points.

In an expression where several operators of the same precedence occur at the
same level, the operations are  performed from left to right.  See   page 50
for special evaluation rules for logical connectives.

        TABLE OF PRECEDENCE

        ^
        * / % & MOD DIV LSH ROT ASH
        + -   LAND LOR
        MAX MIN
        =  <  >  LEQ GEQ NEQ
         AND
         OR

EXPRESSION EVALUATION RULES
Sail does not evaluate expressions in a strictly left-to-right  fashion.  If
we are not constrained to  a left-to-right evaluation, (as is ALGOL  60), we
can in some  cases produce considerably better  code than a  strict left-to-
right  scheme  could  achieve.   Intuitively,  the  essential  features (and
pitfalls) of this evaluation rule can be illustrated by a simple example:

        b _ 2.6 ;
        c _ b + (b _ b/2);

The second statement is executed as  follows: divide b by 2 and  assign this
value (1.3) to b.  Add this value to b and assign the sum to c.  Thus c gets
2.6.  If the expressions were evaluated in a strictly  left-to-right manner,
c would get  2.6 + 1.3.

The evaluation scheme can be stated quite simply:  code is generated for the
operation represented by  a BNF production when   the reduction of  that BNF
production takes  place.  That is,  b + (b _ b/2) isn't reduced  until after
(b _ b/2) is reduced, so the smaller expression gets done first.

"" (OR)
If  an  algebraic  expression  has  as  its  major  connective  the  logical
connective "", the expression has the logical value TRUE  (arithmetic value
some  non-zero  integer)  if  either  of  its  conjuncts   (the  expressions
surrounding the  "") is  true; FALSE  otherwise.  The  reserved word  OR is
equivalent to the symbol  "".   AB does NOT  produce the bit-wise Or  of A
and B if they are  algebraic expressions.  Truth values combined  by numeric

  AND  NOT <>= RELATIONS


operators will in general be meaningless (use the operators LOR and LAND for
bit operations).

The  user  should  be  warned  that  in  an  expression  containing  logical
connectives, only enough of the expression is evaluated (from left to right)
to uniquely determine its truth value.  Thus in the expression

       (J<3  (K_K+1) > 0),

K will not be incremented if J is less than 3 since the entire expression is
already known to be true.  Conversely in the expression

       (X 0  SQRT(X)>2)

there is  never any danger  of attempting  to extract the  square root  of a
negative X, since the failure of the first test testifies to the  falsity of
the entire expression -- the SQRT routine is not even called in this case.

"" (AND)
If  a  disjunctive  expression  has as  its   major  connective  the logical
connective "",  the expression has  the logical value  TRUE if both  of its
disjuncts are TRUE; FALSE otherwise.  Again, if the first disjunct  is FALSE
a  logical value  of FALSE  is obtained  for the  entire  expression without
further evaluation. The reserved word AND is equivalent to "".

"" (NOT)
The  unary  Boolean operator    applied  to an  argument  BE  (a relational
expression, see Syntax) has the value  TRUE if BE is false, and FALSE  if BE
is true.   Notice that A  is not the  bitwise complement of  A, if A  is an
algebraic value.  If used as an  algebraic value, A is simply 0 if  A0 and
some non-zero  Integer otherwise.   The reserved word  NOT is  equivalent to
"".

"<>=" (RELATIONS)
If any of the binary  relational operators is encountered, code  is produced
to convert any String arguments to Integer numbers.  Then type conversion is
done as it is for the + operations (see page 46).  The values  thus obtained
are compared for the indicated condition.  A Boolean value TRUE or  FALSE is
returned as the value of  the expression.  Of course, if this  expression is
used  in  subsequent  arithmetic  operations,  a  conversion  to  integer is
performed to obtain an integer value.  The reserved words LEQ, GEQ,  NEQ are
equivalent to "", "", "" respectively.

The syntax E1 RELOP1 E2 RELOP2  E3 where E1, E2, and E3 are  expressions and
RELOP1, RELOP2  are relational  operators, is  specially interpreted  as (E1
RELOP1 (T_E2))  (T RELOP2  E3).  The compiler can sometimes  produce better
code when the special syntax is used.  Thus a bounds check may be written IF
L<I<U THEN ... . RELOP1 and RELOP2 may be any relational operators, and need
not be in transitive order.  The following are equivalent:

 MAX MIN +- ADDITION SUBTRACTION LAND LOR XOR EQV LNOT */% MULTIPLICATION DIVISION DIV MOD ASH LSH ROT



        IF A < X > B THEN   ...         and
        IF X > (A MAX B) THEN   ...

MAX MIN
A MAX B (where  A and B are appropriate  expressions -- see the  Syntax) has
the  value  of  the larger  of  A  and B  (in  the  algebraic  sense).  Type
conversions are performed as if the operator were `+'.   `0 MAX X MIN 10' is
X if 0X10, 0 if X<0, 10 if X>10.

"+-" (ADDITION AND SUBTRACTION)
The  +  and -  operators  will  do integer  addition  (subtraction)  if both
arguments are integers (or  converted to integers from  strings); otherwise,
rounded Real addition or subtraction, after necessary conversions, is done.

LAND LOR XOR EQV LNOT
LAND,  LOR, XOR,  and EQV  carry  out bit-wise  And, Or,  Exclusive  Or, and
Equivalence operations on their arguments.      No type conversions are done
for  these functions.   The logical  connectives   and   do not  have this
effect -- they simply cause tests and jumps to be compiled.  The type of the
result is that of the first operand.  This allows expressions of the  form X
LAND '777777777, where X is Real, if they are really desired.

The unary operator LNOT produces  the bitwise complement of  its (algebraic)
argument.  No type conversions (except strings to integers) are performed on
the argument.  The type of the result (meaningful or not) is the type of the
argument.

"*/%" (MULTIPLICATION AND DIVISION)
The  operation  *  (multiplication),  like  +  and  -,   represents  Integer
multiplication only if both arguments are integers; Real otherwise.  Integer
multiplication uses the IMUL machine instruction -- no  double-length result
is available.

The  /  operator  (division)  always  does  rounded  Real   division,  after
converting any Integer arguments to Real.

The  % (division)  operator has  the same  type table  as +,  -, and  *.  It
performs whatever division is appropriate.

DIV MOD
DIV and MOD force both arguments to be integers before dividing.  X MOD Y is
the remainder after X DIV Y is performed:

       X MOD Y   =   X - (X DIV Y)*Y .

ASH LSH ROT
LSH and ROT provide logical  shift operations on their first  arguments.  If
the value of the  second argument is positive,  a shift or rotation  of that
many bits to  the left is  performed.  If it  is negative, a  right-shift or

 & CONCATENATION ^ EXPONENTIATION SUBSTRINGS


rotate is done.  ASH does an arithmetic shift.  Assume that A is an integer.
If N is positive then the expression A  ASH N is equal to A * 2^N.  If  N is
negative then A ASH N is equal to FLOOR (A / 2^(-N)).

"&" (CONCATENATION)
This  operator produces  a result  of type  String.  It  is the  String with
length  the  sum  of  the  lengths  of  its  arguments,  containing  all the
characters  of  the  second  string  concatenated  to  the  end  of  all the
characters of the first.  The operands will first be converted to strings if
necessary  as described  in  page 46  above.   Numbers can  be  converted to
strings representing their external forms (and vice-versa)  through explicit
calls  on execution  time routines  like CVS  and CVD  (see page  86 below).
NOTE: Concatenation of constant strings  will be done at compile  time where
possible.  For example, if SS  is a string variable, SS&'12&'15  will result
in two runtime concatenations, while SS&('12&'15) will result in one compile
time concatenation and one runtime concatenation.

"^" (EXPONENTIATION)
A factor is either a primary  or a primary raised to a power  represented by
another primary.  As usual, evaluation is from left to right, so  that A^B^C
is  evaluated  as  (A^B)^C.   In  the  factor  X^Y,  a  suitable  number  of
multiplications and additions is performed to produce an "exact" answer if Y
is  a  positive  integer.   Otherwise a  routine  is  called  to approximate
ANTILOG (Y LOG X).  The result has the type of X in the former case.   It is
always of type Real in the latter.

SUBSTRINGS
A String primary which is qualified by a substring  specification represents
a part of the specified string.  The characters of a string STR are numbered
1, 2, 3, ..., LENGTH (STR).  ST[X FOR Y] represents the substring which is Y
characters long and begins with character X.  ST[X TO Y] represents  the Xth
through Yth characters of ST.

Consider the ST[X TO Y] case.  This is evaluated

    SKIP_FALSE; XT_X; YT_Y;
    IF YT > LENGTH (ST) THEN BEGIN
      YT_LENGTH (ST); righthalf (SKIP)_TRUE END;
    IF YT < 0 THEN   COMMENT result will be NULL;
      BEGIN YT_0; righthalf (SKIP)_TRUE END;
    IF XT < 1 THEN
      BEGIN XT _ 1; lefthalf (SKIP)_TRUE END;
    IF XT > YT THEN   COMMENT result will be NULL;
      BEGIN XT _ YT+1; lefthalf (SKIP)_TRUE END;
    <return the XTth through YTth characters of ST>

LENGTH returns  the number  of characters in  a string  (see page  88).  The
ST[X FOR Y]  operation  is  converted  to  the  ST[X TO Y]  case  before the
substring operation is  performed.  The variable  SKIP can be  examined to
determine if the substring indices were "out of bounds".

  INFINITY SPECIAL LENGTH OPERATOR FUNCTION DESIGNATORS UNARY OPERATORS MEMORY AND LOCATION


"" (SPECIAL LENGTH OPERATOR)
This special primary construct is valid only within substring  brackets.  It
is an algebraic value representing  the length of the most  immediate string
under  consideration.     The  reserved  word  INF  is  equivalent  to  "".
Example:

   A[-2 to ]   yields the last 3 characters of A.

   A[3 for B[-1 for 1]]    uses the next to the last character of string
             B as the number of characters for the A substring operation.

FUNCTION DESIGNATORS
A function designator defines a single value.  This value is produced by the
execution of  a typed user  Procedure or of  a typed  execution-time routine
(See  chapters  6  and  7  for  execution-time  routines).   For  a function
designator  to be an algebraic  primary, its Procedure  must be  declared to
have an algebraic type.  Untyped Procedures may only be called  as Procedure
statements (see page 39).  The value obtained from a  user-defined Procedure
is that provided by a Return Statement within that Procedure.

The  rules for  supplying  actual parameters  in a  function  designator are
identical to those  for supplying parameters  in a procedure  statement (see
page 39).

UNARY OPERATORS
The unary operator ABS is  valid only for algebraic quantities.   It returns
the absolute value of its argument.

-X is equivalent to (0-X).  No type conversions are performed.

X is the logical negation of X.  MEMORY AND LOCATION
One's core image can be considered a giant one dimensional array,  which may
be accessed with the MEMORY construct.   You had better be a good  sport, or
know what you are doing.

       MEMORY [ <integer expression> ]

One can  store and retrieve  from the  elements of MEMORY  just as  with any
other  array.   However,  with  MEMORY, one  can  control  how  the compiler
interprets the  type of  the accessed element  by including  type declarator
reserved words after the <integer expression>.  For example:

                     ..._ MEMORY[X, INTEGER]
        MEMORY[X, REAL] _ ...
                     ..._ MEMORY[X, ITEM]
                COMMENT items and sets are part of Leap;
         MEMORY[X, SET] _ ...
                     ..._ MEMORY[X, INTEGER ITEMVAR]

Note that one  can not specify the  contents of memory to  be an Array  or a
String.

LOCATION


LOCATION is a predeclared Sail  routine that returns the index in  MEMORY of
the Sail construct furnished it.   The following is a list of  constructs it
can handle and what LOCATION will return.

   CONSTRUCT xLOCATION (x)  RETURNS

   variable   address of the variable

   string variable  -1,,address of word2

   array name address of a word containing the the address of the first data
              word of the array

   array element    address of that element

   procedure name   address of the procedure's entry code

   labels     address of the label

Simple example:

    REAL X;
    MEMORY [LOCATION (X), REAL] _ 2.0;
    PRINT (X); COMMENT " 2.000000    ";
    MEMORY [LOCATION (X)] _ 2.0; PRINT (X);
      COMMENT " .0000000@-39", MEMORY is INTEGER
      unless otherwise specified;
    MEMORY [LOCATION (X), INTEGER] _ 2.0;
    PRINT (X); COMMENT same as above;

ASSEMBLY LANGUAGE STATEMENTS Syntax


                                 SECTION  5

                        ASSEMBLY LANGUAGE STATEMENTS




5.1  Syntax


<codeblock>
    ::= <codehead> <codetail>


<codehead>
    ::= <codebegin>
    ::= <codebegin> <blockname>
    ::= <codehead> <declaration> ;


<codebegin>
    ::= STARTCODE
    ::= QUICKCODE


<codetail>
    ::= <instruction> END
    ::= <instruction> END <blockname>
    ::= <instruction> ; <codetail>


<instruction>
    ::= <addresses>
    ::= <opcode>
    ::= <opcode> <addresses>


<addresses>
    ::= <address>
    ::= <acfield> ,
    ::= <acfield> , <address>


<acfield>
    ::= <constantexpression>


<address>
    ::= <indexedaddress>
    ::= @ <indexedaddress>

 Semantics DECLARATIONS IN CODE BLOCKS


<indexedaddress>
    ::= <simpleaddress>
    ::= <simpleaddress> ( <indexfield> )


<simpleaddress>
    ::= <identifier>
    ::= <staticarrayname> [
        <constantsubscriptlist> ]
    ::= <constantexpression>
    ::= <literal>


<literal>
    ::= [ <constantexpression> ]


<indexfield>
    ::= <constantexpression>


<opcode>
    ::= <constantexpression>
    ::= <PDP-10opcode>



5.2  Semantics

Within a STARTCODE (QUICKCODE) block, statements are processed by  a small
and  weak,  but  hopefully  adequate,  assembly  language  translator.  Each
"instruction"  places  one  instruction  word  into  the  output  file.   An
instruction consists of

<label>:<opcode>     <acfield>, @<simpleaddr> (<index>)

or some subset thereof (see syntax).  Each instruction must be followed by a
semi-colon.

DECLARATIONS IN CODE BLOCKS
A codeblock behaves like any  other block with respect to  block structure.
Therefore,  all  declarations  are  valid,  and  the  names  given  in these
declarations will be available  only to the instructions in  the codeblock.
All labels must  be declared as usual.   Labels in codeblocks may  refer to
instructions  which will  be  executed, or  to  those which  are  not really
instructions, but data to be manipulated by these instructions (these latter
words must be bypassed in the code by jump instructions).  The user may find
it easier  to declare  variables or SAFE  arrays as  data areas  rather than
using labels  and null  statements.  As noted  below, identifiers  of simple
variables  are  addresses  of core  locations.   Identifiers  of  arrays are

 PROTECT ACS DECLARATION OPCODES THE <simple addr> FIELD


addresses of the first word of  the array header (see the appendix  on array
implementation).

PROTECT ACS DECLARATION

       PROTECTACS <ac #>, ... , <ac #>;

where <ac #>  is an integer  constant between 0  and '17, is  a declaration.
Its effect is to cause Sail not to use the named accumulators in the code it
emits  for the  block  in which  the  declaration occurred  (only  AFTER the
declaration).  The most common use is with the ACCESS construct (see below);
if one is using  accumulators 2, 3, and 4  in a code block, then  one should
declare PROTECTACS 2, 3,  4 if one is going  to use ACCESS.  This  way, the
code emitted by Sail for doing the ACCESS will not use accumulators 2, 3, or
4.     WARNING: this  does not  prevent you  from clobbering  such  ACs with
procedure  calls  (your  own  procedures  or  Sail's).   However,  most Sail
runtimes save their ACs and restore them after the call.

RESTRICTION: Accumulators  P ('17), SP  ('16), F ('12)  and 1 are  used for,
respectively, the system  push down pointer,  the string push  down pointer,
the  display  pointer,  and  returning  results  from  typed  procedures and
runtimes.  More about these acs on page 60.  The protect mechanism  will not
override these usages, so  attempts to protect 1,  '12, '16, or '17  will be
futile.

OPCODES
The Opcode may be  a constant provided by the  user, or one of  the standard
(non I/O) PDP-10 operation codes, expressed symbolically.  If a constant, it
should take the  form of a complete  PDP-10 instruction, expressed  in octal
radix (e.g. DEFINE TTYUUO = "'51000000000";).  Any bits appearing  in fields
other than  the opcode  field (first  9 bits)  will be  OR'ed with  the bits
supplied by other fields of  instructions in which this opcode  appears.  In
TOPS-10 Sail the MUUOs (ENTER,  LOOKUP, etc.) are available.  In  TENEX Sail
the JSYSes are available.   Within a codeblock opcodes supersede  all other
objects; a  variable, macro, or  procedure with the  same name as  an opcode
will be taken for the opcode instead.

The indirect, index, and AC fields have the same syntax and perform the same
functions as they do in the FAIL or MACRO languages.

THE <simple addr> FIELD
If the <address> in an  instruction is a constant (constant  expression), it
is assumed to be an immediate or data operand, and is not relocated.

If the  <address> is  an identifier,  the machine  address (relative  to the
start of the compilation) is used, and will be relocated to the proper value
by the Loader.

If  the <address>  is an  identifier  which has  been declared  as  a formal
parameter to a procedure,  addressing arithmetic will be  done automatically

 STARTCODE VERSUS QUICKCODE


to get at the  VALUE of the parameter.  Hence  if the <address> is  a formal
reference parameter,  the instruction will  be of the  form  OP AC,@ -x('12)
where x  depends on exactly  where the  parameter is in  the stack.   If the
formal was  from a  simple procedure,  then '17  will be  used as  the index
register rather than '12.  When computing x Sail will assume that  the stack
pointer has  not changed since  the last procedure  entry; if you  use PUSH,
POP, etc. in a Simple Procedure then you must calculate  x yourself.

If a literal is used, the address of the compiled constant will be placed in
the instruction.

Any reference to Strings will result in the address of the second descriptor
word (byte  pointer) to be  placed in the  instruction (see the  appendix on
string implementation for an explanation of string descriptors).

Accessing  parameters  of  procedures global  to  the  current  procedure is
difficult.   ACCESS (<expr>)  may  be used  to  return the  address  of such
parameters.  ACCESS will in fact do all of the computing necessary to obtain
the value of  the expression <expr>, then  return the address of  that value
(which might be a temporary).  Thus, MOVE AC, ACCESS(GP) will put  the value
of the variable GP in AC, while MOVEI AC, ACCESS(GP) will put the address of
the variable GP in AC.  If the expression is an item expression  (see Leap),
then the item's  number will be  stored in a  temp, and that  temp's address
will be returned.   The code emitted  for an Access  uses any acs  that Sail
believes are available, so one  must include a PROTECTACS declaration  in a
Code block that uses ACCESS if you  want to protect  certain acs  from being
munged by the Access.  WARNING:  skipping over an Access won't do  the right
thing.  For example,

   SKIPE  FLAG;
   MOVE   '10,ACCESS ('777 LAND INTIN(CHAN));
   MOVEI  '10,0;

will cause the program to skip into the middle of the code generated  by the
access if FLAG is 0.

STARTCODE VERSUS QUICKCODE
Before your  instructions are  parsed in a  block starting  with STARTCODE,
instructions are executed to leave  all accumulators from 0 through  '11 and
'13 through '15 available for your use.  In this case, you may use a JRST to
transfer control out of the codeblock,  as long as you do not  leave  (1) a
procedure, (2) a  block with array declarations,  (3) a Foreach loop,  (4) a
loop with a  For list, or (5)  a loop which uses  the NEXT construct.   In a
QUICKCODE block, no  accumulator-saving instructions are issued.   Ac's '13
through '15 only are free.  In addition, some recently used variables may be
given the wrong values if used as address identifiers (their  current values
may be contained in Ac's 0-'11); and control should not leave the codeblock
except by "falling through".

 ACCUMULATOR USAGE IN CODE BLOCKS CALLING PROCEDURES FROM INSIDE CODE BLOCKS


ACCUMULATOR USAGE IN CODE BLOCKS
Although we have said that accumulators are "freed" for your use,  this does
not imply  a carte blanche.   Usually this means  the compiler  saves values
currently  stored in  the ACs  which  it wants  to remember  (the  values of
variables mostly), and notes that when the code block is finished, these ACs
will  have  values in them that it doesn't care about.  However, this is not
the case with  the following accumulators, which  are not touched at  all by
the entrance and exit of code blocks:

  NAME  NUMBER    USAGE

   P      '17     The system  push down list  pointer.  All  procedures are
            called  with  a  PUSHJ P, PROC  and  exited   (usually)  with a
            POPJ P.  Use this as your PDL pointer in the code block, but be
            sure that its back to where it was on entrance to the  block by
            the time you exit.

   SP     '16     The string push down  stack pointer.  Used in  all string
            operations.  For how to  do your own string mangling,  read the
            code.

   F      '12     This  is  used  to maintain  the  "display"  structure of
            procedures.  DO NOT HARM  AC F!! Disaster will result.   A more
            exact description of its usage may be found in the  appendix on
            procedures and by reading the code.

CALLING PROCEDURES FROM INSIDE CODE BLOCKS
To call a procedure (say, PROT) from inside a code block, use PUSHJ P, PROT.
If  the  procedure requires  parameters,  PUSH P them  in  order  before you
PUSHJ P (i.e.   the first  one first, the  second one  next, etc.).   If the
formal is a reference, push the address of the actual onto the P  stack.  If
the formal is a  value string, push onto the  SP stack the two words  of the
string  descriptor  (see  the  appendix  on  string  implementation  for  an
explanation of string  descriptors).  If the  formal is a  reference string,
simply PUSH P the address of  the second word of the string  descriptor.  If
the procedure is typed, it will return is value in AC 1, except  that STRING
procedures return  their values  as the top  element of  the SP  stack. More
information  can  be  found in  the  appendix  on  procedure implementation.
Example:

 BEWARE



  INTEGER K; STRING S, SS;
  INTEGER PROCEDURE PROT (REAL T; REFERENCE
          INTEGER TT; STRING TTT; REFERENCE
          STRING TTTT);
                  BEGIN COMMENT BODY; END;

  DEFINE P = '17, SP = '16;

  STARTCODE
  PUSH P, [3.14159];
  MOVEI1, K;
  PUSH P, 1;
  MOVEI          1, S;
  PUSH SP, -1(1);COMMENT if Sail allowed address
                 arithmetic in Startcode, you
                 could have said PUSH SP, S-1;
  PUSH SP, S;
  MOVEI1, SS;
  PUSH P, 1;
  PUSHJ          P,PROT;
  END;

gives  the same effect as

        PROT (3.14159, K, S, SS);

NOTE: procedures  will change your  accumulators unless the  procedure takes
special pains to save and restore them.

BEWARE
The Sail  <code block>  assembler is not  FAIL or  MACRO.  Read  the syntax!
Address  arithmetic is  not permitted.   All integer  constants  are decimal
unless specified explicitly  as octal (e.g.,  '120).  Each instruction  is a
separate <statement> and must be separated from surrounding statements  by a
semicolon.  If you want comments then use COMMENT just like anywhere else in
Sail.  QUICKCODE is for wizards.

INPUT/OUTPUT ROUTINES Execution-time Routines in General SCOPE NOTATIONAL CONVENTIONS


                                 SECTION  6

                           INPUT/OUTPUT ROUTINES




6.1  Execution-time Routines in General

SCOPE
A  large set  of predeclared,  built-in procedures  and functions  have been
compiled  into  a  library  permanently resident  on  the  system  disk area
(SYS:LIBSAn.REL   or <SAIL>LIBSAn.REL  - n  is the  current  version number;
HLBSAn for /H compilations),  and optionally into a special  sharable write-
protected high  segment.  The  library also  contains programs  for managing
storage allocation and initialization, and for certain String functions.  If
a user calls one of these procedures, a request is automatically made to the
loader to include  the procedure, and any  other routines it might  need, in
the core  image (or to  link to the  high segment).  These  routines provide
input/output  (I/O)  facilities,  Arithmetic-String  conversion  facilities,
array-handling  procedures  and miscellaneous  other  interesting functions.
The remainder of this section  and the next describes the  calling sequences
and functions of these routines.

NOTATIONAL CONVENTIONS
A short-hand is used in these descriptions for specifying the types (if any)
of  the  execution-time  routines  and  of  their  parameters.   Before  the
description of each routine there is a sample call of the form

    VALUE _ FUNCTION (ARG1, ARG2, ... ARGn)

If VALUE is omitted, the procedure is an untyped one, and may only be called
at statement level (page 39).

The types of VALUE and  the arguments may be determined using  the following
scheme:

  1)  If "  characters surround the  sample identifier (which  is usually
      mnemonic in nature) a  String argument is expected.   Otherwise the
      argument is Integer or Real.  If it is important which of the types
      Integer or  Real must be  presented, it will  be made clear  in the
      description  of  the  function.   Otherwise  the  compiler  assumes
      Integer arguments (for those functions which are predeclared).  The
      user may pass Real arguments to these routines by re-declaring them
      in the blocks in which the Real arguments are desired.

  2)  If the  @ character  precedes the  sample identifier,  the argument
      will be called by reference.  Otherwise it is a value parameter.

Example:

 SKIP I/O  Channels and Files OPEN



  "RESULT" _ SCAN (@"SOURCE", BREAKTABLE, @BRCHAR)

is a predeclared procedure with the implicit declaration:

      EXTERNAL STRING PROCEDURE SCAN
            (REFERENCE STRING SOURCE;
             INTEGER BREAKTABLE;
             REFERENCE INTEGER BRCHAR);

SKIP
Some routines return  secondary values by  storing them in  SKIP.  Declare
EXTERNAL INTEGER SKIP if you want to examine these values.  In FAIL or DDT
the spelling is ".SKIP.".



6.2  I/O  Channels and Files



**********************************  OPEN  ********************************;

OPEN (CHANNEL, "DEVICE", MODE,
        NUMBEROFINPUTBUFFERS,
        NUMBEROFOUTPUTBUFFERS,
        @COUNT, @BRCHAR, @EOF);

Sail input/output operates at a  very low level in the following  sense: the
operations necessary  to obtain  devices, open and  close files,   etc., are
almost directly  analogous to  the system calls  used in  assembly language.
OPEN is  used to associate  a channel number  (0 to '17)  with a  device, to
determine the data mode of the I/O to occur on this channel (character mode,
binary mode, dump mode,  etc.), to specify storage requirements for the data
buffers used in the operations,  and to provide the system  with information
to be used for input operations.   See page 4 for an example of  TOPS-10 I/O
programming.

CHANNEL  is a user-provided channel number which will be used in subsequent
      I/O operations to identify the device. CHANNEL may range from 0 to 15
      ('17).  A RELEASE will be performed before the OPEN is executed.

DEVICE  must be a String  (i.e. "TTY", "DSK") which is recognizable  by the
      system as a physical or logical device name.

MODE  is the data mode for  the I/O operation. MODE 0 will always  work for
      characters (see  INPUT, page 72  and  OUT, page  74).  Modes  8 ('10)
      and 15 ('17) are applicable for binary and dump-mode operations using
      the functions WORDIN,  WORDOUT, ARRYIN, or  ARRYOUT (see page  75 and
      following).  For other data modes, see [SysCall].  If any of bits 18-



      21 are on  in the MODE  word, the I-O  routines will not  print error
      messages when data errors occur which present the  corresponding bits
      as a response  to the GETSTS UUO.   Instead, the GETSTS bits  will be
      reported to the user as described under EOF below.  If bit 23  is on,
      no  error  message   will  be  printed   if  an  invalid   file  name
      specification  is  presented  to LOOKUP,  ENTER,  or  RENAME,  a code
      identifying  the  problem  will  be returned  (see  page  67  ff. for
      details).  If you  don't understand any  of this, leave  all non-mode
      bits off in the MODE word.

NUMBEROF{INPUT/OUTPUT~BUFFERS  specifies  the number  of  buffers  to be
      reserved  for  the  I/O  operations.  At  least  one  buffer  must be
      specified for input if  any input is to  be done in modes  other than
      '17; similarly for output.  If data is only going one  direction, the
      other buffer specification should be 0.  Two buffers  give reasonable
      performance for  most devices (1  is sufficient for  a TTY,  more are
      required for DSK  if rapid operation is  desired).  The left  half of
      the BUFFER  parameter, if  non-zero, specifies  the buffer  size (mod
      '7777) for the I/O buffers.  Use this only if you desire non-standard
      sizes.

The remaining arguments are applicable only for INPUT (String  input).  They
will  be ignored  for any  other operations  (although their  values  may be
changed  by the Open function).

COUNT   designates a  variable  which will  contain the  maximum  number of
      characters to be read from  "DEVICE" in a given INPUT call  (see page
      72,  page  68).  Fewer characters  may be read  if a  break character
      is encountered or if an end of file is detected.  The count should be
      a  variable or  constant (not  an expression),  since its  address is
      stored, and the temporary storage for an expression may be re-used.

BRCHAR  designates a variable into which the break character (see INPUT and
      BREAKSET  again) will  be  stored.  This  variable can  be  tested to
      determine  which  of  many possible  characters  terminated  the read
      operation.

EOF   designates a variable to be used for two purposes:

      1) Error handling when  OPEN is called.  If  the system call  used by
         OPEN succeeds then  EOF is set to  zero and OPEN returns.   If the
         system call fails  then OPEN looks at  the EOF variable; if  it is
         nonzero then OPEN returns.  If EOF is zero then the user  is given
         the option  of retrying  or continuing without  the device.   If a
         retry  is successful  then EOF  is zeroed.   If the  user proceeds
         (gives up) then EOF is set to nonzero.  The net effect is that the
         program may interpret EOF=0 as  a successful OPEN and EOF0  as an
         unsuccessful OPEN.

      2) Error handling  for subsequent I/O  operations.  EOF will  be made



         non-zero  (TRUE)   if  an  end of  file  condition,  or  any error
         condition among those enabled (see MODE, above) is detected during
         any Sail input/output operation.   It will be 0 (FALSE)  on return
         to the user otherwise.  Subsequent inputs after an EOF return will
         return non-zero values in EOF and a null String result  for INPUT.
         For ARRYIN, a 0 is returned as the value of the call after  end of
         file is detected.  If EOF is TRUE after such an operation, it will
         contain the entire set (18 bits) of GETSTS information in the left
         half.  The EOF bit is '20000, and is the only one you'll  ever see
         if you haven't specially enabled for others.

Here are  the error bits  for SUAI  and TOPS-10; TENEX  Sail uses  the ERSTR
error number instead.
   400000  improper mode (a catchall)
   200000  parity error
   100000  data error
    40000  record number out of bounds
    20000  end of file (input only)

You  are always  enabled for  bit 20000  (EOF).  However,  to be  allowed to
handle any  of the others,  you must  turn on the  corresponding bit  in the
right half of the  MODE word. In addition, the  10000 bit is used  to enable
user handling of invalid  file specifications to ENTER, LOOKUP,  and RENAME.
'7500017  in the  MODE parameter  would  enable a  dump mode  file  for user
handling of ALL  I/O errors on  the channel.  If you  are not enabled  for a
given  error, an  error message  (which may  or may  not be  fatal)  will be
printed, and the error code word set as indicated.  In addition,  the number
of  words  actually transferred  is  stored in  the  right half  of  the EOF
variable for ARRYIN, ARRYOUT.

Assembly Language Approximation to OPEN:

    INIT   CHANNEL, MODE
    SIXBIT /DEVICE/
    XWD    OHED,IHED
    JRST   <handle error condition>
    JUMPE  <NUMBEROFOUTPUTBUFFERS>, GETIN
    <allocate buffer space>
    OUTBUF  CHANNEL, NUMBEROFOUTPUTBUFFERS
GETIN: JUMPE  <NUMBEROFINPUTBUFFERS>, DONE
    <allocate buffer space>
    INBUF  CHANNEL, NUMBEROFINPUTBUFFERS
DONE:  <mark channel open -- internal bookkeeping>
    <return>

OHED: BLOCK 3
IHED: BLOCK 3

 CLOSE, CLOSIN, CLOSO GETCHAN RELEASE


**************************  CLOSE, CLOSIN, CLOSO  ************************;

CLOSE (CHANNEL, BITS(0));
CLOSIN (CHANNEL, BITS(0));
CLOSO (CHANNEL, BITS(0))

The  input (CLOSIN)  or  output (CLOSO)  side  of the  specified  channel is
closed:  all  output  is  forced out  (CLOSO);   the  current  file  name is
forgotten.  However the device is  still active; no OPEN need be  done again
before the  next LOOKUP/ENTER  operation.  Always  CLOSE output  files: Sail
exit code  will deassign the  device, but does  not force out  any remaining
output; you must do a CLOSE when writing on a disk file to have the new file
(or a newly edited old file) entered on your User File Directory.  No INPUT,
OUT, etc., may be  given to a directory  device until an ENTER,   LOOKUP, or
RENAME has been issued for the channel.

CLOSE  is  equivalent to  the execution of  both CLOSIN   and CLOSO  for the
channel.  BITS specifies the close inhibit bits, which default to zero.  See
[SysCall] for the interpretation of the bits.



********************************  GETCHAN  *******************************;

VALUE _ GETCHAN

GETCHAN  returns the  number  of some  channel  which Sail  believes  is not
currently open.  The value -1 is returned if all channels are busy.



********************************  RELEASE  *******************************;

RELEASE (CHANNEL, BITS(0))

If an OPEN has been executed  for this channel, a CLOSE is now  executed for
it.  The device is dissociated from the channel and returned to the resource
pool (unless it  has been assigned by  the monitor ASSIGN command).   No I/O
operation may refer to this channel until another OPEN denoting it  has been
executed.  BITS specifies the CLOSE inhibit bits; see [SysCall].

Release is always  valid.  If the channel  mentioned is not  currently open,
the command is simply ignored.

 LOOKUP, ENTER RENAME


*****************************  LOOKUP, ENTER  ****************************;

LOOKUP (CHANNEL, "FILE", @FLAG);
ENTER (CHANNEL, "FILE", @FLAG)

Before input or  output operations may be  performed for a  directory device
(DECtape or DSK) a  file name must be  associated with the channel  on which
the device has been opened (see  page 63).  LOOKUP names a file which  is to
be  read.  ENTER  names  a file  which is  to  be created  or  extended (see
[SysCall]).  It is recommended that  an ENTER be performed after  every OPEN
of an output device so that  output not normally directed to the DSK  can be
directed there for later processing if desired.  The format for a  file name
string is

        "NAME", or
        "NAME.EXT", or
        "NAME[P,PN]", or
        "NAME.EXT[P,PN]", or
        "NAME.EXT[P,PN"

See  [MonCom] for  the meaning  of these  things if  you do  not immediately
understand.

Sail is not as choosy about the characters it allows as some processors are.
Any character which  is not a comma,  period, right square bracket,  or left
square bracket will be passed on.  Up to 6 characters from NAME, 3 from EXT,
P, or PN will be used -- the rest are ignored.

If the LOOKUP or ENTER  operation fails  then variable FLAG may  be examined
to determine the cause.  The left half of FLAG will be set to  '777777 (Flag
has the logical value TRUE).  The right half will contain the  code returned
by  the  system  giving  the   cause  of  the  failure.   An   invalid  file
specification will return a code  of '10.  In this case, if  the appropriate
bit (bit 23, see OPEN) was OFF  in the MODE parameter of the OPEN,  an error
message  will  be  printed; otherwise,   the  routine  just  returns without
performing the UUO.

If the LOOKUP or ENTER succeeds, FLAG will be set to zero (FALSE).



*********************************  RENAME  *******************************;

RENAME (CHANNEL, "FILE-SPEC",
        PROTECTION, @FLAG)

The file  open on  CHANNEL is renamed  to FILESPEC  (a NULL  file-name will
delete the file) with read/write protection as specified in PROTECTION (nine
bits, described in [SysCall]).  FLAG is set as in LOOKUP and ENTER.

 ERENAME Break Characters BREAKSET


********************************  ERENAME  *******************************;

ERENAME (CHANNEL,"FILE-SPEC",
        PROTECTION, DATE, TIME,
        MODE, @FLAG)

(Not  on   TENEX.)  This   extended  version   of  RENAME   allows  complete
specification of all the data which may be changed by a RENAME.



6.3  Break Characters



********************************  BREAKSET  ******************************;

BREAKSET (TABLE, "BREAKCHARS", MODE)

Character input/output is done using the String features of Sail.   In fact,
I/O is the chief justification for the existence of strings in the language.

String input presents a problem not present in String output.  The length of
an output String can be used to determine the number of  characters written.
However it is often awkward  to require an absolute count for  input.  Quite
often one would like to terminate input, or "break", when one of a specified
set  of  characters is  encountered  in  the input  stream.   In  Sail, this
capability is implemented by means of the BREAKSET, INPUT, TTYIN,   and SCAN
functions.   The value  of TABLE may  range from -17  to 54, but  tables -17
through  -1 are  reserved for  use by  the runtime  system.  Thus  up  to 54
different sets of  user break specifications may  exist at once.   Which set
will  be used  is determined  by the  TABLE parameter  in an  INPUT  or SCAN
function call.  Breaktables are dynamically allocated in blocks of 18 (1-18,
19-36, 37-54).

BREAKSET merely modifies the existing settings in TABLE; use GETBREAK (which
returns a  virgin table)  if you want  to achieve  an absolute  known state.
The function  of a given  BREAKSET command depends  on the MODE,  an integer
which is  interpreted as  a right-justified ASCII  character whose  value is
intended to be vaguely mnemonic.  BREAKSET commands can be  partitioned into
4 groups according to mode:

GROUP 0 -- Conversion specifications



MODE   FUNCTION

"K"   (Konvert)  The  minuscule  letters  (a-z)  will  be   converted  to
      majuscule (A-Z) before doing anything else.

"F"   (Full character  set) Undoes the  effect of "K".   Mode "F"  is the
      default.

"Z"   (Zero bytes) Believe the  breaktable when INPUT reads a  zero byte.
      INPUT automatically omits  zero characters otherwise.  Mode  "Z" is
      turned off by both mode "I" and mode "X".

GROUP 1 -- Break character specifications

MODE   FUNCTION

"I"   (by Inclusion)  The characters in  the BREAKCHARS  String comprise
      the set of characters which will terminate an INPUT (or SCAN).

"X"   (by eXclusion)  Only those  characters (of  the possible  128 ASCII
      characters) which are NOT contained in the String  BREAKCHARS will
      terminate an input when using this table.

"O"   (Omit) The  characters in "BREAKCHARS"  will be  omitted (deleted)
      from the input string.

Any "I" or "X" command completely specifies the break character set  for its
table (i.e., the table is  reset before these characters are stored  in it).
Neither will destroy the omitted character set currently specified  for this
table.  Any "O" command completely specifies the set of  omitted characters,
without  altering the  break characters  for the  table in  question.   If a
character  is  a break-character,  any  role  it might  play  as  an omitted
character is sacrificed.

The next group  of MODEs determines the  disposition of break  characters in
the input stream.  The "BREAKCHARS" argument is ignored in  these commands,
and may in fact be NULL:

GROUP 2 -- Break character disposition

MODE   FUNCTION

"S"   (Skip -- default mode) After execution of an "S" command  the break
      character will  not appear  either in the  resultant  String  or in
      subsequent INPUTs or SCANs-- the character is "skipped".  Its value
      may  be determined  after  the INPUT  by examination  of  the break
      character variable (see page 63).

"A"   (Append) The break character (if  there is one -- see  page  63 and
      page  72) is  appended, or  concatenated to  the end  of  the input
      string.  It will not appear again in subsequent inputs.



"R"   (Retain) The break character does not appear in the resultant INPUT
      or SCAN String,  but will be the  first character processed  in the
      next  operation  referring  to  this  input  source  (file  or SCAN
      String).

Text files containing line numbers present a special problem.  A line number
is a word containing 5  ASCII characters representing the number in  bits 0-
34, with a "1" in bit 35.  No other words in the file contain 1's in bit 35.
Since String  manipulations provide no  way for distinguishing  line numbers
from  other characters,  there must  be a  way to  warn the  user  that line
numbers are present, or to allow him to ignore them entirely.

The next group  of MODEs determines the  disposition of these  line numbers.
Again, the "BREAKCHARS" argument is ignored:

Group 3 -- Line number disposition

MODE   FUNCTION

"P"   (Pass -- default) Line numbers are treated as any other characters.
      Their identity is lost; they simply appear in the result string.

"N"   (No numbers) No line number (or the TAB which always follows  it in
      standard files) will appear in the result string.  They  are simply
      discarded.

"L"   (Line no. break)  The result String will  be terminated early  if a
      line  number is  encountered.  The  characters comprising  the line
      number and the associated TAB will appear as the next  6 characters
      read  or  scanned from  this  character source.   The  user's break
      character variable (see page 63 and  page 72) will be set to  -1 to
      indicate a line number break.

"E"   (Lee Erman's very  own mode) The result  String is terminated  on a
      line number as  with "L", but neither  the line number nor  the TAB
      following it  will appear  in subsequent  inputs.  The  line number
      word, negated, is returned in the user's (integer) BRCHAR variable.

"D"   (Display) obsolete

Once a break table  is set up, it may  be referenced in an INPUT,  TTYIN  or
SCAN call to control the scanning operation.

Example: To delimit a "word", a program might wish to input characters until
a  blank, a  TAB,  a line  feed, a  comma,  or a  semicolon  is encountered,
ignoring line numbers.  Assume also that carriage returns are to be ignored,
and that the break character is to be retained  in the character  source for
the next scanning operation:

 SETBREAK GETBREAK, RELBREAK



    BREAKSET (DELIMS, " , ;"&TAB&LF, "I");
        Comment break on any of these;

    BREAKSET (DELIMS, '15, "O");
        Comment ignore carriage return;

    BREAKSET (DELIMS, NULL, "N");
        Comment ignore line numbers;

    BREAKSET (DELIMS, NULL, "R");
        Comment save break char for next time;

Breaktable  0 is  builtin as  equivalent to  SETBREAK (0, NULL,  NULL, "I").
This is break-on-count for INPUT and returns the whole string from SCAN.



******************************  SETBREAK  ******************************;

SETBREAK (TABLE, "BREAKCHARS",
                "OMITCHARS", "MODES")

SETBREAK is logically equivalent to the Sail statement:

   BEGIN "SETBREAK"
    INTEGER I;

    IF LENGTH (OMITCHARS) > 0 THEN
       BREAKSET (TABLE, OMITCHARS, "O");

    FOR I_1 STEP 1 UNTIL LENGTH (MODES) DO
       BREAKSET (TABLE, BREAKCHARS, MODES[I FOR 1])

   END "SETBREAK"



***************************  GETBREAK, RELBREAK  *************************;

TABLE _ GETBREAK;
RELBREAK (TABLE)

GETBREAK  finds  an  unreserved  breaktable,  reserves  it,  sets  it  to  a
completely  virgin state,  and returns  the number  of the  table.  GETBREAK
returns  -18 if  there  are no  free  tables.  Breaktables  are  reserved by
GETBREAK, SETBREAK, BREAKSET, and  STDBRK.  RELBREAK returns a table  to the
available list.

 STDBRK I/O Routines INPUT


*********************************  STDBRK  *******************************;

STDBRK (CHANNEL)

Eighteen breakset tables  have been selected  as representative of  the more
common  input  scanning  operations.  The  function  STDBRK  initializes the
breakset tables by opening  the file  SYS:BKTBL.BKT  on CHANNEL  and reading
in these tables.   The user may  then reset those  tables which he  does not
like to something he does like.

The eighteen tables are described  here by giving the SETBREAKs  which would
be required for the user to initialize them:

     DELIMS _ '15 & '12 & '40 & '11 & '14;
     Comment carriage return, line feed, space,
                tab, form feed;
     LETTS _ "ABC ... Zabc ... z";
     DIGS _ "0123456789";
     SAILID _ LETTS&DIGS;

     SETBREAK ( 1, '12, '15, "INS");
     SETBREAK ( 2, '12, NULL, "INA");
     SETBREAK ( 3, DELIMS, NULL, "XNR");
     SETBREAK ( 4, SAILID, NULL, "INS");
     SETBREAK ( 5, SAILID, NULL, "INR");
     SETBREAK ( 6, LETTS, NULL, "XNR");
     SETBREAK ( 7, DIGS, NULL, "XNR");
     SETBREAK ( 8, DIGS, NULL, "INS");
     SETBREAK ( 9, DIGS, NULL, "INR");
     SETBREAK (10, DIGS&"+-@.", NULL, "XNR");
     SETBREAK (11, DIGS&"+-@.", NULL, "INS");
     SETBREAK (12, DIGS&"+-@.", NULL, "INR");
     SETBREAK (13-18, NULL, NULL, NULL);



6.4  I/O Routines



*********************************  INPUT  ********************************;

"RESULT" _ INPUT (CHANNEL, BREAKTABLE)

A string  of characters is  obtained for  the file open  on CHANNEL,  and is
returned as the  result.  The INPUT  operation is controlled  by BREAKTABLE
(see page 68) and the  reference variables BRCHAR, EOF, and COUNT  which are
provided by the user  in the OPEN function  for this channel (see  page 63).
Zero bytes  are automatically omitted  (text editor convention)  unless mode
"Z" was specified for the breaktable.    Input may be terminated  in several

 SCAN


ways.  The exact reason for termination can be obtained by  examining BRCHAR
and EOF:

EOF     BRCHAR

0      0   End of  file or  an error  (if enabled,  see page  63) occurred
            while  reading.  The  result is  a String  containing  all non-
            omitted characters  which remained in  the file when  INPUT was
            called.

0       0   No break characters were  encountered.  The result is  a String
            of length  equal to  the current  COUNT specifications  for the
            CHANNEL (see page 63).

0       <0  A line  number was  encountered and  the break  table specified
            that someone  wanted to know.   The result String  contains all
            characters up to the line number.  If mode "L" was specified in
            the Breakset setting  up this table,  bit  35 is turned  off in
            the line number word so that it will be input next time.  -1 is
            placed in BRCHAR.  If  mode "E" was specified, the  line number
            will not appear in the next input String, but its negated ASCII
            value, complete with low-order  line number bit, will  be found
            in BRCHAR.

0       >0  A  break character  was  encountered.  The  break  character is
            stored in BRCHAR (an  INTEGER reference variable, see  page 63)
            as a right-justified 7-bit ASCII value.  It may also  be tacked
            on to  the end  of the result  String or  saved for  next time,
            depending on the BREAKSET mode (see page 68).

If break table 0 is specified, the only criteria for termination are  end of
file or COUNT exhaustion.



**********************************  SCAN  ********************************;

"RESULT" _ SCAN (@"SOURCE",
                BREAKTABLE, @BRCHAR)

SCAN functions identically to INPUT with the following exceptions:

 SCANC OUT LINOUT


   1. The source  is not  a data file  but the  String SOURCE,  called by
      reference. The String SOURCE is truncated from the left  to produce
      the same effect as one would obtain if SOURCE were a data file. The
      disposition of the break character is the same as it is for INPUT.

   2. BRCHAR is directly specified  as a parameter. INPUT gets  its break
      character variable from a table set up by  page 63.

   3. Line number considerations are irrelevant.



*********************************  SCANC  ********************************;

"RESULT" _ SCANC ("SOURCE",
                "BREAK", "OMIT", "MODE");

This routine is equivalent to the following Sail code:

        STRING PROCEDURE SCANC (STRING ARG, BRK,
                OMIT, MODE);
        BEGIN "SCANC" INTEGER TBL, BRCHAR; STRING RSLT;
        TBL_GETBREAK; SETBREAK (TBL, BRK, OMIT, MODE);
        RSLT_SCAN (ARG, TBL, BRCHAR);
        RELBREAK (TBL);
        RETURN (RSLT) END "SCANC";

Note that  the arguments  are all value  parameters, so  that SCANC  will be
called at compile time if the arguments are constants.  It is  intended that
SCANC  be used  with  ASSIGNC in  macros and  conditional  compilation.  For
scanning at execution time, it is much more efficient to use SCAN directly.



**********************************  OUT  *********************************;

OUT (CHANNEL, "STRING")

STRING is output to the file open  on CHANNEL.  If the device is a  TTY, the
String will be typed immediately.  Buffered mode text output is employed for
this operation.  The data mode  specified in the OPEN for this  channel must
be 0 or 1.  The EOF variable will be set non-zero as described in page 63 if
an error is detected and the program is enabled for it; 0 otherwise.



*********************************  LINOUT  *******************************;

LINOUT (CHANNEL, NUMBER)

 SETPL WORDIN WARNING ABOUT DUMP MODE IO ARRYIN


ABS (NUMBER) mod 100,000 is converted to a 5 character ASCII  string.  These
characters are  placed in  a single word  in the  output file  designated by
CHANNEL  with the  low-order  bit (line-number  bit)  turned on.   A  tab is
inserted after the line number.  Mode 0 or 1 must have been specified in the
OPEN (page 63) for the results to be anywhere near satisfactory.  EOF is set
as in OUT.



*********************************  SETPL  ********************************;

SETPL (CHANNEL, @LINNUM,
               @PAGNUM, @SOSNUM)

This routine  allows one  to keep track  of the  string input  from CHANNEL.
Whenever a  '12 is encountered,  LINNUM is incremented.   Whenever a  '14 is
encountered, PAGNUM is  incremented and LINNUM  is zeroed.  Whenever  an SOS
line number is encountered it is placed into SOSNUM.



*********************************  WORDIN  *******************************;

VALUE _ WORDIN (CHANNEL)

The next word from the file open on CHANNEL is returned. A zero is returned,
and  EOF  (see  page  63,  page  72) set,  when  end  of  file  or  error is
encountered.  This  operation is  performed in buffered  mode or  dump mode,
depending on the mode specification in the OPEN.

WARNING ABOUT DUMP MODE IO
Dump  Mode  (mode  '15,  '16, or  '17)  is  sufficiently  device  and system
dependent that you should consult [SysCall] and be extremely careful.



*********************************  ARRYIN  *******************************;

ARRYIN (CHANNEL, @LOC, HOWMANY)

HOWMANY  words are  read from  the  device and  file open  on  CHANNEL, and
deposited in memory  starting at location LOC.   Buffered-mode input is done
if MODE (see page 63) is '10 or '14.  Dump-mode input is done if MODE is '16
or '17.  Other modes are illegal.  See the warning about Dump Mode IO above.
If an end  of file or enabled  error condition occurs before  HOWMANY words
are read in buffered mode then the EOF variable (see page 63) is set  to the
enabled bits in its left half, as usual.  Its right half contains the number
of words actually read.  EOF will be 0 if the full request is satisfied.  No
indication  of  how  many  words  were actually  read  is  given  if  EOF is
encountered while reading a file in DUMP mode.

 WORDOUT ARRYOUT INOUT GETSTS, SETSTS


********************************  WORDOUT  *******************************;

WORDOUT (CHANNEL, VALUE)

VALUE is placed in  the output buffer for  CHANNEL.  An OUTPUT is  done when
the buffer is full or when a CLOSE or RELEASE is executed for  this channel.
Dump mode output  will be done  if dump mode is  specified in the  OPEN (see
page 63).  EOF is set as in OUT.  See the warning about Dump Mode IO above.



********************************  ARRYOUT  *******************************;

ARRYOUT (CHANNEL, @LOC, HOWMANY)

HOWMANY words are written from memory, starting at location LOC,   onto the
device and  file open on  channel CHANNEL.  The  valid modes are  again '10,
'14, '16, and '17.   The EOF variable is set  as in ARRYIN, except  that the
EOF bit itself will never occur.



*********************************  INOUT  ********************************;

INOUT (INCHAN, OUTCHAN, HOWMANY)

INOUT reads HOWMANY words from channel INCHAN and writes them out on channel
OUTCHAN.  Each channel must be open in a mode between 8 and 12.   On return,
the EOF  variables for  the two  channels will be  the same  as if  ARRYIN &
ARRYOUT had been used.  If HOWMANY is less than zero, then transfer  of data
will cease only upon end of file or a device error.  INOUT is  not available
in TENEX Sail.



*****************************  GETSTS, SETSTS  ***************************;

SETSTS (CHAN, NEWSTATUS);

issues a SETSTS uuo on channel CHAN with the status value NEWSTATUS.

STATUS _ GETSTS (CHAN)

returns the results of a GETSTS uuo on channel CHAN.

These functions  do not  exist in  TENEX Sail.   Instead, see  GTSTS, GDSTS,
STSTS, and SDSTS for analogous features.

 MTAPE USETI, USETO REALIN, INTIN


*********************************  MTAPE  ********************************;

MTAPE (CHANNEL, MODE)

MTAPE is  ignored unless the  device associated with  CHANNEL is  a magnetic
tape drive.  It performs tape actions as follows:

    MODE   FUNCTION

    "A"    Advance past one tape mark (or file)
    "B"    Backspace past one tape mark
    "E"    Write tape mark
    "F"    Advance one record
    "I"    Set 'IBM compatible' mode
    "R"    Backspace one record
    "S"    Write 3 inches of blank tape
    "T"    Advance to logical end of tape
    "U"    Rewind and unload
    "W"    Rewind tape
    NULL   Wait until all activity ceases



******************************  USETI, USETO  ****************************;

USETI (CHANNEL, VALUE);
USETO (CHANNEL, VALUE)

These routines are for random file access (see [SysCall]).



*****************************  REALIN, INTIN  ****************************;

VALUE _ REALIN (CHANNEL);
VALUE _ INTIN (CHANNEL)

Number input may be obtained using the  functions REALIN or INTIN, depending
on whether a Real number or an Integer is required.  Both functions  use the
same free field scanner, and take as argument a channel number.

Free field scanning works as follows:  characters are scanned one at  a time
from the input channel, ignoring  everything until a digit or  decimal point
is encountered.   Then a number  is scanned according  to this  syntax, with
zero bytes, line numbers, and carriage returns (but not linefeeds) ignored:


<number>
    ::= <sign> <real number>



<real number>
    ::= <decimal number>
    ::= <decimal number>   <exponent>
    ::= <exponent>


<decimal number>
    ::= <integer>
    ::= <integer> .
    ::= <integer> . <integer>
    ::= . <integer>


<integer>
    ::= <digit>
    ::= <integer> <digit>


<exponent>
    ::= @ <sign> <integer>
    ::= E <sign> <integer>


<sign>
    ::= +
    ::= -
    ::= <empty>

If the digit is  not part of a number  an error message will be  printed and
the  program will  halt.   Typing a  carriage  return will  cause  the input
function to return zero.

On input, leading  zeros are ignored.  The  ten most significant  digits are
used to form the number.  A check for overflow and underflow is made  and an
error message  printed if  this occurs.   When using  INTIN any  exponent is
removed by scaling  the Integer number.  Rounding  is used in  this process.
All numbers are accurate to one half of the least significant bit.

After scanning the number the last delimiter is replaced on the input string
and is returned  as the break  character for the  channel.  If no  number is
found, a zero is returned, and the break variable is set to -1; If an end of
file or  enabled error is  sensed this is  also returned in  the appropriate
channel variable.  The maximum character count appearing in the OPEN call is
ignored.

 REALSCAN, INTSCAN TMPIN, TMPOUT


***************************  REALSCAN, INTSCAN  **************************;

VALUE _ REALSCAN (@"NUMBERSTRING",
                         @BRCHAR);
VALUE _ INTSCAN (@"NUMBERSTRING",
                         @BRCHAR)

These  functions  are identical  in  function to  REALIN  and  INTIN.  Their
inputs,  however, are  obtained from  their NUMBERSTRING  arguments.  These
routines replace  NUMBERSTRING by a  string containing all  characters left
over after the number has been removed from the front.



*****************************  TMPIN, TMPOUT  ****************************;

"RESULT" _ TMPIN ("FILE", @ERRFLAG);
TMPOUT ("FILE", "TEXT", @ERRFLAG)

These routines do input and output to tmpcor files (simulated files  kept in
core storage--see [SysCall]).

TMPIN returns a string consisting of the entire contents of the  tmpcor file
of the specified name.  Only the first three characters in the file name are
significant.  If the  input fails for some  reason (most likely:   no tmpcor
file  with the  specified name)  then ERRFLAG  is set  to true  and  NULL is
returned.  Otherwise ERRFLAG is set to false.

TMPOUT  writes its  string  argument into  the specified  tmpcor  file.  The
ERRFLAG has the same function as in TMPIN; in case of error, the tmpcor file
is not  written.  Likely causes  for error are  running out of  tmpcor space
(currently, the sum of  the sizes of all the  tmpcor files for a  single job
may not exceed =256 words) or attempting to write a null tmpcor  file (i.e.,
calling TMPOUT with the string argument NULL).

TMPIN executes  a TMPCOR  uuo with  code 1,  and hence  does not  delete the
specified tmpcor file.  The length  of the returned string will always  be a
multiple  of five,  since words  rather than  characters are  actually being
transferred.  TMPOUT executes  a TMPCOR uuo with  code 3.  The last  word of
the string  is padded with  nulls if necessary  before the data  transfer is
done.

Neither function is available in TENEX Sail.

 AUXCLR, AUXCLV CHNIOR, CHNIOV TTY and PTY Routines TELETYPE I/O ROUTINES BACKUP CLRBUF INCHRS INCHRW INCHSL INCHWL


*****************************  AUXCLR, AUXCLV  ***************************;

RSLT _ AUXCLR (PORT, @ARG, FUNCTION);
RSLT _ AUXCLV (PORT, ARG, FUNCTION)

(TYMSHARE  only.)  These  functions perform  AUXCAL system  calls;  the only
difference is whether ARG is by reference or by value.  SKIP is set.



*****************************  CHNIOR, CHNIOV  ***************************;

RSLT _ CHNIOR (CHAN, @ARG, FUNCTION);
RSLT _ CHNIOV (CHAN, ARG, FUNCTION)

(TYMSHARE  only.)  These  functions perform  CHANIO system  calls;  the only
difference is whether ARG is by reference or by value.  SKIP is set.



6.5  TTY and PTY Routines



*************************  TELETYPE I/O ROUTINES  ************************;


Each of the I/O functions uses the TTCALL UUO's to do direct TTY I/O.

BACKUP
          The system attempts to back up its TTY input buffer pointer to the
          beginning of the last "line", thus allowing you to reread  it.  In
          general this cannot possibly work, so do not use BACKUP.

CLRBUF
          Flushes the input buffer.

CHAR _ INCHRS
          Returns  a  negative  value  if  no  characters  have  been typed;
          otherwise it is INCHRW.

CHAR _ INCHRW
          Waits for a character to be typed and returns that character.

"STR" _ INCHSL (@FLAG)
          Returns NULL with FLAG  0 if no lines have been typed.  Otherwise
          it sets FLAG to 0 and performs INCHWL.

"STR" _ INCHWL
          Waits for a line to  be typed and returns a string  containing all

 INSTR INSTRL INSTRS IONEOU OUTCHR OUTSTR TTYIN TTYINL TTYINS TTYUP


          characters  up to  (but not  including) the  activation character.
          The activation  character is put  into SKIP.  If  the activation
          character  is CR  then  the next  character is  discarded  (on the
          assumption that it is LF).

"STR" _ INSTR (BRCHAR)
          Returns as a string all  characters up to, but not  including, the
          first instance of BRCHAR.  The BRCHAR instance is lost.

"STR" _ INSTRL (BRCHAR)
          Waits for a line to be typed, then performs INSTR.

"STR" _ INSTRS (@FLAG, BRCHAR)
          Is INCHSL if no lines are waiting; INSTRL otherwise.

IONEOU (CHAR)
          (TYMSHARE only.) The low-order 8 bits of CHAR are sent to  the TTY
          in image mode.

OUTCHR (CHAR)
          Types  its  character  argument  (right-justified  in  an  integer
          variable).

OUTSTR ("STR")
          Types its string  argument until the end  of the string or  a null
          character is reached.

"STR" _ TTYIN (TABLE, @BRCHAR)
          Uses the break table features described in page 68 and page  72 to
          return a string  and break character.   Mode "R" is  illegal; line
          number modes are irrelevant.  The input count (see page 63) is set
          at 100.

"STR" _ TTYINL (TABLE, @BRCHAR)
          Waits for a line to be typed, then does TTYIN.

"STR" _ TTYINS (TABLE, @BRCHAR)
          Sets  BRCHAR to  0  and returns  NULL  if no  lines  are waiting.
          Otherwise it is TTYINL.

OLDVAL _ TTYUP (NEWVAL)
          Causes conversion  of lower case  characters (a-z) to  their upper
          case  equivalents for  strings read  by any  of the  Sail teletype
          routines that  do not use  break tables.  If  NEWVAL is  TRUE then
          conversion  will  take  place  on  all  subsequent   inputs  until
          TTYUP(FALSE) is called.  OLDVAL will be set to the  previous value
          of the conversion flag.  If  TTYUP has never been called,  then no
          conversions  will take  place, and  the first  call to  TTYUP will
          return FALSE.  In TENEX, TTYUP sets the system parameter using the
          STPAR jsys to convert to upper case.

 PSEUDO-TELETYPE FUNCTIONS LODED PTYALL PTCHRS PTCHRW PTOCHS PTOCHW PTOCNT PTIFRE PTOSTR PTYGET PTYGTL PTYIN


***********************  PSEUDO-TELETYPE FUNCTIONS  **********************;


Pseudo-teletype functions are available at SUAI only.

LODED ("STR")
          Loads the line editor with the string argument.  PTOSTR  should be
          used rather than LODED if possible, since LODED works only on a DD
          or III, while PTOSTR works on all terminals.

"STR" _ PTYALL (LINE)
          Returns whatever  is in  the PTY's output  buffer.  No  waiting is
          done.

CHAR _ PTCHRS (LINE)
          Reads a  character from  the PTY if  there is  one, returns  -1 if
          none.

CHAR _ PTCHRW (LINE)
          Waits for a character from the PTY and returns it.

PTOCHS (LINE, CHAR)
          Tries  to  send  a  character  to  a  PTY.   If  the  attempt  was
          successful, the global variable SKIP is -1, otherwise 0.

PTOCHW (LINE, CHAR)
          Sends a character to a PTY, waiting if necessary.

NUMBER _ PTOCNT (LINE)
          Returns the number of free characters in the PTY output buffer.

NUMBER _ PTIFRE (LINE)
          Returns the number of free characters in the PTY input buffer.

PTOSTR (LINE, "STR")
          Sends the  string to  the PTY,  waiting if  necessary.  PTOSTR (0,
          "STR") sends the string to your TTY.

LINE _ PTYGET
          Gets a new pseudo-teletype line number and returns it.  The global
          variable SKIP is -1 if the attempt to get a PTY  was successful,
          and 0 otherwise.

CHARACTERISTICS _ PTYGTL (LINE)
          Returns line characteristics for the PTY.

"STR" _ PTYIN (LINE, BKTBL, @BRCHAR)
          Reads from the PTY (waiting if necessary) according to break table
          conventions.  The break character is stored in BRCHAR.

 PTYREL PTYSTL PTYSTR Example of TOPS-10 I/O


PTYREL (LINE)
          Releases PTY identified by LINE.

PTYSTL (LINE, CHARACTERISTICS)
          Sets line characteristics for the PTY specified by LINE.

"STR" _ PTYSTR (LINE, BRCHAR)
          Reads  characters  from the  PTY,  waiting if  necessary,  until a
          character equal to BRCHAR is seen.  All but the break character is
          returned as the string.  If the break character was  '15 (carriage
          return),  the following  character is  snarfed (on  the assumption
          that it is a linefeed).



6.6  Example of TOPS-10 I/O

BEGIN "COPY"
COMMENT copies a text file, inserting a semicolon at the
beginning of each line, deleting SOS line numbers and
zero bytes, if any.  Prints the page number as it goes;

REQUIRE "[][]" DELIMITERS;
DEFINE CRLF=[('15&'12)],LF=['12], FF=['14];
INTEGER COLONTAB;

RECORDCLASS $FILE (STRING DEVICE, NAME;
  INTEGER CHANNEL, MODE, IBUF, OBUF,
    COUNT, BRCHAR, EOF, LINNUM, PAGNUM, SOSNUM);

RECORDPOINTER($FILE) PROCEDURE OPENUP
  (STRING FILNAM; INTEGER MODE, IBUF, OBUF);
BEGIN "OPENUP"
STRING T; RECORDPOINTER ($FILE) Q; INTEGER BRK;
Q_NEWRECORD ($FILE); T_SCAN (FILNAM, COLONTAB, BRK);
$FILE:DEVICE[Q]_(IF BRK=":" THEN T ELSE "DSK");
$FILE:NAME[Q]_(IF BRK=":" THEN FILNAM ELSE T);
$FILE:MODE[Q]_MODE; $FILE:IBUF[Q]_IBUF;
$FILE:OBUF[Q]_OBUF; OPEN ($FILE:CHANNEL[Q]_GETCHAN,
  $FILE:DEVICE[Q], MODE, IBUF, OBUF, $FILE:COUNT[Q],
  $FILE:BRCHAR[Q], $FILE:EOF[Q]_-1);

IF NOT($FILE:EOF[Q]) THEN BEGIN
  SETPL ($FILE:CHANNEL[Q], $FILE:LINNUM[Q],
    $FILE:PAGNUM[Q], $FILE:SOSNUM[Q]);
  IF IBUF THEN
    LOOKUP ($FILE:CHANNEL[Q], $FILE:NAME[Q], $FILE:EOF[Q]);
  IF OBUF AND NOT ($FILE:EOF[Q]) THEN
    ENTER ($FILE:CHANNEL[Q], $FILE:NAME[Q], $FILE:EOF[Q]);
END;
$FILE:PAGNUM[Q]_1;
IF $FILE:EOF[Q] THEN RELEASE($FILE:CHANNEL[Q]);
RETURN(Q)
END "OPENUP";
COMMENT Sail I/O should be rewritten to do this ^;




RECORDPOINTER ($FILE) PROCEDURE GETFILE
  (STRING PROMPT; INTEGER MODE, I, O);
BEGIN "GETFILE"
RECORDPOINTER ($FILE) F; INTEGER REASON;
WHILE TRUE DO BEGIN "try"
  PRINT (PROMPT);
  IF (REASON_$FILE:EOF[F_OPENUP (INCHWL,
    MODE, I, O)])=0 THEN RETURN (F);
  IF REASON=-1 THEN
    PRINT ("Device ", $FILE:DEVICE[F], " not available.")
  ELSE PRINT("Error, ", CASE (0 MAX REASON MIN 4) OF
    ("no such file ", "illegal PPN ", "protection ",
        "busy ", "??? "), $FILE:NAME[F], CRLF);
END "try";
END "GETFILE";


RECORDPOINTER ($FILE) SRC, SNK;
INTEGER FFLFTAB;

SETBREAK (COLONTAB_GETBREAK, ":", "", "ISN");
WHILE TRUE DO BEGIN "big loop"
    STRING LINE;
    SRC_GETFILE ("Copy from:", 0, 5, 0);
    $FILE:COUNT[SRC]_200;
    SNK_GETFILE (" to:", 0, 0, 5);
    SETBREAK (FFLFTAB_GETBREAK, FF&LF, "", "INA");

    WHILE TRUE DO BEGIN "a line"
        LINE_INPUT ($FILE:CHANNEL[SRC], FFLFTAB);
        IF $FILE:EOF[SRC] THEN DONE;
        IF $FILE:BRCHAR[SRC]=FF THEN BEGIN
          PRINT (" ", $FILE:PAGNUM[SRC]);
          LINE_LINE&
            INPUT ($FILE:CHANNEL[SRC], FFLFTAB) END;
        CPRINT ($FILE:CHANNEL[SNK], ";", LINE)
    END "a line";
    RELEASE ($FILE:CHANNEL[SRC]);
    RELEASE ($FILE:CHANNEL[SNK])
END "big loop";
END "COPY"

EXECUTION TIME ROUTINES Type Conversion Routines SETFORMAT GETFORMAT


                                 SECTION  7

                          EXECUTION TIME ROUTINES


Please  read  Execution  Time  Routines in  General,  page  62,  if  you are
unfamiliar with the format used to describe runtime routines.



7.1  Type Conversion Routines



*******************************  SETFORMAT  ******************************;

SETFORMAT (WIDTH, DIGITS)

This function allows specification of a minimum width for strings created by
the functions CVS, CVOS, CVE, CVF, and CVG (see page 86 and  following).  If
WIDTH  is positive  then enough  blanks  will be  inserted in  front  of the
resultant string  to make the  result at least  WIDTH characters  long.  The
sign,  if any,  will appear  after the  blanks.  If  WIDTH is  negative then
leading zeroes will be used in  place of blanks.  The sign, of  course, will
appear before the zeroes.  The parameter WIDTH is initialized by  the system
to zero.

In addition, the DIGITS parameter allows one to specify the number of digits
to appear following  the decimal point in  strings created by CVE,  CVF, and
CVG.  This number is initially  7.  See the writeups on these  functions for
details.

NOTE: All type conversion  routines, including those that  SETFORMAT applies
to,  are  performed  at  compile  time  if  their  arguments  are constants.
However,  Setformat  does  not   have  its  effect  until   execution  time.
Therefore, CVS, CVOS,  CVE, CVF, and CVG  of constants will have  no leading
zeros and 7 digits (if any) following the decimal point.



*****************************  GETFORMAT  ******************************;

GETFORMAT (@WIDTH, @DIGITS)

The  WIDTH and  DIGIT  settings specified  in  the last  SETFORMAT  call are
returned in the appropriate reference parameters.

 CVS CVD CVOS CVO CVE, CVF, CVG


**********************************  CVS  *********************************;

"ASCIISTRING" _ CVS (VALUE);

The decimal  Integer representation of VALUE is produced as an  ASCII String
with leading zeroes omitted (unless WIDTH has been set by SETFORMAT  to some
negative value).  "-" will  be concatenated to  the String  representing the
decimal absolute value of VALUE if VALUE is negative.



**********************************  CVD  *********************************;

VALUE _ CVD ("ASCIISTRING")

ASCIISTRING  should  be  a  String  of  decimal  ASCII  characters  perhaps
preceded by plus and/or minus  signs.  Characters with ASCII values   SPACE
('40) are  ignored preceding  the number.   Any character  not a  digit will
terminate  the  conversion (with  no  error indication).   The  result  is a
(signed) integer.



**********************************  CVOS  ********************************;

"ASCIISTRING" _ CVOS (VALUE)

The octal  Integer representation of  VALUE is produced  as an  ASCII String
with leading  zeroes omitted  (unless WIDTH  has been  set to  some negative
value by SETFORMAT.  No "-" will be used to indicate negative  numbers.  For
instance, -5 will be represented as "777777777773".



**********************************  CVO  *********************************;

VALUE _ CVO ("ASCIISTRING")

This function is the same as CVD except that the input characters are deemed
to represent Octal values.



*****************************  CVE, CVF, CVG  ****************************;

"STRING" _ CVE (VALUE);
"STRING" _ CVF (VALUE);
"STRING" _ CVG (VALUE)

Real number output  is facilitated by means  of one of three  functions CVE,

 CVASC, CVASTR, CVSTR


CVG, or CVF, corresponding to the  E, G, and F formats of FORTRAN  IV.  Each
of these  functions takes as  argument a real  number and returns  a string.
The  format  of the  string  is controlled  by  another  function  SETFORMAT
(WIDTH, DIGITS) (see page  85) which is used  to change WIDTH from  zero and
DIGITS from  7, their  initial values.  WIDTH  specifies the  minimum string
length.   If  WIDTH is  positive  leading  blanks will  be  inserted  and if
negative leading zeros will be inserted.

The following table indicates the strings returned for some typical numbers.
 indicates a space and it is assumed that WIDTH_10 and DIGITS_3.

          CVF          CVE             CVG
      .000   .100@-3      .100@-3
      .001   .100@-2      .100@-2
      .010   .100@-1      .100@-1
      .100   .100      .100
      1.000   .100@1      1.00
      10.000   .100@2      10.0
      100.000   .100@3      100.
      1000.000   .100@4      .100@4
      10000.000   .100@5      .100@5
     100000.000   .100@6      .100@6
    1000000.000   .100@7      .100@7
    -1000000.000   -.100@7      -.100@7

The first character ahead of the  number is either a blank or a  minus sign.
With WIDTH_-10 plus and minus 1 would print as:

         CVF          CVE             CVG
      00001.000   0.100@1      01.00
      -00001.000   -0.100@1      -01.00

All numbers  are accurate to  one unit  in the eighth  digit.  If  DIGITS is
greater than 8, trailing zeros are included; if less than eight,  the number
is rounded.



**************************  CVASC, CVASTR, CVSTR  ************************;

VALUE _ CVASC ("STRING");
"STRING" _ CVASTR (VALUE);
"STRING" _ CVSTR (VALUE)

These routines  convert between a  Sail String and  an integer  containing 5
ASCII characters left justified in a 36-bit word; the extra bit is made zero
(CVASC) or ignored  (CVASTR, CVSTR).  CVASC  converts from String  to ASCII.
Both CVSTR  and CVASTR  convert from  a word  of ASCII  to a  string.  CVSTR
always returns a string of length five, while CVASTR stops converting at the
first null ('0) character.

 CV6STR, CVSIX, CVXSTR String Manipulation Routines EQU LENGTH



    CVASTR (CVASC ("ABC")) is "ABC"
    CVSTR (CVASC ("ABC")) is "ABC" & 0 & 0



*************************  CV6STR, CVSIX, CVXSTR  ************************;

"STRING" _ CV6STR (VALUE);
VALUE _ CVSIX ("STRING");
"STRING" _ CVXSTR (VALUE)

The routines CV6STR, CVSIX, and  CVXSTR are the SIXBIT analogues  of CVASTR,
CVASC, and CVSTR, respectively.  The character codes are converted, ASCII in
the  String   SIXBIT in  the integer.   CVXSTR always  returns a  string of
length six, while CV6STR stops converting upon reaching a null character.

    CV6STR (CVSIX ("XYZ")) is "XYZ", not "XYZ   ".
    CV6STR (CVSIX ("X Y Z")) is "X", not "X Y Z" or "XYZ".



7.2  String Manipulation Routines



**********************************  EQU  *********************************;

VALUE _ EQU ("STR1", "STR2")

The value of this function is TRUE if STR1 and STR2 are equal in  length and
have identically the same characters in them (in the same order).  The value
of EQU is FALSE otherwise.



*********************************  LENGTH  *******************************;

VALUE _ LENGTH ("STRING")

LENGTH is always an integer-valued  function.  If the argument is  a String,
its length  is the number  of characters  in the string.   The length  of an
algebraic expression is always 1 (see page 46).  LENGTH is  usually compiled
in line.

 LOP SUBSR, SUBST Liberation-from-Sail Routines CODE


**********************************  LOP  *********************************;

VALUE _ LOP (@STRINGVAR)

The LOP operator  applied to a String  variable removes the  first character
from the  String and returns  it in the  form given in  page 46  above.  The
String no longer contains this character.  LOP applied to a null  String has
a zero value.   LOP is usually  compiled in line.  LOP  may not appear  as a
statement.



******************************  SUBSR, SUBST  ****************************;

"RSLT" _ SUBSR ("STRING", LEN, FIRST);
"RSLT" _ SUBST ("STRING", LAST, FIRST)

These  routines  are  the ones  used  for  performing  substring operations.
SUBSR (STR, LEN, FIRST) is  STR[FIRST FOR LEN] and SUBST (STR,  LAST, FIRST)
is STR[FIRST TO LAST].



7.3  Liberation-from-Sail Routines



**********************************  CODE  ********************************;

RESULT _ CODE (INSTR, @ADDR)

This function is equivalent to the FAIL statements:

    EXTERNAL    .SKIP.  ;DECLARE AS SKIP IN SAIL
    SETOM       .SKIP.  ;ASSUME SKIP
    MOVE        0,INSTR
    ADDI        0,@ADDR
    XCT         0
    SETZM       .SKIP.  ;DIDN'T SKIP
    RETURN      (1)

In other words, it executes the instruction formed by adding the  address of
the ADDR  variable (passed by  reference) to the  number INSTR.   Before the
operation is carried out, AC1  is loaded from a special cell  (initially 0).
AC1 is returned as  the result, and also  stored back into the  special cell
after the instruction  is executed.  The  global variable SKIP  (.SKIP. in
DDT or FAIL) is FALSE (0) after the call if the executed instruction did not
skip;  TRUE   (currently  -1)   if  it  did.    Declare  this   variable  as
EXTERNAL INTEGER SKIP if you want to use it.

 CALL CALLI USERCON GOGTAB


**********************************  CALL  ********************************;

RESULT _ CALL (VALUE, "FUNCTION")

This function is equivalent to the FAIL statements:

    EXTERNAL    .SKIP.
    SETOM       .SKIP.
    MOVE        1,VALUE
    CALL        1,[SIXBIT /FUNCTION/]
    SETZM       .SKIP.          ;DID NOT SKIP
    RETURN      (REGISTER 1)

TENEX users should see more on CALL, page 148.



******************************  CALLI  ********************************;

RESULT _ CALLI (VALUE, FUNCTION)

(TYMSHARE only.)  Like CALL, only CALLI.



********************************  USERCON  *******************************;

USERCON (@INDEX, @VALUE, FLAG)

This function  allows inspection  and alteration of  the "User  Table".  The
user table is always loaded with your program and contains  many interesting
variables.  Declare an  index you are interested  in as an  External Integer
(e.g., EXTERNAL INTEGER  REMCHR).  This will,  when loaded, give  an address
which is secretly a small Integer index into the User Table.  When passed by
reference, this index  is available to USERCON.   The names and  meanings of
the various User Table indices can be found in the file HEAD,  wherever Sail
compiler program text files are sold.

USERCON always returns the current value of the appropriate User Table entry
(the Global Upper Segment Table is used if FLAG is negative and  your system
knows about such things).  If FLAG is odd, the contents of VALUE  before the
call replaces the old value in the selected entry of the selected table.

By now the incredible  danger of this feature  must be apparent to  you.  Be
sure you understand  the ramifications of any  changes you make to  any User
Table value.  GOGTAB
Direct access to the user table can be gained by declaring  EXTERNAL INTEGER
ARRAY GOGTAB[0:n];  The clumsy USERCON linkage is obsolete.

The  symbolic names  of  all GOGTAB  entries  can be  obtained  by requiring

 USERERR ERMSBF EDFILE


SYS:GOGTAB.DEF  (<SAIL>GOGTAB.DEF on  TENEX) as  a source  file.   This file
contains DEFINEs for all of the user table entries.



********************************  USERERR  *******************************;

USERERR (VALUE, CODE, "MSG",
                "RESPONSE"(NULL))

USERERR generates an error message.   See page 243 for a description  of the
error  message format.   MSG is  the error  message that  is printed  on the
teletype or sent to the log file.  If CODE = 2, VALUE is printed  in decimal
on the same line.  Then on the next line the "Last SAIL call" message may be
typed which indicates where in the user program the error occurred.  If CODE
is 1 or 2, a "" will  be typed and  execution will be allowed  to continue.
If it  is 0, a  "?" is typed,  and no continuation  will be  permitted.  The
string RESPONSE, if included in the USERERR call, will be scanned before the
input buffer  is scanned.   In fact,  if the  string RESPONSE  satisfies the
error handler, the input buffer will not be scanned at all.  Examples:

    USERERR (0, 1, "LINE TOO LONG"); Gives
        error message and allows continuation.

    USERERR (0, 1, NULL, "QLA"); Resets mode
        of error handler to Quiet, Logging, and
        Automatic continuation.  Then continues.



******************************  ERMSBF  *******************************;

ERMSBF (NEWSIZE)

This  routine  insures that  error  messages of  NEWSIZE  characters  can be
handled.  The  error message  buffer is initially  256 characters,  which is
sufficient  for  any  Sail-generated  error.   USERERR  can  generate longer
messages, however.



*********************************  EDFILE  *******************************;

EDFILE ("FILENAME", LINE, PAGE, BITS(0))

(Not on TENEX.) Exits to an editor.  Which editor is determined by  the bits
which are on in  the second parameter, LINE.  If  bit 0 or bit  1 (600000,,0
bits) is on, then LINE is assumed to be ASCID and SOS is called.  If neither
of  these bits  is  on, then  LINE  is assumed  to  be of  the  form  attach
count,,sequential line  number and  E is  called.  PAGE  is the  binary page
number.  BITS defaults to zero and controls the editing mode.

 INIACS Byte Manipulation Routines LDB, DPB, etc. POINT



    0  edit
    1  no directory (as in /N)
    2  readonly (as in /R)
    4  create (as in /C)

In addition, the accumulators are set up from INIACS (see below) so that the
E command X RUN will run  the dump file from which the current  program was
gotten.   [Accumulators 0  (file name),  1 (extension),  and 6  (device) are
loaded from the corresponding values in INIACS.]



*********************************  INIACS  *******************************;


The contents  of locations  0-'17 are saved  in block  INIACS when  the core
image is started for the first time.  Declare INIACS as an  external integer
and use STARTCODE or MEMORY[LOCATION(INIACS)+n] to reference this block.



7.4  Byte Manipulation Routines



*****************************  LDB, DPB, etc.  ***************************;

VALUE _ LDB (BYTEPOINTER);
VALUE _ ILDB (@ BYTEPOINTER);
DPB (BYTE, BYTEPOINTER);
IDPB (BYTE, @ BYTEPOINTER);
IBP  (@ BYTEPOINTER)

LDB, ILDB, DPB, IDPB, and IBP are Sail constructs used to invoke  the PDP-10
byte loading instructions.  The arguments to these functions are expressions
which are  interpreted as  byte pointers and  bytes.  In  the case  of ILDB,
IDPB, and IBP, you are required to use an algebraic variable as  argument as
the bytepointer,  so that  the byte pointer (i.e. that  algebraic variable)
may be incremented.



*********************************  POINT  ********************************;

VALUE _ POINT (BYTE SIZE,
        @EFFECTIVE ADDRESS, LAST BIT NUMBER)

POINT  returns a  byte pointer  (hence it  is of  type integer).   The three
arguments correspond exactly to  the three arguments to the  POINT pseudo-op
in FAIL.

Other Useful Routines CVFIL FILEINFO ARRINFO


7.5  Other Useful Routines



*********************************  CVFIL  ********************************;

VALUE _ CVFIL ("FILESPEC", @EXTEN, @PPN)

FILESPEC  has the  same form  as a  file name  specification for  LOOKUP or
ENTER.  The SIXBIT  for the file name  is returned in VALUE.   SIXBIT values
for  the  extension  and  project-programmer  numbers  are  returned  in the
respective reference parameters.  Any unspecified portions of  the FILESPEC
will result  in zero values.   The global  variable SKIP will  be 0  if no
errors  occurred,  non-zero  if  an  invalid  file  name   specification  is
presented.



********************************  FILEINFO  ******************************;

FILEINFO (@INFOARRAY)

FILEINFO fills the 6-word array INFOARRAY with the following six  words from
the most recent LOOKUP, ENTER, or RENAME:

        FILENAME
        EXT,,(2)hidate2 (15)date1
        (9)prot (4)Mode (11)time (12)lodate2
        negative swapped word count
        0 (unless opened in magic mode)
        0

See [SysCall]; TENEX users should use JFNS instead.



********************************  ARRINFO  *******************************;

VALUE _ ARRINFO (ARRAY, PARAMETER)

   ARRINFO (ARRAY, -1)    is the number  of dimensions for the  array.  This
                    number is negative for String arrays.

   ARRINFO (ARRAY, 0)     is the total size of the array in words.

   ARRINFO (ARRAY, 1)     is the lower bound for the first dimension.

 ARRBLT ARRTRAN ARRCLR INCONTEXT


   ARRINFO (ARRAY, 2)     is the upper bound for the first dimension.

   ARRINFO (ARRAY, 3)     is the lower bound for the second dimension.

   ARRINFO (...     etc.



*********************************  ARRBLT  *******************************;

ARRBLT (@DEST, @SOURCE, NUM)

NUM words are transferred (using BLT) from consecutive locations starting at
SOURCE to  consecutive locations  starting at DEST.   No bounds  checking is
performed.  This function does not work well for String Arrays (nor  set nor
list arrays).



********************************  ARRTRAN  *******************************;

ARRTRAN (DESTARR, SOURCEARR)

This function  copies information from  SOURCEARR to DESTARR.   The transfer
starts at the first  data word of each array.   The minimum of the  sizes of
SOURCEARR and DESTARR is the number of words transferred.



*********************************  ARRCLR  *******************************;

ARRCLR (ARRAY, VALUE(0))

This routine stores VALUE into  each element of ARRAY.  The most  common use
is with VALUE omitted, which  clears the array; i.e., arithmetic  arrays get
filled  with zeros,  string  arrays with  NULLs, itemvar  arrays  with ANYs,
recordpointer arrays  with NULLRECORD.   One may use  ARRCLR with  set and
list arrays,  but the  set and list  space will  be lost  (i.e., un-garbage-
collectible).   Do not  supply anything  other than  0 (0,  NULL,  PHI, NIL,
NULLRECORD) for VALUE when clearing a string, set, list,  or recordpointer
array unless you know what you are doing.  Using a real value for an itemvar
array is apt to  cause strange results.  (If  you use an integer  then ARRAY
will be filled with CVI (value).)



*******************************  INCONTEXT  *****************************;

VALUE _ INCONTEXT (VARI, CONTXT)

 CHNCDB Numerical Routines SIN COS SIND COSD ASIN ACOS ATAN ATAN2 SINH COSH TANH SQRT


INCONTEXT is a boolean which tells one if the specified variable is  in the
specified context.  VARI may be any variable, array element, array  name, or
Leap variable.  If  that variable, element or  array was REMEMBERed  in that
context, INCONTEXT will return  True.  INCONTEXT will also return  true if
VARI is an array element and the whole array was Remembered in  that context
(by using REMEMBER  <arrayname>).  On the other  hand, if VARI is  an array
name, then INCONTEXT will return true only if one has Remembered that array
with a REMEMBER <arrayname>.



*********************************  CHNCDB  *******************************;

VALUE _ CHNCDB (CHANNEL)

(Not on TENEX.) This integer  procedure returns the address of the  block of
storage  which Sail  uses to  keep track  of the  specified channel.   It is
provided for the benefit  of  assembly language procedures that may  want to
do I/O inside  some fast inner loop,  but which may want  to live in  a Sail
core image & use the Sail OPEN, etc.



7.6  Numerical Routines

These  numerical routines  are  new as  predeclared runtimes  in  Sail.  The
routines themselves are quite standard.

The standard  trigonometric functions.   ASIN, ACOS,  ATAN and  ATAN2 return
results in radians.  The ATAN2 call takes arc-tangent of the quotient of its
arguments; in this way, it correctly preserves sign information.

        REAL PROCEDURE SIN (REAL RADIANS);
        REAL PROCEDURE COS (REAL RADIANS);
        REAL PROCEDURE SIND (REAL DEGREES);
        REAL PROCEDURE COSD (REAL DEGREES);

        REAL PROCEDURE ASIN (REAL ARGUMENT);
        REAL PROCEDURE ACOS (REAL ARGUMENT);
        REAL PROCEDURE ATAN (REAL ARGUMENT);
        REAL PROCEDURE ATAN2 (REAL NUM, DEN)

The hyperbolic trigonometric functions.

        REAL PROCEDURE SINH (REAL ARGUMENT);
        REAL PROCEDURE COSH (REAL ARGUMENT);
        REAL PROCEDURE TANH (REAL ARGUMENT)

The square-root function:

        REAL PROCEDURE SQRT (REAL ARGUMENT)

 RAN LOG EXP TRIGINI OVERFLOW ENTRY POINTS OVERFLOW IMPLEMENTATION


A pseudo-random number  generator.  The argument  specifies a new  value for
the seed (if  the argument is 0,  the old seed value  is used.  Thus  to get
differing  random  numbers,  this argument  should  be  zero.)   Results are
normalized to lie in the range [0,1].

        REAL PROCEDURE RAN (INTEGER SEED)

Logarithm and exponentiation functions.   These functions are the  same ones
used by the Sail exponentiation operator.  The base is e (2.71828182845904).
The logarithm to the base 10 of e is 0.4342944819.

        REAL PROCEDURE LOG (REAL ARGUMENT);
        REAL PROCEDURE EXP (REAL ARGUMENT)

These  functions  may occasionally  be  asked to  compute  numbers  that lie
outside the range of legal  floating-point numbers on the PDP-10.   In these
cases, the routines issue sprightly error messages that are continuable.

OVERFLOW
In order  to better perform  their tasks, these  routines enable  the system
interrupt facility for floating-point overflow and underflow errors.   If an
underflow is detected, the results are set to 0 (a feat not done by the PDP-
10 hardware,  alas).  Be aware  that such underflow  fixups will be  done to
every underflow  that occurs  in your  program.  For  further implementation
details, see the section below.

If you would like to be  informed of any numerical exceptions, you  can call
the runtime:

        TRIGINI (LOCATION (simple-procedure-name))

Every floating-point exception that is not expected by the interrupt handler
(the numerical routines use a special convention to indicate that arithmetic
exception  was expected)  will cause  the specified  simple procedure  to be
called.  This procedure may look around the world as described  for 'export'
interrupt handlers,  page 212.  If  no TRIGINI call  is done,  the interrupt
routine will simply dismiss unexpected floating-point interrupts.

ENTRY POINTS
In order to  avoid confusion (by the  loader) with older trig  packages, the
entry points of the Sail arithmetic routines all have a "$" appended  to the
end.  Thus, SIN has the entry point SIN$, etc.  WARNING: If a  program plans
to use the Sail intrinsic numerical routines, it should NOT include external
declarations to  them, since  this will probably  cause the  FORTRAN library
routines to be loaded.

OVERFLOW IMPLEMENTATION
This  section may  be skipped  by all  but those  interested  in interfacing
number crunching assembly code (where overflow and underflow are expected to
be a problem) with Sail routines.



The Sail arithmetic interrupt routines  first check to see if  the interrupt
was caused by  floating exponent underflow.  If  it was, then the  result is
set  to  zero, be  it  in an  accumulator,  memory, or  both.   Then  if the
arithmetic instruction that caused the interrupt is followed by a  JFCL, the
AC field of the JFCL  is compared with the PC  flag bits to see if  the JFCL
tests for any of the flags that are on.  If it does, those flags are cleared
and the  program proceeds at  the effective address  of the JFCL  (i.e., the
hardware  is  simulated  in  that  case).   Note  that  no  instructions may
intervene  between the  interrupt-causing instruction  and the  JFCL  or the
interrupt routines will  not see the JFCL.   They only look  one instruction
ahead.  Note that in any case, floating exponent underflow always causes the
result to be set to zero.  There is no way to disable that effect.

PRINT Syntax Semantics DEFAULT FORMATS


                                 SECTION  8

                                   PRINT




8.1  Syntax

<printstatement>
    ::= PRINT ( <expressionlist> )
    ::= CPRINT ( <integerexpression> ,
        <expressionlist> )



8.2  Semantics

The new constructs PRINT and CPRINT are conveniences for  handling character
output.  Code which formerly looked like

    OUTSTR ("The values are " & CVS (I) & " and " &
         CVG (X) & " for item " & CVIS (IT, JUNK));

may now be written

    PRINT ("The values are ", I, X, " for item ", IT);

The  first  expression in  <expressionlist>  is evaluated,  formatted  as a
string,  and  routed  to  the  appropriate  destination.   Then  the  second
expression is evaluated, formatted, and dispatched; etc.  (If  an expression
is  an assignment  expression  or a  procedure  call then  side  effects may
occur.)

DEFAULT FORMATS
String  expressions  are  simply  sent  to  the  output   routine.   Integer
expressions are first sent to  CVS, and Real expressions are passed  to CVG;
the current SETFORMAT parameters  are used.  Item expressions use  the print
name for the item if one exists, otherwise ITEM!nnnn, where nnnn is the item
number.   Sets and  lists show  their item  components separated  by commas.
Sets are surrounded  by single braces and  lists by double braces.   PHI and
NIL  are printed  for the  empty set  and empty  list  respectively.  Record
pointers are formatted as the name  of the record class, followed by  a ".",
followed by the (decimal) address of the record.  NULL!RECORD is printed for
the empty record.

If the default format is not satisfactory then the user may give  a function
call as an argument.  For example,

        PRINT (CVOS (I));

 DESTINATIONS SETPRINT, GETPRINT


will print I in octal, since CVOS is called first.  (The expression CVOS (I)
is  of course  a String  expression.) Wizards  may also  change  the default
formatting function for a given syntactic type.

DESTINATIONS
CPRINT interprets  <integerexpression> as a  Sail channel number  and sends
all output to that  channel.  The following two statements  are functionally
equivalent:

    CPRINT (CHAN, "The values are ", I, " and ", X);
    OUT (CHAN, "The values are "&CVS (I)&" and "&CVG (X));

PRINT initially sends all output to the terminal but can also  direct output
to a file or  any combination of terminal  and/or file.  The modes  of PRINT
are (dynamically) established and queried by SETPRINT and GETPRINT.



***************************  SETPRINT, GETPRINT  *************************;

SETPRINT ("FILENAME", "MODE");
"MODE" _ GETPRINT

Here MODE is  a single character which  represents the destination  of PRINT
output.

MODE    MEANING

"T"     the Terminal gets  all PRINT output.  If  an output file  is open
        then close it.  "T" is the mode in which PRINT is initialized.

"F"     File gets  PRINT output.   If no file  is open  then open  one as
        described below.

"B"     Both terminal and file get PRINT output.  If no file is open then
        open one as described below.

"N"     Neither the file nor the terminal gets any output.  If a  file is
        open then close it.

"S"     Suppress all output, but open a file if none is open.

"O"     a file is  Open, but the terminal  is getting all output.   If no
        file is open then open one as described below.

"C"     the terminal  gets output, but  ignore whether or  not a  file is
        open and whether or not it is getting output.

"I"     terminal does not  get output.  Ignore whether  or not a  file is
        open and whether or not file is getting any output.

 SIMPLE USE CAVEATS


The first 6 possibilities represent  the logical states of the  PRINT system
and are  the characters which  GETPRINT can return.   The "C" and  "I" modes
turn terminal output on and off without disturbing anything else.  The PRINT
statement is initialized to mode "T" -- print to Terminal.  Modes  "T", "F",
and "B"  are probably  the most useful.   The other  modes are  included for
completeness  and  allow the  user  to switch  between  various combinations
dynamically.

If SETPRINT is called in  such a way that a  file has to be opened  -- e.g.,
mode "F" and no file is open  -- then FILENAME will be used as the  name of
the output file.   If FILENAME is NULL  then the filename will  be obtained
from the terminal.

    SETPRINT (NULL, "F");

first types the message

    File for PRINT output  *

and uses the response as the name  of a file to open.  On TENEX,  GTJFN with
recognition is used; on TOPS-10  and its variants the filename is  read with
INCHWL.   The  file opened  by  SETPRINT  will be  closed  when  the program
terminates by  falling through the  bottom.  It will  also be closed  if the
user calls SETPRINT with  some mode that closes  the file -- e.g.,  "T" will
close an output file if one is open.

SETPRINT and  GETPRINT are  related only to  PRINT; they  have no  effect on
CPRINT.

SIMPLE USE
Here are a few examples of common output situations.

1)  PRINT to TERMINAL.  Simply use PRINT; do not bother with SETPRINT.

2)  PRINT  to FILE.  Call  SETPRINT (NULL,  "F"); and type  the name  of the
   output file when it asks.

3)   PRINT to  FILE and  TERMINAL.   At the  beginning of  the  program call
   SETPRINT (NULL, "B"); and type the name when asked.

4)   PRINT to  FILE always  and sometimes  also to  TERMINAL.   Use SETPRINT
   (NULL, "B");  and give  the name  of the  file when  it asks.   This sets
   output to both  the terminal and the  file.  Then to ignore  the terminal
   (leaving the file alone), call SETPRINT (NULL, "I");  To resume output at
   the terminal use SETPRINT (NULL,  "C");   This is useful for  obtaining a
   cleaned-up  printout  on  the  file  with  error  messages  going  to the
   terminal.

CAVEATS
Trying to  exploit the normal  Sail type conversions  will probably  lead to

 FOR WIZARDS ONLY


trouble  with  PRINT and  CPRINT.   Printing single  ASCII  characters  is a
particular problem.

    OUTSTR ('14);

prints a form-feed onto the terminal , but

    PRINT ('14);

prints "12".  The reason, of  course, is the default formatting  of integers
by PRINT or  CPRINT.  This problem is  particularly severe with  macros that
have been  defined with  an integer  to represent  an ASCII  character.  For
example,

    DEFINE TAB="'11";
    PRINT (TAB);

will print "9".  The solution is to define the macro so that it expands to a
STRING constant rather than an integer.

    DEFINE TAB="       ";  or
    DEFINE TAB=('11 & NULL);

Also, remember that the first argument to CPRINT is the channel number.

FOR WIZARDS ONLY
All output going to either the PRINT or CPRINT statements can be  trapped by
setting user table  entry $$PROU to the  address of a SIMPLE  procedure that
has one string and one integer argument.

    SIMPLE PROCEDURE MYPRINT
         (INTEGER CHAN; STRING S);
    BEGIN ... END;

    GOGTAB[$$PROU] _ LOCATION (MYPRINT);

The CHAN argument is either the  CHAN argument for CPRINT, or -1  for PRINT.
If this trap is set then  all output from PRINT and CPRINT goes  through the
user routine and is not printed  unless the user invokes OUT or  OUTSTR from
within the trap routine itself.

To trap the formatting function  for any syntactic type the user  should set
the  appropriate user  table  address to  the  location of  a  function that
returns a string  and takes as an  argument the syntactic type  in question.
To print integers in octal , preceded by "'", use

    SIMPLE STRING PROCEDURE MYCVOS (INTEGER I);
    RETURN ("'" & CVOS (I));

    GOGTAB[$$FINT] _ LOCATION (MYCVOS);



The  names  for  the  addresses  in  the  user  table  associated  with each
formatting function are:

    INDEXTYPE

    $$FINT INTEGER
    $$FREL REAL
    $$FITM ITEM
    $$FSET SET
    $$FLST LIST
    $$FSTR STRING
    $$FREC RECORDPOINTER

To restore  any formatting  function to  the default  provided by  the PRINT
system, zero the appropriate entry of the user table.

MACROS AND CONDITIONAL COMPILATION Syntax


                                 SECTION  9

                     MACROS AND CONDITIONAL COMPILATION




9.1  Syntax

<define>
    ::= DEFINE <deflist> ;
    ::= REDEFINE <deflist> ;
    ::= EVALDEFINE <deflist> ;
    ::= EVALREDEFINE <deflist> ;


<deflist>
    ::= <def>
    ::= <deflist> , <def>


<def>
    ::= <identifier> = <macrobody>
    ::= <identifier> ( <idlist> ) =
        <macrobody>
    ::= <identifier> <stringconstant> =
        <macrobody>
    ::= <identifier> ( <idlist> )
        <stringconstant> = <macrobody>


<macrobody>
    ::= <delimitedstring>
    ::= <constantexpression>
    ::= <macrobody> & <macrobody>


<macrocall>
    ::= <macroidentifier>
    ::= <macroidentifier> (
        <macroparamlist> )
    ::= <macroidentifier> <stringconstant>
        ( <macroparamlist> )


<macroidentifier>
    ::= <identifier>


<macroparamlist>
    ::= <macroparam>



    ::= <macroparamlist> , <macroparam>


<condcompstatement>
    ::= <conditionalc.c.s.>
    ::= <whilec.c.s.>
    ::= <forc.c.s.>
    ::= <forlistc.c.s.>
    ::= <casec.c.s.>


<conditionalc.c.s.>
    ::= IFC <constantexpression> THENC
        <anything> ENDC
    ::= IFC <constantexpression> THENC
        <anything> ELSEC <anything> ENDC
    ::= IFCR <constantexpression> THENC
        <anything> ENDC
    ::= IFCR <constantexpression> THENC
        <anything> ELSEC <anything> ENDC


<whilec.c.s.>
    ::= WHILEC <delimitedexpr> DOC
        <delimitedanything> ENDC


<forc.c.s.>
    ::= FORC <identifier> _
        <constantexpression> STEPC
        <constantexpression> UNTILC
        <constantexpression> DOC
        <delimitedanything> ENDC


<forlistc.c.s.>
    ::= FORLC <identifier> _ (
        <macroparamlist> ) DOC
        <delimitedanything> ENDC


<casec.c.s.>
    ::= CASEC <constantexpression> OFC
        <delimitedanythinglist> ENDC


<delimitedanythinglist>
    ::= <delimitedanything>
    ::= <delimitedanythinglist> ,
        <delimitedanything>

 Delimiters


<assignc>
    ::= ASSIGNC <identifier> = <macrobody> ;



<delimitedstring>,   <macroparam>,   <delimitedexpr>,    <anything>   and
<delimitedanything> are explained in the following text.



9.2  Delimiters

There are two types of delimiters used by the Sail macro scanner: macro body
delimiters and  macro parameter delimiters.   Their usage will  be precisely
defined in the sections on  Macro Bodies and Parameters to Macros.   Here we
will discuss their declaration and scope, which is very important when using
source files with different delimiters (see page 24 to find out about source
files).

Sail initializes both left and  right delimiters of both body  and parameter
delimiters to the double quote (").  One may change delimiters by saying

       REQUIRE "<>" DELIMITERS.

In this  example, the  left and right  body delimiters  become ""  and "",
while the left and right  parameter delimiters become "<" and  ">".  Require
Delimiters may  appear wherever  a statement or  declaration is  legal.  One
should Require Delimiters whenever all but the most simple macros  are going
to  be  used.   The  first  Require  Delimiters  will  initialize  the macro
facility; if this is not  done, some of the following conveniences  will not
exist and only very simple macros like defining CRLF = "('12 & '15 )" may be
done.

Delimiters  do  not follow  block  structure.  They  persist  until changed.
Furthermore, each time  new delimiters are Required,  they are stacked  on a
special "delimiters stack".  The old delimiters may be revived by saying

       REQUIRE UNSTACKDELIMITERS

Thus, each source file with  macros should begin with a  Require delimiters,
and end  with an Unstackdelimiters.   It is impossible  to Unstack  off the
bottom of the stack.   The bottom element of  the stack is the  double quote
delimiters that Sail initialized the program to.  If you Unstack from these,
the Unstack will become a no-op, and the double quote delimiters  remain the
delimiters of your program.

One may circumvent the delimiter stacking feature by saying

       REQUIRE "<>" REPLACEDELIMITERS

 Macros


instead of REQUIRE "<>"  DELIMITERS. This doesn't deactivate  the stacking
feature, it merely changes the active delimiters without stacking them.

To revert to the primitive, initial delimiter mode  where  double quotes are
the active delimiters, one may say

       REQUIRE NULL DELIMITERS

Null delimiters are stacked in  the delimiter stack in the  ordinary REQUIRE
"<>" DELIMITERS way.  In null delimiters mode, the double  quote character
may be included  in the macro  body or macro  parameter by using  two double
quotes:

       DEFINE SOR = "OUTSTR(""SORRY"");";

The  Null  Delimiters mode  is  essentially the  macro  facility  of ancient
versions of Sail where "  was the only delimiter.  Programs  written ancient
in Sail versions will run in Null Delimiters mode.  Null delimiters mode has
all the rules and quirks of the prehistoric Sail macro system (the  old Sail
macro  facility  is  described  in  [Swinehart  &  Sproull],   Section  13).
Compatibility with the ancient Sail  is the only reason for Null Delimiters.



9.3  Macros

We  will delay  the  discussion of  macros  with parameters  until  the next
section.  A macro without parameters is declared by saying:

       DEFINE <macroname> = <macrobody> ;

where  <macroname>  is some  legal  identifier  name (see  page  228  for a
definition  of a  legal  identifier name).   <macrobody>s can  be  simply a
sequence of Ascii characters delimited by macro body delimiters, or they can
be  quite complex.   Once the  macro  has been  defined, the  macro  body is
substituted for every subsequent  appearance of the macro name.   Macros may
be  called in  this way  at any  point in  a Sail  program, except  inside a
Comment or a string constant.

Macro declarations  may also  appear virtually anywhere  in a  Sail program.
When the  word DEFINE is  scanned by  Sail, the scanner  traps to  a special
production.  The Define  is parsed, and the  scanner returns to  its regular
mode as if there had been no define there at all.  Thus things like

       I _ J + 5 + DEFINE CON = '777; K^2;....

are perfectly acceptable.  However, don't put a Define in a  string constant
or a Comment.

 SCOPE MACRO BODIES DELIMITED STRINGS


SCOPE
Macros obey block structure.  Each  DEFINE serves both as a  declaration and
an assignment of a macro body to the newly declared symbol.  Two  DEFINEs of
the same  symbol in  the at  the same lexical  level will  be flagged  as an
error.  However, it is possible to change the macro body assigned to a macro
name  without  redeclaring the  name  by using  saying  REDEFINE  instead of
DEFINE.  For example,

   BEGIN
   ...
     BEGIN
     ...
     DEFINE SQUAK = OUTSTR("OUTER BLOCK");;
     ...
       BEGIN
       ...
       REDEFINE SQUAK = OUTSTR("INNER BLOCK");;
       ...
       END;
     ...
     SQUAK  COMMENT Here the program types
                 "INNER BLOCK";
     END;   COMMENT Here SQUAK is undefined.
            If SQUAK were included here, you'd
            get the error message
                "UNDEFINED IDENTIFIER:SQUAK";
   END

REDEFINE of  a name that  has not been  declared in a  DEFINE will act  as a
DEFINE.  That is, it will also declared the macro name as well  as assigning
a body to it.  MACRO BODIES
A Macro Body may be

   1. A  sequence of  Ascii  characters preceded  by a  left  macro body
      delimiter and followed by a right macro body delimiter.

   2. An integer expression that may be evaluated at compile time.

   3. A string expression that may be evaluated at compile time.

   4. Concatenations of the above.

WARNING: Source file switching inside macros  will not work.

DELIMITED STRINGS
Any sequence of Ascii characters, including " may be used as a macro body if
they are properly  delimited.  The macro body  scanner keeps a count  of the
number of left  and right delimiters seen  and will terminate its  scan only
when  it  has seen  the  same number  of  each.  This  lets  the  macro body
delimiters "nest" so that one may include DEFINEs inside a macro  body.  For
example,

 INTEGER COMPILE TIME EXPRESSIONS STRING COMPILE TIME EXPRESSIONS



     DEFINE DEF =
        DEFINE SYM = SYMBOL; SYM ;

One  may  temporarily override  the  active delimiters  by  including  a two
character string before the "=" of the Define statement.  For example:

        DEFINE LES "&%" = & 0X<BIGGEST  Y>X %;

The first character of the two character string becomes the  left delimiter,
and the second becomes the right delimiter.

INTEGER COMPILE TIME EXPRESSIONS
Sail  tries  to  do as  much  arithmetic  as it  can  at  compile  time.  In
particular, if you have an arithmetic expression of constants, such as

        91.504 + (3.1415*8^(9-7))
                % "Sail can convert strings"

then  the  whole  expression  will be  evaluated  at  compile  time  and the
resultant  constant, in  this case  93.9263610, will  be used  in  your code
instead of the constant expression.  Runtime functions of constants  will be
done at  compile time  too, if  possible.  EQU  and the  conversion routines
(CVS, CVO, etc.) will work.

When an integer compile time expression is scanned as part of a  macro body,
it  is  immediately  evaluated.   The  integer  constant  which  results  is
converted to  a character  string, and  that character  string used  for the
place in the macro body of the integer expression.  Thus,

        DEFINE TTYUUO = '51 LSH 27 ;

will  cause  '51  LSH  27  to  be  evaluated,  and  the  resulting constant,
5502926848, will be converted  to the character string 5502926848,  and that
character string assigned to the macro name TTYUUO.

STRING COMPILE TIME EXPRESSIONS
If  a compile  time expression  has the  type string  (constant),  the macro
scanner  will  evaluate  the expression  immediately.   However,  the string
constant that  results will not  be converted to  the character  string that
represents  that  constant,   but  to the  character  string  with  the same
characters that the string constant had.   Thus, the way to use a  macro for
string constants is to delimit the string constant like this:

        DEFINE STRINCON = "Very long
            complex string that is hard
                to type more than once"  ;

However, the automatic conversion  of string constants to  character strings
is helpful and indeed essential for automatic generation of identifiers:

 HYBRID MACRO BODIES



  DEFINE N = 1;
          COMMENT we will use this like a variable;

   DEFINE GENSYM = 
      DEFINE SYM = TEMP & CVS(N);
          COMMENT SYM is defined to be the character
          string  TEMP# where # is an number;

       REDEFINE N = N+1;
          COMMENT This increments N;

       SYM  ;
          COMMENT At the call of SYM, the character
          string is  read like program text. E.g...;

   INTEGER GENSYM, GENSYM, GENSYM, GENSYM;
   REAL GENSYM, GENSYM;
          COMMENT We have generated 6 identifiers with
          unique names, and declared 4 as integers,
          2 as reals;

To convert a macro body to a string constant, one may use  CVMS.  Similarly,
a macro parameter is converted to a string constant by CVPS.

   <string constant> _ CVMS (<macro name>);
   <string constant> _ CVPS (<macro parameter name>)

A  string that  has the  exact same  characters as  the macro  body  will be
returned.  For example:

   DEFINE A = B & C;
   DEFINE ABC = CVMS (A) &  & D;
    COMMENT ABC now stands for the text B & C & D;

HYBRID MACRO BODIES
When  two  delimited  strings  are  concatenated,  the  result  is  a longer
delimited string.  "&"  in compile time expression  behaves the same  way it
behaves in any expression.   When a compile time expression  is concatenated
to a delimited character string in  a macro body, the result is  exactly the
result  one  would get  if  the  delimited character  string  were  a string
constant,  except that  the  result is  a delimited  character  string.  For
example:

   DEFINE N = 1;
   DEFINE M = 2;
   DEFINE SYM = CVS(N*M + N^2) & -SQRT(N*M+1);
   DEFINE SYM1 = 3-SQRT(N*M+1);

Here SYM is exactly the same as SYM1.

Macros with Parameters


9.4  Macros with Parameters

One defines a macro with parameters by specifying the formal parameters in a
list following the macro name:

   DEFINE MAC (A, B) = IF A THEN B ELSE ERR_1;;

One calls a macro with parameters by including a list of delimited character
strings that will  be substituted for  each occurrence of  the corresponding
formal in the macro body.  For example,

    COMMENT we assume that "<" and ">" are the
            parameter delimiters at this point;
    MAC (<BYTES LAND (BITMASK + '2000)>, <
            BEGIN
              WWDAT _ FETCH (BYTES, ENVIRON);
              COLOR[WWDAT] _  '2000;
            END  >)

expands to

    IF BYTES LAND (BITMASK + '2000) THEN
            BEGIN
              WWDAT _ FETCH (BYTES, ENVIRON);
              COLOR[WWDAT] _ '2000;
            END
            ELSE ERR_1;

Parameter delimiters nest.  Furthermore,  if no delimiters are used  about a
parameter, nesting counts are kept of "()", "[]", and "{~"  character pairs.
The parameter scan  will not terminate until  the nesting counts of  each of
the three pairs is zero.  One may temporarily override the  active parameter
delimiters by including a two  character string ahead of the  parameter list
in the macro call:

    MAC "" (BYTES > '2000, MATCH(BYTES))

Formal parameters may not appear  in compile time expressions that  are used
to specify macro  bodies.  This is  quite natural: compile  time expressions
must be evaluated as they are  scanned, but the value of a  formal parameter
isn't  known  until  later.  However,  if  the  macro body  is  a  hybrid of
expressions  and delimited  character  strings, then  formal  parameters may
appear in the delimited string parts.

When doing a CVMS on a macro with parameters, use only the macro name in the
call; the parameters are unnecessary.  The string returned will have the two
character strings "1",  "2", etc. (here   stands for the  Ascii character
'177)  where the  formal parameters  were in  the macro  body.  A  "1" will
appear  wherever the  first formal  parameter of  the formal  parameter list
appear in the macro body,  a "2" will appear wherever the  second parameter

 Conditional Compilation


appeared, etc.  The unfortunate  appearance of the  Ascii character  '177 in
CVMS-generated strings is a product of the representation of macro bodies as
strings ending  in '177, '0  (which CVMS removes),  having '177, n  for each
appearance of the nth formal parameter in the body.



9.5  Conditional Compilation

The compile time equivalents of the Sail IF, WHILE, FOR  and CASE statements
are

    IFC <CT expr> THENC <anything> ENDC

    IFC <CT expr> THENC <anything> ELSEC
            <anything> ENDC

    WHILEC <CT expr> DOC <anything> ENDC

    FORC <CT variable> _ <CT expr> STEPC <CT expr>
            UNTILC <CT expr> DOC  <anything> ENDC

    FORLC <CT variable> _ (<macro param>, ... ,
            <macro param>) DOC  <anything> ENDC

    CASEC <CT expr> OFC <anything>, <anything>,
            ... , <anything> ENDC

where  <CT  expr> is any  compile time expression.   <CT expr>  could itself
include IFCs, FORCs or  whatever.  <CT variable> is  a macro name such  as N
from a define such as DEFINE  N = MUMBLE; <macro param> is anything  that is
delimited like a macro parameter.  <anything> can be anything one could want
in  his  program at  that  point, including  Defines  and  other conditional
compilation statements.  The  usual care must be  taken with nested  IFCs so
that the ELSECs match the desired THENCs.  The "" and ""  characters above
are to stand for the current MACRO BODY DELIMITER pair.

The semantics  are exactly  those of  the corresponding  runtime statements,
with one exception.  When the list to a FORLC is null (i.e. it looks like "(
)" ), then the <anything> is inserted in the compilation once, with  the <CT
variable> assigned to the null macro body.

Situations frequently  occur where the  false part of  an IFC must  have the
macros in  it expanded in  order to delimit  the false part  correctly.  For
example,

 Type Determination at Compile Time DECLARATION CHECKTYPE EXPRTYPE



        DEFINE DEBUGSELECT =
           IFC DEBNUM = 2 THENC ;
        DEFINE DEBUGEND  =
           ELSEC OUTSTR ("DEBUG POINT") ENDC;

        Debugselect
              OUTSTR ("DEBUG POINT #" & CVS (DBN));
        Debugend

If DEBNUM  is not 2,  then the  program must expand  the macro  Debugend in
order  to  pick  up  the  ELSEC   that  terminates  the  false  part  of the
conditional.  The expansion is  only to pick up  such tokens -- the  text of
the false part is not sent to the scanner as the true part is.  In  order to
avoid  such expansion,  one  may use  IFCR  (the R  stands  for "recursive")
instead of IFC.

As an added feature, when delimiters are required about an <anything> in the
above (such constructs are  named <delimitedanything> in the BNF),  one may
substitute a  concatenation of constant  expressions and  delimited strings.
This is  just like a  macro body, except  the concatenation MUST  contain at
least one delimited string, thereby forcing the result of  the concatenation
to be a delimited string, rather than a naked expression.

As a further added feature,

   IFC <CT expr> THENC <anything> ELSEC
        <anything> ENDC

may  be  substituted  in  FORCs,  FORLCs,  and  WHILECs  for  the <anything>
following DOC.

NOTE: In  a WHILEC, the  expression must be  delimited with  the appropriate
macro body delimiters (hence the construct <delimitedexpr> in the BNF).



9.6  Type Determination at Compile Time

To ascertain  the type of  an identifier  at compile time,  one may  use the
integer function DECLARATION (<identifier>).   This returns an  integer with
bits turned on to represent  the type of identifier.  Exactly what  the bits
represent is a dark secret and changes periodically anyway.  The best way to
decode the integer returned by  Declaration is to compare it to  the integer
returned by CHECKTYPE (<a string of Sail declarators>).  A  Sail declarator
is  any of  the  reserved words  used  an a  declaration.   Furthermore, the
declarators must be listed in a  legal order, namely, an order that is legal
in  declarations  (i.e.  ARRAY  INTEGER won't  work).   One  may  include as
arguments to CHECKTYPE the following special tokens:

 BUILTIN LEAPARRAY RESERVED DEFINE CONOK


  TOKEN                 EFFECT

  BUILTIN    The bit that is on when a procedure is known to preserve ACs
              0-'11 (except AC1 if  returning a value) is  returned.  Sail
              does not clear the ACs  when compiling a call on  a BUILTIN
              procedure.

  LEAPARRAY  The bit that is on when an identifier is an item  or itemvar
              with a declared array  datum is returned (the  discussion of
              Leap starts on page 151).

  RESERVED    The bit that is on for a reserved word is returned.

  DEFINE      The bit  that indicates  the identifier is  a macro  name is
              returned (note: a macro name  as the argument to DECLARATION
              will not be expanded).

  CONOK       The  bit which  says "this  procedure will  be  evaluated at
              compile time if all its arguments are  constant expressions"
              is returned.

Examples:

  DECLARATION (FOO) = CHECKTYPE (INTEGER)
          This is an exact compare. Only if Foo is
          an integer variable will equality hold.

  DECLARATION (A)  LAND CHECKTYPE (ARRAY)
          This is not an exact compare.  If A is any
          kind of an array, the  LAND will be non-zero.

  DECLARATION (CVS) = CHECKTYPE(EXTERNAL CONOK
          OWN BUILTIN FORWARD STRING PROCEDURE)
          The equality holds.  FORWARD so that you can
          redeclare it without complaints; OWN as a hack
          which saves space in the compiler.

  DECLARATION (BEG)  LAND CHECKTYPE (RESERVED)
          This is non-zero only if one has said
          LET BEG = BEGIN.  DEFINE BEG = BEGIN
          will only turn the Define bit of BEG on.

NOTE: if the  <identifier> of DECLARATION has  not yet been declared  or was
declared in an inner block, then 0 is returned -- it is undeclared so it has
no type.

EXPRTYPE  returns the  same  bits that  DECLARATION does,  except  that the
argument to EXPRTYPE may be an expression and not just an identifier.

Miscelaneous Features COMPILE TIME I/O EVALDEFINE, EVALREDEFINE ASSIGNC NOMAC COMPILERBANNER


9.7  Miscelaneous Features

COMPILE TIME I/O
Compile  time  input is  handled  by the  REQUIRE  "<filename>" SOURCEFILE
construct.  <filename> can be any legal file, including TTY: and  MTA0: and
of course disk files.  (MTA does not work for TENEX.) The file will  be read
until  the  its  end of  file  delimiter  is scanned  (<ctrl>Z  for  TTYs or
<meta><ctrl><lf> at SUAI), and  its text will replace the  REQUIRE statement
in the main file.

Compile time output is limited  to typing a message on the  user's teletype.
To do this say REQUIRE <stringconstant> MESSAGE, and  the <stringconstant>
will appear on  your teletype when the  compilation hits that point  in your
file.

EVALDEFINE, EVALREDEFINE
The reserved word EVALDEFINE may be used in place of the word DEFINE  if one
would like the identifier that  follows to be expanded.  When one  follows a
DEFINE with a macro name, the  macro is not expanded, but  rather  the macro
name is  declared at the  current lexical level  and assigned  the specified
macro  body.   EVALDEFINE  gets  you  around  that.   Helps  with  automatic
generation of macro names.  EVALREDEFINE is also available.

ASSIGNC
The following compile time construct makes recursive macros easier.

        ASSIGNC <name1> = <macrobody>;

<name1> must be a formal to a macro, and <macrobody> may be any macro body.
Thereafter,  whenever <name1>  is  instantiated, the  body  corresponding to
<macrobody> is  used in the  expansion rather than  the text passed  to the
formal at the macro call.

RESTRICTION: ASSIGNC may only appear  in the body of the macro  that <name1>
is a formal of.  If it  appears anywhere else, the <name1> will  be expanded
like any good formal, and that text used in the ASSIGNC as  <name1>.  Unless
you're being very clever, this is probably not what you want.

NOMAC
Preceding anything  by the token  NOMAC will inhibit  the expansion  of that
thing should that thing turn out to be a macro.

COMPILERBANNER
This is a predefined macro which expands to a string constant containing the
text of the  two-line banner which  would appear at  the top of  the current
page if  a listing  file were being  made.  This  string contains  the date,
time, name and page of the source file, the value of all  compiler switches,
the name of the  outer block, and the name  of the current block.   Thus you
can automatically  include the  date of  compilation in  a program  by using
COMPILERBANNER[n   TO   m]  for   appropriate   n  and   m.    Try  REQUIRE
COMPILERBANNER MESSAGE; or look at a listing for the exact format.

Hints


9.8  Hints

The following is a set of hints and aids in debugging programs  with macros.
Unless otherwise stated array brackets "[]" are the macro body delimiters.

IFC and  friends will not  trigger at  the point of  macro definition,  in a
macro actual parameter list, or inside a string constant.

    DEFINE FOO = [IFC A THENC B ELSEC D ENDC];
            which is not the same as
    DEFINE FOO = IFC A THENC [B] ELSEC [D] ENDC;
            which is the same as
    IFC A THENC DEFINE FOO = [B]
          ELSEC DEFINE FOO = [D] ENDC;

    DEFINE BAZ (A) = [OUTSTR ("A");];
    BAZ (IFC B THENC C ELSEC D ENDC)
        will result in the following string typed
        on your terminal:
    IFC B THENC C ELSEC D ENDC

    STRING A;
    A_"IFC WILL NOT TRIGGER HERE";

Macros will not be expanded in strings, but macro formal parameters  will be
expanded  when they  occur in  strings within  macro bodies  as seen  in the
second example above.

    DEFINE FOO = [BAZ];
    OUTSTR ("FOO");

which will type out the string FOO on your terminal rather than BAZ.

Caution  should  be  employed  when  using  letters  (specifically   )  as
delimiters.  This may lead to problems when defining macros within macros.

    DEFINE MAC(A) "" = REDEFINE FOO =A;;

Inside the macro body of MAC, A will not be recognized as a formal since the
scanner has scanned  A as an identifier  by virtue of   being internally
represented as letters so that they  could be defined to mean BEGIN  and END
respectively (also   as COMMENT).  More  justification for this  feature is
seen by the following example:

    DEFINE MAC(ABC) "AC" = A V_ABC; C;

We want ABC in the text to be  the parameter and not B if we were  to ignore
the macro delimiters.

When scanning lists of actual parameters, macros are not expanded.




    DEFINE FOO = [A,B];
        MAC (FOO) will not have the result MAC(A,B). However,
    DEFINE FOO = [(A, B)];
        followed by MAC FOO will have the same effect as
        MAC (A, B).

The same reasoning holds for parameter lists to FORLC.

    DEFINE FOO = [A, B, C];
    FORLC I = (FOO) DOC [OUTSTR ("I");] ENDC
        will result in FOO typed out on your terminal.

    DEFINE FOO = [(A, B, C)];
    FORLC I = FOO DOC [OUTSTR ("I");] ENDC
        will have the desired result ABC typed out.

In  order  to  take  advantage of  the  nestable  character  feature  in the
parameters  to  a  macro  call, one  must  be  in  REQUIRE  DELIMITERS mode.
Otherwise scanning will break upon seeing a comma or a right parenthesis.

    BEGIN
      DEFINE FOO(A) = "A";
      INTEGER ARRAY ABC[1:10, 1:10];
      FOO (ABC[1, 2])_3;
    END;

This is identical to:

    BEGIN
      INTEGER ARRAY ABC[1:10, 1:10];
      ABC[1_3;  Comment illegal;
    END;

However, if the original program had included a REQUIRE DELIMITERS statement
prior  to the  macro call,  as  below, then  the desired  effect  would have
resulted - i.e., ABC[1, 2]_3 .

    BEGIN
      REQUIRE "{~%$" DELIMITERS;
      DEFINE FOO (A) = {A~;
      INTEGER ARRAY ABC[1:10, 1:10];
      FOO (ABC[1, 2])_3;
    END;

RECORD STRUCTURES Introduction Declaration Syntax Declaration Semantics


                                SECTION  10

                             RECORD STRUCTURES




10.1  Introduction

Record structures are new to Sail.  They provide  a means by which a  number
of closely related  variables may be allocated  and manipulated  as  a unit,
without the overhead  or limitations associated  with using parallel  arrays
and without the restriction that the variables all be of the same data type.
In   the current  implementation, each   record is  an instance  of  a user-
defined record class,  which serves as   a template describing  the  various
fields of the record.  Internally, records are small blocks of storage which
contain  space  for   the  various   fields  and   a  pointer   to  a  class
descriptor record.   Fields  are allocated  one per  word and   are accessed
by  constant  indexing off the   record pointer.  Deallocation  is performed
automatically by a garbage collector or manually through explicit calls to a
deallocation procedure.



10.2  Declaration Syntax


<recordclassdeclaration>
    ::= RECORDCLASS <classid> (
        <fielddeclarations> )
    ::= RECORDCLASS <classid> (
        <fielddeclarations> ) [ <handler> ]


<recordpointerdeclaration>
    ::= RECORDPOINTER ( <classidlist> )
        <idlist>
    ::= RECORDPOINTER ( ANYCLASS )
        <idlist>



10.3  Declaration Semantics

The <fielddeclarations>  have  the  same form as the <formalparamdecl> of
a procedure,  except that  the  words  VALUE and  REFERENCE should   not  be
used, and default  values are ignored.   Each record  class   declaration is
compiled into  a record  descriptor (which is  a record  of  constant record
class  $CLASS)  and   is  used  by   the  runtime  system   for  allocation,



deallocation, garbage collection, etc.  At runtime record  pointer variables
contain either the value NULLRECORD (internally, zero) or else a pointer to
a  record.  The  <classid list>  is  used to  make a  compile-time  check on
assignments and  field references.  The  pseudo-class ANYCLASS  matches all
classes, and effectively disables this compile-time check.

For instance,

    RECORDCLASS VECTOR (REAL X, Y, Z);
    RECORDCLASS CELL
        (RECORDPOINTER (ANYCLASS) CAR, CDR);
    RECORDCLASS TABLEAU
        (REAL ARRAY A, B, C; INTEGER N, M);
    RECORDCLASS FOO (LIST L; ITEMVAR A);

    RECORDPOINTER (VECTOR) V1,V2;
    RECORDPOINTER (VECTOR, TABLEAU) T1,T2;
    RECORDPOINTER (ANYCLASS) R;

    RECORDPOINTER (FOO, BAR) FB1, FB2;
    RECORDPOINTER (FOO) FB3;
    RECORDPOINTER (CELL) C;
    RECORDPOINTER (ANYCLASS) RP;

    COMMENT the following are all ok syntactically;
    C _ NEWRECORD (CELL);
    RP _ C;
    FB2 _ NEWRECORD (FOO);
    FB1 _ FB3;
    FB3 _ RP; COMMENT This is probably a runtime bug
              since RP will contain a cell record.  Sail
              won't catch it, however;
    CELL:CAR[RP] _ FB1;
    CELL:CAR[RP] _ FB1;

    COMMENT The compiler will complain about these: ;
    FB1 _ C;
    FB3 _ NEWRECORD (CELL);
    RP _ CELL:CAR[FB3];

NO runtime class information is kept with the record pointer  variables, and
no  runtime class  checks are  made on  record assignment  or  field access.
Record pointer  variables are  allocated quantities,  and should  not appear
inside SIMPLE procedures.   They resemble  lists in that they are  not given
any  special  value upon  block  entry and  they  are set  to  a  null value
(NULLRECORD) when the block in which they are declared is exited.  (This is
so that any records referred to  only in that block can be reclaimed  by the
garbage collector.)

Record pointers are regular Sail data types, just like integers  or strings;

 Allocation Fields


record pointer procedures, arrays, and items all work in the normal way.  As
indicated earlier, the constant NULLRECORD produces a null reference.



10.4  Allocation

Records are allocated by

    NEWRECORD (<classid>)

which returns a new  record of the specified  class.  All fields of  the new
record are  set to the  null or zero  value for that  field; i.e.,  real and
integer fields will be set to  0, itemvar fields to ANY, lists to  NIL, etc.
Note that entry  into a block with  local record pointer variables  does NOT
cause records to be allocated and assigned to those variables.



10.5  Fields

Record fields are referenced by

    <classid> : <fieldid> [ <record pointer expression> ]

and may be used wherever an array element may be used.  For example

    RECORDPOINTER (VECTOR) V;
    RECORDPOINTER (CELL) C;
    RECORDPOINTER (FOO) F;

    VECTOR:X[V] _ VECTOR:Y[V];
    CELL:CAR[C _ NEWRECORD (CELL)] _ V;
    VECTOR:Z[V] _ VECTOR:X[CELL:CAR[C]];
    SUBLIS _ FOO:L[F][1 TO 3];

If the <record pointer expression> gives a null record, then a runtime error
message will be generated.  This is  the only runtime check that is  made at
present.  I.e., no runtime checks  are made to verify that the  <classid> in
the field  statement matches the  class of the  record whose field  is being
extracted.

An array field may be used as an array name, as in

    RECORDPOINTER (TABLEAU) T;

    TABLEAU:A[T][I,J] _ 2.5;

provided  that a  valid array  descriptor has  been stored  into  the field.
Unfortunately, Sail does not provide any clean way to do this.   One unclean
way is

 Garbage Collection Internal Representations



    EXTERNAL INTEGER PROCEDURE ARMAK
        (INTEGER LB, UB, #DIMS);
    COMMENT returns address of first data word of new
        array. For String arrays set #DIMS to -1,,n.
        For higher dimensions declare with more LB, UB pairs;

    EXTERNAL PROCEDURE ARYEL (INTEGER ARR);
    COMMENT deallocates an array.  ARR is the address of
        the first data word;

    RECORDCLASS FUBAR (INTEGER ARRAY A);
    RECORDPOINTER (FUBAR) FB;

    MEMORY[LOCATION (FUBAR:A[FB])] _ ARMAK (1, 100, 1);
    ARYEL (MEMORY[LOCATION (FUBAR:A[FB])]);

(Warning: the above advice  is primarily intended for hackers.   No promises
are  made  that it  will  always  work, although  this  particular  trick is
unlikely to be made obsolete in the forseeable future.)



10.6  Garbage Collection

The  Sail record  service  routines allocate  records as  small  blocks from
larger buffers of  free storage obtained from  the normal Sail  free storage
system.  (The format of these records will be discussed in a later section.)
From time to time a garbage  collector is called to reclaim the  storage for
records  which are  no longer  accessible by  the user's  program  (i.e., no
variables or accessible records  point to them).  The garbage  collector may
be called explicitly  from Sail programs  as external procedure  $RECGC, and
automatic invocation of the  garbage collection may be inhibited  by setting
user  table  entry RGCOFF  to  TRUE.  (In  this  case, Sail  will  just keep
allocating more space, with nothing being reclaimed until RGCOFF is set back
to FALSE  or $RECGC  is called  explicitly).  In  addition, Sail  provides a
number of hooks that allow a user to control the automatic invocation of the
garbage collector.  These are discussed later.



10.7  Internal Representations

Each record has the following form:

   -1:  <ptrs to ring of all records of class>
    0:  <garbage collector ptr>,,<ptr to class descriptor>
   +1:     <first field>
    :            :
   +n:     <last field>

 $CLASS RECRNG HNDLER RECSIZ TYPARR TXTARR


Record  pointer    variables  point   at   word  0   of   such   records.  A
String field contains the address of word2 of a string descriptor,  like the
string was a REFERENCE parameter to a procedure.  The string descriptors are
also dynamically allocated.

The predefined record class $CLASS defines all record classes, and is itself
a record of class $CLASS.

    RECORDCLASS $CLASS
        (INTEGER RECRNG, HNDLER, RECSIZ;
         INTEGER ARRAY TYPARR; STRING ARRAY TXTARR);

RECRNG  is  a  ring  (bidirectional  linked  list)  of  all  records  of the
        particular class.

HNDLER  is a pointer to the handler procedure for the class (default $REC$).

RECSIZ  is the number of fields in the class.

TYPARR  is an array of field descriptors for each field of the class.

TXTARR  is an array of field names for the class.

The  normal  value  for  the  handler  procedure  is  $REC$,  which   is the
standard procedure  for such  functions as  allocation, deallocation, etc.

TYPARR and  TXTARR are  indexed [0:RECSIZ].   TXTARR[0] is  the name  of the
record class.  TYPARR[0] contains type bits for the record class.

 Handler Procedures



Example:

    RECORDCLASS FOO (LIST L; ITEMVAR A);

The record class descriptor for FOO contain:

    FOO-1:  <ptrs for ring of all records of $CLASS>
    FOO:    <ptr to $CLASS>
    FOO+1:  <ptrs for ring of all records of class FOO;
                    initialized to <FOO+2,,FOO+2> >.
    FOO+2:  <ptr to handler procedure $REC$>
    FOO+3:  2
    FOO+4   <ptr to TYPARR>
    FOO+5:  <ptr to TXTARR>

The fields of FOO are:

    $CLASS:RECRNG[FOO] = <initialized to null ring,
                          i.e., xwd(loc(FOO)+2,loc(FOO)+2)>
    $CLASS:HNDLER[FOO] = $REC$
    $CLASS:RECSIZ[FOO] = 2
    $CLASS:TXTARR[FOO] [0] = "FOO"
    $CLASS:TXTARR[FOO] [1] = "L"
    $CLASS:TXTARR[FOO] [2] = "A"
    $CLASS:TYPARR[FOO] [0] = <bits for garbage collector>
    $CLASS:TYPARR[FOO] [1] = <descriptor for LIST>
    $CLASS:TYPARR[FOO] [2] = <descriptor for ITEMVAR>



10.8  Handler Procedures

Sail uses a  single runtime routine $RECFN (OP,  REC) to handle  such system
functions  as allocation,  deallocation,  etc.  The  code compiled  for  r _
NEWRECORD (foo) is

    PUSH    P,[1]
    PUSH    P,[foo]
    PUSHJ   P,$RECFN
    MOVEM   1,r

$RECFN performs some type checking  and then jumps to the  handler procedure
for the class.  The normal value for this handler procedure is $REC$.  It is
possible  to  substitute another  handler  procedure for  a  given  class of
records by including the procedure name  in brackets after the  record class
declaration.  The handler must have the form

    RECORDPOINTER (ANYCLASS) PROCEDURE <procid>
        (INTEGER OP; RECORDPOINTER (ANYCLASS) R);



Here OP  will be a  small integer  saying what is  to be  done.  The current
assignments for OP are:

 value  meaning

   0    invalid
   1    allocate a new record of record class R
   2    not used
   3    not used
   4    mark all fields of record R
   5    delete all space for record R

At SUAI,  macro definitions  for these functions  may be  found in  the file
SYS:RECORD.DEF, which also includes EXTERNAL declarations for $CLASS, $REC$,
and $RECFN.

$REC$ (1, R) allocates  a record of the  record class specified by  R, which
must  be  a  record  of  class  $CLASS.   All  fields  (except  string)  are
initialized to zero.  String fields are initialized to a pointer to a string
descriptor with length zero (null string).

$REC$ (4, R) is used by  the garbage collector to mark all record  fields of
R.

$REC$ (5,  R) deallocates  record R,  and deallocates  all string  and array
fields of record R.  Care must be exercised to prevent multiple  pointers to
string and  array fields; i.e.,  DO NOT  store the location  of an  array in
fields of two  different records unless extreme  caution is taken  to handle
deletion.  This  can be accomplished  through user handler  procedures which
zero array fields (without actually  deleting the arrays) prior to  the call
on $REC$ (5, R).

NOTE:  When an alternate handler  procedure is supplied it must  perform all
the necessary functions.  One good way  to do this is to test for  those OPs
performed by the alternate handler and call $REC$ for the others.   If $REC$
is used  to allocate space  for the record  then it should  also be  used to
release the space.  These points are illustrated by the following example:

 More about Garbage Collection



    FORWARD RECORDPOINTER (ANYCLASS) PROCEDURE
        FOOH (INTEGER OP;
                 RECORDPOINTER (ANYCLASS) R);
    RECORDCLASS FOO (ITEMVAR IV) [FOOH];
    RECORDPOINTER (ANYCLASS) PROCEDURE FOOH
        (INTEGER OP; RECORDPOINTER (ANYCLASS) R);
      BEGIN
      PRINT("CALLING FOOH.  OP = ", OP);
      IF OP = 1 THEN
          BEGIN
          RECORDPOINTER (FOO) F;
          F _ $REC$ (1,R);
          FOO:IV[F] _ NEW;
          RETURN (F);
          END
      ELSE IF OP = 5 THEN
          DELETE (FOO:IV[R]);
      RETURN ($REC$ (OP, R));
      END;



10.9  More about Garbage Collection

The information used by the system to decide when to call $RECGC on  its own
is accessible through the global array $SPCAR.  In general, $SPCAR[n] points
at a descriptor block  used to control the  allocation of small blocks  of n
words.  This descriptor includes the following fields:

BLKSIZ number of words per block in this space
TRIGGERa counter controlling time of garbage collection
TGRMIN described below
TUNUSEDnumber of unused blocks on the free list
TINUSE total number of blocks in use for this space
CULPRITthe number of times this space has caused
       collection

The appropriate macro definitions for access to these fields may be found in
the source  file SUAISYS:RECORD.DEF.  The  decision to invoke  the garbage
collector is  made  as part of the  block allocation procedure,  which works
roughly as follows:




    INTEGER spc,size;
    size _ $CLASS:RECSIZ[classid]+2;
    IF size>16 THEN return a CORGET block;
    spc _ $SPCAR[size];
    L1:
    IF (MEMORY[spc+TRIGGER]
                _ MEMORY[spc+TRIGGER]-1) <0
        THEN BEGIN
      IF MEMORY[GOGTAB+RGCOFF] THEN BEGIN
        MEMORY[spc+CULPRIT] _ MEMORY[spc+CULPRIT]+1;
        $RECGC;
        GO TO L1;
    END END;
    <allocate the block from space spc,
     update counters, etc.>

Once $RECGC  has returned all  unused records to  the free  lists associated
with their respective block sizes, it must adjust the trigger levels  in the
various spaces.  To do this, it first looks to see if the user has specified
the location of  an adjustment procedure in  TGRADJ(USER).  If this  cell is
non-zero then $RECGC calls  that procedure (which must have  no parameters).
Otherwise it calls a default system procedure that works roughly like this:

    <set all TRIGGER levels to -1>
    FOR size _ 3 STEP 1 UNTIL 16 DO BEGIN
        spc _ $SPCAR[size];
        IF MEMORY[spc+TRIGGER]<0 THEN BEGIN
            t_MEMORY[spc+TINUSE]*RGCRHO(USER);
            t_MAX(t, MEMORY[spc+TUNUSED],
                    MEMORY[spc+TGRMIN]);
    END END;

RGCRHO(USER) is a real number  currently initialized by the system  to 0.33.
Thus  the behavior  of  Sail's automatic  garbage collection  system  may be
modified by

   Setting RGCOFF(USER).
   Supplying a procedure in TGRADJ(USER).
   Modifying RGCRHO(USER).
   Modifying the TGRMIN entries in the space descriptors.

One word of  caution: User procedures that  set trigger levels must  set the
trigger level of the space  that caused garbage collection to  some positive
value.  If not then a runtime error message will be generated.

Look at  the  file  SUAIRECAUX.SAI[CSP,SYS], which  contains a   number of
useful examples and auxilliary functions.

TENEX ROUTINES Introduction TOPS-10 Style Input/Output


                                SECTION  11

                               TENEX ROUTINES




11.1  Introduction

This  section  describes  routines  which  interface  Sail  with  the  TENEX
operating system.   Routines for file  input/output, terminal  handling, and
miscellaneous system calls  are described here.  For  TENEX-specific details
of other routines (such as interrupts) consult the appropriate chapter.



11.2  TOPS-10 Style Input/Output

"Standard" Sail programs  written using TOPS-10  I/O routines such  as OPEN,
LOOKUP, etc., will run under  TENEX with little or no  conversion necessary.
The TENEX  Sail routines  simulate most of  the effects  of the  TOPS-10 I/O
calls without using the PA-1050 emulator.

In TENEX Sail the non-zero  values of error flags returned by  routines such
as LOOKUP are ERSTR JSYS error numbers.  The interpretation  of zero/nonzero
is  the same  as with  the TOPS-10  I/O routines,  but the  specific nonzero
values are probably different.

Here are the TOPS-10 I/O routines and the differences, if any, under TENEX.

ARRYIN  TENEX dump mode implies a single DUMPI JSYS.

ARRYOUT similar to ARRYIN.

CLOSE   The close inhibit bits have no effect.

CLOSIN  same as CLOSE.

CLOSO   same as CLOSE.

ENTER   no differences.

GETCHAN In TOPS-10,  GETCHAN returns the  number of a  channel for  which no
        OPEN  is  currently  in effect.   Thus  successive  GETCHANs without
        intervening OPENs  will return  the same  channel number.   In TENEX
        Sail, GETCHAN returns the number  of a channel for which no  OPEN or
        GETCHAN is currently in effect; thus successive GETCHANs will return
        different channel numbers.

GETSTS  not available; see GDSTS, GTSTS.



INOUT   not available.

INPUT   assumes  200  characters  maximum if  no  length  variable  has been
        associated with the channel.

INTIN   no differences.

LINOUT  no differences.

LOOKUP  no differences.

MTAPE   Options "I" and NULL are not available.

OPEN    MODE is mostly  ignored (exception: dump  mode on a  dectape ignores
        the directory).  The number of input and output buffers  serves only
        to indicate whether reading or writing is desired.

OUT     no differences.

REALIN  no differences.

RELEASE The close inhibit bits have no effect.

RENAME  Changing the protection does not work.  See GTFDB and CHFDB.

SETPL   The routines CHARIN and SINI do not update the  variables associated
        with the channel by SETPL.

SETSTS  not available; see SDSTS, STSTS.

STDBRK  no differences.

TMPIN   not available.

TMPOUT  not available.

USETI   works  only on  those  devices where  the  SFPTR JSYS  works.   On a
        dectape the MTOPR JSYS is used, and may not produce the same results
        as  on  a  TOPS-10  system.   USETI  takes  effect  immediately (the
        nondeterminancy  of the  standard TOPS-10  (not SUAI)  USETI  is not
        simulated).  Equivalent to SFPTR (chan, (N-1)*'200);

USETO   same as USETI.   TENEX has only one  file pointer, so in  fact USETI
        and USETO are EXACTLY the same function.

WORDIN  no differences.

WORDOUT no differences.

 MAGTAPE I/O TENEX Style Input/Output OBTAINING ACCESS


MAGTAPE I/O
The user  is warned that  there are serious  limitations in  TENEX regarding
magtapes.   While  TENEX is  supposed  to have  device-independent  I/O, the
magtape code in TENEX  (as of v. 1-31)  is minimal, allowing only  dump mode
transfers.  Further, end of file markers must be written explicitly,  and it
is  sometimes necessary  to do  an MTOPR  operation 0  to reset  the magtape
status bits.

TENEX Sail has been  designed to handle some of  these things in a  way that
makes  features  available  on  a standard  TOPS-10  system  available  in a
transparent way.  For example, string input and output functions  work, with
Sail assuming 128-word  records on the tape.   ARRYIN and ARRYOUT  cause the
DUMPI and DUMPO JSYSes to be executed for the specified word  counts.  TENEX
Sail  does not  actually open  tapes for  write until  a write  operation is
requested.  A  CLOSF or  CFILE on a  tape will  write two  EOF'S (MTAPE (ch,
"E")) and  backspace over  one of  them, if and  only if  the file  has been
opened.  Do not rewind a tape unless it has been closed.  The user who wants
to write magtape code for  operations other than the above is  hereby warned
that the  TENEX magtape code  is fraught with  peril.  TENEX  Sail certainly
allows full access to TENEX in this regard, however.



11.3  TENEX Style Input/Output

The following functions satisfy most Sail and TENEX needs:

ARRYIN  Read in an array (36-bit words)

ARRYOUT Write an array

CFILE   Release a file

CPRINT  Write a string

INPUT   Read in a string

JFNS    Read file name

OPENFILEObtain a file

OUT     Write a string

SETINPUTSet parameters for input

OBTAINING ACCESS
The main procedure for obtaining  access to files is OPENFILE.  In  terms of
JSYSes,  OPENFILE  does  a GTJFN  and  OPENF.   Additional  routines provide
support to OPENFILE, including SETINPUT, INDEXFILE, and CFILE.

 DATA TRANSFER RANDOM I/O ERROR HANDLING DIRECT DSK OPERATIONS ASND, RELD


DATA TRANSFER
The TENEX routines for transferring data are generally the same as the TOPS-
10 routines.  One improvement in TENEX Sail is that characters and words can
be mixed in reading or writing to a file, provided the file is on  the disk.
Such I/O is called "data mixed I/O".

The  following interpretation  is given  to data  mixed I/O.   There  is one
logical  character pointer  into  the file.   When  a character  is  read or
written the  routines access  the byte  designated by  the pointer  and then
increment the pointer.  There is only one pointer for both input and output.
When a word is read or written, the next full word in the file  is accessed.
Accessing a word advances the character pointer to the next full word in the
file, where five 7-bit ASCII  characters occupy one 36-bit word.  If  a read
passes the end of the file  then the EOF variable (specified by  SETINPUT or
OPEN) and the external integer SKIP are set to -1.  If a write  passes the
end of file then the end of file is advanced.

RANDOM I/O
The routines  RCHPTR, SCHPTR,  RWDPTR, and  SWDPTR give  access to  the file
pointer.  USETI and USETO are equivalent to SWDPTR (chan, (N-1)*'200);.

ERROR HANDLING
When  errors  occur the  runtime  routines will  sometimes  trap  the errors
themselves.  This practice is held  to a minimum since the error  itself may
be information that the user is interested in seeing.  Usually  the routines
(as marked) put the TENEX error code in SKIP, which may be examined by the
program.  The TENEX error numbers do not always make good sense, but for the
cases that  they do the  ERSTR routine  will print out  on the  terminal the
message associated with a given error number.

DIRECT DSK OPERATIONS
The routines DSKIN and DSKOUT do direct DSK operations in TENEX  Sail, using
the DSKOP JSYS.  These routines  relate only to the IMSSS version  of TENEX-
Sail.



*******************************  ASND, RELD  *****************************;

SUCCESS _ ASND (DEVICEDESCRIPTOR);
SUCCESS _ RELD (DEVICEDESCRIPTOR)

DEVICEDESCRIPTOR (in the TENEX sense) is assigned to or deassigned from the
job.  If DEVICEDESCRIPTOR is -1 when calling RELD then all devices assigned
to the job are deassigned.  TENEX error codes are returned in  SKIP, which
is zero if no errors occurred.

 BKJFN CFILE CHARIN CHAROUT CHFDB CLOSF


*********************************  BKJFN  ********************************;

BKJFN (CHAN)

Does the BKJFN JSYS on CHAN. TENEX error codes are returned in SKIP, which
is zero if no errors occurred.  This function is escape from Sail.



*********************************  CFILE  ********************************;

SUCCESS _ CFILE (CHAN)

This routine closes the file (CLOSF) and releases the CHAN (RLJFN).  This is
the ordinary way  to dispense with  a file.  CFILE  returns TRUE if  CHAN is
legal and released; it returns FALSE otherwise.



*********************************  CHARIN  *******************************;

CHAR _ CHARIN (CHAN)

The next character from CHAN is returned. Zero is returned if the file is at
the end.



********************************  CHAROUT  *******************************;

CHAROUT (CHAN, CHAR)

The single character CHAR is written to CHAN.



*********************************  CHFDB  ********************************;

CHFDB (CHAN, DISPLACEMENT,
        MASK, CHANGEDBITS)

This routine performs the CHFDB  JSYS on CHAN, with DISPLACEMENT,  MASK, and
CHANGEDBITS as described in the JSYS manual.



*********************************  CLOSF  ********************************;

CLOSF (CHAN)

 CVJFN DELF DELNF DEVST, STDEV


This routine does a CLOSF on CHAN.  CHAN is not released.  If the  device is
a magtape open for output then  2 file marks are written and a  backspace is
performed.  This writes a standard end-of-file on the tape.



*********************************  CVJFN  ********************************;

REALJFN _ CVJFN (CHAN)

The full TENEX JFN (including flags in the left half) corresponding  to Sail
channel CHAN is returned.  Only a hacker will ever need this.



**********************************  DELF  ********************************;

DELF (CHAN)

The file on CHAN (which must NOT be open) is deleted.  TENEX error codes are
returned in SKIP, which is zero if no errors occurred.



*********************************  DELNF  ********************************;

DELETED _ DELNF (CHAN, KEPT)

This routine deletes all but KEPT  versions of the file on CHAN,  which must
have had a CLOSF done on it first.  If KEPT=0 then all versions of  the file
are  deleted.   If KEPT=1  then  all  versions except  the  most  recent are
deleted.  The number of files  actually deleted is returned as the  value of
DELNF.



******************************  DEVST, STDEV  ****************************;

"DEVICENAME" _ DEVST (DEVICEDESIGNATOR);
DEVICEDESIGNATOR _ STDEV ("DEVICENAME")

These  routines convert  between string  DEVICENAMEs (such  as  "DTA0") and
TENEX DEVICEDESIGNATORs.   TENEX does not  believe that lower  case letters
are  equivalent to  upper  case letters  in  STDEV.  TENEX  error  codes are
returned in SKIP, which is zero if no errors occurred.

 DEVTYPE DSKIN, DSKOUT DVCHR ERSTR GDSTS, SDSTS


********************************  DEVTYPE  *******************************;

DEVICETYPE _ DEVTYPE (CHAN)

The DVCHR JSYS is used to return the device type of the device open on CHAN.



*****************************  DSKIN, DSKOUT  ****************************;

DSKIN (MODULE, RECNO, COUNT, @LOC);
DSKOUT (MODULE, RECNO, COUNT, @LOC)

[IMSSS only.] These routines do  direct DSK I/O.  MODULEs 4-7 are  legal for
everyone; other modules require enabled status.  The routines transfer COUNT
('1000) words, starting  at location LOC in  memory and at record  RECNO in
MODULE.   TENEX error  codes are  returned in  SKIP, which  is zero  if no
errors occurred.  WARNING:   No bounds checking is  performed to see  if the
LOC is a legal Sail array.



*********************************  DVCHR  ********************************;

DEVICECHAR _ DVCHR (CHAN, @AC1, @AC3)

The DEVCHR JSYS is performed.  The flags from AC2 are returned as  the value
of the call, and AC1 and AC3 get the contents of ac's 1 and 3.



*********************************  ERSTR  ********************************;

ERSTR (ERRNO, FORK)

Using the  ERSTR JSYS,  this routine types  on the  console the  TENEX error
string associated with ERRNO for  fork FORK ('400000 for the  current fork).
Parameters (in  the sense  of the ERSTR  JSYS) are  expanded.          Types
ERSTR:  UNDEFINED ERROR NUMBER (and sets SKIP to -1) if something is wrong
with ERRNO or FORK.



******************************  GDSTS, SDSTS  ****************************;

STATUS _ GDSTS (CHAN, @WORDCOUNT);
SDSTS (CHAN, NEWSTATUS)

The  status  of the  device  on CHAN  is  returned or  changed.   For GDSTS,
@WORDCOUNT is set to the contents of AC3.

 GNJFN GTFDB GTJFN


Remark:  some magtape  statuses (such as  EOF) are set  by MTOPR and  not by
SDSTS.  Ordinarily  the Sail runtime  system takes care  of this, but  it is
worth mentioning since  so many users have  run into this  poorly documented
fact about TENEX.



*********************************  GNJFN  ********************************;

MOREFILES _ GNJFN (CHAN)

Does the GNJFN JSYS.  A file  that is open cannot have GNJFN applied  to it.
INDEXFILE should  normally be  used instead  of GNJFN.   An exception  is if
files  are being  indexed without  actually being  opened (i.e.,  without an
OPENF  JSYS), which  is  a sensible  way  of performing  operations  such as
counting the number of files in a group.



*********************************  GTFDB  ********************************;

GTFDB (CHAN, @BUF)

The entire FDB of  CHAN is read into the  array BUF.  No bounds  checking is
performed, so BUF should be at least '25 words.



*********************************  GTJFN  ********************************;

CHAN _ GTJFN ("NAME", FLAGS)

Does a GTJFN.  If NAME is  non-null then it is used, otherwise  the terminal
is queried for a filename.  Any error code is returned in SKIP.   The Sail
channel number obtained is returned as the value of GTJFN.

The following values for FLAGS  will be translated by Sail before  doing the
JSYS:

    value       translated to

      0         '100001000000 (ordinary input)
      1         '600001000000 (ordinary output)

Other values are taken literally.

Ordinarily OPENFILE  will be  used rather than  GTJFN.  The  routines GTJFN,
OPENF,  GNJFN, CLOSF,  RLJFN, and  DVCHR are  all in  the category  of being
included only for completeness; they are not necessary in most programs.

 GTJFNL GTSTS, STSTS INDEXFILE


*********************************  GTJFNL  *******************************;

CHAN _ GTJFNL ("ORIGSTR", FLAGS, JFNJFN,
                "DEV", "DIR", "NAM", "EXT",
                "PROT", "ACCOUNT", DESIREDJFN)

Does  the long  form of  the GTJFN  JSYS (and  does not  do an  OPENF).  The
arguments are put into the accumulators and locations in the  table accepted
by the long form of the GTJFN JSYS.  These arguments are given  below, where
"AC X" means an accumulator and "E+X" means in the Xth address of the table.

Argument Where placed What

"ORIGSTR"AC 2 Partial or complete string
FLAGS    E+0 Flags to GTJFN
JFNJFN  E+1 xwd input JFN, output JFN
"DEV"    E+2 device
"DIR"    E+3 directory
"NAME"   E+4 name
"EXT"    E+5 extension
"PROT"   E+6 protection
"ACCOUNT"E+7 account
DESIREDJFN E+'10 desired JFN if B11 on



******************************  GTSTS, STSTS  ****************************;

STATUS _ GTSTS (CHAN);
STSTS (CHAN, NEWSTATUS)

These routines examine and change  the file status using the  JSYSes.  TENEX
error codes are returned in SKIP, which is zero if no errors occurred.

WARNING:  The  results  of   GTSTS  are  not  necessarily   appropriate  for
determining end-of-file if the file  is being page-mapped by Sail.   Look at
the EOF variable instead.  See SETINPUT.



*******************************  INDEXFILE  ******************************;

ANOTHER _ INDEXFILE (CHAN)

If CHAN was opened with the  "*" option by OPENFILE then INDEXFILE  will try
to get the next  file in the "*" group.   INDEXFILE returns TRUE as  long as
another file can be found on CHAN.  Example:

 JFNS JFNSL MTOPR



    JFN _ OPENFILE ("<JONES>*.SAI;*", "RO*");
        COMMENT Read all of Jones's Sail programs;
    SETINPUT (JFN, 200, 0, EOF);

    DO  BEGIN "INDEX"
        DO BEGIN "READ FILE"
            STRING S;
            S _ INPUT (JFN, BREAKTABLE);
            COMMENT process ...;
           END "READ FILE" UNTIL EOF;
        END "INDEX" UNTIL NOT INDEXFILE (JFN);

The "*"  option takes  the place  of reading the  MFD and  UFD on  a TOPS-10
system.   INDEXFILE clears  the EOF,  LINNUM, SOSNUM,  and  PAGNUM variables
associated with CHAN if these have been set by SETINPUT and SETPL.



**********************************  JFNS  ********************************;

"NAME" _ JFNS (CHAN, FLAGS)

The  name of  the file  associated  with CHAN  is returned.   FLAGS  are for
accumulator 3 as described in  the JSYS manual.  Zero is a  reasonable value
for FLAGS.



*********************************  JFNSL  ********************************;

"NAME" _ JFNSL (CHAN, FLAGS, LHFLAGS)

(This routine corrects a deficiency  in the JFNS function.) The name  of the
file associated  with CHAN is  returned, using FLAGS  for accumulator  3 and
putting LHFLAGS into the left half of accumulator 2 as described in the JSYS
manual.  If LHFLAGS is -1 then the value returned by GTJFN is used.



*********************************  MTOPR  ********************************;

MTOPR (CHAN, FUNCTION, VALUE)

The MTOPR JSYS is executed with FUNCTION placed into AC2 and VALUE into AC3.
The TOPS-10 style MTAPE function may be more comfortable.   [(Stupid!) IMSSS
and SUMEX: skip to end of tape does not work.]

 OPENF OPENFILE


*********************************  OPENF  ********************************;

OPENF (CHAN, FLAGS)

Does the OPENF  JSYS on CHAN  with FLAGS as  the contents of  accumulator 2.
TENEX  error  codes are  returned  in SKIP,  which  is zero  if  no errors
occurred.  The following values for FLAGS will be translated by  Sail before
setting AC2:

    value       translated to
      0         '070000200000  (input characters)
      1         '070000100000  (output characters)
      2         '440000200000  (input words)
      3         '440000100000  (output words)
      4         '447400200000  (dump read)
      5         '447400100000  (dump write)

Values 6-10 are reserved for expansion; other values are taken literally.

Best results are obtained by opening a TTY in 7-bit mode, the DSK or  DTA in
36-bit mode, and a magtape in 36-bit dump mode.



********************************  OPENFILE  ******************************;

CHAN _ OPENFILE ("NAME", "OPTIONS")

NAME is the name of the file to be opened.  If it is null then OPENFILE gets
the filename from the terminal using TENEX filename recognition.   CHAN, the
value  returned  by  OPENFILE,  is  a  Sail  channel  number.  This  is  not
necessarily the same as the TENEX JFN (see CVJFN).  All TENEX Sail functions
(except SETCHAN) require Sail channel numbers for arguments.  OPTIONS is one
or  more  characters  specifying  the kind  of  access  desired.   The legal
characters are

Read or write:
   R   read
   W   write
   A   append

Version numbering, old-new:
   O   old file
   N   new file
   T   temporary file
   *   index with INDEXFILE routine

Independent bits to be set:
   C   require confirmation
   D   ignore deleted bit
   H   "thawed" access

 RCHPTR, SCHPTR


Error handling:
   E   return errors to user in the external
integer SKIP.  TENEX error codes are used.
(CHAN will be released in this case.)

If an error  occurs and mode  "E" was not  specified then OPENFILE  gives an
error message and attempts to obtain  a file name from the terminal.   If an
error occurs when  "E" was specified then  OPENFILE will return -1  for CHAN
and the TENEX error code will be put into SKIP.

Examples:

    COMMENT get a filename from the terminal
        and write the file;
    BEGIN
    INTEGER JFN;
    OUTSTR (CRLF & "FILE NAME* ");
    JFN _ OPENFILE (NULL, "WC");
        COMMENT write, confirm name;
    CPRINT (JFN, "text
    ");
    CFILE (JFN);        COMMENT close the file;
    END;

    COMMENT read a known file;
    BEGIN
    STRING S;
    INTEGER JFN, BRCHAR, EOF;
    SETBREAK (1, '12, '15&'14, "IN");
    JFN _ OPENFILE ("<JONES>SECRET.DATA", "RCO");
    SETINPUT (JFN, 200, BRCHAR, EOF);
    DO  BEGIN
        S _ INPUT (JFN, 1);
        END UNTIL EOF;
    CFILE (JFN);
    END;

Wizards:  The  OPENF is for  36-bit transfers; except  that TTY, LPT,  and a
device for which a 36-bit OPENF fails get 7-bit mode.



*****************************  RCHPTR, SCHPTR  ***************************;

PTR _ RCHPTR (CHAN);
SCHPTR (CHAN, NEWPTR)

The number  of the  byte which  will be  accessed next  by character  I/O is
returned or set.  The first character  of a file is character number  0.  If
NEWPTR=-1 for SCHPTR then  the pointer is set  to end of file.   Setting the

 RFBSZ RFPTR, SFPTR RLJFN RNAMF


pointer beyond end of file will change the length of the file if it is being
written.  TENEX  error codes  are returned in  SKIP, which  is zero  if no
errors occurred.



*********************************  RFBSZ  ********************************;

BYTESIZE _ RFBSZ (CHAN)

The bytesize of the file open on CHAN is returned.  This function is escape
from Sail.



******************************  RFPTR, SFPTR  ****************************;

PTR _ RFPTR (CHAN);
SFPTR (CHAN, NEWPTR)

These routines perform JSYSes and  are escape from Sail.  TENEX  error codes
are returned in SKIP, which is zero if no errors occurred.



*********************************  RLJFN  ********************************;

RLJFN (CHAN)

This routine does the RLJFN JSYS.



*********************************  RNAMF  ********************************;

SUCCESS _ RNAMF (EXISTINGCHAN, NEWCHAN)

The RNAMF JSYS is performed,  renaming the file on EXISTINGCHAN to  the name
of   the   (vestigial)   file   on   NEWCHAN.    It   is    necessary   that
CLOSF(EXISTINGCHAN) be done before RNAMF and that OPENF be  done afterwards.
The TOPS-10  style RENAME is  sometimes more convenient  to use  than RNAMF,
since  RENAME  performs the  GTJFN  and OPENFs  necessary  for  the renaming
operation.   However, the  actual  JFN associated  with CHAN  is  changed by
RENAME.

 RWDPTR, SWDPTR SETCHAN SETINPUT SINI


*****************************  RWDPTR, SWDPTR  ***************************;

PTR _ RWDPTR (CHAN);
SWDPTR (CHAN, NEWPTR)

The number of the word which  will be accessed next by word I/O  is returned
or set.  The first word of a file is word number 0.  If NEWPTR=-1 for SWDPTR
then the pointer is set to  end of file.  Setting the pointer beyond  end of
file will change the length of the file if it is being written.



********************************  SETCHAN  *******************************;

CHAN _ SETCHAN (REALJFN,
        GTJFNFLAGS, OPENFFLAGS)

This function is  liberation from Sail I/O.   It is provided for  doing Sail
I/O on  a JFN that  is obtained from  some means other  than the  Sail file-
opening routines -- for example, a JFN passed from a superior fork.

REALJFN is  a 36-bit JFN  (or JFN substitute,  such as a  Teletype number),
GTJFNFLAGS and OPENFFLAGS are the flags that should be recorded describing
how the GTJFN and OPENF were accomplished.  REALJFN need not be  open.  The
value returned by  SETCHAN is the Sail  channel number which should  be used
for subsequent Sail  I/O.  SETCHAN is the  only function in TENEX  Sail that
takes an actual JFN as an argument.



********************************  SETINPUT  ******************************;

SETINPUT (CHAN, @COUNT, @BRCHAR, @EOF)

This function relates the COUNT,  BRCHAR, and EOF variables to  channel CHAN
in the same way that OPEN  does.  The INPUT function (page 72) uses  200 for
the default value of COUNT if no location has been associated with CHAN.

All I/O transfer  routines also set SKIP  to indicate end-of-file  and I/O
errors.  For example, on return from  INPUT SKIP will be -1 if  an end-of-
file  occurred,  a  TENEX  error  number  if  an  error  occurred,  and zero
otherwise.



**********************************  SINI  ********************************;

"STRING" _ SINI (CHAN, MAXLENGTH, BRCHAR)

A  string  of  characters  terminated by  BRCHAR  or  by  reaching MAXLENGTH

 SIZEF UNDELETE Terminal Handling THE TERMINAL AS A DEVICE


characters, whichever happens first, is read from CHAN.  SINI sets SKIP to
-1 if the string was terminated  for count; otherwise SKIP will be  set to
BRCHAR.  To determine end-of-file, examine the EOF variable for  the channel
(see SETINPUT).



*********************************  SIZEF  ********************************;

SIZE _ SIZEF (CHAN)

The size in pages of the  file open on CHAN is returned.  TENEX  error codes
are returned in SKIP, which is zero if no errors occurred.



********************************  UNDELETE  ******************************;

UNDELETE (CHAN)

The  file open  on CHAN  is undeleted.   TENEX error  codes are  returned in
SKIP, which is zero if no errors occurred.



11.4  Terminal Handling

The simplest way to write strings  on the terminal is with PRINT.   See page
98.  The simplest way to read strings from the terminal is with  INTTY.  See
page 145.   The following detailed  discussion about terminal  handling will
normally be  of interest  only to  advanced programmers.   The rest  of this
section is new.

THE TERMINAL AS A DEVICE
We first  discuss some of  the problems  in using the  terminal as  a device
(i.e., when  device "TTY:"  is opened  by OPENFILE  or a  similar function).
Since Sail has  various functions for  reading strings, reals,  and integers
from an arbitrary device, this can be a useful feature.

TENEX  provides quite  general  teletype service.   However, the  lack  of a
default system line editor creates some problems.  Note the proliferation of
line editors in the many  commonly used TENEX programs.  Some of  them, such
as  the INTERLISP  editor, are  carefully and  cleanly written.   Most TENEX
utility programs, however, work quite poorly and inconsistently  with regard
to the controlling terminal.

The TOPS-10 system has a simple line editor.  On a standard Teletype device,
the  standard  TOPS-10  editor  activates  on  a  carriage  return, altmode,
control-G, or  control-Z. ASCII DEL  ('177) deletes the  previous character;
control-U deletes the current line; control-R retypes the current  line; and



control-Z signifies end-of-file  when the terminal  is INITted as  a device.
(The  SUAI  display  line editor  also  has  character  insertion, deletion,
searching, kill-to-character, and settable activation characters.) The great
virtue  of this  is that  programs can  be written  in  a device-independent
manner.  When the terminal is  accessed as a device the system  handles line
editing.

Many TOPS-10 programs take advantage of this device-independence,  using the
INPUT, REALIN and INTIN functions  to access the system line  editor.  TENEX
has had no system line editor; while IMSSS and SUMEX have had a  line editor
in their TENEX for some time, it is not in general use.

Therefore, the  features of a  "system" line editor  have been put  into the
TENEX Sail runtime system.   Several schemes have been implemented  in TENEX
Sail  as of  this writing.   When  a channel  is opened  to  the controlling
terminal, three kinds  of line editing are  available:  1)  a  TOPS-10 style
line editor, 2)  a TENEX-style line  editor, and 3)  no line editor  at all.
The TOPS-10 style editor is the default with a channel opened via  OPEN; the
TENEX-style editor is the default when a TENEX function (such as OPENFILE or
GTJFN) is used to obtain the  channel.  The function SETEDIT can be  used to
change which convention is  used.  More detailed description of  these three
kinds of editing follows.

TOPS-10  Style  Editor.   The OPEN  function  to  the  controlling terminal,
usually "TTY" in the second argument, gets the following editing conventions
for functions INPUT, INTIN and REALIN:

  '25 (control-U) deletes the entire  line and echoes control-G (BEL)  CR LF
      to the terminal.

  '32 (control-Z) means end-of-file, after all previous input is read in.

  '33 (ESC, altmode) activates and is sent to the program as  '33.   This is
      consistent with current TOPS-10  practice.  Over the years  there have
      been several altmodes: '33,  '175, and '176.  On terminals  that TENEX
      believes to be a model  33 teletype, the characters '175 and  '176 are
      transliterated to  '33 by  TENEX before the  Sail runtime  system sees
      them.

  '37 (US, TENEX EOL), which is  found in the input buffer when CR  is typed
      at the terminal, is transliterated to a '15 '12 (CRLF) sequence.

  '177  (DEL,  rubout)  deletes  the  last  character;  consecutive  deleted
      characters are echoed, surrounded  by backslashes "\".  (At  IMSSS and
      SUMEX  the deleted  characters are  removed from  the screen  with the
      DELCH JSYS, which is not supported by BBN.)

The editor activates on line feed, altmode, control-G, and control-Z.

All this means that programs  written for the TOPS-10 system,  accessing the



controlling terminal with INPUT et  al, should work with regard  to teletype
input.  The above is also  a description of the operation of  INCHWL, except
that control-Z is simply a break character to INCHWL.

TENEX-Style  Editor.   The  OPENFILE, GTJFN,  and  GTJFNL  functions  to the
controlling  terminal  set  the  TENEX Sail  line  editor  to  the following
conventions:

IMSSS and SUMEX.  These sites use the PSTIN JSYS for line editing  in TENEX,
with the following conventions:

  '12 (linefeed) allows input to continue on the next line.

  '22 (control-R) retypes the current line.

  '27 (control-W) deletes a "word"  (up to the next space).  This  prints as
      "___" on the terminal.

  '30 (control-X) deletes the entire line.

  '32 (control-Z) signifies end of file.

  '37 (TENEX EOL) is transliterated to a '15 '12 sequence.

  '177  (rubout) or  '1 (control-A)  deletes the  last character,  using the
      DELCH JSYS to remove it from the display (if any).

The PSTIN JSYS transliterates '175 and '176 to '33.

The editor  activates on the  characters defined by  the PSTIN  JSYS (q.v.);
      these  include  linefeed  ('12 after  EOL),  escape  ('33), control-G,
      control-Z.

Sites other than IMSSS and SUMEX have the following editing conventions when
the channel is opened with the TENEX routines OPENFILE, GTJFN, etc.:

  '22 (control-R) retypes the current contents of the buffer.

  '30 (control-X) deletes the entire line and echoes CR LF to the terminal.

  '32 (control-Z) signifies end-of-file.

  '37 (TENEX EOL) is transliterated to a '15 '12 sequence.

  '177 (rubout) or  '1 (control-A) deletes the last  character.  Consecutive
      deleted characters are echoed surrounded by backslashes.

The editor  activates on line  feed ('12), escape  ('33), control-G  (7) and
      control-Z ('32).

 SETEDIT TERMINAL MODE FUNCTIONS GTTYP, STTYP


This  is also  the action  of the  INTTY routine,  except that  control-Z is
simply a break character to INTTY.

The third mode is  the BBN standard mode.   In this mode all  characters are
simply passed  through.  In  particular, control-Z does  not signify  end of
file, typing a rubout  gives a '177, ESC gives  a '33, CR gives a  '37, etc.
No editing  is done by  the system.  This  is the mode  in which  a terminal
other than the controlling terminal is accessed using any of the functions.



********************************  SETEDIT  *******************************;

"OLDMODE" _ SETEDIT (CHAN, "NEWMODE")

If CHAN is not the controlling terminal then SETEDIT is a no-op.  Otherwise,
it  sets the  line  editing mode  to  NEWMODE" and  returns  OLDMODE, both
according to the following code:

MODE   Meaning

"D"    TOPS-10 mode, as above
"T"    TENEX mode, as above
"B"    (BBN bag)Byte(ing) mode, no editing

Notes:

(1)  MODE SETTINGS.  SETEDIT does not change or access the parameters set by
    such functions as  SFMOD, SFCOC, STPAR,  TTYUP, etc.  Changes  made with
    these latter functions will affect editing.

(2)  NON-CONTROLLiNG TERMINALS.   Terminals   other  than   the  controlling
    terminal will have byte mode -- no editing.

(3)  INCHWL  no longer  transliterates '33  to '175.   Previous  versions of
    TENEX Sail transliterated '33 to '175.

TERMINAL MODE FUNCTIONS
The routines in  this section really refer  to terminals only in  the "mini-
system" version of  TENEX.  The argument CHAN  may be either a  Sail channel
number associated with a terminal, or a terminal specifier (such as  '100 or
'101 for the controlling terminal).



******************************  GTTYP, STTYP  ****************************;

TERMINALTYPE _ GTTYP (CHAN, @BUFFERS);
STTYP (CHAN, AC2)

 RFCOC, SFCOC RFMOD, SFMOD STPAR STI DATA TRANSFER


The indicated JSYS  is performed.  In  GTTYP the additional  values returned
from accumulator 2 are stored into reference parameter BUFFERS.



******************************  RFCOC, SFCOC  ****************************;

RFCOC (CHAN, @AC2, @AC3);
SFCOC (CHAN, AC2, AC3)

The indicated JSYS is performed.



******************************  RFMOD, SFMOD  ****************************;

MODEWORD _ RFMOD (CHAN);
SFMOD (CHAN, AC2)

A file's  mode word is  queried or altered  using the JSYS.   WARNING:  some
features, such as upper case conversion, that are advertised by BBN as being
accomplished with the  SFMOD JSYS are  actually accomplished with  the STPAR
JSYS.



*********************************  STPAR  ********************************;

STPAR (CHAN, AC2)

Does the STPAR JSYS, setting to AC2.



**********************************  STI  *********************************;

STI (CHAN, CHAR)

Does the  STI jsys (Simulate  Terminal Input) to  channel CHAN  (usually the
controlling terminal), inserting byte CHAR into the input stream.

DATA TRANSFER
The usual Sail  routines for teletype I/O  (see page 80) are  available.  In
addition,  PBIN, PBOUT,  and PSOUT  have been  added, although  they execute
exactly the same code as INCHRW, OUTCHR, and OUTSTR respectively.

 INTTY PBTIN SUPPRESSING OUTPUT


*********************************  INTTY  ********************************;

"STRING" _ INTTY

INTTY does  a TENEX-style  input.  (Note  that INCHWL  does a  TOPS-10 style
input.)  Up to 200  characters are transfered.  The activation  character is
not appended to the string, but is put into SKIP.  The value -1  is placed
into  SKIP if  the input  is terminated  for exceeding  the  200 character
limit.

The normal  activation characters  are EOL,  ESC, control-Z,  and control-G;
however, see the section regarding line editing in TENEX Sail.  At IMSSS and
SUMEX  this routine  uses  the PSTIN  JSYS  with the  standard  system break
characters; no timing is available.



*********************************  PBTIN  ********************************;

CHAR _ PBTIN (SECONDS)

[IMSSS only.]  Executes the PBTIN JSYS with timing of SECONDS.

SUPPRESSING OUTPUT
This new section is for  advanced Sail users only, and supposes  a knowledge
of  the pseudo-interrupt  system;   see the  JSYS manual  and  the interrupt
section of this manual.

The TOPS-10 system allows the  user to type a control-O and  suspend program
output to the  terminal until either another  control-O is typed  or program
input is requested.  (See  [MonCom] for a complete description.)  TENEX does
not  have  this  at  the  system  level,  but  pseudo-interrupts  provide an
alternative with which the program can receive control and  abort processing
as well as flush output.

TENEX Sail  has complete  access to the  TENEX pseudo-interrupt  system.  In
order to  facilitate handling  of control-O an  EXTERNAL INTEGER  CTLOSW has
been added to  the TENEX Sail  runtime system.  If  CTLOSW is TRUE  then any
output  to  the  controlling  terminal  (device  "TTY")  is  flushed  by the
following functions:

    PBOUT
    PSOUT
    OUT  to a channel open to "TTY", or to '101
    OUTCHR
    OUTSTR

CTLOSW  is  likewise  made FALSE  when  input  is requested  by  any  of the
following:




   INCHRS    INPUT     INTIN     TTYIN
   INCHRW    INSTR     INTTY     TTYINS
   INCHSL    INSTRL    PBTIN     TTYINL
   INCHWL    INSTRS    REALIN    TTYUP

Note:  functions SINI, CHARIN and  CHAROUT are not affected.  CTLOSW  may be
accessed by declaring it as an EXTERNAL INTEGER.

Here is an example of a control-O handler.

    ENTRY; BEGIN
    REQUIRE "<><>" DELIMITERS;
    DEFINE  !=<COMMENT>;
    ! This program sets up a control-O interrupt
      using PSI channel 0, level 0.
    ;

    EXTERNAL INTEGER CTLOSW,PS1ACS;

    SIMPLE PROCEDURE CTLO; BEGIN
    INTEGER USERPC,PSL1,USERINST,AC1,SAVEADDR;
    LABEL LEAVE;
    DEFINE  PSOUTJSYS=<'104000000076>,
            SOUTJSYS=<'104000000053>;

      SIMPLE INTEGER PROCEDURE DEV (INTEGER JFN);
      STARTCODE
        HRRZ    2,JFN;      ! THE JFN;
        SETZ    4,;
        HRROI   1,4;        ! PUT STRING IN 4;
        MOVSI   3,'200000;  ! ONLY THE DEVICE;
        JFNS;               ! GET THE STRING;
        MOVEM   4,1;        ! CVASC("DEV");
      END;

    ! this is Sail immediate interrupt level.
      No dynamic strings are accessed.;

    IF CTLOSW THEN
        BEGIN
            CTLOSW _ FALSE;         ! TOGGLE IT;
            RETURN;                 ! AND RETURN;
        END;
    STARTCODE
      MOVEI   1,'101;
      CFOBF;
    END;
    OUTSTR("^O
    ");
    CTLOSW _ TRUE;            ! NO MORE OUTPUT;



    ! get user PC and address into LEVTAB;
    STARTCODE
      MOVEI   1,'400000;
      RIR;
      HLRZ    2,2;            ! LEVTAB ADDRESS;
      MOVE    2,(2);          ! PC FOR LEVEL 1;
      MOVEM   2,PSL1;
      MOVE    2,(2);          ! USER PC;
      MOVEM   2,USERPC;
    END;

    ! return if user mode;
    IF (USERPC LAND '010000000000) THEN RETURN;

    ! in monitor. Return if not in the middle
      of a PSOUT or (SOUT to '101);
    IF NOT (
        (USERINST _ MEMORY[USERPC-1])=PSOUTJSYS
        OR (USERINST=SOUTJSYS AND
           ((AC1 _ MEMORY[LOCATION(PS1ACS) + 1])
             = '101 OR DEV(AC1)=CVASC("TTY"))))
    THEN RETURN;

    ! modify return so that output stops;
    SAVEADDR _ (MEMORY[PSL1] LAND '777777000000)
       + LOCATION(LEAVE);
    MEMORY[PSL1] SWAP SAVEADDR;
    RETURN;         ! to Sail interrupt handler;

    STARTCODE LEAVE:  JRST @SAVEADDR; END;
    END;

    INTERNAL PROCEDURE INITIALIZE;
    BEGIN
    PSIMAP(0,CTLO,0,1);
    ENABLE(0);
    ATI(0,"O"-'100);
    END;

    REQUIRE INITIALIZE INITIALIZATION;

    END;

 Utility TENEX System Calls CALL CNDIR DIRST, STDIR


11.5  Utility TENEX System Calls

An effort has been made to  provide calls that read and write  strings which
may be inconvenient  to perform from STARTCODE.   Note that the  TENEX Sail
compiler has the TENEX JSYS mnemonics defined in STARTCODE.   In STARTCODE
these definitions take precedence over the function calls of the same name.



**********************************  CALL  ********************************;

RESULT _ CALL (ACARG, "FUNCTION")

A limited set of CALLs is simulated by TENEX Sail.  Those available are

    EXIT
    DATE
    DATSAV  [IMSSS only.]
    GETINF  [IMSSS only.]
    GETPPN
    LOGOUT
    MSTIME
    PJOB
    PUTINF  [IMSSS only.]
    RANDOM  [IMSSS only.]
    RUN
    RUNTIM
    TIMER

If  any other  FUNCTION is  specified then  a continuable  error  message is
given.



*********************************  CNDIR  ********************************;

CNDIR (DIRNO, "PASSWORD")

Does  the CNDIR  jsys,  connecting to  DIRNO with  password  "PASSWORD".  If
"PASSWORD" is null then the user must have connect privileges.   TENEX error
codes are returned in SKIP, which is zero if no errors occurred.



******************************  DIRST, STDIR  ****************************;

"DIRECTORY" _ DIRST (DIRNO);
DIRNO _ STDIR ("DIRECTORY", DORECOGNITION)

These routines convert between  TENEX directory numbers and  strings.  TENEX

 GJINF GTAD IDTIM, ODTIM PMAP


error codes  are returned in  SKIP, which is  zero if no  errors occurred.
For STDIR the error codes in SKIP are

    1  string does not match
    2  string is ambiguous.

Note that DIRECTORY must be in uppercase for the STDIR JSYS.



*********************************  GJINF  ********************************;

JOBNO _ GJINF (@LOGDIR, @CONDIR, @TTYNO)

The job number is returned as the value of the call.  Reference  values are:
the  number  of  the  logged  directory  (LOGDIR),  the  connected directory
(CONDIR), and the TENEX Teletype number (TTYNO).



**********************************  GTAD  ********************************;

DT _ GTAD

The current date and time (in TENEX representation) is returned.



******************************  IDTIM, ODTIM  ****************************;

DT _ IDTIM ("DATIME");
"DATIME" _ ODTIM (DT, FORMAT)

These routines convert between  TENEX internal representation DT  and string
representation DATIME.  If DT is -1 in ODTIM then the current date  and time
is used.  If FORMAT is -1  then the format used is "TUESDAY, APRIL  16, 1974
16:33:32".  For IDTIM,  TENEX error codes are  returned in SKIP,  which is
zero if no errors occurred.   WARNING:  the IDTIM JSYS is nearly  an inverse
to the ODTIM JSYS; however, the format returned by ODTIM with FORMAT=-1 will
NOT be recognized  by IDTIM unless the  day ("TUESDAY, ") is  first removed.
Blame BBN.



**********************************  PMAP  ********************************;

PMAP (AC1, AC2, AC3)

Does the PMAP JSYS, using the accumulators for the arguments.

 RDSEG RUNPRG RUNTIM


*********************************  RDSEG  ********************************;

RDSEG (@SEGPAGES, @BUFPAGES)

This function returns the pages which are specially used by the Sail runtime
system.  The starting and ending  pages of the runtime segment  are returned
in the left and right halves, respectively, of SEGPAGES.  The first and last
pages  used for  bufferring are  returned in  the left  and right  halves of
BUFPAGES.  This function is escape from Sail.

Memory map, in general:

   pages     contents

(Compile time)
   0-n       impure data
   400-450   compiler code
   600-604   STARTCODE table, if needed
   640-670   runtime system
   770-m     UDDT

(Run time)
   0-n       impure data
   400-m     code and pure data
   600-637   I/O buffers
   640-677   runtime system
   770-p     UDDT



*********************************  RUNPRG  *******************************;

RUNPRG ("PROGRAM", INCREMENT, NEWFORK)

This does two entirely different  things depending on the value  of NEWFORK.
If NEWFORK is true then a new fork is created, capabilities are transmitted,
and PROGRAM is  run in the  new fork (with the  current fork suspended  by a
WFORK).  INCREMENT  is added to  the entry vector  location.  If  NEWFORK is
false then the current fork  is replaced with PROGRAM.  In this  case RUNPRG
is like  the TOPS-10  RUN UUO;  if the INCREMENT  is 1  then the  program is
started at  the CCL  address.  If  RUNPRG returns  at all  then there  was a
problem with the file.  Remember to say .SAV as the PROGRAM extension.



*********************************  RUNTIM  *******************************;

RUNNING _ RUNTM (FORK, @CONSOLE)

The  running time  in  milliseconds for  FORK  is returned  and  the console
connect time is returned in CONSOLE.

LEAP DATA TYPES Introduction


                                SECTION  12

                              LEAP DATA TYPES




12.1  Introduction

In  addition to  the standard  algol-like statements  and  expressions, Sail
contains an  associative data  store and  auxiliary facilities  called LEAP.
Sail's version of  LEAP is based on  the associative components of  the LEAP
language implemented by J. Feldman and P. Rovner as described in [Feldman].

An  associative store  allows the  retrieval of  data based  on  the partial
specification of  that data.  LEAP  stores associative data  in the  form of
ASSOCIATIONS,  which are  ordered three-tuples  of ITEMS.   Associations are
frequently called TRIPLES.  Associations are placed in the associative store
by MAKE  statements and  removed from  the store  by ERASE  statements.  The
associative searches  allow us to  specify items and  their position  in the
triple  and  then  have  the LEAP  interpreter  search  for  triples  in the
associative store  which have  the same  items in  the same  positions.  The
interpreter will extract  the items from  such triples, which  correspond to
the positions left unspecified in the original search request.   For example
say we had  triples representing the binary  relation Fatherof, and  we had
"made" associations of the form

        Fatherof  John  Tom
        Fatherof  Tom  Harry,
        Fatherof  Jerry  Tom,

where Fatherof, John, Tom, Harry,  and Jerry are names of items.   We could
then perform  searches to find  the sons  of Tom by  specifying to  the leap
search routines  that we wanted  to find triples  whose first  component was
Fatherof  and  whose   third  component  was  Tom.    Associative  searches
inherently produce multiple  values (i.e., both Jerry  and John are  sons of
Tom).  To deal with multiple values, Leap has SETs and LISTs of items.

Items are constants.  They may be created by declaration or by  the function
NEW.   Items  may have  a  single  algebraic variable,  set,  list  or array
associated with  them which  is accessible  by use  of the  DATUM construct.
Declared  items   have  names  which  may  be  used  to  identify   them  in
expressions, etc.  The simple variable  whose value is an item is  called an
ITEMVAR.

Syntax


12.2  Syntax

The  following syntax  is  meant to  REPLACE  not supplement  the  syntax of
algebraic declarations, except where noted.


<declaration>
    ::= <typedeclaration>
    ::= <arraydeclaration>
    ::= <preloadspecification>
    ::= <labeldeclaration>
    ::= <proceduredeclaration>
    ::= <synonymdeclaration>
    ::= <requirespecification>
    ::= <contextdeclaration>
    ::= <recordclassdeclaration>
    ::= <protectacs declaration>
    ::= <cleanupdeclaration>
    ::= <typequalifier> <declaration>
    ::= <sproutdefaultdeclaration>


<simpletype>
    ::= BOOLEAN
    ::= INTEGER
    ::= LIST
    ::= REAL
    ::= RECORDPOINTER ( <classidlist> )
    ::= SET
    ::= STRING


<itemvartype>
    ::= ITEMVAR
    ::= <simpletype> ITEMVAR
    ::= <arraytype> ARRAY ITEMVAR
    ::= CHECKED <itemvartype>
    ::= GLOBAL <itemvartype>


<itemtype>
    ::= ITEM
    ::= <simpletype> ITEM


<arraytype>
    ::= <simpletype>
    ::= <itemvartype>
    ::= <itemtype>


<typedeclaration>
    ::= <simpletype> <identifierlist>
    ::= <itemvartype> <identifierlist>
    ::= <itemtype> <identifierlist>
    ::= <arraytype> ARRAY <arraylist>
    ::= <arraytype> ARRAY ITEM <arraylist>
    ::= <typequalifier> <typedeclaration>


<arraylist> -- as on page 9


<proceduredeclaration>
    ::= PROCEDURE <identifier> <procedurehead>
        <procedurebody>
    ::= <proceduretype> PROCEDURE <identifier>
        <procedurehead> <procedurebody>
    ::= <typequalifier> <proceduredeclaration>


<proceduretype>
    ::= <simpletype>
    ::= <itemvartype>
    ::= MATCHING <proceduretype>
    ::= MESSAGE <proceduretype>


<procedurehead> and <procedurebody> -- as on page 10 except:


<simpleformaltype>
    ::= <simpletype>
    ::= <itemvartype>
    ::= ? <itemvartype>
    ::= <simpletype> ARRAY
    ::= <itemvartype> ARRAY
    ::= <simpletype> PROCEDURE
    ::= <itemvartype> PROCEDURE


<preloadspecification>, <synonymdeclaration>, <labeldeclaration>,

and <requirespecification> as on page 9


<contextdeclaration> as on page 182

 Semantics ITEM GENESIS SCOPE OF ITEMS DATUMS OF ITEMS


12.3  Semantics

ITEM GENESIS
Although items are constants, they must be created before they can  be used.
Items may  be created in three ways:

   1) A Declared Item may created  by declaration of an identifier  to be
      of type ITEM.

   2) An item may be created with the NEW construct (see page 178).

   3) A bracketed triple  item is created by  the MAKEing of  a bracketed
      triple (see MAKE, page 163).

Items of type 1 and 2 are   the same except those of type 1 may  be referred
to by the identifier that is associated with them.  For example one  may say
... ITEM DAD; ...  X_DAD; ....   NOTE: DAD is  the name of  an  item,  not a
variable!  Saying DAD_X is just as illegal as saying 15_X.

Items of type  3 are different  from those of type  1 and 2.   Discussion of
them will be left until the creation of associations with the MAKE statement
is discussed (page 163).

SCOPE OF ITEMS
Items do not obey the traditional Algol scope rules.  All declared items are
allocated in the  outer block.  All  other items are  allocated dynamically.
All items exist until a DELETE (<item expression>) is done on them (see page
162 for the details  of DELETE),  or until the outer block is exited  at the
end of  the program.   HOWEVER, the  identifiers of  declared items  (type 1
above) DO obey scope rules.  After exiting     the block in which item X was
declared,  it  will  be impossible  to  refer  to X  by  its  declared name.
However, X may have been  stored in an itemvar, associations, etc.  and thus
still be retrieved and used.

Warning: items in recursive procedures behave differently from  variables in
recursive  procedures.  At  each recursive  call of  a procedure,  the local
variables are  reinstantiated (unless  they were  declared OWN).   Items are
constants.  There is never more than one instantiation of an item  around at
a time.

DATUMS OF ITEMS
An item of type  1 or 2 may have  an associated variable, called  its DATUM.
The Datum of an  item is like any variable;  it may be declared to  have any
type that a variable may have, except the type Itemvar.  Because an item may
have only one datum from   its creation until its death, we  frequently will
say the "type of an item" referring to the type of the datum.  RESTRICTIONS:
It  is currently  impossible to  make either  items or  their  datums either
Internal  or  External.   However,  the  effect  of  External  items  can be
duplicated by manipulating the order  in which items are declared  (see page
157).  OWN is not applicable  as items are constants, not  variables.  Items

 ITEMVARS


of type ARRAY must be declared with constant bounds since they are allocated
upon entering the outer block.

Example declarations of items with datums:

    INTEGER ITEM FATHEROF;
    STRING ITEM FOO;
    INTEGER ARRAY ITEM NAMES [1:4, 1:8]; COMMENT note
            the specification of the array's dimensions;
    SHORT REAL ITEM POINT;

    EXTERNAL ITEM BLAT;        COMMENT illegal;
    ITEMVAR ITEM BLAT;         COMMENT illegal;
    STRING ITEMVAR ITEM BLAT;  COMMENT illegal;
    REAL PROCEDURE ITEM BLAT;  COMMENT illegal;
    PROCEDURE ITEM BLAT;       COMMENT illegal,
                                     use ASSIGN;

The syntax for variable includes the Datum construct.  That is, if AGE  is a
declared an Integer Item,  then DATUM (AGE) behaves exactly like  an Integer
variable.  If ARR is declared as

        STRING ARRAY ITEM ARR [2:4, 1:9+2]

then DATUM (ARR) is a string array with two dimensions of the declared size.
A new array may  not be assigned to the  Datum of ARR, though of  course the
individual elements of the array may be changed.  Datums obey the  same type
checking and type conversion rules that the algebraic variables of  Sail do.
For example,  when a string  is  assigned to  an integer datum,  the integer
stored in  the integer  datum is  the ASCII  of the  first character  of the
string.  ITEMVARS
An Itemvar is  a variable whose  value is an  Item.  Just as  the statements
"X_3;  Y_X"  and "Y_3"  are  equivalent with  respect to  Y,  the statements
"X_DAD;  Y_X" and "Y_DAD" are equivalent with  respect to Y, if X and  Y are
itemvars,  DAD  an item.   The  distinction between  itemvars  and  items is
identical to  the distinction  between integer  variables and  integers.  An
integer variable may only contain an integer and a variable declared ITEMVAR
may only contain an item.  This may be confusing since historically, integer
variables have always been called INTEGER rather than INTEGERVAR.

Properly  speaking, one  should have  INTEGERVAR ARRAYs  instead  of INTEGER
ARRAYs.  Originally,  Sail only  allowed ITEMVAR  ARRAYs.  However,  so many
people found this confusing that now one may say ITEM ARRAY, and it  will be
interpreted to mean ITEMVAR ARRAY.  Similarly, an Item procedure  is exactly
the same as an Itemvar  procedure.

An  itemvar  may  contain  items  of  any  type.   However,  when  one  says
DATUM (ITMVR) where ITMVR is an itemvar, the compiler must know the  type of
the datum of the item (i.e. the type of the item) contained in  the  itemvar
so  that the  the correct  conversions,  etc. may  be done.   Thus,  one may

 SETS AND LISTS


declare itemvars to have  the same types that  are legal for items.   If one
has declared  STRING ITEMVAR ITMVR, then the compiler assumes that  you have
stored an string item in ITMVR, and and will treat DATUM (ITMVR) as a string
variable.

An Itemvar may be declared CHECKED  if the user desires the type  of itemvar
checked against the  type of the datum  of the item expressions  assigned to
it.  That  is, only  a string  item could  be stored    in a  Checked String
Itemvar.  If the itemvar is not declared Checked, it may have an item of any
type assigned to it and their types need not match at all.  This can be very
dangerous.  For example, an integer array item might be assigned to a string
itemvar.  When  the datum of  this itemvar is  later assigned to  an integer
variable, say, Sail will try to  treat the array header as a  string pointer
and get very confused.  The runtime routine TYPEIT, page 218, returns a code
for the type of its argument,  and can be useful for avoiding  type matching
errors with un-checked itemvars.

GLOBAL itemvars  are a  special kind  for SUAI  global model  users.  Global
model operation  allows several  jobs to  share a  data segment,  and GLOBAL
itemvars are  used to build  the data structures  in this  segment.  MESSAGE
procedures are also related to global model operations.  These features have
fallen into disuse.

EXTERNAL, OWN and INTERNAL Itemvars  are legal.  SAFE applies to  either the
array of an array itemvar, the array of an itemvar array, or both  arrays of
an array itemvar array.

Itemvars obey traditional Algol block structure.  Upon exiting the  block of
their  declaration,  their  names  are  unavailable  and  their  storage  is
reallocated.  However, the item  stored in an itemvar is not affected  -- it
continues to exist until DELETEd or until the end of the program.

Itemvars are initialized to the  special item ANY at the beginning  of one's
program.

SETS AND LISTS
Sets and Lists are collections of items.  There are two distinctions between
Sets and Lists: a list may contain multiple occurrences of any item  while a
set contains at  most a single  instance of an  item.  Second, the  order in
which items  appear within a  list is completely  within the control  of the
user  program,  while  with  a  set, the  order  is  fixed  by  the internal
representation of the items.   Lists and Sets do  not care what type  if any
the datums of their members are.

List and Set Arrays, Itemvars, Items, and Procedures are all legal,  as well
as External, Own and Internal  Sets and Lists.  Like itemvars, the  scope of
Set and  List variables is  the block they  were declared in.   Exiting that
block does not destroy the items stored in the departed sets or lists.

 ASSOCIATIONS PROCEDURES IMPLEMENTATION


ASSOCIATIONS
Perhaps the most important form  of storage of items is the  Association, or
TRIPLE.  Triples of  items may be written  into or retrieved from  a special
store, the  associative store.  The  method of storage  of these  triples is
designed    to  facilitate   fast   and  flexible   retrieval.    Sail  uses
approximately two words of storage for each triple in the associative store.
There is at  most one copy  of a triple  in the store  at any time.   Once a
triple has been stored in the associative store, its component items can not
be changed, although  an approximation to this  can  be obtained  by erasing
the association then making  a new association with the  altered components.
You will note there is no  syntax for declaring a triple.  Triples  can only
be created with the MAKE statement.  In the examples which follow,  a triple
is represented by :

       A  O  V

where  A, O,  and V  represent  the items  stored in  the  association.  The
associative store is  accessed by the  FOREACH statement, derived  sets, and
binding triples (see Searching the Associative Store, page 164).

PROCEDURES
Itemvar, Item, List, and Set procedures all exist.   Itemvar  procedures may
be CHECKED if  one desires the  item RETURNed to have  the same type  as the
type of the Itemvar procedure.   Otherwise, the compiler only checks  to see
that the value returned to an itemvar procedure is an item.

Every type except Item may  be used in formal parameter  declarations; items
are constants yet parameters always  have something assigned to them  in the
procedure call.  Since you can't  assign something to a constant,  you can't
have item parameters.

WARNING: when using Checked  Reference Itemvar formals, no type  checking is
performed as  the actual is  assigned to the  formal at the  procedure call.
However, type checking will only be done during the procedure, and  when the
formal is assigned  to the actual upon  the (normal) exit of  the procedure.
IMPLEMENTATION
Each Item is represented by  a unique integer in the compiler.   The numbers
are assigned in  the order the items  are declared, e.g. the  first declared
item gets 1, the second gets 2, etc.  (Actually, Sail has already declared 8
items  that  it  needs,  so  user item  numbers  start  with  9.   REQUIRE n
ITEMSTART changes  the number at  which user items  start (only  useful for
SUAI global model users).  Lexical  nesting is not observed; it is  only the
sequence  in  which  the  declarations  are  scanned  that  determines their
numbers.   The NEW  function  does not  affect this  assignment  of numbers.
Items created by the New function are assigned the next available  number at
the time of the execution of the New.

Those who use separately compiled procedures (see page 25) may wish  to have
declared  items common  to both  programs.  However,  Internal  and External
items do not exist.  The same effect may be achieved by  carefully declaring



the desired items in the same  order in both programs so that  their numbers
match.  The message  "Warning -- two programs  with items in them."  will be
issued at the begining of execution,  and may be ignored if you  are certain
the  items are  declared in  the same  relative positions.   No  checking of
names, types, arrays bounds, etc. is done, so be very careful.

Items occupy no space (neither  does the constant integer 15).   The numbers
ascribed to  items are  stored in Itemvars  and Associations.   Itemvars are
simply a word  of storage.  An association  is two    words of  storage, one
with three  12 bit bytes, each containing the number of one of the  items of
the  association, and  a second  word containing  two pointers  relating the
association to  the associative  search structure.  Since  the number  of an
item must fit in 12 bits, the number of items is limited to about 4090.

The number of an item may be retrieved from the item as a  integer  with the
predeclared  function CVN (<itemexpression>).   The item  represented  by a
certain    integer   may   be   retrieved   by   the   predeclared  function
CVI (<algebraicexpression>).  CVN and CVI should only be used by  those who
know what they're doing  and have kept themselves  up to date on  changes in
Leap.

LEAP STATEMENTS Syntax


                                SECTION  13

                              LEAP STATEMENTS




13.1  Syntax


<leapstatement>
    ::= <leapassignmentstatement>
    ::= <leapswapstatement>
    ::= <setstatement>
    ::= <liststatement>
    ::= <associativestatement>
    ::= <foreachstatement>
    ::= <sucfailstatement>


<leapassignmentstatement>
    ::= <itemvarvariable> _ <itemexpression>
    ::= <setvariable> _ <setexpression>
    ::= <listvariable> _ <listexpression>


<leapswapstatement>
    ::= <itemvarvariable>  <itemvarvariable>
    ::= <setvariable>  <setvariable>
    ::= <listvariable>  <listvariable>


<setstatement>
    ::= PUT <itemexpression> IN <setvariable>
    ::= REMOVE <itemexpression> FROM
        <setvariable>


<liststatement>
    ::= PUT <itemexpression> IN <listvariable>
        <locationspecification>
    ::= REMOVE <itemexpression> FROM
        <listvariable>
    ::= REMOVE ALL <itemexpression> FROM
        <listvariable>


<locationspecification>
    ::= BEFORE <elementlocation>
    ::= AFTER <elementlocation>




<elementlocation>
    ::= <itemexpression>
    ::= <algebraicexpression>


<associativestatement>
    ::= DELETE ( <itemexpression> )
    ::= MAKE <triple>
    ::= ERASE <triple>



<triple>
    ::= <itemexpression>  <itemexpression>
         <itemexpression>


<foreachstatement>
    ::= FOREACH <bindinglist> SUCH THAT
        <elementlist> DO <statement>
    ::= NEEDNEXT <foreachstatement>


<bindinglist>
    ::= <itemvarvariable>
    ::= <bindinglist> , <itemvarvariable>


<elementlist>
    ::= <element>
    ::= <elementlist> AND <element>


<element>
    ::= <itemexpression> IN
        <listexpression>
    ::= ( <booleanexpression> )
    ::= <retrievaltriple>
    ::= <matchingprocedurecall>


<retrievaltriple>
    ::= <rettripelement>  <rettripelement>
         <rettripelement>


<rettripelement>
    ::= <itemexpression>
    ::= <derivedset>

 Restrictions Semantics ASSIGNMENT STATEMENTS



<matchingprocedurecall>
    ::= <procedurecall>


<sucfailstatement>
    ::= SUCCEED
    ::= FAIL




13.2  Restrictions

SUCCEED  and FAIL  statements  must be  lexically nested  inside  a matching
procedure to be legal.



13.3  Semantics

ASSIGNMENT STATEMENTS
Assignment statements in Leap are similar to those in Algol.   Itemvars, Set
variables,  and  List  variables   may  be  assigned  item,  set   and  list
expressions,  respectively.   Only one  automatic  coercion is  done:  a set
expression  may be  assigned to  a list  variable.  NOTE:  lists may  not be
assigned to set variables (use CVSET).

The type of an  itemvar is checked against  the type of the  item expression
assigned to it if and only  if the itemvar is declared Checked.  If  a typed
item is assigned to an un-Checked itemvar of different or no type, the datum
is not affected.  Assign an integer item to a string itemvar and  the string
itemvar will now contain an item with an integer datum.  Sail will  not know
that you have  in effect switched  the type of the  datum and will  get very
confused if you later try to use the datum of the itemvar; it will treat the
integer as a pointer to a two word string descriptor in this case.

DATUM (X) is legal only  when X is a  typed item expression, namely  an item
expression that the compiler can  discover the type of (not  COP (<set>) for
example).  See page 226 for the BNF of typed item expressions.  DATUM (X) is
syntactically  a variable.  It has the type of the typed item expression, X.
If  X   has  an   array  type,   then  DATUM (X)   should  be   followed  by
[<subscriptlist>].  Appropriate coercions    will be done (i.e.,  string to
integer,  integer  to  real,  etc.)  just  as  with  regular   variables  in
expressions.  NOTE: the user is responsible for seeing that the datum  of an
item expression really is the type that Datum thinks it is (i.e., Datum of a
Real Itemvar that has had a string item stored in it will give you garbage).

PROPS (X), where X is an item expression, is legal regardless of the type of
X.  X  may even  evaluate to  a  bracketed triple  item, procedure  item, or

 PUT REMOVE DELETE


event item.  PROPS (X) is syntactically an integer variable.  It  is limited
to  integers  n  where 0  n 4095.   If  negative  (i.e.  two's complement)
integers or  integers larger  than 4095 are  assigned to  a PROPS,  only the
right 12 bits are stored.  The rest of the integer is lost.

PUT
Sets and lists are initially empty.  One may put items in them with  the PUT
statement.   "PUT <item expression> IN <set variable>" does exactly  what it
says.

"PUT  <item expression>  IN <list  variable> BEFORE  <algebraic expression>"
evaluates  the  item  expression,  evaluates  the  algebraic  expression and
coerces it into an integer, say n,  then puts the item into the list  at the
nth position, bumping the old nth item to the n+1th position, and so on down
the list.  This increases the length of the list by one.  "PUT item  IN list
AFTER n"  places the item in the n+1th position and bumps the old n+1th item
down to  the n+2th  position, and  so on.   If n < 0  or n > (1 + length-of-
list), then an error message is given.  The special token "" may be used in
the expression for n to stand for the length of the list.

"PUT  <item expression  1> IN  <list variable>  BEFORE  <item expression 2>"
cause a search to be made  of the list for the item of  <item expression 2>.
If it  is  found,  the  item of  <item expression 1> is  placed in  the list
immediately ahead of the item found by the search.  "PUT item IN  list AFTER
item" proceeds the same way, but puts the first item in the list immediately
following the  second item.  If  the second  item is not  an element  of the
list, a BEFORE will put the first item at the begining of the list, while an
AFTER will put it at the end of the list.

REMOVE
To remove an item from a set or list, one may use REMOVE.  "REMOVE item FROM
set" does just what it says.  If  the item to be removed from  the  set does
not occur in the set, this statement is a no-op.

"REMOVE n FROM list" removes the nth item from the list.  The old n+1th item
becomes  the  nth,  and  so  forth.   An  error  is  indicated  if  n  0 or
n > length-of-list.  As before,  should stand  for the length of  the list.
However,

"REMOVE item FROM  list" removes the first  occurrence of the item  from the
list.  If the item is not found, this statement is a no-op.

"REMOVE ALL  item FROM list"  removes all occurrences  of the item  from the
list.  DELETE
Items  are  represented by  unique  integer  numbers in  Sail.   Due  to the
overwhelming desire to  store an association in  one word of  storage, these
unique numbers are limited  to 12 bits.  Thus  the total number of  items is
limited to 4090.  The DELETE statement allows one to free numbers for reuse.
It is also the only way to get rid of an item short of exiting  the program.
WARNING: The Delete statement in no way alters the instances of  the Deleted

 MAKE BRACKETED TRIPLE ITEMS


items which are present in sets, lists, associations, or itemvars.  The user
should be sure that there are no instances of the Deleted item  occurring in
itemvars, sets,  lists or associations.   Even saying   DELETE (ITMVR) where
ITMVR is an itemvar  with an  item to be  deleted in it will not  remove the
item from ITMVR; one must be careful to change the contents of  ITMVR before
using it again.

MAKE
The MAKE statement is the only way to create Associations (Triples)  and add
them to  the associative store.   If the association  already exists  in the
store, no  alterations are made.   The argument to  the Make statement  is a
triple of item expressions:

   MAKE item1  item2  item3
   MAKE item1  itemvar1  NEW
   MAKE itemvararray[23]  item1  itemvar2

The component item expressions are evaluated left to right.  The three items
that the three expressions evaluate to are then formed into  an association,
and  the  association  is  hashed  into  the  associative  store.   The item
expressions must be constructive, that is, one may use the NEW  function but
not the ANY or BINDIT items (see NEW, page 178,  ANY, page 179,  and BINDIT,
page 179).

BRACKETED TRIPLE ITEMS
Items  may be  created by  declaration,  by the  NEW function,  or  by using
BRACKETED TRIPLEs in Make statements.  A Bracketed Triple item may  not have
a datum, but may have a PROPS or a PNAME (see page 220 for pnames,  page 161
for props).  Instead, a  Bracketed Triple item has an  Association connected
to it.  One creates a Bracketed Triple item by executing a Make statement:

        MAKE item1  [item2item3item4]  item5

where  the itemN  are  item  expressions.  "[item2item3item4]"  is the
Bracketed Triple item, and of course need not always be the second component
of the association.  The association connected to the Bracketed  Triple item
is "item2  item3  item4".  The  above Make statement  actually creates
two triples and one item.  Namely, the associations

   item1  itemXX  item5
   item2  item3  item4

and the item "itemXX" which is   a Bracketed Triple item and has  the second
association connected to  it.  One can  access  a Bracket Triple  item, with
the an associative search called the Bracketed Triple Item Retrieval:

    itmvar _ [itm2  itm3  itm4];
        COMMENT itmvar now contains itmXX;

The Bracket Triple construct may be used in any expression.  See page 166.

 ERASE Searching the Associative Store


Having "itmXX",  one may access  the items of  the association  connected to
with the predeclared functions FIRST,  SECOND, and THIRD (see page  222  for
more information on these runtime functions):

   FIRST (itemXX)  is item2
   SECOND (itemXX)  is item3
   THIRD (itemXX)  is item4

ERASE
The  way to remove an association from the associative store and  destroy it
is to ERASE it:

   ERASE item1  item2  item3

where  the  itemN  are  item  expressions.   The  item  expressions  must be
retrieval item expressions; that is, one  may use the ANY item  but  not the
NEW function or the BINDIT item  (see ANY, page 179, and NEW, page  178, and
BINDIT page 179).  Using ANY as  one, two, or three of the  item expressions
allows  many associations to be erased in one statement.  If the association
to be erased does not exist, Erase is a no-op.

Whenever one Erases an association, none of the items of the association are
deleted.   In  particular,  when  one Erases   an   association  that  has a
Bracketed Triple item as one of its components, the Bracketed Triple item is
not deleted.  Furthermore, the association connected to the Bracketed Triple
item  is not  automatically erased  by erasing  an association  containing a
Bracketed Triple item.  The following Erase erases only one association:

   ERASE item1  [item2item3item4]  item5

However, erasing the association connected to a Bracketed Triple deletes the
item.  Deleting  the Bracketed  Triple item DOES  NOT erase  the association
connected to it.



13.4  Searching the Associative Store

Flexible  searching and  retrieval  are the  main motivations  for  using an
associative store.  It  follows that this is  the most important  section of
the Leap part of this manual.  It  is a rare Leap program that does  not use
at least one of the searches described below.

Four methods of searching the associative store exist in Sail:

   Binding Booleans
   Derived Sets
   Bracketed Triple item retrieval
   Foreach Statements

 BINDING BOOLEANS


The first three are properly  part of the discussion of Leap  Expressions in
the next chapter, but are included here for completeness.

Throughout  this  section  we  will  use  the  following  notation   for  an
association:

   A  O  V

where A, O and V stand for the "attribute", "object" and "value" items of an
association.

The terms "bound" and "unbound" will find heavy use in this  section.  Bound
describes an itemvar that has an item assigned to it.  Unbound  describes an
itemvar that,  at this time  in the  execution of the  program, has  no item
bound to it.   The object of searching  the associative store is  usually to
bind unbound itemvars to specific, but unknown, items.  If the itemvar to be
bound  was  declared Checked,  then  type  checking will  be  done,  and the
appropriate error message  will be issue if  the binding item does  not have
the same type as the itemvar.

Throughout this  section, references  to item  expressions will  always mean
retrieval item expressions.  Do not use NEW in such expressions.

A hashing algorithm is used in storing and retrieving associations  in Leap.
The user  can increase the  speed of associative  searching or  decrease his
core image by using the REQUIRE  n BUCKETS construct to control the  size of
his associative search hash table  to reflect the number of  associations he
will be using.  A hash table will be allocated with (2^m) hash codes where m
is the smallest integer such that (2^m)  n.  Sail initializes the hash size
to '1000.

BINDING BOOLEANS
A Binding  Boolean searches  the associative store  for a  specified triple,
returning true if one can be found, and false otherwise.  A  Binding Boolean
is a triple:

   itm1  itm2  itm3

where "itmN"  is one of  three things: an  item expression, or  the reserved
word "BIND" followed by an itemvar, or the token "?" followed by an itemvar.
An  item  expression  as  a component  of  the  Binding  Boolean  means that
component of the triple that the boolean finds must be the item specified by
the item expression (unless the  item expression evaluates to the  item ANY,
which specifies that any item is okay).  If a "BIND" itemvar is the A,  O or
V  of  the  triple,  then  the  Binding  Boolean  will  attempt  to  find an
association which meets the constraints imposed by the item expression  A, O
or V components, and then binds to the "BIND" itemvar the items  occuring in
the  corresponding positions  of the  association that  the  Binding Boolean
found.   If no  such  association can  be  found, then  the  Binding Boolean
returns FALSE and leaves the "BIND" itemvars with their previous values.  If

 DERIVED SETS BRACKETED TRIPLE ITEM RETRIEVAL THE FOREACH STATEMENT


"?" precedes an itemvar, then the itemvar will behave like a  "BIND" itemvar
if it is currently contains BINDIT, but will behave like an  item expression
if it is bound to some other item than BINDIT.  Example:

   IF Father  ?Son  ANY THEN PUT Son IN Sonset;
   IF Father  BIND Son  Bob THEN CHILDLESS (Bob);
   ERCHEK _ Father  COP(Sonset)  ANY;

DERIVED SETS
Derived Sets are quite  simple:   "Foo  Garp"  where Foo and Garp  are item
expressions, is the set of all items X such that  Foo  Garp  X exists.
"Garp  Sister"   is    the   set   of    all   items   X    such   that
X  Garp  Sister exists.  "Foo ' Sister" is the set of all items X such
that Foo  X  Sister exists.  Examples:

        Dadset _ Father  ANY;
        Danson _ Father ` Dan;
        News _ (Son  Dad)  attset;

ANY specifies "I don't care"  to the search.  BINDIT has no  special meaning
to the search,  and behaves like any  other items.  Since  BINDIT  can never
appear in  an association, this  means the set  returned will always  be the
empty set PHI.

BRACKETED TRIPLE ITEM RETRIEVAL
A Bracketed Triple item can  be referenced by specifying the  association it
is connected to.  For example,

   Itmvar _ [itm1  itm2  ANY]
   PUT [ANY  ANY  ANY] IN Bracset
   IF Foo  Garp  [itm1  itm2  ANY] THEN ...
   Itmvar _ [itm1  [itm2  itm3  itm4]  itm5]

where itmN is any item  expression not containing NEW or BINDIT.   ANY means
you  don't  care  what  item occupies  that  component.   If  the designated
Bracketed Triple is not found  then BINDIT is returned and no  error message
is given.  THE FOREACH STATEMENT
This statement is the heart of Leap.  It is similar to the FOR  statement of
Algol in that a statement is  executed once for each binding of  a variable.
In  this semi-schematic example,

   FOREACH X SUCH THAT <element> AND ... AND
      <element> DO <statement>;

the <statement>  is executed once  for each binding  of the itemvar  X.  The
<element>s  in  the  element  list  (i.e.  <element>   AND...AND  <element>)
determine  the  bindings  of  the itemvar,  and  hence  how  many  times the
<statement>  is executed.   If  the <element>s  are  such that  there  is no
binding possible for X, then the <statement> is never executed.  Like a Sail
FOR statement, one may use DONE, NEXT, and CONTINUE within  the <statement>.



As before,  when one uses  a NEXT  inside the loop,  the word  NEEDNEXT must
precede  the FOREACH  of the  Foreach that  one wants  checked  and possibly
terminated.  See pages 37, 38, and 38 for more information about Done, Next,
and Continue.

Restriction:  Jumping  (i.e.  with  a GO  TO)  into  a  Foreach  is illegal.
However, it is legal to jump out of a Foreach, or to jump around  within the
same Foreach.

Foreach statements differ from For statements in that more than  one itemvar
may be included to be given bindings:

   FOREACH X, Y, Z SUCH THAT <element>....

X, Y,  and Z  are called  Foreach itemvars.   Just as  one must  declare the
integer I before using it in  the Sail For statement

   FOR I _ 1 STEP 2 UNTIL 21 DO...

so  must  one  declare  Foreach itemvars  before  using  them  in Foreaches.
Foreach  itemvars  are  no  more  than  normal  itemvars  receiving  special
assignments; they  may have any  type.  If a  Foreach itemvar that  has been
declared Checked is assigned an item by the search that has a different type
than the Checked itemvar, an error message will result.

Foreach itemvars  differ from For  variables in a  more radical way.   It is
possible  to specify  to the  Foreach that  a certain  Foreach itemvar  be a
variable to the search only on the condition that that the  itemvar contains
the special  item BINDIT at  the time the  Foreach is called.   One precedes
such itemvars with the "?" token.  For example:

   FOREACH ?X, ? Y, Z SUCH THAT <element>....

If X contains BINDIT but Y does not when this Foreach starts execution, then
the search will be conducted exactly as if the statement

    FOREACH X,Z SUCH THAT <element>....

were  the Foreach  specified.  The  itemvar  X will  then act  just  like an
ordinary, non-foreach itemvar that  was bound previous to the  Foreach.  All
Foreach itemvars may be "?" itemvars if this is desired.

There are  four different  types of <element>  that may  be used  in foreach
element lists:

        Set Membership
        Boolean Expressions
        Retrieval Triples
        Matching Procedures

 THE LIST MEMBERSHIP <ELEMENT>


The order of  the <element>s in  the element list  is very important,  as we
shall see.

Terminology:  we say  that a  certain binding  of the  the  Foreach itemvars
"satisfies" an <element>.  If  that binding satisfies each <element>  of the
element list, then we say  it "satisfies the associative context".   A fancy
way of refering to the element list is "associative context".  We also refer
to the collection  of bindings that satisfy  the associative context  as the
"satisfier group" of the Foreach.

The execution of a  Foreach proceeds as follows.  After  initialization, the
Foreach  proceeds with  a search  specified by  the first  <element>  of the
element list.  If a binding can be found that satisfies the first <element>,
the Foreach proceeds forward to the  new <element> of the list and  tries to
satisfy it, and so  on.  When the Foreach  can not satisfy an  <element>, it
"backs up" to the previous element and tries to get a different binding.  If
it can't find satisfaction there, it backs up again and tries again to get a
different  binding.  When  a Foreach  proceeds forward  off the  end  of the
element  list  (i.e.  the   associative  context  is  satisfied)   then  the
<statement> is executed, and the  Foreach backs up to the last  <element> of
the element list.  When the Foreach backs up off the left end of the element
list,  the Foreach is exited.

When a Foreach is  exited by backing up  off the left, the  Foreach itemvars
are restored to the last  satisfier group bound to them, regardless  of what
the  <statement>  may  have  done.  If  the  associative  context  was never
satisfied, then the  Foreach itemvars have the  values that they  had before
the Foreach.  When a Foreach is  exited with a  GO TO, DONE, or  RETURN, the
Foreach leave  the itemvars  with the  bindings they  had at  the GO  TO, or
whatever,  including any modifications that the <statement> may have made to
them.

THE LIST MEMBERSHIP <ELEMENT>
[In the following,  one may also read  "set" for "list";  Sail automatically
coerces  set expressions  into list  expressions.] This  <element>  does not
search the associative store to bind an itemvar, but merely binds it with an
item of a specified list.  In the Foreach,

    FOREACH X | X IN L DO <statement>;

(here  we have  used the  Sail  synonym "|"  for "SUCH  THAT"),  the Foreach
itemvar X is bound  successively to each element  of the set L,  starting at
the beginning of the list.  If an  item occurs n times in L, then X  will be
bound to that item n times  in the course of the Foreach.  Thus,  the number
of satisfiers to the above Foreach is LENGTH (L).

In the current implementation of Leap, there is a difficulty that  should be
pointed out.  If inside the  <statement>, one changes L by  list assignment,
Removes, etc. in such a way as to remove the next item of the list  that the

 THE BOOLEAN EXPRESSION <ELEMENT>


Foreach itemvar  would have  been bound  to,  Leap  may  go  crazy.  Foreach
searches look one ahead and save a pointer to the next items to be  bound to
the Foreach itemvars.   This allows one to  remove the items of  the current
bindings of  the Foreach itemvars  from lists or  whatever, but  makes other
removals hazardous.  For example,

    FOREACH X | X IN L DO REMOVE X FROM L;

will work, but

    PUT V IN L BEFORE FOO;
    FOREACH X | X IN L DO  REMOVE V FROM L;

will probably fail.  No error checking is done.

Whenever the Foreach itemvar of a list <element> has been  bound previously,
the list element behaves like a boolean.  It does not rebind the itemvar but
only checks to see that it is in the list.  For example,

    FOREACH X | X IN L AND X IN LL DO <statement>;

X is bound by the <element> "X  IN L".  <element> "X IN LL" is  satisfied if
the item contained in the itemvar X is in the list LL.

If two different Foreach itemvars are used with two different lists, i.e.

    FOREACH X,Y | X IN L AND Y IN LL
    DO <statement>;

then after execution of the  <statement>, the Foreach will go back  the last
<element> that searches for bindings, in this case "Y IN LL" and gets  a new
binding for Y.  It is only  on failure of this search that the  Foreach goes
back to the first <element>, "X IN  S", and gets a new binding for  X.  Thus
the <statement> will  be executed once for  each possible X,Y pair.   In the
Foreach,

    FOREACH X,Y | X IN L AND Y IN L  ...;

X   and Y  will be  bound  to all  possible pairs  of elements  in  L.  This
includes pairs with duplicate elements, like (a,a).  Different  orderings of
the same  elements will NOT  be ignored.  Thus,  pairs like (a,b)  and (b,a)
will each be a satisfier group sometime during the Foreach.  Furthermore, if
the list  L contains duplications  of the same  item, identical  pairs  will
occur in proportion to the  number of duplications.  That is,  regardless of
the duplications  within the  list, the  number of  satisfier groups  to the
Foreach above is LENGTH (L)^2.  THE BOOLEAN EXPRESSION <ELEMENT>
 Any Sail boolean expression may be used as an <element> in  the Associative
Context of a Foreach if it is inclosed by parentheses.  A Boolean Expression
<element> is satisfied if it is TRUE.  Note that the boolean expression must
have parentheses around it.

 THE RETRIEVAL TRIPLE <ELEMENT>


WARNING:  Foreach  itemvars  can  not  be  bound  by  a  Boolean  Expression
<element>.  Therefore, all itemvars  used in a Boolean  Expression <element>
must     be  bound by previous  <element>s in the  element list.   A Boolean
Expression <element>  with unbound  Foreach itemvars in  it causes  an error
message. THE RETRIEVAL TRIPLE <ELEMENT>
To  search the  associative store  with a  Foreach, one  uses  the Retrieval
Triple  <element>.  A  Retrieval Triple  is satisfied  if a  binding  of the
Foreach itemvars can be found such that the triple is an extant association.
If all of the itemvars of the Retrieval Triple <element> were bound previous
to the execution of the Retrieval Triple <element>, then the Triple  does no
further  binding;  it   is  satisfied if  the  specified  triple  is  in the
associative store.  For example,

    FOREACH X | FATHER  TOM  X AND
        X IN PTASET DO <statement>;

    FOREACH X | X IN PTASET AND
        FATHER  TOM  X DO <statement>;

The two Foreaches have  the same effect.  However,  in the first case,  X is
bound  by a search of the  associative store for any triple that  has FATHER
as its attribute component, and TOM as its object component.    When  such a
triple is found, X is bound to the item that is the value  component.  Then,
if X is in the PTASET, the Foreach lets the statement execute.  If X is not
in PTASET, then the Foreach backs up and tries to find  another triple with
FATHER as its attribute and TOM  as its value.  In the second Foreach,  X is
bound with an  item from PTASET, then  the associative store is  checked to
see that the triple FATHERTOMx, where x is the binding of X, is in the
store.  If it is, the  <statement> is executed, otherwise the  Foreach backs
up and gets  a different item  from PTASET and  binds that to  X.  Assuming
that Tom has only one father, the first search is much faster.

Using ANY  in a  Retrieval Triple indicated  that you  don't care  what item
occupies that position.  For instance, in

    FOREACH X | FATHER  ANY  X DO <statement>;

X is bound successively to  all fathers.  However, if the  associative store
included the following three associations,

        FATHER  KAREN  PAUL
        FATHER  LYNN  PAUL
        FATHER  TERRY  PAUL

then X would be bound to PAUL only once, not thrice.  BINDIT has  no special
meaning  to the search.  Since BINDIT can never appear in an  association, a
Retrieval Triple containing it will cause the search to always fail.

Different kinds of associative searches proceed with different efficiencies.
Listed below  in order of  decreasing efficiency are  the  various  forms of



Retrieval Triple <element>s  that are  legal.  A, O, and V  represent either
bound  Foreach  itemvars or  items  from explicit  item  expressions  in the
triple.  x,  y, and z  represent unbound Foreach  itemvars or the  item ANY.
(note that x  x  V is really x  O  V, and so on).  The two forms
of the List Membership <element> are included for comparison.

    x  IN  L All items x in the list L.
    A  O  x Only the value is free.
    x  y  V Attribute and object are free.
    A  IN  L Verification that item A is in list L.
    A  O  V Verification that the triple
             is in the store.
    A  x  V Only the object is free.
    x  O  V Only the attribute is free.
    A  x  y Object and value are free.
    x  O  y Attribute and value are free.
    x  y  z Attribute, value and object are free.

Note  that  MAKEing an association  inside a Foreach  may or may  not affect
subsequent bindings.  For example, in

      FOREACH X,Y | Link  X  Y DO
               MAKE Link  X  Newlink;

it is uncertain whether Y will ever receive Newlink as its binding or not.

The A, O, and V used in a Retrieval Triple of a Foreach may be a derived set
expressions  as well as item expressions.  For example,

      FOREACH X, Y | Link  (FatherY)  X DO ...;

ERASE in the <statement>  of a Foreach that  binds any of its  itemvars with
Retrieval Triples  may cause problems.   This is similar  to REMOVE  used in
Foreaches with List Membership  <element>s controling some  bindings.  ERASE
can only be guaranteed  to to work safely  if the association erased  is the
one we just got a binding from, e.g.

      FOREACH X | A  O  X DO ERASE A  O  X;

or if the association erased could  not possible be used for a binding  of a
Foreach itemvar, such as,

      FOREACH X | Link  X  Node DO
              ERASE Node  X  ANY;

Foreaches look one ahead to the  next binding of its itemvars, and  leaves a
pointer to those associations.  If you Erase any of those  associations, the
Foreach gets lost in the boondocks.  No error checking is done.

However,  as  long  as  the associative  store  is  not  changed  during the

 THE MATCHING PROCEDURE <ELEMENT>


execution  of the  Foreach,  a Retrieval  Triple  will not  itself  repeat a
particular set  of bindings  that it bound  before.  THE  MATCHING PROCEDURE
<ELEMENT>
Matching Procedures are  the most general  search mechanism in   Leap.  They
also provide a convenient method of writing coroutines.

A  MATCHING  Procedure  is  very similar  to  a boolean  procedure  (in fact
outside of Foreach associative contexts, it behaves like a boolean procedure
and  may be  called within  expressions, etc.).   It must  be  declared type
MATCHING.   It may  not  be declared  SIMPLE.   The formal  parameters  of a
Matching  Procedure  may  include  zero  or  more  "?"  itemvars (pronounced
"question itemvars") which may have any  datum type but may not be  VALUE or
REFERENCE.  These parameters correspond  roughly to either call by  value or
call by reference, depending on  the actual parameter when the  procedure is
called.  When the actual parameter is an item expression or a  bound itemvar
the parameter is equivalent to   a value parameter.  However, if  the actual
parameter is an unbound Foreach itemvar, then the parameter is treated  as a
reference parameter,  and on entry  is is  initialized  to the  special item
BINDIT.

Matching Procedures  are exited  by SUCCEED and  FAIL statements  instead of
RETURN statements.   When used  outside of  an associative  context, SUCCEED
corresponds to RETURN(TRUE) and  FAIL corresponds to RETURN(FALSE)  [this is
not strictly true  when the matching procedure  is sprouted as a  process --
see page 190].   Inside an associative  context, Succeed and  Fail determine
whether the Foreach is to proceed to the next <element> of the  element list
or   to backup  to the  previous <element>  of the  element list.   When the
Foreach backs up into a  Matching Procedure, the procedure is  not recalled,
but resumed at the statement  following the last Succeed executed.   On  the
other hand, when a Foreach  proceeds forward into a Matching  Procedure, the
procedure is called, not resumed.

When a Matching Procedure is the last <element> of the  associative context,
Succeeding will cause the  <statement> to be executed;     the  Foreach then
backs up into the Matching Procedure, and the Matching Procedure  is resumed
at the statement  following the Succeed.  When  a Matching Procedure  is the
first <element> of an associative context, Failing will exit the Foreach.

WARNING: Matching procedures are  implemented as processes and  two calls of
the same matching procedure may  share the same memory unless  the procedure
is declared RECURSIVE.   See Memory Accessible to a Process, page 188.

If  a  Matching Procedure  is  explicitly  SPROUTed as  a  process  then the
Matching Procedure can be made running by a RESUME.  In such a case the item
sent by RESUME  is returned as  the value of  the SUCCEED or  FAIL statement
which suspended the Matching Procedure, just as though SUCCEED or  FAIL were
an item procedure.  (In fact  Succeed and Fail always return an  item value,
but the value  is ANY except  in this special  case.)  Being Resumed  is the
only was in which a Matching Procedure can be reactivated after a FAIL.



When a Matching Procedure is  used exterior to the associative context  of a
Foreach, one may use "BIND"  in the call preceding those actuals  which  one
wishes bound regardless of their current binding.  Preceding the actual with
"?" will have the save effect as "BIND" if the current value of  the itemvar
is  BINDIT,  and  will  have no  effect  otherwise (the  procedure  will not
attempt to find it a binding).

That is all there is to Matching Procedures.  Their power lies in  the using
them cleverly.   The following  program illustrates  techniques one  may use
with matching  procedures by  simulating the  List Membership  and Retrieval
Triple <element>s  with matching procedures.

RECURSIVE MATCHING PROCEDURE INLIST
                (? ITEMVAR X; LIST L);
BEGIN "INLIST"
    COMMENT THIS PROCEDURE SIMULATES THE CONSTRUCT
        X  L FOR ALL CASES EXCEPT THE SIMPLE
        PREDICATE BINDITL;
    IF X  BINDIT THEN
        BEGIN WHILE LENGTH (L) DO IF X = LOP (L)
            THEN BEGIN SUCCEED; DONE END;
            FAIL
        END;
    WHILE LENGTH (L) DO BEGIN X_LOP (L);
                SUCCEED END;
END "INLIST";

MATCHING PROCEDURE TRIPLE (? ITEMVAR A, O, V);
BEGIN "TRIPLE"
    DEFINE BINDING (A)="(A=BINDIT)";
    SET SET1; INTEGER INDX;
    RECURSIVE PROCEDURE SUCCSET (REFERENCE
                             ITEMVAR X; SET S1);
        WHILE LENGTH (S1) DO BEGIN X_LOP (S1);
                SUCCEED END;

    INDX _ 0;
    IF BINDING (A) THEN INDX _ 1;
    IF BINDING (O) THEN INDX _ INDX + 2;
    IF BINDING (V) THEN INDX _ INDX + 4;
    CASE INDX OF
    BEGIN [0] "AOV" IF AOV THEN SUCCEED;
          [1] "?OV" SUCCSET (A, OV);
          [2] "A?V" SUCCSET (O, A`V);
          [3] "??V" BEGIN SET1 _ ANY  V;
                WHILE (LENGTH (SET1)) DO
                    BEGIN A _ LOP (SET1);
                        SUCCSET (O, A`V) END END;
          [4] "AO?" SUCCSET (V, AV);
          [5] "?O?" BEGIN SET1 _ O  ANY;
                WHILE (LENGTH (SET1)) DO



                    BEGIN A _ LOP (SET1);
                        SUCCSET (V, AO) END END;
          [6] "A??" BEGIN SET1 _ A ` ANY;
                WHILE (LENGTH (SET1)) DO
                    BEGIN O _ LOP (SET1);
                        SUCCSET (V, AO) END END;
          [7] "???"
              USERERR(0, 1, "ANYANYANY IS IN BAD TASTE")
    END;
END "TRIPLE";

LEAP EXPRESSIONS Syntax


                                SECTION  14

                              LEAP EXPRESSIONS




14.1  Syntax


<leapexpression>
    ::= <itemexpression>
    ::= <setexpression>
    ::= <listexpression>


<itemexpression>
    ::= <itemprimary>
    ::= [ <itemprimary>  <itemprimary> 
        <itemprimary> ]


<itemprimary>
    ::= NEW
    ::= NEW ( <algebraicexpression> )
    ::= NEW ( <setexpression> )
    ::= NEW ( <listexpression> )
    ::= NEW ( <arrayname> )
    ::= ANY
    ::= BINDIT
    ::= <itemidentifier>
    ::= <itemvarvariable>
    ::= <listexpression> [
        <algebraicexpression> ]
    ::= <itemvarprocedurecall>
    ::= <resumeconstruct>
    ::= <interrogateconstruct>


<itemvarprocedurecall>
    ::= <procedurecall>


<listexpression>
    ::= <listprimary>
    ::= <listexpression> & <listexpression>


<listprimary>
    ::= NIL
    ::= <listvariable>



    ::= {{ <itemexprlist> ~~
    ::= ( <listexpression> )
    ::= <listprimary> [ <substringspec> ]
    ::= <setprimary>


<itemexprlist>
    ::= <itemexpression>
    ::= <itemexprlist> , <itemexpression>


<setexpression>
    ::= <setterm>
    ::= <setexpression>  <setterm>


<setterm>
    ::= <setfactor>
    ::= <setterm>  <setfactor>


<setfactor>
    ::= <setprimary>
    ::= <setfactor> - <setprimary>


<setprimary>
    ::= PHI
    ::= <setvariable>
    ::= {itemexprlist~
    ::= ( <setexpression> )
    ::= <derivedset>


<derivedset>
    ::= <itemexpression> <associativeoperator>
        <itemexpression>


<associativeoperator>
    ::= 
    ::= `
    ::= 


<itemvarvariable>
    ::= <variable>


<setvariable>
    ::= <variable>

 Semantics ITEM EXPRESSIONS


<listvariable>
    ::= <variable>


<leaprelational>
    ::= <itemexpression> IN
        <setexpression>
    ::= <itemexpression> IN
        <listexpression>
    ::= <itemexpression>
        <itemrelationaloperator>
        <itemexpression>
    ::= <setexpression>
        <setrelationaloperator>
        <setexpression>
    ::= <listexpression>
        <listrelationaloperator>
        <listexpression>
    ::= <triple>


<itemrelationaloperator>
    ::= =
    ::= 


<setrelationaloperator>
    ::= =
    ::= 
    ::= <
    ::= >
    ::= 
    ::= 


<listrelationaloperator>
    ::= =
    ::= 




14.2  Semantics

ITEM EXPRESSIONS
Itemvars  and  itemvar  arrays  may be  used  in  item  expressions  just as
algebraic variables and algebraic arrays are used in  algebraic expressions.
Itemvars and itemvar arrays are initialized to the special Sail item ANY.

Items may be retrieved from sets  and lists with the Sail functions  COP and

 NEW


LOP.  COP (<set expression or list expression>) yields the item which is the
first element of the set or  list that the set or list  expression evaluated
to.  LOP also yields the first  item of the set or list,  but   removes that
item from the set or list.   Because LOP changes the contents of the  set or
list that is  its argument, it  can only accept  set or list  variables, not
expressions.  See page 89.

List  element  designators may  be  used as  itemvars  in  expressions.  For
example, if RECORD is a list, and ITMVR an itemvar,

   RECORD[5] _ ITMVR;
   ITMVR _ RECORD[-1];
   RECORD[] _ RECORD[1];

are all legal.  The special token "" means the length of the list when used
in this context.  The contents  of the square brackets may be  any algebraic
expression   as   long   as   it   evaluates   to   an   integer   n   where
1  n  LENGTH (list).

<listexpression> [<algebraicexpression>] returns a particular element of a
list, but  may not appear  on the left of an assignment  expression, because
assignment must be to variables.

NEW
The function  NEW creates an  item at execution  time.  Since space  must be
allocated at loading for various   tables,  one must  indicate approximately
how may NEW items he will create (the compiler counts the declared items for
you).  Therefore,  one should  say "REQUIRE  n NEWITEMS"   where n  is some
integer less than 4090 (the maximum number of items allowed in Sail).  n may
be larger than the actual number  of New items created, but the  excess will
be wasted space.  If 0 < n < 50, you get tables for 50 New items anyway.

NEW  may take an argument.  In  this case, the datum of the created  item is
preloaded with the value passed as argument.  If this argument is algebraic,
set or list, then the datum  will be of the same type.  No  type conversions
are done when passing the algebraic argument.  NEW will also accept an array
name as argument.  In this case, the created item will be of the type array.
In fact, the array cited as  argument will be copied into the  newly created
array.  The new array will have the same bounds and number of  dimensions as
the array  cited as  argument.  This array  will not  disappear even  if the
block that the original  array was declared in  is exited.  It will  only be
deallocated if the item is deleted.

NEW in an  item expression makes that  item expression a  "constructive item
expression".   Constructive item  expressions  are illegal  in  some places,
namely anywhere  that attempts to  gets an item  from an  existing structure
(i.e.,  ERASE,  REMOVE,  and Associative  searches).   It  is  usually clear
whether or not a constructive item expression is illegal.

 ANY BINDIT TYPES AGAIN SET AND LIST EXPRESSIONS


ANY
Some associative searches may need only partial specification. The ANY  item
is  used  to   specify  exactly  which   parts  of  the   specification  are
"don't cares"'s.  Examples:

   FOREACH X SUCH THAT Father  X  ANY DO ...
   IF Father  BIND X  ANY THEN ...

ANY  in an  item expression  makes that  item expression  a  "retrieval item
expression".  This is the opposite of a constructive item expression, and is
illegal anywhere  the statement  is creating new  structure, namely,  a MAKE
statement.   Thus,  ANY  is  legal  everywhere  items  are,  except  a  MAKE
statement.

BINDIT
Like  ANY,  BINDIT  specifies  no  constraints  on  the  associative search.
However, BINDIT has a special  meaning to some searches, namely  the Binding
Boolean  and Matching  Procedures (depending  on how  they're  written).  An
itemvar containing  BINDIT will be  bound by  the search to  an item  of the
association that the search found.  For example:

   X _ BINDIT;
   IF Father  ? X  Bob
       THEN PUT X IN Bobfatherset;

Like  ANY, BINDIT  is illegal  in MAKE  statements.  In  certain associative
searches, namely  the ERASE statement,  the Bracketed Triple  Item retrieval
expression, and the  Retrieval Triple <element>  of a Foreach,  inclusion of
BINDIT will cause the search to always fail, because BINDIT can appear in no
association.

TYPES AGAIN
The compiler can determine the type  of items when the item expression  is a
typed itemvar, a  typed itemvar  procedure, a  declared item with a  type, a
typed itemvar  array, or  a  NEW with  an argument.   When the  compiler can
determine the type of the item expression, then and only then is it legal to
use  the  Datum construct  on  the item  expression  or to  assign  the item
expression to a Checked itemvar.  For example, the following are ILLEGAL:

    DATUM (COP (<set>))
    DATUM (RECORD[]);    COMMENT RECORD is a list;
    CHEC _ NEW;  COMMENT CHEC is a Checked itemvar;

SET AND LIST EXPRESSIONS
Three rather standard operations  are implemented for use with  sets.  These
are union (), intersection (), and subtraction (-).  These  operators have
the  standard  mathematical interpretations.   The  only  possible confusion
pertains to subtractions: if we perform the set operation

   set1 - set2

 DERIVED SETS BOOLEANS


and if  there is  an instance  of an item  x in  set2 but  not in  set1, the
subtraction proceeds and no error message is given.

If one  considers a list  to be  a string of  items, then  concatenation and
taking sublists  suggest themselves as  likely list operations.   The syntax
and semantics for sublisting and list concatenation are identical with those
of strings, with the natural  exception that the results are lists,  and not
strings.   There  is  also  a  difference in  that  if  the  indices  to the
substringer do  not make sense,  an error message  is generated  rather than
setting of the SKIP variable.  Examples:

   LISTVAR _ LISTVAR[2 TO -1];
   LISTVAR _ LISTVAR[9 FOR 2*N];
   LISTVAR _ LISTVAR[1 FOR 2] & LISTVAR[3 TO ];

One may generate sets with

   {item1, item2, item3~

and may generate lists with

   {{item1, item1, item2, item3~~.

Sets are initialized  to the empty set,  PHI.  Lists are initialized  to the
null list, NIL.  Initialization occurs at the beginning of the  execution of
the program.   Sets and  list are  reinitialized on  entering the  blocks of
their declaration only when such blocks are in recursive procedures.

DERIVED SETS
Derived  sets are  really  sets of  answers  to questions  which  search the
associative memory.  The conventions are:

   a  b  -- the set of all x such that a  b  x
   a  b -- the set of all x such that x  a  b
   a ` b  -- the set of all x such that a  x  b

BOOLEANS
Several boolean  primaries are  implemented for  comparing sets,  lists, and
items.  In the following discussion, "ix" means item expression,  "se" means
set expressions, and "le" means list expression.  These are:

  1)  Set and List Membership.  The boolean "ix IN se" evaluates  the set
      or list expression, and returns TRUE if the item value specified by
      the item expression is a member of the set or list.

  2)  Association Existence.  The binding  boolean "ix  ix   ix", where
      the ix  are item  expressions or  itemvars preceded  by ?  or BIND,
      returns TRUE if a binding of the BIND itemvars (and ? itemvars that
      contained BINDIT) can be found such that the association  exists in
      the  associative  store.   See page  165  for  more  information on
      binding booleans.

 PNAMES PROPS


  3)  Relations.

     ix = ix     obvious interpretation
     ix  ix     obvious interpretation
     se1 < se2   true if se1 is a proper
                 subset of se2
     se1  se2   true if se1 is identical to
                 se2 or is a proper subset of se2
     se1 = se2   obvious interpretation
     se1  se2   obvious interpretation
     se1 > se2   equivalent to se2 < se1
     se1  se2   equivalent to se2  se1
     le1 = le2   obvious interpretation
     le1  le2   obvious interpretation

PNAMES
For those desire them, each item may have a string, called its PNAME, linked
with  it.  This  is completely  independent of  the Datum  construct.    New
items and  Bracketed Triple  items are  created with  NULL strings  as their
Pnames.  One may delete an  item's Pname with the DELPNAME  function  which
takes an item expression as its  argument.  One may give a Pnameless  item a
Pname with the  NEWPNAME procedure, which  takes  an item expression  and a
string as its arguments.  CVSI will give you the Pname of an item,  and CVIS
with give you the item with the specified Pname.  No two items may  have the
same Pname.  Pnames do not follow  Algol scope rules.  See page 220  to find
out how to use the above four functions.

If you would like  your declared items to have  Pnames that are the  same as
the identifier used in their declaration, say "REQUIRE PNAMES" or "REQUIRE n
PNAMES" before their declaration at the beginning of the program.  The  n is
an estimate of the number of dynamically created items with pnames  you will
use --  this causes  tables for  n pnames  to be  allocated at  compile time
rather than runtime, thus making your program more efficient.

PROPS
Any item may have a PROPS.  This is an extra 12 bits of  storage (frequently
used for  bits).  PROPS (X)  where  X is  an item  expression is  exactly an
integer variable  in its syntax.   See page 161  for further  information on
props.

BACKTRACKING Introduction Syntax


                                SECTION  15

                                BACKTRACKING




15.1  Introduction

Backup or backtracking is the  ability to "back up" execution to  a previous
point.  Sail facilitates backtracking  by allowing one to  REMEMBER, FORGET,
or RESTORE variables in the  data type CONTEXT.



15.2  Syntax


<contextdeclaration>
    ::= CONTEXT <idlist>
    ::= CONTEXT ARRAY <arraylist>
    ::= CONTEXT ITEM <idlist>
    ::= CONTEXT ITEMVAR <idlist>


<backtrackingstatement>
    ::= <remkeyword> <variablelist>
        <rempreposition> <contextvariable>


<remkeyword>
    ::= REMEMBER
    ::= FORGET
    ::= RESTORE


<rempreposition>
    ::= IN
    ::= FROM


<variablelist>
    ::= <varilist>
    ::= ( <varilist> )
    ::= ALL
    ::= <contextvariable>


<varilist>
    ::= <vari>
    ::= <varilist> , <vari>

 Semantics THE CONTEXT DATA TYPE



<vari>
    ::= <variable>
    ::= <arrayidentifier>


<contextvariable>
    ::= <variable>


<arrayidentifier>
    ::= <identifier>


<contextelement>
    ::= <contextvariable> : <variable>



15.3  Semantics

THE CONTEXT DATA TYPE
A context is  essentially a storage place   of undefined capacity.   When we
REMEMBER a variable in a context, we remember the name of the variable along
with its current value (if an array, values).  If we remember a  value which
we have already remembered in the named context, we destroy the old value we
had  remembered and  replace it  with the  current  value  of  the variable.
Values can be given back to variables with the RESTORE statement.

Context variables are just like  any other variables with respect  to scope.
Also, at execution time, context  variables are destroyed when the  block in
which they were declared is exited in order to reclaim their space.  Context
arrays, items, and itemvars are legal (items and itemvars are part of Leap).
NEW( <context variable> ) is legal (NEW is also part of Leap).

RESTRICTIONS:

   1. Context  procedures  do  not exist.   Use  context  itemvar procedures
      instead.

   2. Context variables may only be passed by reference to procedures (i.e.,
      contexts are not copied).

   3. Contexts  may not  be declared  "GLOBAL" (shared  between jobs  - SUAI
      only).

   4. +,  *, /,  and all  other arithmetic  operators have  no  meaning when
      applied to Context variables.  Therefore, context variable expressions
      always consist only of a context variable.

 REMEMBER RESTORE


The empty  context is  NULLCONTEXT.  Context  variables are  initialized to
NULLCONTEXT at program entry.

REMEMBER
To  save  the  current  values of  variables,  list  them,  with  or without
surrounding parentheses, in the remember statement.  All of an array will be
remembered if subscripts of an array are not used, otherwise, only the value
indicated will be remembered.  If a variable has already been  remembered in
context, its value is replaced by the current value.  If one wants to update
all the variables so far remembered in this context, one may say

       REMEMBER ALL IN <context>.

If you have several contexts active,

       REMEMBER CNTXT1 IN CNTXT2

will note  the variables  Remembered in  CNTXT1, and  automatically Remember
their CURRENT values in CNTXT2.

RESTORE
To restore the values of variables  that were saved in a context,  list them
(with  or  without   surrounding  parentheses)  in  a   restore   statement.
Restoring an array without using subscripts causes as much of the array that
was remembered to be restored magically to the right locations in the array.
You can  remember a whole  array, then restore  all or selected  parts (e.g.
RESTORE A[1, 2] FROM IX;).  If you remembered only A[1, 2], then restoring A
will only update A[1, 2].  RESTORE ALL IN IX will of course restore  all the
variables from IX.  RESTORE CNTXT1 FROM  CNTXT2 will act like a list  of the
variables in CNTXT1 was presented  to the Restore instead of  the identifier
CNTXT1.

Astute Leap  users will have  noted that the  syntax for  variables includes
Datum(typed itemvar)  and similar  things.  If  one executes  REMEMBER DATUM
(typeditemexpression1) IN CNTXT, then RESTORE DATUM (<itemexpression2>)
FROM CNTXT will give  an error message unless  the <typeditemexpression2>
returns the same item as <typeditemexpression1>.

WARNING!!! Restoring variables that have been destroyed by block  exits will
give you garbage.  For example, the following will blow up:

    BEGIN "BLOWS UP"
        CONTEXT J1;
        INTEGER J;
        BEGIN INTEGER ARRAY L[1:J];
                REMEMBER J, L IN J1;
        END;
        RESTORE ALL FROM J1;
    END "BLOWS UP";

 FORGET INCONTEXT CONTEXT ELEMENTS


FORGET
The  forget statement  just deletes  the variable  from the  context without
touching the  current variable's value.   Variables remembered in  a context
should be forgotten before the  block in which the variables   were declared
is exited.  FORGET ALL  FROM X1 and FORGET  CNTXT1 FROM CNTXT2 work  just as
the  similar  Restore  statements work,  only  the  variables  are Forgotten
instead of Restored.

INCONTEXT
The runtime boolean INCONTEXT returns true if the specified variable  is in
the specified context.  For details, see page 95.

CONTEXT ELEMENTS
Context elements provide a convenient method of accessing a variable that is
being remembered in a context.  Examples of context elements:

        CNTXTVARI : SOMEVARI
        DATUM (CNTXTITEM) : SOMEVARI
        CNTXTAR[2,3] : ARRY[4]
        DATUM (CNTXTVARI : ITMVR)
        CNTXTVARI : DATUM(ITMVR)

A context element is syntactically and semantically equivalent to a variable
of the  same type  as the variable  following the  colon.  For  the complete
syntax of variables, see page 226.   Assignments to context  elements change
the Remembered value (i.e., X_5; REMEMBER  X IN C; C:X_6; RESTORE X  FROM C;
will leave X with the value 6).

As with the Restore statement, one may not use Context Elements of variables
destroyed by block exits.

RESTRICTIONS:  (1)  One  may not  Remember  Context  Elements.   (2) Passing
Context  Elements   by  reference  to  procedures  that  change  contexts is
dangerous.  Namely, if the procedure Forgets the element that was  passed to
it by  reference, then the  user is  left with a  dangling pointer.   A more
subtle variation of this disaster occurs when the Context element  passed is
an array  element.  If  the procedure  Remembers the  array that  that array
element was a part of, the formal that had the array element Context Element
passed to it is left with a dangling pointer.

PROCESSES Introduction Syntax Semantics STATUS OF A PROCESS


                                SECTION  16

                                 PROCESSES




16.1  Introduction

A PROCESS  is a procedure  call that  may be run  independently of  the main
program.  Several  processes may  "run" concurrently.   When dealing  with a
multi-process  system,  it  is  not quite  correct  to  speak  of  "the main
program".  The main program is actually a process itself, the main process.

This  section  will deal  with  the creation,  control,  and  destruction of
processes,  as well  as  define the  memory  accessible to  a  process.  The
following section will describe communication between processes.



16.2  Syntax


<processstatement>
    ::= <sproutstatement>


<sproutstatement>
    ::= SPROUT ( <itemexpression> ,
        <procedurecall> ,
        <algebraicexpression> )
    ::= SPROUT ( <itemexpression> ,
        <procedurecall> )
    ::= SPROUT ( <itemexpression> ,
        <applyconstruct> )


<sproutdefaultdeclaration>
    ::= SPROUTDEFAULTS <integerconstant>



16.3  Semantics

STATUS OF A PROCESS
A process  can be in  one of four  states: terminated, suspended,  ready, or
running.  A terminated process can never be run again.  A  suspended process
can be run again, but it must be explicitly told to run by some process that
is  running.  Since  Sail  is currently  implemented on  a  single processor

 SPROUTING A PROCESS


machine, one cannot really execute two procedures simultaneously.  Sail uses
a  scheduler to  swap processes  from ready  to running  status.   A running
process is  actually executing, while  a ready process  is one which  may be
picked  by  the scheduler  to  become  the running  process.   The  user may
retrieve the status  of a process with  the execution time  routine PSTATUS,
page 195.  SPROUTING A PROCESS
One creates a process  with the SPROUT statement:

    SPROUT (<item>, <procedure call>, <options>)
    SPROUT (<item>, <procedure call>)

<item> is a  construction item expression (i.e.  do not use ANY  or BINDIT).
Such an item will  be called a process item.   The item may be of  any type;
however, its current datum will be writen over by the SPROUT  statement, and
its  type  will  be  changed  to  "process  item"  (see  TYPEIT,  page 218).
RESTRICTION: A user must never modify the datum of a process item.

<procedure call> is any procedure call on a regular or  recursive procedure,
but  not a  simple procedure.   This procedure  will be  called  the process
procedure for the new process.

<options> is an integer that may  be used to specify special options  to the
SPROUTer.  If <options> is left  out, 0 will be used.  The  different fields
of the word are as follows:

BITS    NAME         DESCRIPTION

14-17   QUANTUM (X)   Q _ IF X=0 THEN 4 ELSE 2^X; The process will  be given
               a quantum of  Q clock ticks, indicating  that if the  user is
               using CLKMOD to  handle clock interrupts, the  process should
               be  run  for  at  most  Q  clock  ticks,  before  calling the
               scheduler.  (see about CLKMOD, page 214 for details on making
               processes "time share").

18-21   STRINGSTACK (X) S _  IF X=0 THEN 16  ELSE X*32; The process  will be
               given S words of string stack.

22-27   PSTACK (X)  P_IF X=0 THEN 32 ELSE X*32; The process will be  given P
               words of arithmetic stack.

28-31   PRIORITY (X)  P _ IF X=0 THEN 7 ELSE X; The process will be  given a
               priority of P.  0  is the highest priority, and  reserved for
               the  Sail system.   15  is the  lowest  priority.  Priorities
               determine which ready process the scheduler will next pick to
               make running.

32      SUSPHIMIf set, suspend the newly sprouted process.

33                   Not used at present.

 SPROUTDEFAULTS MEMORY ACCESSIBLE TO A PROCESS


34      SUSPME If set,  suspend the process  in which this  sprout statement
               occurs.

35      RUNME  If  set, continue  to run  the process  in which  this sprout
               statement occurs.

The  names  are defined  in  the file  SUAISYS:PROCES.DEF,  which  one may
require  as  a  source  file.  Options  words  may  be  assembled  by simple
addition, e.g.  RUNME + PRIORITY (3) + PSTACK (2).

DEFAULT STATUS: If none of bits 32,  34, or 35 are set, then the  process in
which the sprout statement occurs will revert to ready status, and the newly
sprouted process will become the running process.

The default values of QUANTUM, STRINGSTACK, PSTACK, and PRIORITY  are stored
in the  system variables  DEFQNT, DEFSSS,  DEFPSS, and  DEFPRI respectively.
These values may be  changed.  The variables are declared  EXTERNAL INTEGERs
in SUAISYS:PROCES.DEF.

SPROUTDEFAULTS
If  one of the "allocation" fields of  the options word passed to the SPROUT
routine  -- i.e.,  QUANTUM, STRINGSTACK,  PSTACK, or   PRIORITY --  is zero,
then   SPROUT  will  look  at   the corresponding   field of   the specified
<integerconstant> of the SPROUTDEFAULTS for the procedure  being sprouted.
If the field is  non-zero   then  that value  will be  used;  otherwise  the
current "system" default will be used.

NOTE:  SPROUTDEFAULTS  only  applies  to  "allocations", i.e.,  the process
status control bits (e.g. SUSPME) are not affected.  Example:

        RECURSIVE PROCEDURE FOO;
         BEGIN
         SPROUTDEFAULTS STRINGSTACK (10);
         INTEGER XXX;
         :
         :
         END;
        :
        SPROUT (P1, FOO, STRINGSTACK (3));
        SPROUT (P2, FOO);
        COMMENT P1 will have a string stack of 3*32 words.
         P2 will have a string stack of 10*32 words;

MEMORY ACCESSIBLE TO A PROCESS
A process has access to the  same global variables as would a  "normal" call
of the process procedure at the point of the SPROUT statement.  For example,
suppose you  Sprouted a process  in the first  instantiation of  a recursive
procedure and  immediately suspended it.   Then in another  instantiation of
the procedure, you resumed the process.  Since each  recursive instantiation
of a procedure creates and initializes new instances of its local variables,



the process uses the instances of the recursive procedure's locals that were
current at the time of the SPROUT, namely those of the first instantiation.

Sail  will give  you an  error message  whenever the  global variables  of a
process are deallocated but  the process still exists.  Usually,  this means
that when the block in  which the process procedure was declared  is exited,
the corresponding process must be terminated (one can insure this by using a
small Cleanup procedure that will TERMINATE the fated process or JOIN  it to
the current  one --  see about Cleanup,  page 23,  Terminate, page  192, and
Join, page  194).  When  the process  procedure has  been declared  inside a
recursive procedure, things become a bit more complex.  As  mentioned above,
the process  takes its  globals from  the context  of the  Sprout statement.
Therefore, it is only in  the instantiation of the recursive  procedure that
executed the Sprout that trouble can occur.  For example,

    RECURSIVE PROCEDURE TENLEVEL (INTEGER I);
    BEGIN "TROUBLE"
        PROCEDURE FOO;
        ;   COMMENT does nothing;

        IF I=5 THEN SPROUT (NEW, FOO, SUSPHIM);

          COMMENT sprouts FOO on the 5th
          instantiation of TENLEVEL, then
          immediately suspends it;

        IF I<10 THEN  TENLEVEL (I+1);
        RETURN;

          COMMENT assuming TENLEVEL is called
          with I=0, it will do 10 instantiations,
          then come back up;

    END "TROUBLE";

TENLEVEL will nest 10 deep, then start returning.  This means "TROUBLE" will
be exited five  times will no ill  effects.  However, when Sail  attempts to
exit "TROUBLE" a sixth time, it  will be exiting a block in which  a process
was  sprouted   and  declared.    It  will   generate  the   error  message,
"Unterminated process dependent on block exited".

The  construct  DEPENDENTS (<blockname>), where  <blockname>  is  a string
constant, produces a set of  process items.  The process items are  those of
all the processes which depend on the current instance of the named block --
i.e. all  processes whose process  procedures obtain their  global variables
from that block (via the position of the process procedure's declaration, or
occasionaly via the location of the Sprout in a nest of  recursive procedure
instantiations).   This  construct  may  be  used  together  with  a CLEANUP
procedure (see page 23)  to avoid having a block exit before  all procedures
dependent on it have been terminated.

 SPROUT APPLY SPROUTING MATCHING PROCEDURES SCHEDULING


If  one  Sprouts  the  same non-recursive  procedure  more  than  once (with
different process items,  of course), the  local variables of  the procedure
are not copied.  In other words, "X_5" in process A will store 5 in the same
location that "X_10" in process B would store 10.  If such sharing of memory
is  undesirable,  declare  the process  procedure  RECURSIVE,  and  then new
instances of the local variables of the procedure will be created  with each
Sprout involving  that procedure.   Then "X" in  process A  will refer  to a
different memory location than "X" in process B.

SPROUT APPLY
The <procedure call>  in a SPROUT statement  may be an APPLY  construct.  In
this case  SPROUT will  do  the "right" thing   about setting up  the static
link for  the APPLY.  That is, "up-level" references by the process  will be
made to the same variable instances that would be used if the APPLY  did not
occur in a SPROUT statement.  (See page 206.)

However, there is a glitch.  The sprout mechanism is not yet smart enough to
find out the block  of the declaration of  the procedure used to  define the
procedure item.  It would  be nice if it did,  since then it could  warn the
user when that  block was exited  and yet the  process was still  alive, and
thus potentially able to refer to deallocated arrays, etc.  What  the sprout
does instead is assume the procedure was declared in the outer  block.  This
may be fixed eventually, but in the meantime some extra care should be taken
when  using apply  in  sprouts to  avoid  exiting a  block  with dependents.
Similarly,  be warned  that the  "DEPENDENTS (<blockid>)" construct  may not
give the "right" result for sprout applies.

SPROUTING MATCHING PROCEDURES
When a matching procedure is the object of a Sprout statement, the  FAIL and
SUCCEED statements are interpreted  differently than they would be  were the
matching procedure called in a  Foreach or as a regular procedure.   FAIL is
equivalent to  RESUME (CALLER (MYPROC), CVI (0)).  SUCCEED is  equivalent to
RESUME (CALLER (MYPROC), CVI (-1)).

SCHEDULING
One may  change the status  of a process  between terminated,  suspended and
ready/running  with  the  TERMINATE, SUSPEND,  RESUME,  and  JOIN constructs
discussed above, and the  CAUSE and INTERROGATE constructs discussed  in the
next chapter.  This section will  describe how the the status  of  processes
may change between ready and running.

Whenever the currently running process performs some action that  causes its
status to  change (to  ready, terminated,  or suspended)  without specifying
which process is to be run next, the Sail process scheduler will be invoked.
It  chooses a  process from  the pool  of ready  processes.  The  process it
chooses will be made the next running process.  The scheduling  algorithm is
essentially  round  robin  within  priority  class.   In  other  words,  the
scheduler  finds the  highest priority  class that  has at  least  one ready
process in it.  Each class has  a list of processes associated with  it, and
the scheduler choses the first ready process on the list.  This process then

 POLLING POINTS 


becomes  the running  process and  is put  on the  end of  the list.   If no
processes have ready  status, the scheduler looks  to see if the  program is
enabled for any interrupts (see Interrupts, page 207).    If the  program is
enabled for some kind of  interrupt that might still happen  (not arithmetic
overflow, for instance),  then the scheduler  puts the program  in interrupt
wait.  After the interrupt is dismissed, the scheduler tries again to find a
ready process.   If no  interrupts that  may still  happen are  enabled, and
there are no ready processes, the error message "No one to run" is issued.

The rescheduling operation may be explicitly invoked by calling  the runtime
routine URSCHD, which has no parameters.

POLLING POINTS
Polling  points are  located at  "clean" or  "safe" points  in  the program;
points where a process may change from running to ready and back with no bad
effects.  Polling points cause conditional rescheduling.  A polling point is
an efficient version of the  statement:

    IF INTRPT  NOPOLL THEN
            BEGIN INTRPT_0; URSCHD END;

INTRPT is an  external integer that is  used to request rescheduling  at the
next polling point.   It is commonly set  by the deferred  interrupt routine
DFRINT (for all about deferred  interrupts, see page 215)  and by  the clock
interrupt routine  CLKMOD (for how  to make processes  time share,  see page
214).  The user may use INTRPT for his own purposes (carefully, so as not to
interfere  with DFRINT  or CLKMOD)  by including  the  declaration "EXTERNAL
INTEGER INTRPT", then assigning INTRPT a non-zero value any time  he desires
the next polling  point to cause  rescheduling.  NOPOLL is  another external
integer that is provided to give the user a means of  dynamically inhibiting
polling points.  For example, suppose one is time sharing using  CLKMOD.  In
one of the processes, a point is reached where it becomes important that the
processes not be swapped out until a certain tight loop is finished  up.  By
assigning NOPOLL (which was declared an EXTERNAL INTEGER) a  non-zero value,
the  polling points  in the  loop are  efficiently ignored.   Zeroing NOPOLL
restores normal time sharing.

A  single  polling point  can  be  inserted with  the  statement  POLL.  The
construct

       REQUIRE n POLLINGINTERVAL

where n is a positive integer, causes polling points to be inserted  at safe
points in the code, namely: at the start of every statement provided that at
least n instructions have been  emitted since the last polling  point, after
every label, and at the end of every loop.  If n  0 then no further polling
points will  be put out  until another Require  n (n>0)  PollingInterval is
seen.

Process Runtimes TERMINATE SUSPEND RESUME


16.4  Process Runtimes



*******************************  TERMINATE  ******************************;

TERMINATE (PROCITM)

The process  for which PROCITM  is the process  item is terminated.   It is
legal  to terminate  a terminated  process.  A  terminated process  is truly
dead.  The item may be used  over for anything you want, but after  you have
used it for something else, you  may not do a terminate on  it.  Termination
of a process causes all blocks of the process to be exited.



********************************  SUSPEND  *******************************;

ITM _ SUSPEND (PROCITM)

The process  for which PROCITM  is the process  item is suspended.   If the
process being suspended is not  the currently running process then  the item
returned is ANY.  In cases such as

    X _ SUSPEND (MYPROC);

where the process suspends itself, it might happen that this process is made
running  by a  RESUME from  another  process.  If  so, then  X  receives the
SENDITM  that was an argument to the RESUME.

One may suspend a  suspended process.  Suspending a terminated  process will
cause an  error message.  If  the process being  suspended is  the currently
running process (i.e. the process suspends itself), then the  scheduler will
be called to find another process  to run.  A process may also  be suspended
as the result of RESUME or JOIN.



*********************************  RESUME  *******************************;

RETITM _ RESUME (PROCITM,
                 SENDITM, OPTIONS(0))

RESUME provides a  means for one process  to restore a suspended  process to
ready/running status  while at the  same time communicating  an item  to the
awakened process.  It may also specify   what its own status should  be.  It
may be  used anywhere  that an itemvar  procedure is  syntactically correct.
When  a  process  which  has  suspended  itself  by  means  of  a  RESUME is
subsequently  awakened  by another  resume,  the SENDITM  of  the awakening
RESUME is used as the RETITM of the RESUME that caused the suspension.  For
example, suppose that process A has suspended itself:

 CALLER



        STARTINFO _ RESUME (Z, NEEDTOOL);

If later a process B executes the statement

        INFOFLAG _ RESUME (A, HAMMER);

then  B will  suspend itself  and A  will become  the running  process.  A's
process information will  be updated to remember  that it was awakened  by B
(so than  the runtime routine  CALLER can work).   Finally, A's  RESUME will
return the value HAMMER, which will be assigned to STARTINFO.  If A had been
suspended by SUSPEND or JOIN then the SENDITM of B's RESUME is ignored.

A process that has been suspended  in any manner will run from the  point of
suspension onward when it is resumed.

OPTIONS is an integer used to change the effect of the RESUME on the current
process (MYPROC) and the newly resumed process.

BITS    NAME         DESCRIPTION

33-32   READYMEIf  33-32  is  1,  then  the  current  process  will  not be
               suspended, but be made ready.

        KILLME If 33-32 is 2, then the current process will be terminated.

        IRUN   If  33-32  is  3,  then  the  current  process  will  not be
               suspended, but be  made running.  The newly  resumed process
               will be made ready.

34                   This should always be zero.

35      NOTNOW If  set,  this bit  makes  the newly  resumed  process ready
               instead of running. If 33-32 are not 3, then this bit causes
               a rescheduling.

DEFAULT: If none of bits 35 to 32 are set, then the current process  will be
suspended  and the  newly resumed  process will  be made  running.   At SUAI
include a REQUIRE  "SYS:PROCES.DEF" SOURCEFILE in  your program to  get the
above bit names defined.   Options may then be specified by simple addition,
e.g. KILLME + NOTNOW.



*********************************  CALLER  *******************************;

PROCITEM _ CALLER (PROCITEM2)

CALLER returns the  process item of the  process that most  recently resumed
the process referred to PROCITEM2.  PROCITEM2 must be the process item of an

 DDFINT JOIN MYPROC PRISET


unterminated  process,  otherwise  an  error  message  will  be  issued.  If
PROCITEM2's process  has never  been called,  then the  process item  of the
process that sprouted PROCITEM2 is returned.



*********************************  DDFINT  *******************************;

DDFINT

A  polling point  is SKIPE  INTRPT; PUSHJ  P, DDFINT.   DDFINT  suspends the
current process  (but leaves  it ready  to run),  then calls  the scheduler;
DDFINT is like SUSPEND (MYPROC, IRUN+NOTNOW).



**********************************  JOIN  ********************************;

JOIN (SETOFPROCESSITEMS)

The current  process (the one  with the JOIN  statement in it)  is suspended
until all  of the  processes in the  set are  terminated.  WARNING:  Be very
careful; you can get into infinite wait situations.

   1. Do not join  to the current process;  since the current  process is
      now suspended, it will never terminate of its own accord.

   2. Do not suspend any of  the joined processes unless you  are assured
      they will be resumed.

   3. Do not do  an interrogate-wait in any  of the processes  unless you
      are sure  that the  event it is  waiting for  will be  caused (page
      196).



******************************  MYPROC  *******************************;

PROCITEM _ MYPROC

MYPROC returns the process item of  the process that it is executed  in.  If
it is  executed not inside  a process,  then MAINPI (the  item for  the main
process) is returned.



*********************************  PRISET  *******************************;

PRISET (PROCITM, PRIORITY)

 PSTATUS URSCHD


PRISET  sets the  priority  of the  process  specified by  PROCITM  (an item
expression  that  must evaluate  to  the process  item  of  a non-terminated
process)  to  the priority  specified  by the  integer  expression PRIORITY.
Meaningful priorities are  the integer between  1, the highest  priority, to
15,  the  lowest  priority.   Whenever a  rescheduling  is  called  for, the
scheduler  finds the  highest priority  class that  has at  least  one ready
process in it, and makes the first process on that list the running process.
See about the scheduler, page 190.



********************************  PSTATUS  *******************************;

STATUS _ PSTATUS (PROCITM)

PSTATUS returns an integer indicating the status of the process specified by
the item expression PROCITM.

        -1      running
         0      suspended
         1      ready
         2      terminated



*********************************  URSCHD  *******************************;

URSCHD

URSCHD  is  essentially the  Sail  Scheduler.  When  one  calls  URSCHD, the
scheduler  finds the  highest priority  class that  has at  least  one Ready
process in it.  Each class has  a list of processes associated with  it, and
the scheduler choses the first ready process on the list.  This process then
becomes  the running  process and  is put  on the  end of  the list.   If no
processes have ready  status, the scheduler looks  to see if the  program is
enabled for  any interrupts.   If the program  is enabled  for some  kind of
interrupt that  may still  happen (not  arithmetic overflow,  for instance),
then  the  scheduler  puts  the  program  into  interrupt  wait.   After the
interrupt is dismissed, the scheduler  tries again to find a  ready process.
If no interrupts that may still  happen are enabled, and there are  no ready
processes, the error message "No one to run" is issued.

EVENTS Syntax Introduction


                                SECTION  17

                                   EVENTS




17.1  Syntax


<eventstatement>
    ::= <causestatement>
    ::= <interruptstatement>


<causestatement>
    ::= CAUSE ( <itemexpression> ,
        <itemexpression> ,
        <algebraicexpression> )
    ::= CAUSE  ( <itemexpression> ,
        <itemexpression> )


<interrogateconstruct>
    ::= INTERROGATE ( <itemexpression> ,
        <algebraicexpression> )
    ::= INTERROGATE ( <itemexpression> )
    ::= INTERROGATE ( <listexpression> ,
        <algebraicexpression> )
    ::= INTERROGATE ( <listexpression> )



17.2  Introduction

The Sail event mechanism is really a general message processing system which
provides a  means by which  an occurrence in  one process can  influence the
flow  of control  in  other processes.   The  mechanism allows  the  user to
classify  the  messages, or  "event  notices", into  distinct  types ("event
types") and specify how each type is to be handled.

Any leap  item may be  used as an  event notice.  An  event type is  an item
which has been given a special runtime data type and datum by means   of the
runtime routine:

    MKEVTT (et)

where et  is any  item expression (except  ANY or  BINDIT).  With  each such
event type Sail associates:

 Sail-defined Cause and Interrogate THE CAUSE STATEMENT


   1. a "notice queue" of items  which have been "caused" for  this event
      type.

   2. a "wait queue" of processes which are waiting for an event  of this
      type.

   3. procedures for manipulating the queues.

The  principle  actions  associated  with the  event  system  are  the CAUSE
statement and the INTERROGATE construct.  Ordinarily these  statements cause
standard  Sail  runtime  routines  to be  invoked.   However,  the  user may
substitute his own  procedures for any  event  type (see User  Defined Cause
and Interrogate procedures, page 199).  The Cause and Interrogate statements
are here described in terms of the Sail system supplied procedures.



17.3  Sail-defined Cause and Interrogate

THE CAUSE STATEMENT

    CAUSE (<event type>, <event notice>, <options>)
    CAUSE (<event type>, <event notice> )

<event type>  is an item  expression, which must  yield an event  type item.
<event  notice>  is  an  item expression,  and  can  yield  any  legal item.
<options> is an integer expression.  If <options> is left out, 0 is used.

The Cause statement  causes the wait queue  of <event type> to  be examined.
If it  is non-empty, then  the system  will give the  <event notice>  to the
first process waiting on the  queue (see about the WAIT bit  in Interrogate,
below).  Otherwise, <event notice> will  be placed at the end of  the notice
queue for <event type>.

The effect of Cause may be modified by the appropriate bits being set in the
options word:

BITS    NAME         DESCRIPTION

35      DONTSAVE     Never put  the <event  item> on  the notice  queue. If
               there is no process on the wait queue, this makes  the cause
               statement a no-op.

34      TELLALLSet the status  of all processes  waiting for this  event to
               READY.

33      RESCHEDULE Reschedule as soon as possible (i.e.,  immediately after
               the cause procedure has completed executed).

DEFAULT:  If bits  35 to  33  are 0,  then the  either a  single  process is

 THE INTERROGATE CONSTRUCT - SIMPLE FORM THE INTERROGATE CONSTRUCT - SET FORM


awakened from the wait  queue, or the event  is placed on the  notice queue.
The  process  doing   the  Cause  continues   to  run.   At   SUAI,  REQUIRE
"SYS:PROCES.DEF" SOURCEFILE  to get the  above bit names  defined.  Options
can then be constructed with  simple addition, e.g. DONTSAVE +  TELLALL. THE
INTERROGATE CONSTRUCT - SIMPLE FORM

    <itemvar> _ INTERROGATE (<event type>, <options>)
    <itemvar> _ INTERROGATE (<event type>)

<event type>  is an item  expression, which must  yield an event  type item.
<options> is an integer expression.  If <options> is left out, 0 is used.

The notice queue of  <event type> is examined.  If it is non-empty, then the
first  element is  removed and  returned as  the value  of  the Interrogate.
Otherwise, the special item BINDIT is returned.

<options> modifies the effect of the interrogate statement as follows:

BITS    NAME         DESCRIPTION

35      RETAIN Leave the event notice on the notice queue, but still return
               the notice as the value of the interrogate.  If  the process
               goes into a wait state as a result of this  interrogate, and
               is subsequently awakened by  a Cause, then the  DONTSAVE bit
               in the Cause statement  will override the RETAIN bit  in the
               Interrogate if both are on.

34      WAIT   If  the  notice queue  is  empty, then  suspend  the process
               executing the  interrogate and put  its process item  on the
               wait queue.

33      RESCHEDULE Reschedule as soon as possible (i.e.,  immediately after
               execution of the interrogate procedure).

32      SAYWHICH   Creates the association  EVENTTYPE  <event  notice> 
               <event type>  where <event  type> is the  type of  the event
               returned.   Useful  with  the set  form  of  the Interrogate
               construct, below.

DEFAULT: If bits 35 to 32 are 0, then the interrogate removes an  event from
the event queue,  and returns it.   If the event  queue is empty,  BINDIT is
returned and no waiting is done; the process continues to run.  At SUAI, use
a REQUIRE "SYS:PROCES.DEF" SOURCEFILE to get the names defined;  use simple
addition to form options, e.g.  RETAIN + WAIT.  THE INTERROGATE  CONSTRUCT -
SET FORM

    <itemvar> _ INTERROGATE (<event type set>)
    <itemvar> _ INTERROGATE (<event type set>, <options>)

<event type  set> is a  set of  event type items.   <options> is  an integer
expression.  If it is left out, 0 will be used.

 User-defined Cause and Interrogate EVENT TYPE DATA STRUCTURE


The  set form  of interrogate  allows the  user to  examine a  whole  set of
possible  event types.   This form  of interrogate  will first  look  at the
notice queues, in turn, of each  event type in <event type set>.  If  one of
these notice queues is non-empty,  then the first notice in that  queue will
be remved and that notice will be returned as the value of  the Interrogate.
If all  the notice queues  are empty,  and WAITing is  not specified  in the
options word, then BINDIT will be  returned.  When the WAIT bit is  set, the
process doing the interrogate gets put at the end of the wait queues of each
event type in <event type  set>.  Then, when a notice is  finally available,
the process  is removed  from all of  the wait  queues before  returning the
notice.  Note  that the  option SAYWHICH provides  a means  for determining
which event type produced the returned notice.



17.4  User-defined Cause and Interrogate

By executing the appropriate runtime routine, the user can specify that some
non-standard action  is to  be associated  with CAUSE  or INTERROGATE  for a
particular event type.  Such user specified cause or  interrogate procedures
may  then manipulate  the  event data  structure directly  or  by themselves
invoking the primitives used  by the Sail Cause and  Interrogate constructs.
User defined Cause and Interrogate  are not for novice programmers  (this is
an understatement).

EVENT TYPE DATA STRUCTURE
The datum of an event type item points to a six word block of  memory.  This
block contains the following information:

 WORD   NAME  TYPE     DESCRIPTION

  0     NOTCQ   LIST   The list of all notices pending for this event type.

  1     WAITQ   LIST   The list  of all processes  currently waiting  for a
                 notice of this type.

  2     ---     ---    Procedure  specifier  for the  user  specified cause
                 procedure (zero if system procedure is to be used).

  3     ---     ---    Procedure   specifier   for   the   user   specified
                 interrogate procedure (zero  if system procedure is  to be
                 used).

  4     USER1   INTEGER Reserved for user.

  5     USER2   INTEGER Reserved for user.

The  appropriate  macro  definitions  for  these  names  (e.g.  WAITQ (et) =
"MEMORY[   DATUM (et)+1,    LIST ]"   )   are    included   in    the   file
SUAISYS:PROCES.DEF.

 USER CAUSE PROCEDURES


USER CAUSE PROCEDURES
A procedure to  be used as  a Cause procedure  must have three  formal value
parameters corresponding to the event type, event notice, and options of the
Cause.  Such a procedure  is associated with an  event type by means  of the
runtime SETCP:

        SETCP (<event type>, <procedure specifier>);

where <event type> must yield  an event type item and  <procedure specifier>
is either a procedure name or DATUM (<procedure item>).  For example:

    PROCEDURE CX (ITEMVAR ET, EN; INTEGER OPT);
        BEGIN
        PRINT ("Causing ", EN,
        " as an event of type ", ET);
        CAUSE1 (ET, EN, OPT);
        END;
    ...

    SETCP (FOO, CX);

Now,
        CAUSE (FOO, BAZ);

would cause  CX (FOO,  BAZ) to be  called.  This  procedure would  print out
"Causing BAZ  as an event  of type  FOO" and then  call CAUSE1.  The runtime
CAUSE1 (ITEMVAR etype, enot; INTEGER  opt) is the Sail runtime  routine that
does  all  the actual  work  of causing  a  particular notice,  enot,  as an
instance of  event type etype.   It is essentially  this procedure  which is
replaced by a user specified cause procedure.

CAUSE1 uses  an important subroutine  which is also  available to  the user.
The  integer runtime ANSWER (ITEMVAR evtype, evnot, processitem)  is used
to wake up a process that has suspended itself with an interrogate.   If the
process named by processitem is suspended, it  will be set to  ready status
and be removed from any wait queues it may be on.  ANSWER will return as its
value  the options  bits from  the interrogate  that caused  the  process to
suspend  itself.   If  the  named process  was  not  suspended,  then ANSWER
returns an integer  word with bit  18 (the '400000 bit  in the right  half =
NOJOY in  SUAISYS:PROCES.DEF) set to  1.  The evtype  and evnot  must be
included in case  the SAYWHICH bit was  on in the interrogate  which caused
the suspension.   ANSWER has no effect on the notice queue of evtype.

Frequently one may wish to  use a cause procedure to re-direct  some notices
to other event types.  For instance:

 USER INTERROGATE PROCEDURES



    PROCEDURE CXX (ITEMVAR ET, EN; INTEGER OPT);
        BEGIN ITEMVAR OTH; LABEL C;
        IF redirecttest(ET, EN) THEN
            FOREACH OTH | OTHERCAUSEETOTH DO
                 C:     CAUSE1 (ET, EN, OPT)
        ELSE  CAUSE1 (ET, EN, OPT);
        END;

In order to avoid some interesting race conditions, the  implementation will
not execute the causes  at C immediately.  Rather,  it will save ET,  EN and
OPT,  then, when  the procedure  CXX is  finally exited,  any  such deferred
causes will  be executed in  the order in  which they were  requested.  USER
INTERROGATE PROCEDURES
A user specified interrogate procedure must have two value formal parameters
corresponding to the two arguments to INTERROGATE and should return  an item
as the value.  The statement

        SETIP (<event type>, <procedure specifier>);

where  <event type>  is an  event type  item, and  <procedure  specifier> is
either a procedure name or DATUM (<procedure item>), will make the specified
procedure  become  the  new interrogate  procedure  for  <event  type>.  For
instance:

    ITEMVAR PROCEDURE IX (ITEMVAR ET; INTEGER OPT);
        BEGIN ITEMVAR NOTI;
        NOTI _ ASKNTC (ET, OPT);
        PRINT ("Notice ", NOTI, " returned
           from interrogation of ", ET);
        RETURN (NOTI);
        END;
    ...

    SETIP (FOO, IX);


Now,
        ... _ INTERROGATE (FOO);

would  cause NOTI  to be  set to  the value  of ASKNTC (FOO,  0).   Then the
message "Notice BAZ returned from  interrogate of FOO" would be  printed and
IX would return NOTI as its value.

The runtime ASKNTC (ITEMVAR etype;  INTEGER opt) is the Sail  system routine
for handling the interrogation of  a single event type.   Essentially  it is
the procedure being replaced by the user interrogate procedure.

In the case of  multiple interrogations, Sail sets  a special bit (bit  19 =
'200000 in the right half  = MULTIN in SUAISYS:PROCES.DEF) in  the options



word before doing any of the interrogates specified by the event  type items
in the event type  set.  The effect of this  bit, which will also be  set in
the options word passed to a user interrogate procedure, is to  cause ASKNTC
always to return BINDIT instead of ever waiting for an event  notice.  Then,
if  ASKNTC  returns  BINDIT  for  all  event  types,  Sail  will  cause  the
interrogating process to Wait until its request is satisfied.  If  multin is
not set, then ASKNTC will do the WAIT if it is told to.

PROCEDURE VARIABLES Syntax Semantics ASSIGN


                                SECTION  18

                            PROCEDURE VARIABLES




18.1  Syntax


<assignstatement>
    ::= ASSIGN ( <itemexpr> ,
        <procedurename> )
    ::= ASSIGN ( <itemexpr> ,                                     DATUM (
        <itemexpr> ) )


<refitemconstruct>
    ::= REFITEM ( <expression> )
    ::= REFITEM ( VALUE <expression> )
    ::= REFITEM ( BIND <itemvar> )
    ::= REFITEM ( ? <itemvar> )


<applyconstruct>
    ::= APPLY ( <procedurename> )
    ::= APPLY ( <procedurename> ,
        <arglistspecifier> )
    ::= APPLY ( DATUM ( <item> ) )
    ::= APPLY ( DATUM ( <item> ) ,
        <arglistspecifier> )


<arglistspecifier>
    ::= <listexpression>
    ::= ARGLIST ( <exprlist> )



18.2  Semantics

ASSIGN
One  may give  an  item a  procedure  "datum" using  the  ASSIGN  statement.
ASSIGN accepts as  its first  argument an  item expression (do  not  use ANY
or  BINDIT).   To  this is  bound  the  procedure  identified by its name or
to the "datum"  of another  procedure  item.      The  procedure may  be any
type.   However,  the  value  it returns  will  only  be  accessible  if the
procedure is an itemvar or item procedure.  Apply assumes that  whatever the
procedure left in AC 1,  (the register used by all non-string  procedures to
return a value) on exiting  is an item number.     Warning: a  procedure  is

 REFITEM ARGLIST


no  ordinary datum.   Using DATUM  on a  procedure item except in the  above
context will not  work.  Use APPLY  instead.

REFITEM
Reference items are  created at run time  by the REFITEM construct  and are
used principally in argument lists for the APPLY construct.  The datum  of a
reference item  contains a  pointer to  a data  object, together  with  type
information about that object.  To create a reference item one executes

        itm _ REFITEM (<expression>)

A NEW item  is created.  If  the expression is (a)  a simple variable  or an
array element, then the address will  be saved in the item's datum.   If the
expression  is (b)  a constant  or "calculated"  expression, then  Sail will
dynamically allocate a cell into  which the value of the expression  will be
saved, and the address of that cell will be saved in the datum of  the item.
The item is then noted as having the datum type "reference" and  returned as
the  value  of  the  REFITEM construct.     One  can  slightly  modify this
procedure by using one of the following variations.

        itm _ REFITEM (VALUE <expression>)

In  this  case,   a  temp  cell  will   always  be  allocated.    Thus  X_3;
XI_REFITEM (VALUE X); X_4; would cause the  datum of XI to point at  a cell
containing 3.

        itm _ REFITEM (? itmvr)
        itm _ REFITEM (BIND itmvr)

where itmvr must be an itemvar or an element of an itemvar array, will cause
the reference  item's datum  to contain  information that  Apply can  use to
obtain the effect of using "? itmvr" or "BIND itmvr" as an  actual parameter
in a procedure call.

ARGLIST
The ARGLIST construct assembles a list of "temporary" reference  items that
will be deleted by APPLY after the applied procedure returns.   Arguments to
ARGLIST may be anything legal for REFITEM.  Thus

        APPLY (proc, ARGLIST (foo, bar, VALUE baz))

is roughly equivalent to

        tmplst _ {{REFITEM (foo), REFITEM (bar),
                        REFITEM (VALUE baz)~~;
        APPLY (proc, tmplst);
        WHILE LENGTH (tmplst) DO DELETE (LOP (tmplst));

but is somewhat  easier to type.  Note  that the reference items  created by
ARGLIST  are just  like those  created by  REFITEM, except  that  they are
marked so that APPLY will know to kill them.

 APPLY


APPLY
APPLY  uses  the  items  in  the  <arglistspecifier>,  together  with  the
environment  information  from  the  procedure  item  (or  from  the current
environment, if the procedure  is named explicitly) to make  the appropriate
procedure call.  <arglistspecifier> is an ordinary list expression, except
that each element of the list must be a reference item.  The elements of the
list will be used  as the actuals in the  procedure call.  There must  be at
least as  many list  elements as there  are formals  in the  procedure.  The
reference  items  must  refer  to  an  object  of  the  same  type   as  the
corresponding formal parameter  in the procedure being  called.  (EXCEPTION:
if the formal parameter is an untyped itemvar or untyped itemvar array, then
the  reference  item  may  refer  to  a  typed  itemvar  or  itemvar  array,
respectively.) At present,  type checking (but  not type coercion)  is done.
If the formal  parameter is a reference  parameter, then a reference  to the
object pointed to by the reference item is passed.  If the  formal parameter
is  a value  parameter,  then the  value of  the  object pointed  to  by the
reference item  is used.  Similarly,  "?" formals are  handled appropriately
when  the  reference  item  contains a  "?"  or  "BIND"  reference.   If the
procedure to be  called has no  parameters, the <arglistspecifier>  may be
left out.

Apply may  be used  wherever an  itemvar procedure  call is  permitted.  The
value returned will be whatever value would normally be returned by  the the
applied procedure, but Apply will  treat it as an item number.   Care should
therefore be taken when using  the result of Apply when the  procedure being
invoked is not itself an itemvar procedure, since this may cause  an invalid
item number to be  used as a valid item  (for instance, in a  MAKE).  Recall
that when a typed procedure (or an Apply) is called at statement  level, the
value it returns is ignored.  Here is an example of the use of APPLY.

    BEGIN
    LIST L;INTEGER XX;
    INTEGER ITEMVAR YY;ITEMVAR ZZ;
    REAL ARRAY AA[1:2];
    PROCEDURE FOO (INTEGER X;
        ITEMVAR Y,Z; REAL ARRAY A);
            BEGIN
            Y _ NEW (X);
            Z _ NEW (A);
            A[X]_3;
            END;
    XX_0;
    L _ {{REFITEM (XX), REFITEM (YY),
        REFITEM (ZZ), REFITEM (AA)~~;
    XX _ 2; AA[1] _ AA[2] _ 1;
    APPLY (FOO, L);
    COMMENT Y now contains an item whose
        datum is 2, Z contains an item whose
        datum is the   array (1.0, 1.0),
        A[1]=1.0, and A[2]=3.0.;
    END;



The variables  accessed by  a  procedure called  with APPLY  may  not always
be what you would think they were.  Temporary terminology: the "environment"
of  a procedure   is the  collection of   variables, arrays  and  procedures
accessible to  it.    "Environment" is  not  meant  to include the  state of
the  associative store  or the  universe  of  items.  The  environment  of a
procedure item is the environment  of the ASSIGN, and that  environment will
be  used regardless of the position  of the APPLY.   Since   procedure items
are  untouched by   block  exits, yet environments  are, it is  possible  to
Apply a procedure item  when  its environment is gone; Sail catches  most of
these  situations   and  gives an  error  message.   Consider  the following
example:


    BEGIN
    ITEM P; LABEL L;
    RECURSIVE PROCEDURE FOO (INTEGER J);
    BEGIN "FOO"
        INTEGER I;
        PROCEDURE BAZ;
          PRINT ("J=", J, " I=", I);
        IF J=1 THEN
            BEGIN
            I_2;
            ASSIGN (P, BAZ);
            FOO (-1);
            END
        ELSE APPLY (DATUM (P));
    END "FOO";
    FOO (1);
L:  APPLY (DATUM (P)); COMMENT will cause a
        runtime error -- see discussion below;
    END

The   effect  of  the  program   is to   Assign  Baz  to  P   on   the first
instantiation   of   Foo,   then  Apply   P   on   the   second  (recursive)
instantiation.   However,   the  environment at   the  time  of   the Assign
includes  {I=2,  J=1~  but   the environment   at  the  time  of   the Apply
includes   {I=0, J=-1~   instead.   At the   time   of the   Apply,   Baz is
executed with the environment from  the time of the Assign,  and  will print
out

       J=1 I=2

The   Apply  at  L   will  cause   a  runtime  error   message   because the
environment of the Assign has been destroyed by the exiting of Foo.

INTERRUPTS Introduction 


                                SECTION  19

                                 INTERRUPTS




19.1  Introduction

The  interrupt facilities  of  Sail are  based on  the  interrupt facilities
provided by the operating system under which Sail is running.   For programs
running  at  SUAI  or  on  TENEX  this  results  in  satisfactory  interrupt
operation.   TOPS-10 programs  are at  a distinct  disadvantage  because the
operating system does not prevent interrupt handlers from  being interrupted
themselves.   At SUAI  the Sail  system uses  new-style  interrupts [Frost];
programs may  also enable  for old-style  interrupts and  the two  will work
together provided that the same  condition is not enabled under  both kinds.
On TENEX  the pseudointerrupt  (PSI) system  is used;  programs may  use the
interrupt system independently of Sail.  Only interrupt functions pertaining
to the current fork are  provided.  TOPS-10 interrupts are directly  tied to
the APRENB system; Sail and non-Sail use do not mix.

Sail  gives control  to the  user program  as soon  as the  operating system
informs the Sail interrupt handler.  This can be dangerous because  the Sail
runtime  system  may  be  in  the  middle  of  core  allocation  or  garbage
collection.   Therefore Sail  provides a  special runtime  DFRINT  which can
receive  control  in the  restricted  environment of  an  interrupt.  DFRINT
records  the fact  that an  interrupt happened  and that  a  particular user
procedure  is to  be run  at the  next polling  point (page  191),  when the
integrity of all runtime data structures is (normally) assured.  If the Sail
interrupt handler passes control to DFRINT then the user procedure (which is
run at the next polling  point) is called a "deferred  interrupt procedure",
even though the only connection it has with interrupts is the special status
and priority given to  it by the Sail  Process machinery.  If DFRINT  is not
used then  the user  procedure to  which the  Sail interrupt  handler passes
control is called an  "immediate interrupt procedure".  (This  is orthogonal
to the TENEX distinction between immediate and deferred TTY interrupts.)

To use interrupts  a program must first  tell Sail what procedure(s)  to run
when  an interrupt  happens.  The  routines INTMAP  and PSIMAP  perform this
task.  Deferred  interrupts use  the Sail process  machinery (page  186), so
INTSET is used to sprout  the interrupt process.  Then the  operating system
must  be  told  to  activate (and  deactivate)  interrupts  for  the desired
conditions.  ENABLE and DISABLE are used by the program to tell  Sail, which
tells the operating system.

A good knowledge  of the interrupt structure  of the operating  system which
you are trying to use should be considered a prerequisite for this chapter.

Interrupt Routines ATI, DTI DFR1IN DFRINT


19.2  Interrupt Routines



********************************  ATI, DTI  ******************************;

ATI (PSICHAN, CODE);
DTI (PSICHAN, CODE)

(TENEX  only.) CODE  is associated  or dissociated  with PSICHAN,  using the
appropriate JSYS.  Executing ATI is an additional step (beyond ENABLE) which
is necessary to receive TENEX TTY interrupts.



*********************************  DFR1IN  *******************************;

DFR1IN (AOBJNPTR)

DFR1IN  is the  procedure used  by DFRINT  to record  the interrupt  and the
AOBJNPTR.  Thus DFRINT is (partially) equivalent to

    SIMPLE PROCEDURE DFRINT; BEGIN
    DFR1IN (<AOBJNPTR specified
        to INTMAP>) END;

To  have  more  than  one  procedure run  (deferred)  as  the  result  of an
interrupt, a  program may  use DFR1IN to  record the  AOBJNPTRs explicitly.
Example:

    SIMPLE PROCEDURE ZORCH;
        BEGIN
        DFR1IN (<AOBJN pointer for FOO call>);
        DFR1IN (<AOBJN pointer for BAZ call>);
        END;
    ...
    INTMAP (INTTTYINX, ZORCH, 0);
    ENABLE (INTTTYINX);

Both  FOO  and  BAZ will  be  run  (deferred) as  the  result  of INTTTYINX
interrupt.



*********************************  DFRINT  *******************************;

DFRINT

DFRINT  is a  predeclared  simple procedure  which handles  the  queueing of
deferred interrupts.     Specify DFRINT to  INTMAP for each  interrupt which

 DISABLE, ENABLE INTMAP


will be  run as a  Sail deferred interrupt.   When run as  the result  of an
interrupt,  DFRINT  grabs  the AOBJNPTR  pointer  specified  to  INTMAP (or
PSIMAP) and copies  the block along with  other useful information  into the
circular deferred interrupt buffer.  (See INTTBL.)  DFRINT then  changes the
status of the interrupt process  INTPRO from suspend to ready, and  turns on
the global integer INTRPT.



****************************  DISABLE, ENABLE  ***************************;

DISABLE (INDEX);
ENABLE (INDEX)

Sail  tells the  operating system  to  ignore (DISABLE)  or to  send  to the
program (ENABLE) interrupts for the condition specified by INDEX.   INDEX is
a bit number (0-35) which  varies from system to system;  consult [SysCall].
INDEX is sometimes called a "PSI channel" on TENEX.



*********************************  INTMAP  *******************************;

INTMAP (INDEX, PROC, AOBJNPTR)

(TENEX  users should  see  PSIMAP.) The  routine INTMAP  specifies  that the
simple  procedure PROC  is to  be run  whenever the  Sail  interrupt handler
receives an interrupt corresponding to the condition specified by  INDEX.  A
separate INTMAP must be executed for each interrupt condition.  If  the same
INDEX is specified on two calls  to INTMAP then the most recent call  is the
one in effect.  PROC must  be a simple procedure with no  formal parameters.
If PROC is a user procedure then PROC is run as a Sail immediate interrupt.

AOBJNPTR should be  zero unless DFRINT is  specified for PROC.  If  PROC is
DFRINT (and thus will be a Sail deferred interrupt) then AOBJNPTR gives the
length and location of a block of memory describing a procedure  call.  Such
a block has the form

      <number of words in the block>
      <1st parameter to the procedure>
      <second parameter to the procedure>
      ...
      <last parameter to the procedure>
      -1,,<address of the procedure>

and an AOBJNPTR to it has the form

      -<number of words>,,<starting address>.

Here is  an example in  which FOO (I, J,  K) is to  be called as  a deferred
interrupt.

 INTSET INTTBL



    PROCEDURE FOO (INTEGER i, j, k); ...;
    ...
    SAFE INTEGER ARRAY FOOBLK [1:5];
    ITEMVAR IPRO;  COMMENT for process item of INTPRO;
    ...
    FOOBLK [1] _ 5;
    FOOBLK [2] _ I;
    FOOBLK [3] _ J;
    FOOBLK [4] _ K;
    FOOBLK [5] _ (-1 LSH 18)+LOCATION (FOO);
    ...
    INTSET (IPRO _ NEW, 0);  COMMENT sprout INTPRO;
    INTMAP (INTTTIINX, DFRINT,
      (-5 LSH 18) + LOCATION (FOOBLK[1]));
    ENABLE(INTTTIINX)

NOTE:  The procedure  (FOO in  this case)  must not  be declared  inside any
process except  the main  program.  Otherwise, its  environment will  not be
available when INTPRO runs.  However,  there is a rather complex way  to get
around this  by using  <environment>,,PDA as  the last  word of  the calling
block.   See  a  Sail  hacker  if you  must  do  this  and  don't  know what
<environment> or PDA mean.



*********************************  INTSET  *******************************;

INTSET (ITM, OPTIONS)

INTSET sprouts  the interrupt process  INTPRO with process  options OPTIONS;
see page 187.  The default priority  of INTPRO is zero; this is  the highest
possible priority and no other process may have priority zero.   Thus INTPRO
is sure to be run first at any polling point.  ITM must be an item;  it will
become the process  item of INTPRO, the  interrupt process.  INTSET  must be
called before any deferred interrupts are used.



*********************************  INTTBL  *******************************;

INTTBL (NEWSIZE)

The  buffer used  to queue  deferred interrupts  is initially  128 locations
long.  The queue has not been know to overflow except for programs  which do
not POLL very  often.  INTTBL changes the  buffer size to NEWSIZE.   Do not
call INTTBL if  there are any deferred  interrupts pending; wait  until they
have all been executed.

 PSIMAP Immediate Interrupts IN SUAI Sail


*********************************  PSIMAP  *******************************;

PSIMAP (PSICHAN, PROC,
             AOBJNPTR, LEVEL)

(TENEX only.) This routine  is the same as  INTMAP except that LEVEL  may be
specified.  ROUTINE is executed at interrupt level LEVEL.  (TENEX  INTMAP is
equivalent to PSIMAP ( , , ,3).) PROC and AOBJNPTR have the same meaning as
for INTMAP.



19.3  Immediate Interrupts

Do not access, create, or destroy strings, records, arrays, sets,  or lists.
If these data structures are needed then use deferred interrupts.

To set up an immediate interrupt say

   INTMAP (<index>, <simple procedure name>, 0);
   ENABLE (<index>)
        or on TENEX,
   PSIMAP (<PSIchan>, <simple procedure name>, 0, <PSIlev>);
   ENABLE (<PSIchan>)

where  <index>  is a  code  for the  interrupt  condition.  To  turn  off an
interrupt use

    DISABLE (<index>)

The  system will  not provide  user interrupts  for the  specified condition
until another ENABLE statement is executed.

IN SUAI Sail
A  procedure specified  by  an INTMAP  statement  will be  executed  at user
interrupt level.  A program operating in this mode will not  be interrupted,
but must finish whatever  it is doing within 8/60  ths of a second.   It may
not do any UUOs that can cause it to be rescheduled.  Also, the accumulators
will not be the same ones as those that were in use by the  regular program.
Certain locations are set up as follows:

   ACs 1-6Set up by the system as in [Frost].

   AC '15 (USER)  Address of the Sail user table.

   AC '16  (SP)  A  temporary string  push down  stack pointer  (for the
          foolhardy who chose to disregard the warnings about strings in
          immediate interrupts).

   AC '17 (P)  A temporary push down stack pointer.

 IN TOPS-10


   XJBCNI (declared in SYS:PROCES.DEF as an external integer.)  Bit mask
          with a bit on corresponding to the current condition.

   XJBTPC (declared in SYS:PROCES.DEF as an external integer.)   Full PC
          word of regular user level program.

The  interrupt will  be  dismissed and  the  user program  resumed  when the
interrupt  procedure is  exited.  For  more information  on  interrupt level
programming consult [Frost].

IN TOPS-10
The interrupt handler again will decode the interrupt condition and call the
appropriate procedure.  Since there  is no "interrupt level",  the interrupt
procedure must not itself generate any interrupt conditions, since this will
cause Sail to  lose track of  where in the  user program it  was interrupted
(trapped).

Also,  the Sail  interrupt module  sets up  some temporary  accumulators and
JOBTPC:

   AC '10 index of the interrupt  condition.

   AC '15 (USER) Address of the Sail user table.

   AC '16 (SP)  A temporary string push down list.  Beware.

   AC '17 (P)  A temporary push down pointer.

   JOBTPC (an external integer) Full PC word of regular user program.

The "real" acs -- i.e., the values of all accumulators at the time  the trap
occurred -- are stored in  locations APRACS to APRACS+17.  Thus you  can get
at the value of accumulator x by declaring APRACS as an external integer and
referring to MEMORY [LOCATION (APRACS)+x].  When the interrupt  procedure is
exited the acs are restored from APRACS to APRACS+17 and the  Sail interrupt
handler  jumps  to the  location  stored in  JOBTPC  (which was  set  by the
operating system to the location at which the trap occurred).  Thus,  if you
want to transfer control to some location in your user program, a way  to do
it is to have an interrupt routine like:

 IN TENEX Sail GTRPW RTIW, STIW 



   SIMPLE PROCEDURE IROUT;
     BEGIN
     EXTERNAL INTEGER JOBTPC;
     :
     JOBTPC_LOCATION (GTFOO);
     COMMENT GTFOO is a non-simple procedure
         that contains a GO TO FOO, where FOO
         is the location to which control
         is to be passed.  This allows the
         "go to solver" to be called and clean
         up any unwanted procedure activations.;
     END;

WARNING: this does not work very well if you were interrupted at a bad time.

IN TENEX Sail
Sail initialization does a SIR,  setting up the tables to  external integers
LEVTAB and  CHNTAB, then  an EIR to  turn on  the interrupt  system.  PSIMAP
fills the appropriate CHNTAB  location with XWD LEV,LEVROU, where  LEVROU is
the  address of  the  routine that  handles  the interrupts  for  level LEV.
LEVROU saves the  accumulators in blocks  PS1ACS, PS2ACS, and  PS3ACS, which
are external  integers, for  levels 1  through 3  respectively.  Thus  for a
level 3 interrupt accumulator x can be accessed by MEMORY [LOCATION (PS3ACS)
+ x].  The  PC can be  obtained by reading the  LEVTAB address with  the RIR
JSYS.   Temporary  stacks  are  set  up  for  both  immediate  and  deferred
interrupts.

See page 145  for an example of  TENEX immediate interrupts.   The functions
GTRPW, RTIW, STIW provide  for some of the  information set up in  ACs under
SUAI or TOPS-10.



*********************************  GTRPW  ********************************;

STATUS _ GTRPW (FORK);

The trap status of FORK is returned, using the GTRPW JSYS.



*******************************  RTIW, STIW  *****************************;

AC1 _ RTIW (PSICHAN, @AC2);
STIW (PSICHAN, AC2, AC3);

The indicated JSYS is performed.

Clock Interrupts PSIDISMS


19.4  Clock Interrupts

(This feature  is currently  available only  in SUAI  Sail and  TENEX Sail.)
Clock interrupts are a kind of immediate interrupt used to  approximate time
sharing among processes.  Every time the scheduler decides to run  a process
it copies the procedure's time quantum (see all about quantums of processes,
page 187) into the Sail  user table location TIMER.  Consider  the following
procedure, which is roughly equivalent to the one predeclared in Sail:

    SIMPLE PROCEDURE CLKMOD;
      IF (TIMER_TIMER-1)  0 THEN INTRPT_-1;

To time share several ready  processes one should include polling  points in
the relevant process procedures and should execute the following statements:

    INTMAP (INTCLKINX, CLKMOD, 0);
    ENABLE (INTCLKINX);
         or on TENEX
    PSIMAP (1, CLKMOD, 0, 3);
    ENABLE (1);
    PSIDISMS (1, 1000/60);

The  macro SCHEDULEONCLOCKINTERRUPTS  defined in  SUAISYS:PROCES.DEF is
equivalent  to these  statements.  When  the time  quantum of  a  process is
exceeded by the  number of clock  ticks since it  began to run,  the integer
INTRPT is  set, and this  causes the  next polling point  in the  process to
cause a rescheduling (see about  rescheduling and INTRPT on page  190).  The
current running  process will  be made ready,  and the  scheduling algorithm
chooses a ready process to run.

In TENEX Sail  clock interrupts are  handled differently.  Since  TENEX does
not  directly provide  for interrupting  user processes  on clock  ticks, an
inferior fork is created which periodically generates the interrupts.



********************************  PSIDISMS  ******************************;

PSIDISMS (PSICHAN, MSTIME)

An inferior fork is created  which interrupts the current fork  every MSTIME
milliseconds of real time.  The inferior is approximately

WAIT:   MOVE    1,MSTIME    ;HOW LONG
        DISMS               ;GO AWAY
        MOVEI   1,-1        ;HANDLE TO SUPERIOR
        MOVE    2,[bit mask];SELECTED CHANNEL
        IIC                 ;CAUSE AN INTERRUPT
        JRST    WAIT        ;CONTINUE

 PSIRUNTM KPSITIME Deferred Interrupts


********************************  PSIRUNTM  ******************************;

PSIRUNTM (PSICHAN, MSTIME)

The current fork is  interrupted every MSTIME milliseconds of  runtime.  The
inferior is approximately

WAIT:   MOVE    1,MSTIME    ;HOW LONG
        DISMS
        MOVEI   1,-1        ;SUPERIOR FORK
        RUNTM               ;RUNTIME OF SUPERIOR
        CAMGE   1,NEXTTIME  ;READY?
         JRST WAIT          ;NO
        ADD     1,MSTIME
        MOVEM   1,NEXTTIME  ;RECHARGE
        MOVEI   1,-1        ;SUPERIOR
        MOVE    2,[bit mask];SELECTED CHANNEL
        IIC                 ;CAUSE INTERRUPT
        JRST    WAIT



********************************  KPSITIME  ******************************;

KPSITIME (PSICHAN)

Discontinues clock interrupts on PSICHAN.

Several  channels can  be  interrupted by  PSIRUNTM or  PSIDISMS,  each with
different timing interval.



19.5  Deferred Interrupts

Deferred interrupts use the Sail Process machinery (page 186) to synchronize
the Sail runtime system with  the running of user procedures in  response to
interrupts.  The  routine INTSET sprouts  the interrupt process  INTPRO, the
process which eventually does the calling of deferred  interrupt procedures.
This process  is special  because it  is (ordinarily)  guaranteed to  be the
first process  run after  a rescheduling.  (See  page 191  and page  195 for
information  on  rescheduling.)   When  DFRINT  runs  as  the  result  of an
interrupt,  it  copies  the  calling block  (specified  to  INTMAP  with the
AOBJNPTR)  into  the deferred  interrupt  buffer and  turns  on  the global
integer  INTRPT.  At  the  next polling  point the  process  supervisor will
suspend the  current process  and run INTPRO.   INTPRO calls  the procedures
specified by the calling blocks in the deferred interrupt buffer,  turns off
INTRPT, and suspends itself.  The process scheduler then runs the process of
highest priority.



One very common use of deferred  interrupts is to cause an event  soon after
some asynchronous condition (say,  TTY activation) occurs.  This  effect may
be obtained by the following sequence:

   INTSET (IPRO_NEW, 0); COMMENT this will cause
        the interrupt process to be sprouted and
        assigned to IPRO.  This process will execute
        procedure INTPRO and will have priority zero
        (the highest possible).;
   :
   INTMAP (<index>, DFRINT,
     DFCPKT (0, <event type>, <event notice>,
                 <cause options>));
   :
   ENABLE (<index>);

In SUAISYS:PROCES.DEF is the useful macro

    DEFERREDCAUSEONINTERRUPT (<index>,
        <event type>, <notice>, <options>)

which may be used to replace the INTMAP statement.

The following program  illustrates how deferred  interrupts on TENEX  can be
accomplished.

    BEGIN REQUIRE 1 NEWITEM;
    ITEMVAR IPRO; COMMENT for process item;

    PROCEDURE FOO (INTEGER I, J);
    PRINT ("HI ", I, " ", J);

    INTEGER ARRAY FOOBLK[1:4];
    FOOBLK[1] _ 4;    COMMENT # words;
    FOOBLK[2] _ 12;   COMMENT arguments;
    FOOBLK[3] _ 13;
    FOOBLK[4] _ -1 LSH 18 + LOCATION (FOO);

    INTSET (IPRO _ NEW, 0);
    PSIMAP (1, DFRINT,
        -4 LSH 18 + LOCATION (FOOBLK[1]), 3);
    ENABLE (1); ATI (1, "Q"-'100);

    DO BEGIN OUTCHR ("."); POLL; END UNTIL FALSE;
    END;

The program  prints dots, interspersed  with "HI 12  13" for  each control-Q
typed on  the console.  Whenever  a control-Q is  typed, DFRINT  buffers the
request and makes INTPRO ready to  run; then DFRINT DEBRKs (in the  sense of
the DEBRK JSYS) back to the  interrupted code.  At Sail user level  the POLL

 THE DEFERRED INTERRUPT PROCESS - INTPRO


statement causes  the process  scheduler to run  INTPRO, where  the deferred
interrupt calling block  (which was copied by  DFRINT) is used to  call FOO.
THE DEFERRED INTERRUPT PROCESS - INTPRO
INTPRO first  restores the following information which was stored  by DFRINT
at the time of the interrupt.

   LOCATION CONTENTS

   USER     The base of the user table (GOGTAB).

   AC 1     Status of spacewar buttons.

   AC 2     Your job status word (JBTSTS). See [Frost].

   IJBCNI(USER)  XJBCNI (i.e., JOBCNI) at time of interrupt.

   IJBTPC(USER)  XJBTPC (i.e., JOBTPC) at time of interrupt.

   IRUNNR(USER)  Item number of running process at time of interrupt.

Then INTPRO calles the procedure  described by the calling block.   When the
procedure is finished, INTPRO looks to see if the deferred  interrupt buffer
has any  more entries left.   If it  does, INTPRO handles  them in  the same
manner.  Otherwise INTPRO  suspends itself  and the  highest  priority ready
process takes over.

LEAP RUNTIMES Types and Type Conversion TYPEIT


                                SECTION  20

                               LEAP RUNTIMES


We  will follow  the  same conventions  for describing  Leap  execution time
routines as  were used in  describing the runtimes  of the Algol  section of
Sail (see page 62).



20.1  Types and Type Conversion



*********************************  TYPEIT  *******************************;

CODE _ TYPEIT (ITM);

The type of the datum linked to  an item is called the type of an  item.  An
item without a datum is called untyped.  TYPEIT is an integer function which
returns an integer CODE for the type of the item expression ITM that  is its
argument.  The codes are:

          0 - item deleted or never allocated
          1 - untyped
          2 - Bracketed Triple item
          3 - string
          4 - real
          5 - integer
          6 - set
          7 - list
          8 - procedure item
          9 - process item
         10 - event item
         11 - context item
         12 - reference item
         13 - record pointer
         14 - label
         15 - record class
         23 - string array
         24 - real array
         25 - integer array
         26 - set array
         27 - list array
         31 - context array
         33 - record pointer array
         37 - error (the runtime screwed up)

The user is encouraged to use  TYPEIT.  It requires the execution of  only a
few  machine instructions and can save considerable debugging time.

 CVSET CVLIST CVN and CVI MKEVTT 


*********************************  CVSET  ********************************;

SET _ CVSET (LIST)

CVSET  returns  a  set  given  a  list  expression  by   removing  duplicate
occurrences of items in the list, and reordering the items into the order of
their internal integer representations.



*********************************  CVLIST  *******************************;

LIST _ CVLIST (SET)

CVLIST  returns a  list  given a  set  expression.  It  executes  no machine
instructions, but merely lets you  get around Sail type checking  at compile
time.



******************************  CVN and CVI  *****************************;

INTEGR _ CVN (ITM);
ITM _ CVI (INTEGR)

CVN returns the integer that is the internal representation of the item that
is the the value of the  item expression ITM.  CVI returns the item  that is
represented by the integer expression  INTEGR that  is its  argument.  Legal
item numbers are between (inclusively) 1 and 4095, but you'll get in trouble
if  you  CVI  when  no  item has  been  created  with  that  integer  as its
representation.  Absolutely no  error checking is  done.  CVI is  for daring
men.  See about  item implementation, page  157, for more  information about
the internal representations of items.



*********************************  MKEVTT  *******************************;

MKEVTT (ITEM)

MKEVTT will convert its item argument to an event type item.  The  old datum
will be overwritten.  The type of  the item will  now be "event  type".  Any
item except an  event type item  may be converted to  an event type  item by
MKEVTT.

Make and Erase Breakpoints BRKERS, BRKMAK, BRKOFF Pname Runtimes CVIS


20.2  Make and Erase Breakpoints



*************************  BRKERS, BRKMAK, BRKOFF  ***********************;

BRKMAK (BREAKPTPROC);
BRKERS (BREAKPTPROC);
BRKOFF

In  order to  give the  programmer  some idea  of what  is going  on  in the
associative store,  there is a  provision to interrupt  each MAKE  and ERASE
operation, and enter a breakpoint procedure.  The user can then  do whatever
he wants with the three items of the association being created or destroyed.
ERASE Foo  ANY   ANY will cause  the breakpoint procedure to  be activated
once for each association that matches the pattern.   MAKE it1  it2 [it3 
it4  it5] will cause the breakpoint procedure to be activated twice.

The user's breakpoint procedures must have the form:

    PROCEDURE Breakptproc (ITEMVAR a, o, v )

If the association being made or erased is AOV, then directly before doing
the Make or Erase, Breakptproc is called with the items A, O, and V for the
formals a, o, and v.

To make  the procedure  Breakptproc into a  breakpoint procedure  for MAKE,
call  BRKMAK  with  Breakptproc  as a  parameter.   To  make  the procedure
Breakptproc  into  a  breakpoint  procedure  for  ERASE,  call  BRKERS with
Breakptproc as its parameter.  To turn off both breakpoint procedures, call
BRKOFF with no parameters.

NOTE: BRKMAK, BRKERS and BRKOFF are not predeclared.  The user  must include
the declarations:

    EXTERNAL PROCEDURE BRKERS (PROCEDURE BP);
    EXTERNAL PROCEDURE BRKMAK (PROCEDURE BP);
    EXTERNAL PROCEDURE BRKOFF



20.3  Pname Runtimes



**********************************  CVIS  ********************************;

"PNAME" _ CVIS (ITEM, @FLAG)

The print name of ITEM is returned as a string.  Items have print names only

 CVSI DELPNAME NEWPNAME Other Useful Runtimes LISTX


if one includes a REQUIRE n  PNAMES statement in his program, where n  is an
estimate of the number of pnames the program will use.  An Item's print name
is the identifier used to declare  it, or that pname explicitly given  it by
the  NEWPNAME  function (see  below).   FLAG is  set  to False  (0)  if the
appropriate string  is found.   Otherwise it is  set to  TRUE (-1),  and one
should not put great faith in the string result.



**********************************  CVSI  ********************************;

ITEM _ CVSI ("PNAME", @FLAG)

The Item whose pname  is the same as  the string argument PNAME  is returned
and FLAG is set to FALSE if such an ITEM exists.  Otherwise,  something very
random is returned, and FLAG is set to TRUE.



*******************************  DELPNAME  ******************************;

DELPNAME (ITEM)

This function deletes any string PNAME associates with this ITEM.



*******************************  NEWPNAME  ******************************;

NEWPNAME (ITEM, "STRING")

This function  assigns to the  Item the name  "STRING".  Don't  perform this
twice  for the  same  Item without  first  deleting the  previous  one.  The
corresponding name or Item may be retrieved using CVIS or CVSI  (see above).
The NULL string is prohibited as the second argument.



20.4  Other Useful Runtimes



*********************************  LISTX  ********************************;

VALUE _ LISTX (LIST, ITEM, N)

The value of  this integer function  is 0 if  the ITEM (an  item expression)
does not  occur in  the list at  least N  (an integer  expression) different
times in the LIST (a list expression).  Otherwise  LISTX is the index of the
Nth occurrence of ITEM in LIST.  For example,

 FIRST, SECOND, THIRD ISTRIPLE LOP COP



    LISTX ({{Foo, Baz, Garp, Baz~~, Baz, 2)  is 4.



**************************  FIRST, SECOND, THIRD  ************************;

ITEM _ FIRST (BRACTRIPITEM);
ITEM _ SECOND (BRACTRIPITEM);
ITEM _ THIRD (BRACTRIPITEM)

The Item  which is the  FIRST, SECOND, or  THIRD element of  the association
connected to a bracketed  triple item (BRACTRIPITEM) is returned.   If the
item expression BRACTRIPITEM does  not evaluate to a bracketed  triple, an
error messages issues forth.



********************************  ISTRIPLE  ******************************;

RSLT _ ISTRIPLE (ITM)

If ITM is a bracketed  triple item then ISTRIPLE returns TRUE;  otherwise it
returns FALSE.  ISTRIPLE (ITM) is equivalent to (TYPEIT (ITM) = 2).



**********************************  LOP  *********************************;

ITEM _ LOP (@SETVARIABLE);
ITEM _ LOP (@LISTVARIABLE)

LOP will remove the first  item of a set or  list from the set or  list, and
return that item as  its value.  Note that  the argument must be  a variable
because the contents of  the set or list is  changed.  If one LOPs  an empty
set or a null list, an  error message  will be issued.



**********************************  COP  *********************************;

ITEM _ COP (SETEXPR);
ITEM _ COP (LISTEXPR)

COP will return the first item of the set or list just as LOP  (above) will.
However, it will NOT remove that  item from the set or list.  Since  the set
or list will be unchanged, COP's  argument may be a set or  list expression.
As with LOP, an error message will be returned if one COPs an empty set or a
null list.

 LENGTH SAMEIV Runtimes for User Cause and Interrogate Procedures SETCP AND SETIP


*********************************  LENGTH  *******************************;

VALUE _ LENGTH (SETEXPR);
VALUE _ LENGTH (LISTEXPR)

LENGTH will  return the  number of  items in that  set or  list that  is its
argument.  LENGTH (S) = 0 is  a much  faster test  for the null set  or list
that S = PHI or S = NIL.



*********************************  SAMEIV  *******************************;

VALUE _ SAMEIV (ITMVAR1, ITMVAR2)

SAMEIV is useful in Matching  Procedures to solve a particular  problem that
arises when a Matching Procedure  has at least two ? itemvar  arguments.  An
example will demonstrate the problem:

    FOREACH X | Matchingproc ( X, X ) DO ...;
    FOREACH X, Y | Matchingproc ( X, Y ) DO ... ;

Clearly, the matching procedure with both arguments the same may want  to do
something different from the  matching procedure with two  different Foreach
itemvars as its arguments.  However, there is no way inside the body  of the
matching procedure to differentiate the  two cases since in both  cases both
itemvar formals have the value BINDIT.  SAMEIV will return True only  in the
first case,  namely 1) both  of  its  arguments are ?  itemvar formals  to a
matching procedure, 2) both had the same Foreach itemvar passed by reference
to them.   It will return  False under all  other conditions,  including the
case where the Foreach  itemvar is bound at the  time of the call (so  it is
not passed  by reference,  but its  item value  is passed  by value  to both
formals).



20.5  Runtimes for User Cause and Interrogate Procedures



****************************  SETCP AND SETIP  ***************************;

SETCP (ETYPE, PROCNAME);
SETCP (ETYPE, DATUM (PROCITEM));
SETIP (ETYPE, PROCNAME);
SETIP (ETYPE, DATUM (PROCITEM))

SETCP  and  SETIP  associate  with the  event  type  specified  by  the item
expression  ETYPE  a  procedure specified  by  its name  or the  datum  of a
procedure item expression.