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 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 C00620 00169 THE BOOLEAN EXPRESSION C00624 00170 THE RETRIEVAL TRIPLE C00629 00171 C00633 00172 THE MATCHING PROCEDURE 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 "" 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 (). Creates a new item which has a datum that is a context. 2. Using a 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 ::= ::= ; ::= BEGIN ::= BEGIN ::= ; ::= END ::= END ::= ; ::= BEGIN ::= BEGIN ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= Semantics DECLARATIONS STATEMENTS BLOCK NAMES ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= : ::= 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 construct is equivalent in action to the 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 ::= ::= , ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= BOOLEAN ::= INTEGER ::= REAL ::= RECORDPOINTER ( ) ::= STRING ::= EXTERNAL ::= FORTRAN ::= FORWARD ::= INTERNAL ::= OWN ::= RECURSIVE ::= SAFE ::= SHORT ::= SIMPLE ::= ::= ::= ARRAY ::= ::= ::= , ::= [ ] ::= ::= , ::= : ::= ::= ::= PRELOADWITH ::= PRESETWITH ::= ::= , ::= ::= [expression] ::= LABEL ::= PROCEDURE ::= PROCEDURE ::= ::= ::= ( ) ::= ::= ; ::= ::= ; ::= ::= ( ) ::= ::= REFERENCE ::= VALUE ::= ::= ARRAY ::= PROCEDURE ::= LET ::= ::= , Restrictions ::= = ::= CLEANUP ::= REQUIRE ::= ::= , ::= ::= INITIALIZATION ::= INITIALIZATION [ ] ::= 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 for in the productions for and . 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. () INTEGER I, J, K EXTERNAL REAL X, Y INTERNAL STRING K () 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] () LABEL L, X, Y () 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 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 . This specification gives the values which are to be placed in consecutive core locations of the Arrays declared immediately following the . "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 (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 ::= _ ::=  ::= SWAP ::= ::= ELSE ::= IF THEN ::= GO TO ::= GOTO ::= GO ::= ::= FOR _ DO ::= NEEDNEXT ::= ::= , ::= ::= STEP UNTIL ::= STEP WHILE ::= WHILE DO ::= NEEDNEXT ::= DO UNTIL ::= ::= ::= CASE OF BEGIN ::= CASE OF BEGIN ::= END ::= END ::= ::= ; ::= [ ] ::= [ ] ::= ; [ ] Semantics ::= RETURN ::= RETURN ( ) ::= DONE ::= DONE ::= NEXT ::= NEXT ::= CONTINUE ::= CONTINUE ::= ::= ::= ( ) ::= ::= , ::= ::= ::= ::= NOWSAFE ::= NOWUNSAFE 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 ::= ::= ::= ::= ::= IF THEN ELSE ::= _ ::= CASE OF ( ) ::= ::= , ::= ::= ::= ::= ::=  ::= OR ::= ::=  ::= AND ::=  ::= NOT ::= ::= ::= ::= ::= ::= < ::= > ::= = ::=  ::=  ::=  ::= LEQ ::= GEQ ::= NEQ ::= ::= MAX ::= MIN ::= ::= ::= + ::= - ::= LAND ::= LOR ::= EQV ::= XOR ::= ::= ::= * ::= / ::= % ::= LSH ::= ASH ::= ROT ::= MOD ::= DIV ::= & ::= ::= ^ ::= ::= - ::= LNOT ::= ABS ::= [ ] ::=  ::= INF ::= ::= ::= LOCATION ( ) ::= ( ) ::= Type Conversion ::= TO ::= FOR ::= ::= ::= ::= ::= ::= 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 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; 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 [ ] 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 . 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 ::= ::= ::= ::= ; ::= STARTCODE ::= QUICKCODE ::= END ::= END ::= ; ::= ::= ::= ::=
::= , ::= ,
::=
::= ::= @ Semantics DECLARATIONS IN CODE BLOCKS ::= ::= ( ) ::= ::= [ ] ::= ::= ::= [ ] ::= ::= ::= 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