Trailing-Edge
-
PDP-10 Archives
-
decuslib20-01
-
decus/20-0002/sail.man
There is 1 other file named sail.man in the archive. Click here to see a list.
COMMENT VALID 00294 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00022 00002 Stanford Artificial Intelligence Laboratory August 1976
C00025 00003 PREFACE HISTORY OF THE LANGUAGE LEARNING ABOUT SAIL CHANGES IN THE LANGUAGE OPERATING SYSTEMS UNIMPLEMENTED CONSTRUCTS ACKNOWLEDGEMENTS
C00032 00004 TABLE OF CONTENTS
C00051 00005 PROGRAMS AND BLOCKS Syntax
C00053 00006 Semantics DECLARATIONS STATEMENTS BLOCK NAMES
C00057 00007 EXAMPLES
C00058 00008 ALGOL DECLARATIONS Syntax
C00060 00009
C00062 00010
C00064 00011 Restrictions
C00066 00012
C00067 00013 Examples
C00070 00014 Semantics SCOPE OF DECLARATIONS TYPE QUALIFIERS
C00073 00015 NUMERIC DECLARATIONS STRING DECLARATIONS ARRAY DECLARATIONS
C00078 00016 PRELOAD SPECIFICATIONS
C00084 00017 PROCEDURE DECLARATIONS FORMAL PARAMETERS
C00088 00018 DEFAULT PARAMETER VALUES FORWARD PROCEDURE DECLARATIONS
C00093 00019 RECURSIVE PROCEDURES SIMPLE PROCEDURES
C00097 00020 EXTERNAL PROCEDURES
C00102 00021 PARAMETRIC PROCEDURES DEFAULTS IN PROCEDURE DECLARATIONS RESTRICTIONS ON PROCEDURE DECLARATIONS ALLOCATION AND DEALLOCATION
C00106 00022 INITIALIZATION AND REINITIALIZATION SYNONYMS
C00110 00023 CLEANUP DECLARATIONS REQUIREMENTS
C00114 00024
C00119 00025 Separately Compiled Procedures
C00124 00026 FORTRAN PROCEDURES
C00129 00027 ASSEMBLY LANGUAGE PROCEDURES
C00132 00028 ALGOL STATEMENTS Syntax
C00134 00029
C00136 00030 Semantics
C00138 00031 ASSIGNMENT STATEMENTS SWAP ASSIGNMENT CONDITIONAL STATEMENTS
C00142 00032 AMBIGUITY IN CONDITIONAL STATEMENTS GO TO STATEMENTS
C00145 00033
C00149 00034 FOR STATEMENTS
C00152 00035 WHILE STATEMENT DO STATEMENT
C00155 00036 CASE STATEMENTS RETURN STATEMENT
C00159 00037 DONE STATEMENT
C00163 00038 NEXT STATEMENT CONTINUE STATEMENT
C00167 00039 PROCEDURE STATEMENTS
C00171 00040 PROCEDURES AS ACTUAL PARAMETERS FORTRAN PROCEDURES
C00176 00041 NOW!SAFE and NOW!UNSAFE
C00178 00042 ALGOL EXPRESSIONS Syntax
C00180 00043
C00182 00044
C00184 00045 Type Conversion
C00186 00046
C00190 00047
C00194 00048 Semantics CONDITIONAL EXPRESSIONS
C00197 00049 ASSIGNMENT EXPESSIONS CASE EXPRESSIONS SIMPLE EXPRESSIONS
C00201 00050 PRECEDENCE OF ALGEBRAIC OPERATORS EXPRESSION EVALUATION RULES OR
C00205 00051 AND NOT <>= RELATIONS
C00210 00052 MAX MIN +- ADDITION SUBTRACTION LAND LOR XOR EQV LNOT */% MULTIPLICATION DIVISION DIV MOD ASH LSH ROT
C00214 00053 & CONCATENATION ^ EXPONENTIATION SUBSTRINGS
C00219 00054 INFINITY SPECIAL LENGTH OPERATOR FUNCTION DESIGNATORS UNARY OPERATORS MEMORY AND LOCATION
C00223 00055 LOCATION
C00225 00056 ASSEMBLY LANGUAGE STATEMENTS Syntax
C00227 00057 Semantics DECLARATIONS IN CODE BLOCKS
C00230 00058 PROTECT ACS DECLARATION OPCODES THE <simple addr> FIELD
C00235 00059 START!CODE VERSUS QUICK!CODE
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 IN!CONTEXT
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 CHECK!TYPE EXPR!TYPE
C00425 00113 BUILT!IN LEAP!ARRAY RESERVED DEFINE CONOK
C00429 00114 Miscelaneous Features COMPILE TIME I/O EVALDEFINE, EVALREDEFINE ASSIGNC NOMAC COMPILER!BANNER
C00434 00115 Hints
C00437 00116
C00440 00117 RECORD STRUCTURES Introduction Declaration Syntax Declaration Semantics
C00444 00118
C00448 00119 Allocation Fields
C00451 00120 Garbage Collection Internal Representations
C00455 00121 $CLASS RECRNG HNDLER RECSIZ TYPARR TXTARR
C00457 00122 Handler Procedures
C00460 00123
C00463 00124 More about Garbage Collection
C00466 00125
C00469 00126 TENEX ROUTINES Introduction TOPS-10 Style Input/Output
C00472 00127
C00475 00128 MAGTAPE I/O TENEX Style Input/Output OBTAINING ACCESS
C00479 00129 DATA TRANSFER RANDOM I/O ERROR HANDLING DIRECT DSK OPERATIONS ASND, RELD
C00483 00130 BKJFN CFILE CHARIN CHAROUT CHFDB CLOSF
C00486 00131 CVJFN DELF DELNF DEVST, STDEV
C00489 00132 DEVTYPE DSKIN, DSKOUT DVCHR ERSTR GDSTS, SDSTS
C00492 00133 GNJFN GTFDB GTJFN
C00495 00134 GTJFNL GTSTS, STSTS INDEXFILE
C00498 00135 JFNS JFNSL MTOPR
C00501 00136 OPENF OPENFILE
C00504 00137 RCHPTR, SCHPTR
C00507 00138 RFBSZ RFPTR, SFPTR RLJFN RNAMF
C00510 00139 RWDPTR, SWDPTR SETCHAN SETINPUT SINI
C00514 00140 SIZEF UNDELETE Terminal Handling THE TERMINAL AS A DEVICE
C00518 00141
C00523 00142
C00526 00143 SETEDIT TERMINAL MODE FUNCTIONS GTTYP, STTYP
C00529 00144 RFCOC, SFCOC RFMOD, SFMOD STPAR STI DATA TRANSFER
C00532 00145 INTTY PBTIN SUPPRESSING OUTPUT
C00536 00146
C00539 00147
C00541 00148 Utility TENEX System Calls CALL CNDIR DIRST, STDIR
C00544 00149 GJINF GTAD IDTIM, ODTIM PMAP
C00547 00150 RDSEG RUNPRG RUNTIM
C00550 00151 LEAP DATA TYPES Introduction
C00554 00152 Syntax
C00556 00153
C00558 00154 Semantics ITEM GENESIS SCOPE OF ITEMS DATUMS OF ITEMS
C00563 00155 ITEMVARS
C00568 00156 SETS AND LISTS
C00573 00157 ASSOCIATIONS PROCEDURES IMPLEMENTATION
C00578 00158
C00581 00159 LEAP STATEMENTS Syntax
C00583 00160
C00585 00161 Restrictions Semantics ASSIGNMENT STATEMENTS
C00589 00162 PUT REMOVE DELETE
C00594 00163 MAKE BRACKETED TRIPLE ITEMS
C00598 00164 ERASE Searching the Associative Store
C00602 00165 BINDING BOOLEANS
C00607 00166 DERIVED SETS BRACKETED TRIPLE ITEM RETRIEVAL THE FOREACH STATEMENT
C00611 00167
C00615 00168 THE LIST MEMBERSHIP <ELEMENT>
C00620 00169 THE BOOLEAN EXPRESSION <ELEMENT>
C00624 00170 THE RETRIEVAL TRIPLE <ELEMENT>
C00629 00171
C00633 00172 THE MATCHING PROCEDURE <ELEMENT>
C00639 00173
C00643 00174
C00644 00175 LEAP EXPRESSIONS Syntax
C00646 00176
C00648 00177 Semantics ITEM EXPRESSIONS
C00650 00178 NEW
C00655 00179 ANY BINDIT TYPES AGAIN SET AND LIST EXPRESSIONS
C00659 00180 DERIVED SETS BOOLEANS
C00663 00181 PNAMES PROPS
C00667 00182 BACKTRACKING Introduction Syntax
C00669 00183 Semantics THE CONTEXT DATA TYPE
C00672 00184 REMEMBER RESTORE
C00676 00185 FORGET IN!CONTEXT CONTEXT ELEMENTS
C00680 00186 PROCESSES Introduction Syntax Semantics STATUS OF A PROCESS
C00683 00187 SPROUTING A PROCESS
C00687 00188 SPROUT!DEFAULTS 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 REF!ITEM ARG!LIST
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 DEL!PNAME NEW!PNAME Other Useful Runtimes LISTX
C00810 00222 FIRST, SECOND, THIRD ISTRIPLE LOP COP
C00813 00223 LENGTH SAMEIV Runtimes for User Cause and Interrogate Procedures SETCP AND SETIP
C00817 00224 CAUSE1 ASKNTC
C00821 00225 ANSWER DFCPKT
C00824 00226 BASIC CONSTRUCTS Syntax
C00827 00227 Semantics VARIABLES DATUMS PROPS
C00832 00228 RECORD FIELDS IDENTIFIERS
C00837 00229 ARITHMETIC CONSTANTS STRING CONSTANTS COMMENTS
C00841 00230
C00842 00231 USING SAIL For TOPS-10 Beginners
C00846 00232 For TENEX Beginners The Complete use of Sail
C00850 00233 Compiling Sail Programs COMMAND LINE SYNTAX TOPS-10 COMMAND LINE SYNTAX
C00853 00234
C00855 00235 TENEX SAIL COMMAND LINE SYNTAX
C00857 00236 COMMAND LINE SEMANTICS
C00862 00237 SWITCHES
C00866 00238
C00871 00239
C00876 00240
C00880 00241 Loading Sail Programs Starting Sail Programs STARTING THE PROGRAM IN RPG MODE
C00885 00242 Storage Reallocation with REEnter
C00887 00243 DEBUGGING SAIL PROGRAMS Error Messages
C00891 00244 ERROR MODES
C00896 00245 STOPPING RUNAWAY COMPILATIONS EXECUTION TIME ERROR MESSAGES USER ERROR PROCEDURES
C00900 00246 Debugging
C00905 00247 SYMBOLS BLOCKS SAIL GENERATED SYMBOLS
C00909 00248 WARNINGS
C00913 00249 BAIL
C00919 00250 COMPILE-TIME ACTION
C00924 00251
C00929 00252 RUN-TIME ACTION DEBUGGING REQUESTS
C00934 00253 ARGS BREAK COORD
C00937 00254 DDT DEFINE HELP SETLEX
C00940 00255 SHOW TEXT TRACE TRAPS UNBREAK
C00943 00256 UNTRACE !!GO !!GSTEP !!STEP GOGTAB
C00946 00257 STRING TYPEOUT BLOCK STRUCTURE BAIL and DDT
C00950 00258 WARNINGS !!GOTO !!UP (LEVEL) !!QUERY SETSCOPE
C00955 00259 RESOURCES USED EXAMPLE
C00957 00260
C00959 00261
C00961 00262 CURRENT STATUS
C00963 00263
C00967 00264 APPENDICES Characters Stanford (SUAI) Character Set
C00970 00265
C00974 00266
C00975 00267 Sail Reserved Words
C00978 00268
C00981 00269
C00982 00270 Sail Predeclared Identifiers
C00985 00271 SUAI ONLY TOPS-10 ONLY CMU ONLY TYMSHARE ONLY TENEX ONLY
C00987 00272
C00988 00273 Indices for Interrupts
C00992 00274
C00994 00275
C00996 00276 Bit Names for Process Constructs SPROUT OPTIONS RESUME OPTIONS
C01000 00277 CAUSE OPTIONS INTERROGATE OPTIONS
C01003 00278 Statement Counter System GENERAL DISCUSSION HOW TO GET COUNTERS PROFILE PROGRAM
C01008 00279 SAMPLE RUN
C01012 00280
C01013 00281 Array Implementation
C01016 00282
C01018 00283 String Implementation STRING DESCRIPTORS INEXHAUSTIBLE STRING SPACE
C01022 00284
C01024 00285 Save/Continue
C01027 00286
C01029 00287 Procedure Implementation STACK FRAME
C01033 00288 ACCESSING THINGS ON THE STACK
C01037 00289 ACTIONS IN THE PROLOGUE FOR NON-SIMPLE PROCEDURES
C01040 00290 ACTIONS AT THE EPILOGUE FOR NON-SIMPLE PROCEDURES
C01043 00291 PROCEDURE DESCRIPTORS
C01046 00292
C01048 00293 REFERENCES
C01052 00294 INDEX
C01092 ENDMK
C;
Stanford Artificial Intelligence Laboratory August 1976
Memo AIM-289 Sailon 57.4
Computer Science Department
Report No. STAN-CS-76-574
SAIL
edited by
John F. Reiser
ABSTRACT
Sail is a high-level programming language for the PDP-10 computer. It
includes an extended ALGOL 60 compiler and a companion set of
execution-time routines. In addition to ALGOL, the language features: (1)
flexible linking to hand-coded machine language algorithms, (2) complete
access to the PDP-10 I/O facilities, (3) a complete system of compile-time
arithmetic and logic as well as a flexible macro system, (4) a high-level
debugger, (5) records and references, (6) sets and lists, (7) an
associative data structure, (8) independent processes (9) procedure
varaiables, (10) user modifiable error handling, (11) backtracking, and
(12) interrupt facilities.
This manual describes the Sail language and the execution-time routines
for the typical Sail user: a non-novice programmer with some knowledge of
ALGOL. It lies somewhere between being a tutorial and a reference manual.
This manual was supported by the Advanced Research Projects Agency under
Contract MDA 903-76-C-0206.
The views and conclusions contained in this document are those of the
author(s) and should not be interpreted as necessarily representing the
official policies, either expressed or implied, of Stanford University,
ARPA, NSF, or the United States Government.
We thank Bernard A. Goldhirsch and the Institute for Advancement of
Sailing for their kind permission to use the cover design of the August
1976 issue of SAIL magazine.
Reproduced in the U.S.A. Available from the National Technical
Information Service, Springfield, Virginia 22161.
PREFACE HISTORY OF THE LANGUAGE LEARNING ABOUT SAIL CHANGES IN THE LANGUAGE OPERATING SYSTEMS UNIMPLEMENTED CONSTRUCTS ACKNOWLEDGEMENTS
PREFACE
HISTORY OF THE LANGUAGE
The GOGOL III compiler, developed principally by Dan Swinehart at the
Stanford Artificial Intelligence Project, was the basis for the non-LEAP
portions of SAIL. Robert Sproull joined Swinehart in incorporating the
features of LEAP The first version of the language was released in November,
1969. SAIL's intermediate development was the responsibility of Russell
Taylor, Jim Low, and Hanan Samet, who introduced processes, procedure
variables, interrupts, contexts, matching procedures, a new macro system,
and other features. Most recently John Reiser, Robert Smith, and Russell
Taylor maintained and extended SAIL. They added a high-level debugger,
conversion to TENEX, a print statement, and records and references.
LEARNING ABOUT SAIL
A novice programmer (or one who is unfamiliar with ALGOL) should start with
the Sail Tutorial [SmithN]. An experienced programmer with a knowledge of
ALGOL should be able to use this Sail manual at once. Begin with Appendix
A, Characters; in this manual the symbol "!" designates the character with
code '030. For the first reading, a light skim of sections 1, 2, 3, 4, and
8, followed by a careful perusal of subsection 21.1 should be adequate to
familiarize the new user with the differences between ALGOL and SAIL and
allow him to start writing programs in SAIL. The other sections of this
manual are relatively self contained, and can be read when one wants to know
about the features they describe. The exceptions to this rule are sections
12, 13, and 14. These describe the basics of the LEAP and are essential for
understanding of the following sections.
Special effort has gone into making the index more comprehensive than in
previous versions of this manual. Please use it.
CHANGES IN THE LANGUAGE
There are no known incompatibilities at the SAIL source level with the
language described in [vanLehn]. PRINT, BAIL, operation under TENEX, and
records are major additions to the language. Significant revisions to
[vanLehn] or points deserving emphasis are marked by vertical bars in the
margin. This paragraph is so marked, as an example.
OPERATING SYSTEMS
Sail runs under several operating systems. In this manual distinction is
drawn between the operating system at the Stanford Artificial Intelligence
Laboratory (SUAI), the TOPS-10 operating system from Digital Equipment
Corporation, the TENEX operating system from Bolt Beranek and Newman, and
the TYMSHARE operating system. The major distinction is between TENEX and
non-TENEX systems, although the differences between SUAI and TOPS-10 are
also significant. The TOPS-20 operating system from Digital Equipment
Corporation is the same as TENEX as far as Sail is concerned. TENEX users
should substitute "<SAIL>" for "SYS:" wherever the latter appears in a file
name (except when talking to the LOADER).
UNIMPLEMENTED CONSTRUCTS
The following items are described in the manual as if they existed. As the
manual goes to press, they are not implemented.
1. NEW (<context!variable>). Creates a new item which has a datum that is
a context.
2. Using a <context!variable> instead of a list of variables in any of the
REMEMBER, FORGET or RESTORE statements.
3. Using in the expression n of REMOVE n FROM list.
4. ANYANYANY searches in Leap (searches where no constraints at all are
placed on the triple returned.)
5. CHECKED itemvars (the dynamic comparison of the datum type of an item to
the datum type of the CHECKED itemvar to which the item is being
assigned.) It is currently the user's responsibility to insure that the
type of the item agrees with the type of the itemvar whenever DATUM is
used.
ACKNOWLEDGEMENTS
Les Earnest and Robert Smith assisted the editor in PUB wizardry and reading
drafts.
TABLE OF CONTENTS
T A B L E O F C O N T E N T S
SECTION PAGE
1 PROGRAMS AND BLOCKS
1 Syntax 5
2 Semantics 6
2 ALGOL DECLARATIONS
1 Syntax 8
2 Restrictions 12
3 Examples 13
4 Semantics 14
5 Separately Compiled Procedures 25
3 ALGOL STATEMENTS
1 Syntax 28
2 Semantics 31
4 ALGOL EXPRESSIONS
1 Syntax 42
2 Type Conversion 45
3 Semantics 48
5 ASSEMBLY LANGUAGE STATEMENTS
1 Syntax 56
2 Semantics 57
6 INPUT/OUTPUT ROUTINES
1 Execution-time Routines in General 62
2 I/O Channels and Files 63
3 Break Characters 68
4 I/O Routines 72
5 TTY and PTY Routines 80
6 Example of TOPS-10 I/O 83
7 EXECUTION TIME ROUTINES
1 Type Conversion Routines 85
2 String Manipulation Routines 88
3 Liberation-from-Sail Routines 89
4 Byte Manipulation Routines 92
5 Other Useful Routines 93
6 Numerical Routines 95
8 PRINT
1 Syntax 98
2 Semantics 98
9 MACROS AND CONDITIONAL COMPILATION
1 Syntax 103
2 Delimiters 105
3 Macros 106
4 Macros with Parameters 110
5 Conditional Compilation 111
6 Type Determination at Compile Time 112
7 Miscelaneous Features 114
8 Hints 115
10 RECORD STRUCTURES
1 Introduction 117
2 Declaration Syntax 117
3 Declaration Semantics 117
4 Allocation 119
5 Fields 119
6 Garbage Collection 120
7 Internal Representations 120
8 Handler Procedures 122
9 More about Garbage Collection 124
11 TENEX ROUTINES
1 Introduction 126
2 TOPS-10 Style Input/Output 126
3 TENEX Style Input/Output 128
4 Terminal Handling 140
5 Utility TENEX System Calls 148
12 LEAP DATA TYPES
1 Introduction 151
2 Syntax 152
3 Semantics 154
13 LEAP STATEMENTS
1 Syntax 159
2 Restrictions 161
3 Semantics 161
4 Searching the Associative Store 164
14 LEAP EXPRESSIONS
1 Syntax 175
2 Semantics 177
15 BACKTRACKING
1 Introduction 182
2 Syntax 182
3 Semantics 183
16 PROCESSES
1 Introduction 186
2 Syntax 186
3 Semantics 186
4 Process Runtimes 192
17 EVENTS
1 Syntax 196
2 Introduction 196
3 Sail-defined Cause and Interrogate 197
4 User-defined Cause and Interrogate 199
18 PROCEDURE VARIABLES
1 Syntax 203
2 Semantics 203
19 INTERRUPTS
1 Introduction 207
2 Interrupt Routines 208
3 Immediate Interrupts 211
4 Clock Interrupts 214
5 Deferred Interrupts 215
20 LEAP RUNTIMES
1 Types and Type Conversion 218
2 Make and Erase Breakpoints 220
3 Pname Runtimes 220
4 Other Useful Runtimes 221
5 Runtimes for User Cause and Interrogate Procedures 223
21 BASIC CONSTRUCTS
1 Syntax 226
2 Semantics 227
22 USING SAIL
1 For TOPS-10 Beginners 231
2 For TENEX Beginners 232
3 The Complete use of Sail 232
4 Compiling Sail Programs 233
5 Loading Sail Programs 241
6 Starting Sail Programs 241
7 Storage Reallocation with REEnter 242
23 DEBUGGING SAIL PROGRAMS
1 Error Messages 243
2 Debugging 246
3 BAIL 249
APPENDICES
A Characters 264
B Sail Reserved Words 267
C Sail Predeclared Identifiers 270
D Indices for Interrupts 273
E Bit Names for Process Constructs 276
F Statement Counter System 278
G Array Implementation 281
H String Implementation 283
I Save/Continue 285
J Procedure Implementation 287
REFERENCES 293
INDEX 294
PROGRAMS AND BLOCKS Syntax
SECTION 1
PROGRAMS AND BLOCKS
1.1 Syntax
<program>
::= <block>
<block>
::= <block!head> ; <compound!tail>
<block!head>
::= BEGIN <declaration>
::= BEGIN <block!name> <declaration>
::= <block!head> ; <declaration>
<compound!tail>
::= <statement> END
::= <statement> END <block!name>
::= <statement> ; <compound!tail>
<compound!statement>
::= BEGIN <compound!tail>
::= BEGIN <block!name> <compound!tail>
<statement>
::= <block>
::= <compound!statement>
::= <require!specification>
::= <assignment>
::= <swap!statement>
::= <conditional!statement>
::= <if!statement>
::= <go!to!statement>
::= <for!statement>
::= <while!statement>
::= <do!statement>
::= <case!statement>
::= <print!statement>
::= <return!statement>
::= <done!statement>
::= <next!statement>
Semantics DECLARATIONS STATEMENTS BLOCK NAMES
::= <continue!statement>
::= <procedure!statement>
::= <safety!statement>
::= <backtracking!statement>
::= <code!block>
::= <leap!statement>
::= <process!statement>
::= <event!statement>
::= <string!constant> <statement>
::= <label!identifier> : <statement>
::= <empty>
1.2 Semantics
DECLARATIONS
Sail programs are organized in the traditional block structure of ALGOL-60
[Nauer].
Declarations serve to define the data types and dimensions of simple and
subscripted (array) variables (arithmetic variables, strings, sets, lists,
record pointers, and items). They are also used to describe procedures
(subroutines) and record classes, and to name program labels.
Any identifier referred to in a program must be described in some
declaration. An identifier may only be referenced by statements within the
scope (see page 14) of its declaration.
STATEMENTS
As in ALGOL, the statement is the fundamental unit of operation in the Sail
language. Since a statement within a block or compound statement may itself
be a block or compound statement, the concept of statement must be
understood recursively.
The block representing the program is known as the "outer block". All
blocks internal to this one will be referred to as "inner blocks".
BLOCK NAMES
The block name construct is used to describe the block structure of a Sail
program to a symbolic debugging routine (see page 246). The name of the
outer block becomes the title of the binary output file (not necessarily the
file name). In addition, if a block name is used following an END then the
compiler compares it with the block name which followed the corresponding
BEGIN. A mismatch is reported to the user as evidence of a missing (extra)
BEGIN or END somewhere.
The <string!constant> <statement> construct is equivalent in action to the
<statement> alone; that is, the string constant serves only as a comment.
EXAMPLES
EXAMPLES
Given:
S is a statement,
Sc is a Compound Statement,
D is a Declaration,
B is a Block.
Then:
(Sc) BEGIN S; S; S; ... ; S END
(Sc) BEGIN "SORT" S; S; ... ;S END "SORT"
(B) BEGIN D; D; D; ... ; S; S; S; ... ; S END
(B) BEGIN "ENTER NEW INFO" D; D; ... ;
S; ... ;S END
are syntactically valid Sail constructs.
ALGOL DECLARATIONS Syntax
SECTION 2
ALGOL DECLARATIONS
2.1 Syntax
<id!list>
::= <identifier>
::= <identifier> , <id!list>
<declaration>
::= <type!declaration>
::= <array!declaration>
::= <preload!specification>
::= <label!declaration>
::= <procedure!declaration>
::= <synonym!declaration>
::= <require!specification>
::= <context!declaration>
::= <leap!declaration>
::= <record!class!declaration>
::= <protect!acs declaration>
::= <cleanup!declaration>
::= <type!qualifier> <declaration>
<simple!type>
::= BOOLEAN
::= INTEGER
::= REAL
::= RECORD!POINTER ( <classid!list> )
::= STRING
<type!qualifier>
::= EXTERNAL
::= FORTRAN
::= FORWARD
::= INTERNAL
::= OWN
::= RECURSIVE
::= SAFE
::= SHORT
::= SIMPLE
<type!declaration>
::= <simple!type> <id!list>
::= <type!qualifier> <type!declaration>
<array!declaration>
::= <simple!type> ARRAY <array!list>
::= <type!qualifier> <array declaration>
<array!list>
::= <array!segment>
::= <array!list> , <array!segment>
<array!segment>
::= <id!list> [ <bound!pair!list> ]
<bound!pair!list>
::= <bound!pair>
::= <bound!pair!list> , <bound!pair>
<bound!pair>
::= <lower!bound> : <upper!bound>
<lower!bound>
::= <algebraic!expression>
<upper!bound>
::= <algebraic!expression>
<preload!specification>
::= PRELOAD!WITH <preload!list>
::= PRESET!WITH <preload!list>
<preload!list>
::= <preload!element>
::= <preload!list> , <preload!element>
<preload!element>
::= <expression>
::= [expression] <expression>
<label!declaration>
::= LABEL <id!list>
<procedure!declaration>
::= PROCEDURE <identifier> <procedure!head>
<procedure!body>
::= <simple!type> PROCEDURE <identifier>
<procedure!head> <procedure!body>
::= <type!qualifier> <procedure!declaration>
<procedure!head>
::= <empty>
::= ( <formal!param!decl> )
<procedure!body>
::= <empty>
::= ; <statement>
<formal!param!decl>
::= <formal!parameter!list>
::= <formal!parameter!list> ;
<formal!param!decl>
<formal!parameter!list>
::= <formal!type> <id!list>
::= <formal!type> <id!list> (
<default!value> )
<formal!type>
::= <simple!formal!type>
::= REFERENCE <simple!formal!type>
::= VALUE <simple!formal!type>
<simple!formal!type>
::= <simple!type>
::= <simple!type> ARRAY
::= <simple!type> PROCEDURE
<synonym!declaration>
::= LET <synonym!list>
<synonym!list>
::= <synonym>
::= <synonym!list> , <synonym>
Restrictions
<synonym>
::= <identifier> = <reserved!word>
<cleanup!declaration>
::= CLEANUP <procedure!identifier!list>
<require!specification>
::= REQUIRE <require!list>
<require!list>
::= <require!element>
::= <require!list> , <require!element>
<require!element>
::= <constant!expression> <require!spec>
::= <procedure!name> INITIALIZATION
::= <procedure!name> INITIALIZATION [
<phase> ]
<require!spec>
::= STRING!SPACE
::= SYSTEM!PDL
::= STRING!PDL
::= ITEM!START
::= NEW!ITEMS
::= PNAMES
::= LOAD!MODULE
::= LIBRARY
::= SOURCE!FILE
::= SEGMENT!FILE
::= SEGMENT!NAME
::= POLLING!INTERVAL
::= POLLING!POINTS
::= VERSION
::= ERROR!MODES
::= DELIMITERS
::= NULL!DELIMITERS
::= REPLACE!DELIMITERS
::= UNSTACK!DELIMITERS
::= BUCKETS
::= MESSAGE
::= COMPILER!SWITCHES
2.2 Restrictions
For simplicity, the type!qualifiers 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 <constant!expression>
for <algebraic!expression> in the productions for <lower!bound> and
<upper!bound>.
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.
(<type!declaration>)
INTEGER I, J, K
EXTERNAL REAL X, Y
INTERNAL STRING K
(<array!declaration>)
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!declaration>)
LABEL L, X, Y
(<procedure declaration>)
PROCEDURE P; S
PROCEDURE P (INTEGER I, J;
REFERENCE REAL X; REAL Y); S
INTEGER PROCEDURE P (REAL PROCEDURE L;
STRING I, J; INTEGER ARRAY K); S
EXTERNAL PROCEDURE P (REAL X)
FORWARD INTEGER PROCEDURE X (INTEGER I)
Note that these sample declarations are all given without the semicolons
which would normally separate them from the surrounding declarations and
statements. Here is a sample block to bring it all together (again, let S
be any statement, D any declaration, and other identifiers as above):
Semantics SCOPE OF DECLARATIONS TYPE QUALIFIERS
BEGIN "SAMPLE BLOCK"
INTEGER I, J, K;
REAL X, Y;
STRING A;
INTEGER PROCEDURE P (REFERENCE REAL X);
BEGIN "P"
D; D; D; ... ;S; ... ; S
END "P";
REAL ARRAY DIPHTHONGS [0:10, 1:100];
S; S; S; S
END "SAMPLE BLOCK"
2.4 Semantics
SCOPE OF DECLARATIONS
Every block automatically introduces a new level of nomenclature. Any
identifier declared in a block's head is said to be LOCAL to that block.
This means that:
a. The entity represented by this identifier inside the block has no
existence outside the block.
b. Any entity represented by the same identifier outside the block is
completely inaccessible (unless it has been passed as a parameter)
inside the block.
An identifier occurring within an inner block and not declared within that
block will be nonlocal (global) to it; that is, the identifier will
represent the same entity inside the block and in the block or blocks within
which it is nested, up to and including the level in which the identifier is
declared.
The Scope of an entity is the set of blocks in which the entity is
represented, using the above rules, by its identifier. An entity may not be
referenced by any statement outside its scope.
TYPE QUALIFIERS
An array, variable, or procedure declared OWN will behave as if it were
declared globally to the current procedure; the OWN type qualifier on a
variable, etc. declared in a block not nested inside a procedure declaration
will have no effect. This means that in a second call of a procedure with
OWN locals (or a recursive call)
NUMERIC DECLARATIONS STRING DECLARATIONS ARRAY DECLARATIONS
the OWN variables will not be reinitialized; they will have the values that
they had when the first call of the procedure finished. Furthermore, OWN
arrays, etc. will not be deallocated upon exiting the procedure in which
they are declared.
INTERNAL and EXTERNAL procedures, variables, etc. let one link programs
that are loaded together but were compiled separately. See page 25 for
more information.
RECURSIVE, SHORT, FORTRAN, FORWARD, SIMPLE, and SAFE will be explained when
the data types they modify are discussed.
NUMERIC DECLARATIONS
Identifiers which appear in type declarations with types REAL or INTEGER can
subsequently be used to refer to numeric variables. An Integer variable may
take on values from -2^35 to 2^35-1 (-2^26 to 2^26-1 for SHORT INTEGERS).
A Real variable may take on positive and negative values from about 10^-38
to 10^38 with a precision of 27 bits (same range for SHORT REALs as for
SHORT INTEGERs). REAL and INTEGER variables (and constants) may be used in
the same arithmetic expressions; type conversions are carried out
automatically (see page 46) when necessary.
The advantage of SHORT reals and integers is that the conversion from
integer to real is sped by a factor of 8 if either the integer or the real
is SHORT. See page 46 for more information.
The BOOLEAN type is identical to INTEGER. BOOLEAN and algebraic expressions
are really equivalent syntactically. The syntactic context in which they
appear determines their meaning. Non-zero integers correspond to TRUE and 0
corresponds to FALSE. The declarator BOOLEAN is included for program
clarity.
STRING DECLARATIONS
A variable defined in a String declaration is a two-word descriptor
containing the information necessary to represent a Sail character string.
A String may be thought of as a variable-length, one-dimensional array of 7-
bit ASCII characters. Its descriptor contains a character count and a byte
pointer to the first character (see page 283). Strings originate as
constants at compile time (page 229), as the result of a String INPUT
operation from some device (see page 72), or from the concatenation or
decomposition of already existing strings (see page 53).
When strings appear in arithmetic operations or vice-versa, a somewhat
arbitrary conversion is performed to obtain the proper type (by arbitrary we
do not mean to imply random -- see page 46). For this reason arithmetic,
String, and Record!pointer variables are referred to as "algebraic
variables" and their corresponding expressions are called "algebraic
expressions" (to differentiate them them from the variables and expressions
of LEAP -- see page 151).
PRELOAD SPECIFICATIONS
ARRAY DECLARATIONS
In general, any data type which is applicable to a simple variable may be
applied in an Array declaration to an array of variables. The entity
represented by the name of an Array, qualified with subscript expressions to
locate a particular element (e.g. A[I, J]) behaves in every way like a
simple variable. Therefore, in the future we shall refer to both simple
variables and single elements of Arrays (subscripted variables) as
"variables". The formal syntax for <variable> can be found on page 227.
For an Array which is not qualified by the SAFE attribute, nor had a
NOW!SAFE statement done on it (Now!Safe - 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
<preload!specification>. This specification gives the values which are to
be placed in consecutive core locations of the Arrays declared immediately
following the <preload!specification>. "Immediately", in this case, means
all identifiers up to and including one which is followed by bound!pair!list
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:
PRELOAD!WITH [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
PRESET!WITH is just like PRELOAD!WITH 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 START!CODE 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. RECORD!POINTERs may not be declared or passed as arguments to
other procedures, and the code must not cause the compiler to
create RECORD!POINTER 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, Record!pointer,
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 -- STRING!SPACE, SYSTEM!PDL, etc.
The inclusion of the specification "REQUIRE 1000 STRING!SPACE" 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 -- LOAD!MODULE, LIBRARY, SOURCE!FILE, etc.
The inclusion of the specification REQUIRE "PROCS1" LOAD!MODULE,
"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 LOAD!MODULE and LIBRARY file names to 6 characters in the main
name and 3 characters in the extension.
LOAD!MODULES (.REL files to be loaded) may themselves contain requests for
other LOAD!MODULES and LIBRARYs. LIBRARYs may only contain requests for
other LIBRARYs. The LOADER may do strange things with files requested
twice.
Sail automatically places a request for the library SYS:LIBSAn (<SAIL>LIBSAn
on TENEX) [HLBSAn for /H compilations] in each main program, where n is the
version number of the current Sail library of runtime routines.
The inclusion of REQUIRE "PREAMB.SAI" SOURCE!FILE 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 SOURCE!FILE request must be followed by a semicolon (only
one per REQUIREment), and must be the last text on the line in which it
appears. SOURCE!FILE switching must not be specified from within a DEFINE
body (see page 106). SOURCE!FILEs may be nested to a depth of about 10
levels.
The SEGMENT!NAME, SEGMENT!FILE 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 procedure!name 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
<assignment!statement>
::= <algebraic!variable> _
<algebraic!expression>
<swap!statement>
::= <variable> <variable>
::= <variable> SWAP <variable>
<conditional!statement>
::= <if!statement>
::= <if!statement> ELSE <statement>
<if!statement>
::= IF <boolean!expression> THEN <statement>
<go!to!statement>
::= GO TO <label!identifier>
::= GOTO <label!identifier>
::= GO <label!identifier>
<label!identifier>
::= <identifier>
<for!statement>
::= FOR <algebraic!variable> _ <for!list>
DO <statement>
::= NEEDNEXT <for!statement>
<for!list>
::= <for!list!element>
::= <for!list> , <for!list!element>
<for!list!element>
::= <algebraic!expression>
::= <algebraic!expression> STEP
<algebraic!expression> UNTIL
<algebraic!expression>
::= <algebraic!expression> STEP
<algebraic!expression> WHILE
<boolean!expression>
<while!statement>
::= WHILE <boolean!expression> DO
<statement>
::= NEEDNEXT <while!statement>
<do!statement>
::= DO <statement> UNTIL
<boolean!expression>
<case!statement>
::= <case!statement!head> <statement!list>
<case!statement!tail>
::= <case!statement!head>
<numbered!state!list>
<case!statement!tail>
<case!statement!head>
::= CASE <algebraic!expression> OF BEGIN
::= CASE <algebraic!expression> OF BEGIN
<block!name>
<case!statement!tail>
::= END
::= END <block!name>
<statement!list>
::= <statement>
::= <statement!list> ; <statement>
<numbered!state!list>
::= [ <integer!constant> ] <statement>
::= [ <integer!constant> ]
<numbered!state!list>
::= <numbered!state!list> ; [
<integer!constant> ] <statement>
<return!statement>
Semantics
::= RETURN
::= RETURN ( <expression> )
<done!statement>
::= DONE
::= DONE <block!name>
<next!statement>
::= NEXT
::= NEXT <block!name>
<continue!statement>
::= CONTINUE
::= CONTINUE <block!name>
<procedure!statement>
::= <procedure!call>
<procedure!call>
::= <procedure!identifier>
::= <procedure!identifier> (
<actual!parameter!list> )
<actual!parameter!list>
::= <actual!parameter>
::= <actual!parameter!list> ,
<actual!parameter>
<actual!parameter>
::= <expression>
::= <array!identifier>
::= <procedure!identifier>
<safety!statement>
::= NOW!SAFE <id!list>
::= NOW!UNSAFE <id!list>
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 GIANTS!ON!TV THEN BEGIN
PHONE!EXCUSE ("GRANDMOTHER DIED");
ENJOY (GAME);
SUFFER (CONSCIENCE!PANGS)
END
ELSE IF REALLY!SICK THEN BEGIN
PHONE!EXCUSE ("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 COWS!COME!HOME;
...
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);
PROCESS!INPUT(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
NOW!SAFE and NOW!UNSAFE
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]).
NOW!SAFE and NOW!UNSAFE
The NOW!SAFE and NOW!UNSAFE statements both take a list of Array names
(names only - no indices) following them. From a NOW!SAFE until the end of
the program or the next NOW!UNSAFE, the specified arrays will not have
bounds checking code emitted for them. If an array has had a NOW!SAFE done
on it, or has been declared SAFE, NOW!UNSAFE will cause bounds checking code
to be emitted until the array is made safe again (if ever). Note that
NOW!SAFE and NOW!UNSAFE are compile time statements. "IF BE THEN NOW!SAFE
..." will not work.
ALGOL EXPRESSIONS Syntax
SECTION 4
ALGOL EXPRESSIONS
4.1 Syntax
<expression>
::= <simple!expression>
::= <conditional!expression>
::= <assignment!expression>
::= <case!expression>
<conditional!expression>
::= IF <boolean!expression> THEN
<expression> ELSE <expression>
<assignment!expression>
::= <variable> _ <expression>
<case!expression>
::= CASE <algebraic!expression> OF (
<expression!list> )
<expression!list>
::= <expression>
::= <expression!list> , <expression>
<simple!expression>
::= <algebraic!expression>
::= <leap!expression>
<boolean!expression>
::= <expression>
<algebraic!expression>
::= <disjunctive!expression>
::= <algebraic!expression>
<disjunctive!expression>
::= <algebraic!expression> OR
<disjunctive!expression>
<disjunctive!expression>
::= <negated!expression>
::= <disjunctive!expression>
<negated!expression>
::= <disjunctive!expression> AND
<negated!expression>
<negated!expression>
::= <relational!expression>
::= NOT <relational!expression>
::= <relational!expression>
<relational!expression>
::= <algebraic!relational>
::= <leap!relational>
<algebraic!relational>
::= <bounded!expression>
::= <relational!expression>
<relational!operator>
<bounded!expression>
<relational!operator>
::= <
::= >
::= =
::=
::=
::=
::= LEQ
::= GEQ
::= NEQ
<bounded!expression>
::= <adding!expression>
::= <bounded!expression> MAX
<adding!expression>
::= <bounded!expression> MIN
<adding!expression>
<adding!expression>
::= <term>
::= <adding!expression> <add!operator>
<term>
<adding!operator>
::= +
::= -
::= LAND
::= LOR
::= EQV
::= XOR
<term>
::= <factor>
::= <term> <mult!operator> <factor>
<mult!operator>
::= *
::= /
::= %
::= LSH
::= ASH
::= ROT
::= MOD
::= DIV
::= &
<factor>
::= <primary>
::= <primary> ^ <primary>
<primary>
::= <algebraic!variable>
::= - <primary>
::= LNOT <primary>
::= ABS <primary>
::= <string!expression> [ <substring!spec> ]
::=
::= INF
::= <constant>
::= <function!designator>
::= LOCATION ( <loc!specifier> )
::= ( <algebraic!expression> )
<string!expression>
::= <algebraic!expression>
<substring!spec>
Type Conversion
::= <algebraic!expression> TO
<algebraic!expression>
::= <algebraic!expression> FOR
<algebraic!expression>
<function!designator>
::= <procedure!call>
<loc!specifier>
::= <variable>
::= <array!identifier>
::= <procedure!identifier>
::= <label!identifier>
<algebraic!variable>
::= <variable>
4.2 Type Conversion
Sail automatically converts between the data types Integer, Real, String and
Boolean. The following table illustrates by description and example these
conversions. The data type boolean is identical to integer under the
mapping TRUE0 and FALSE=0.
F |To
r |
o | INTEGER REAL STRING
m!|!
I | | Left justify | Make a string
N | | and raise to | of 1 character
T | | appropriate | with the low
E | | power. | 7 bits for its
G | | 13451.345@3 | ASCII code.
E | | -678-6.78@2 | 48 "0"
R!|!|!|!
| | |
R | Take greatest | | Convert to in-
E | integer. | | teger, then to
A | 1.345@2 134 | | string.
L | -6.71@1 -68 | | 4.8@1 "0"
| 2.3@-2 0 | | 4.899@1 "0"
|!|!|!
| The ASCII code| Convert to in-|
S | for the first | teger then |
T | character of | to real. |
R | string. | |
I | "0SUM" 48 | "0SUM" 4.8@1 |
N | NULL 0 | NULL 0 |
G!|!|!|!
NOTES: The NULL string is converted to 0, but 0 is converted to the one
character string with the ASCII code of 0. If an integer requires more than
27 bits of precision (2^27 = 134217728) then some low order significance
will be lost in the conversion to real; otherwise, conversion to real and
then back to integer will result in the same integer value. If a real
number has magnitude greater than 2^35 - 2^8 (=34359738112) then conversion
to integer will produce an invalid result. UUOFIX does no error checking
for this case; KIFIX and FIXR will set Overflow and Trap 1.
The default instruction compiled for a real to integer conversion is a UUO
which computes FLOOR (x), the greatest integer function. This can be
changed with the /A switch (page 237) to one of several other instructions.
For real to integer conversion the choices are UUOFIX(opcode 003),
KIFIX(122) and FIXR(126); the effect of each is shown in the following
table.
real UUOFIX KIFIX FIXR
1.4 1 1 1
1.5 1 1 2
1.6 1 1 2
-1.4 -2 -1 -1
-1.5 -2 -1 -1
-1.6 -2 -1 -2
UUOFIX is the default. In mathematical terms, UUOFIX (x)=FLOOR (x)=[x]
where [x] is the traditional notation for the greatest integer less than or
equal to x. This UUO requires execution of 18.125 instructions (32 memory
references) on the average. Many FORTRANs use the function implemented by
KIFIX; KIFIX (x)=SIGN (x)*FLOOR (ABS (x)). Many ALGOLs use FIXR;
FIXR (x)=FLOOR (x+0.5). Note that FIXR (-1.5) is not equal to -FIXR (1.5).
For integer to real conversion the choices are UUOFLOAT(002) and FLTR(127).
FLTR rounds while UUOFLOAT (the default) truncates. It only makes a
difference when the magnitude of the integer being converted is greater than
134217728. In such cases it is always true that UUOFLOAT (i)i and
FLTR (i)i. UUOFLOAT merely truncates after normalization, while FLTR adds
+0.5 lsb and then truncates. Most users will never see the difference.
UUOFLOAT takes 18.625 instructions (32 memory references) on the average.
[For integer to real conversion involving a SHORT quantity, FSC ac,233 is
used. At SUAI real to integer conversion involving a SHORT quantity uses
KAFIX ac,233000; as this manual went to press KAFIX was simulated by the
system and was very expensive.]
The binary arithmetic, logical, and String operations which follow will
accept combinations of arguments of any algebraic types. The type of the
result of such an operation is sometimes dependent on the type of its
arguments and sometimes fixed. An argument may be converted to a different
algebraic type before the operation is performed. The following table
describes the results of the arithmetic and logical operations given various
combinations of Real and Integer inputs. ARG1 and ARG2 represent the types
of the actual arguments. ARG1' and ARG2' represent the types of the
arguments after any necessary conversions have been made.
Beware: automatic type conversion can be a curse as well as a blessing.
Study the conversion rules carefully; note that Sail has three division
operators, %, DIV, and /.
Semantics CONDITIONAL EXPRESSIONS
OPERATIONARG1 ARG2 ARG1'ARG2'RESULT
+ - INT INT INT INT INT*
* ^ % REAL INT REAL REAL REAL
MAX MIN INT REAL REAL REAL REAL
REAL REAL REAL REAL REAL
LAND LOR INT INT INT INT INT
EQV XOR REAL INT REAL INT REAL
INT REAL INT REAL INT
REAL REAL REAL REAL REAL
LSH ROT INT INT INT INT INT
ASH REAL INT REAL INT REAL
INT REAL INT INT INT
REAL REAL REAL INT REAL
/ INT INT REAL REAL REAL
REAL INT REAL REAL REAL
INT REAL REAL REAL REAL
REAL REAL REAL REAL REAL
MOD DIV INT INT INT INT INT
REAL INT INT INT INT
INT REAL INT INT INT
REAL REAL INT INT INT
* For the operator ^, ARG2' and RESULT are REAL unless ARG2 is a positive
integer constant.
4.3 Semantics
CONDITIONAL EXPRESSIONS
A conditional expression returns one of two possible values depending on the
logical truth value of the Boolean expression. If the Boolean expression
(BE) is true, the value of the conditional expression is the value of the
expression following the delimiter THEN. If BE is false, the other value is
used. If both expressions are of an algebraic type, the precise type of the
entire conditional expression is that of the "THEN part". In particular,
the "ELSE part" will be converted to the type of the "THEN part" before
being returned as the value of the conditional expression. Reread and
understand the last sentence.
Unlike the nested If statement problem, there can be no ambiguity for
conditional expressions, since there is an ELSE part in every such
expression. Example:
ASSIGNMENT EXPESSIONS CASE EXPRESSIONS SIMPLE EXPRESSIONS
FOURTHDOWN (YARDSTOGO,YARDLINE,
IF YARDLINE < 70 THEN PUNT ELSE
IF YARDLINE < 90 THEN FIELDGOAL ELSE
RUNFORIT)
ASSIGNMENT EXPESSIONS
The somewhat weird syntax for an assignment expression (it is equivalent to
that for an assignment statement) is nonetheless accurate: the two function
identically as far as the new value of the left part variable is concerned.
The difference is that the value of this left part variable is also retained
as the value of the entire expression. Assuming that the assignment itself
is legal (following the rules given in page 31 above), the type of the
expression is that of the left part variable. This variable may now
participate in any surrounding expressions as if it had been given its new
value in a separate statement on the previous line. Only the _ operator is
valid in assignment expressions. The operator is valid only at statement
level. Example:
IF (K_K+1) < 30 THEN K_0 ELSE K_K+1;
CASE EXPRESSIONS
The expression
CASE AE OF (E0, E1, E2, ... , En)
is equivalent to:
IF AE=0 THEN E0
ELSE IF AE=1 THEN E1
ELSE IF AE=2 THEN E2
...
ELSE IF AE=n THEN En
ELSE ERROR
The type of the entire expression is therefore that of E0. If any of the
expressions E1 ... En cannot be fit into this mold an error message is
issued by the compiler. Case expressions differ from Case statements in
that one may not use the [n] construct to number the expressions. Example:
OUT (TTY, CASE ERRNO OF ("BAD DIRECTORY",
"IMPROPER DATA MODE",
"UNKNOWN I/O ERROR",
...
"COMPUTER IN BAD MOOD"));
SIMPLE EXPRESSIONS
Simple expressions are simple only in that they are not conditional, case,
or assignment expressions. There are in fact some exciting complexities to
be discussed with respect to simple expressions.
PRECEDENCE OF ALGEBRAIC OPERATORS EXPRESSION EVALUATION RULES OR
PRECEDENCE OF ALGEBRAIC OPERATORS
The binary operators in Sail generally follow "normal" precedence rules.
That is, exponentiations are performed before multiplications or divisions,
which in turn are performed before additions and subtractions, etc. The
bounding operators MAX and MIN are performed after these operations. The
logical connectives and , when they occur, are performed last ( before
). The order of operation can be changed by including parentheses at
appropriate points.
In an expression where several operators of the same precedence occur at the
same level, the operations are performed from left to right. See page 50
for special evaluation rules for logical connectives.
TABLE OF PRECEDENCE
^
* / % & MOD DIV LSH ROT ASH
+ - LAND LOR
MAX MIN
= < > LEQ GEQ NEQ
AND
OR
EXPRESSION EVALUATION RULES
Sail does not evaluate expressions in a strictly left-to-right fashion. If
we are not constrained to a left-to-right evaluation, (as is ALGOL 60), we
can in some cases produce considerably better code than a strict left-to-
right scheme could achieve. Intuitively, the essential features (and
pitfalls) of this evaluation rule can be illustrated by a simple example:
b _ 2.6 ;
c _ b + (b _ b/2);
The second statement is executed as follows: divide b by 2 and assign this
value (1.3) to b. Add this value to b and assign the sum to c. Thus c gets
2.6. If the expressions were evaluated in a strictly left-to-right manner,
c would get 2.6 + 1.3.
The evaluation scheme can be stated quite simply: code is generated for the
operation represented by a BNF production when the reduction of that BNF
production takes place. That is, b + (b _ b/2) isn't reduced until after
(b _ b/2) is reduced, so the smaller expression gets done first.
"" (OR)
If an algebraic expression has as its major connective the logical
connective "", the expression has the logical value TRUE (arithmetic value
some non-zero integer) if either of its conjuncts (the expressions
surrounding the "") is true; FALSE otherwise. The reserved word OR is
equivalent to the symbol "". AB does NOT produce the bit-wise Or of A
and B if they are algebraic expressions. Truth values combined by numeric
AND NOT <>= RELATIONS
operators will in general be meaningless (use the operators LOR and LAND for
bit operations).
The user should be warned that in an expression containing logical
connectives, only enough of the expression is evaluated (from left to right)
to uniquely determine its truth value. Thus in the expression
(J<3 (K_K+1) > 0),
K will not be incremented if J is less than 3 since the entire expression is
already known to be true. Conversely in the expression
(X 0 SQRT(X)>2)
there is never any danger of attempting to extract the square root of a
negative X, since the failure of the first test testifies to the falsity of
the entire expression -- the SQRT routine is not even called in this case.
"" (AND)
If a disjunctive expression has as its major connective the logical
connective "", the expression has the logical value TRUE if both of its
disjuncts are TRUE; FALSE otherwise. Again, if the first disjunct is FALSE
a logical value of FALSE is obtained for the entire expression without
further evaluation. The reserved word AND is equivalent to "".
"" (NOT)
The unary Boolean operator applied to an argument BE (a relational
expression, see Syntax) has the value TRUE if BE is false, and FALSE if BE
is true. Notice that A is not the bitwise complement of A, if A is an
algebraic value. If used as an algebraic value, A is simply 0 if A0 and
some non-zero Integer otherwise. The reserved word NOT is equivalent to
"".
"<>=" (RELATIONS)
If any of the binary relational operators is encountered, code is produced
to convert any String arguments to Integer numbers. Then type conversion is
done as it is for the + operations (see page 46). The values thus obtained
are compared for the indicated condition. A Boolean value TRUE or FALSE is
returned as the value of the expression. Of course, if this expression is
used in subsequent arithmetic operations, a conversion to integer is
performed to obtain an integer value. The reserved words LEQ, GEQ, NEQ are
equivalent to "", "", "" respectively.
The syntax E1 RELOP1 E2 RELOP2 E3 where E1, E2, and E3 are expressions and
RELOP1, RELOP2 are relational operators, is specially interpreted as (E1
RELOP1 (T_E2)) (T RELOP2 E3). The compiler can sometimes produce better
code when the special syntax is used. Thus a bounds check may be written IF
L<I<U THEN ... . RELOP1 and RELOP2 may be any relational operators, and need
not be in transitive order. The following are equivalent:
MAX MIN +- ADDITION SUBTRACTION LAND LOR XOR EQV LNOT */% MULTIPLICATION DIVISION DIV MOD ASH LSH ROT
IF A < X > B THEN ... and
IF X > (A MAX B) THEN ...
MAX MIN
A MAX B (where A and B are appropriate expressions -- see the Syntax) has
the value of the larger of A and B (in the algebraic sense). Type
conversions are performed as if the operator were `+'. `0 MAX X MIN 10' is
X if 0X10, 0 if X<0, 10 if X>10.
"+-" (ADDITION AND SUBTRACTION)
The + and - operators will do integer addition (subtraction) if both
arguments are integers (or converted to integers from strings); otherwise,
rounded Real addition or subtraction, after necessary conversions, is done.
LAND LOR XOR EQV LNOT
LAND, LOR, XOR, and EQV carry out bit-wise And, Or, Exclusive Or, and
Equivalence operations on their arguments. No type conversions are done
for these functions. The logical connectives and do not have this
effect -- they simply cause tests and jumps to be compiled. The type of the
result is that of the first operand. This allows expressions of the form X
LAND '777777777, where X is Real, if they are really desired.
The unary operator LNOT produces the bitwise complement of its (algebraic)
argument. No type conversions (except strings to integers) are performed on
the argument. The type of the result (meaningful or not) is the type of the
argument.
"*/%" (MULTIPLICATION AND DIVISION)
The operation * (multiplication), like + and -, represents Integer
multiplication only if both arguments are integers; Real otherwise. Integer
multiplication uses the IMUL machine instruction -- no double-length result
is available.
The / operator (division) always does rounded Real division, after
converting any Integer arguments to Real.
The % (division) operator has the same type table as +, -, and *. It
performs whatever division is appropriate.
DIV MOD
DIV and MOD force both arguments to be integers before dividing. X MOD Y is
the remainder after X DIV Y is performed:
X MOD Y = X - (X DIV Y)*Y .
ASH LSH ROT
LSH and ROT provide logical shift operations on their first arguments. If
the value of the second argument is positive, a shift or rotation of that
many bits to the left is performed. If it is negative, a right-shift or
& CONCATENATION ^ EXPONENTIATION SUBSTRINGS
rotate is done. ASH does an arithmetic shift. Assume that A is an integer.
If N is positive then the expression A ASH N is equal to A * 2^N. If N is
negative then A ASH N is equal to FLOOR (A / 2^(-N)).
"&" (CONCATENATION)
This operator produces a result of type String. It is the String with
length the sum of the lengths of its arguments, containing all the
characters of the second string concatenated to the end of all the
characters of the first. The operands will first be converted to strings if
necessary as described in page 46 above. Numbers can be converted to
strings representing their external forms (and vice-versa) through explicit
calls on execution time routines like CVS and CVD (see page 86 below).
NOTE: Concatenation of constant strings will be done at compile time where
possible. For example, if SS is a string variable, SS&'12&'15 will result
in two runtime concatenations, while SS&('12&'15) will result in one compile
time concatenation and one runtime concatenation.
"^" (EXPONENTIATION)
A factor is either a primary or a primary raised to a power represented by
another primary. As usual, evaluation is from left to right, so that A^B^C
is evaluated as (A^B)^C. In the factor X^Y, a suitable number of
multiplications and additions is performed to produce an "exact" answer if Y
is a positive integer. Otherwise a routine is called to approximate
ANTILOG (Y LOG X). The result has the type of X in the former case. It is
always of type Real in the latter.
SUBSTRINGS
A String primary which is qualified by a substring specification represents
a part of the specified string. The characters of a string STR are numbered
1, 2, 3, ..., LENGTH (STR). ST[X FOR Y] represents the substring which is Y
characters long and begins with character X. ST[X TO Y] represents the Xth
through Yth characters of ST.
Consider the ST[X TO Y] case. This is evaluated
!SKIP!_FALSE; XT_X; YT_Y;
IF YT > LENGTH (ST) THEN BEGIN
YT_LENGTH (ST); righthalf (!SKIP!)_TRUE END;
IF YT < 0 THEN COMMENT result will be NULL;
BEGIN YT_0; righthalf (!SKIP!)_TRUE END;
IF XT < 1 THEN
BEGIN XT _ 1; lefthalf (!SKIP!)_TRUE END;
IF XT > YT THEN COMMENT result will be NULL;
BEGIN XT _ YT+1; lefthalf (!SKIP!)_TRUE END;
<return the XTth through YTth characters of ST>
LENGTH returns the number of characters in a string (see page 88). The
ST[X FOR Y] operation is converted to the ST[X TO Y] case before the
substring operation is performed. The variable !SKIP! can be examined to
determine if the substring indices were "out of bounds".
INFINITY SPECIAL LENGTH OPERATOR FUNCTION DESIGNATORS UNARY OPERATORS MEMORY AND LOCATION
"" (SPECIAL LENGTH OPERATOR)
This special primary construct is valid only within substring brackets. It
is an algebraic value representing the length of the most immediate string
under consideration. The reserved word INF is equivalent to "".
Example:
A[-2 to ] yields the last 3 characters of A.
A[3 for B[-1 for 1]] uses the next to the last character of string
B as the number of characters for the A substring operation.
FUNCTION DESIGNATORS
A function designator defines a single value. This value is produced by the
execution of a typed user Procedure or of a typed execution-time routine
(See chapters 6 and 7 for execution-time routines). For a function
designator to be an algebraic primary, its Procedure must be declared to
have an algebraic type. Untyped Procedures may only be called as Procedure
statements (see page 39). The value obtained from a user-defined Procedure
is that provided by a Return Statement within that Procedure.
The rules for supplying actual parameters in a function designator are
identical to those for supplying parameters in a procedure statement (see
page 39).
UNARY OPERATORS
The unary operator ABS is valid only for algebraic quantities. It returns
the absolute value of its argument.
-X is equivalent to (0-X). No type conversions are performed.
X is the logical negation of X. MEMORY AND LOCATION
One's core image can be considered a giant one dimensional array, which may
be accessed with the MEMORY construct. You had better be a good sport, or
know what you are doing.
MEMORY [ <integer expression> ]
One can store and retrieve from the elements of MEMORY just as with any
other array. However, with MEMORY, one can control how the compiler
interprets the type of the accessed element by including type declarator
reserved words after the <integer expression>. For example:
..._ MEMORY[X, INTEGER]
MEMORY[X, REAL] _ ...
..._ MEMORY[X, ITEM]
COMMENT items and sets are part of Leap;
MEMORY[X, SET] _ ...
..._ MEMORY[X, INTEGER ITEMVAR]
Note that one can not specify the contents of memory to be an Array or a
String.
LOCATION
LOCATION is a predeclared Sail routine that returns the index in MEMORY of
the Sail construct furnished it. The following is a list of constructs it
can handle and what LOCATION will return.
CONSTRUCT xLOCATION (x) RETURNS
variable address of the variable
string variable -1,,address of word2
array name address of a word containing the the address of the first data
word of the array
array element address of that element
procedure name address of the procedure's entry code
labels address of the label
Simple example:
REAL X;
MEMORY [LOCATION (X), REAL] _ 2.0;
PRINT (X); COMMENT " 2.000000 ";
MEMORY [LOCATION (X)] _ 2.0; PRINT (X);
COMMENT " .0000000@-39", MEMORY is INTEGER
unless otherwise specified;
MEMORY [LOCATION (X), INTEGER] _ 2.0;
PRINT (X); COMMENT same as above;
ASSEMBLY LANGUAGE STATEMENTS Syntax
SECTION 5
ASSEMBLY LANGUAGE STATEMENTS
5.1 Syntax
<code!block>
::= <code!head> <code!tail>
<code!head>
::= <code!begin>
::= <code!begin> <block!name>
::= <code!head> <declaration> ;
<code!begin>
::= START!CODE
::= QUICK!CODE
<code!tail>
::= <instruction> END
::= <instruction> END <block!name>
::= <instruction> ; <code!tail>
<instruction>
::= <addresses>
::= <opcode>
::= <opcode> <addresses>
<addresses>
::= <address>
::= <ac!field> ,
::= <ac!field> , <address>
<ac!field>
::= <constant!expression>
<address>
::= <indexed!address>
::= @ <indexed!address>
Semantics DECLARATIONS IN CODE BLOCKS
<indexed!address>
::= <simple!address>
::= <simple!address> ( <index!field> )
<simple!address>
::= <identifier>
::= <static!array!name> [
<constant!subscript!list> ]
::= <constant!expression>
::= <literal>
<literal>
::= [ <constant!expression> ]
<index!field>
::= <constant!expression>
<opcode>
::= <constant!expression>
::= <PDP-10!opcode>
5.2 Semantics
Within a START!CODE (QUICK!CODE) block, statements are processed by a small
and weak, but hopefully adequate, assembly language translator. Each
"instruction" places one instruction word into the output file. An
instruction consists of
<label>:<opcode> <ac!field>, @<simple!addr> (<index>)
or some subset thereof (see syntax). Each instruction must be followed by a
semi-colon.
DECLARATIONS IN CODE BLOCKS
A code!block behaves like any other block with respect to block structure.
Therefore, all declarations are valid, and the names given in these
declarations will be available only to the instructions in the code!block.
All labels must be declared as usual. Labels in code!blocks may refer to
instructions which will be executed, or to those which are not really
instructions, but data to be manipulated by these instructions (these latter
words must be bypassed in the code by jump instructions). The user may find
it easier to declare variables or SAFE arrays as data areas rather than
using labels and null statements. As noted below, identifiers of simple
variables are addresses of core locations. Identifiers of arrays are
PROTECT ACS DECLARATION OPCODES THE <simple addr> FIELD
addresses of the first word of the array header (see the appendix on array
implementation).
PROTECT ACS DECLARATION
PROTECT!ACS <ac #>, ... , <ac #>;
where <ac #> is an integer constant between 0 and '17, is a declaration.
Its effect is to cause Sail not to use the named accumulators in the code it
emits for the block in which the declaration occurred (only AFTER the
declaration). The most common use is with the ACCESS construct (see below);
if one is using accumulators 2, 3, and 4 in a code block, then one should
declare PROTECT!ACS 2, 3, 4 if one is going to use ACCESS. This way, the
code emitted by Sail for doing the ACCESS will not use accumulators 2, 3, or
4. WARNING: this does not prevent you from clobbering such ACs with
procedure calls (your own procedures or Sail's). However, most Sail
runtimes save their ACs and restore them after the call.
RESTRICTION: Accumulators P ('17), SP ('16), F ('12) and 1 are used for,
respectively, the system push down pointer, the string push down pointer,
the display pointer, and returning results from typed procedures and
runtimes. More about these acs on page 60. The protect mechanism will not
override these usages, so attempts to protect 1, '12, '16, or '17 will be
futile.
OPCODES
The Opcode may be a constant provided by the user, or one of the standard
(non I/O) PDP-10 operation codes, expressed symbolically. If a constant, it
should take the form of a complete PDP-10 instruction, expressed in octal
radix (e.g. DEFINE TTYUUO = "'51000000000";). Any bits appearing in fields
other than the opcode field (first 9 bits) will be OR'ed with the bits
supplied by other fields of instructions in which this opcode appears. In
TOPS-10 Sail the MUUOs (ENTER, LOOKUP, etc.) are available. In TENEX Sail
the JSYSes are available. Within a code!block opcodes supersede all other
objects; a variable, macro, or procedure with the same name as an opcode
will be taken for the opcode instead.
The indirect, index, and AC fields have the same syntax and perform the same
functions as they do in the FAIL or MACRO languages.
THE <simple addr> FIELD
If the <address> in an instruction is a constant (constant expression), it
is assumed to be an immediate or data operand, and is not relocated.
If the <address> is an identifier, the machine address (relative to the
start of the compilation) is used, and will be relocated to the proper value
by the Loader.
If the <address> is an identifier which has been declared as a formal
parameter to a procedure, addressing arithmetic will be done automatically
START!CODE VERSUS QUICK!CODE
to get at the VALUE of the parameter. Hence if the <address> is a formal
reference parameter, the instruction will be of the form OP AC,@ -x('12)
where x depends on exactly where the parameter is in the stack. If the
formal was from a simple procedure, then '17 will be used as the index
register rather than '12. When computing x Sail will assume that the stack
pointer has not changed since the last procedure entry; if you use PUSH,
POP, etc. in a Simple Procedure then you must calculate x yourself.
If a literal is used, the address of the compiled constant will be placed in
the instruction.
Any reference to Strings will result in the address of the second descriptor
word (byte pointer) to be placed in the instruction (see the appendix on
string implementation for an explanation of string descriptors).
Accessing parameters of procedures global to the current procedure is
difficult. ACCESS (<expr>) may be used to return the address of such
parameters. ACCESS will in fact do all of the computing necessary to obtain
the value of the expression <expr>, then return the address of that value
(which might be a temporary). Thus, MOVE AC, ACCESS(GP) will put the value
of the variable GP in AC, while MOVEI AC, ACCESS(GP) will put the address of
the variable GP in AC. If the expression is an item expression (see Leap),
then the item's number will be stored in a temp, and that temp's address
will be returned. The code emitted for an Access uses any acs that Sail
believes are available, so one must include a PROTECT!ACS declaration in a
Code block that uses ACCESS if you want to protect certain acs from being
munged by the Access. WARNING: skipping over an Access won't do the right
thing. For example,
SKIPE FLAG;
MOVE '10,ACCESS ('777 LAND INTIN(CHAN));
MOVEI '10,0;
will cause the program to skip into the middle of the code generated by the
access if FLAG is 0.
START!CODE VERSUS QUICK!CODE
Before your instructions are parsed in a block starting with START!CODE,
instructions are executed to leave all accumulators from 0 through '11 and
'13 through '15 available for your use. In this case, you may use a JRST to
transfer control out of the code!block, as long as you do not leave (1) a
procedure, (2) a block with array declarations, (3) a Foreach loop, (4) a
loop with a For list, or (5) a loop which uses the NEXT construct. In a
QUICK!CODE block, no accumulator-saving instructions are issued. Ac's '13
through '15 only are free. In addition, some recently used variables may be
given the wrong values if used as address identifiers (their current values
may be contained in Ac's 0-'11); and control should not leave the code!block
except by "falling through".
ACCUMULATOR USAGE IN CODE BLOCKS CALLING PROCEDURES FROM INSIDE CODE BLOCKS
ACCUMULATOR USAGE IN CODE BLOCKS
Although we have said that accumulators are "freed" for your use, this does
not imply a carte blanche. Usually this means the compiler saves values
currently stored in the ACs which it wants to remember (the values of
variables mostly), and notes that when the code block is finished, these ACs
will have values in them that it doesn't care about. However, this is not
the case with the following accumulators, which are not touched at all by
the entrance and exit of code blocks:
NAME NUMBER USAGE
P '17 The system push down list pointer. All procedures are
called with a PUSHJ P, PROC and exited (usually) with a
POPJ P. Use this as your PDL pointer in the code block, but be
sure that its back to where it was on entrance to the block by
the time you exit.
SP '16 The string push down stack pointer. Used in all string
operations. For how to do your own string mangling, read the
code.
F '12 This is used to maintain the "display" structure of
procedures. DO NOT HARM AC F!! Disaster will result. A more
exact description of its usage may be found in the appendix on
procedures and by reading the code.
CALLING PROCEDURES FROM INSIDE CODE BLOCKS
To call a procedure (say, PROT) from inside a code block, use PUSHJ P, PROT.
If the procedure requires parameters, PUSH P them in order before you
PUSHJ P (i.e. the first one first, the second one next, etc.). If the
formal is a reference, push the address of the actual onto the P stack. If
the formal is a value string, push onto the SP stack the two words of the
string descriptor (see the appendix on string implementation for an
explanation of string descriptors). If the formal is a reference string,
simply PUSH P the address of the second word of the string descriptor. If
the procedure is typed, it will return is value in AC 1, except that STRING
procedures return their values as the top element of the SP stack. More
information can be found in the appendix on procedure implementation.
Example:
BEWARE
INTEGER K; STRING S, SS;
INTEGER PROCEDURE PROT (REAL T; REFERENCE
INTEGER TT; STRING TTT; REFERENCE
STRING TTTT);
BEGIN COMMENT BODY; END;
DEFINE P = '17, SP = '16;
START!CODE
PUSH P, [3.14159];
MOVEI1, K;
PUSH P, 1;
MOVEI 1, S;
PUSH SP, -1(1);COMMENT if Sail allowed address
arithmetic in Start!code, you
could have said PUSH SP, S-1;
PUSH SP, S;
MOVEI1, SS;
PUSH P, 1;
PUSHJ P,PROT;
END;
gives the same effect as
PROT (3.14159, K, S, SS);
NOTE: procedures will change your accumulators unless the procedure takes
special pains to save and restore them.
BEWARE
The Sail <code block> assembler is not FAIL or MACRO. Read the syntax!
Address arithmetic is not permitted. All integer constants are decimal
unless specified explicitly as octal (e.g., '120). Each instruction is a
separate <statement> and must be separated from surrounding statements by a
semicolon. If you want comments then use COMMENT just like anywhere else in
Sail. QUICK!CODE is for wizards.
INPUT/OUTPUT ROUTINES Execution-time Routines in General SCOPE NOTATIONAL CONVENTIONS
SECTION 6
INPUT/OUTPUT ROUTINES
6.1 Execution-time Routines in General
SCOPE
A large set of predeclared, built-in procedures and functions have been
compiled into a library permanently resident on the system disk area
(SYS:LIBSAn.REL or <SAIL>LIBSAn.REL - n is the current version number;
HLBSAn for /H compilations), and optionally into a special sharable write-
protected high segment. The library also contains programs for managing
storage allocation and initialization, and for certain String functions. If
a user calls one of these procedures, a request is automatically made to the
loader to include the procedure, and any other routines it might need, in
the core image (or to link to the high segment). These routines provide
input/output (I/O) facilities, Arithmetic-String conversion facilities,
array-handling procedures and miscellaneous other interesting functions.
The remainder of this section and the next describes the calling sequences
and functions of these routines.
NOTATIONAL CONVENTIONS
A short-hand is used in these descriptions for specifying the types (if any)
of the execution-time routines and of their parameters. Before the
description of each routine there is a sample call of the form
VALUE _ FUNCTION (ARG1, ARG2, ... ARGn)
If VALUE is omitted, the procedure is an untyped one, and may only be called
at statement level (page 39).
The types of VALUE and the arguments may be determined using the following
scheme:
1) If " characters surround the sample identifier (which is usually
mnemonic in nature) a String argument is expected. Otherwise the
argument is Integer or Real. If it is important which of the types
Integer or Real must be presented, it will be made clear in the
description of the function. Otherwise the compiler assumes
Integer arguments (for those functions which are predeclared). The
user may pass Real arguments to these routines by re-declaring them
in the blocks in which the Real arguments are desired.
2) If the @ character precedes the sample identifier, the argument
will be called by reference. Otherwise it is a value parameter.
Example:
!SKIP! I/O Channels and Files OPEN
"RESULT" _ SCAN (@"SOURCE", BREAK!TABLE, @BRCHAR)
is a predeclared procedure with the implicit declaration:
EXTERNAL STRING PROCEDURE SCAN
(REFERENCE STRING SOURCE;
INTEGER BREAK!TABLE;
REFERENCE INTEGER BRCHAR);
!SKIP!
Some routines return secondary values by storing them in !SKIP!. Declare
EXTERNAL INTEGER !SKIP! if you want to examine these values. In FAIL or DDT
the spelling is ".SKIP.".
6.2 I/O Channels and Files
********************************** OPEN ********************************;
OPEN (CHANNEL, "DEVICE", MODE,
NUMBER!OF!INPUT!BUFFERS,
NUMBER!OF!OUTPUT!BUFFERS,
@COUNT, @BRCHAR, @EOF);
Sail input/output operates at a very low level in the following sense: the
operations necessary to obtain devices, open and close files, etc., are
almost directly analogous to the system calls used in assembly language.
OPEN is used to associate a channel number (0 to '17) with a device, to
determine the data mode of the I/O to occur on this channel (character mode,
binary mode, dump mode, etc.), to specify storage requirements for the data
buffers used in the operations, and to provide the system with information
to be used for input operations. See page 4 for an example of TOPS-10 I/O
programming.
CHANNEL is a user-provided channel number which will be used in subsequent
I/O operations to identify the device. CHANNEL may range from 0 to 15
('17). A RELEASE will be performed before the OPEN is executed.
DEVICE must be a String (i.e. "TTY", "DSK") which is recognizable by the
system as a physical or logical device name.
MODE is the data mode for the I/O operation. MODE 0 will always work for
characters (see INPUT, page 72 and OUT, page 74). Modes 8 ('10)
and 15 ('17) are applicable for binary and dump-mode operations using
the functions WORDIN, WORDOUT, ARRYIN, or ARRYOUT (see page 75 and
following). For other data modes, see [SysCall]. If any of bits 18-
21 are on in the MODE word, the I-O routines will not print error
messages when data errors occur which present the corresponding bits
as a response to the GETSTS UUO. Instead, the GETSTS bits will be
reported to the user as described under EOF below. If bit 23 is on,
no error message will be printed if an invalid file name
specification is presented to LOOKUP, ENTER, or RENAME, a code
identifying the problem will be returned (see page 67 ff. for
details). If you don't understand any of this, leave all non-mode
bits off in the MODE word.
NUMBER!OF!{INPUT/OUTPUT~!BUFFERS specifies the number of buffers to be
reserved for the I/O operations. At least one buffer must be
specified for input if any input is to be done in modes other than
'17; similarly for output. If data is only going one direction, the
other buffer specification should be 0. Two buffers give reasonable
performance for most devices (1 is sufficient for a TTY, more are
required for DSK if rapid operation is desired). The left half of
the BUFFER parameter, if non-zero, specifies the buffer size (mod
'7777) for the I/O buffers. Use this only if you desire non-standard
sizes.
The remaining arguments are applicable only for INPUT (String input). They
will be ignored for any other operations (although their values may be
changed by the Open function).
COUNT designates a variable which will contain the maximum number of
characters to be read from "DEVICE" in a given INPUT call (see page
72, page 68). Fewer characters may be read if a break character
is encountered or if an end of file is detected. The count should be
a variable or constant (not an expression), since its address is
stored, and the temporary storage for an expression may be re-used.
BRCHAR designates a variable into which the break character (see INPUT and
BREAKSET again) will be stored. This variable can be tested to
determine which of many possible characters terminated the read
operation.
EOF designates a variable to be used for two purposes:
1) Error handling when OPEN is called. If the system call used by
OPEN succeeds then EOF is set to zero and OPEN returns. If the
system call fails then OPEN looks at the EOF variable; if it is
nonzero then OPEN returns. If EOF is zero then the user is given
the option of retrying or continuing without the device. If a
retry is successful then EOF is zeroed. If the user proceeds
(gives up) then EOF is set to nonzero. The net effect is that the
program may interpret EOF=0 as a successful OPEN and EOF0 as an
unsuccessful OPEN.
2) Error handling for subsequent I/O operations. EOF will be made
non-zero (TRUE) if an end of file condition, or any error
condition among those enabled (see MODE, above) is detected during
any Sail input/output operation. It will be 0 (FALSE) on return
to the user otherwise. Subsequent inputs after an EOF return will
return non-zero values in EOF and a null String result for INPUT.
For ARRYIN, a 0 is returned as the value of the call after end of
file is detected. If EOF is TRUE after such an operation, it will
contain the entire set (18 bits) of GETSTS information in the left
half. The EOF bit is '20000, and is the only one you'll ever see
if you haven't specially enabled for others.
Here are the error bits for SUAI and TOPS-10; TENEX Sail uses the ERSTR
error number instead.
400000 improper mode (a catchall)
200000 parity error
100000 data error
40000 record number out of bounds
20000 end of file (input only)
You are always enabled for bit 20000 (EOF). However, to be allowed to
handle any of the others, you must turn on the corresponding bit in the
right half of the MODE word. In addition, the 10000 bit is used to enable
user handling of invalid file specifications to ENTER, LOOKUP, and RENAME.
'7500017 in the MODE parameter would enable a dump mode file for user
handling of ALL I/O errors on the channel. If you are not enabled for a
given error, an error message (which may or may not be fatal) will be
printed, and the error code word set as indicated. In addition, the number
of words actually transferred is stored in the right half of the EOF
variable for ARRYIN, ARRYOUT.
Assembly Language Approximation to OPEN:
INIT CHANNEL, MODE
SIXBIT /DEVICE/
XWD OHED,IHED
JRST <handle error condition>
JUMPE <NUMBER!OF!OUTPUT!BUFFERS>, GETIN
<allocate buffer space>
OUTBUF CHANNEL, NUMBER!OF!OUTPUT!BUFFERS
GETIN: JUMPE <NUMBER!OF!INPUT!BUFFERS>, DONE
<allocate buffer space>
INBUF CHANNEL, NUMBER!OF!INPUT!BUFFERS
DONE: <mark channel open -- internal bookkeeping>
<return>
OHED: BLOCK 3
IHED: BLOCK 3
CLOSE, CLOSIN, CLOSO GETCHAN RELEASE
************************** CLOSE, CLOSIN, CLOSO ************************;
CLOSE (CHANNEL, BITS(0));
CLOSIN (CHANNEL, BITS(0));
CLOSO (CHANNEL, BITS(0))
The input (CLOSIN) or output (CLOSO) side of the specified channel is
closed: all output is forced out (CLOSO); the current file name is
forgotten. However the device is still active; no OPEN need be done again
before the next LOOKUP/ENTER operation. Always CLOSE output files: Sail
exit code will deassign the device, but does not force out any remaining
output; you must do a CLOSE when writing on a disk file to have the new file
(or a newly edited old file) entered on your User File Directory. No INPUT,
OUT, etc., may be given to a directory device until an ENTER, LOOKUP, or
RENAME has been issued for the channel.
CLOSE is equivalent to the execution of both CLOSIN and CLOSO for the
channel. BITS specifies the close inhibit bits, which default to zero. See
[SysCall] for the interpretation of the bits.
******************************** GETCHAN *******************************;
VALUE _ GETCHAN
GETCHAN returns the number of some channel which Sail believes is not
currently open. The value -1 is returned if all channels are busy.
******************************** RELEASE *******************************;
RELEASE (CHANNEL, BITS(0))
If an OPEN has been executed for this channel, a CLOSE is now executed for
it. The device is dissociated from the channel and returned to the resource
pool (unless it has been assigned by the monitor ASSIGN command). No I/O
operation may refer to this channel until another OPEN denoting it has been
executed. BITS specifies the CLOSE inhibit bits; see [SysCall].
Release is always valid. If the channel mentioned is not currently open,
the command is simply ignored.
LOOKUP, ENTER RENAME
***************************** LOOKUP, ENTER ****************************;
LOOKUP (CHANNEL, "FILE", @FLAG);
ENTER (CHANNEL, "FILE", @FLAG)
Before input or output operations may be performed for a directory device
(DECtape or DSK) a file name must be associated with the channel on which
the device has been opened (see page 63). LOOKUP names a file which is to
be read. ENTER names a file which is to be created or extended (see
[SysCall]). It is recommended that an ENTER be performed after every OPEN
of an output device so that output not normally directed to the DSK can be
directed there for later processing if desired. The format for a file name
string is
"NAME", or
"NAME.EXT", or
"NAME[P,PN]", or
"NAME.EXT[P,PN]", or
"NAME.EXT[P,PN"
See [MonCom] for the meaning of these things if you do not immediately
understand.
Sail is not as choosy about the characters it allows as some processors are.
Any character which is not a comma, period, right square bracket, or left
square bracket will be passed on. Up to 6 characters from NAME, 3 from EXT,
P, or PN will be used -- the rest are ignored.
If the LOOKUP or ENTER operation fails then variable FLAG may be examined
to determine the cause. The left half of FLAG will be set to '777777 (Flag
has the logical value TRUE). The right half will contain the code returned
by the system giving the cause of the failure. An invalid file
specification will return a code of '10. In this case, if the appropriate
bit (bit 23, see OPEN) was OFF in the MODE parameter of the OPEN, an error
message will be printed; otherwise, the routine just returns without
performing the UUO.
If the LOOKUP or ENTER succeeds, FLAG will be set to zero (FALSE).
********************************* RENAME *******************************;
RENAME (CHANNEL, "FILE-SPEC",
PROTECTION, @FLAG)
The file open on CHANNEL is renamed to FILE!SPEC (a NULL file-name will
delete the file) with read/write protection as specified in PROTECTION (nine
bits, described in [SysCall]). FLAG is set as in LOOKUP and ENTER.
ERENAME Break Characters BREAKSET
******************************** ERENAME *******************************;
ERENAME (CHANNEL,"FILE-SPEC",
PROTECTION, DATE, TIME,
MODE, @FLAG)
(Not on TENEX.) This extended version of RENAME allows complete
specification of all the data which may be changed by a RENAME.
6.3 Break Characters
******************************** BREAKSET ******************************;
BREAKSET (TABLE, "BREAK!CHARS", MODE)
Character input/output is done using the String features of Sail. In fact,
I/O is the chief justification for the existence of strings in the language.
String input presents a problem not present in String output. The length of
an output String can be used to determine the number of characters written.
However it is often awkward to require an absolute count for input. Quite
often one would like to terminate input, or "break", when one of a specified
set of characters is encountered in the input stream. In Sail, this
capability is implemented by means of the BREAKSET, INPUT, TTYIN, and SCAN
functions. The value of TABLE may range from -17 to 54, but tables -17
through -1 are reserved for use by the runtime system. Thus up to 54
different sets of user break specifications may exist at once. Which set
will be used is determined by the TABLE parameter in an INPUT or SCAN
function call. Breaktables are dynamically allocated in blocks of 18 (1-18,
19-36, 37-54).
BREAKSET merely modifies the existing settings in TABLE; use GETBREAK (which
returns a virgin table) if you want to achieve an absolute known state.
The function of a given BREAKSET command depends on the MODE, an integer
which is interpreted as a right-justified ASCII character whose value is
intended to be vaguely mnemonic. BREAKSET commands can be partitioned into
4 groups according to mode:
GROUP 0 -- Conversion specifications
MODE FUNCTION
"K" (Konvert) The minuscule letters (a-z) will be converted to
majuscule (A-Z) before doing anything else.
"F" (Full character set) Undoes the effect of "K". Mode "F" is the
default.
"Z" (Zero bytes) Believe the breaktable when INPUT reads a zero byte.
INPUT automatically omits zero characters otherwise. Mode "Z" is
turned off by both mode "I" and mode "X".
GROUP 1 -- Break character specifications
MODE FUNCTION
"I" (by Inclusion) The characters in the BREAK!CHARS String comprise
the set of characters which will terminate an INPUT (or SCAN).
"X" (by eXclusion) Only those characters (of the possible 128 ASCII
characters) which are NOT contained in the String BREAK!CHARS will
terminate an input when using this table.
"O" (Omit) The characters in "BREAK!CHARS" will be omitted (deleted)
from the input string.
Any "I" or "X" command completely specifies the break character set for its
table (i.e., the table is reset before these characters are stored in it).
Neither will destroy the omitted character set currently specified for this
table. Any "O" command completely specifies the set of omitted characters,
without altering the break characters for the table in question. If a
character is a break-character, any role it might play as an omitted
character is sacrificed.
The next group of MODEs determines the disposition of break characters in
the input stream. The "BREAK!CHARS" argument is ignored in these commands,
and may in fact be NULL:
GROUP 2 -- Break character disposition
MODE FUNCTION
"S" (Skip -- default mode) After execution of an "S" command the break
character will not appear either in the resultant String or in
subsequent INPUTs or SCANs-- the character is "skipped". Its value
may be determined after the INPUT by examination of the break
character variable (see page 63).
"A" (Append) The break character (if there is one -- see page 63 and
page 72) is appended, or concatenated to the end of the input
string. It will not appear again in subsequent inputs.
"R" (Retain) The break character does not appear in the resultant INPUT
or SCAN String, but will be the first character processed in the
next operation referring to this input source (file or SCAN
String).
Text files containing line numbers present a special problem. A line number
is a word containing 5 ASCII characters representing the number in bits 0-
34, with a "1" in bit 35. No other words in the file contain 1's in bit 35.
Since String manipulations provide no way for distinguishing line numbers
from other characters, there must be a way to warn the user that line
numbers are present, or to allow him to ignore them entirely.
The next group of MODEs determines the disposition of these line numbers.
Again, the "BREAK!CHARS" argument is ignored:
Group 3 -- Line number disposition
MODE FUNCTION
"P" (Pass -- default) Line numbers are treated as any other characters.
Their identity is lost; they simply appear in the result string.
"N" (No numbers) No line number (or the TAB which always follows it in
standard files) will appear in the result string. They are simply
discarded.
"L" (Line no. break) The result String will be terminated early if a
line number is encountered. The characters comprising the line
number and the associated TAB will appear as the next 6 characters
read or scanned from this character source. The user's break
character variable (see page 63 and page 72) will be set to -1 to
indicate a line number break.
"E" (Lee Erman's very own mode) The result String is terminated on a
line number as with "L", but neither the line number nor the TAB
following it will appear in subsequent inputs. The line number
word, negated, is returned in the user's (integer) BRCHAR variable.
"D" (Display) obsolete
Once a break table is set up, it may be referenced in an INPUT, TTYIN or
SCAN call to control the scanning operation.
Example: To delimit a "word", a program might wish to input characters until
a blank, a TAB, a line feed, a comma, or a semicolon is encountered,
ignoring line numbers. Assume also that carriage returns are to be ignored,
and that the break character is to be retained in the character source for
the next scanning operation:
SETBREAK GETBREAK, RELBREAK
BREAKSET (DELIMS, " , ;"&TAB&LF, "I");
Comment break on any of these;
BREAKSET (DELIMS, '15, "O");
Comment ignore carriage return;
BREAKSET (DELIMS, NULL, "N");
Comment ignore line numbers;
BREAKSET (DELIMS, NULL, "R");
Comment save break char for next time;
Breaktable 0 is builtin as equivalent to SETBREAK (0, NULL, NULL, "I").
This is break-on-count for INPUT and returns the whole string from SCAN.
****************************** SETBREAK ******************************;
SETBREAK (TABLE, "BREAK!CHARS",
"OMIT!CHARS", "MODES")
SETBREAK is logically equivalent to the Sail statement:
BEGIN "SETBREAK"
INTEGER I;
IF LENGTH (OMIT!CHARS) > 0 THEN
BREAKSET (TABLE, OMIT!CHARS, "O");
FOR I_1 STEP 1 UNTIL LENGTH (MODES) DO
BREAKSET (TABLE, BREAK!CHARS, MODES[I FOR 1])
END "SETBREAK"
*************************** GETBREAK, RELBREAK *************************;
TABLE _ GETBREAK;
RELBREAK (TABLE)
GETBREAK finds an unreserved breaktable, reserves it, sets it to a
completely virgin state, and returns the number of the table. GETBREAK
returns -18 if there are no free tables. Breaktables are reserved by
GETBREAK, SETBREAK, BREAKSET, and STDBRK. RELBREAK returns a table to the
available list.
STDBRK I/O Routines INPUT
********************************* STDBRK *******************************;
STDBRK (CHANNEL)
Eighteen breakset tables have been selected as representative of the more
common input scanning operations. The function STDBRK initializes the
breakset tables by opening the file SYS:BKTBL.BKT on CHANNEL and reading
in these tables. The user may then reset those tables which he does not
like to something he does like.
The eighteen tables are described here by giving the SETBREAKs which would
be required for the user to initialize them:
DELIMS _ '15 & '12 & '40 & '11 & '14;
Comment carriage return, line feed, space,
tab, form feed;
LETTS _ "ABC ... Zabc ... z!";
DIGS _ "0123456789";
SAILID _ LETTS&DIGS;
SETBREAK ( 1, '12, '15, "INS");
SETBREAK ( 2, '12, NULL, "INA");
SETBREAK ( 3, DELIMS, NULL, "XNR");
SETBREAK ( 4, SAILID, NULL, "INS");
SETBREAK ( 5, SAILID, NULL, "INR");
SETBREAK ( 6, LETTS, NULL, "XNR");
SETBREAK ( 7, DIGS, NULL, "XNR");
SETBREAK ( 8, DIGS, NULL, "INS");
SETBREAK ( 9, DIGS, NULL, "INR");
SETBREAK (10, DIGS&"+-@.", NULL, "XNR");
SETBREAK (11, DIGS&"+-@.", NULL, "INS");
SETBREAK (12, DIGS&"+-@.", NULL, "INR");
SETBREAK (13-18, NULL, NULL, NULL);
6.4 I/O Routines
********************************* INPUT ********************************;
"RESULT" _ INPUT (CHANNEL, BREAK!TABLE)
A string of characters is obtained for the file open on CHANNEL, and is
returned as the result. The INPUT operation is controlled by BREAK!TABLE
(see page 68) and the reference variables BRCHAR, EOF, and COUNT which are
provided by the user in the OPEN function for this channel (see page 63).
Zero bytes are automatically omitted (text editor convention) unless mode
"Z" was specified for the breaktable. Input may be terminated in several
SCAN
ways. The exact reason for termination can be obtained by examining BRCHAR
and EOF:
EOF BRCHAR
0 0 End of file or an error (if enabled, see page 63) occurred
while reading. The result is a String containing all non-
omitted characters which remained in the file when INPUT was
called.
0 0 No break characters were encountered. The result is a String
of length equal to the current COUNT specifications for the
CHANNEL (see page 63).
0 <0 A line number was encountered and the break table specified
that someone wanted to know. The result String contains all
characters up to the line number. If mode "L" was specified in
the Breakset setting up this table, bit 35 is turned off in
the line number word so that it will be input next time. -1 is
placed in BRCHAR. If mode "E" was specified, the line number
will not appear in the next input String, but its negated ASCII
value, complete with low-order line number bit, will be found
in BRCHAR.
0 >0 A break character was encountered. The break character is
stored in BRCHAR (an INTEGER reference variable, see page 63)
as a right-justified 7-bit ASCII value. It may also be tacked
on to the end of the result String or saved for next time,
depending on the BREAKSET mode (see page 68).
If break table 0 is specified, the only criteria for termination are end of
file or COUNT exhaustion.
********************************** SCAN ********************************;
"RESULT" _ SCAN (@"SOURCE",
BREAK!TABLE, @BRCHAR)
SCAN functions identically to INPUT with the following exceptions:
SCANC OUT LINOUT
1. The source is not a data file but the String SOURCE, called by
reference. The String SOURCE is truncated from the left to produce
the same effect as one would obtain if SOURCE were a data file. The
disposition of the break character is the same as it is for INPUT.
2. BRCHAR is directly specified as a parameter. INPUT gets its break
character variable from a table set up by page 63.
3. Line number considerations are irrelevant.
********************************* SCANC ********************************;
"RESULT" _ SCANC ("SOURCE",
"BREAK", "OMIT", "MODE");
This routine is equivalent to the following Sail code:
STRING PROCEDURE SCANC (STRING ARG, BRK,
OMIT, MODE);
BEGIN "SCANC" INTEGER TBL, BRCHAR; STRING RSLT;
TBL_GETBREAK; SETBREAK (TBL, BRK, OMIT, MODE);
RSLT_SCAN (ARG, TBL, BRCHAR);
RELBREAK (TBL);
RETURN (RSLT) END "SCANC";
Note that the arguments are all value parameters, so that SCANC will be
called at compile time if the arguments are constants. It is intended that
SCANC be used with ASSIGNC in macros and conditional compilation. For
scanning at execution time, it is much more efficient to use SCAN directly.
********************************** OUT *********************************;
OUT (CHANNEL, "STRING")
STRING is output to the file open on CHANNEL. If the device is a TTY, the
String will be typed immediately. Buffered mode text output is employed for
this operation. The data mode specified in the OPEN for this channel must
be 0 or 1. The EOF variable will be set non-zero as described in page 63 if
an error is detected and the program is enabled for it; 0 otherwise.
********************************* LINOUT *******************************;
LINOUT (CHANNEL, NUMBER)
SETPL WORDIN WARNING ABOUT DUMP MODE IO ARRYIN
ABS (NUMBER) mod 100,000 is converted to a 5 character ASCII string. These
characters are placed in a single word in the output file designated by
CHANNEL with the low-order bit (line-number bit) turned on. A tab is
inserted after the line number. Mode 0 or 1 must have been specified in the
OPEN (page 63) for the results to be anywhere near satisfactory. EOF is set
as in OUT.
********************************* SETPL ********************************;
SETPL (CHANNEL, @LINNUM,
@PAGNUM, @SOSNUM)
This routine allows one to keep track of the string input from CHANNEL.
Whenever a '12 is encountered, LINNUM is incremented. Whenever a '14 is
encountered, PAGNUM is incremented and LINNUM is zeroed. Whenever an SOS
line number is encountered it is placed into SOSNUM.
********************************* WORDIN *******************************;
VALUE _ WORDIN (CHANNEL)
The next word from the file open on CHANNEL is returned. A zero is returned,
and EOF (see page 63, page 72) set, when end of file or error is
encountered. This operation is performed in buffered mode or dump mode,
depending on the mode specification in the OPEN.
WARNING ABOUT DUMP MODE IO
Dump Mode (mode '15, '16, or '17) is sufficiently device and system
dependent that you should consult [SysCall] and be extremely careful.
********************************* ARRYIN *******************************;
ARRYIN (CHANNEL, @LOC, HOW!MANY)
HOW!MANY words are read from the device and file open on CHANNEL, and
deposited in memory starting at location LOC. Buffered-mode input is done
if MODE (see page 63) is '10 or '14. Dump-mode input is done if MODE is '16
or '17. Other modes are illegal. See the warning about Dump Mode IO above.
If an end of file or enabled error condition occurs before HOW!MANY words
are read in buffered mode then the EOF variable (see page 63) is set to the
enabled bits in its left half, as usual. Its right half contains the number
of words actually read. EOF will be 0 if the full request is satisfied. No
indication of how many words were actually read is given if EOF is
encountered while reading a file in DUMP mode.
WORDOUT ARRYOUT INOUT GETSTS, SETSTS
******************************** WORDOUT *******************************;
WORDOUT (CHANNEL, VALUE)
VALUE is placed in the output buffer for CHANNEL. An OUTPUT is done when
the buffer is full or when a CLOSE or RELEASE is executed for this channel.
Dump mode output will be done if dump mode is specified in the OPEN (see
page 63). EOF is set as in OUT. See the warning about Dump Mode IO above.
******************************** ARRYOUT *******************************;
ARRYOUT (CHANNEL, @LOC, HOW!MANY)
HOW!MANY words are written from memory, starting at location LOC, onto the
device and file open on channel CHANNEL. The valid modes are again '10,
'14, '16, and '17. The EOF variable is set as in ARRYIN, except that the
EOF bit itself will never occur.
********************************* INOUT ********************************;
INOUT (INCHAN, OUTCHAN, HOWMANY)
INOUT reads HOWMANY words from channel INCHAN and writes them out on channel
OUTCHAN. Each channel must be open in a mode between 8 and 12. On return,
the EOF variables for the two channels will be the same as if ARRYIN &
ARRYOUT had been used. If HOWMANY is less than zero, then transfer of data
will cease only upon end of file or a device error. INOUT is not available
in TENEX Sail.
***************************** GETSTS, SETSTS ***************************;
SETSTS (CHAN, NEW!STATUS);
issues a SETSTS uuo on channel CHAN with the status value NEW!STATUS.
STATUS _ GETSTS (CHAN)
returns the results of a GETSTS uuo on channel CHAN.
These functions do not exist in TENEX Sail. Instead, see GTSTS, GDSTS,
STSTS, and SDSTS for analogous features.
MTAPE USETI, USETO REALIN, INTIN
********************************* MTAPE ********************************;
MTAPE (CHANNEL, MODE)
MTAPE is ignored unless the device associated with CHANNEL is a magnetic
tape drive. It performs tape actions as follows:
MODE FUNCTION
"A" Advance past one tape mark (or file)
"B" Backspace past one tape mark
"E" Write tape mark
"F" Advance one record
"I" Set 'IBM compatible' mode
"R" Backspace one record
"S" Write 3 inches of blank tape
"T" Advance to logical end of tape
"U" Rewind and unload
"W" Rewind tape
NULL Wait until all activity ceases
****************************** USETI, USETO ****************************;
USETI (CHANNEL, VALUE);
USETO (CHANNEL, VALUE)
These routines are for random file access (see [SysCall]).
***************************** REALIN, INTIN ****************************;
VALUE _ REALIN (CHANNEL);
VALUE _ INTIN (CHANNEL)
Number input may be obtained using the functions REALIN or INTIN, depending
on whether a Real number or an Integer is required. Both functions use the
same free field scanner, and take as argument a channel number.
Free field scanning works as follows: characters are scanned one at a time
from the input channel, ignoring everything until a digit or decimal point
is encountered. Then a number is scanned according to this syntax, with
zero bytes, line numbers, and carriage returns (but not linefeeds) ignored:
<number>
::= <sign> <real number>
<real number>
::= <decimal number>
::= <decimal number> <exponent>
::= <exponent>
<decimal number>
::= <integer>
::= <integer> .
::= <integer> . <integer>
::= . <integer>
<integer>
::= <digit>
::= <integer> <digit>
<exponent>
::= @ <sign> <integer>
::= E <sign> <integer>
<sign>
::= +
::= -
::= <empty>
If the digit is not part of a number an error message will be printed and
the program will halt. Typing a carriage return will cause the input
function to return zero.
On input, leading zeros are ignored. The ten most significant digits are
used to form the number. A check for overflow and underflow is made and an
error message printed if this occurs. When using INTIN any exponent is
removed by scaling the Integer number. Rounding is used in this process.
All numbers are accurate to one half of the least significant bit.
After scanning the number the last delimiter is replaced on the input string
and is returned as the break character for the channel. If no number is
found, a zero is returned, and the break variable is set to -1; If an end of
file or enabled error is sensed this is also returned in the appropriate
channel variable. The maximum character count appearing in the OPEN call is
ignored.
REALSCAN, INTSCAN TMPIN, TMPOUT
*************************** REALSCAN, INTSCAN **************************;
VALUE _ REALSCAN (@"NUMBER!STRING",
@BRCHAR);
VALUE _ INTSCAN (@"NUMBER!STRING",
@BRCHAR)
These functions are identical in function to REALIN and INTIN. Their
inputs, however, are obtained from their NUMBER!STRING arguments. These
routines replace NUMBER!STRING by a string containing all characters left
over after the number has been removed from the front.
***************************** TMPIN, TMPOUT ****************************;
"RESULT" _ TMPIN ("FILE", @ERRFLAG);
TMPOUT ("FILE", "TEXT", @ERRFLAG)
These routines do input and output to tmpcor files (simulated files kept in
core storage--see [SysCall]).
TMPIN returns a string consisting of the entire contents of the tmpcor file
of the specified name. Only the first three characters in the file name are
significant. If the input fails for some reason (most likely: no tmpcor
file with the specified name) then ERRFLAG is set to true and NULL is
returned. Otherwise ERRFLAG is set to false.
TMPOUT writes its string argument into the specified tmpcor file. The
ERRFLAG has the same function as in TMPIN; in case of error, the tmpcor file
is not written. Likely causes for error are running out of tmpcor space
(currently, the sum of the sizes of all the tmpcor files for a single job
may not exceed =256 words) or attempting to write a null tmpcor file (i.e.,
calling TMPOUT with the string argument NULL).
TMPIN executes a TMPCOR uuo with code 1, and hence does not delete the
specified tmpcor file. The length of the returned string will always be a
multiple of five, since words rather than characters are actually being
transferred. TMPOUT executes a TMPCOR uuo with code 3. The last word of
the string is padded with nulls if necessary before the data transfer is
done.
Neither function is available in TENEX Sail.
AUXCLR, AUXCLV CHNIOR, CHNIOV TTY and PTY Routines TELETYPE I/O ROUTINES BACKUP CLRBUF INCHRS INCHRW INCHSL INCHWL
***************************** AUXCLR, AUXCLV ***************************;
RSLT _ AUXCLR (PORT, @ARG, FUNCTION);
RSLT _ AUXCLV (PORT, ARG, FUNCTION)
(TYMSHARE only.) These functions perform AUXCAL system calls; the only
difference is whether ARG is by reference or by value. !SKIP! is set.
***************************** CHNIOR, CHNIOV ***************************;
RSLT _ CHNIOR (CHAN, @ARG, FUNCTION);
RSLT _ CHNIOV (CHAN, ARG, FUNCTION)
(TYMSHARE only.) These functions perform CHANIO system calls; the only
difference is whether ARG is by reference or by value. !SKIP! is set.
6.5 TTY and PTY Routines
************************* TELETYPE I/O ROUTINES ************************;
Each of the I/O functions uses the TTCALL UUO's to do direct TTY I/O.
BACKUP
The system attempts to back up its TTY input buffer pointer to the
beginning of the last "line", thus allowing you to reread it. In
general this cannot possibly work, so do not use BACKUP.
CLRBUF
Flushes the input buffer.
CHAR _ INCHRS
Returns a negative value if no characters have been typed;
otherwise it is INCHRW.
CHAR _ INCHRW
Waits for a character to be typed and returns that character.
"STR" _ INCHSL (@FLAG)
Returns NULL with FLAG 0 if no lines have been typed. Otherwise
it sets FLAG to 0 and performs INCHWL.
"STR" _ INCHWL
Waits for a line to be typed and returns a string containing all
INSTR INSTRL INSTRS IONEOU OUTCHR OUTSTR TTYIN TTYINL TTYINS TTYUP
characters up to (but not including) the activation character.
The activation character is put into !SKIP!. If the activation
character is CR then the next character is discarded (on the
assumption that it is LF).
"STR" _ INSTR (BRCHAR)
Returns as a string all characters up to, but not including, the
first instance of BRCHAR. The BRCHAR instance is lost.
"STR" _ INSTRL (BRCHAR)
Waits for a line to be typed, then performs INSTR.
"STR" _ INSTRS (@FLAG, BRCHAR)
Is INCHSL if no lines are waiting; INSTRL otherwise.
IONEOU (CHAR)
(TYMSHARE only.) The low-order 8 bits of CHAR are sent to the TTY
in image mode.
OUTCHR (CHAR)
Types its character argument (right-justified in an integer
variable).
OUTSTR ("STR")
Types its string argument until the end of the string or a null
character is reached.
"STR" _ TTYIN (TABLE, @BRCHAR)
Uses the break table features described in page 68 and page 72 to
return a string and break character. Mode "R" is illegal; line
number modes are irrelevant. The input count (see page 63) is set
at 100.
"STR" _ TTYINL (TABLE, @BRCHAR)
Waits for a line to be typed, then does TTYIN.
"STR" _ TTYINS (TABLE, @BRCHAR)
Sets BRCHAR to 0 and returns NULL if no lines are waiting.
Otherwise it is TTYINL.
OLDVAL _ TTYUP (NEWVAL)
Causes conversion of lower case characters (a-z) to their upper
case equivalents for strings read by any of the Sail teletype
routines that do not use break tables. If NEWVAL is TRUE then
conversion will take place on all subsequent inputs until
TTYUP(FALSE) is called. OLDVAL will be set to the previous value
of the conversion flag. If TTYUP has never been called, then no
conversions will take place, and the first call to TTYUP will
return FALSE. In TENEX, TTYUP sets the system parameter using the
STPAR jsys to convert to upper case.
PSEUDO-TELETYPE FUNCTIONS LODED PTYALL PTCHRS PTCHRW PTOCHS PTOCHW PTOCNT PTIFRE PTOSTR PTYGET PTYGTL PTYIN
*********************** PSEUDO-TELETYPE FUNCTIONS **********************;
Pseudo-teletype functions are available at SUAI only.
LODED ("STR")
Loads the line editor with the string argument. PTOSTR should be
used rather than LODED if possible, since LODED works only on a DD
or III, while PTOSTR works on all terminals.
"STR" _ PTYALL (LINE)
Returns whatever is in the PTY's output buffer. No waiting is
done.
CHAR _ PTCHRS (LINE)
Reads a character from the PTY if there is one, returns -1 if
none.
CHAR _ PTCHRW (LINE)
Waits for a character from the PTY and returns it.
PTOCHS (LINE, CHAR)
Tries to send a character to a PTY. If the attempt was
successful, the global variable !SKIP! is -1, otherwise 0.
PTOCHW (LINE, CHAR)
Sends a character to a PTY, waiting if necessary.
NUMBER _ PTOCNT (LINE)
Returns the number of free characters in the PTY output buffer.
NUMBER _ PTIFRE (LINE)
Returns the number of free characters in the PTY input buffer.
PTOSTR (LINE, "STR")
Sends the string to the PTY, waiting if necessary. PTOSTR (0,
"STR") sends the string to your TTY.
LINE _ PTYGET
Gets a new pseudo-teletype line number and returns it. The global
variable !SKIP! is -1 if the attempt to get a PTY was successful,
and 0 otherwise.
CHARACTERISTICS _ PTYGTL (LINE)
Returns line characteristics for the PTY.
"STR" _ PTYIN (LINE, BKTBL, @BRCHAR)
Reads from the PTY (waiting if necessary) according to break table
conventions. The break character is stored in BRCHAR.
PTYREL PTYSTL PTYSTR Example of TOPS-10 I/O
PTYREL (LINE)
Releases PTY identified by LINE.
PTYSTL (LINE, CHARACTERISTICS)
Sets line characteristics for the PTY specified by LINE.
"STR" _ PTYSTR (LINE, BRCHAR)
Reads characters from the PTY, waiting if necessary, until a
character equal to BRCHAR is seen. All but the break character is
returned as the string. If the break character was '15 (carriage
return), the following character is snarfed (on the assumption
that it is a linefeed).
6.6 Example of TOPS-10 I/O
BEGIN "COPY"
COMMENT copies a text file, inserting a semicolon at the
beginning of each line, deleting SOS line numbers and
zero bytes, if any. Prints the page number as it goes;
REQUIRE "[][]" DELIMITERS;
DEFINE CRLF=[('15&'12)],LF=['12], FF=['14];
INTEGER COLONTAB;
RECORD!CLASS $FILE (STRING DEVICE, NAME;
INTEGER CHANNEL, MODE, IBUF, OBUF,
COUNT, BRCHAR, EOF, LINNUM, PAGNUM, SOSNUM);
RECORD!POINTER($FILE) PROCEDURE OPENUP
(STRING FILNAM; INTEGER MODE, IBUF, OBUF);
BEGIN "OPENUP"
STRING T; RECORD!POINTER ($FILE) Q; INTEGER BRK;
Q_NEW!RECORD ($FILE); T_SCAN (FILNAM, COLONTAB, BRK);
$FILE:DEVICE[Q]_(IF BRK=":" THEN T ELSE "DSK");
$FILE:NAME[Q]_(IF BRK=":" THEN FILNAM ELSE T);
$FILE:MODE[Q]_MODE; $FILE:IBUF[Q]_IBUF;
$FILE:OBUF[Q]_OBUF; OPEN ($FILE:CHANNEL[Q]_GETCHAN,
$FILE:DEVICE[Q], MODE, IBUF, OBUF, $FILE:COUNT[Q],
$FILE:BRCHAR[Q], $FILE:EOF[Q]_-1);
IF NOT($FILE:EOF[Q]) THEN BEGIN
SETPL ($FILE:CHANNEL[Q], $FILE:LINNUM[Q],
$FILE:PAGNUM[Q], $FILE:SOSNUM[Q]);
IF IBUF THEN
LOOKUP ($FILE:CHANNEL[Q], $FILE:NAME[Q], $FILE:EOF[Q]);
IF OBUF AND NOT ($FILE:EOF[Q]) THEN
ENTER ($FILE:CHANNEL[Q], $FILE:NAME[Q], $FILE:EOF[Q]);
END;
$FILE:PAGNUM[Q]_1;
IF $FILE:EOF[Q] THEN RELEASE($FILE:CHANNEL[Q]);
RETURN(Q)
END "OPENUP";
COMMENT Sail I/O should be rewritten to do this ^;
RECORD!POINTER ($FILE) PROCEDURE GETFILE
(STRING PROMPT; INTEGER MODE, I, O);
BEGIN "GETFILE"
RECORD!POINTER ($FILE) F; INTEGER REASON;
WHILE TRUE DO BEGIN "try"
PRINT (PROMPT);
IF (REASON_$FILE:EOF[F_OPENUP (INCHWL,
MODE, I, O)])=0 THEN RETURN (F);
IF REASON=-1 THEN
PRINT ("Device ", $FILE:DEVICE[F], " not available.")
ELSE PRINT("Error, ", CASE (0 MAX REASON MIN 4) OF
("no such file ", "illegal PPN ", "protection ",
"busy ", "??? "), $FILE:NAME[F], CRLF);
END "try";
END "GETFILE";
RECORD!POINTER ($FILE) SRC, SNK;
INTEGER FFLFTAB;
SETBREAK (COLONTAB_GETBREAK, ":", "", "ISN");
WHILE TRUE DO BEGIN "big loop"
STRING LINE;
SRC_GETFILE ("Copy from:", 0, 5, 0);
$FILE:COUNT[SRC]_200;
SNK_GETFILE (" to:", 0, 0, 5);
SETBREAK (FFLFTAB_GETBREAK, FF&LF, "", "INA");
WHILE TRUE DO BEGIN "a line"
LINE_INPUT ($FILE:CHANNEL[SRC], FFLFTAB);
IF $FILE:EOF[SRC] THEN DONE;
IF $FILE:BRCHAR[SRC]=FF THEN BEGIN
PRINT (" ", $FILE:PAGNUM[SRC]);
LINE_LINE&
INPUT ($FILE:CHANNEL[SRC], FFLFTAB) END;
CPRINT ($FILE:CHANNEL[SNK], ";", LINE)
END "a line";
RELEASE ($FILE:CHANNEL[SRC]);
RELEASE ($FILE:CHANNEL[SNK])
END "big loop";
END "COPY"
EXECUTION TIME ROUTINES Type Conversion Routines SETFORMAT GETFORMAT
SECTION 7
EXECUTION TIME ROUTINES
Please read Execution Time Routines in General, page 62, if you are
unfamiliar with the format used to describe runtime routines.
7.1 Type Conversion Routines
******************************* SETFORMAT ******************************;
SETFORMAT (WIDTH, DIGITS)
This function allows specification of a minimum width for strings created by
the functions CVS, CVOS, CVE, CVF, and CVG (see page 86 and following). If
WIDTH is positive then enough blanks will be inserted in front of the
resultant string to make the result at least WIDTH characters long. The
sign, if any, will appear after the blanks. If WIDTH is negative then
leading zeroes will be used in place of blanks. The sign, of course, will
appear before the zeroes. The parameter WIDTH is initialized by the system
to zero.
In addition, the DIGITS parameter allows one to specify the number of digits
to appear following the decimal point in strings created by CVE, CVF, and
CVG. This number is initially 7. See the writeups on these functions for
details.
NOTE: All type conversion routines, including those that SETFORMAT applies
to, are performed at compile time if their arguments are constants.
However, Setformat does not have its effect until execution time.
Therefore, CVS, CVOS, CVE, CVF, and CVG of constants will have no leading
zeros and 7 digits (if any) following the decimal point.
***************************** GETFORMAT ******************************;
GETFORMAT (@WIDTH, @DIGITS)
The WIDTH and DIGIT settings specified in the last SETFORMAT call are
returned in the appropriate reference parameters.
CVS CVD CVOS CVO CVE, CVF, CVG
********************************** CVS *********************************;
"ASCII!STRING" _ CVS (VALUE);
The decimal Integer representation of VALUE is produced as an ASCII String
with leading zeroes omitted (unless WIDTH has been set by SETFORMAT to some
negative value). "-" will be concatenated to the String representing the
decimal absolute value of VALUE if VALUE is negative.
********************************** CVD *********************************;
VALUE _ CVD ("ASCII!STRING")
ASCII!STRING should be a String of decimal ASCII characters perhaps
preceded by plus and/or minus signs. Characters with ASCII values SPACE
('40) are ignored preceding the number. Any character not a digit will
terminate the conversion (with no error indication). The result is a
(signed) integer.
********************************** CVOS ********************************;
"ASCII!STRING" _ CVOS (VALUE)
The octal Integer representation of VALUE is produced as an ASCII String
with leading zeroes omitted (unless WIDTH has been set to some negative
value by SETFORMAT. No "-" will be used to indicate negative numbers. For
instance, -5 will be represented as "777777777773".
********************************** CVO *********************************;
VALUE _ CVO ("ASCII!STRING")
This function is the same as CVD except that the input characters are deemed
to represent Octal values.
***************************** CVE, CVF, CVG ****************************;
"STRING" _ CVE (VALUE);
"STRING" _ CVF (VALUE);
"STRING" _ CVG (VALUE)
Real number output is facilitated by means of one of three functions CVE,
CVASC, CVASTR, CVSTR
CVG, or CVF, corresponding to the E, G, and F formats of FORTRAN IV. Each
of these functions takes as argument a real number and returns a string.
The format of the string is controlled by another function SETFORMAT
(WIDTH, DIGITS) (see page 85) which is used to change WIDTH from zero and
DIGITS from 7, their initial values. WIDTH specifies the minimum string
length. If WIDTH is positive leading blanks will be inserted and if
negative leading zeros will be inserted.
The following table indicates the strings returned for some typical numbers.
! indicates a space and it is assumed that WIDTH_10 and DIGITS_3.
CVF CVE CVG
.000 .100@-3! .100@-3!
.001 .100@-2! .100@-2!
.010 .100@-1! .100@-1!
.100 .100 .100
!1.000 .100@1 1.00
10.000 .100@2 10.0
!100.000 .100@3 100.
1000.000 .100@4 .100@4
!10000.000 .100@5 .100@5
!100000.000 .100@6 .100@6
!1000000.000 .100@7 .100@7
-1000000.000 !-.100@7 !-.100@7
The first character ahead of the number is either a blank or a minus sign.
With WIDTH_-10 plus and minus 1 would print as:
CVF CVE CVG
!00001.000 !0.100@1 !01.00
-00001.000 -0.100@1 -01.00
All numbers are accurate to one unit in the eighth digit. If DIGITS is
greater than 8, trailing zeros are included; if less than eight, the number
is rounded.
************************** CVASC, CVASTR, CVSTR ************************;
VALUE _ CVASC ("STRING");
"STRING" _ CVASTR (VALUE);
"STRING" _ CVSTR (VALUE)
These routines convert between a Sail String and an integer containing 5
ASCII characters left justified in a 36-bit word; the extra bit is made zero
(CVASC) or ignored (CVASTR, CVSTR). CVASC converts from String to ASCII.
Both CVSTR and CVASTR convert from a word of ASCII to a string. CVSTR
always returns a string of length five, while CVASTR stops converting at the
first null ('0) character.
CV6STR, CVSIX, CVXSTR String Manipulation Routines EQU LENGTH
CVASTR (CVASC ("ABC")) is "ABC"
CVSTR (CVASC ("ABC")) is "ABC" & 0 & 0
************************* CV6STR, CVSIX, CVXSTR ************************;
"STRING" _ CV6STR (VALUE);
VALUE _ CVSIX ("STRING");
"STRING" _ CVXSTR (VALUE)
The routines CV6STR, CVSIX, and CVXSTR are the SIXBIT analogues of CVASTR,
CVASC, and CVSTR, respectively. The character codes are converted, ASCII in
the String SIXBIT in the integer. CVXSTR always returns a string of
length six, while CV6STR stops converting upon reaching a null character.
CV6STR (CVSIX ("XYZ")) is "XYZ", not "XYZ ".
CV6STR (CVSIX ("X Y Z")) is "X", not "X Y Z" or "XYZ".
7.2 String Manipulation Routines
********************************** EQU *********************************;
VALUE _ EQU ("STR1", "STR2")
The value of this function is TRUE if STR1 and STR2 are equal in length and
have identically the same characters in them (in the same order). The value
of EQU is FALSE otherwise.
********************************* LENGTH *******************************;
VALUE _ LENGTH ("STRING")
LENGTH is always an integer-valued function. If the argument is a String,
its length is the number of characters in the string. The length of an
algebraic expression is always 1 (see page 46). LENGTH is usually compiled
in line.
LOP SUBSR, SUBST Liberation-from-Sail Routines CODE
********************************** LOP *********************************;
VALUE _ LOP (@STRINGVAR)
The LOP operator applied to a String variable removes the first character
from the String and returns it in the form given in page 46 above. The
String no longer contains this character. LOP applied to a null String has
a zero value. LOP is usually compiled in line. LOP may not appear as a
statement.
****************************** SUBSR, SUBST ****************************;
"RSLT" _ SUBSR ("STRING", LEN, FIRST);
"RSLT" _ SUBST ("STRING", LAST, FIRST)
These routines are the ones used for performing substring operations.
SUBSR (STR, LEN, FIRST) is STR[FIRST FOR LEN] and SUBST (STR, LAST, FIRST)
is STR[FIRST TO LAST].
7.3 Liberation-from-Sail Routines
********************************** CODE ********************************;
RESULT _ CODE (INSTR, @ADDR)
This function is equivalent to the FAIL statements:
EXTERNAL .SKIP. ;DECLARE AS !SKIP! IN SAIL
SETOM .SKIP. ;ASSUME SKIP
MOVE 0,INSTR
ADDI 0,@ADDR
XCT 0
SETZM .SKIP. ;DIDN'T SKIP
RETURN (1)
In other words, it executes the instruction formed by adding the address of
the ADDR variable (passed by reference) to the number INSTR. Before the
operation is carried out, AC1 is loaded from a special cell (initially 0).
AC1 is returned as the result, and also stored back into the special cell
after the instruction is executed. The global variable !SKIP! (.SKIP. in
DDT or FAIL) is FALSE (0) after the call if the executed instruction did not
skip; TRUE (currently -1) if it did. Declare this variable as
EXTERNAL INTEGER !SKIP! if you want to use it.
CALL CALLI USERCON GOGTAB
********************************** CALL ********************************;
RESULT _ CALL (VALUE, "FUNCTION")
This function is equivalent to the FAIL statements:
EXTERNAL .SKIP.
SETOM .SKIP.
MOVE 1,VALUE
CALL 1,[SIXBIT /FUNCTION/]
SETZM .SKIP. ;DID NOT SKIP
RETURN (REGISTER 1)
TENEX users should see more on CALL, page 148.
****************************** CALLI ********************************;
RESULT _ CALLI (VALUE, FUNCTION)
(TYMSHARE only.) Like CALL, only CALLI.
******************************** USERCON *******************************;
USERCON (@INDEX, @VALUE, FLAG)
This function allows inspection and alteration of the "User Table". The
user table is always loaded with your program and contains many interesting
variables. Declare an index you are interested in as an External Integer
(e.g., EXTERNAL INTEGER REMCHR). This will, when loaded, give an address
which is secretly a small Integer index into the User Table. When passed by
reference, this index is available to USERCON. The names and meanings of
the various User Table indices can be found in the file HEAD, wherever Sail
compiler program text files are sold.
USERCON always returns the current value of the appropriate User Table entry
(the Global Upper Segment Table is used if FLAG is negative and your system
knows about such things). If FLAG is odd, the contents of VALUE before the
call replaces the old value in the selected entry of the selected table.
By now the incredible danger of this feature must be apparent to you. Be
sure you understand the ramifications of any changes you make to any User
Table value. GOGTAB
Direct access to the user table can be gained by declaring EXTERNAL INTEGER
ARRAY GOGTAB[0:n]; The clumsy USERCON linkage is obsolete.
The symbolic names of all GOGTAB entries can be obtained by requiring
USERERR ERMSBF EDFILE
SYS:GOGTAB.DEF (<SAIL>GOGTAB.DEF on TENEX) as a source file. This file
contains DEFINEs for all of the user table entries.
******************************** USERERR *******************************;
USERERR (VALUE, CODE, "MSG",
"RESPONSE"(NULL))
USERERR generates an error message. See page 243 for a description of the
error message format. MSG is the error message that is printed on the
teletype or sent to the log file. If CODE = 2, VALUE is printed in decimal
on the same line. Then on the next line the "Last SAIL call" message may be
typed which indicates where in the user program the error occurred. If CODE
is 1 or 2, a "" will be typed and execution will be allowed to continue.
If it is 0, a "?" is typed, and no continuation will be permitted. The
string RESPONSE, if included in the USERERR call, will be scanned before the
input buffer is scanned. In fact, if the string RESPONSE satisfies the
error handler, the input buffer will not be scanned at all. Examples:
USERERR (0, 1, "LINE TOO LONG"); Gives
error message and allows continuation.
USERERR (0, 1, NULL, "QLA"); Resets mode
of error handler to Quiet, Logging, and
Automatic continuation. Then continues.
****************************** ERMSBF *******************************;
ERMSBF (NEWSIZE)
This routine insures that error messages of NEWSIZE characters can be
handled. The error message buffer is initially 256 characters, which is
sufficient for any Sail-generated error. USERERR can generate longer
messages, however.
********************************* EDFILE *******************************;
EDFILE ("FILENAME", LINE, PAGE, BITS(0))
(Not on TENEX.) Exits to an editor. Which editor is determined by the bits
which are on in the second parameter, LINE. If bit 0 or bit 1 (600000,,0
bits) is on, then LINE is assumed to be ASCID and SOS is called. If neither
of these bits is on, then LINE is assumed to be of the form attach
count,,sequential line number and E is called. PAGE is the binary page
number. BITS defaults to zero and controls the editing mode.
INIACS Byte Manipulation Routines LDB, DPB, etc. POINT
0 edit
1 no directory (as in /N)
2 readonly (as in /R)
4 create (as in /C)
In addition, the accumulators are set up from INIACS (see below) so that the
E command X RUN will run the dump file from which the current program was
gotten. [Accumulators 0 (file name), 1 (extension), and 6 (device) are
loaded from the corresponding values in INIACS.]
********************************* INIACS *******************************;
The contents of locations 0-'17 are saved in block INIACS when the core
image is started for the first time. Declare INIACS as an external integer
and use START!CODE or MEMORY[LOCATION(INIACS)+n] to reference this block.
7.4 Byte Manipulation Routines
***************************** LDB, DPB, etc. ***************************;
VALUE _ LDB (BYTE!POINTER);
VALUE _ ILDB (@ BYTE!POINTER);
DPB (BYTE, BYTE!POINTER);
IDPB (BYTE, @ BYTE!POINTER);
IBP (@ BYTE!POINTER)
LDB, ILDB, DPB, IDPB, and IBP are Sail constructs used to invoke the PDP-10
byte loading instructions. The arguments to these functions are expressions
which are interpreted as byte pointers and bytes. In the case of ILDB,
IDPB, and IBP, you are required to use an algebraic variable as argument as
the byte!pointer, so that the byte pointer (i.e. that algebraic variable)
may be incremented.
********************************* POINT ********************************;
VALUE _ POINT (BYTE SIZE,
@EFFECTIVE ADDRESS, LAST BIT NUMBER)
POINT returns a byte pointer (hence it is of type integer). The three
arguments correspond exactly to the three arguments to the POINT pseudo-op
in FAIL.
Other Useful Routines CVFIL FILEINFO ARRINFO
7.5 Other Useful Routines
********************************* CVFIL ********************************;
VALUE _ CVFIL ("FILE!SPEC", @EXTEN, @PPN)
FILE!SPEC has the same form as a file name specification for LOOKUP or
ENTER. The SIXBIT for the file name is returned in VALUE. SIXBIT values
for the extension and project-programmer numbers are returned in the
respective reference parameters. Any unspecified portions of the FILE!SPEC
will result in zero values. The global variable !SKIP! will be 0 if no
errors occurred, non-zero if an invalid file name specification is
presented.
******************************** FILEINFO ******************************;
FILEINFO (@INFOARRAY)
FILEINFO fills the 6-word array INFOARRAY with the following six words from
the most recent LOOKUP, ENTER, or RENAME:
FILENAME
EXT,,(2)hidate2 (15)date1
(9)prot (4)Mode (11)time (12)lodate2
negative swapped word count
0 (unless opened in magic mode)
0
See [SysCall]; TENEX users should use JFNS instead.
******************************** ARRINFO *******************************;
VALUE _ ARRINFO (ARRAY, PARAMETER)
ARRINFO (ARRAY, -1) is the number of dimensions for the array. This
number is negative for String arrays.
ARRINFO (ARRAY, 0) is the total size of the array in words.
ARRINFO (ARRAY, 1) is the lower bound for the first dimension.
ARRBLT ARRTRAN ARRCLR IN!CONTEXT
ARRINFO (ARRAY, 2) is the upper bound for the first dimension.
ARRINFO (ARRAY, 3) is the lower bound for the second dimension.
ARRINFO (... etc.
********************************* ARRBLT *******************************;
ARRBLT (@DEST, @SOURCE, NUM)
NUM words are transferred (using BLT) from consecutive locations starting at
SOURCE to consecutive locations starting at DEST. No bounds checking is
performed. This function does not work well for String Arrays (nor set nor
list arrays).
******************************** ARRTRAN *******************************;
ARRTRAN (DESTARR, SOURCEARR)
This function copies information from SOURCEARR to DESTARR. The transfer
starts at the first data word of each array. The minimum of the sizes of
SOURCEARR and DESTARR is the number of words transferred.
********************************* ARRCLR *******************************;
ARRCLR (ARRAY, VALUE(0))
This routine stores VALUE into each element of ARRAY. The most common use
is with VALUE omitted, which clears the array; i.e., arithmetic arrays get
filled with zeros, string arrays with NULLs, itemvar arrays with ANYs,
record!pointer arrays with NULL!RECORD. One may use ARRCLR with set and
list arrays, but the set and list space will be lost (i.e., un-garbage-
collectible). Do not supply anything other than 0 (0, NULL, PHI, NIL,
NULL!RECORD) for VALUE when clearing a string, set, list, or record!pointer
array unless you know what you are doing. Using a real value for an itemvar
array is apt to cause strange results. (If you use an integer then ARRAY
will be filled with CVI (value).)
******************************* IN!CONTEXT *****************************;
VALUE _ IN!CONTEXT (VARI, CONTXT)
CHNCDB Numerical Routines SIN COS SIND COSD ASIN ACOS ATAN ATAN2 SINH COSH TANH SQRT
IN!CONTEXT is a boolean which tells one if the specified variable is in the
specified context. VARI may be any variable, array element, array name, or
Leap variable. If that variable, element or array was REMEMBERed in that
context, IN!CONTEXT will return True. IN!CONTEXT will also return true if
VARI is an array element and the whole array was Remembered in that context
(by using REMEMBER <array!name>). On the other hand, if VARI is an array
name, then IN!CONTEXT will return true only if one has Remembered that array
with a REMEMBER <array!name>.
********************************* CHNCDB *******************************;
VALUE _ CHNCDB (CHANNEL)
(Not on TENEX.) This integer procedure returns the address of the block of
storage which Sail uses to keep track of the specified channel. It is
provided for the benefit of assembly language procedures that may want to
do I/O inside some fast inner loop, but which may want to live in a Sail
core image & use the Sail OPEN, etc.
7.6 Numerical Routines
These numerical routines are new as predeclared runtimes in Sail. The
routines themselves are quite standard.
The standard trigonometric functions. ASIN, ACOS, ATAN and ATAN2 return
results in radians. The ATAN2 call takes arc-tangent of the quotient of its
arguments; in this way, it correctly preserves sign information.
REAL PROCEDURE SIN (REAL RADIANS);
REAL PROCEDURE COS (REAL RADIANS);
REAL PROCEDURE SIND (REAL DEGREES);
REAL PROCEDURE COSD (REAL DEGREES);
REAL PROCEDURE ASIN (REAL ARGUMENT);
REAL PROCEDURE ACOS (REAL ARGUMENT);
REAL PROCEDURE ATAN (REAL ARGUMENT);
REAL PROCEDURE ATAN2 (REAL NUM, DEN)
The hyperbolic trigonometric functions.
REAL PROCEDURE SINH (REAL ARGUMENT);
REAL PROCEDURE COSH (REAL ARGUMENT);
REAL PROCEDURE TANH (REAL ARGUMENT)
The square-root function:
REAL PROCEDURE SQRT (REAL ARGUMENT)
RAN LOG EXP TRIGINI OVERFLOW ENTRY POINTS OVERFLOW IMPLEMENTATION
A pseudo-random number generator. The argument specifies a new value for
the seed (if the argument is 0, the old seed value is used. Thus to get
differing random numbers, this argument should be zero.) Results are
normalized to lie in the range [0,1].
REAL PROCEDURE RAN (INTEGER SEED)
Logarithm and exponentiation functions. These functions are the same ones
used by the Sail exponentiation operator. The base is e (2.71828182845904).
The logarithm to the base 10 of e is 0.4342944819.
REAL PROCEDURE LOG (REAL ARGUMENT);
REAL PROCEDURE EXP (REAL ARGUMENT)
These functions may occasionally be asked to compute numbers that lie
outside the range of legal floating-point numbers on the PDP-10. In these
cases, the routines issue sprightly error messages that are continuable.
OVERFLOW
In order to better perform their tasks, these routines enable the system
interrupt facility for floating-point overflow and underflow errors. If an
underflow is detected, the results are set to 0 (a feat not done by the PDP-
10 hardware, alas). Be aware that such underflow fixups will be done to
every underflow that occurs in your program. For further implementation
details, see the section below.
If you would like to be informed of any numerical exceptions, you can call
the runtime:
TRIGINI (LOCATION (simple-procedure-name))
Every floating-point exception that is not expected by the interrupt handler
(the numerical routines use a special convention to indicate that arithmetic
exception was expected) will cause the specified simple procedure to be
called. This procedure may look around the world as described for 'export'
interrupt handlers, page 212. If no TRIGINI call is done, the interrupt
routine will simply dismiss unexpected floating-point interrupts.
ENTRY POINTS
In order to avoid confusion (by the loader) with older trig packages, the
entry points of the Sail arithmetic routines all have a "$" appended to the
end. Thus, SIN has the entry point SIN$, etc. WARNING: If a program plans
to use the Sail intrinsic numerical routines, it should NOT include external
declarations to them, since this will probably cause the FORTRAN library
routines to be loaded.
OVERFLOW IMPLEMENTATION
This section may be skipped by all but those interested in interfacing
number crunching assembly code (where overflow and underflow are expected to
be a problem) with Sail routines.
The Sail arithmetic interrupt routines first check to see if the interrupt
was caused by floating exponent underflow. If it was, then the result is
set to zero, be it in an accumulator, memory, or both. Then if the
arithmetic instruction that caused the interrupt is followed by a JFCL, the
AC field of the JFCL is compared with the PC flag bits to see if the JFCL
tests for any of the flags that are on. If it does, those flags are cleared
and the program proceeds at the effective address of the JFCL (i.e., the
hardware is simulated in that case). Note that no instructions may
intervene between the interrupt-causing instruction and the JFCL or the
interrupt routines will not see the JFCL. They only look one instruction
ahead. Note that in any case, floating exponent underflow always causes the
result to be set to zero. There is no way to disable that effect.
PRINT Syntax Semantics DEFAULT FORMATS
SECTION 8
PRINT
8.1 Syntax
<print!statement>
::= PRINT ( <expression!list> )
::= CPRINT ( <integer!expression> ,
<expression!list> )
8.2 Semantics
The new constructs PRINT and CPRINT are conveniences for handling character
output. Code which formerly looked like
OUTSTR ("The values are " & CVS (I) & " and " &
CVG (X) & " for item " & CVIS (IT, JUNK));
may now be written
PRINT ("The values are ", I, X, " for item ", IT);
The first expression in <expression!list> is evaluated, formatted as a
string, and routed to the appropriate destination. Then the second
expression is evaluated, formatted, and dispatched; etc. (If an expression
is an assignment expression or a procedure call then side effects may
occur.)
DEFAULT FORMATS
String expressions are simply sent to the output routine. Integer
expressions are first sent to CVS, and Real expressions are passed to CVG;
the current SETFORMAT parameters are used. Item expressions use the print
name for the item if one exists, otherwise ITEM!nnnn, where nnnn is the item
number. Sets and lists show their item components separated by commas.
Sets are surrounded by single braces and lists by double braces. PHI and
NIL are printed for the empty set and empty list respectively. Record
pointers are formatted as the name of the record class, followed by a ".",
followed by the (decimal) address of the record. NULL!RECORD is printed for
the empty record.
If the default format is not satisfactory then the user may give a function
call as an argument. For example,
PRINT (CVOS (I));
DESTINATIONS SETPRINT, GETPRINT
will print I in octal, since CVOS is called first. (The expression CVOS (I)
is of course a String expression.) Wizards may also change the default
formatting function for a given syntactic type.
DESTINATIONS
CPRINT interprets <integer!expression> as a Sail channel number and sends
all output to that channel. The following two statements are functionally
equivalent:
CPRINT (CHAN, "The values are ", I, " and ", X);
OUT (CHAN, "The values are "&CVS (I)&" and "&CVG (X));
PRINT initially sends all output to the terminal but can also direct output
to a file or any combination of terminal and/or file. The modes of PRINT
are (dynamically) established and queried by SETPRINT and GETPRINT.
*************************** SETPRINT, GETPRINT *************************;
SETPRINT ("FILE!NAME", "MODE");
"MODE" _ GETPRINT
Here MODE is a single character which represents the destination of PRINT
output.
MODE MEANING
"T" the Terminal gets all PRINT output. If an output file is open
then close it. "T" is the mode in which PRINT is initialized.
"F" File gets PRINT output. If no file is open then open one as
described below.
"B" Both terminal and file get PRINT output. If no file is open then
open one as described below.
"N" Neither the file nor the terminal gets any output. If a file is
open then close it.
"S" Suppress all output, but open a file if none is open.
"O" a file is Open, but the terminal is getting all output. If no
file is open then open one as described below.
"C" the terminal gets output, but ignore whether or not a file is
open and whether or not it is getting output.
"I" terminal does not get output. Ignore whether or not a file is
open and whether or not file is getting any output.
SIMPLE USE CAVEATS
The first 6 possibilities represent the logical states of the PRINT system
and are the characters which GETPRINT can return. The "C" and "I" modes
turn terminal output on and off without disturbing anything else. The PRINT
statement is initialized to mode "T" -- print to Terminal. Modes "T", "F",
and "B" are probably the most useful. The other modes are included for
completeness and allow the user to switch between various combinations
dynamically.
If SETPRINT is called in such a way that a file has to be opened -- e.g.,
mode "F" and no file is open -- then FILE!NAME will be used as the name of
the output file. If FILE!NAME is NULL then the filename will be obtained
from the terminal.
SETPRINT (NULL, "F");
first types the message
File for PRINT output *
and uses the response as the name of a file to open. On TENEX, GTJFN with
recognition is used; on TOPS-10 and its variants the filename is read with
INCHWL. The file opened by SETPRINT will be closed when the program
terminates by falling through the bottom. It will also be closed if the
user calls SETPRINT with some mode that closes the file -- e.g., "T" will
close an output file if one is open.
SETPRINT and GETPRINT are related only to PRINT; they have no effect on
CPRINT.
SIMPLE USE
Here are a few examples of common output situations.
1) PRINT to TERMINAL. Simply use PRINT; do not bother with SETPRINT.
2) PRINT to FILE. Call SETPRINT (NULL, "F"); and type the name of the
output file when it asks.
3) PRINT to FILE and TERMINAL. At the beginning of the program call
SETPRINT (NULL, "B"); and type the name when asked.
4) PRINT to FILE always and sometimes also to TERMINAL. Use SETPRINT
(NULL, "B"); and give the name of the file when it asks. This sets
output to both the terminal and the file. Then to ignore the terminal
(leaving the file alone), call SETPRINT (NULL, "I"); To resume output at
the terminal use SETPRINT (NULL, "C"); This is useful for obtaining a
cleaned-up printout on the file with error messages going to the
terminal.
CAVEATS
Trying to exploit the normal Sail type conversions will probably lead to
FOR WIZARDS ONLY
trouble with PRINT and CPRINT. Printing single ASCII characters is a
particular problem.
OUTSTR ('14);
prints a form-feed onto the terminal , but
PRINT ('14);
prints "12". The reason, of course, is the default formatting of integers
by PRINT or CPRINT. This problem is particularly severe with macros that
have been defined with an integer to represent an ASCII character. For
example,
DEFINE TAB="'11";
PRINT (TAB);
will print "9". The solution is to define the macro so that it expands to a
STRING constant rather than an integer.
DEFINE TAB=" "; or
DEFINE TAB=('11 & NULL);
Also, remember that the first argument to CPRINT is the channel number.
FOR WIZARDS ONLY
All output going to either the PRINT or CPRINT statements can be trapped by
setting user table entry $$PROU to the address of a SIMPLE procedure that
has one string and one integer argument.
SIMPLE PROCEDURE MYPRINT
(INTEGER CHAN; STRING S);
BEGIN ... END;
GOGTAB[$$PROU] _ LOCATION (MYPRINT);
The CHAN argument is either the CHAN argument for CPRINT, or -1 for PRINT.
If this trap is set then all output from PRINT and CPRINT goes through the
user routine and is not printed unless the user invokes OUT or OUTSTR from
within the trap routine itself.
To trap the formatting function for any syntactic type the user should set
the appropriate user table address to the location of a function that
returns a string and takes as an argument the syntactic type in question.
To print integers in octal , preceded by "'", use
SIMPLE STRING PROCEDURE MYCVOS (INTEGER I);
RETURN ("'" & CVOS (I));
GOGTAB[$$FINT] _ LOCATION (MYCVOS);
The names for the addresses in the user table associated with each
formatting function are:
INDEXTYPE
$$FINT INTEGER
$$FREL REAL
$$FITM ITEM
$$FSET SET
$$FLST LIST
$$FSTR STRING
$$FREC RECORD!POINTER
To restore any formatting function to the default provided by the PRINT
system, zero the appropriate entry of the user table.
MACROS AND CONDITIONAL COMPILATION Syntax
SECTION 9
MACROS AND CONDITIONAL COMPILATION
9.1 Syntax
<define>
::= DEFINE <def!list> ;
::= REDEFINE <def!list> ;
::= EVALDEFINE <def!list> ;
::= EVALREDEFINE <def!list> ;
<def!list>
::= <def>
::= <def!list> , <def>
<def>
::= <identifier> = <macro!body>
::= <identifier> ( <id!list> ) =
<macro!body>
::= <identifier> <string!constant> =
<macro!body>
::= <identifier> ( <id!list> )
<string!constant> = <macro!body>
<macro!body>
::= <delimited!string>
::= <constant!expression>
::= <macro!body> & <macro!body>
<macro!call>
::= <macro!identifier>
::= <macro!identifier> (
<macro!param!list> )
::= <macro!identifier> <string!constant>
( <macro!param!list> )
<macro!identifier>
::= <identifier>
<macro!param!list>
::= <macro!param>
::= <macro!param!list> , <macro!param>
<cond!comp!statement>
::= <conditional!c.c.s.>
::= <while!c.c.s.>
::= <for!c.c.s.>
::= <for!list!c.c.s.>
::= <case!c.c.s.>
<conditional!c.c.s.>
::= IFC <constant!expression> THENC
<anything> ENDC
::= IFC <constant!expression> THENC
<anything> ELSEC <anything> ENDC
::= IFCR <constant!expression> THENC
<anything> ENDC
::= IFCR <constant!expression> THENC
<anything> ELSEC <anything> ENDC
<while!c.c.s.>
::= WHILEC <delimited!expr> DOC
<delimited!anything> ENDC
<for!c.c.s.>
::= FORC <identifier> _
<constant!expression> STEPC
<constant!expression> UNTILC
<constant!expression> DOC
<delimited!anything> ENDC
<for!list!c.c.s.>
::= FORLC <identifier> _ (
<macro!param!list> ) DOC
<delimited!anything> ENDC
<case!c.c.s.>
::= CASEC <constant!expression> OFC
<delimited!anything!list> ENDC
<delimited!anything!list>
::= <delimited!anything>
::= <delimited!anything!list> ,
<delimited!anything>
Delimiters
<assignc>
::= ASSIGNC <identifier> = <macro!body> ;
<delimited!string>, <macro!param>, <delimited!expr>, <anything> and
<delimited!anything> are explained in the following text.
9.2 Delimiters
There are two types of delimiters used by the Sail macro scanner: macro body
delimiters and macro parameter delimiters. Their usage will be precisely
defined in the sections on Macro Bodies and Parameters to Macros. Here we
will discuss their declaration and scope, which is very important when using
source files with different delimiters (see page 24 to find out about source
files).
Sail initializes both left and right delimiters of both body and parameter
delimiters to the double quote ("). One may change delimiters by saying
REQUIRE "<>" DELIMITERS.
In this example, the left and right body delimiters become "" and "",
while the left and right parameter delimiters become "<" and ">". Require
Delimiters may appear wherever a statement or declaration is legal. One
should Require Delimiters whenever all but the most simple macros are going
to be used. The first Require Delimiters will initialize the macro
facility; if this is not done, some of the following conveniences will not
exist and only very simple macros like defining CRLF = "('12 & '15 )" may be
done.
Delimiters do not follow block structure. They persist until changed.
Furthermore, each time new delimiters are Required, they are stacked on a
special "delimiters stack". The old delimiters may be revived by saying
REQUIRE UNSTACK!DELIMITERS
Thus, each source file with macros should begin with a Require delimiters,
and end with an Unstack!delimiters. It is impossible to Unstack off the
bottom of the stack. The bottom element of the stack is the double quote
delimiters that Sail initialized the program to. If you Unstack from these,
the Unstack will become a no-op, and the double quote delimiters remain the
delimiters of your program.
One may circumvent the delimiter stacking feature by saying
REQUIRE "<>" REPLACE!DELIMITERS
Macros
instead of REQUIRE "<>" DELIMITERS. This doesn't deactivate the stacking
feature, it merely changes the active delimiters without stacking them.
To revert to the primitive, initial delimiter mode where double quotes are
the active delimiters, one may say
REQUIRE NULL DELIMITERS
Null delimiters are stacked in the delimiter stack in the ordinary REQUIRE
"<>" DELIMITERS way. In null delimiters mode, the double quote character
may be included in the macro body or macro parameter by using two double
quotes:
DEFINE SOR = "OUTSTR(""SORRY"");";
The Null Delimiters mode is essentially the macro facility of ancient
versions of Sail where " was the only delimiter. Programs written ancient
in Sail versions will run in Null Delimiters mode. Null delimiters mode has
all the rules and quirks of the prehistoric Sail macro system (the old Sail
macro facility is described in [Swinehart & Sproull], Section 13).
Compatibility with the ancient Sail is the only reason for Null Delimiters.
9.3 Macros
We will delay the discussion of macros with parameters until the next
section. A macro without parameters is declared by saying:
DEFINE <macro!name> = <macro!body> ;
where <macro!name> is some legal identifier name (see page 228 for a
definition of a legal identifier name). <macro!body>s can be simply a
sequence of Ascii characters delimited by macro body delimiters, or they can
be quite complex. Once the macro has been defined, the macro body is
substituted for every subsequent appearance of the macro name. Macros may
be called in this way at any point in a Sail program, except inside a
Comment or a string constant.
Macro declarations may also appear virtually anywhere in a Sail program.
When the word DEFINE is scanned by Sail, the scanner traps to a special
production. The Define is parsed, and the scanner returns to its regular
mode as if there had been no define there at all. Thus things like
I _ J + 5 + DEFINE CON = '777; K^2;....
are perfectly acceptable. However, don't put a Define in a string constant
or a Comment.
SCOPE MACRO BODIES DELIMITED STRINGS
SCOPE
Macros obey block structure. Each DEFINE serves both as a declaration and
an assignment of a macro body to the newly declared symbol. Two DEFINEs of
the same symbol in the at the same lexical level will be flagged as an
error. However, it is possible to change the macro body assigned to a macro
name without redeclaring the name by using saying REDEFINE instead of
DEFINE. For example,
BEGIN
...
BEGIN
...
DEFINE SQUAK = OUTSTR("OUTER BLOCK");;
...
BEGIN
...
REDEFINE SQUAK = OUTSTR("INNER BLOCK");;
...
END;
...
SQUAK COMMENT Here the program types
"INNER BLOCK";
END; COMMENT Here SQUAK is undefined.
If SQUAK were included here, you'd
get the error message
"UNDEFINED IDENTIFIER:SQUAK";
END
REDEFINE of a name that has not been declared in a DEFINE will act as a
DEFINE. That is, it will also declared the macro name as well as assigning
a body to it. MACRO BODIES
A Macro Body may be
1. A sequence of Ascii characters preceded by a left macro body
delimiter and followed by a right macro body delimiter.
2. An integer expression that may be evaluated at compile time.
3. A string expression that may be evaluated at compile time.
4. Concatenations of the above.
WARNING: Source file switching inside macros will not work.
DELIMITED STRINGS
Any sequence of Ascii characters, including " may be used as a macro body if
they are properly delimited. The macro body scanner keeps a count of the
number of left and right delimiters seen and will terminate its scan only
when it has seen the same number of each. This lets the macro body
delimiters "nest" so that one may include DEFINEs inside a macro body. For
example,
INTEGER COMPILE TIME EXPRESSIONS STRING COMPILE TIME EXPRESSIONS
DEFINE DEF =
DEFINE SYM = SYMBOL; SYM ;
One may temporarily override the active delimiters by including a two
character string before the "=" of the Define statement. For example:
DEFINE LES "&%" = & 0X<BIGGEST Y>X %;
The first character of the two character string becomes the left delimiter,
and the second becomes the right delimiter.
INTEGER COMPILE TIME EXPRESSIONS
Sail tries to do as much arithmetic as it can at compile time. In
particular, if you have an arithmetic expression of constants, such as
91.504 + (3.1415*8^(9-7))
% "Sail can convert strings"
then the whole expression will be evaluated at compile time and the
resultant constant, in this case 93.9263610, will be used in your code
instead of the constant expression. Runtime functions of constants will be
done at compile time too, if possible. EQU and the conversion routines
(CVS, CVO, etc.) will work.
When an integer compile time expression is scanned as part of a macro body,
it is immediately evaluated. The integer constant which results is
converted to a character string, and that character string used for the
place in the macro body of the integer expression. Thus,
DEFINE TTYUUO = '51 LSH 27 ;
will cause '51 LSH 27 to be evaluated, and the resulting constant,
5502926848, will be converted to the character string 5502926848, and that
character string assigned to the macro name TTYUUO.
STRING COMPILE TIME EXPRESSIONS
If a compile time expression has the type string (constant), the macro
scanner will evaluate the expression immediately. However, the string
constant that results will not be converted to the character string that
represents that constant, but to the character string with the same
characters that the string constant had. Thus, the way to use a macro for
string constants is to delimit the string constant like this:
DEFINE STRINCON = "Very long
complex string that is hard
to type more than once" ;
However, the automatic conversion of string constants to character strings
is helpful and indeed essential for automatic generation of identifiers:
HYBRID MACRO BODIES
DEFINE N = 1;
COMMENT we will use this like a variable;
DEFINE GENSYM =
DEFINE SYM = TEMP! & CVS(N);
COMMENT SYM is defined to be the character
string TEMP!# where # is an number;
REDEFINE N = N+1;
COMMENT This increments N;
SYM ;
COMMENT At the call of SYM, the character
string is read like program text. E.g...;
INTEGER GENSYM, GENSYM, GENSYM, GENSYM;
REAL GENSYM, GENSYM;
COMMENT We have generated 6 identifiers with
unique names, and declared 4 as integers,
2 as reals;
To convert a macro body to a string constant, one may use CVMS. Similarly,
a macro parameter is converted to a string constant by CVPS.
<string constant> _ CVMS (<macro name>);
<string constant> _ CVPS (<macro parameter name>)
A string that has the exact same characters as the macro body will be
returned. For example:
DEFINE A = B & C;
DEFINE ABC = CVMS (A) & & D;
COMMENT ABC now stands for the text B & C & D;
HYBRID MACRO BODIES
When two delimited strings are concatenated, the result is a longer
delimited string. "&" in compile time expression behaves the same way it
behaves in any expression. When a compile time expression is concatenated
to a delimited character string in a macro body, the result is exactly the
result one would get if the delimited character string were a string
constant, except that the result is a delimited character string. For
example:
DEFINE N = 1;
DEFINE M = 2;
DEFINE SYM = CVS(N*M + N^2) & -SQRT(N*M+1);
DEFINE SYM1 = 3-SQRT(N*M+1);
Here SYM is exactly the same as SYM1.
Macros with Parameters
9.4 Macros with Parameters
One defines a macro with parameters by specifying the formal parameters in a
list following the macro name:
DEFINE MAC (A, B) = IF A THEN B ELSE ERR_1;;
One calls a macro with parameters by including a list of delimited character
strings that will be substituted for each occurrence of the corresponding
formal in the macro body. For example,
COMMENT we assume that "<" and ">" are the
parameter delimiters at this point;
MAC (<BYTES LAND (BITMASK + '2000)>, <
BEGIN
WWDAT _ FETCH (BYTES, ENVIRON);
COLOR[WWDAT] _ '2000;
END >)
expands to
IF BYTES LAND (BITMASK + '2000) THEN
BEGIN
WWDAT _ FETCH (BYTES, ENVIRON);
COLOR[WWDAT] _ '2000;
END
ELSE ERR_1;
Parameter delimiters nest. Furthermore, if no delimiters are used about a
parameter, nesting counts are kept of "()", "[]", and "{~" character pairs.
The parameter scan will not terminate until the nesting counts of each of
the three pairs is zero. One may temporarily override the active parameter
delimiters by including a two character string ahead of the parameter list
in the macro call:
MAC "" (BYTES > '2000, MATCH(BYTES))
Formal parameters may not appear in compile time expressions that are used
to specify macro bodies. This is quite natural: compile time expressions
must be evaluated as they are scanned, but the value of a formal parameter
isn't known until later. However, if the macro body is a hybrid of
expressions and delimited character strings, then formal parameters may
appear in the delimited string parts.
When doing a CVMS on a macro with parameters, use only the macro name in the
call; the parameters are unnecessary. The string returned will have the two
character strings "1", "2", etc. (here stands for the Ascii character
'177) where the formal parameters were in the macro body. A "1" will
appear wherever the first formal parameter of the formal parameter list
appear in the macro body, a "2" will appear wherever the second parameter
Conditional Compilation
appeared, etc. The unfortunate appearance of the Ascii character '177 in
CVMS-generated strings is a product of the representation of macro bodies as
strings ending in '177, '0 (which CVMS removes), having '177, n for each
appearance of the nth formal parameter in the body.
9.5 Conditional Compilation
The compile time equivalents of the Sail IF, WHILE, FOR and CASE statements
are
IFC <CT expr> THENC <anything> ENDC
IFC <CT expr> THENC <anything> ELSEC
<anything> ENDC
WHILEC <CT expr> DOC <anything> ENDC
FORC <CT variable> _ <CT expr> STEPC <CT expr>
UNTILC <CT expr> DOC <anything> ENDC
FORLC <CT variable> _ (<macro param>, ... ,
<macro param>) DOC <anything> ENDC
CASEC <CT expr> OFC <anything>, <anything>,
... , <anything> ENDC
where <CT expr> is any compile time expression. <CT expr> could itself
include IFCs, FORCs or whatever. <CT variable> is a macro name such as N
from a define such as DEFINE N = MUMBLE; <macro param> is anything that is
delimited like a macro parameter. <anything> can be anything one could want
in his program at that point, including Defines and other conditional
compilation statements. The usual care must be taken with nested IFCs so
that the ELSECs match the desired THENCs. The "" and "" characters above
are to stand for the current MACRO BODY DELIMITER pair.
The semantics are exactly those of the corresponding runtime statements,
with one exception. When the list to a FORLC is null (i.e. it looks like "(
)" ), then the <anything> is inserted in the compilation once, with the <CT
variable> assigned to the null macro body.
Situations frequently occur where the false part of an IFC must have the
macros in it expanded in order to delimit the false part correctly. For
example,
Type Determination at Compile Time DECLARATION CHECK!TYPE EXPR!TYPE
DEFINE DEBUG!SELECT =
IFC DEBNUM = 2 THENC ;
DEFINE DEBUG!END =
ELSEC OUTSTR ("DEBUG POINT") ENDC;
Debug!select
OUTSTR ("DEBUG POINT #" & CVS (DBN));
Debug!end
If DEBNUM is not 2, then the program must expand the macro Debug!end in
order to pick up the ELSEC that terminates the false part of the
conditional. The expansion is only to pick up such tokens -- the text of
the false part is not sent to the scanner as the true part is. In order to
avoid such expansion, one may use IFCR (the R stands for "recursive")
instead of IFC.
As an added feature, when delimiters are required about an <anything> in the
above (such constructs are named <delimited!anything> in the BNF), one may
substitute a concatenation of constant expressions and delimited strings.
This is just like a macro body, except the concatenation MUST contain at
least one delimited string, thereby forcing the result of the concatenation
to be a delimited string, rather than a naked expression.
As a further added feature,
IFC <CT expr> THENC <anything> ELSEC
<anything> ENDC
may be substituted in FORCs, FORLCs, and WHILECs for the <anything>
following DOC.
NOTE: In a WHILEC, the expression must be delimited with the appropriate
macro body delimiters (hence the construct <delimited!expr> in the BNF).
9.6 Type Determination at Compile Time
To ascertain the type of an identifier at compile time, one may use the
integer function DECLARATION (<identifier>). This returns an integer with
bits turned on to represent the type of identifier. Exactly what the bits
represent is a dark secret and changes periodically anyway. The best way to
decode the integer returned by Declaration is to compare it to the integer
returned by CHECK!TYPE (<a string of Sail declarators>). A Sail declarator
is any of the reserved words used an a declaration. Furthermore, the
declarators must be listed in a legal order, namely, an order that is legal
in declarations (i.e. ARRAY INTEGER won't work). One may include as
arguments to CHECK!TYPE the following special tokens:
BUILT!IN LEAP!ARRAY RESERVED DEFINE CONOK
TOKEN EFFECT
BUILT!IN The bit that is on when a procedure is known to preserve ACs
0-'11 (except AC1 if returning a value) is returned. Sail
does not clear the ACs when compiling a call on a BUILT!IN
procedure.
LEAP!ARRAY The bit that is on when an identifier is an item or itemvar
with a declared array datum is returned (the discussion of
Leap starts on page 151).
RESERVED The bit that is on for a reserved word is returned.
DEFINE The bit that indicates the identifier is a macro name is
returned (note: a macro name as the argument to DECLARATION
will not be expanded).
CONOK The bit which says "this procedure will be evaluated at
compile time if all its arguments are constant expressions"
is returned.
Examples:
DECLARATION (FOO) = CHECK!TYPE (INTEGER)
This is an exact compare. Only if Foo is
an integer variable will equality hold.
DECLARATION (A) LAND CHECK!TYPE (ARRAY)
This is not an exact compare. If A is any
kind of an array, the LAND will be non-zero.
DECLARATION (CVS) = CHECK!TYPE(EXTERNAL CONOK
OWN BUILT!IN FORWARD STRING PROCEDURE)
The equality holds. FORWARD so that you can
redeclare it without complaints; OWN as a hack
which saves space in the compiler.
DECLARATION (BEG) LAND CHECK!TYPE (RESERVED)
This is non-zero only if one has said
LET BEG = BEGIN. DEFINE BEG = BEGIN
will only turn the Define bit of BEG on.
NOTE: if the <identifier> of DECLARATION has not yet been declared or was
declared in an inner block, then 0 is returned -- it is undeclared so it has
no type.
EXPR!TYPE returns the same bits that DECLARATION does, except that the
argument to EXPR!TYPE may be an expression and not just an identifier.
Miscelaneous Features COMPILE TIME I/O EVALDEFINE, EVALREDEFINE ASSIGNC NOMAC COMPILER!BANNER
9.7 Miscelaneous Features
COMPILE TIME I/O
Compile time input is handled by the REQUIRE "<file!name>" SOURCE!FILE
construct. <file!name> can be any legal file, including TTY: and MTA0: and
of course disk files. (MTA does not work for TENEX.) The file will be read
until the its end of file delimiter is scanned (<ctrl>Z for TTYs or
<meta><ctrl><lf> at SUAI), and its text will replace the REQUIRE statement
in the main file.
Compile time output is limited to typing a message on the user's teletype.
To do this say REQUIRE <string!constant> MESSAGE, and the <string!constant>
will appear on your teletype when the compilation hits that point in your
file.
EVALDEFINE, EVALREDEFINE
The reserved word EVALDEFINE may be used in place of the word DEFINE if one
would like the identifier that follows to be expanded. When one follows a
DEFINE with a macro name, the macro is not expanded, but rather the macro
name is declared at the current lexical level and assigned the specified
macro body. EVALDEFINE gets you around that. Helps with automatic
generation of macro names. EVALREDEFINE is also available.
ASSIGNC
The following compile time construct makes recursive macros easier.
ASSIGNC <name1> = <macro!body>;
<name1> must be a formal to a macro, and <macro!body> may be any macro body.
Thereafter, whenever <name1> is instantiated, the body corresponding to
<macro!body> is used in the expansion rather than the text passed to the
formal at the macro call.
RESTRICTION: ASSIGNC may only appear in the body of the macro that <name1>
is a formal of. If it appears anywhere else, the <name1> will be expanded
like any good formal, and that text used in the ASSIGNC as <name1>. Unless
you're being very clever, this is probably not what you want.
NOMAC
Preceding anything by the token NOMAC will inhibit the expansion of that
thing should that thing turn out to be a macro.
COMPILER!BANNER
This is a predefined macro which expands to a string constant containing the
text of the two-line banner which would appear at the top of the current
page if a listing file were being made. This string contains the date,
time, name and page of the source file, the value of all compiler switches,
the name of the outer block, and the name of the current block. Thus you
can automatically include the date of compilation in a program by using
COMPILER!BANNER[n TO m] for appropriate n and m. Try REQUIRE
COMPILER!BANNER MESSAGE; or look at a listing for the exact format.
Hints
9.8 Hints
The following is a set of hints and aids in debugging programs with macros.
Unless otherwise stated array brackets "[]" are the macro body delimiters.
IFC and friends will not trigger at the point of macro definition, in a
macro actual parameter list, or inside a string constant.
DEFINE FOO = [IFC A THENC B ELSEC D ENDC];
which is not the same as
DEFINE FOO = IFC A THENC [B] ELSEC [D] ENDC;
which is the same as
IFC A THENC DEFINE FOO = [B]
ELSEC DEFINE FOO = [D] ENDC;
DEFINE BAZ (A) = [OUTSTR ("A");];
BAZ (IFC B THENC C ELSEC D ENDC)
will result in the following string typed
on your terminal:
IFC B THENC C ELSEC D ENDC
STRING A;
A_"IFC WILL NOT TRIGGER HERE";
Macros will not be expanded in strings, but macro formal parameters will be
expanded when they occur in strings within macro bodies as seen in the
second example above.
DEFINE FOO = [BAZ];
OUTSTR ("FOO");
which will type out the string FOO on your terminal rather than BAZ.
Caution should be employed when using letters (specifically ) as
delimiters. This may lead to problems when defining macros within macros.
DEFINE MAC(A) "" = REDEFINE FOO =A;;
Inside the macro body of MAC, A will not be recognized as a formal since the
scanner has scanned A as an identifier by virtue of being internally
represented as letters so that they could be defined to mean BEGIN and END
respectively (also as COMMENT). More justification for this feature is
seen by the following example:
DEFINE MAC(ABC) "AC" = A V_ABC; C;
We want ABC in the text to be the parameter and not B if we were to ignore
the macro delimiters.
When scanning lists of actual parameters, macros are not expanded.
DEFINE FOO = [A,B];
MAC (FOO) will not have the result MAC(A,B). However,
DEFINE FOO = [(A, B)];
followed by MAC FOO will have the same effect as
MAC (A, B).
The same reasoning holds for parameter lists to FORLC.
DEFINE FOO = [A, B, C];
FORLC I = (FOO) DOC [OUTSTR ("I");] ENDC
will result in FOO typed out on your terminal.
DEFINE FOO = [(A, B, C)];
FORLC I = FOO DOC [OUTSTR ("I");] ENDC
will have the desired result ABC typed out.
In order to take advantage of the nestable character feature in the
parameters to a macro call, one must be in REQUIRE DELIMITERS mode.
Otherwise scanning will break upon seeing a comma or a right parenthesis.
BEGIN
DEFINE FOO(A) = "A";
INTEGER ARRAY ABC[1:10, 1:10];
FOO (ABC[1, 2])_3;
END;
This is identical to:
BEGIN
INTEGER ARRAY ABC[1:10, 1:10];
ABC[1_3; Comment illegal;
END;
However, if the original program had included a REQUIRE DELIMITERS statement
prior to the macro call, as below, then the desired effect would have
resulted - i.e., ABC[1, 2]_3 .
BEGIN
REQUIRE "{~%$" DELIMITERS;
DEFINE FOO (A) = {A~;
INTEGER ARRAY ABC[1:10, 1:10];
FOO (ABC[1, 2])_3;
END;
RECORD STRUCTURES Introduction Declaration Syntax Declaration Semantics
SECTION 10
RECORD STRUCTURES
10.1 Introduction
Record structures are new to Sail. They provide a means by which a number
of closely related variables may be allocated and manipulated as a unit,
without the overhead or limitations associated with using parallel arrays
and without the restriction that the variables all be of the same data type.
In the current implementation, each record is an instance of a user-
defined record class, which serves as a template describing the various
fields of the record. Internally, records are small blocks of storage which
contain space for the various fields and a pointer to a class
descriptor record. Fields are allocated one per word and are accessed
by constant indexing off the record pointer. Deallocation is performed
automatically by a garbage collector or manually through explicit calls to a
deallocation procedure.
10.2 Declaration Syntax
<record!class!declaration>
::= RECORD!CLASS <class!id> (
<field!declarations> )
::= RECORD!CLASS <class!id> (
<field!declarations> ) [ <handler> ]
<record!pointer!declaration>
::= RECORD!POINTER ( <classid!list> )
<id!list>
::= RECORD!POINTER ( ANY!CLASS )
<id!list>
10.3 Declaration Semantics
The <field!declarations> have the same form as the <formal!param!decl> of
a procedure, except that the words VALUE and REFERENCE should not be
used, and default values are ignored. Each record class declaration is
compiled into a record descriptor (which is a record of constant record
class $CLASS) and is used by the runtime system for allocation,
deallocation, garbage collection, etc. At runtime record pointer variables
contain either the value NULL!RECORD (internally, zero) or else a pointer to
a record. The <classid list> is used to make a compile-time check on
assignments and field references. The pseudo-class ANY!CLASS matches all
classes, and effectively disables this compile-time check.
For instance,
RECORD!CLASS VECTOR (REAL X, Y, Z);
RECORD!CLASS CELL
(RECORD!POINTER (ANY!CLASS) CAR, CDR);
RECORD!CLASS TABLEAU
(REAL ARRAY A, B, C; INTEGER N, M);
RECORD!CLASS FOO (LIST L; ITEMVAR A);
RECORD!POINTER (VECTOR) V1,V2;
RECORD!POINTER (VECTOR, TABLEAU) T1,T2;
RECORD!POINTER (ANY!CLASS) R;
RECORD!POINTER (FOO, BAR) FB1, FB2;
RECORD!POINTER (FOO) FB3;
RECORD!POINTER (CELL) C;
RECORD!POINTER (ANY!CLASS) RP;
COMMENT the following are all ok syntactically;
C _ NEW!RECORD (CELL);
RP _ C;
FB2 _ NEW!RECORD (FOO);
FB1 _ FB3;
FB3 _ RP; COMMENT This is probably a runtime bug
since RP will contain a cell record. Sail
won't catch it, however;
CELL:CAR[RP] _ FB1;
CELL:CAR[RP] _ FB1;
COMMENT The compiler will complain about these: ;
FB1 _ C;
FB3 _ NEW!RECORD (CELL);
RP _ CELL:CAR[FB3];
NO runtime class information is kept with the record pointer variables, and
no runtime class checks are made on record assignment or field access.
Record pointer variables are allocated quantities, and should not appear
inside SIMPLE procedures. They resemble lists in that they are not given
any special value upon block entry and they are set to a null value
(NULL!RECORD) when the block in which they are declared is exited. (This is
so that any records referred to only in that block can be reclaimed by the
garbage collector.)
Record pointers are regular Sail data types, just like integers or strings;
Allocation Fields
record pointer procedures, arrays, and items all work in the normal way. As
indicated earlier, the constant NULL!RECORD produces a null reference.
10.4 Allocation
Records are allocated by
NEW!RECORD (<classid>)
which returns a new record of the specified class. All fields of the new
record are set to the null or zero value for that field; i.e., real and
integer fields will be set to 0, itemvar fields to ANY, lists to NIL, etc.
Note that entry into a block with local record pointer variables does NOT
cause records to be allocated and assigned to those variables.
10.5 Fields
Record fields are referenced by
<classid> : <fieldid> [ <record pointer expression> ]
and may be used wherever an array element may be used. For example
RECORD!POINTER (VECTOR) V;
RECORD!POINTER (CELL) C;
RECORD!POINTER (FOO) F;
VECTOR:X[V] _ VECTOR:Y[V];
CELL:CAR[C _ NEW!RECORD (CELL)] _ V;
VECTOR:Z[V] _ VECTOR:X[CELL:CAR[C]];
SUBLIS _ FOO:L[F][1 TO 3];
If the <record pointer expression> gives a null record, then a runtime error
message will be generated. This is the only runtime check that is made at
present. I.e., no runtime checks are made to verify that the <classid> in
the field statement matches the class of the record whose field is being
extracted.
An array field may be used as an array name, as in
RECORD!POINTER (TABLEAU) T;
TABLEAU:A[T][I,J] _ 2.5;
provided that a valid array descriptor has been stored into the field.
Unfortunately, Sail does not provide any clean way to do this. One unclean
way is
Garbage Collection Internal Representations
EXTERNAL INTEGER PROCEDURE ARMAK
(INTEGER LB, UB, #DIMS);
COMMENT returns address of first data word of new
array. For String arrays set #DIMS to -1,,n.
For higher dimensions declare with more LB, UB pairs;
EXTERNAL PROCEDURE ARYEL (INTEGER ARR);
COMMENT deallocates an array. ARR is the address of
the first data word;
RECORD!CLASS FUBAR (INTEGER ARRAY A);
RECORD!POINTER (FUBAR) FB;
MEMORY[LOCATION (FUBAR:A[FB])] _ ARMAK (1, 100, 1);
ARYEL (MEMORY[LOCATION (FUBAR:A[FB])]);
(Warning: the above advice is primarily intended for hackers. No promises
are made that it will always work, although this particular trick is
unlikely to be made obsolete in the forseeable future.)
10.6 Garbage Collection
The Sail record service routines allocate records as small blocks from
larger buffers of free storage obtained from the normal Sail free storage
system. (The format of these records will be discussed in a later section.)
From time to time a garbage collector is called to reclaim the storage for
records which are no longer accessible by the user's program (i.e., no
variables or accessible records point to them). The garbage collector may
be called explicitly from Sail programs as external procedure $RECGC, and
automatic invocation of the garbage collection may be inhibited by setting
user table entry RGCOFF to TRUE. (In this case, Sail will just keep
allocating more space, with nothing being reclaimed until RGCOFF is set back
to FALSE or $RECGC is called explicitly). In addition, Sail provides a
number of hooks that allow a user to control the automatic invocation of the
garbage collector. These are discussed later.
10.7 Internal Representations
Each record has the following form:
-1: <ptrs to ring of all records of class>
0: <garbage collector ptr>,,<ptr to class descriptor>
+1: <first field>
: :
+n: <last field>
$CLASS RECRNG HNDLER RECSIZ TYPARR TXTARR
Record pointer variables point at word 0 of such records. A
String field contains the address of word2 of a string descriptor, like the
string was a REFERENCE parameter to a procedure. The string descriptors are
also dynamically allocated.
The predefined record class $CLASS defines all record classes, and is itself
a record of class $CLASS.
RECORD!CLASS $CLASS
(INTEGER RECRNG, HNDLER, RECSIZ;
INTEGER ARRAY TYPARR; STRING ARRAY TXTARR);
RECRNG is a ring (bidirectional linked list) of all records of the
particular class.
HNDLER is a pointer to the handler procedure for the class (default $REC$).
RECSIZ is the number of fields in the class.
TYPARR is an array of field descriptors for each field of the class.
TXTARR is an array of field names for the class.
The normal value for the handler procedure is $REC$, which is the
standard procedure for such functions as allocation, deallocation, etc.
TYPARR and TXTARR are indexed [0:RECSIZ]. TXTARR[0] is the name of the
record class. TYPARR[0] contains type bits for the record class.
Handler Procedures
Example:
RECORD!CLASS FOO (LIST L; ITEMVAR A);
The record class descriptor for FOO contain:
FOO-1: <ptrs for ring of all records of $CLASS>
FOO: <ptr to $CLASS>
FOO+1: <ptrs for ring of all records of class FOO;
initialized to <FOO+2,,FOO+2> >.
FOO+2: <ptr to handler procedure $REC$>
FOO+3: 2
FOO+4 <ptr to TYPARR>
FOO+5: <ptr to TXTARR>
The fields of FOO are:
$CLASS:RECRNG[FOO] = <initialized to null ring,
i.e., xwd(loc(FOO)+2,loc(FOO)+2)>
$CLASS:HNDLER[FOO] = $REC$
$CLASS:RECSIZ[FOO] = 2
$CLASS:TXTARR[FOO] [0] = "FOO"
$CLASS:TXTARR[FOO] [1] = "L"
$CLASS:TXTARR[FOO] [2] = "A"
$CLASS:TYPARR[FOO] [0] = <bits for garbage collector>
$CLASS:TYPARR[FOO] [1] = <descriptor for LIST>
$CLASS:TYPARR[FOO] [2] = <descriptor for ITEMVAR>
10.8 Handler Procedures
Sail uses a single runtime routine $RECFN (OP, REC) to handle such system
functions as allocation, deallocation, etc. The code compiled for r _
NEW!RECORD (foo) is
PUSH P,[1]
PUSH P,[foo]
PUSHJ P,$RECFN
MOVEM 1,r
$RECFN performs some type checking and then jumps to the handler procedure
for the class. The normal value for this handler procedure is $REC$. It is
possible to substitute another handler procedure for a given class of
records by including the procedure name in brackets after the record class
declaration. The handler must have the form
RECORD!POINTER (ANY!CLASS) PROCEDURE <procid>
(INTEGER OP; RECORD!POINTER (ANY!CLASS) R);
Here OP will be a small integer saying what is to be done. The current
assignments for OP are:
value meaning
0 invalid
1 allocate a new record of record class R
2 not used
3 not used
4 mark all fields of record R
5 delete all space for record R
At SUAI, macro definitions for these functions may be found in the file
SYS:RECORD.DEF, which also includes EXTERNAL declarations for $CLASS, $REC$,
and $RECFN.
$REC$ (1, R) allocates a record of the record class specified by R, which
must be a record of class $CLASS. All fields (except string) are
initialized to zero. String fields are initialized to a pointer to a string
descriptor with length zero (null string).
$REC$ (4, R) is used by the garbage collector to mark all record fields of
R.
$REC$ (5, R) deallocates record R, and deallocates all string and array
fields of record R. Care must be exercised to prevent multiple pointers to
string and array fields; i.e., DO NOT store the location of an array in
fields of two different records unless extreme caution is taken to handle
deletion. This can be accomplished through user handler procedures which
zero array fields (without actually deleting the arrays) prior to the call
on $REC$ (5, R).
NOTE: When an alternate handler procedure is supplied it must perform all
the necessary functions. One good way to do this is to test for those OPs
performed by the alternate handler and call $REC$ for the others. If $REC$
is used to allocate space for the record then it should also be used to
release the space. These points are illustrated by the following example:
More about Garbage Collection
FORWARD RECORD!POINTER (ANY!CLASS) PROCEDURE
FOOH (INTEGER OP;
RECORD!POINTER (ANY!CLASS) R);
RECORD!CLASS FOO (ITEMVAR IV) [FOOH];
RECORD!POINTER (ANY!CLASS) PROCEDURE FOOH
(INTEGER OP; RECORD!POINTER (ANY!CLASS) R);
BEGIN
PRINT("CALLING FOOH. OP = ", OP);
IF OP = 1 THEN
BEGIN
RECORD!POINTER (FOO) F;
F _ $REC$ (1,R);
FOO:IV[F] _ NEW;
RETURN (F);
END
ELSE IF OP = 5 THEN
DELETE (FOO:IV[R]);
RETURN ($REC$ (OP, R));
END;
10.9 More about Garbage Collection
The information used by the system to decide when to call $RECGC on its own
is accessible through the global array $SPCAR. In general, $SPCAR[n] points
at a descriptor block used to control the allocation of small blocks of n
words. This descriptor includes the following fields:
BLKSIZ number of words per block in this space
TRIGGERa counter controlling time of garbage collection
TGRMIN described below
TUNUSEDnumber of unused blocks on the free list
TINUSE total number of blocks in use for this space
CULPRITthe number of times this space has caused
collection
The appropriate macro definitions for access to these fields may be found in
the source file SUAISYS:RECORD.DEF. The decision to invoke the garbage
collector is made as part of the block allocation procedure, which works
roughly as follows:
INTEGER spc,size;
size _ $CLASS:RECSIZ[classid]+2;
IF size>16 THEN return a CORGET block;
spc _ $SPCAR[size];
L1:
IF (MEMORY[spc+TRIGGER]
_ MEMORY[spc+TRIGGER]-1) <0
THEN BEGIN
IF MEMORY[GOGTAB+RGCOFF] THEN BEGIN
MEMORY[spc+CULPRIT] _ MEMORY[spc+CULPRIT]+1;
$RECGC;
GO TO L1;
END END;
<allocate the block from space spc,
update counters, etc.>
Once $RECGC has returned all unused records to the free lists associated
with their respective block sizes, it must adjust the trigger levels in the
various spaces. To do this, it first looks to see if the user has specified
the location of an adjustment procedure in TGRADJ(USER). If this cell is
non-zero then $RECGC calls that procedure (which must have no parameters).
Otherwise it calls a default system procedure that works roughly like this:
<set all TRIGGER levels to -1>
FOR size _ 3 STEP 1 UNTIL 16 DO BEGIN
spc _ $SPCAR[size];
IF MEMORY[spc+TRIGGER]<0 THEN BEGIN
t_MEMORY[spc+TINUSE]*RGCRHO(USER);
t_MAX(t, MEMORY[spc+TUNUSED],
MEMORY[spc+TGRMIN]);
END END;
RGCRHO(USER) is a real number currently initialized by the system to 0.33.
Thus the behavior of Sail's automatic garbage collection system may be
modified by
Setting RGCOFF(USER).
Supplying a procedure in TGRADJ(USER).
Modifying RGCRHO(USER).
Modifying the TGRMIN entries in the space descriptors.
One word of caution: User procedures that set trigger levels must set the
trigger level of the space that caused garbage collection to some positive
value. If not then a runtime error message will be generated.
Look at the file SUAIRECAUX.SAI[CSP,SYS], which contains a number of
useful examples and auxilliary functions.
TENEX ROUTINES Introduction TOPS-10 Style Input/Output
SECTION 11
TENEX ROUTINES
11.1 Introduction
This section describes routines which interface Sail with the TENEX
operating system. Routines for file input/output, terminal handling, and
miscellaneous system calls are described here. For TENEX-specific details
of other routines (such as interrupts) consult the appropriate chapter.
11.2 TOPS-10 Style Input/Output
"Standard" Sail programs written using TOPS-10 I/O routines such as OPEN,
LOOKUP, etc., will run under TENEX with little or no conversion necessary.
The TENEX Sail routines simulate most of the effects of the TOPS-10 I/O
calls without using the PA-1050 emulator.
In TENEX Sail the non-zero values of error flags returned by routines such
as LOOKUP are ERSTR JSYS error numbers. The interpretation of zero/nonzero
is the same as with the TOPS-10 I/O routines, but the specific nonzero
values are probably different.
Here are the TOPS-10 I/O routines and the differences, if any, under TENEX.
ARRYIN TENEX dump mode implies a single DUMPI JSYS.
ARRYOUT similar to ARRYIN.
CLOSE The close inhibit bits have no effect.
CLOSIN same as CLOSE.
CLOSO same as CLOSE.
ENTER no differences.
GETCHAN In TOPS-10, GETCHAN returns the number of a channel for which no
OPEN is currently in effect. Thus successive GETCHANs without
intervening OPENs will return the same channel number. In TENEX
Sail, GETCHAN returns the number of a channel for which no OPEN or
GETCHAN is currently in effect; thus successive GETCHANs will return
different channel numbers.
GETSTS not available; see GDSTS, GTSTS.
INOUT not available.
INPUT assumes 200 characters maximum if no length variable has been
associated with the channel.
INTIN no differences.
LINOUT no differences.
LOOKUP no differences.
MTAPE Options "I" and NULL are not available.
OPEN MODE is mostly ignored (exception: dump mode on a dectape ignores
the directory). The number of input and output buffers serves only
to indicate whether reading or writing is desired.
OUT no differences.
REALIN no differences.
RELEASE The close inhibit bits have no effect.
RENAME Changing the protection does not work. See GTFDB and CHFDB.
SETPL The routines CHARIN and SINI do not update the variables associated
with the channel by SETPL.
SETSTS not available; see SDSTS, STSTS.
STDBRK no differences.
TMPIN not available.
TMPOUT not available.
USETI works only on those devices where the SFPTR JSYS works. On a
dectape the MTOPR JSYS is used, and may not produce the same results
as on a TOPS-10 system. USETI takes effect immediately (the
nondeterminancy of the standard TOPS-10 (not SUAI) USETI is not
simulated). Equivalent to SFPTR (chan, (N-1)*'200);
USETO same as USETI. TENEX has only one file pointer, so in fact USETI
and USETO are EXACTLY the same function.
WORDIN no differences.
WORDOUT no differences.
MAGTAPE I/O TENEX Style Input/Output OBTAINING ACCESS
MAGTAPE I/O
The user is warned that there are serious limitations in TENEX regarding
magtapes. While TENEX is supposed to have device-independent I/O, the
magtape code in TENEX (as of v. 1-31) is minimal, allowing only dump mode
transfers. Further, end of file markers must be written explicitly, and it
is sometimes necessary to do an MTOPR operation 0 to reset the magtape
status bits.
TENEX Sail has been designed to handle some of these things in a way that
makes features available on a standard TOPS-10 system available in a
transparent way. For example, string input and output functions work, with
Sail assuming 128-word records on the tape. ARRYIN and ARRYOUT cause the
DUMPI and DUMPO JSYSes to be executed for the specified word counts. TENEX
Sail does not actually open tapes for write until a write operation is
requested. A CLOSF or CFILE on a tape will write two EOF'S (MTAPE (ch,
"E")) and backspace over one of them, if and only if the file has been
opened. Do not rewind a tape unless it has been closed. The user who wants
to write magtape code for operations other than the above is hereby warned
that the TENEX magtape code is fraught with peril. TENEX Sail certainly
allows full access to TENEX in this regard, however.
11.3 TENEX Style Input/Output
The following functions satisfy most Sail and TENEX needs:
ARRYIN Read in an array (36-bit words)
ARRYOUT Write an array
CFILE Release a file
CPRINT Write a string
INPUT Read in a string
JFNS Read file name
OPENFILEObtain a file
OUT Write a string
SETINPUTSet parameters for input
OBTAINING ACCESS
The main procedure for obtaining access to files is OPENFILE. In terms of
JSYSes, OPENFILE does a GTJFN and OPENF. Additional routines provide
support to OPENFILE, including SETINPUT, INDEXFILE, and CFILE.
DATA TRANSFER RANDOM I/O ERROR HANDLING DIRECT DSK OPERATIONS ASND, RELD
DATA TRANSFER
The TENEX routines for transferring data are generally the same as the TOPS-
10 routines. One improvement in TENEX Sail is that characters and words can
be mixed in reading or writing to a file, provided the file is on the disk.
Such I/O is called "data mixed I/O".
The following interpretation is given to data mixed I/O. There is one
logical character pointer into the file. When a character is read or
written the routines access the byte designated by the pointer and then
increment the pointer. There is only one pointer for both input and output.
When a word is read or written, the next full word in the file is accessed.
Accessing a word advances the character pointer to the next full word in the
file, where five 7-bit ASCII characters occupy one 36-bit word. If a read
passes the end of the file then the EOF variable (specified by SETINPUT or
OPEN) and the external integer !SKIP! are set to -1. If a write passes the
end of file then the end of file is advanced.
RANDOM I/O
The routines RCHPTR, SCHPTR, RWDPTR, and SWDPTR give access to the file
pointer. USETI and USETO are equivalent to SWDPTR (chan, (N-1)*'200);.
ERROR HANDLING
When errors occur the runtime routines will sometimes trap the errors
themselves. This practice is held to a minimum since the error itself may
be information that the user is interested in seeing. Usually the routines
(as marked) put the TENEX error code in !SKIP!, which may be examined by the
program. The TENEX error numbers do not always make good sense, but for the
cases that they do the ERSTR routine will print out on the terminal the
message associated with a given error number.
DIRECT DSK OPERATIONS
The routines DSKIN and DSKOUT do direct DSK operations in TENEX Sail, using
the DSKOP JSYS. These routines relate only to the IMSSS version of TENEX-
Sail.
******************************* ASND, RELD *****************************;
SUCCESS _ ASND (DEVICE!DESCRIPTOR);
SUCCESS _ RELD (DEVICE!DESCRIPTOR)
DEVICE!DESCRIPTOR (in the TENEX sense) is assigned to or deassigned from the
job. If DEVICE!DESCRIPTOR is -1 when calling RELD then all devices assigned
to the job are deassigned. TENEX error codes are returned in !SKIP!, which
is zero if no errors occurred.
BKJFN CFILE CHARIN CHAROUT CHFDB CLOSF
********************************* BKJFN ********************************;
BKJFN (CHAN)
Does the BKJFN JSYS on CHAN. TENEX error codes are returned in !SKIP!, which
is zero if no errors occurred. This function is escape from Sail.
********************************* CFILE ********************************;
SUCCESS _ CFILE (CHAN)
This routine closes the file (CLOSF) and releases the CHAN (RLJFN). This is
the ordinary way to dispense with a file. CFILE returns TRUE if CHAN is
legal and released; it returns FALSE otherwise.
********************************* CHARIN *******************************;
CHAR _ CHARIN (CHAN)
The next character from CHAN is returned. Zero is returned if the file is at
the end.
******************************** CHAROUT *******************************;
CHAROUT (CHAN, CHAR)
The single character CHAR is written to CHAN.
********************************* CHFDB ********************************;
CHFDB (CHAN, DISPLACEMENT,
MASK, CHANGED!BITS)
This routine performs the CHFDB JSYS on CHAN, with DISPLACEMENT, MASK, and
CHANGED!BITS as described in the JSYS manual.
********************************* CLOSF ********************************;
CLOSF (CHAN)
CVJFN DELF DELNF DEVST, STDEV
This routine does a CLOSF on CHAN. CHAN is not released. If the device is
a magtape open for output then 2 file marks are written and a backspace is
performed. This writes a standard end-of-file on the tape.
********************************* CVJFN ********************************;
REAL!JFN _ CVJFN (CHAN)
The full TENEX JFN (including flags in the left half) corresponding to Sail
channel CHAN is returned. Only a hacker will ever need this.
********************************** DELF ********************************;
DELF (CHAN)
The file on CHAN (which must NOT be open) is deleted. TENEX error codes are
returned in !SKIP!, which is zero if no errors occurred.
********************************* DELNF ********************************;
DELETED _ DELNF (CHAN, KEPT)
This routine deletes all but KEPT versions of the file on CHAN, which must
have had a CLOSF done on it first. If KEPT=0 then all versions of the file
are deleted. If KEPT=1 then all versions except the most recent are
deleted. The number of files actually deleted is returned as the value of
DELNF.
****************************** DEVST, STDEV ****************************;
"DEVICE!NAME" _ DEVST (DEVICE!DESIGNATOR);
DEVICE!DESIGNATOR _ STDEV ("DEVICE!NAME")
These routines convert between string DEVICE!NAMEs (such as "DTA0") and
TENEX DEVICE!DESIGNATORs. TENEX does not believe that lower case letters
are equivalent to upper case letters in STDEV. TENEX error codes are
returned in !SKIP!, which is zero if no errors occurred.
DEVTYPE DSKIN, DSKOUT DVCHR ERSTR GDSTS, SDSTS
******************************** DEVTYPE *******************************;
DEVICE!TYPE _ DEVTYPE (CHAN)
The DVCHR JSYS is used to return the device type of the device open on CHAN.
***************************** DSKIN, DSKOUT ****************************;
DSKIN (MODULE, RECNO, COUNT, @LOC);
DSKOUT (MODULE, RECNO, COUNT, @LOC)
[IMSSS only.] These routines do direct DSK I/O. MODULEs 4-7 are legal for
everyone; other modules require enabled status. The routines transfer COUNT
('1000) words, starting at location LOC in memory and at record RECNO in
MODULE. TENEX error codes are returned in !SKIP!, which is zero if no
errors occurred. WARNING: No bounds checking is performed to see if the
LOC is a legal Sail array.
********************************* DVCHR ********************************;
DEVICE!CHAR _ DVCHR (CHAN, @AC1, @AC3)
The DEVCHR JSYS is performed. The flags from AC2 are returned as the value
of the call, and AC1 and AC3 get the contents of ac's 1 and 3.
********************************* ERSTR ********************************;
ERSTR (ERRNO, FORK)
Using the ERSTR JSYS, this routine types on the console the TENEX error
string associated with ERRNO for fork FORK ('400000 for the current fork).
Parameters (in the sense of the ERSTR JSYS) are expanded. Types
ERSTR: UNDEFINED ERROR NUMBER (and sets !SKIP! to -1) if something is wrong
with ERRNO or FORK.
****************************** GDSTS, SDSTS ****************************;
STATUS _ GDSTS (CHAN, @WORD!COUNT);
SDSTS (CHAN, NEW!STATUS)
The status of the device on CHAN is returned or changed. For GDSTS,
@WORD!COUNT is set to the contents of AC3.
GNJFN GTFDB GTJFN
Remark: some magtape statuses (such as EOF) are set by MTOPR and not by
SDSTS. Ordinarily the Sail runtime system takes care of this, but it is
worth mentioning since so many users have run into this poorly documented
fact about TENEX.
********************************* GNJFN ********************************;
MORE!FILES _ GNJFN (CHAN)
Does the GNJFN JSYS. A file that is open cannot have GNJFN applied to it.
INDEXFILE should normally be used instead of GNJFN. An exception is if
files are being indexed without actually being opened (i.e., without an
OPENF JSYS), which is a sensible way of performing operations such as
counting the number of files in a group.
********************************* GTFDB ********************************;
GTFDB (CHAN, @BUF)
The entire FDB of CHAN is read into the array BUF. No bounds checking is
performed, so BUF should be at least '25 words.
********************************* GTJFN ********************************;
CHAN _ GTJFN ("NAME", FLAGS)
Does a GTJFN. If NAME is non-null then it is used, otherwise the terminal
is queried for a filename. Any error code is returned in !SKIP!. The Sail
channel number obtained is returned as the value of GTJFN.
The following values for FLAGS will be translated by Sail before doing the
JSYS:
value translated to
0 '100001000000 (ordinary input)
1 '600001000000 (ordinary output)
Other values are taken literally.
Ordinarily OPENFILE will be used rather than GTJFN. The routines GTJFN,
OPENF, GNJFN, CLOSF, RLJFN, and DVCHR are all in the category of being
included only for completeness; they are not necessary in most programs.
GTJFNL GTSTS, STSTS INDEXFILE
********************************* GTJFNL *******************************;
CHAN _ GTJFNL ("ORIGSTR", FLAGS, JFN!JFN,
"DEV", "DIR", "NAM", "EXT",
"PROT", "ACCOUNT", DESIRED!JFN)
Does the long form of the GTJFN JSYS (and does not do an OPENF). The
arguments are put into the accumulators and locations in the table accepted
by the long form of the GTJFN JSYS. These arguments are given below, where
"AC X" means an accumulator and "E+X" means in the Xth address of the table.
Argument Where placed What
"ORIGSTR"AC 2 Partial or complete string
FLAGS E+0 Flags to GTJFN
JFN!JFN E+1 xwd input JFN, output JFN
"DEV" E+2 device
"DIR" E+3 directory
"NAME" E+4 name
"EXT" E+5 extension
"PROT" E+6 protection
"ACCOUNT"E+7 account
DESIRED!JFN E+'10 desired JFN if B11 on
****************************** GTSTS, STSTS ****************************;
STATUS _ GTSTS (CHAN);
STSTS (CHAN, NEW!STATUS)
These routines examine and change the file status using the JSYSes. TENEX
error codes are returned in !SKIP!, which is zero if no errors occurred.
WARNING: The results of GTSTS are not necessarily appropriate for
determining end-of-file if the file is being page-mapped by Sail. Look at
the EOF variable instead. See SETINPUT.
******************************* INDEXFILE ******************************;
ANOTHER _ INDEXFILE (CHAN)
If CHAN was opened with the "*" option by OPENFILE then INDEXFILE will try
to get the next file in the "*" group. INDEXFILE returns TRUE as long as
another file can be found on CHAN. Example:
JFNS JFNSL MTOPR
JFN _ OPENFILE ("<JONES>*.SAI;*", "RO*");
COMMENT Read all of Jones's Sail programs;
SETINPUT (JFN, 200, 0, EOF);
DO BEGIN "INDEX"
DO BEGIN "READ FILE"
STRING S;
S _ INPUT (JFN, BREAK!TABLE);
COMMENT process ...;
END "READ FILE" UNTIL EOF;
END "INDEX" UNTIL NOT INDEXFILE (JFN);
The "*" option takes the place of reading the MFD and UFD on a TOPS-10
system. INDEXFILE clears the EOF, LINNUM, SOSNUM, and PAGNUM variables
associated with CHAN if these have been set by SETINPUT and SETPL.
********************************** JFNS ********************************;
"NAME" _ JFNS (CHAN, FLAGS)
The name of the file associated with CHAN is returned. FLAGS are for
accumulator 3 as described in the JSYS manual. Zero is a reasonable value
for FLAGS.
********************************* JFNSL ********************************;
"NAME" _ JFNSL (CHAN, FLAGS, LHFLAGS)
(This routine corrects a deficiency in the JFNS function.) The name of the
file associated with CHAN is returned, using FLAGS for accumulator 3 and
putting LHFLAGS into the left half of accumulator 2 as described in the JSYS
manual. If LHFLAGS is -1 then the value returned by GTJFN is used.
********************************* MTOPR ********************************;
MTOPR (CHAN, FUNCTION, VALUE)
The MTOPR JSYS is executed with FUNCTION placed into AC2 and VALUE into AC3.
The TOPS-10 style MTAPE function may be more comfortable. [(Stupid!) IMSSS
and SUMEX: skip to end of tape does not work.]
OPENF OPENFILE
********************************* OPENF ********************************;
OPENF (CHAN, FLAGS)
Does the OPENF JSYS on CHAN with FLAGS as the contents of accumulator 2.
TENEX error codes are returned in !SKIP!, which is zero if no errors
occurred. The following values for FLAGS will be translated by Sail before
setting AC2:
value translated to
0 '070000200000 (input characters)
1 '070000100000 (output characters)
2 '440000200000 (input words)
3 '440000100000 (output words)
4 '447400200000 (dump read)
5 '447400100000 (dump write)
Values 6-10 are reserved for expansion; other values are taken literally.
Best results are obtained by opening a TTY in 7-bit mode, the DSK or DTA in
36-bit mode, and a magtape in 36-bit dump mode.
******************************** OPENFILE ******************************;
CHAN _ OPENFILE ("NAME", "OPTIONS")
NAME is the name of the file to be opened. If it is null then OPENFILE gets
the filename from the terminal using TENEX filename recognition. CHAN, the
value returned by OPENFILE, is a Sail channel number. This is not
necessarily the same as the TENEX JFN (see CVJFN). All TENEX Sail functions
(except SETCHAN) require Sail channel numbers for arguments. OPTIONS is one
or more characters specifying the kind of access desired. The legal
characters are
Read or write:
R read
W write
A append
Version numbering, old-new:
O old file
N new file
T temporary file
* index with INDEXFILE routine
Independent bits to be set:
C require confirmation
D ignore deleted bit
H "thawed" access
RCHPTR, SCHPTR
Error handling:
E return errors to user in the external
integer !SKIP!. TENEX error codes are used.
(CHAN will be released in this case.)
If an error occurs and mode "E" was not specified then OPENFILE gives an
error message and attempts to obtain a file name from the terminal. If an
error occurs when "E" was specified then OPENFILE will return -1 for CHAN
and the TENEX error code will be put into !SKIP!.
Examples:
COMMENT get a filename from the terminal
and write the file;
BEGIN
INTEGER JFN;
OUTSTR (CRLF & "FILE NAME* ");
JFN _ OPENFILE (NULL, "WC");
COMMENT write, confirm name;
CPRINT (JFN, "text
");
CFILE (JFN); COMMENT close the file;
END;
COMMENT read a known file;
BEGIN
STRING S;
INTEGER JFN, BRCHAR, EOF;
SETBREAK (1, '12, '15&'14, "IN");
JFN _ OPENFILE ("<JONES>SECRET.DATA", "RCO");
SETINPUT (JFN, 200, BRCHAR, EOF);
DO BEGIN
S _ INPUT (JFN, 1);
END UNTIL EOF;
CFILE (JFN);
END;
Wizards: The OPENF is for 36-bit transfers; except that TTY, LPT, and a
device for which a 36-bit OPENF fails get 7-bit mode.
***************************** RCHPTR, SCHPTR ***************************;
PTR _ RCHPTR (CHAN);
SCHPTR (CHAN, NEWPTR)
The number of the byte which will be accessed next by character I/O is
returned or set. The first character of a file is character number 0. If
NEWPTR=-1 for SCHPTR then the pointer is set to end of file. Setting the
RFBSZ RFPTR, SFPTR RLJFN RNAMF
pointer beyond end of file will change the length of the file if it is being
written. TENEX error codes are returned in !SKIP!, which is zero if no
errors occurred.
********************************* RFBSZ ********************************;
BYTE!SIZE _ RFBSZ (CHAN)
The byte!size of the file open on CHAN is returned. This function is escape
from Sail.
****************************** RFPTR, SFPTR ****************************;
PTR _ RFPTR (CHAN);
SFPTR (CHAN, NEWPTR)
These routines perform JSYSes and are escape from Sail. TENEX error codes
are returned in !SKIP!, which is zero if no errors occurred.
********************************* RLJFN ********************************;
RLJFN (CHAN)
This routine does the RLJFN JSYS.
********************************* RNAMF ********************************;
SUCCESS _ RNAMF (EXISTINGCHAN, NEWCHAN)
The RNAMF JSYS is performed, renaming the file on EXISTINGCHAN to the name
of the (vestigial) file on NEWCHAN. It is necessary that
CLOSF(EXISTINGCHAN) be done before RNAMF and that OPENF be done afterwards.
The TOPS-10 style RENAME is sometimes more convenient to use than RNAMF,
since RENAME performs the GTJFN and OPENFs necessary for the renaming
operation. However, the actual JFN associated with CHAN is changed by
RENAME.
RWDPTR, SWDPTR SETCHAN SETINPUT SINI
***************************** RWDPTR, SWDPTR ***************************;
PTR _ RWDPTR (CHAN);
SWDPTR (CHAN, NEWPTR)
The number of the word which will be accessed next by word I/O is returned
or set. The first word of a file is word number 0. If NEWPTR=-1 for SWDPTR
then the pointer is set to end of file. Setting the pointer beyond end of
file will change the length of the file if it is being written.
******************************** SETCHAN *******************************;
CHAN _ SETCHAN (REAL!JFN,
GTJFN!FLAGS, OPENF!FLAGS)
This function is liberation from Sail I/O. It is provided for doing Sail
I/O on a JFN that is obtained from some means other than the Sail file-
opening routines -- for example, a JFN passed from a superior fork.
REAL!JFN is a 36-bit JFN (or JFN substitute, such as a Teletype number),
GTJFN!FLAGS and OPENF!FLAGS are the flags that should be recorded describing
how the GTJFN and OPENF were accomplished. REAL!JFN need not be open. The
value returned by SETCHAN is the Sail channel number which should be used
for subsequent Sail I/O. SETCHAN is the only function in TENEX Sail that
takes an actual JFN as an argument.
******************************** SETINPUT ******************************;
SETINPUT (CHAN, @COUNT, @BRCHAR, @EOF)
This function relates the COUNT, BRCHAR, and EOF variables to channel CHAN
in the same way that OPEN does. The INPUT function (page 72) uses 200 for
the default value of COUNT if no location has been associated with CHAN.
All I/O transfer routines also set !SKIP! to indicate end-of-file and I/O
errors. For example, on return from INPUT !SKIP! will be -1 if an end-of-
file occurred, a TENEX error number if an error occurred, and zero
otherwise.
********************************** SINI ********************************;
"STRING" _ SINI (CHAN, MAXLENGTH, BRCHAR)
A string of characters terminated by BRCHAR or by reaching MAXLENGTH
SIZEF UNDELETE Terminal Handling THE TERMINAL AS A DEVICE
characters, whichever happens first, is read from CHAN. SINI sets !SKIP! to
-1 if the string was terminated for count; otherwise !SKIP! will be set to
BRCHAR. To determine end-of-file, examine the EOF variable for the channel
(see SETINPUT).
********************************* SIZEF ********************************;
SIZE _ SIZEF (CHAN)
The size in pages of the file open on CHAN is returned. TENEX error codes
are returned in !SKIP!, which is zero if no errors occurred.
******************************** UNDELETE ******************************;
UNDELETE (CHAN)
The file open on CHAN is undeleted. TENEX error codes are returned in
!SKIP!, which is zero if no errors occurred.
11.4 Terminal Handling
The simplest way to write strings on the terminal is with PRINT. See page
98. The simplest way to read strings from the terminal is with INTTY. See
page 145. The following detailed discussion about terminal handling will
normally be of interest only to advanced programmers. The rest of this
section is new.
THE TERMINAL AS A DEVICE
We first discuss some of the problems in using the terminal as a device
(i.e., when device "TTY:" is opened by OPENFILE or a similar function).
Since Sail has various functions for reading strings, reals, and integers
from an arbitrary device, this can be a useful feature.
TENEX provides quite general teletype service. However, the lack of a
default system line editor creates some problems. Note the proliferation of
line editors in the many commonly used TENEX programs. Some of them, such
as the INTERLISP editor, are carefully and cleanly written. Most TENEX
utility programs, however, work quite poorly and inconsistently with regard
to the controlling terminal.
The TOPS-10 system has a simple line editor. On a standard Teletype device,
the standard TOPS-10 editor activates on a carriage return, altmode,
control-G, or control-Z. ASCII DEL ('177) deletes the previous character;
control-U deletes the current line; control-R retypes the current line; and
control-Z signifies end-of-file when the terminal is INITted as a device.
(The SUAI display line editor also has character insertion, deletion,
searching, kill-to-character, and settable activation characters.) The great
virtue of this is that programs can be written in a device-independent
manner. When the terminal is accessed as a device the system handles line
editing.
Many TOPS-10 programs take advantage of this device-independence, using the
INPUT, REALIN and INTIN functions to access the system line editor. TENEX
has had no system line editor; while IMSSS and SUMEX have had a line editor
in their TENEX for some time, it is not in general use.
Therefore, the features of a "system" line editor have been put into the
TENEX Sail runtime system. Several schemes have been implemented in TENEX
Sail as of this writing. When a channel is opened to the controlling
terminal, three kinds of line editing are available: 1) a TOPS-10 style
line editor, 2) a TENEX-style line editor, and 3) no line editor at all.
The TOPS-10 style editor is the default with a channel opened via OPEN; the
TENEX-style editor is the default when a TENEX function (such as OPENFILE or
GTJFN) is used to obtain the channel. The function SETEDIT can be used to
change which convention is used. More detailed description of these three
kinds of editing follows.
TOPS-10 Style Editor. The OPEN function to the controlling terminal,
usually "TTY" in the second argument, gets the following editing conventions
for functions INPUT, INTIN and REALIN:
'25 (control-U) deletes the entire line and echoes control-G (BEL) CR LF
to the terminal.
'32 (control-Z) means end-of-file, after all previous input is read in.
'33 (ESC, altmode) activates and is sent to the program as '33. This is
consistent with current TOPS-10 practice. Over the years there have
been several altmodes: '33, '175, and '176. On terminals that TENEX
believes to be a model 33 teletype, the characters '175 and '176 are
transliterated to '33 by TENEX before the Sail runtime system sees
them.
'37 (US, TENEX EOL), which is found in the input buffer when CR is typed
at the terminal, is transliterated to a '15 '12 (CRLF) sequence.
'177 (DEL, rubout) deletes the last character; consecutive deleted
characters are echoed, surrounded by backslashes "\". (At IMSSS and
SUMEX the deleted characters are removed from the screen with the
DELCH JSYS, which is not supported by BBN.)
The editor activates on line feed, altmode, control-G, and control-Z.
All this means that programs written for the TOPS-10 system, accessing the
controlling terminal with INPUT et al, should work with regard to teletype
input. The above is also a description of the operation of INCHWL, except
that control-Z is simply a break character to INCHWL.
TENEX-Style Editor. The OPENFILE, GTJFN, and GTJFNL functions to the
controlling terminal set the TENEX Sail line editor to the following
conventions:
IMSSS and SUMEX. These sites use the PSTIN JSYS for line editing in TENEX,
with the following conventions:
'12 (linefeed) allows input to continue on the next line.
'22 (control-R) retypes the current line.
'27 (control-W) deletes a "word" (up to the next space). This prints as
"___" on the terminal.
'30 (control-X) deletes the entire line.
'32 (control-Z) signifies end of file.
'37 (TENEX EOL) is transliterated to a '15 '12 sequence.
'177 (rubout) or '1 (control-A) deletes the last character, using the
DELCH JSYS to remove it from the display (if any).
The PSTIN JSYS transliterates '175 and '176 to '33.
The editor activates on the characters defined by the PSTIN JSYS (q.v.);
these include linefeed ('12 after EOL), escape ('33), control-G,
control-Z.
Sites other than IMSSS and SUMEX have the following editing conventions when
the channel is opened with the TENEX routines OPENFILE, GTJFN, etc.:
'22 (control-R) retypes the current contents of the buffer.
'30 (control-X) deletes the entire line and echoes CR LF to the terminal.
'32 (control-Z) signifies end-of-file.
'37 (TENEX EOL) is transliterated to a '15 '12 sequence.
'177 (rubout) or '1 (control-A) deletes the last character. Consecutive
deleted characters are echoed surrounded by backslashes.
The editor activates on line feed ('12), escape ('33), control-G (7) and
control-Z ('32).
SETEDIT TERMINAL MODE FUNCTIONS GTTYP, STTYP
This is also the action of the INTTY routine, except that control-Z is
simply a break character to INTTY.
The third mode is the BBN standard mode. In this mode all characters are
simply passed through. In particular, control-Z does not signify end of
file, typing a rubout gives a '177, ESC gives a '33, CR gives a '37, etc.
No editing is done by the system. This is the mode in which a terminal
other than the controlling terminal is accessed using any of the functions.
******************************** SETEDIT *******************************;
"OLD!MODE" _ SETEDIT (CHAN, "NEW!MODE")
If CHAN is not the controlling terminal then SETEDIT is a no-op. Otherwise,
it sets the line editing mode to NEW!MODE" and returns OLD!MODE, both
according to the following code:
MODE Meaning
"D" TOPS-10 mode, as above
"T" TENEX mode, as above
"B" (BBN bag)Byte(ing) mode, no editing
Notes:
(1) MODE SETTINGS. SETEDIT does not change or access the parameters set by
such functions as SFMOD, SFCOC, STPAR, TTYUP, etc. Changes made with
these latter functions will affect editing.
(2) NON-CONTROLLiNG TERMINALS. Terminals other than the controlling
terminal will have byte mode -- no editing.
(3) INCHWL no longer transliterates '33 to '175. Previous versions of
TENEX Sail transliterated '33 to '175.
TERMINAL MODE FUNCTIONS
The routines in this section really refer to terminals only in the "mini-
system" version of TENEX. The argument CHAN may be either a Sail channel
number associated with a terminal, or a terminal specifier (such as '100 or
'101 for the controlling terminal).
****************************** GTTYP, STTYP ****************************;
TERMINAL!TYPE _ GTTYP (CHAN, @BUFFERS);
STTYP (CHAN, AC2)
RFCOC, SFCOC RFMOD, SFMOD STPAR STI DATA TRANSFER
The indicated JSYS is performed. In GTTYP the additional values returned
from accumulator 2 are stored into reference parameter BUFFERS.
****************************** RFCOC, SFCOC ****************************;
RFCOC (CHAN, @AC2, @AC3);
SFCOC (CHAN, AC2, AC3)
The indicated JSYS is performed.
****************************** RFMOD, SFMOD ****************************;
MODE!WORD _ RFMOD (CHAN);
SFMOD (CHAN, AC2)
A file's mode word is queried or altered using the JSYS. WARNING: some
features, such as upper case conversion, that are advertised by BBN as being
accomplished with the SFMOD JSYS are actually accomplished with the STPAR
JSYS.
********************************* STPAR ********************************;
STPAR (CHAN, AC2)
Does the STPAR JSYS, setting to AC2.
********************************** STI *********************************;
STI (CHAN, CHAR)
Does the STI jsys (Simulate Terminal Input) to channel CHAN (usually the
controlling terminal), inserting byte CHAR into the input stream.
DATA TRANSFER
The usual Sail routines for teletype I/O (see page 80) are available. In
addition, PBIN, PBOUT, and PSOUT have been added, although they execute
exactly the same code as INCHRW, OUTCHR, and OUTSTR respectively.
INTTY PBTIN SUPPRESSING OUTPUT
********************************* INTTY ********************************;
"STRING" _ INTTY
INTTY does a TENEX-style input. (Note that INCHWL does a TOPS-10 style
input.) Up to 200 characters are transfered. The activation character is
not appended to the string, but is put into !SKIP!. The value -1 is placed
into !SKIP! if the input is terminated for exceeding the 200 character
limit.
The normal activation characters are EOL, ESC, control-Z, and control-G;
however, see the section regarding line editing in TENEX Sail. At IMSSS and
SUMEX this routine uses the PSTIN JSYS with the standard system break
characters; no timing is available.
********************************* PBTIN ********************************;
CHAR _ PBTIN (SECONDS)
[IMSSS only.] Executes the PBTIN JSYS with timing of SECONDS.
SUPPRESSING OUTPUT
This new section is for advanced Sail users only, and supposes a knowledge
of the pseudo-interrupt system; see the JSYS manual and the interrupt
section of this manual.
The TOPS-10 system allows the user to type a control-O and suspend program
output to the terminal until either another control-O is typed or program
input is requested. (See [MonCom] for a complete description.) TENEX does
not have this at the system level, but pseudo-interrupts provide an
alternative with which the program can receive control and abort processing
as well as flush output.
TENEX Sail has complete access to the TENEX pseudo-interrupt system. In
order to facilitate handling of control-O an EXTERNAL INTEGER CTLOSW has
been added to the TENEX Sail runtime system. If CTLOSW is TRUE then any
output to the controlling terminal (device "TTY") is flushed by the
following functions:
PBOUT
PSOUT
OUT to a channel open to "TTY", or to '101
OUTCHR
OUTSTR
CTLOSW is likewise made FALSE when input is requested by any of the
following:
INCHRS INPUT INTIN TTYIN
INCHRW INSTR INTTY TTYINS
INCHSL INSTRL PBTIN TTYINL
INCHWL INSTRS REALIN TTYUP
Note: functions SINI, CHARIN and CHAROUT are not affected. CTLOSW may be
accessed by declaring it as an EXTERNAL INTEGER.
Here is an example of a control-O handler.
ENTRY; BEGIN
REQUIRE "<><>" DELIMITERS;
DEFINE !=<COMMENT>;
! This program sets up a control-O interrupt
using PSI channel 0, level 0.
;
EXTERNAL INTEGER CTLOSW,PS1ACS;
SIMPLE PROCEDURE CTLO; BEGIN
INTEGER USERPC,PSL1,USERINST,AC1,SAVEADDR;
LABEL LEAVE;
DEFINE PSOUT!JSYS=<'104000000076>,
SOUT!JSYS=<'104000000053>;
SIMPLE INTEGER PROCEDURE DEV (INTEGER JFN);
START!CODE
HRRZ 2,JFN; ! THE JFN;
SETZ 4,;
HRROI 1,4; ! PUT STRING IN 4;
MOVSI 3,'200000; ! ONLY THE DEVICE;
JFNS; ! GET THE STRING;
MOVEM 4,1; ! CVASC("DEV");
END;
! this is Sail immediate interrupt level.
No dynamic strings are accessed.;
IF CTLOSW THEN
BEGIN
CTLOSW _ FALSE; ! TOGGLE IT;
RETURN; ! AND RETURN;
END;
START!CODE
MOVEI 1,'101;
CFOBF;
END;
OUTSTR("^O
");
CTLOSW _ TRUE; ! NO MORE OUTPUT;
! get user PC and address into LEVTAB;
START!CODE
MOVEI 1,'400000;
RIR;
HLRZ 2,2; ! LEVTAB ADDRESS;
MOVE 2,(2); ! PC FOR LEVEL 1;
MOVEM 2,PSL1;
MOVE 2,(2); ! USER PC;
MOVEM 2,USERPC;
END;
! return if user mode;
IF (USERPC LAND '010000000000) THEN RETURN;
! in monitor. Return if not in the middle
of a PSOUT or (SOUT to '101);
IF NOT (
(USERINST _ MEMORY[USERPC-1])=PSOUT!JSYS
OR (USERINST=SOUT!JSYS AND
((AC1 _ MEMORY[LOCATION(PS1ACS) + 1])
= '101 OR DEV(AC1)=CVASC("TTY"))))
THEN RETURN;
! modify return so that output stops;
SAVEADDR _ (MEMORY[PSL1] LAND '777777000000)
+ LOCATION(LEAVE);
MEMORY[PSL1] SWAP SAVEADDR;
RETURN; ! to Sail interrupt handler;
START!CODE LEAVE: JRST @SAVEADDR; END;
END;
INTERNAL PROCEDURE INITIALIZE;
BEGIN
PSIMAP(0,CTLO,0,1);
ENABLE(0);
ATI(0,"O"-'100);
END;
REQUIRE INITIALIZE INITIALIZATION;
END;
Utility TENEX System Calls CALL CNDIR DIRST, STDIR
11.5 Utility TENEX System Calls
An effort has been made to provide calls that read and write strings which
may be inconvenient to perform from START!CODE. Note that the TENEX Sail
compiler has the TENEX JSYS mnemonics defined in START!CODE. In START!CODE
these definitions take precedence over the function calls of the same name.
********************************** CALL ********************************;
RESULT _ CALL (AC!ARG, "FUNCTION")
A limited set of CALLs is simulated by TENEX Sail. Those available are
EXIT
DATE
DATSAV [IMSSS only.]
GETINF [IMSSS only.]
GETPPN
LOGOUT
MSTIME
PJOB
PUTINF [IMSSS only.]
RANDOM [IMSSS only.]
RUN
RUNTIM
TIMER
If any other FUNCTION is specified then a continuable error message is
given.
********************************* CNDIR ********************************;
CNDIR (DIRNO, "PASSWORD")
Does the CNDIR jsys, connecting to DIRNO with password "PASSWORD". If
"PASSWORD" is null then the user must have connect privileges. TENEX error
codes are returned in !SKIP!, which is zero if no errors occurred.
****************************** DIRST, STDIR ****************************;
"DIRECTORY" _ DIRST (DIRNO);
DIRNO _ STDIR ("DIRECTORY", DORECOGNITION)
These routines convert between TENEX directory numbers and strings. TENEX
GJINF GTAD IDTIM, ODTIM PMAP
error codes are returned in !SKIP!, which is zero if no errors occurred.
For STDIR the error codes in !SKIP! are
1 string does not match
2 string is ambiguous.
Note that DIRECTORY must be in uppercase for the STDIR JSYS.
********************************* GJINF ********************************;
JOBNO _ GJINF (@LOGDIR, @CONDIR, @TTYNO)
The job number is returned as the value of the call. Reference values are:
the number of the logged directory (LOGDIR), the connected directory
(CONDIR), and the TENEX Teletype number (TTYNO).
********************************** GTAD ********************************;
DT _ GTAD
The current date and time (in TENEX representation) is returned.
****************************** IDTIM, ODTIM ****************************;
DT _ IDTIM ("DATIME");
"DATIME" _ ODTIM (DT, FORMAT)
These routines convert between TENEX internal representation DT and string
representation DATIME. If DT is -1 in ODTIM then the current date and time
is used. If FORMAT is -1 then the format used is "TUESDAY, APRIL 16, 1974
16:33:32". For IDTIM, TENEX error codes are returned in !SKIP!, which is
zero if no errors occurred. WARNING: the IDTIM JSYS is nearly an inverse
to the ODTIM JSYS; however, the format returned by ODTIM with FORMAT=-1 will
NOT be recognized by IDTIM unless the day ("TUESDAY, ") is first removed.
Blame BBN.
********************************** PMAP ********************************;
PMAP (AC1, AC2, AC3)
Does the PMAP JSYS, using the accumulators for the arguments.
RDSEG RUNPRG RUNTIM
********************************* RDSEG ********************************;
RDSEG (@SEGPAGES, @BUFPAGES)
This function returns the pages which are specially used by the Sail runtime
system. The starting and ending pages of the runtime segment are returned
in the left and right halves, respectively, of SEGPAGES. The first and last
pages used for bufferring are returned in the left and right halves of
BUFPAGES. This function is escape from Sail.
Memory map, in general:
pages contents
(Compile time)
0-n impure data
400-450 compiler code
600-604 START!CODE table, if needed
640-670 runtime system
770-m UDDT
(Run time)
0-n impure data
400-m code and pure data
600-637 I/O buffers
640-677 runtime system
770-p UDDT
********************************* RUNPRG *******************************;
RUNPRG ("PROGRAM", INCREMENT, NEWFORK)
This does two entirely different things depending on the value of NEWFORK.
If NEWFORK is true then a new fork is created, capabilities are transmitted,
and PROGRAM is run in the new fork (with the current fork suspended by a
WFORK). INCREMENT is added to the entry vector location. If NEWFORK is
false then the current fork is replaced with PROGRAM. In this case RUNPRG
is like the TOPS-10 RUN UUO; if the INCREMENT is 1 then the program is
started at the CCL address. If RUNPRG returns at all then there was a
problem with the file. Remember to say .SAV as the PROGRAM extension.
********************************* RUNTIM *******************************;
RUNNING _ RUNTM (FORK, @CONSOLE)
The running time in milliseconds for FORK is returned and the console
connect time is returned in CONSOLE.
LEAP DATA TYPES Introduction
SECTION 12
LEAP DATA TYPES
12.1 Introduction
In addition to the standard algol-like statements and expressions, Sail
contains an associative data store and auxiliary facilities called LEAP.
Sail's version of LEAP is based on the associative components of the LEAP
language implemented by J. Feldman and P. Rovner as described in [Feldman].
An associative store allows the retrieval of data based on the partial
specification of that data. LEAP stores associative data in the form of
ASSOCIATIONS, which are ordered three-tuples of ITEMS. Associations are
frequently called TRIPLES. Associations are placed in the associative store
by MAKE statements and removed from the store by ERASE statements. The
associative searches allow us to specify items and their position in the
triple and then have the LEAP interpreter search for triples in the
associative store which have the same items in the same positions. The
interpreter will extract the items from such triples, which correspond to
the positions left unspecified in the original search request. For example
say we had triples representing the binary relation Father!of, and we had
"made" associations of the form
Father!of John Tom
Father!of Tom Harry,
Father!of Jerry Tom,
where Father!of, John, Tom, Harry, and Jerry are names of items. We could
then perform searches to find the sons of Tom by specifying to the leap
search routines that we wanted to find triples whose first component was
Father!of and whose third component was Tom. Associative searches
inherently produce multiple values (i.e., both Jerry and John are sons of
Tom). To deal with multiple values, Leap has SETs and LISTs of items.
Items are constants. They may be created by declaration or by the function
NEW. Items may have a single algebraic variable, set, list or array
associated with them which is accessible by use of the DATUM construct.
Declared items have names which may be used to identify them in
expressions, etc. The simple variable whose value is an item is called an
ITEMVAR.
Syntax
12.2 Syntax
The following syntax is meant to REPLACE not supplement the syntax of
algebraic declarations, except where noted.
<declaration>
::= <type!declaration>
::= <array!declaration>
::= <preload!specification>
::= <label!declaration>
::= <procedure!declaration>
::= <synonym!declaration>
::= <require!specification>
::= <context!declaration>
::= <record!class!declaration>
::= <protect!acs declaration>
::= <cleanup!declaration>
::= <type!qualifier> <declaration>
::= <sprout!default!declaration>
<simple!type>
::= BOOLEAN
::= INTEGER
::= LIST
::= REAL
::= RECORD!POINTER ( <classid!list> )
::= SET
::= STRING
<itemvar!type>
::= ITEMVAR
::= <simple!type> ITEMVAR
::= <array!type> ARRAY ITEMVAR
::= CHECKED <itemvar!type>
::= GLOBAL <itemvar!type>
<item!type>
::= ITEM
::= <simple!type> ITEM
<array!type>
::= <simple!type>
::= <itemvar!type>
::= <item!type>
<type!declaration>
::= <simple!type> <identifier!list>
::= <itemvar!type> <identifier!list>
::= <item!type> <identifier!list>
::= <array!type> ARRAY <array!list>
::= <array!type> ARRAY ITEM <array!list>
::= <type!qualifier> <type!declaration>
<array!list> -- as on page 9
<procedure!declaration>
::= PROCEDURE <identifier> <procedure!head>
<procedure!body>
::= <procedure!type> PROCEDURE <identifier>
<procedure!head> <procedure!body>
::= <type!qualifier> <procedure!declaration>
<procedure!type>
::= <simple!type>
::= <itemvar!type>
::= MATCHING <procedure!type>
::= MESSAGE <procedure!type>
<procedure!head> and <procedure!body> -- as on page 10 except:
<simple!formal!type>
::= <simple!type>
::= <itemvar!type>
::= ? <itemvar!type>
::= <simple!type> ARRAY
::= <itemvar!type> ARRAY
::= <simple!type> PROCEDURE
::= <itemvar!type> PROCEDURE
<preload!specification>, <synonym!declaration>, <label!declaration>,
and <require!specification> as on page 9
<context!declaration> as on page 182
Semantics ITEM GENESIS SCOPE OF ITEMS DATUMS OF ITEMS
12.3 Semantics
ITEM GENESIS
Although items are constants, they must be created before they can be used.
Items may be created in three ways:
1) A Declared Item may created by declaration of an identifier to be
of type ITEM.
2) An item may be created with the NEW construct (see page 178).
3) A bracketed triple item is created by the MAKEing of a bracketed
triple (see MAKE, page 163).
Items of type 1 and 2 are the same except those of type 1 may be referred
to by the identifier that is associated with them. For example one may say
... ITEM DAD; ... X_DAD; .... NOTE: DAD is the name of an item, not a
variable! Saying DAD_X is just as illegal as saying 15_X.
Items of type 3 are different from those of type 1 and 2. Discussion of
them will be left until the creation of associations with the MAKE statement
is discussed (page 163).
SCOPE OF ITEMS
Items do not obey the traditional Algol scope rules. All declared items are
allocated in the outer block. All other items are allocated dynamically.
All items exist until a DELETE (<item expression>) is done on them (see page
162 for the details of DELETE), or until the outer block is exited at the
end of the program. HOWEVER, the identifiers of declared items (type 1
above) DO obey scope rules. After exiting the block in which item X was
declared, it will be impossible to refer to X by its declared name.
However, X may have been stored in an itemvar, associations, etc. and thus
still be retrieved and used.
Warning: items in recursive procedures behave differently from variables in
recursive procedures. At each recursive call of a procedure, the local
variables are reinstantiated (unless they were declared OWN). Items are
constants. There is never more than one instantiation of an item around at
a time.
DATUMS OF ITEMS
An item of type 1 or 2 may have an associated variable, called its DATUM.
The Datum of an item is like any variable; it may be declared to have any
type that a variable may have, except the type Itemvar. Because an item may
have only one datum from its creation until its death, we frequently will
say the "type of an item" referring to the type of the datum. RESTRICTIONS:
It is currently impossible to make either items or their datums either
Internal or External. However, the effect of External items can be
duplicated by manipulating the order in which items are declared (see page
157). OWN is not applicable as items are constants, not variables. Items
ITEMVARS
of type ARRAY must be declared with constant bounds since they are allocated
upon entering the outer block.
Example declarations of items with datums:
INTEGER ITEM FATHER!OF;
STRING ITEM FOO;
INTEGER ARRAY ITEM NAMES [1:4, 1:8]; COMMENT note
the specification of the array's dimensions;
SHORT REAL ITEM POINT;
EXTERNAL ITEM BLAT; COMMENT illegal;
ITEMVAR ITEM BLAT; COMMENT illegal;
STRING ITEMVAR ITEM BLAT; COMMENT illegal;
REAL PROCEDURE ITEM BLAT; COMMENT illegal;
PROCEDURE ITEM BLAT; COMMENT illegal,
use ASSIGN;
The syntax for variable includes the Datum construct. That is, if AGE is a
declared an Integer Item, then DATUM (AGE) behaves exactly like an Integer
variable. If ARR is declared as
STRING ARRAY ITEM ARR [2:4, 1:9+2]
then DATUM (ARR) is a string array with two dimensions of the declared size.
A new array may not be assigned to the Datum of ARR, though of course the
individual elements of the array may be changed. Datums obey the same type
checking and type conversion rules that the algebraic variables of Sail do.
For example, when a string is assigned to an integer datum, the integer
stored in the integer datum is the ASCII of the first character of the
string. ITEMVARS
An Itemvar is a variable whose value is an Item. Just as the statements
"X_3; Y_X" and "Y_3" are equivalent with respect to Y, the statements
"X_DAD; Y_X" and "Y_DAD" are equivalent with respect to Y, if X and Y are
itemvars, DAD an item. The distinction between itemvars and items is
identical to the distinction between integer variables and integers. An
integer variable may only contain an integer and a variable declared ITEMVAR
may only contain an item. This may be confusing since historically, integer
variables have always been called INTEGER rather than INTEGERVAR.
Properly speaking, one should have INTEGERVAR ARRAYs instead of INTEGER
ARRAYs. Originally, Sail only allowed ITEMVAR ARRAYs. However, so many
people found this confusing that now one may say ITEM ARRAY, and it will be
interpreted to mean ITEMVAR ARRAY. Similarly, an Item procedure is exactly
the same as an Itemvar procedure.
An itemvar may contain items of any type. However, when one says
DATUM (ITMVR) where ITMVR is an itemvar, the compiler must know the type of
the datum of the item (i.e. the type of the item) contained in the itemvar
so that the the correct conversions, etc. may be done. Thus, one may
SETS AND LISTS
declare itemvars to have the same types that are legal for items. If one
has declared STRING ITEMVAR ITMVR, then the compiler assumes that you have
stored an string item in ITMVR, and and will treat DATUM (ITMVR) as a string
variable.
An Itemvar may be declared CHECKED if the user desires the type of itemvar
checked against the type of the datum of the item expressions assigned to
it. That is, only a string item could be stored in a Checked String
Itemvar. If the itemvar is not declared Checked, it may have an item of any
type assigned to it and their types need not match at all. This can be very
dangerous. For example, an integer array item might be assigned to a string
itemvar. When the datum of this itemvar is later assigned to an integer
variable, say, Sail will try to treat the array header as a string pointer
and get very confused. The runtime routine TYPEIT, page 218, returns a code
for the type of its argument, and can be useful for avoiding type matching
errors with un-checked itemvars.
GLOBAL itemvars are a special kind for SUAI global model users. Global
model operation allows several jobs to share a data segment, and GLOBAL
itemvars are used to build the data structures in this segment. MESSAGE
procedures are also related to global model operations. These features have
fallen into disuse.
EXTERNAL, OWN and INTERNAL Itemvars are legal. SAFE applies to either the
array of an array itemvar, the array of an itemvar array, or both arrays of
an array itemvar array.
Itemvars obey traditional Algol block structure. Upon exiting the block of
their declaration, their names are unavailable and their storage is
reallocated. However, the item stored in an itemvar is not affected -- it
continues to exist until DELETEd or until the end of the program.
Itemvars are initialized to the special item ANY at the beginning of one's
program.
SETS AND LISTS
Sets and Lists are collections of items. There are two distinctions between
Sets and Lists: a list may contain multiple occurrences of any item while a
set contains at most a single instance of an item. Second, the order in
which items appear within a list is completely within the control of the
user program, while with a set, the order is fixed by the internal
representation of the items. Lists and Sets do not care what type if any
the datums of their members are.
List and Set Arrays, Itemvars, Items, and Procedures are all legal, as well
as External, Own and Internal Sets and Lists. Like itemvars, the scope of
Set and List variables is the block they were declared in. Exiting that
block does not destroy the items stored in the departed sets or lists.
ASSOCIATIONS PROCEDURES IMPLEMENTATION
ASSOCIATIONS
Perhaps the most important form of storage of items is the Association, or
TRIPLE. Triples of items may be written into or retrieved from a special
store, the associative store. The method of storage of these triples is
designed to facilitate fast and flexible retrieval. Sail uses
approximately two words of storage for each triple in the associative store.
There is at most one copy of a triple in the store at any time. Once a
triple has been stored in the associative store, its component items can not
be changed, although an approximation to this can be obtained by erasing
the association then making a new association with the altered components.
You will note there is no syntax for declaring a triple. Triples can only
be created with the MAKE statement. In the examples which follow, a triple
is represented by :
A O V
where A, O, and V represent the items stored in the association. The
associative store is accessed by the FOREACH statement, derived sets, and
binding triples (see Searching the Associative Store, page 164).
PROCEDURES
Itemvar, Item, List, and Set procedures all exist. Itemvar procedures may
be CHECKED if one desires the item RETURNed to have the same type as the
type of the Itemvar procedure. Otherwise, the compiler only checks to see
that the value returned to an itemvar procedure is an item.
Every type except Item may be used in formal parameter declarations; items
are constants yet parameters always have something assigned to them in the
procedure call. Since you can't assign something to a constant, you can't
have item parameters.
WARNING: when using Checked Reference Itemvar formals, no type checking is
performed as the actual is assigned to the formal at the procedure call.
However, type checking will only be done during the procedure, and when the
formal is assigned to the actual upon the (normal) exit of the procedure.
IMPLEMENTATION
Each Item is represented by a unique integer in the compiler. The numbers
are assigned in the order the items are declared, e.g. the first declared
item gets 1, the second gets 2, etc. (Actually, Sail has already declared 8
items that it needs, so user item numbers start with 9. REQUIRE n
ITEM!START changes the number at which user items start (only useful for
SUAI global model users). Lexical nesting is not observed; it is only the
sequence in which the declarations are scanned that determines their
numbers. The NEW function does not affect this assignment of numbers.
Items created by the New function are assigned the next available number at
the time of the execution of the New.
Those who use separately compiled procedures (see page 25) may wish to have
declared items common to both programs. However, Internal and External
items do not exist. The same effect may be achieved by carefully declaring
the desired items in the same order in both programs so that their numbers
match. The message "Warning -- two programs with items in them." will be
issued at the begining of execution, and may be ignored if you are certain
the items are declared in the same relative positions. No checking of
names, types, arrays bounds, etc. is done, so be very careful.
Items occupy no space (neither does the constant integer 15). The numbers
ascribed to items are stored in Itemvars and Associations. Itemvars are
simply a word of storage. An association is two words of storage, one
with three 12 bit bytes, each containing the number of one of the items of
the association, and a second word containing two pointers relating the
association to the associative search structure. Since the number of an
item must fit in 12 bits, the number of items is limited to about 4090.
The number of an item may be retrieved from the item as a integer with the
predeclared function CVN (<item!expression>). The item represented by a
certain integer may be retrieved by the predeclared function
CVI (<algebraic!expression>). CVN and CVI should only be used by those who
know what they're doing and have kept themselves up to date on changes in
Leap.
LEAP STATEMENTS Syntax
SECTION 13
LEAP STATEMENTS
13.1 Syntax
<leap!statement>
::= <leap!assignment!statement>
::= <leap!swap!statement>
::= <set!statement>
::= <list!statement>
::= <associative!statement>
::= <foreach!statement>
::= <suc!fail!statement>
<leap!assignment!statement>
::= <itemvar!variable> _ <item!expression>
::= <set!variable> _ <set!expression>
::= <list!variable> _ <list!expression>
<leap!swap!statement>
::= <itemvar!variable> <itemvar!variable>
::= <set!variable> <set!variable>
::= <list!variable> <list!variable>
<set!statement>
::= PUT <item!expression> IN <set!variable>
::= REMOVE <item!expression> FROM
<set!variable>
<list!statement>
::= PUT <item!expression> IN <list!variable>
<location!specification>
::= REMOVE <item!expression> FROM
<list!variable>
::= REMOVE ALL <item!expression> FROM
<list!variable>
<location!specification>
::= BEFORE <element!location>
::= AFTER <element!location>
<element!location>
::= <item!expression>
::= <algebraic!expression>
<associative!statement>
::= DELETE ( <item!expression> )
::= MAKE <triple>
::= ERASE <triple>
<triple>
::= <item!expression> <item!expression>
<item!expression>
<foreach!statement>
::= FOREACH <binding!list> SUCH THAT
<element!list> DO <statement>
::= NEEDNEXT <foreach!statement>
<binding!list>
::= <itemvar!variable>
::= <binding!list> , <itemvar!variable>
<element!list>
::= <element>
::= <element!list> AND <element>
<element>
::= <item!expression> IN
<list!expression>
::= ( <boolean!expression> )
::= <retrieval!triple>
::= <matching!procedure!call>
<retrieval!triple>
::= <ret!trip!element> <ret!trip!element>
<ret!trip!element>
<ret!trip!element>
::= <item!expression>
::= <derived!set>
Restrictions Semantics ASSIGNMENT STATEMENTS
<matching!procedure!call>
::= <procedure!call>
<suc!fail!statement>
::= SUCCEED
::= FAIL
13.2 Restrictions
SUCCEED and FAIL statements must be lexically nested inside a matching
procedure to be legal.
13.3 Semantics
ASSIGNMENT STATEMENTS
Assignment statements in Leap are similar to those in Algol. Itemvars, Set
variables, and List variables may be assigned item, set and list
expressions, respectively. Only one automatic coercion is done: a set
expression may be assigned to a list variable. NOTE: lists may not be
assigned to set variables (use CVSET).
The type of an itemvar is checked against the type of the item expression
assigned to it if and only if the itemvar is declared Checked. If a typed
item is assigned to an un-Checked itemvar of different or no type, the datum
is not affected. Assign an integer item to a string itemvar and the string
itemvar will now contain an item with an integer datum. Sail will not know
that you have in effect switched the type of the datum and will get very
confused if you later try to use the datum of the itemvar; it will treat the
integer as a pointer to a two word string descriptor in this case.
DATUM (X) is legal only when X is a typed item expression, namely an item
expression that the compiler can discover the type of (not COP (<set>) for
example). See page 226 for the BNF of typed item expressions. DATUM (X) is
syntactically a variable. It has the type of the typed item expression, X.
If X has an array type, then DATUM (X) should be followed by
[<subscript!list>]. Appropriate coercions will be done (i.e., string to
integer, integer to real, etc.) just as with regular variables in
expressions. NOTE: the user is responsible for seeing that the datum of an
item expression really is the type that Datum thinks it is (i.e., Datum of a
Real Itemvar that has had a string item stored in it will give you garbage).
PROPS (X), where X is an item expression, is legal regardless of the type of
X. X may even evaluate to a bracketed triple item, procedure item, or
PUT REMOVE DELETE
event item. PROPS (X) is syntactically an integer variable. It is limited
to integers n where 0 n 4095. If negative (i.e. two's complement)
integers or integers larger than 4095 are assigned to a PROPS, only the
right 12 bits are stored. The rest of the integer is lost.
PUT
Sets and lists are initially empty. One may put items in them with the PUT
statement. "PUT <item expression> IN <set variable>" does exactly what it
says.
"PUT <item expression> IN <list variable> BEFORE <algebraic expression>"
evaluates the item expression, evaluates the algebraic expression and
coerces it into an integer, say n, then puts the item into the list at the
nth position, bumping the old nth item to the n+1th position, and so on down
the list. This increases the length of the list by one. "PUT item IN list
AFTER n" places the item in the n+1th position and bumps the old n+1th item
down to the n+2th position, and so on. If n < 0 or n > (1 + length-of-
list), then an error message is given. The special token "" may be used in
the expression for n to stand for the length of the list.
"PUT <item expression 1> IN <list variable> BEFORE <item expression 2>"
cause a search to be made of the list for the item of <item expression 2>.
If it is found, the item of <item expression 1> is placed in the list
immediately ahead of the item found by the search. "PUT item IN list AFTER
item" proceeds the same way, but puts the first item in the list immediately
following the second item. If the second item is not an element of the
list, a BEFORE will put the first item at the begining of the list, while an
AFTER will put it at the end of the list.
REMOVE
To remove an item from a set or list, one may use REMOVE. "REMOVE item FROM
set" does just what it says. If the item to be removed from the set does
not occur in the set, this statement is a no-op.
"REMOVE n FROM list" removes the nth item from the list. The old n+1th item
becomes the nth, and so forth. An error is indicated if n 0 or
n > length-of-list. As before, should stand for the length of the list.
However,
"REMOVE item FROM list" removes the first occurrence of the item from the
list. If the item is not found, this statement is a no-op.
"REMOVE ALL item FROM list" removes all occurrences of the item from the
list. DELETE
Items are represented by unique integer numbers in Sail. Due to the
overwhelming desire to store an association in one word of storage, these
unique numbers are limited to 12 bits. Thus the total number of items is
limited to 4090. The DELETE statement allows one to free numbers for reuse.
It is also the only way to get rid of an item short of exiting the program.
WARNING: The Delete statement in no way alters the instances of the Deleted
MAKE BRACKETED TRIPLE ITEMS
items which are present in sets, lists, associations, or itemvars. The user
should be sure that there are no instances of the Deleted item occurring in
itemvars, sets, lists or associations. Even saying DELETE (ITMVR) where
ITMVR is an itemvar with an item to be deleted in it will not remove the
item from ITMVR; one must be careful to change the contents of ITMVR before
using it again.
MAKE
The MAKE statement is the only way to create Associations (Triples) and add
them to the associative store. If the association already exists in the
store, no alterations are made. The argument to the Make statement is a
triple of item expressions:
MAKE item1 item2 item3
MAKE item1 itemvar1 NEW
MAKE itemvar!array[23] item1 itemvar2
The component item expressions are evaluated left to right. The three items
that the three expressions evaluate to are then formed into an association,
and the association is hashed into the associative store. The item
expressions must be constructive, that is, one may use the NEW function but
not the ANY or BINDIT items (see NEW, page 178, ANY, page 179, and BINDIT,
page 179).
BRACKETED TRIPLE ITEMS
Items may be created by declaration, by the NEW function, or by using
BRACKETED TRIPLEs in Make statements. A Bracketed Triple item may not have
a datum, but may have a PROPS or a PNAME (see page 220 for pnames, page 161
for props). Instead, a Bracketed Triple item has an Association connected
to it. One creates a Bracketed Triple item by executing a Make statement:
MAKE item1 [item2item3item4] item5
where the itemN are item expressions. "[item2item3item4]" is the
Bracketed Triple item, and of course need not always be the second component
of the association. The association connected to the Bracketed Triple item
is "item2 item3 item4". The above Make statement actually creates
two triples and one item. Namely, the associations
item1 itemXX item5
item2 item3 item4
and the item "itemXX" which is a Bracketed Triple item and has the second
association connected to it. One can access a Bracket Triple item, with
the an associative search called the Bracketed Triple Item Retrieval:
itmvar _ [itm2 itm3 itm4];
COMMENT itmvar now contains itmXX;
The Bracket Triple construct may be used in any expression. See page 166.
ERASE Searching the Associative Store
Having "itmXX", one may access the items of the association connected to
with the predeclared functions FIRST, SECOND, and THIRD (see page 222 for
more information on these runtime functions):
FIRST (itemXX) is item2
SECOND (itemXX) is item3
THIRD (itemXX) is item4
ERASE
The way to remove an association from the associative store and destroy it
is to ERASE it:
ERASE item1 item2 item3
where the itemN are item expressions. The item expressions must be
retrieval item expressions; that is, one may use the ANY item but not the
NEW function or the BINDIT item (see ANY, page 179, and NEW, page 178, and
BINDIT page 179). Using ANY as one, two, or three of the item expressions
allows many associations to be erased in one statement. If the association
to be erased does not exist, Erase is a no-op.
Whenever one Erases an association, none of the items of the association are
deleted. In particular, when one Erases an association that has a
Bracketed Triple item as one of its components, the Bracketed Triple item is
not deleted. Furthermore, the association connected to the Bracketed Triple
item is not automatically erased by erasing an association containing a
Bracketed Triple item. The following Erase erases only one association:
ERASE item1 [item2item3item4] item5
However, erasing the association connected to a Bracketed Triple deletes the
item. Deleting the Bracketed Triple item DOES NOT erase the association
connected to it.
13.4 Searching the Associative Store
Flexible searching and retrieval are the main motivations for using an
associative store. It follows that this is the most important section of
the Leap part of this manual. It is a rare Leap program that does not use
at least one of the searches described below.
Four methods of searching the associative store exist in Sail:
Binding Booleans
Derived Sets
Bracketed Triple item retrieval
Foreach Statements
BINDING BOOLEANS
The first three are properly part of the discussion of Leap Expressions in
the next chapter, but are included here for completeness.
Throughout this section we will use the following notation for an
association:
A O V
where A, O and V stand for the "attribute", "object" and "value" items of an
association.
The terms "bound" and "unbound" will find heavy use in this section. Bound
describes an itemvar that has an item assigned to it. Unbound describes an
itemvar that, at this time in the execution of the program, has no item
bound to it. The object of searching the associative store is usually to
bind unbound itemvars to specific, but unknown, items. If the itemvar to be
bound was declared Checked, then type checking will be done, and the
appropriate error message will be issue if the binding item does not have
the same type as the itemvar.
Throughout this section, references to item expressions will always mean
retrieval item expressions. Do not use NEW in such expressions.
A hashing algorithm is used in storing and retrieving associations in Leap.
The user can increase the speed of associative searching or decrease his
core image by using the REQUIRE n BUCKETS construct to control the size of
his associative search hash table to reflect the number of associations he
will be using. A hash table will be allocated with (2^m) hash codes where m
is the smallest integer such that (2^m) n. Sail initializes the hash size
to '1000.
BINDING BOOLEANS
A Binding Boolean searches the associative store for a specified triple,
returning true if one can be found, and false otherwise. A Binding Boolean
is a triple:
itm1 itm2 itm3
where "itmN" is one of three things: an item expression, or the reserved
word "BIND" followed by an itemvar, or the token "?" followed by an itemvar.
An item expression as a component of the Binding Boolean means that
component of the triple that the boolean finds must be the item specified by
the item expression (unless the item expression evaluates to the item ANY,
which specifies that any item is okay). If a "BIND" itemvar is the A, O or
V of the triple, then the Binding Boolean will attempt to find an
association which meets the constraints imposed by the item expression A, O
or V components, and then binds to the "BIND" itemvar the items occuring in
the corresponding positions of the association that the Binding Boolean
found. If no such association can be found, then the Binding Boolean
returns FALSE and leaves the "BIND" itemvars with their previous values. If
DERIVED SETS BRACKETED TRIPLE ITEM RETRIEVAL THE FOREACH STATEMENT
"?" precedes an itemvar, then the itemvar will behave like a "BIND" itemvar
if it is currently contains BINDIT, but will behave like an item expression
if it is bound to some other item than BINDIT. Example:
IF Father ?Son ANY THEN PUT Son IN Sonset;
IF Father BIND Son Bob THEN CHILDLESS (Bob);
ERCHEK _ Father COP(Sonset) ANY;
DERIVED SETS
Derived Sets are quite simple: "Foo Garp" where Foo and Garp are item
expressions, is the set of all items X such that Foo Garp X exists.
"Garp Sister" is the set of all items X such that
X Garp Sister exists. "Foo ' Sister" is the set of all items X such
that Foo X Sister exists. Examples:
Dadset _ Father ANY;
Danson _ Father ` Dan;
News _ (Son Dad) attset;
ANY specifies "I don't care" to the search. BINDIT has no special meaning
to the search, and behaves like any other items. Since BINDIT can never
appear in an association, this means the set returned will always be the
empty set PHI.
BRACKETED TRIPLE ITEM RETRIEVAL
A Bracketed Triple item can be referenced by specifying the association it
is connected to. For example,
Itmvar _ [itm1 itm2 ANY]
PUT [ANY ANY ANY] IN Bracset
IF Foo Garp [itm1 itm2 ANY] THEN ...
Itmvar _ [itm1 [itm2 itm3 itm4] itm5]
where itmN is any item expression not containing NEW or BINDIT. ANY means
you don't care what item occupies that component. If the designated
Bracketed Triple is not found then BINDIT is returned and no error message
is given. THE FOREACH STATEMENT
This statement is the heart of Leap. It is similar to the FOR statement of
Algol in that a statement is executed once for each binding of a variable.
In this semi-schematic example,
FOREACH X SUCH THAT <element> AND ... AND
<element> DO <statement>;
the <statement> is executed once for each binding of the itemvar X. The
<element>s in the element list (i.e. <element> AND...AND <element>)
determine the bindings of the itemvar, and hence how many times the
<statement> is executed. If the <element>s are such that there is no
binding possible for X, then the <statement> is never executed. Like a Sail
FOR statement, one may use DONE, NEXT, and CONTINUE within the <statement>.
As before, when one uses a NEXT inside the loop, the word NEEDNEXT must
precede the FOREACH of the Foreach that one wants checked and possibly
terminated. See pages 37, 38, and 38 for more information about Done, Next,
and Continue.
Restriction: Jumping (i.e. with a GO TO) into a Foreach is illegal.
However, it is legal to jump out of a Foreach, or to jump around within the
same Foreach.
Foreach statements differ from For statements in that more than one itemvar
may be included to be given bindings:
FOREACH X, Y, Z SUCH THAT <element>....
X, Y, and Z are called Foreach itemvars. Just as one must declare the
integer I before using it in the Sail For statement
FOR I _ 1 STEP 2 UNTIL 21 DO...
so must one declare Foreach itemvars before using them in Foreaches.
Foreach itemvars are no more than normal itemvars receiving special
assignments; they may have any type. If a Foreach itemvar that has been
declared Checked is assigned an item by the search that has a different type
than the Checked itemvar, an error message will result.
Foreach itemvars differ from For variables in a more radical way. It is
possible to specify to the Foreach that a certain Foreach itemvar be a
variable to the search only on the condition that that the itemvar contains
the special item BINDIT at the time the Foreach is called. One precedes
such itemvars with the "?" token. For example:
FOREACH ?X, ? Y, Z SUCH THAT <element>....
If X contains BINDIT but Y does not when this Foreach starts execution, then
the search will be conducted exactly as if the statement
FOREACH X,Z SUCH THAT <element>....
were the Foreach specified. The itemvar X will then act just like an
ordinary, non-foreach itemvar that was bound previous to the Foreach. All
Foreach itemvars may be "?" itemvars if this is desired.
There are four different types of <element> that may be used in foreach
element lists:
Set Membership
Boolean Expressions
Retrieval Triples
Matching Procedures
THE LIST MEMBERSHIP <ELEMENT>
The order of the <element>s in the element list is very important, as we
shall see.
Terminology: we say that a certain binding of the the Foreach itemvars
"satisfies" an <element>. If that binding satisfies each <element> of the
element list, then we say it "satisfies the associative context". A fancy
way of refering to the element list is "associative context". We also refer
to the collection of bindings that satisfy the associative context as the
"satisfier group" of the Foreach.
The execution of a Foreach proceeds as follows. After initialization, the
Foreach proceeds with a search specified by the first <element> of the
element list. If a binding can be found that satisfies the first <element>,
the Foreach proceeds forward to the new <element> of the list and tries to
satisfy it, and so on. When the Foreach can not satisfy an <element>, it
"backs up" to the previous element and tries to get a different binding. If
it can't find satisfaction there, it backs up again and tries again to get a
different binding. When a Foreach proceeds forward off the end of the
element list (i.e. the associative context is satisfied) then the
<statement> is executed, and the Foreach backs up to the last <element> of
the element list. When the Foreach backs up off the left end of the element
list, the Foreach is exited.
When a Foreach is exited by backing up off the left, the Foreach itemvars
are restored to the last satisfier group bound to them, regardless of what
the <statement> may have done. If the associative context was never
satisfied, then the Foreach itemvars have the values that they had before
the Foreach. When a Foreach is exited with a GO TO, DONE, or RETURN, the
Foreach leave the itemvars with the bindings they had at the GO TO, or
whatever, including any modifications that the <statement> may have made to
them.
THE LIST MEMBERSHIP <ELEMENT>
[In the following, one may also read "set" for "list"; Sail automatically
coerces set expressions into list expressions.] This <element> does not
search the associative store to bind an itemvar, but merely binds it with an
item of a specified list. In the Foreach,
FOREACH X | X IN L DO <statement>;
(here we have used the Sail synonym "|" for "SUCH THAT"), the Foreach
itemvar X is bound successively to each element of the set L, starting at
the beginning of the list. If an item occurs n times in L, then X will be
bound to that item n times in the course of the Foreach. Thus, the number
of satisfiers to the above Foreach is LENGTH (L).
In the current implementation of Leap, there is a difficulty that should be
pointed out. If inside the <statement>, one changes L by list assignment,
Removes, etc. in such a way as to remove the next item of the list that the
THE BOOLEAN EXPRESSION <ELEMENT>
Foreach itemvar would have been bound to, Leap may go crazy. Foreach
searches look one ahead and save a pointer to the next items to be bound to
the Foreach itemvars. This allows one to remove the items of the current
bindings of the Foreach itemvars from lists or whatever, but makes other
removals hazardous. For example,
FOREACH X | X IN L DO REMOVE X FROM L;
will work, but
PUT V IN L BEFORE FOO;
FOREACH X | X IN L DO REMOVE V FROM L;
will probably fail. No error checking is done.
Whenever the Foreach itemvar of a list <element> has been bound previously,
the list element behaves like a boolean. It does not rebind the itemvar but
only checks to see that it is in the list. For example,
FOREACH X | X IN L AND X IN LL DO <statement>;
X is bound by the <element> "X IN L". <element> "X IN LL" is satisfied if
the item contained in the itemvar X is in the list LL.
If two different Foreach itemvars are used with two different lists, i.e.
FOREACH X,Y | X IN L AND Y IN LL
DO <statement>;
then after execution of the <statement>, the Foreach will go back the last
<element> that searches for bindings, in this case "Y IN LL" and gets a new
binding for Y. It is only on failure of this search that the Foreach goes
back to the first <element>, "X IN S", and gets a new binding for X. Thus
the <statement> will be executed once for each possible X,Y pair. In the
Foreach,
FOREACH X,Y | X IN L AND Y IN L ...;
X and Y will be bound to all possible pairs of elements in L. This
includes pairs with duplicate elements, like (a,a). Different orderings of
the same elements will NOT be ignored. Thus, pairs like (a,b) and (b,a)
will each be a satisfier group sometime during the Foreach. Furthermore, if
the list L contains duplications of the same item, identical pairs will
occur in proportion to the number of duplications. That is, regardless of
the duplications within the list, the number of satisfier groups to the
Foreach above is LENGTH (L)^2. THE BOOLEAN EXPRESSION <ELEMENT>
Any Sail boolean expression may be used as an <element> in the Associative
Context of a Foreach if it is inclosed by parentheses. A Boolean Expression
<element> is satisfied if it is TRUE. Note that the boolean expression must
have parentheses around it.
THE RETRIEVAL TRIPLE <ELEMENT>
WARNING: Foreach itemvars can not be bound by a Boolean Expression
<element>. Therefore, all itemvars used in a Boolean Expression <element>
must be bound by previous <element>s in the element list. A Boolean
Expression <element> with unbound Foreach itemvars in it causes an error
message. THE RETRIEVAL TRIPLE <ELEMENT>
To search the associative store with a Foreach, one uses the Retrieval
Triple <element>. A Retrieval Triple is satisfied if a binding of the
Foreach itemvars can be found such that the triple is an extant association.
If all of the itemvars of the Retrieval Triple <element> were bound previous
to the execution of the Retrieval Triple <element>, then the Triple does no
further binding; it is satisfied if the specified triple is in the
associative store. For example,
FOREACH X | FATHER TOM X AND
X IN PTA!SET DO <statement>;
FOREACH X | X IN PTA!SET AND
FATHER TOM X DO <statement>;
The two Foreaches have the same effect. However, in the first case, X is
bound by a search of the associative store for any triple that has FATHER
as its attribute component, and TOM as its object component. When such a
triple is found, X is bound to the item that is the value component. Then,
if X is in the PTA!SET, the Foreach lets the statement execute. If X is not
in PTA!SET, then the Foreach backs up and tries to find another triple with
FATHER as its attribute and TOM as its value. In the second Foreach, X is
bound with an item from PTA!SET, then the associative store is checked to
see that the triple FATHERTOMx, where x is the binding of X, is in the
store. If it is, the <statement> is executed, otherwise the Foreach backs
up and gets a different item from PTA!SET and binds that to X. Assuming
that Tom has only one father, the first search is much faster.
Using ANY in a Retrieval Triple indicated that you don't care what item
occupies that position. For instance, in
FOREACH X | FATHER ANY X DO <statement>;
X is bound successively to all fathers. However, if the associative store
included the following three associations,
FATHER KAREN PAUL
FATHER LYNN PAUL
FATHER TERRY PAUL
then X would be bound to PAUL only once, not thrice. BINDIT has no special
meaning to the search. Since BINDIT can never appear in an association, a
Retrieval Triple containing it will cause the search to always fail.
Different kinds of associative searches proceed with different efficiencies.
Listed below in order of decreasing efficiency are the various forms of
Retrieval Triple <element>s that are legal. A, O, and V represent either
bound Foreach itemvars or items from explicit item expressions in the
triple. x, y, and z represent unbound Foreach itemvars or the item ANY.
(note that x x V is really x O V, and so on). The two forms
of the List Membership <element> are included for comparison.
x IN L All items x in the list L.
A O x Only the value is free.
x y V Attribute and object are free.
A IN L Verification that item A is in list L.
A O V Verification that the triple
is in the store.
A x V Only the object is free.
x O V Only the attribute is free.
A x y Object and value are free.
x O y Attribute and value are free.
x y z Attribute, value and object are free.
Note that MAKEing an association inside a Foreach may or may not affect
subsequent bindings. For example, in
FOREACH X,Y | Link X Y DO
MAKE Link X Newlink;
it is uncertain whether Y will ever receive Newlink as its binding or not.
The A, O, and V used in a Retrieval Triple of a Foreach may be a derived set
expressions as well as item expressions. For example,
FOREACH X, Y | Link (FatherY) X DO ...;
ERASE in the <statement> of a Foreach that binds any of its itemvars with
Retrieval Triples may cause problems. This is similar to REMOVE used in
Foreaches with List Membership <element>s controling some bindings. ERASE
can only be guaranteed to to work safely if the association erased is the
one we just got a binding from, e.g.
FOREACH X | A O X DO ERASE A O X;
or if the association erased could not possible be used for a binding of a
Foreach itemvar, such as,
FOREACH X | Link X Node DO
ERASE Node X ANY;
Foreaches look one ahead to the next binding of its itemvars, and leaves a
pointer to those associations. If you Erase any of those associations, the
Foreach gets lost in the boondocks. No error checking is done.
However, as long as the associative store is not changed during the
THE MATCHING PROCEDURE <ELEMENT>
execution of the Foreach, a Retrieval Triple will not itself repeat a
particular set of bindings that it bound before. THE MATCHING PROCEDURE
<ELEMENT>
Matching Procedures are the most general search mechanism in Leap. They
also provide a convenient method of writing coroutines.
A MATCHING Procedure is very similar to a boolean procedure (in fact
outside of Foreach associative contexts, it behaves like a boolean procedure
and may be called within expressions, etc.). It must be declared type
MATCHING. It may not be declared SIMPLE. The formal parameters of a
Matching Procedure may include zero or more "?" itemvars (pronounced
"question itemvars") which may have any datum type but may not be VALUE or
REFERENCE. These parameters correspond roughly to either call by value or
call by reference, depending on the actual parameter when the procedure is
called. When the actual parameter is an item expression or a bound itemvar
the parameter is equivalent to a value parameter. However, if the actual
parameter is an unbound Foreach itemvar, then the parameter is treated as a
reference parameter, and on entry is is initialized to the special item
BINDIT.
Matching Procedures are exited by SUCCEED and FAIL statements instead of
RETURN statements. When used outside of an associative context, SUCCEED
corresponds to RETURN(TRUE) and FAIL corresponds to RETURN(FALSE) [this is
not strictly true when the matching procedure is sprouted as a process --
see page 190]. Inside an associative context, Succeed and Fail determine
whether the Foreach is to proceed to the next <element> of the element list
or to backup to the previous <element> of the element list. When the
Foreach backs up into a Matching Procedure, the procedure is not recalled,
but resumed at the statement following the last Succeed executed. On the
other hand, when a Foreach proceeds forward into a Matching Procedure, the
procedure is called, not resumed.
When a Matching Procedure is the last <element> of the associative context,
Succeeding will cause the <statement> to be executed; the Foreach then
backs up into the Matching Procedure, and the Matching Procedure is resumed
at the statement following the Succeed. When a Matching Procedure is the
first <element> of an associative context, Failing will exit the Foreach.
WARNING: Matching procedures are implemented as processes and two calls of
the same matching procedure may share the same memory unless the procedure
is declared RECURSIVE. See Memory Accessible to a Process, page 188.
If a Matching Procedure is explicitly SPROUTed as a process then the
Matching Procedure can be made running by a RESUME. In such a case the item
sent by RESUME is returned as the value of the SUCCEED or FAIL statement
which suspended the Matching Procedure, just as though SUCCEED or FAIL were
an item procedure. (In fact Succeed and Fail always return an item value,
but the value is ANY except in this special case.) Being Resumed is the
only was in which a Matching Procedure can be reactivated after a FAIL.
When a Matching Procedure is used exterior to the associative context of a
Foreach, one may use "BIND" in the call preceding those actuals which one
wishes bound regardless of their current binding. Preceding the actual with
"?" will have the save effect as "BIND" if the current value of the itemvar
is BINDIT, and will have no effect otherwise (the procedure will not
attempt to find it a binding).
That is all there is to Matching Procedures. Their power lies in the using
them cleverly. The following program illustrates techniques one may use
with matching procedures by simulating the List Membership and Retrieval
Triple <element>s with matching procedures.
RECURSIVE MATCHING PROCEDURE INLIST
(? ITEMVAR X; LIST L);
BEGIN "INLIST"
COMMENT THIS PROCEDURE SIMULATES THE CONSTRUCT
X L FOR ALL CASES EXCEPT THE SIMPLE
PREDICATE BINDITL;
IF X BINDIT THEN
BEGIN WHILE LENGTH (L) DO IF X = LOP (L)
THEN BEGIN SUCCEED; DONE END;
FAIL
END;
WHILE LENGTH (L) DO BEGIN X_LOP (L);
SUCCEED END;
END "INLIST";
MATCHING PROCEDURE TRIPLE (? ITEMVAR A, O, V);
BEGIN "TRIPLE"
DEFINE BINDING (A)="(A=BINDIT)";
SET SET1; INTEGER INDX;
RECURSIVE PROCEDURE SUCC!SET (REFERENCE
ITEMVAR X; SET S1);
WHILE LENGTH (S1) DO BEGIN X_LOP (S1);
SUCCEED END;
INDX _ 0;
IF BINDING (A) THEN INDX _ 1;
IF BINDING (O) THEN INDX _ INDX + 2;
IF BINDING (V) THEN INDX _ INDX + 4;
CASE INDX OF
BEGIN [0] "AOV" IF AOV THEN SUCCEED;
[1] "?OV" SUCC!SET (A, OV);
[2] "A?V" SUCC!SET (O, A`V);
[3] "??V" BEGIN SET1 _ ANY V;
WHILE (LENGTH (SET1)) DO
BEGIN A _ LOP (SET1);
SUCC!SET (O, A`V) END END;
[4] "AO?" SUCC!SET (V, AV);
[5] "?O?" BEGIN SET1 _ O ANY;
WHILE (LENGTH (SET1)) DO
BEGIN A _ LOP (SET1);
SUCC!SET (V, AO) END END;
[6] "A??" BEGIN SET1 _ A ` ANY;
WHILE (LENGTH (SET1)) DO
BEGIN O _ LOP (SET1);
SUCC!SET (V, AO) END END;
[7] "???"
USERERR(0, 1, "ANYANYANY IS IN BAD TASTE")
END;
END "TRIPLE";
LEAP EXPRESSIONS Syntax
SECTION 14
LEAP EXPRESSIONS
14.1 Syntax
<leap!expression>
::= <item!expression>
::= <set!expression>
::= <list!expression>
<item!expression>
::= <item!primary>
::= [ <item!primary> <item!primary>
<item!primary> ]
<item!primary>
::= NEW
::= NEW ( <algebraic!expression> )
::= NEW ( <set!expression> )
::= NEW ( <list!expression> )
::= NEW ( <array!name> )
::= ANY
::= BINDIT
::= <item!identifier>
::= <itemvar!variable>
::= <list!expression> [
<algebraic!expression> ]
::= <itemvar!procedure!call>
::= <resume!construct>
::= <interrogate!construct>
<itemvar!procedure!call>
::= <procedure!call>
<list!expression>
::= <list!primary>
::= <list!expression> & <list!expression>
<list!primary>
::= NIL
::= <list!variable>
::= {{ <item!expr!list> ~~
::= ( <list!expression> )
::= <list!primary> [ <substring!spec> ]
::= <set!primary>
<item!expr!list>
::= <item!expression>
::= <item!expr!list> , <item!expression>
<set!expression>
::= <set!term>
::= <set!expression> <set!term>
<set!term>
::= <set!factor>
::= <set!term> <set!factor>
<set!factor>
::= <set!primary>
::= <set!factor> - <set!primary>
<set!primary>
::= PHI
::= <set!variable>
::= {item!expr!list~
::= ( <set!expression> )
::= <derived!set>
<derived!set>
::= <item!expression> <associative!operator>
<item!expression>
<associative!operator>
::=
::= `
::=
<itemvar!variable>
::= <variable>
<set!variable>
::= <variable>
Semantics ITEM EXPRESSIONS
<list!variable>
::= <variable>
<leap!relational>
::= <item!expression> IN
<set!expression>
::= <item!expression> IN
<list!expression>
::= <item!expression>
<item!relational!operator>
<item!expression>
::= <set!expression>
<set!relational!operator>
<set!expression>
::= <list!expression>
<list!relational!operator>
<list!expression>
::= <triple>
<item!relational!operator>
::= =
::=
<set!relational!operator>
::= =
::=
::= <
::= >
::=
::=
<list!relational!operator>
::= =
::=
14.2 Semantics
ITEM EXPRESSIONS
Itemvars and itemvar arrays may be used in item expressions just as
algebraic variables and algebraic arrays are used in algebraic expressions.
Itemvars and itemvar arrays are initialized to the special Sail item ANY.
Items may be retrieved from sets and lists with the Sail functions COP and
NEW
LOP. COP (<set expression or list expression>) yields the item which is the
first element of the set or list that the set or list expression evaluated
to. LOP also yields the first item of the set or list, but removes that
item from the set or list. Because LOP changes the contents of the set or
list that is its argument, it can only accept set or list variables, not
expressions. See page 89.
List element designators may be used as itemvars in expressions. For
example, if RECORD is a list, and ITMVR an itemvar,
RECORD[5] _ ITMVR;
ITMVR _ RECORD[-1];
RECORD[] _ RECORD[1];
are all legal. The special token "" means the length of the list when used
in this context. The contents of the square brackets may be any algebraic
expression as long as it evaluates to an integer n where
1 n LENGTH (list).
<list!expression> [<algebraic!expression>] returns a particular element of a
list, but may not appear on the left of an assignment expression, because
assignment must be to variables.
NEW
The function NEW creates an item at execution time. Since space must be
allocated at loading for various tables, one must indicate approximately
how may NEW items he will create (the compiler counts the declared items for
you). Therefore, one should say "REQUIRE n NEW!ITEMS" where n is some
integer less than 4090 (the maximum number of items allowed in Sail). n may
be larger than the actual number of New items created, but the excess will
be wasted space. If 0 < n < 50, you get tables for 50 New items anyway.
NEW may take an argument. In this case, the datum of the created item is
preloaded with the value passed as argument. If this argument is algebraic,
set or list, then the datum will be of the same type. No type conversions
are done when passing the algebraic argument. NEW will also accept an array
name as argument. In this case, the created item will be of the type array.
In fact, the array cited as argument will be copied into the newly created
array. The new array will have the same bounds and number of dimensions as
the array cited as argument. This array will not disappear even if the
block that the original array was declared in is exited. It will only be
deallocated if the item is deleted.
NEW in an item expression makes that item expression a "constructive item
expression". Constructive item expressions are illegal in some places,
namely anywhere that attempts to gets an item from an existing structure
(i.e., ERASE, REMOVE, and Associative searches). It is usually clear
whether or not a constructive item expression is illegal.
ANY BINDIT TYPES AGAIN SET AND LIST EXPRESSIONS
ANY
Some associative searches may need only partial specification. The ANY item
is used to specify exactly which parts of the specification are
"don't cares"'s. Examples:
FOREACH X SUCH THAT Father X ANY DO ...
IF Father BIND X ANY THEN ...
ANY in an item expression makes that item expression a "retrieval item
expression". This is the opposite of a constructive item expression, and is
illegal anywhere the statement is creating new structure, namely, a MAKE
statement. Thus, ANY is legal everywhere items are, except a MAKE
statement.
BINDIT
Like ANY, BINDIT specifies no constraints on the associative search.
However, BINDIT has a special meaning to some searches, namely the Binding
Boolean and Matching Procedures (depending on how they're written). An
itemvar containing BINDIT will be bound by the search to an item of the
association that the search found. For example:
X _ BINDIT;
IF Father ? X Bob
THEN PUT X IN Bobfatherset;
Like ANY, BINDIT is illegal in MAKE statements. In certain associative
searches, namely the ERASE statement, the Bracketed Triple Item retrieval
expression, and the Retrieval Triple <element> of a Foreach, inclusion of
BINDIT will cause the search to always fail, because BINDIT can appear in no
association.
TYPES AGAIN
The compiler can determine the type of items when the item expression is a
typed itemvar, a typed itemvar procedure, a declared item with a type, a
typed itemvar array, or a NEW with an argument. When the compiler can
determine the type of the item expression, then and only then is it legal to
use the Datum construct on the item expression or to assign the item
expression to a Checked itemvar. For example, the following are ILLEGAL:
DATUM (COP (<set>))
DATUM (RECORD[]); COMMENT RECORD is a list;
CHEC _ NEW; COMMENT CHEC is a Checked itemvar;
SET AND LIST EXPRESSIONS
Three rather standard operations are implemented for use with sets. These
are union (), intersection (), and subtraction (-). These operators have
the standard mathematical interpretations. The only possible confusion
pertains to subtractions: if we perform the set operation
set1 - set2
DERIVED SETS BOOLEANS
and if there is an instance of an item x in set2 but not in set1, the
subtraction proceeds and no error message is given.
If one considers a list to be a string of items, then concatenation and
taking sublists suggest themselves as likely list operations. The syntax
and semantics for sublisting and list concatenation are identical with those
of strings, with the natural exception that the results are lists, and not
strings. There is also a difference in that if the indices to the
substringer do not make sense, an error message is generated rather than
setting of the !SKIP! variable. Examples:
LISTVAR _ LISTVAR[2 TO -1];
LISTVAR _ LISTVAR[9 FOR 2*N];
LISTVAR _ LISTVAR[1 FOR 2] & LISTVAR[3 TO ];
One may generate sets with
{item1, item2, item3~
and may generate lists with
{{item1, item1, item2, item3~~.
Sets are initialized to the empty set, PHI. Lists are initialized to the
null list, NIL. Initialization occurs at the beginning of the execution of
the program. Sets and list are reinitialized on entering the blocks of
their declaration only when such blocks are in recursive procedures.
DERIVED SETS
Derived sets are really sets of answers to questions which search the
associative memory. The conventions are:
a b -- the set of all x such that a b x
a b -- the set of all x such that x a b
a ` b -- the set of all x such that a x b
BOOLEANS
Several boolean primaries are implemented for comparing sets, lists, and
items. In the following discussion, "ix" means item expression, "se" means
set expressions, and "le" means list expression. These are:
1) Set and List Membership. The boolean "ix IN se" evaluates the set
or list expression, and returns TRUE if the item value specified by
the item expression is a member of the set or list.
2) Association Existence. The binding boolean "ix ix ix", where
the ix are item expressions or itemvars preceded by ? or BIND,
returns TRUE if a binding of the BIND itemvars (and ? itemvars that
contained BINDIT) can be found such that the association exists in
the associative store. See page 165 for more information on
binding booleans.
PNAMES PROPS
3) Relations.
ix = ix obvious interpretation
ix ix obvious interpretation
se1 < se2 true if se1 is a proper
subset of se2
se1 se2 true if se1 is identical to
se2 or is a proper subset of se2
se1 = se2 obvious interpretation
se1 se2 obvious interpretation
se1 > se2 equivalent to se2 < se1
se1 se2 equivalent to se2 se1
le1 = le2 obvious interpretation
le1 le2 obvious interpretation
PNAMES
For those desire them, each item may have a string, called its PNAME, linked
with it. This is completely independent of the Datum construct. New
items and Bracketed Triple items are created with NULL strings as their
Pnames. One may delete an item's Pname with the DEL!PNAME function which
takes an item expression as its argument. One may give a Pnameless item a
Pname with the NEW!PNAME procedure, which takes an item expression and a
string as its arguments. CVSI will give you the Pname of an item, and CVIS
with give you the item with the specified Pname. No two items may have the
same Pname. Pnames do not follow Algol scope rules. See page 220 to find
out how to use the above four functions.
If you would like your declared items to have Pnames that are the same as
the identifier used in their declaration, say "REQUIRE PNAMES" or "REQUIRE n
PNAMES" before their declaration at the beginning of the program. The n is
an estimate of the number of dynamically created items with pnames you will
use -- this causes tables for n pnames to be allocated at compile time
rather than runtime, thus making your program more efficient.
PROPS
Any item may have a PROPS. This is an extra 12 bits of storage (frequently
used for bits). PROPS (X) where X is an item expression is exactly an
integer variable in its syntax. See page 161 for further information on
props.
BACKTRACKING Introduction Syntax
SECTION 15
BACKTRACKING
15.1 Introduction
Backup or backtracking is the ability to "back up" execution to a previous
point. Sail facilitates backtracking by allowing one to REMEMBER, FORGET,
or RESTORE variables in the data type CONTEXT.
15.2 Syntax
<context!declaration>
::= CONTEXT <id!list>
::= CONTEXT ARRAY <array!list>
::= CONTEXT ITEM <id!list>
::= CONTEXT ITEMVAR <id!list>
<backtracking!statement>
::= <rem!keyword> <variable!list>
<rem!preposition> <context!variable>
<rem!keyword>
::= REMEMBER
::= FORGET
::= RESTORE
<rem!preposition>
::= IN
::= FROM
<variable!list>
::= <vari!list>
::= ( <vari!list> )
::= ALL
::= <context!variable>
<vari!list>
::= <vari>
::= <vari!list> , <vari>
Semantics THE CONTEXT DATA TYPE
<vari>
::= <variable>
::= <array!identifier>
<context!variable>
::= <variable>
<array!identifier>
::= <identifier>
<context!element>
::= <context!variable> : <variable>
15.3 Semantics
THE CONTEXT DATA TYPE
A context is essentially a storage place of undefined capacity. When we
REMEMBER a variable in a context, we remember the name of the variable along
with its current value (if an array, values). If we remember a value which
we have already remembered in the named context, we destroy the old value we
had remembered and replace it with the current value of the variable.
Values can be given back to variables with the RESTORE statement.
Context variables are just like any other variables with respect to scope.
Also, at execution time, context variables are destroyed when the block in
which they were declared is exited in order to reclaim their space. Context
arrays, items, and itemvars are legal (items and itemvars are part of Leap).
NEW( <context variable> ) is legal (NEW is also part of Leap).
RESTRICTIONS:
1. Context procedures do not exist. Use context itemvar procedures
instead.
2. Context variables may only be passed by reference to procedures (i.e.,
contexts are not copied).
3. Contexts may not be declared "GLOBAL" (shared between jobs - SUAI
only).
4. +, *, /, and all other arithmetic operators have no meaning when
applied to Context variables. Therefore, context variable expressions
always consist only of a context variable.
REMEMBER RESTORE
The empty context is NULL!CONTEXT. Context variables are initialized to
NULL!CONTEXT at program entry.
REMEMBER
To save the current values of variables, list them, with or without
surrounding parentheses, in the remember statement. All of an array will be
remembered if subscripts of an array are not used, otherwise, only the value
indicated will be remembered. If a variable has already been remembered in
context, its value is replaced by the current value. If one wants to update
all the variables so far remembered in this context, one may say
REMEMBER ALL IN <context>.
If you have several contexts active,
REMEMBER CNTXT1 IN CNTXT2
will note the variables Remembered in CNTXT1, and automatically Remember
their CURRENT values in CNTXT2.
RESTORE
To restore the values of variables that were saved in a context, list them
(with or without surrounding parentheses) in a restore statement.
Restoring an array without using subscripts causes as much of the array that
was remembered to be restored magically to the right locations in the array.
You can remember a whole array, then restore all or selected parts (e.g.
RESTORE A[1, 2] FROM IX;). If you remembered only A[1, 2], then restoring A
will only update A[1, 2]. RESTORE ALL IN IX will of course restore all the
variables from IX. RESTORE CNTXT1 FROM CNTXT2 will act like a list of the
variables in CNTXT1 was presented to the Restore instead of the identifier
CNTXT1.
Astute Leap users will have noted that the syntax for variables includes
Datum(typed itemvar) and similar things. If one executes REMEMBER DATUM
(typed!item!expression!1) IN CNTXT, then RESTORE DATUM (<item!expression!2>)
FROM CNTXT will give an error message unless the <typed!item!expression!2>
returns the same item as <typed!item!expression!1>.
WARNING!!! Restoring variables that have been destroyed by block exits will
give you garbage. For example, the following will blow up:
BEGIN "BLOWS UP"
CONTEXT J1;
INTEGER J;
BEGIN INTEGER ARRAY L[1:J];
REMEMBER J, L IN J1;
END;
RESTORE ALL FROM J1;
END "BLOWS UP";
FORGET IN!CONTEXT CONTEXT ELEMENTS
FORGET
The forget statement just deletes the variable from the context without
touching the current variable's value. Variables remembered in a context
should be forgotten before the block in which the variables were declared
is exited. FORGET ALL FROM X1 and FORGET CNTXT1 FROM CNTXT2 work just as
the similar Restore statements work, only the variables are Forgotten
instead of Restored.
IN!CONTEXT
The runtime boolean IN!CONTEXT returns true if the specified variable is in
the specified context. For details, see page 95.
CONTEXT ELEMENTS
Context elements provide a convenient method of accessing a variable that is
being remembered in a context. Examples of context elements:
CNTXT!VARI : SOME!VARI
DATUM (CNTXT!ITEM) : SOME!VARI
CNTXT!AR[2,3] : ARRY[4]
DATUM (CNTXT!VARI : ITMVR)
CNTXT!VARI : DATUM(ITMVR)
A context element is syntactically and semantically equivalent to a variable
of the same type as the variable following the colon. For the complete
syntax of variables, see page 226. Assignments to context elements change
the Remembered value (i.e., X_5; REMEMBER X IN C; C:X_6; RESTORE X FROM C;
will leave X with the value 6).
As with the Restore statement, one may not use Context Elements of variables
destroyed by block exits.
RESTRICTIONS: (1) One may not Remember Context Elements. (2) Passing
Context Elements by reference to procedures that change contexts is
dangerous. Namely, if the procedure Forgets the element that was passed to
it by reference, then the user is left with a dangling pointer. A more
subtle variation of this disaster occurs when the Context element passed is
an array element. If the procedure Remembers the array that that array
element was a part of, the formal that had the array element Context Element
passed to it is left with a dangling pointer.
PROCESSES Introduction Syntax Semantics STATUS OF A PROCESS
SECTION 16
PROCESSES
16.1 Introduction
A PROCESS is a procedure call that may be run independently of the main
program. Several processes may "run" concurrently. When dealing with a
multi-process system, it is not quite correct to speak of "the main
program". The main program is actually a process itself, the main process.
This section will deal with the creation, control, and destruction of
processes, as well as define the memory accessible to a process. The
following section will describe communication between processes.
16.2 Syntax
<process!statement>
::= <sprout!statement>
<sprout!statement>
::= SPROUT ( <item!expression> ,
<procedure!call> ,
<algebraic!expression> )
::= SPROUT ( <item!expression> ,
<procedure!call> )
::= SPROUT ( <item!expression> ,
<apply!construct> )
<sprout!default!declaration>
::= SPROUT!DEFAULTS <integer!constant>
16.3 Semantics
STATUS OF A PROCESS
A process can be in one of four states: terminated, suspended, ready, or
running. A terminated process can never be run again. A suspended process
can be run again, but it must be explicitly told to run by some process that
is running. Since Sail is currently implemented on a single processor
SPROUTING A PROCESS
machine, one cannot really execute two procedures simultaneously. Sail uses
a scheduler to swap processes from ready to running status. A running
process is actually executing, while a ready process is one which may be
picked by the scheduler to become the running process. The user may
retrieve the status of a process with the execution time routine PSTATUS,
page 195. SPROUTING A PROCESS
One creates a process with the SPROUT statement:
SPROUT (<item>, <procedure call>, <options>)
SPROUT (<item>, <procedure call>)
<item> is a construction item expression (i.e. do not use ANY or BINDIT).
Such an item will be called a process item. The item may be of any type;
however, its current datum will be writen over by the SPROUT statement, and
its type will be changed to "process item" (see TYPEIT, page 218).
RESTRICTION: A user must never modify the datum of a process item.
<procedure call> is any procedure call on a regular or recursive procedure,
but not a simple procedure. This procedure will be called the process
procedure for the new process.
<options> is an integer that may be used to specify special options to the
SPROUTer. If <options> is left out, 0 will be used. The different fields
of the word are as follows:
BITS NAME DESCRIPTION
14-17 QUANTUM (X) Q _ IF X=0 THEN 4 ELSE 2^X; The process will be given
a quantum of Q clock ticks, indicating that if the user is
using CLKMOD to handle clock interrupts, the process should
be run for at most Q clock ticks, before calling the
scheduler. (see about CLKMOD, page 214 for details on making
processes "time share").
18-21 STRINGSTACK (X) S _ IF X=0 THEN 16 ELSE X*32; The process will be
given S words of string stack.
22-27 PSTACK (X) P_IF X=0 THEN 32 ELSE X*32; The process will be given P
words of arithmetic stack.
28-31 PRIORITY (X) P _ IF X=0 THEN 7 ELSE X; The process will be given a
priority of P. 0 is the highest priority, and reserved for
the Sail system. 15 is the lowest priority. Priorities
determine which ready process the scheduler will next pick to
make running.
32 SUSPHIMIf set, suspend the newly sprouted process.
33 Not used at present.
SPROUT!DEFAULTS MEMORY ACCESSIBLE TO A PROCESS
34 SUSPME If set, suspend the process in which this sprout statement
occurs.
35 RUNME If set, continue to run the process in which this sprout
statement occurs.
The names are defined in the file SUAISYS:PROCES.DEF, which one may
require as a source file. Options words may be assembled by simple
addition, e.g. RUNME + PRIORITY (3) + PSTACK (2).
DEFAULT STATUS: If none of bits 32, 34, or 35 are set, then the process in
which the sprout statement occurs will revert to ready status, and the newly
sprouted process will become the running process.
The default values of QUANTUM, STRINGSTACK, PSTACK, and PRIORITY are stored
in the system variables DEFQNT, DEFSSS, DEFPSS, and DEFPRI respectively.
These values may be changed. The variables are declared EXTERNAL INTEGERs
in SUAISYS:PROCES.DEF.
SPROUT!DEFAULTS
If one of the "allocation" fields of the options word passed to the SPROUT
routine -- i.e., QUANTUM, STRINGSTACK, PSTACK, or PRIORITY -- is zero,
then SPROUT will look at the corresponding field of the specified
<integer!constant> of the SPROUT!DEFAULTS for the procedure being sprouted.
If the field is non-zero then that value will be used; otherwise the
current "system" default will be used.
NOTE: SPROUT!DEFAULTS only applies to "allocations", i.e., the process
status control bits (e.g. SUSPME) are not affected. Example:
RECURSIVE PROCEDURE FOO;
BEGIN
SPROUT!DEFAULTS STRINGSTACK (10);
INTEGER XXX;
:
:
END;
:
SPROUT (P1, FOO, STRINGSTACK (3));
SPROUT (P2, FOO);
COMMENT P1 will have a string stack of 3*32 words.
P2 will have a string stack of 10*32 words;
MEMORY ACCESSIBLE TO A PROCESS
A process has access to the same global variables as would a "normal" call
of the process procedure at the point of the SPROUT statement. For example,
suppose you Sprouted a process in the first instantiation of a recursive
procedure and immediately suspended it. Then in another instantiation of
the procedure, you resumed the process. Since each recursive instantiation
of a procedure creates and initializes new instances of its local variables,
the process uses the instances of the recursive procedure's locals that were
current at the time of the SPROUT, namely those of the first instantiation.
Sail will give you an error message whenever the global variables of a
process are deallocated but the process still exists. Usually, this means
that when the block in which the process procedure was declared is exited,
the corresponding process must be terminated (one can insure this by using a
small Cleanup procedure that will TERMINATE the fated process or JOIN it to
the current one -- see about Cleanup, page 23, Terminate, page 192, and
Join, page 194). When the process procedure has been declared inside a
recursive procedure, things become a bit more complex. As mentioned above,
the process takes its globals from the context of the Sprout statement.
Therefore, it is only in the instantiation of the recursive procedure that
executed the Sprout that trouble can occur. For example,
RECURSIVE PROCEDURE TENLEVEL (INTEGER I);
BEGIN "TROUBLE"
PROCEDURE FOO;
; COMMENT does nothing;
IF I=5 THEN SPROUT (NEW, FOO, SUSPHIM);
COMMENT sprouts FOO on the 5th
instantiation of TENLEVEL, then
immediately suspends it;
IF I<10 THEN TENLEVEL (I+1);
RETURN;
COMMENT assuming TENLEVEL is called
with I=0, it will do 10 instantiations,
then come back up;
END "TROUBLE";
TENLEVEL will nest 10 deep, then start returning. This means "TROUBLE" will
be exited five times will no ill effects. However, when Sail attempts to
exit "TROUBLE" a sixth time, it will be exiting a block in which a process
was sprouted and declared. It will generate the error message,
"Unterminated process dependent on block exited".
The construct DEPENDENTS (<block!name>), where <block!name> is a string
constant, produces a set of process items. The process items are those of
all the processes which depend on the current instance of the named block --
i.e. all processes whose process procedures obtain their global variables
from that block (via the position of the process procedure's declaration, or
occasionaly via the location of the Sprout in a nest of recursive procedure
instantiations). This construct may be used together with a CLEANUP
procedure (see page 23) to avoid having a block exit before all procedures
dependent on it have been terminated.
SPROUT APPLY SPROUTING MATCHING PROCEDURES SCHEDULING
If one Sprouts the same non-recursive procedure more than once (with
different process items, of course), the local variables of the procedure
are not copied. In other words, "X_5" in process A will store 5 in the same
location that "X_10" in process B would store 10. If such sharing of memory
is undesirable, declare the process procedure RECURSIVE, and then new
instances of the local variables of the procedure will be created with each
Sprout involving that procedure. Then "X" in process A will refer to a
different memory location than "X" in process B.
SPROUT APPLY
The <procedure call> in a SPROUT statement may be an APPLY construct. In
this case SPROUT will do the "right" thing about setting up the static
link for the APPLY. That is, "up-level" references by the process will be
made to the same variable instances that would be used if the APPLY did not
occur in a SPROUT statement. (See page 206.)
However, there is a glitch. The sprout mechanism is not yet smart enough to
find out the block of the declaration of the procedure used to define the
procedure item. It would be nice if it did, since then it could warn the
user when that block was exited and yet the process was still alive, and
thus potentially able to refer to deallocated arrays, etc. What the sprout
does instead is assume the procedure was declared in the outer block. This
may be fixed eventually, but in the meantime some extra care should be taken
when using apply in sprouts to avoid exiting a block with dependents.
Similarly, be warned that the "DEPENDENTS (<blockid>)" construct may not
give the "right" result for sprout applies.
SPROUTING MATCHING PROCEDURES
When a matching procedure is the object of a Sprout statement, the FAIL and
SUCCEED statements are interpreted differently than they would be were the
matching procedure called in a Foreach or as a regular procedure. FAIL is
equivalent to RESUME (CALLER (MYPROC), CVI (0)). SUCCEED is equivalent to
RESUME (CALLER (MYPROC), CVI (-1)).
SCHEDULING
One may change the status of a process between terminated, suspended and
ready/running with the TERMINATE, SUSPEND, RESUME, and JOIN constructs
discussed above, and the CAUSE and INTERROGATE constructs discussed in the
next chapter. This section will describe how the the status of processes
may change between ready and running.
Whenever the currently running process performs some action that causes its
status to change (to ready, terminated, or suspended) without specifying
which process is to be run next, the Sail process scheduler will be invoked.
It chooses a process from the pool of ready processes. The process it
chooses will be made the next running process. The scheduling algorithm is
essentially round robin within priority class. In other words, the
scheduler finds the highest priority class that has at least one ready
process in it. Each class has a list of processes associated with it, and
the scheduler choses the first ready process on the list. This process then
POLLING POINTS
becomes the running process and is put on the end of the list. If no
processes have ready status, the scheduler looks to see if the program is
enabled for any interrupts (see Interrupts, page 207). If the program is
enabled for some kind of interrupt that might still happen (not arithmetic
overflow, for instance), then the scheduler puts the program in interrupt
wait. After the interrupt is dismissed, the scheduler tries again to find a
ready process. If no interrupts that may still happen are enabled, and
there are no ready processes, the error message "No one to run" is issued.
The rescheduling operation may be explicitly invoked by calling the runtime
routine URSCHD, which has no parameters.
POLLING POINTS
Polling points are located at "clean" or "safe" points in the program;
points where a process may change from running to ready and back with no bad
effects. Polling points cause conditional rescheduling. A polling point is
an efficient version of the statement:
IF INTRPT NOPOLL THEN
BEGIN INTRPT_0; URSCHD END;
INTRPT is an external integer that is used to request rescheduling at the
next polling point. It is commonly set by the deferred interrupt routine
DFRINT (for all about deferred interrupts, see page 215) and by the clock
interrupt routine CLKMOD (for how to make processes time share, see page
214). The user may use INTRPT for his own purposes (carefully, so as not to
interfere with DFRINT or CLKMOD) by including the declaration "EXTERNAL
INTEGER INTRPT", then assigning INTRPT a non-zero value any time he desires
the next polling point to cause rescheduling. NOPOLL is another external
integer that is provided to give the user a means of dynamically inhibiting
polling points. For example, suppose one is time sharing using CLKMOD. In
one of the processes, a point is reached where it becomes important that the
processes not be swapped out until a certain tight loop is finished up. By
assigning NOPOLL (which was declared an EXTERNAL INTEGER) a non-zero value,
the polling points in the loop are efficiently ignored. Zeroing NOPOLL
restores normal time sharing.
A single polling point can be inserted with the statement POLL. The
construct
REQUIRE n POLLING!INTERVAL
where n is a positive integer, causes polling points to be inserted at safe
points in the code, namely: at the start of every statement provided that at
least n instructions have been emitted since the last polling point, after
every label, and at the end of every loop. If n 0 then no further polling
points will be put out until another Require n (n>0) Polling!Interval is
seen.
Process Runtimes TERMINATE SUSPEND RESUME
16.4 Process Runtimes
******************************* TERMINATE ******************************;
TERMINATE (PROC!ITM)
The process for which PROC!ITM is the process item is terminated. It is
legal to terminate a terminated process. A terminated process is truly
dead. The item may be used over for anything you want, but after you have
used it for something else, you may not do a terminate on it. Termination
of a process causes all blocks of the process to be exited.
******************************** SUSPEND *******************************;
ITM _ SUSPEND (PROC!ITM)
The process for which PROC!ITM is the process item is suspended. If the
process being suspended is not the currently running process then the item
returned is ANY. In cases such as
X _ SUSPEND (MYPROC);
where the process suspends itself, it might happen that this process is made
running by a RESUME from another process. If so, then X receives the
SEND!ITM that was an argument to the RESUME.
One may suspend a suspended process. Suspending a terminated process will
cause an error message. If the process being suspended is the currently
running process (i.e. the process suspends itself), then the scheduler will
be called to find another process to run. A process may also be suspended
as the result of RESUME or JOIN.
********************************* RESUME *******************************;
RET!ITM _ RESUME (PROC!ITM,
SEND!ITM, OPTIONS(0))
RESUME provides a means for one process to restore a suspended process to
ready/running status while at the same time communicating an item to the
awakened process. It may also specify what its own status should be. It
may be used anywhere that an itemvar procedure is syntactically correct.
When a process which has suspended itself by means of a RESUME is
subsequently awakened by another resume, the SEND!ITM of the awakening
RESUME is used as the RET!ITM of the RESUME that caused the suspension. For
example, suppose that process A has suspended itself:
CALLER
STARTINFO _ RESUME (Z, NEED!TOOL);
If later a process B executes the statement
INFOFLAG _ RESUME (A, HAMMER);
then B will suspend itself and A will become the running process. A's
process information will be updated to remember that it was awakened by B
(so than the runtime routine CALLER can work). Finally, A's RESUME will
return the value HAMMER, which will be assigned to STARTINFO. If A had been
suspended by SUSPEND or JOIN then the SEND!ITM of B's RESUME is ignored.
A process that has been suspended in any manner will run from the point of
suspension onward when it is resumed.
OPTIONS is an integer used to change the effect of the RESUME on the current
process (MYPROC) and the newly resumed process.
BITS NAME DESCRIPTION
33-32 READYMEIf 33-32 is 1, then the current process will not be
suspended, but be made ready.
KILLME If 33-32 is 2, then the current process will be terminated.
IRUN If 33-32 is 3, then the current process will not be
suspended, but be made running. The newly resumed process
will be made ready.
34 This should always be zero.
35 NOTNOW If set, this bit makes the newly resumed process ready
instead of running. If 33-32 are not 3, then this bit causes
a rescheduling.
DEFAULT: If none of bits 35 to 32 are set, then the current process will be
suspended and the newly resumed process will be made running. At SUAI
include a REQUIRE "SYS:PROCES.DEF" SOURCE!FILE in your program to get the
above bit names defined. Options may then be specified by simple addition,
e.g. KILLME + NOTNOW.
********************************* CALLER *******************************;
PROCITEM _ CALLER (PROCITEM2)
CALLER returns the process item of the process that most recently resumed
the process referred to PROCITEM2. PROCITEM2 must be the process item of an
DDFINT JOIN MYPROC PRISET
unterminated process, otherwise an error message will be issued. If
PROCITEM2's process has never been called, then the process item of the
process that sprouted PROCITEM2 is returned.
********************************* DDFINT *******************************;
DDFINT
A polling point is SKIPE INTRPT; PUSHJ P, DDFINT. DDFINT suspends the
current process (but leaves it ready to run), then calls the scheduler;
DDFINT is like SUSPEND (MYPROC, IRUN+NOTNOW).
********************************** JOIN ********************************;
JOIN (SET!OF!PROCESS!ITEMS)
The current process (the one with the JOIN statement in it) is suspended
until all of the processes in the set are terminated. WARNING: Be very
careful; you can get into infinite wait situations.
1. Do not join to the current process; since the current process is
now suspended, it will never terminate of its own accord.
2. Do not suspend any of the joined processes unless you are assured
they will be resumed.
3. Do not do an interrogate-wait in any of the processes unless you
are sure that the event it is waiting for will be caused (page
196).
****************************** MYPROC *******************************;
PROCITEM _ MYPROC
MYPROC returns the process item of the process that it is executed in. If
it is executed not inside a process, then MAINPI (the item for the main
process) is returned.
********************************* PRISET *******************************;
PRISET (PROCITM, PRIORITY)
PSTATUS URSCHD
PRISET sets the priority of the process specified by PROCITM (an item
expression that must evaluate to the process item of a non-terminated
process) to the priority specified by the integer expression PRIORITY.
Meaningful priorities are the integer between 1, the highest priority, to
15, the lowest priority. Whenever a rescheduling is called for, the
scheduler finds the highest priority class that has at least one ready
process in it, and makes the first process on that list the running process.
See about the scheduler, page 190.
******************************** PSTATUS *******************************;
STATUS _ PSTATUS (PROCITM)
PSTATUS returns an integer indicating the status of the process specified by
the item expression PROCITM.
-1 running
0 suspended
1 ready
2 terminated
********************************* URSCHD *******************************;
URSCHD
URSCHD is essentially the Sail Scheduler. When one calls URSCHD, the
scheduler finds the highest priority class that has at least one Ready
process in it. Each class has a list of processes associated with it, and
the scheduler choses the first ready process on the list. This process then
becomes the running process and is put on the end of the list. If no
processes have ready status, the scheduler looks to see if the program is
enabled for any interrupts. If the program is enabled for some kind of
interrupt that may still happen (not arithmetic overflow, for instance),
then the scheduler puts the program into interrupt wait. After the
interrupt is dismissed, the scheduler tries again to find a ready process.
If no interrupts that may still happen are enabled, and there are no ready
processes, the error message "No one to run" is issued.
EVENTS Syntax Introduction
SECTION 17
EVENTS
17.1 Syntax
<event!statement>
::= <cause!statement>
::= <interrupt!statement>
<cause!statement>
::= CAUSE ( <item!expression> ,
<item!expression> ,
<algebraic!expression> )
::= CAUSE ( <item!expression> ,
<item!expression> )
<interrogate!construct>
::= INTERROGATE ( <item!expression> ,
<algebraic!expression> )
::= INTERROGATE ( <item!expression> )
::= INTERROGATE ( <list!expression> ,
<algebraic!expression> )
::= INTERROGATE ( <list!expression> )
17.2 Introduction
The Sail event mechanism is really a general message processing system which
provides a means by which an occurrence in one process can influence the
flow of control in other processes. The mechanism allows the user to
classify the messages, or "event notices", into distinct types ("event
types") and specify how each type is to be handled.
Any leap item may be used as an event notice. An event type is an item
which has been given a special runtime data type and datum by means of the
runtime routine:
MKEVTT (et)
where et is any item expression (except ANY or BINDIT). With each such
event type Sail associates:
Sail-defined Cause and Interrogate THE CAUSE STATEMENT
1. a "notice queue" of items which have been "caused" for this event
type.
2. a "wait queue" of processes which are waiting for an event of this
type.
3. procedures for manipulating the queues.
The principle actions associated with the event system are the CAUSE
statement and the INTERROGATE construct. Ordinarily these statements cause
standard Sail runtime routines to be invoked. However, the user may
substitute his own procedures for any event type (see User Defined Cause
and Interrogate procedures, page 199). The Cause and Interrogate statements
are here described in terms of the Sail system supplied procedures.
17.3 Sail-defined Cause and Interrogate
THE CAUSE STATEMENT
CAUSE (<event type>, <event notice>, <options>)
CAUSE (<event type>, <event notice> )
<event type> is an item expression, which must yield an event type item.
<event notice> is an item expression, and can yield any legal item.
<options> is an integer expression. If <options> is left out, 0 is used.
The Cause statement causes the wait queue of <event type> to be examined.
If it is non-empty, then the system will give the <event notice> to the
first process waiting on the queue (see about the WAIT bit in Interrogate,
below). Otherwise, <event notice> will be placed at the end of the notice
queue for <event type>.
The effect of Cause may be modified by the appropriate bits being set in the
options word:
BITS NAME DESCRIPTION
35 DONTSAVE Never put the <event item> on the notice queue. If
there is no process on the wait queue, this makes the cause
statement a no-op.
34 TELLALLSet the status of all processes waiting for this event to
READY.
33 RESCHEDULE Reschedule as soon as possible (i.e., immediately after
the cause procedure has completed executed).
DEFAULT: If bits 35 to 33 are 0, then the either a single process is
THE INTERROGATE CONSTRUCT - SIMPLE FORM THE INTERROGATE CONSTRUCT - SET FORM
awakened from the wait queue, or the event is placed on the notice queue.
The process doing the Cause continues to run. At SUAI, REQUIRE
"SYS:PROCES.DEF" SOURCE!FILE to get the above bit names defined. Options
can then be constructed with simple addition, e.g. DONTSAVE + TELLALL. THE
INTERROGATE CONSTRUCT - SIMPLE FORM
<itemvar> _ INTERROGATE (<event type>, <options>)
<itemvar> _ INTERROGATE (<event type>)
<event type> is an item expression, which must yield an event type item.
<options> is an integer expression. If <options> is left out, 0 is used.
The notice queue of <event type> is examined. If it is non-empty, then the
first element is removed and returned as the value of the Interrogate.
Otherwise, the special item BINDIT is returned.
<options> modifies the effect of the interrogate statement as follows:
BITS NAME DESCRIPTION
35 RETAIN Leave the event notice on the notice queue, but still return
the notice as the value of the interrogate. If the process
goes into a wait state as a result of this interrogate, and
is subsequently awakened by a Cause, then the DONTSAVE bit
in the Cause statement will override the RETAIN bit in the
Interrogate if both are on.
34 WAIT If the notice queue is empty, then suspend the process
executing the interrogate and put its process item on the
wait queue.
33 RESCHEDULE Reschedule as soon as possible (i.e., immediately after
execution of the interrogate procedure).
32 SAY!WHICH Creates the association EVENT!TYPE <event notice>
<event type> where <event type> is the type of the event
returned. Useful with the set form of the Interrogate
construct, below.
DEFAULT: If bits 35 to 32 are 0, then the interrogate removes an event from
the event queue, and returns it. If the event queue is empty, BINDIT is
returned and no waiting is done; the process continues to run. At SUAI, use
a REQUIRE "SYS:PROCES.DEF" SOURCE!FILE to get the names defined; use simple
addition to form options, e.g. RETAIN + WAIT. THE INTERROGATE CONSTRUCT -
SET FORM
<itemvar> _ INTERROGATE (<event type set>)
<itemvar> _ INTERROGATE (<event type set>, <options>)
<event type set> is a set of event type items. <options> is an integer
expression. If it is left out, 0 will be used.
User-defined Cause and Interrogate EVENT TYPE DATA STRUCTURE
The set form of interrogate allows the user to examine a whole set of
possible event types. This form of interrogate will first look at the
notice queues, in turn, of each event type in <event type set>. If one of
these notice queues is non-empty, then the first notice in that queue will
be remved and that notice will be returned as the value of the Interrogate.
If all the notice queues are empty, and WAITing is not specified in the
options word, then BINDIT will be returned. When the WAIT bit is set, the
process doing the interrogate gets put at the end of the wait queues of each
event type in <event type set>. Then, when a notice is finally available,
the process is removed from all of the wait queues before returning the
notice. Note that the option SAY!WHICH provides a means for determining
which event type produced the returned notice.
17.4 User-defined Cause and Interrogate
By executing the appropriate runtime routine, the user can specify that some
non-standard action is to be associated with CAUSE or INTERROGATE for a
particular event type. Such user specified cause or interrogate procedures
may then manipulate the event data structure directly or by themselves
invoking the primitives used by the Sail Cause and Interrogate constructs.
User defined Cause and Interrogate are not for novice programmers (this is
an understatement).
EVENT TYPE DATA STRUCTURE
The datum of an event type item points to a six word block of memory. This
block contains the following information:
WORD NAME TYPE DESCRIPTION
0 NOTCQ LIST The list of all notices pending for this event type.
1 WAITQ LIST The list of all processes currently waiting for a
notice of this type.
2 --- --- Procedure specifier for the user specified cause
procedure (zero if system procedure is to be used).
3 --- --- Procedure specifier for the user specified
interrogate procedure (zero if system procedure is to be
used).
4 USER1 INTEGER Reserved for user.
5 USER2 INTEGER Reserved for user.
The appropriate macro definitions for these names (e.g. WAITQ (et) =
"MEMORY[ DATUM (et)+1, LIST ]" ) are included in the file
SUAISYS:PROCES.DEF.
USER CAUSE PROCEDURES
USER CAUSE PROCEDURES
A procedure to be used as a Cause procedure must have three formal value
parameters corresponding to the event type, event notice, and options of the
Cause. Such a procedure is associated with an event type by means of the
runtime SETCP:
SETCP (<event type>, <procedure specifier>);
where <event type> must yield an event type item and <procedure specifier>
is either a procedure name or DATUM (<procedure item>). For example:
PROCEDURE CX (ITEMVAR ET, EN; INTEGER OPT);
BEGIN
PRINT ("Causing ", EN,
" as an event of type ", ET);
CAUSE1 (ET, EN, OPT);
END;
...
SETCP (FOO, CX);
Now,
CAUSE (FOO, BAZ);
would cause CX (FOO, BAZ) to be called. This procedure would print out
"Causing BAZ as an event of type FOO" and then call CAUSE1. The runtime
CAUSE1 (ITEMVAR etype, enot; INTEGER opt) is the Sail runtime routine that
does all the actual work of causing a particular notice, enot, as an
instance of event type etype. It is essentially this procedure which is
replaced by a user specified cause procedure.
CAUSE1 uses an important subroutine which is also available to the user.
The integer runtime ANSWER (ITEMVAR ev!type, ev!not, process!item) is used
to wake up a process that has suspended itself with an interrogate. If the
process named by process!item is suspended, it will be set to ready status
and be removed from any wait queues it may be on. ANSWER will return as its
value the options bits from the interrogate that caused the process to
suspend itself. If the named process was not suspended, then ANSWER
returns an integer word with bit 18 (the '400000 bit in the right half =
NOJOY in SUAISYS:PROCES.DEF) set to 1. The ev!type and ev!not must be
included in case the SAY!WHICH bit was on in the interrogate which caused
the suspension. ANSWER has no effect on the notice queue of ev!type.
Frequently one may wish to use a cause procedure to re-direct some notices
to other event types. For instance:
USER INTERROGATE PROCEDURES
PROCEDURE CXX (ITEMVAR ET, EN; INTEGER OPT);
BEGIN ITEMVAR OTH; LABEL C;
IF redirecttest(ET, EN) THEN
FOREACH OTH | OTHER!CAUSEETOTH DO
C: CAUSE1 (ET, EN, OPT)
ELSE CAUSE1 (ET, EN, OPT);
END;
In order to avoid some interesting race conditions, the implementation will
not execute the causes at C immediately. Rather, it will save ET, EN and
OPT, then, when the procedure CXX is finally exited, any such deferred
causes will be executed in the order in which they were requested. USER
INTERROGATE PROCEDURES
A user specified interrogate procedure must have two value formal parameters
corresponding to the two arguments to INTERROGATE and should return an item
as the value. The statement
SETIP (<event type>, <procedure specifier>);
where <event type> is an event type item, and <procedure specifier> is
either a procedure name or DATUM (<procedure item>), will make the specified
procedure become the new interrogate procedure for <event type>. For
instance:
ITEMVAR PROCEDURE IX (ITEMVAR ET; INTEGER OPT);
BEGIN ITEMVAR NOTI;
NOTI _ ASKNTC (ET, OPT);
PRINT ("Notice ", NOTI, " returned
from interrogation of ", ET);
RETURN (NOTI);
END;
...
SETIP (FOO, IX);
Now,
... _ INTERROGATE (FOO);
would cause NOTI to be set to the value of ASKNTC (FOO, 0). Then the
message "Notice BAZ returned from interrogate of FOO" would be printed and
IX would return NOTI as its value.
The runtime ASKNTC (ITEMVAR etype; INTEGER opt) is the Sail system routine
for handling the interrogation of a single event type. Essentially it is
the procedure being replaced by the user interrogate procedure.
In the case of multiple interrogations, Sail sets a special bit (bit 19 =
'200000 in the right half = MULTIN in SUAISYS:PROCES.DEF) in the options
word before doing any of the interrogates specified by the event type items
in the event type set. The effect of this bit, which will also be set in
the options word passed to a user interrogate procedure, is to cause ASKNTC
always to return BINDIT instead of ever waiting for an event notice. Then,
if ASKNTC returns BINDIT for all event types, Sail will cause the
interrogating process to Wait until its request is satisfied. If multin is
not set, then ASKNTC will do the WAIT if it is told to.
PROCEDURE VARIABLES Syntax Semantics ASSIGN
SECTION 18
PROCEDURE VARIABLES
18.1 Syntax
<assign!statement>
::= ASSIGN ( <item!expr> ,
<procedure!name> )
::= ASSIGN ( <item!expr> , DATUM (
<item!expr> ) )
<ref!item!construct>
::= REF!ITEM ( <expression> )
::= REF!ITEM ( VALUE <expression> )
::= REF!ITEM ( BIND <itemvar> )
::= REF!ITEM ( ? <itemvar> )
<apply!construct>
::= APPLY ( <procedure!name> )
::= APPLY ( <procedure!name> ,
<arg!list!specifier> )
::= APPLY ( DATUM ( <item> ) )
::= APPLY ( DATUM ( <item> ) ,
<arg!list!specifier> )
<arg!list!specifier>
::= <list!expression>
::= ARG!LIST ( <expr!list> )
18.2 Semantics
ASSIGN
One may give an item a procedure "datum" using the ASSIGN statement.
ASSIGN accepts as its first argument an item expression (do not use ANY
or BINDIT). To this is bound the procedure identified by its name or
to the "datum" of another procedure item. The procedure may be any
type. However, the value it returns will only be accessible if the
procedure is an itemvar or item procedure. Apply assumes that whatever the
procedure left in AC 1, (the register used by all non-string procedures to
return a value) on exiting is an item number. Warning: a procedure is
REF!ITEM ARG!LIST
no ordinary datum. Using DATUM on a procedure item except in the above
context will not work. Use APPLY instead.
REF!ITEM
Reference items are created at run time by the REF!ITEM construct and are
used principally in argument lists for the APPLY construct. The datum of a
reference item contains a pointer to a data object, together with type
information about that object. To create a reference item one executes
itm _ REF!ITEM (<expression>)
A NEW item is created. If the expression is (a) a simple variable or an
array element, then the address will be saved in the item's datum. If the
expression is (b) a constant or "calculated" expression, then Sail will
dynamically allocate a cell into which the value of the expression will be
saved, and the address of that cell will be saved in the datum of the item.
The item is then noted as having the datum type "reference" and returned as
the value of the REF!ITEM construct. One can slightly modify this
procedure by using one of the following variations.
itm _ REF!ITEM (VALUE <expression>)
In this case, a temp cell will always be allocated. Thus X_3;
XI_REF!ITEM (VALUE X); X_4; would cause the datum of XI to point at a cell
containing 3.
itm _ REF!ITEM (? itmvr)
itm _ REF!ITEM (BIND itmvr)
where itmvr must be an itemvar or an element of an itemvar array, will cause
the reference item's datum to contain information that Apply can use to
obtain the effect of using "? itmvr" or "BIND itmvr" as an actual parameter
in a procedure call.
ARG!LIST
The ARG!LIST construct assembles a list of "temporary" reference items that
will be deleted by APPLY after the applied procedure returns. Arguments to
ARG!LIST may be anything legal for REFITEM. Thus
APPLY (proc, ARG!LIST (foo, bar, VALUE baz))
is roughly equivalent to
tmplst _ {{REF!ITEM (foo), REF!ITEM (bar),
REF!ITEM (VALUE baz)~~;
APPLY (proc, tmplst);
WHILE LENGTH (tmplst) DO DELETE (LOP (tmplst));
but is somewhat easier to type. Note that the reference items created by
ARG!LIST are just like those created by REF!ITEM, except that they are
marked so that APPLY will know to kill them.
APPLY
APPLY
APPLY uses the items in the <arg!list!specifier>, together with the
environment information from the procedure item (or from the current
environment, if the procedure is named explicitly) to make the appropriate
procedure call. <arg!list!specifier> is an ordinary list expression, except
that each element of the list must be a reference item. The elements of the
list will be used as the actuals in the procedure call. There must be at
least as many list elements as there are formals in the procedure. The
reference items must refer to an object of the same type as the
corresponding formal parameter in the procedure being called. (EXCEPTION:
if the formal parameter is an untyped itemvar or untyped itemvar array, then
the reference item may refer to a typed itemvar or itemvar array,
respectively.) At present, type checking (but not type coercion) is done.
If the formal parameter is a reference parameter, then a reference to the
object pointed to by the reference item is passed. If the formal parameter
is a value parameter, then the value of the object pointed to by the
reference item is used. Similarly, "?" formals are handled appropriately
when the reference item contains a "?" or "BIND" reference. If the
procedure to be called has no parameters, the <arg!list!specifier> may be
left out.
Apply may be used wherever an itemvar procedure call is permitted. The
value returned will be whatever value would normally be returned by the the
applied procedure, but Apply will treat it as an item number. Care should
therefore be taken when using the result of Apply when the procedure being
invoked is not itself an itemvar procedure, since this may cause an invalid
item number to be used as a valid item (for instance, in a MAKE). Recall
that when a typed procedure (or an Apply) is called at statement level, the
value it returns is ignored. Here is an example of the use of APPLY.
BEGIN
LIST L;INTEGER XX;
INTEGER ITEMVAR YY;ITEMVAR ZZ;
REAL ARRAY AA[1:2];
PROCEDURE FOO (INTEGER X;
ITEMVAR Y,Z; REAL ARRAY A);
BEGIN
Y _ NEW (X);
Z _ NEW (A);
A[X]_3;
END;
XX_0;
L _ {{REF!ITEM (XX), REF!ITEM (YY),
REF!ITEM (ZZ), REF!ITEM (AA)~~;
XX _ 2; AA[1] _ AA[2] _ 1;
APPLY (FOO, L);
COMMENT Y now contains an item whose
datum is 2, Z contains an item whose
datum is the array (1.0, 1.0),
A[1]=1.0, and A[2]=3.0.;
END;
The variables accessed by a procedure called with APPLY may not always
be what you would think they were. Temporary terminology: the "environment"
of a procedure is the collection of variables, arrays and procedures
accessible to it. "Environment" is not meant to include the state of
the associative store or the universe of items. The environment of a
procedure item is the environment of the ASSIGN, and that environment will
be used regardless of the position of the APPLY. Since procedure items
are untouched by block exits, yet environments are, it is possible to
Apply a procedure item when its environment is gone; Sail catches most of
these situations and gives an error message. Consider the following
example:
BEGIN
ITEM P; LABEL L;
RECURSIVE PROCEDURE FOO (INTEGER J);
BEGIN "FOO"
INTEGER I;
PROCEDURE BAZ;
PRINT ("J=", J, " I=", I);
IF J=1 THEN
BEGIN
I_2;
ASSIGN (P, BAZ);
FOO (-1);
END
ELSE APPLY (DATUM (P));
END "FOO";
FOO (1);
L: APPLY (DATUM (P)); COMMENT will cause a
runtime error -- see discussion below;
END
The effect of the program is to Assign Baz to P on the first
instantiation of Foo, then Apply P on the second (recursive)
instantiation. However, the environment at the time of the Assign
includes {I=2, J=1~ but the environment at the time of the Apply
includes {I=0, J=-1~ instead. At the time of the Apply, Baz is
executed with the environment from the time of the Assign, and will print
out
J=1 I=2
The Apply at L will cause a runtime error message because the
environment of the Assign has been destroyed by the exiting of Foo.
INTERRUPTS Introduction
SECTION 19
INTERRUPTS
19.1 Introduction
The interrupt facilities of Sail are based on the interrupt facilities
provided by the operating system under which Sail is running. For programs
running at SUAI or on TENEX this results in satisfactory interrupt
operation. TOPS-10 programs are at a distinct disadvantage because the
operating system does not prevent interrupt handlers from being interrupted
themselves. At SUAI the Sail system uses new-style interrupts [Frost];
programs may also enable for old-style interrupts and the two will work
together provided that the same condition is not enabled under both kinds.
On TENEX the pseudointerrupt (PSI) system is used; programs may use the
interrupt system independently of Sail. Only interrupt functions pertaining
to the current fork are provided. TOPS-10 interrupts are directly tied to
the APRENB system; Sail and non-Sail use do not mix.
Sail gives control to the user program as soon as the operating system
informs the Sail interrupt handler. This can be dangerous because the Sail
runtime system may be in the middle of core allocation or garbage
collection. Therefore Sail provides a special runtime DFRINT which can
receive control in the restricted environment of an interrupt. DFRINT
records the fact that an interrupt happened and that a particular user
procedure is to be run at the next polling point (page 191), when the
integrity of all runtime data structures is (normally) assured. If the Sail
interrupt handler passes control to DFRINT then the user procedure (which is
run at the next polling point) is called a "deferred interrupt procedure",
even though the only connection it has with interrupts is the special status
and priority given to it by the Sail Process machinery. If DFRINT is not
used then the user procedure to which the Sail interrupt handler passes
control is called an "immediate interrupt procedure". (This is orthogonal
to the TENEX distinction between immediate and deferred TTY interrupts.)
To use interrupts a program must first tell Sail what procedure(s) to run
when an interrupt happens. The routines INTMAP and PSIMAP perform this
task. Deferred interrupts use the Sail process machinery (page 186), so
INTSET is used to sprout the interrupt process. Then the operating system
must be told to activate (and deactivate) interrupts for the desired
conditions. ENABLE and DISABLE are used by the program to tell Sail, which
tells the operating system.
A good knowledge of the interrupt structure of the operating system which
you are trying to use should be considered a prerequisite for this chapter.
Interrupt Routines ATI, DTI DFR1IN DFRINT
19.2 Interrupt Routines
******************************** ATI, DTI ******************************;
ATI (PSICHAN, CODE);
DTI (PSICHAN, CODE)
(TENEX only.) CODE is associated or dissociated with PSICHAN, using the
appropriate JSYS. Executing ATI is an additional step (beyond ENABLE) which
is necessary to receive TENEX TTY interrupts.
********************************* DFR1IN *******************************;
DFR1IN (AOBJN!PTR)
DFR1IN is the procedure used by DFRINT to record the interrupt and the
AOBJN!PTR. Thus DFRINT is (partially) equivalent to
SIMPLE PROCEDURE DFRINT; BEGIN
DFR1IN (<AOBJN!PTR specified
to INTMAP>) END;
To have more than one procedure run (deferred) as the result of an
interrupt, a program may use DFR1IN to record the AOBJN!PTRs explicitly.
Example:
SIMPLE PROCEDURE ZORCH;
BEGIN
DFR1IN (<AOBJN pointer for FOO call>);
DFR1IN (<AOBJN pointer for BAZ call>);
END;
...
INTMAP (INTTTY!INX, ZORCH, 0);
ENABLE (INTTTY!INX);
Both FOO and BAZ will be run (deferred) as the result of INTTTY!INX
interrupt.
********************************* DFRINT *******************************;
DFRINT
DFRINT is a predeclared simple procedure which handles the queueing of
deferred interrupts. Specify DFRINT to INTMAP for each interrupt which
DISABLE, ENABLE INTMAP
will be run as a Sail deferred interrupt. When run as the result of an
interrupt, DFRINT grabs the AOBJN!PTR pointer specified to INTMAP (or
PSIMAP) and copies the block along with other useful information into the
circular deferred interrupt buffer. (See INTTBL.) DFRINT then changes the
status of the interrupt process INTPRO from suspend to ready, and turns on
the global integer INTRPT.
**************************** DISABLE, ENABLE ***************************;
DISABLE (INDEX);
ENABLE (INDEX)
Sail tells the operating system to ignore (DISABLE) or to send to the
program (ENABLE) interrupts for the condition specified by INDEX. INDEX is
a bit number (0-35) which varies from system to system; consult [SysCall].
INDEX is sometimes called a "PSI channel" on TENEX.
********************************* INTMAP *******************************;
INTMAP (INDEX, PROC, AOBJN!PTR)
(TENEX users should see PSIMAP.) The routine INTMAP specifies that the
simple procedure PROC is to be run whenever the Sail interrupt handler
receives an interrupt corresponding to the condition specified by INDEX. A
separate INTMAP must be executed for each interrupt condition. If the same
INDEX is specified on two calls to INTMAP then the most recent call is the
one in effect. PROC must be a simple procedure with no formal parameters.
If PROC is a user procedure then PROC is run as a Sail immediate interrupt.
AOBJN!PTR should be zero unless DFRINT is specified for PROC. If PROC is
DFRINT (and thus will be a Sail deferred interrupt) then AOBJN!PTR gives the
length and location of a block of memory describing a procedure call. Such
a block has the form
<number of words in the block>
<1st parameter to the procedure>
<second parameter to the procedure>
...
<last parameter to the procedure>
-1,,<address of the procedure>
and an AOBJN!PTR to it has the form
-<number of words>,,<starting address>.
Here is an example in which FOO (I, J, K) is to be called as a deferred
interrupt.
INTSET INTTBL
PROCEDURE FOO (INTEGER i, j, k); ...;
...
SAFE INTEGER ARRAY FOOBLK [1:5];
ITEMVAR IPRO; COMMENT for process item of INTPRO;
...
FOOBLK [1] _ 5;
FOOBLK [2] _ I;
FOOBLK [3] _ J;
FOOBLK [4] _ K;
FOOBLK [5] _ (-1 LSH 18)+LOCATION (FOO);
...
INTSET (IPRO _ NEW, 0); COMMENT sprout INTPRO;
INTMAP (INTTTI!INX, DFRINT,
(-5 LSH 18) + LOCATION (FOOBLK[1]));
ENABLE(INTTTI!INX)
NOTE: The procedure (FOO in this case) must not be declared inside any
process except the main program. Otherwise, its environment will not be
available when INTPRO runs. However, there is a rather complex way to get
around this by using <environment>,,PDA as the last word of the calling
block. See a Sail hacker if you must do this and don't know what
<environment> or PDA mean.
********************************* INTSET *******************************;
INTSET (ITM, OPTIONS)
INTSET sprouts the interrupt process INTPRO with process options OPTIONS;
see page 187. The default priority of INTPRO is zero; this is the highest
possible priority and no other process may have priority zero. Thus INTPRO
is sure to be run first at any polling point. ITM must be an item; it will
become the process item of INTPRO, the interrupt process. INTSET must be
called before any deferred interrupts are used.
********************************* INTTBL *******************************;
INTTBL (NEW!SIZE)
The buffer used to queue deferred interrupts is initially 128 locations
long. The queue has not been know to overflow except for programs which do
not POLL very often. INTTBL changes the buffer size to NEW!SIZE. Do not
call INTTBL if there are any deferred interrupts pending; wait until they
have all been executed.
PSIMAP Immediate Interrupts IN SUAI Sail
********************************* PSIMAP *******************************;
PSIMAP (PSICHAN, PROC,
AOBJN!PTR, LEVEL)
(TENEX only.) This routine is the same as INTMAP except that LEVEL may be
specified. ROUTINE is executed at interrupt level LEVEL. (TENEX INTMAP is
equivalent to PSIMAP ( , , ,3).) PROC and AOBJN!PTR have the same meaning as
for INTMAP.
19.3 Immediate Interrupts
Do not access, create, or destroy strings, records, arrays, sets, or lists.
If these data structures are needed then use deferred interrupts.
To set up an immediate interrupt say
INTMAP (<index>, <simple procedure name>, 0);
ENABLE (<index>)
or on TENEX,
PSIMAP (<PSIchan>, <simple procedure name>, 0, <PSIlev>);
ENABLE (<PSIchan>)
where <index> is a code for the interrupt condition. To turn off an
interrupt use
DISABLE (<index>)
The system will not provide user interrupts for the specified condition
until another ENABLE statement is executed.
IN SUAI Sail
A procedure specified by an INTMAP statement will be executed at user
interrupt level. A program operating in this mode will not be interrupted,
but must finish whatever it is doing within 8/60 ths of a second. It may
not do any UUOs that can cause it to be rescheduled. Also, the accumulators
will not be the same ones as those that were in use by the regular program.
Certain locations are set up as follows:
ACs 1-6Set up by the system as in [Frost].
AC '15 (USER) Address of the Sail user table.
AC '16 (SP) A temporary string push down stack pointer (for the
foolhardy who chose to disregard the warnings about strings in
immediate interrupts).
AC '17 (P) A temporary push down stack pointer.
IN TOPS-10
XJBCNI (declared in SYS:PROCES.DEF as an external integer.) Bit mask
with a bit on corresponding to the current condition.
XJBTPC (declared in SYS:PROCES.DEF as an external integer.) Full PC
word of regular user level program.
The interrupt will be dismissed and the user program resumed when the
interrupt procedure is exited. For more information on interrupt level
programming consult [Frost].
IN TOPS-10
The interrupt handler again will decode the interrupt condition and call the
appropriate procedure. Since there is no "interrupt level", the interrupt
procedure must not itself generate any interrupt conditions, since this will
cause Sail to lose track of where in the user program it was interrupted
(trapped).
Also, the Sail interrupt module sets up some temporary accumulators and
JOBTPC:
AC '10 index of the interrupt condition.
AC '15 (USER) Address of the Sail user table.
AC '16 (SP) A temporary string push down list. Beware.
AC '17 (P) A temporary push down pointer.
JOBTPC (an external integer) Full PC word of regular user program.
The "real" acs -- i.e., the values of all accumulators at the time the trap
occurred -- are stored in locations APRACS to APRACS+17. Thus you can get
at the value of accumulator x by declaring APRACS as an external integer and
referring to MEMORY [LOCATION (APRACS)+x]. When the interrupt procedure is
exited the acs are restored from APRACS to APRACS+17 and the Sail interrupt
handler jumps to the location stored in JOBTPC (which was set by the
operating system to the location at which the trap occurred). Thus, if you
want to transfer control to some location in your user program, a way to do
it is to have an interrupt routine like:
IN TENEX Sail GTRPW RTIW, STIW
SIMPLE PROCEDURE IROUT;
BEGIN
EXTERNAL INTEGER JOBTPC;
:
JOBTPC_LOCATION (GTFOO);
COMMENT GTFOO is a non-simple procedure
that contains a GO TO FOO, where FOO
is the location to which control
is to be passed. This allows the
"go to solver" to be called and clean
up any unwanted procedure activations.;
END;
WARNING: this does not work very well if you were interrupted at a bad time.
IN TENEX Sail
Sail initialization does a SIR, setting up the tables to external integers
LEVTAB and CHNTAB, then an EIR to turn on the interrupt system. PSIMAP
fills the appropriate CHNTAB location with XWD LEV,LEVROU, where LEVROU is
the address of the routine that handles the interrupts for level LEV.
LEVROU saves the accumulators in blocks PS1ACS, PS2ACS, and PS3ACS, which
are external integers, for levels 1 through 3 respectively. Thus for a
level 3 interrupt accumulator x can be accessed by MEMORY [LOCATION (PS3ACS)
+ x]. The PC can be obtained by reading the LEVTAB address with the RIR
JSYS. Temporary stacks are set up for both immediate and deferred
interrupts.
See page 145 for an example of TENEX immediate interrupts. The functions
GTRPW, RTIW, STIW provide for some of the information set up in ACs under
SUAI or TOPS-10.
********************************* GTRPW ********************************;
STATUS _ GTRPW (FORK);
The trap status of FORK is returned, using the GTRPW JSYS.
******************************* RTIW, STIW *****************************;
AC1 _ RTIW (PSICHAN, @AC2);
STIW (PSICHAN, AC2, AC3);
The indicated JSYS is performed.
Clock Interrupts PSIDISMS
19.4 Clock Interrupts
(This feature is currently available only in SUAI Sail and TENEX Sail.)
Clock interrupts are a kind of immediate interrupt used to approximate time
sharing among processes. Every time the scheduler decides to run a process
it copies the procedure's time quantum (see all about quantums of processes,
page 187) into the Sail user table location TIMER. Consider the following
procedure, which is roughly equivalent to the one predeclared in Sail:
SIMPLE PROCEDURE CLKMOD;
IF (TIMER_TIMER-1) 0 THEN INTRPT_-1;
To time share several ready processes one should include polling points in
the relevant process procedures and should execute the following statements:
INTMAP (INTCLK!INX, CLKMOD, 0);
ENABLE (INTCLK!INX);
or on TENEX
PSIMAP (1, CLKMOD, 0, 3);
ENABLE (1);
PSIDISMS (1, 1000/60);
The macro SCHEDULE!ON!CLOCK!INTERRUPTS defined in SUAISYS:PROCES.DEF is
equivalent to these statements. When the time quantum of a process is
exceeded by the number of clock ticks since it began to run, the integer
INTRPT is set, and this causes the next polling point in the process to
cause a rescheduling (see about rescheduling and INTRPT on page 190). The
current running process will be made ready, and the scheduling algorithm
chooses a ready process to run.
In TENEX Sail clock interrupts are handled differently. Since TENEX does
not directly provide for interrupting user processes on clock ticks, an
inferior fork is created which periodically generates the interrupts.
******************************** PSIDISMS ******************************;
PSIDISMS (PSICHAN, MSTIME)
An inferior fork is created which interrupts the current fork every MSTIME
milliseconds of real time. The inferior is approximately
WAIT: MOVE 1,MSTIME ;HOW LONG
DISMS ;GO AWAY
MOVEI 1,-1 ;HANDLE TO SUPERIOR
MOVE 2,[bit mask];SELECTED CHANNEL
IIC ;CAUSE AN INTERRUPT
JRST WAIT ;CONTINUE
PSIRUNTM KPSITIME Deferred Interrupts
******************************** PSIRUNTM ******************************;
PSIRUNTM (PSICHAN, MSTIME)
The current fork is interrupted every MSTIME milliseconds of runtime. The
inferior is approximately
WAIT: MOVE 1,MSTIME ;HOW LONG
DISMS
MOVEI 1,-1 ;SUPERIOR FORK
RUNTM ;RUNTIME OF SUPERIOR
CAMGE 1,NEXTTIME ;READY?
JRST WAIT ;NO
ADD 1,MSTIME
MOVEM 1,NEXTTIME ;RECHARGE
MOVEI 1,-1 ;SUPERIOR
MOVE 2,[bit mask];SELECTED CHANNEL
IIC ;CAUSE INTERRUPT
JRST WAIT
******************************** KPSITIME ******************************;
KPSITIME (PSICHAN)
Discontinues clock interrupts on PSICHAN.
Several channels can be interrupted by PSIRUNTM or PSIDISMS, each with
different timing interval.
19.5 Deferred Interrupts
Deferred interrupts use the Sail Process machinery (page 186) to synchronize
the Sail runtime system with the running of user procedures in response to
interrupts. The routine INTSET sprouts the interrupt process INTPRO, the
process which eventually does the calling of deferred interrupt procedures.
This process is special because it is (ordinarily) guaranteed to be the
first process run after a rescheduling. (See page 191 and page 195 for
information on rescheduling.) When DFRINT runs as the result of an
interrupt, it copies the calling block (specified to INTMAP with the
AOBJN!PTR) into the deferred interrupt buffer and turns on the global
integer INTRPT. At the next polling point the process supervisor will
suspend the current process and run INTPRO. INTPRO calls the procedures
specified by the calling blocks in the deferred interrupt buffer, turns off
INTRPT, and suspends itself. The process scheduler then runs the process of
highest priority.
One very common use of deferred interrupts is to cause an event soon after
some asynchronous condition (say, TTY activation) occurs. This effect may
be obtained by the following sequence:
INTSET (IPRO_NEW, 0); COMMENT this will cause
the interrupt process to be sprouted and
assigned to IPRO. This process will execute
procedure INTPRO and will have priority zero
(the highest possible).;
:
INTMAP (<index>, DFRINT,
DFCPKT (0, <event type>, <event notice>,
<cause options>));
:
ENABLE (<index>);
In SUAISYS:PROCES.DEF is the useful macro
DEFERRED!CAUSE!ON!INTERRUPT (<index>,
<event type>, <notice>, <options>)
which may be used to replace the INTMAP statement.
The following program illustrates how deferred interrupts on TENEX can be
accomplished.
BEGIN REQUIRE 1 NEW!ITEM;
ITEMVAR IPRO; COMMENT for process item;
PROCEDURE FOO (INTEGER I, J);
PRINT ("HI ", I, " ", J);
INTEGER ARRAY FOOBLK[1:4];
FOOBLK[1] _ 4; COMMENT # words;
FOOBLK[2] _ 12; COMMENT arguments;
FOOBLK[3] _ 13;
FOOBLK[4] _ -1 LSH 18 + LOCATION (FOO);
INTSET (IPRO _ NEW, 0);
PSIMAP (1, DFRINT,
-4 LSH 18 + LOCATION (FOOBLK[1]), 3);
ENABLE (1); ATI (1, "Q"-'100);
DO BEGIN OUTCHR ("."); POLL; END UNTIL FALSE;
END;
The program prints dots, interspersed with "HI 12 13" for each control-Q
typed on the console. Whenever a control-Q is typed, DFRINT buffers the
request and makes INTPRO ready to run; then DFRINT DEBRKs (in the sense of
the DEBRK JSYS) back to the interrupted code. At Sail user level the POLL
THE DEFERRED INTERRUPT PROCESS - INTPRO
statement causes the process scheduler to run INTPRO, where the deferred
interrupt calling block (which was copied by DFRINT) is used to call FOO.
THE DEFERRED INTERRUPT PROCESS - INTPRO
INTPRO first restores the following information which was stored by DFRINT
at the time of the interrupt.
LOCATION CONTENTS
USER The base of the user table (GOGTAB).
AC 1 Status of spacewar buttons.
AC 2 Your job status word (JBTSTS). See [Frost].
IJBCNI(USER) XJBCNI (i.e., JOBCNI) at time of interrupt.
IJBTPC(USER) XJBTPC (i.e., JOBTPC) at time of interrupt.
IRUNNR(USER) Item number of running process at time of interrupt.
Then INTPRO calles the procedure described by the calling block. When the
procedure is finished, INTPRO looks to see if the deferred interrupt buffer
has any more entries left. If it does, INTPRO handles them in the same
manner. Otherwise INTPRO suspends itself and the highest priority ready
process takes over.
LEAP RUNTIMES Types and Type Conversion TYPEIT
SECTION 20
LEAP RUNTIMES
We will follow the same conventions for describing Leap execution time
routines as were used in describing the runtimes of the Algol section of
Sail (see page 62).
20.1 Types and Type Conversion
********************************* TYPEIT *******************************;
CODE _ TYPEIT (ITM);
The type of the datum linked to an item is called the type of an item. An
item without a datum is called untyped. TYPEIT is an integer function which
returns an integer CODE for the type of the item expression ITM that is its
argument. The codes are:
0 - item deleted or never allocated
1 - untyped
2 - Bracketed Triple item
3 - string
4 - real
5 - integer
6 - set
7 - list
8 - procedure item
9 - process item
10 - event item
11 - context item
12 - reference item
13 - record pointer
14 - label
15 - record class
23 - string array
24 - real array
25 - integer array
26 - set array
27 - list array
31 - context array
33 - record pointer array
37 - error (the runtime screwed up)
The user is encouraged to use TYPEIT. It requires the execution of only a
few machine instructions and can save considerable debugging time.
CVSET CVLIST CVN and CVI MKEVTT
********************************* CVSET ********************************;
SET _ CVSET (LIST)
CVSET returns a set given a list expression by removing duplicate
occurrences of items in the list, and reordering the items into the order of
their internal integer representations.
********************************* CVLIST *******************************;
LIST _ CVLIST (SET)
CVLIST returns a list given a set expression. It executes no machine
instructions, but merely lets you get around Sail type checking at compile
time.
****************************** CVN and CVI *****************************;
INTEGR _ CVN (ITM);
ITM _ CVI (INTEGR)
CVN returns the integer that is the internal representation of the item that
is the the value of the item expression ITM. CVI returns the item that is
represented by the integer expression INTEGR that is its argument. Legal
item numbers are between (inclusively) 1 and 4095, but you'll get in trouble
if you CVI when no item has been created with that integer as its
representation. Absolutely no error checking is done. CVI is for daring
men. See about item implementation, page 157, for more information about
the internal representations of items.
********************************* MKEVTT *******************************;
MKEVTT (ITEM)
MKEVTT will convert its item argument to an event type item. The old datum
will be overwritten. The type of the item will now be "event type". Any
item except an event type item may be converted to an event type item by
MKEVTT.
Make and Erase Breakpoints BRKERS, BRKMAK, BRKOFF Pname Runtimes CVIS
20.2 Make and Erase Breakpoints
************************* BRKERS, BRKMAK, BRKOFF ***********************;
BRKMAK (BREAKPT!PROC);
BRKERS (BREAKPT!PROC);
BRKOFF
In order to give the programmer some idea of what is going on in the
associative store, there is a provision to interrupt each MAKE and ERASE
operation, and enter a breakpoint procedure. The user can then do whatever
he wants with the three items of the association being created or destroyed.
ERASE Foo ANY ANY will cause the breakpoint procedure to be activated
once for each association that matches the pattern. MAKE it1 it2 [it3
it4 it5] will cause the breakpoint procedure to be activated twice.
The user's breakpoint procedures must have the form:
PROCEDURE Breakpt!proc (ITEMVAR a, o, v )
If the association being made or erased is AOV, then directly before doing
the Make or Erase, Breakpt!proc is called with the items A, O, and V for the
formals a, o, and v.
To make the procedure Breakpt!proc into a breakpoint procedure for MAKE,
call BRKMAK with Breakpt!proc as a parameter. To make the procedure
Breakpt!proc into a breakpoint procedure for ERASE, call BRKERS with
Breakpt!proc as its parameter. To turn off both breakpoint procedures, call
BRKOFF with no parameters.
NOTE: BRKMAK, BRKERS and BRKOFF are not predeclared. The user must include
the declarations:
EXTERNAL PROCEDURE BRKERS (PROCEDURE BP);
EXTERNAL PROCEDURE BRKMAK (PROCEDURE BP);
EXTERNAL PROCEDURE BRKOFF
20.3 Pname Runtimes
********************************** CVIS ********************************;
"PNAME" _ CVIS (ITEM, @FLAG)
The print name of ITEM is returned as a string. Items have print names only
CVSI DEL!PNAME NEW!PNAME Other Useful Runtimes LISTX
if one includes a REQUIRE n PNAMES statement in his program, where n is an
estimate of the number of pnames the program will use. An Item's print name
is the identifier used to declare it, or that pname explicitly given it by
the NEW!PNAME function (see below). FLAG is set to False (0) if the
appropriate string is found. Otherwise it is set to TRUE (-1), and one
should not put great faith in the string result.
********************************** CVSI ********************************;
ITEM _ CVSI ("PNAME", @FLAG)
The Item whose pname is the same as the string argument PNAME is returned
and FLAG is set to FALSE if such an ITEM exists. Otherwise, something very
random is returned, and FLAG is set to TRUE.
******************************* DEL!PNAME ******************************;
DEL!PNAME (ITEM)
This function deletes any string PNAME associates with this ITEM.
******************************* NEW!PNAME ******************************;
NEW!PNAME (ITEM, "STRING")
This function assigns to the Item the name "STRING". Don't perform this
twice for the same Item without first deleting the previous one. The
corresponding name or Item may be retrieved using CVIS or CVSI (see above).
The NULL string is prohibited as the second argument.
20.4 Other Useful Runtimes
********************************* LISTX ********************************;
VALUE _ LISTX (LIST, ITEM, N)
The value of this integer function is 0 if the ITEM (an item expression)
does not occur in the list at least N (an integer expression) different
times in the LIST (a list expression). Otherwise LISTX is the index of the
Nth occurrence of ITEM in LIST. For example,
FIRST, SECOND, THIRD ISTRIPLE LOP COP
LISTX ({{Foo, Baz, Garp, Baz~~, Baz, 2) is 4.
************************** FIRST, SECOND, THIRD ************************;
ITEM _ FIRST (BRAC!TRIP!ITEM);
ITEM _ SECOND (BRAC!TRIP!ITEM);
ITEM _ THIRD (BRAC!TRIP!ITEM)
The Item which is the FIRST, SECOND, or THIRD element of the association
connected to a bracketed triple item (BRAC!TRIP!ITEM) is returned. If the
item expression BRAC!TRIP!ITEM does not evaluate to a bracketed triple, an
error messages issues forth.
******************************** ISTRIPLE ******************************;
RSLT _ ISTRIPLE (ITM)
If ITM is a bracketed triple item then ISTRIPLE returns TRUE; otherwise it
returns FALSE. ISTRIPLE (ITM) is equivalent to (TYPEIT (ITM) = 2).
********************************** LOP *********************************;
ITEM _ LOP (@SETVARIABLE);
ITEM _ LOP (@LISTVARIABLE)
LOP will remove the first item of a set or list from the set or list, and
return that item as its value. Note that the argument must be a variable
because the contents of the set or list is changed. If one LOPs an empty
set or a null list, an error message will be issued.
********************************** COP *********************************;
ITEM _ COP (SETEXPR);
ITEM _ COP (LISTEXPR)
COP will return the first item of the set or list just as LOP (above) will.
However, it will NOT remove that item from the set or list. Since the set
or list will be unchanged, COP's argument may be a set or list expression.
As with LOP, an error message will be returned if one COPs an empty set or a
null list.
LENGTH SAMEIV Runtimes for User Cause and Interrogate Procedures SETCP AND SETIP
********************************* LENGTH *******************************;
VALUE _ LENGTH (SETEXPR);
VALUE _ LENGTH (LISTEXPR)
LENGTH will return the number of items in that set or list that is its
argument. LENGTH (S) = 0 is a much faster test for the null set or list
that S = PHI or S = NIL.
********************************* SAMEIV *******************************;
VALUE _ SAMEIV (ITMVAR1, ITMVAR2)
SAMEIV is useful in Matching Procedures to solve a particular problem that
arises when a Matching Procedure has at least two ? itemvar arguments. An
example will demonstrate the problem:
FOREACH X | Matchingproc ( X, X ) DO ...;
FOREACH X, Y | Matchingproc ( X, Y ) DO ... ;
Clearly, the matching procedure with both arguments the same may want to do
something different from the matching procedure with two different Foreach
itemvars as its arguments. However, there is no way inside the body of the
matching procedure to differentiate the two cases since in both cases both
itemvar formals have the value BINDIT. SAMEIV will return True only in the
first case, namely 1) both of its arguments are ? itemvar formals to a
matching procedure, 2) both had the same Foreach itemvar passed by reference
to them. It will return False under all other conditions, including the
case where the Foreach itemvar is bound at the time of the call (so it is
not passed by reference, but its item value is passed by value to both
formals).
20.5 Runtimes for User Cause and Interrogate Procedures
**************************** SETCP AND SETIP ***************************;
SETCP (ETYPE, PROC!NAME);
SETCP (ETYPE, DATUM (PROC!ITEM));
SETIP (ETYPE, PROC!NAME);
SETIP (ETYPE, DATUM (PROC!ITEM))
SETCP and SETIP associate with the event type specified by the item
expression ETYPE a procedure specified by its name or the datum of a
procedure item expression.
CAUSE1 ASKNTC
After the SETCP, whenever a Cause statement of the specified event type is
executed, the procedure specified by PROC!NAME or PROC!ITEM is called.
The procedure must have three formal parameters corresponding to the event
type, event notice, and options words of the CAUSE statement. For example,
PROCEDURE CAUSEIT (ITEMVAR ETYP, ENOT;
INTEGER OP)
After SETIP, whenever an Interrogate statement of the specified event type
is executed, the procedure specified by PROC!NAME or PROC!ITEM is called.
The procedure must have two formal parameters corresponding to the event
type and options words of the Interrogate statement and return an item.
For example,
ITEM PROCEDURE ASK!IT (ITEMVAR ETYP;
INTEGER OP)
It is an error if a Cause or Interrogate statement tries to call a procedure
whose environment (static - as determined by position of its declaration,
and dynamic - as determined by the execution of the SETCP or SETIP) has been
exited.
See page 200 and page 201 for more information on the use of SETCP and
SETIP, respectively.
********************************* CAUSE1 *******************************;
ITMVAR _ CAUSE1 (ETYPE, ENOT, OPTIONS);
ITMVAR _ CAUSE1 (ETYPE, ENOT);
ITMVAR _ CAUSE1 (ETYPE)
CAUSE1 is essentially the procedure executed for CAUSE statements if no
SETCP has been done for the event type ETYPE. See the description of the
Sail defined Cause statement, page 200, for further elucidation.
********************************* ASKNTC *******************************;
ITMVR _ ASKNTC (ETYPE ,OPTIONS);
ITMVR _ ASKNTC (ETYPE)
ASKNTC is the procedure executed for INTERROGATE statements if no SETIP has
been done for the event type ETYPE. See the description of the Sail defined
Interrogate statement, page 201, for further elucidation.
ANSWER DFCPKT
********************************* ANSWER *******************************;
BITS _ ANSWER (ETYPE, ENOT, PROC!ITEM)
ANSWER will attempt to wake up from an interrogate wait the process
specified by the item expression PROC!ITEM. If the process is not in a
suspended state, Answer will return an integer with the bit '400000 in the
right half (NOJOY in SUAISYS:PROCES.DEF) turned on. If the process is
suspended, it will be made ready, and removed from any wait queues it may be
on. The bits corresponding to the options word of the interrogate statement
that put it in a wait state will be returned. Furthermore, if the SAY!WHICH
bit was on, the appropriate association, namely EVENT!TYPE ENOT ETYPE,
will be made. See page 200 for more information on the use of ANSWER.
********************************* DFCPKT *******************************;
AOBJN!PTR _ DFCPKT (@BLOCK, EVTYP,
EVNOT, OPTS)
This routine is a convenience for causing an event as a deferred interrupt.
If BLOCK is non-zero then it should be an array with at least 5 elements; if
BLOCK is zero then a five-word block is allocated. DFCPKT constructs a call
for CAUSE (EVTYP, EVNOT, OPTS) in this block and returns an AOBJN pointer to
it.
BASIC CONSTRUCTS Syntax
SECTION 21
BASIC CONSTRUCTS
21.1 Syntax
<variable>
::= <identifier>
::= <identifier> [ <subscript!list> ]
::= DATUM ( <typed!item!expression> )
::= DATUM ( <typed!item!expression> ) [
<subscript!list> ]
::= PROPS ( <item!expression> )
::= <context!element>
::= <record!class> : <field> [
<record!pointer!expression> ]
<typed!item!expression>
::= <typed!itemvar>
::= <typed!item>
::= <typed!itemvar!procedure>
::= <typed!item!procedure>
::= <typed!itemvar!array> [
<subscript!list> ]
::= <typed!item!array> [
<subscript!list> ]
::= <itemvar> _ <typed!item!expression>
::= IF <boolean!expression> THEN
<typed!item!expression> ELSE
<typed!item!expression>
::= CASE <algebraic!expression> OF (
<typed!item!expression!list> )
<typed!item!expression!list>
::= <typed!item!expression>
::= <typed!item!expression!list> ,
<type!item!expression>
<subscript!list>
::= <algebraic!expression>
::= <subscript!list> ,
<algebraic!expression>
Semantics VARIABLES DATUMS PROPS
21.2 Semantics
VARIABLES
If a variable is simply an identifier, it represents a single value of the
type given in its declaration.
If it is an identifier qualified by a subscript list it represents an
element from the array bearing the name of the identifier. However, an
identifier qualified by a subscript list containing only a single subscript
may be either an element from a one dimensional array, or an element of a
list. Note that the token "" may be used in the subscript expression of a
list to stand for the length of the list, e.g. LISTVAR[-2]_LISTVAR[-1].
The array should contain as many dimensions as there are elements in the
subscript list. A[I] represents the I+1th element of the vector A (if the
vector has a lower bound of 0). B[I, J] is the element from the I+1th row
and J+1th column of the two-dimensional array B. To explain the indexing
scheme precisely, all arrays behave as if each dimension had its origin at
0, with (integral) indices extending infinitely far in either direction.
However, only the part of an array between (and including) the lower and
upper bounds given in the declaration are available for use (and in fact,
these are the only parts allocated). If the array is not declared SAFE,
each subscript is tested against the bounds for its dimension. If it is
outside its range, a fatal message is printed identifying the array and
subscript position at fault. SAFE arrays are not bounds-checked. Users
must take the consequences of the journeys of errant subscripts for SAFE
arrays. The bounds checking causes at least three extra machine
instructions (two of which are always executed for valid subscripts) to be
added for each subscript in each array reference. The algebraic expressions
for lower and upper bounds in array declarations, and for subscripts in
subscripted variables, are always converted to Integer values (see page 46)
before use.
For more information about the implementation of Sail arrays, see page 281.
DATUMS
DATUM (X) where X is a typed item expression, will act exactly like a
variable with the type of the item expression. The programmer is
responsible for seeing that the type of the item is that which the DATUM
construct thinks it is. For example, the Datum of a Real Itemvar will
always interpret the contents of the Datum location as a floating point
number even if the program has assigned a string item to the Real Itemvar.
PROPS
The PROPS of an item will always act as an integer variable. Any algebraic
value assigned to a props will be coerced to an integer (see about type
conversions, page 46) then the low order 12 bits will be stored in the props
of the item. Thus, the value returned from a props will always be a non-
negative integer less than '7777 (4095 in decimal).
RECORD FIELDS IDENTIFIERS
RECORD FIELDS
A field in a record is also a variable. The variable is allocated and
deallocated with the other fields of the same record as the result of calls
to NEW!RECORD and the record garbage collector. For more information see
page 119.
IDENTIFIERS
You will notice that no syntax was included for the non-terminal symbols
<identifier> or <constant>. It is far easier to explain these constructs in
an informal manner.
A Sail letter is any of the upper or lower case letters A through Z, or the
underline character (! or !, they are treated equivalently). Lower case
letters are mapped into the corresponding upper case letters for purposes of
symbol table comparisons (SCHLUFF is the same symbol as Schluff). A digit
is any of the characters 0 through 9.
An identifier is a string of characters consisting of a letter followed by
virtually any number of letters and digits There must be a character which
is neither a letter nor a digit (nor either of the characters "." or "$")
both before and after every identifier. In other words, if YOU can't
determine where one identifier ends and another begins in a program you have
never seen before, well, neither can Sail.
There is a set of identifiers which are used as Sail delimiters (in the
Algol sense -- that is, BEGIN is treated by Algol as if it were a single
character; such an approach is not practical, so a reserved identifier is
used). These identifiers are called Reserved Words and may not be used for
any purpose other than those given explicitly in the syntax, or in
declarations (DEFINES) which mask their reserved-word status over the scope
of the declarations. E.g., "INTEGER BEGIN" is allowed, but a Synonym (see
page 22) should have been provided for BEGIN if any new blocks are desired
within this one, because BEGIN is ONLY an Integer in this block. Another
set of identifiers have preset declarations -- these are the execution time
functions. These latter identifiers may also be redefined by the user; they
behave as if they were declared in a block surrounding the outer block. A
list of reserved words and predeclared identifiers may be found in the
appendices. It should be noted that due to the stupidity of the parser, it
is impossible to declare certain reserved words to be identifiers. For
example, INTEGER REAL; will give one the syntax error "Bogus token in
declaration".
Some of the reserved words are equivalent to certain special characters
(e.g. "|" for "SUCH THAT"). A table of these equivalences may be found in
the appendices.
ARITHMETIC CONSTANTS STRING CONSTANTS COMMENTS
ARITHMETIC CONSTANTS
12369 Integer with decimal value 12369
'12357 Integer with octal value 12357
123. Real with floating point value 123.0
0123.0 Real with floating point value 123.0
.524 Real with floating point value 0.524
5.3@2 Real with floating point value 530.0
5.342@-3 Real with floating point value 0.005342
The character ' (right quote) precedes a string of digits to be converted
into an OCTAL number.
If a . or a @ appears in a numeric constant, the type of the constant is
returned as Real (even if it has an integral value). Otherwise it is an
integer. Type conversions are made at compile time to make the type of a
constant commensurate with that required by a given operation. Expressions
involving only constants are evaluated by the compiler and the resultant
values are substituted for the expressions.
The reserved word TRUE is equivalent to the Integer (Boolean) constant -1;
FALSE is equivalent to the constant 0.
STRING CONSTANTS
A String constant is a string of ASCII characters (any which you can get
into a text file) delimited at each end by the character ". If the "
character is desired in the string, insert two " characters (after the
initial delimiting " character, of course).
A String constant behaves like any other (algebraic) primary. It is
originally of type String, but may be converted to Integer by extracting the
first character if necessary (see page 46).
The reserved word NULL represents a String constant containing no characters
(length=0).
Examples: The left hand column in the table that follows gives the required
input
INPUT RESULT LENGTH
"A STRING" A STRING 8
"WHAT'S ""DOK"" MEAN?" WHAT'S "DOK" MEAN? 18
"""A QUOTED STRING""" "A QUOTED STRING" 17
"" 0
NULL 0
COMMENTS
If the scanner detects the identifier COMMENT, all characters up to and
including the next semicolon (;) will be ignored. A comment may appear
anywhere as long as the word COMMENT is properly delimited (not in a String
constant, of course);
A string constant appearing just before a statement also has the effect of a
comment.
USING SAIL For TOPS-10 Beginners
SECTION 22
USING SAIL
22.1 For TOPS-10 Beginners
If you simply want your Sail program compiled, loaded, and executed, do the
following:
1. Create a file called "XXXXXX.SAI" with your program in it, where
"XXXXXX" may be any name you wish.
2. Get your job to monitor level and type "EXECUTE XXXXXX".
3. The system program (variously called SNAIL, COMPILE, RPG) which
handles requests like EXECUTE will then start Sail. Sail will say
"Sail: XXXXXX". When Sail hits a page boundary in your file, it
will type "1" or whatever the number of the page that it is
starting to read.
4. When the compilation is complete Sail swaps to the loader, which
will say "LOADING".
5. When the loading is complete the loader will type "LOADER nP CORE"
where n is your core size. The loader then says "EXECUTION".
6. When execution is complete Sail will type "End of Sail execution"
and exit.
At any time during 3 through 6 above, you could get an error message from
Sail of the form "DRYROT: <cryptic text>", or from the system, such as "ILL
MEM REF", "ILLEGAL UUO" etc. followed by some core locations. These are
Sail bugs. You will have to see a Sail hacker about them, or attempt to
avoid them by rewriting the offending part of your program, or try again
tomorrow.
If you misspell the name of your file then SNAIL will complain "File not
found: YYYYYY" where "YYYYYY" is your misspelling. Otherwise, the error
messages you receive during 3 above will be compilation errors (bad syntax,
type mismatch, begin-end mismatch, unknown identifiers, etc.). See page 243
about these.
If you get through compilation (step 3) with no error messages, the loading
of your program will rarely fail. If it somehow does, it will tell you.
See a Sail hacker about these.
If you also get through loading (step 4) with no errors, you aren't yet
For TENEX Beginners The Complete use of Sail
safe. Sail will give you error messages during the execution of your
program if you exceed the bounds of an array, refer to a field of a null
record, etc. See section 1 about these too.
If you never get an error message, and yet you don't get the results you
thought you'd get, then you've probably made some mistakes in your
programming. Use BAIL (or RAID or DDT) and section 2 to aid in
debugging. It is quite rare for Sail to have compiled runable but incorrect
code from a correct program. The only way to ascertain whether this is the
case is to isolate the section of your program that is causing Sail to
generate the bad code, and then patiently step through it instruction by
instruction using RAID or DDT, and check to see that everything it does
makes sense.
22.2 For TENEX Beginners
If you simply want your Sail program compiled, loaded, and executed, do the
following.
1. Create a file called "XXXXXX.SAI" with your program in it, where
XXXXXX may be any name you wish.
2. Type "Sail", followed by a carriage return, to the TENEX EXEC.
3. The EXEC will load and start Sail. Sail will say "Tenex Sail 8.1
8-5-76 *". Type "XXXXXX<cr>" (your file name). Sail will create a
file XXXXXX.REL, and will type the page number of the source file
as it begins to compile each page.
4. When Sail finishes it will type "End of compilation.". Return to
the EXEC and type "LOADER<cr>". The loader will type "*". Type
"SYS:LOWTSA,DSK:XXXXXX$", where $ is the altmode key. This loads
your program into core.
5. When the LOADER exits, the program is loaded. You may now either
SAVE the program, for later use, or run it with the EXEC START command.
22.3 The Complete use of Sail
The general sequence of events in using Sail is:
Compiling Sail Programs COMMAND LINE SYNTAX TOPS-10 COMMAND LINE SYNTAX
1. Start Sail.
2. Compile one or more files into one or more binary files, with
possibly a listing file generated.
3. Load the binary file(s) with the appropriate upper segment or with
the Sail runtime library, and possibly with RAID or DDT.
4. Start the program, possibly under the control of BAIL, RAID or DDT.
5. Let the program finish, or stop it to use a debugger or to
reallocate storage with the REENTER command.
Starting Sail is automatic with the SNAIL commands described below.
Otherwise, "R SAIL" will do.
22.4 Compiling Sail Programs
When started explicitly by monitor command, Sail will type back an "*" at
you and wait for you to type in a <command line>. It will do the
compilation specified by that command line, then ask for another, etc.
If you use SNAIL then follow the SNAIL command with a list of <command
line>s separated by commas. The compilation of each <command line> will be
done before the next <command line> is read and processed. The SNAIL
commands are:
EXecute compile, load, start
TRY compile, load with BAIL, start
DEBug compile, load with BAIL,
start BAIL
LOAd compile, load
PREPare compile, load with BAIL
COMpile compile
See [MonCom] for more information about the use of SNAIL and the switches
available to it.
COMMAND LINE SYNTAX
TOPS-10 COMMAND LINE SYNTAX
<command!line>
::= <binary!name> <listing!name> _
<source!list>
::= <file!spec> @
::= <file!spec> !
<binary!name>
::= <file!spec>
::= <empty>
<listing!name>
::= , <file!spec>
::= <empty>
<source!list>
::= <file!spec>
::= <source!list> , <file!spec>
<file!spec>
::= <file!name> <file!ext> <proj!prog>
::= <device!name> <file!spec> <switches>
::= <device!name> <switches>
<file!name>
::= <legal!sixbit!id>
<file!ext>
::= . <legal!sixbit!id>
::= <empty>
<proj!prog>
::= [ <legal!sixbit!id> ,
<legal!sixbit!id> ]
::= <empty>
<device!name>
::= <legal!sixbit!id>
<switches>
::= ( <unslashed!switch!list> )
::= <slashed!switch!list>
::= <empty>
<unslashed!switch!list>
::= <switch!spec>
::= <unslashed!switch!list> <switch!spec>
TENEX SAIL COMMAND LINE SYNTAX
<slashed!switch!list>
::= / <switch!spec>
::= <slashed!switch!list> / <switch!spec>
<switch!spec>
::= <valid!switch!name>
::= <signed!integer> <valid!switch!name>
<valid!switch!name>
::= A
::= B
::= C
::= D
::= F
::= H
::= K
::= L
::= P
::= Q
::= R
::= S
::= V
::= W
::= X
TENEX SAIL COMMAND LINE SYNTAX
<command!line>
::= <file!list> CR
::= <file!list> , CR
::= <file!list> _
::= <file!list> , _
::= _ <file!list>
::= ?
<file!list>
::= <file> , <file!list>
<subcommand>
::= CR
::= <control-R>
::= <control-L>
::= / <switch>
::= ?
COMMAND LINE SEMANTICS
<switch>
::= <number> <switch>
::= <TOPS-10 switch>
::= G
::= I
::= T
COMMAND LINE SEMANTICS
All this is by way of saying that Sail accepts commands in essentially the
same format accepted by other processors written for the operating system on
which you are running. The binary file name is the name of the output
device and file on which the ready to load object program will be written.
The listing file, if included, will contain a copy of the source files with
a header at the top of each page and an octal program counter entry at the
head of each line (see page 237). The listing file name is often omitted
(no listing created). The source file list specifies a set of user-prepared
files which, when concatenated, form a valid Sail program (one outer block).
If file!ext is omitted from the binary!name then the extension for the
output file will be .REL. The default extension for the listing file is
.LST. Sail will first try to find source files under the names given. If
this fails, and the extension is omitted, the same file with a .SAI
extension will be tried.
If device!name is omitted then DSK: is assumed. If proj!prog is omitted,
the project-programmer number for the job is assumed.
Switches are parameters which affect the operation of the compiler. A list
of switches may appear after any file name on TOPS-10; use subcommand mode
on TENEX. The parameters specified are changed immediately after the file
name associated with them is processed. The meanings of the switches are
given below.
The binary, listing and (first) source file names are processed before
compilation -- subsequent source names (and their switches) are processed
whenever an end-of-file condition is detected in the current source file.
Source files which appear after the one containing the outer block's END
delimiter are not ignored, but should contain only comments.
Each new line in the command file (or entered from the teletype) specifies a
separate program compilation. Any number of programs can be compiled by the
same Sail core image.
The file!spec@ command causes the compiler to open the specified file as the
command file. Subsequent commands will come from this file. If any of
these commands is file!spec@, another switch will occur.
The file!spec ! command will cause the specified file to be run as the next
processor. This program will be started in "RPG mode". That is, it will
look on the disk for its commmands if its standard command file is there --
SWITCHES
otherwise, command control will revert to the TTY. The default option for
this file name is .DMP. The default device is SYS.
TENEX Sail command syntax is much like the syntax of the TENEX DIRECTORY
command. Filenames are obtained from the terminal using recognition; .SAI,
.REL, and .LST are the default extensions. Command lines ending in comma or
comma backarrow enter subcommand mode. Command lines ending in backarrow
cause termination of command scanning and start compilation; the program
will be loaded with DDT and DDT will be started. A file name appearing
before a backarrow is taken as a source file; the .REL file will have the
same (first) filename. A command line beginning with backarrow causes no
.REL file to be generated. In subcommand mode the characters control-R and
control-L allow complete specification of the binary and listing file names,
respectively.
SWITCHES
The following table describes the Sail parameter switches. If the switch
letter is preceded in the table by the D character, a decimal number is
expected as an argument. 0 is the default value. The character O indicates
that an octal number is expected for this switch. Otherwise the argument is
ignored.
ARG SWITCH FUNCTION
O A The octal number O specifies bits which determine the code compiled
in certain cases.
1 use KIFIX for real to integer conversion
2 use FIXR
;otherwise use UUOFIX
4 use FLTR for integer to real conversion
;otherwise use UUOFLOAT
10 use ADJSP whenever possible
;otherwise use SUB, or ADD with
PDLOV detection
20 use FORTRAN-10 calling sequence for calling
Fortran Procedures; else old F40 style
The compiler is initialized with /0A; the compiled code will run on
a KA10 using F40 calling sequence for Fortran Procedures.
O B The octal number O specifies bits which determine how much
information is produced for BAIL.
1 Program counter to source/listing directory.
2 Include information on all symbols. If not
selected then do not include non-internal
local variables.
4 SIMPLE procedures get proc. descriptors.
10 Don't automatically load SYS:BAIL.REL.
20 Make the Sail predeclared runtimes
known by requiring SYS:BAIPDn.REL.
C This switch turns on CREFfing. The listing file (which must exist)
will be in a format suitable for processing by CREF, the program
which will generate a cross-reference listing of your Sail program
from your listing files.
D D If the decimal number D is zero or does not appear then double the
amount of space for the push down stack used in expanding macros
(see page 106). If D is not zero then set the stack size to D. Use
this switch if the compiler indicates to you that this stack has
overflowed. This shouldn't happen unless you nest DEFINE calls
extremely deeply.
O F O is an octal number which specifies exactly what kind of listing
format is generated. O contains information about 7 separate
listing features, each of which is assigned a bit in O.
1 List the program counter (see / L switch ).
2 List with line numbers from the source text.
4 List the macro names before expansion.
10 Expand macro texts in the listing file.
20 Surround each listed macro expansion
with < and > .
40 Suspend listing.
100 No banner at the top of each page.
[This is a way to "permanently" expand
macros. A /110F listing is (almost)
suitable as a Sail source file.]
The compiler is initialized with /7f (i.e., list program counter,
line numbers, and macro names).
G (TENEX only) Load after compilation, exiting to the monitor.
H (Default on TENEX) This switch is used to make your program
sharable. When loaded, the code and constants will be placed in the
second (write-protected) segment, while data areas will be allocated
in the lower, non-shared segment. Programs compiled with /H request
SYS:HLBSAn as a library (<SAIL>HLBSAn on TENEX). The sharable
library HLBSAn is identical to LIBSAn, except that it expects to run
mostly in the upper (shared) segment. Recall that n is the current
version number. At SUAI, use the monitor command SETUWP to write
protect the upper segment. Then SSAVE the core image.
I (TENEX only) Do not compile two-segment code.
K The counter mechanism of Sail is activated, enabling one to
determine the frequency of execution of each statement in your Sail
program. See Appendix F, the Statement Counter System. This switch
is ignored unless a listing is specified with a /LIST.
O L In compiling a Sail program, an internal variable called PCNT (for
program counter) is incremented (by one) for each word of code
generated. This value, initially 0, represents the address of a
word of code in the running program, relative to the load point for
this program. The current octal value of PCNT plus the value of
another internal variable called LSTOFFSET, is printed at the
beginning of each output line in a listing file. For the first
program compiled by a given Sail core image, LSTOFFSET is initially
0. If the L switch occurs in the command and the value O is non-
negative, O replaces the current value of LSTOFFSET. If O is -1,
the current size of DDT is put into LSTOFFSET. If O is -2, the
current size of RAID is used. In "RPG mode" the final value of PCNT
is added to LSTOFFSET after each compilation. Thus by deleting all
.REL files produced by Sail, and by compiling all Sail programs
which are to be loaded together with one RPG command which includes
the L switch, you can obtain listing files such that each of these
octal numbers represents the actual starting core address of the
code produced by the line it precedes. At the time of this writing,
SNAIL would not accept minus signs in switches to be sent to
processors. Keep trying.
D P Set the size of the system pushdown list to D (decimal). If D is
zero or does not appear then double the (current) size of the list.
Thus /35P/P will first set the stack size to 35, then double it to
70. It has never been known to overflow.
D Q Set the size of the string pushdown list to D (decimal). If D is
zero or does not appear then double the size of the list. No
trouble has been encountered here, either.
D R Set the size of the compiler's parsing and semantic stacks to D
(decimal). If D is zero or does not appear then double the size of
the stacks. A long conditional statement of the form (IF ... THEN
... ELSE IF ... THEN ... ELSE IF ... ) has been known to cause
these stacks to overflow their normally allocated sizes.
D S The size of String space is Set to D words. String space usage is a
function of the number of identifiers, especially macros, declared
by the user. In the rare case of String space exhaustion, 5000 is a
good first number to try.
T (TENEX only) Load with DDT, exit to DDT.
V Always put loader link blocks and the characters for constant
strings into the low segment, even if /H is selected. This is
intended for use in overlay systems where code is overlaid but data
is not.
W Generate additional suppressed DDT symbols. These symbols are
designed to serve as comments to a programmer or processor rummaging
though the generated code. Symbols generated by this switch all
begin with a percent sign (%), and many come in pairs. A %$ symbol
points to the first word of an area and a %. symbol points to the
first word beyond the area. Thus the length of an area is the
difference of its %. and %$ symbols. The symbols are:
%$ADCN %.ADCN address constants
%$LIT %.LIT literals
%$RLIT %.RLIT reference literals
%$SCOD %.SCOD START(or QUICK)!CODE
%$STRC %$STRC string variables
%$VARS %.VARS simple variables
%ALSTO start to clear registers
%$ARRY first data word of a fixed array
%$FORE FOREACH satisfier block
%$SUCC SUCCEED/FAIL return block
/W tends to increase the number of DDT symbols by a factor of 2 or 3.
X Enable compiler save/continue (page 285).
Here is an example of a compile string which a user who just has to try
every bell and whistle available to him might type to compile a file named
NULL:
COMPILE /LIST /SAIL NULL(RR-2L5000S)
The switch information contained in parentheses will be sent
unchanged to Sail. Note the convention which allows one set of
parentheses enclosing a myriad of switches to replace a "/" character
inserted before each one. This string tells the compiler to compile NULL
using parse and semantic stacks four times larger than usual (RR). A
listing file is to be made which assumes that RAID will be loaded and NULL
will be loaded right after RAID (-2L). His program is big enough to
need 5000 words of String space (5000S). The statement REQUIRE "chars"
COMPILER!SWITCHES; can be used to change the settings of the compiler
switches. "chars" must be a string constant which is a legitimate switch
string, containing none of the characters "(/)"; e.g.,
REQUIRE "20F" COMPILER!SWITCHES;
Loading Sail Programs Starting Sail Programs STARTING THE PROGRAM IN RPG MODE
The string of characters is merely passed to the switch processor, and it
may be possible to cause all sorts of problems depending on the switches you
try to modify. Switches A, B, and F are the only ones usually modified.
The switches which set stack sizes (D, P, Q, R) or string space (S) should
be avoided. Switches which control the format of files (B, F) should only
be used if you have such a file open.
22.5 Loading Sail Programs
Load the main program, any separately compiled procedure files (see page
25), any assembly language (see page 27) or Fortran procedures, and DDT or
RAID if desired. This is all automatic if you use the LOAD or DEBUG or
EXECUTE system commands (see [MonCom]). Any of the Sail execution time
routines requested by your program will be searched out and loaded
automatically from SYS:LIBSAn.REL (<SAIL>LIBSAn on TENEX). If the shared
segment is available and desired, type SYS:SAILOW (SYS:LOWTSA for TENEX) as
as your very first LOADER command (before /D even). SUAI people can
abbreviate SYS:SAILOW as /Y. All this is done automatically by SNAIL at
SUAI. Other loaders (e.g., LINK10) can also be used.
22.6 Starting Sail Programs
For most applications, Sail programs can by started using the START, RUN,
EXECUTE, or TRY system commands, or by using the $G command of DDT (RAID).
The Sail storage areas will be initialized. This means that all knowledge
of I/O activity, associative data structures, strings, etc. from any
previous activation of the program will be lost. All strings (except
constants) will be cleared to NULL. All compiled-in arrays will not be
reinitialized (PRELOADed arrays are preloaded at compile time - OWN arrays
are never initialized). Then execution will begin with the first statement
in the outer block of your main program. As each block is entered, its
arrays will be cleared as they are allocated. Variables are not cleared.
The program will exit when it leaves this outer block.
MODESTARTING THE PROGRAM IN "RPG" MODE
Sail programs may be started at one of two consecutive locations: at the
address contained in the cell JOBSA in the job data area, or at the address
just following that one. The global variable RPGSW is set to 0 in the
former case, -1 in the latter. Aside from this, there is no difference
between the two methods. This cell may be examined by declaring RPGSW as an
EXTERNAL INTEGER.
Storage Reallocation with REEnter
22.7 Storage Reallocation with REEnter
The compiler dynamically allocates working storage for its push down lists,
symbol tables, string spaces, etc. It normally runs with a standard
allocation adequate for most programs. Switch settings given above may be
used to change these allocations. If desired, these allocations may also be
changed by typing ^C, followed by REE (reenter). The compiler will ask you
if you want to allocate. Type Y to allocate, N to use the standard
allocation, and any other character to use the standard allocations and
print out what they are. All entries will be prompted. Numbers should be
decimal. Typing alt-mode instead of CR will cause standard allocation to be
used for the remaining values. The compiler will then start, awaiting
command input from the teletype.
For SUAI "Global Model" users, the REE command will also delete any REQUIREd
or previously typed segment name information. The initialization sequence
will then ask for new names.
DEBUGGING SAIL PROGRAMS Error Messages
SECTION 23
DEBUGGING SAIL PROGRAMS
23.1 Error Messages
If the compiler detects a syntax or semantic error while compiling a program
it will provide the user with the following information:
1) The error message. These are English phrases or sentences which
attempt to diagnose the problem. If a message is vague it is
because no specific test for the error has been made and a catchall
routine detected it. If the message begins with the word "DRYROT"
it means that there is a bug in the compiler which some strangeness
in your program was able to tickle. See a system programmer about
this.
2) The current input line. Page and line number, along with the text
of the line being scanned, are typed. A line feed will occur at
the point in the line just following the last program element
scanned. The absence of a position indicator means that a macro
(DEFINE) body is being expanded.
3) A question mark (?) or arrow (^).
Respond to the prompt in any of the following ways:
<cr> Try to continue compilation. A message will be printed and
the sequence reentered if recovery is impossible (if a "?" was
typed instead of an arrow).
<lf> Try to continue the compilation, but don't stop for user
response after future errors. I.e., automatic continuation.
Messages will fly by (at an unreadable rate on DPYs) until the
compilation is complete or an error occurs from which no recovery
is possible. In the latter case the question sequence is
reentered.
A same as <lf>
B Enter BAIL if it is loaded.
C same as <cr>
D Enter DDT or RAID if one is loaded. Otherwise, type "No DDT" and
re-question. Do not type D if you really mean B.
ERROR MODES
E Edit. This command must be followed by a carriage return, or a
space, a filename (in standard format, assumes DSK) and a carriage
return. If the filename is missing, the SOS editor (see
[Savitzky]) is started, given instructions to edit the current
source file and to move the editing pointer to the current page and
line number. If a file name is present, that file is edited
starting at the beginning. This feature is available outside SUAI
only if the SOS editor is available, and is modified to read a
standard CCL file for its input. If you change your mind and do
not wish to edit, typing an altmode will get you back to the
question loop.
S Restart. Sometimes useful if you are debugging the compiler (or if
you were compiling the wrong file). The program is restarted,
accepting compilation commands from the TTY.
T TV edit. Same as E except that E is used at SUAI, TVEDIT at IMSSS
and SUMEX.
X Exit. All files are closed in their current state. The program
exits to the system.
Any other character will cause the error routines to spew forth a summary of
this table and re-enter the question sequence.
ERROR MODES
For errors which occur during compilation, the above procedure can be
modified slightly by setting various modes. One sets a mode by including
the appropriate letter before the response. Any of the four modes may be
reset by including a minus sign (-) before them. E.g. "-Q". Error modes can
also be set with REQUIRE <string!const> ERROR!MODES. When the compiler sees
this it reads through the string constant and sets the modes as it sees
their letters. These modes remain in effect until the end of the
compilation or until reset with a response to an error message, or another
require error!modes.
The available modes are:
K KEEP type-ahead. The error handler flushes all typeahead except a
LF (linefeed). If KEEP mode is ever implemented then the input
buffer will not be flushed.
L LOGGING. The first and second items of the error message will be
sent to a file named <prognam>.LOG where <prognam> is the name of
the file of the main program. If you would rather have another
name, use F<file specification>, where <file specification> must
be a legal file name and PPN. The default extension is .LOG and
the default PPN is that of the job. The .LOG file (or whatever
it's called) is closed when one's program finishes compilation, or
the compilation is terminated with the S, X, E, or T responses.
STOPPING RUNAWAY COMPILATIONS EXECUTION TIME ERROR MESSAGES USER ERROR PROCEDURES
N NUMBERS. This mode causes the message "Called from xxxx Last SAIL
call at yyyy" to be typed before the question mark or arrow.
Useful to compiler debuggers and hand coders.
Q QUIET. If the error is continuable, none of the above will be
typed. However, you will always be notified of a non-continuable
error.
Note that setting a mode does nothing but set a mode; it does not cause
continuation.
STOPPING RUNAWAY COMPILATIONS
Typing [ESC] I at SUAI or control-H on TENEX will immediately cause the Q
and A modes to be reset so that the next error will (a) be typed, and (b)
wait for a response rather than continuing automatically.
EXECUTION TIME ERROR MESSAGES
Error messages have nearly the same format as those from the compiler (page
243). They indicate that
1) an array subscript has overflowed;
2) a case index is out of range;
3) a stack has overflowed while allocating space for a recursive
procedure; or
4) one of the execution time routines has detected an error.
In Numbers mode, the "Called from" address identifies, in the first 3 cases,
the location in the user program where the error occurred ; the "Last SAIL
call at" address gives the location of the faulty call on the Sail routine
for type 4 messages.
All the replies to error messages described in page 243 are valid. If no
file name is typed with the "E" or "T" option, the editor re-opens the last
file mentioned in the EDIT system command.
The function USERERR may be used to activate the Sail error message
mechanism. Facilities are provided for changing the mode. See page 91 for
details.
USER ERROR PROCEDURES
A user error procedure is a user procedure that is run before or
instead of the Sail error handler every time an error occurs at
runtime. This includes all array errors, IO errors, Leapish errors and
all USERERRs. It does not include system errors, such as Ill Mem Ref or Ill
UUO.
The procedure one uses for a user error procedure must be of the
following type:
Debugging
SIMPLE INTEGER PROCEDURE proc
(INTEGER loc; STRING msg, rsp);
Only the names proc, loc, msg, and rsp may vary from the example above,
except that one may declare the procedure INTERNAL if one wishes to use
it across files.
Whenever the external integer !ERRP! is loaded with LOCATION (proc), the
error handler will call proc before it does anything else. It will set
loc to the core location of the call to the error handler. Msg will be
the message that it would have printed. Rsp will be non-NULL only if the
error was from a USERERR which had response string argument. Proc can do
anything that a simple procedure can do. When it exits, it should return
an integer which tells the error handler if it should do anything more.
If the integer is 0, the error handler will (1) print the message, (2)
print the location, and (3) query the tty and dispatch on the response
character (i.e., ask for a <cr>, <lf>, etc.). If the right half of the
integer is non-zero, it is taken as the ascii for a character to dispatch
upon. The left half may have two bits to control printing. If bit 17 in
the integer is on, message printing is inhibited. If bit 16 is on, then the
location printing is inhibited. For example, "X"+(1 LSH 18) will cause the
location to be printed and the program exited. "C"+(3 LSH 18) will cause the
error handler to continue without printing anything.
Note that simple procedures can not do a non-local GOTO. However, the
effect of a non-local GOTO can be achieved in a user error procedure
by loading the external integer !ERRJ! with the LOCATION of a label. The
label should be a on a call to a non-simple procedure which does the desired
GOTO. The error handler clears !ERRJ! before calling the procedure in
!ERRP!. If !ERRJ! is non-zero when the user procedure returns, and
continuing was specified, then the error handler's exit consists of a
simple transfer to that location. Note that for this simple transfer to
work properly, the place where the error occurred (or the call to USERERR)
must be in the same static (lexical) scope as the label whose LOCATION is in
!ERRJ!. If this is really important to you, see a Sail hacker.
WARNING! Handling errors from strange places like the string garbage
collector and the core management routines will get you into deep trouble.
23.2 Debugging
Sail has a high-level debugger called BAIL; see the description beginning in
the next subsection. This subsection gives necessary information for those
who wish to use DDT or RAID. The code output for Sail programs is designed
to be fairly easy to understand when examined using the DDT debugging
language or SUAI's display oriented RAID program. A knowledge of the
debugger you have chosen is required before this section will be
comprehensible.
SYMBOLS BLOCKS SAIL GENERATED SYMBOLS
SYMBOLS
Only those symbols which have been declared INTERNAL (see page 25) and those
declared in the currently open "program" are available at a given time. The
name of a Sail program as far as DDT or RAID (henceforth DDRAID) is
concerned is the name of the outer block of that program. If no name is
given for this block, the name M. will be the default.
Only the first six non-blank characters of a block name or identifier will
be used in forming a DDRAID symbol. If two identifiers in the same block
have the same first six characters the program using them will not get
confused, but the user might when trying to locate these identifiers.
BLOCKS
All block names and identifiers used as variables, procedures or labels in a
given (main or separate procedure) program are available for typeout when
that program is "open" (NAME$: has been typed). To refer to a symbol, type
BLOCK!NAME&SYMBOL/ (substitute ; for / in RAID). The block name may be
omitted if you have "opened" the block with BLOCK!NAME$&. The symbol table
is block-structured only to the extent that block names have appeared in the
source program. For instance, in the program
BEGIN "NAME1"
INTEGER I, J;
...
BEGIN
INTEGER I, K;
...
END;
...
END "NAME1"
the symbols J, K, and both symbols I are considered by DDRAID to belong in
the same block. Therefore confusion can result with respect to I. This
approach was taken to avoid the necessity of generating meaningless block
names for DDRAID when none were given in the source program. A compound
statement will be considered by DDRAID to be a block if it has a name.
SAIL GENERATED SYMBOLS
Some extra symbols are generated by Sail and will show up when you are using
DDRAID. They are:
ACS The accumulators P (system push down list pointer), and SP
(string push down pointer) are given symbolic names. Currently
P='17, SP='16.
OPS The op codes for the UUOs FIX, FLOAT, and ARERR (subscript
overflow UUO) are included to make these easy to detect in the
code.
ARRAYS For each array declared in the outer block (built-in arrays),
WARNINGS
the fixed address of its first element is given a symbolic name.
This name is constructed from the characters of the array name
(up to the first 5) followed by a period. For instance, the
first element of array CHT is CHT.; the first element of PDQARR
is PDQAR.; The last semicolon was really a period. This dotted
symbol points to the second word of the first descriptor for
String Arrays (see page 283, page 281).
STRINGS For each string declared in the outer block (built-in
strings), the second word of the two word string descriptor is
given the name of the string variable, truncated to six letters.
The first word of the string descriptor is given a name
consisting of the first five letters of the string's name
followed by a period. For example, if you declare a string
INSTRING, then the two word descriptor:
INSTR. : <first word>
INSTRI : <second word>
More about string descriptors on page 283.
BLOCKS The first word of the first executable statement of every
block or compound statement which has been given a name is given
a label created in the same way as those for arrays above. This
label cannot be gone to in the source program. It causes no
program inefficiency. This label points at the first word of the
compound tail -- not the first word of code generated for the
block (skips any procedure or array declaration code).
STARTThe first word of code generated for any given program is given
the name "S.".
PROCEDURES The word at entry address -1 of an INTERNAL procedure
contains the address of the procedure descriptor. (This enables
APPLY of an EXTERNAL procedure to work.) The first word of the
procedure descriptor is given a name consisting of the first 5
characters of the procedure name, followed by a dollar sign ($).
WARNINGS
Since only the first 6 characters of an identifier are available, it is wise
to declare symbols which will be examined by DDRAID in such a way that
these six characters will uniquely identify them.
BAIL
23.3 BAIL
BAIL [Reiser] is a high-level breakpoint package for use with Sail
programs. Communication between the programmer and BAIL is in character
strings which are the names and values of Sail objects. BAIL reads
general Sail expressions typed by the programmer, evaluates them in the
context of the place in the program where execution was suspended, and
prints the resulting value in an appropriate format. The evaluation and
printing are performed just as if the programmer had inserted an extra
statement into the original program at the point where execution was
suspended. BAIL also provides a way to talk about the program, to
answer the questions "Where was execution suspended?", "By what
chain of procedure calls did execution proceed to that point?", and "What
is the text of the program?"
In order to perform these functions, BAIL must have some information about
the program being debugged. The Sail compiler will produce this
information on a file with extension .SM1 if the program is compiled with an
appropriate value supplied for the /B switch. The .SM1 information consists
of the name, type, and accessing information for each variable
and procedure, the location of the beginning and end of each statement,
and a description of the block structure.
The code for BAIL itself is loaded automatically when the program is
loaded. In order for the added information and code to be of any use,
it must be possible to give control to BAIL at the appropriate time. An
explicit call to BAIL is possible by declaring EXTERNAL PROCEDURE BAIL;
in the program and using the procedure call BAIL;. This works well if it
can be predicted in advance where BAILing might be helpful. Runtime errors,
such as subscript overflow or CASE index errors, are not as predictable; but
responding "B" to the Sail error handler will activate BAIL.
Interrupting the program while it is running (to investigate a possible
infinite loop, for example) can be achieved under the TENEX operating system
by typing control-B. On a DEC TOPS-10 operating system, first return to
monitor mode by typing one or more control-C's, then activate BAIL by typing
DD<cr>.
BAIL performs some initialization the first time it is entered. The
information in the .SM1 file(s) is collected and processed into a .BAI
file. This new file reflects all of the information from the .SM1
files of any separately-compiled programs, and the relocation
performed by the loader. If the core image was SAVEd or SSAVEd then in
subsequent runs BAIL will use the .BAI file and bypass much of the
initialization.
BAIL prompts the programmer for input by typing a number and a colon. The
number indicates how many times BAIL has been entered but not yet exited,
and thus is the recursion depth inside BAIL. Input to BAIL can be edited
using the standard Sail input-editing characters for the particular
operating system under which the program is running. [BAIL requests input
COMPILE-TIME ACTION
via INCHWL on DEC TOPS-10 systems and via INTTY on TENEX systems.] Input is
terminated whenever the editor activates, string quotation marks balance,
and the last character is a semicolon; otherwise input lines are
concatenated into one string before being processed further.
The programmer may ask BAIL to evaluate any Sail expression or
procedure call whose evaluation would be legal at the point at which
execution of the program being debugged was suspended (except that
expressions involving AND, OR, IF-THEN-ELSE, and CASE are not
allowed.) BAIL evaluates the expression, prints the resulting value in an
appropriate format, and requests further input.
Declared inside BAIL are several procedures whose values or side
effects are useful in the debugging process. These procedures handle the
insertion and deletion of breakpoints, display the static and dynamic
scope of the current breakpoint, display selected statements from the
source program, allow escape to an assembly- language debugging
program, and cause resumption of the suspended main program.
COMPILE-TIME ACTION
The principal result of activating BAIL at compile-time is the generation
of a file of information about the source program for use by the run-time
interpreter. This file has the same name as the .REL file produced by the
compilation, except that the extension is .SM1. If requested, BAIL will
also generate some additional code for SIMPLE procedures to make them
more palatable to the run-time interpreter.
The action of BAIL at compile time is governed by the value of the /B
switch passed to the compiler. If the value of this switch is zero (the
default if no value is specified) then BAIL is completely inactive.
Otherwise, the low-order bits determine the actions which BAIL
performs. [The value of the /B switch is interpreted as octal.]
bit action if on
1 The .SM1 file will contain the program counter to source/listing text
directory.
2 The .SM1 file will contain symbol information for all Sail symbols
encountered in the source. If this bit is off, then information is
kept only for procedures, parameters, blocks, and internals; i.e.,
non-internal local variables are not recorded.
4 SIMPLE procedures will get procedure descriptors, and one additional
instruction (a JFCL 0) is inserted at the beginning of SIMPLE
procedures. Except for these two changes, all properties of
SIMPLE procedures remain the same as before. The procedure
descriptor is necessary if the procedure is to be called
interpretively or if the procedure is to be TRACEd.
'10 BAIL will not be automatically loaded and initialized, although
all other actions requested are performed. This is primarily
intended to make it easier to debug new versions of BAIL
without interfering with SYS:BAIL.REL. By using this switch the
decision to load BAIL is delayed until load time.
'20 A request to load SYS:BAIPDn.REL is generated. This file contains
requests to load procedure descriptors for most of the predeclared
runtime routines, making it possible to call them from BAIL. The
procedure descriptors and their symbols occupy about 12P. Subsets of
these procedure descriptors can be loaded individually to reduce memory
space requirements, at the cost of not being able to talk about the
routines omitted. The subsets are BAICLC (containing SQRT, EXP,
LOG, SIN, COS, RAN, CVOS, CVSTR, CVXSTR), BAIIO1 (major
input/output and string procedures), BAIIO2 (minor input/output
and string procedures), BAIMSC (terminal functions and
miscellaneous), and BAIPRC (process and interrupt routines). To
use these subsets, request them explicitly (e.g., REQUIRE
"SYS:BAICLC" LOAD!MODULE; or on TENEX, "<SAIL>BAICLC") and leave the
/20B bit off.
The B switch must occur on the binary term, not the listing or source term.
Thus:
.R SAIL or .COM PROG(27B,)
*PROG/27B_PROG
The program counter to source/listing index is kept in terms of
coordinates. The coordinate counter is zeroed at the beginning of the
compilation and is incremented by one for each BEGIN, ELSE, and semicolon
seen by the parser, provided at least one word of code has been compiled
since the previous coordinate was defined. Note that COMMENTs are seen
only by the scanner, not the parser, and that DEFINEs and many
declarations merely define symbols and do not cause instructions to be
generated. For each coordinate the directory contains the coordinate
number, the value of the program counter, and a file pointer to the
appropriate place. The appropriate place is the source file unless a
listing file is being produced and the CREF switch is off, in which case
it is the listing file. [The listing file produced for CREF is nearly
unreadable.] On a non-CREF listing, the program counter is replaced by the
coordinate number if bit 1 of the /B switch is on.
The symbol table information consists of the block structure and the name,
access information, and type for each symbol.
If a BEGIN-END pair has declarations (i.e., is a true block and not just
a compound statement) but does not have a name, then BAIL will invent
one. The name is of the form Bnnnn where nnnn is the decimal value of
the current coordinate.
RUN-TIME ACTION DEBUGGING REQUESTS
RUN-TIME ACTION
The BAIL run-time interpreter is itself a Sail program which resides on
the system disk area. This program is usually loaded automatically, and
does some initialization when entered for the first time. The
initialization generates a .BAI file of information collected from the
.SM1 files produced by separate compilations (if any). The .SM1 files
correspond to .REL files, and the .BAI file corresponds to the .DMP or
.SAV file. Like RPG or CCL, BAIL will try to bypass much of the
initialization and use an existing .BAI file if appropriate. During
initialization BAIL displays the names of the .SM1 files it is
processing. For each .SM1 file which contains program counter/text index
information, BAIL displays the names of the text files and determines
whether the text files are accessible.
The interpreter is activated by explicit call, previously inserted
breakpoints, or the Sail error handler. For an explicit call, say
EXTERNAL PROCEDURE BAIL; ... BAIL;. From the error handler, respond
B. Breakpoints will be described later in this section.
DEBUGGING REQUESTS
When entered, BAIL prints the debugging recursion level followed by a colon,
and awaits a debugging request. BAIL accepts ALGOL and LEAP expressions
of the Sail language. The following exceptions should be noted.
Expressions involving control structure are not allowed, hence BAIL will
not recognize AND, OR, IF-THEN-ELSE, or CASE. Bracketed triple items are
not allowed. The TO and FOR substring and sublist operators have
been extended to operate as array subscript ranges, FOR PRINT-OUT ONLY.
If FOO is an array, then FOO[3 TO 7]; will act like FOO[3], FOO[4], FOO[5],
FOO[6], FOO[7]; but is easier to type. This extension is for print-out
only; no general APL syntax or semantics are provided.
BAIL evaluates symbolic names according to the scope rules of ALGOL,
extended to always recognize names which are globally unique and have a
fixed memory location (everything except parameters and recursive locals).
For any activation of BAIL, the initial scope is the ALGOL scope of the
statement from which BAIL was activated. The procedure SETLEX (see below)
may be used to change the scope to that of any one of the links in the
dynamic activation chain. See also the section below on BLOCK STRUCTURE for
a way to evade the scope rules.
Several procedures are predeclared in the outermost block to handle
breakpoints and display information. These are described individually
below.
ARGS BREAK COORD
********************************** ARGS ********************************;
"STR" _ ARGS
The arguments to the procedure which was most recently called.
********************************* BREAK ********************************;
BREAK ("LOCATION", "CONDITION"(NULL),
"ACTION"(NULL), COUNT(0))
A breakpoint is inserted. The syntax for the first argument is
<location>
::= <label>
::= <procedure>
::= <block name>
::= #<nnnn>
::= <block name> . <location>
<nnnn>
::= <decimal coordinate number>
If the location is specified by the <block name>.<location> construct then
the blocks of the core image are searched in ascending order of address of
BEGINs until the first <block name> is matched. The search continues until
the second <block name> is matched, etc. The breakpoint is inserted at the
label, procedure, or coordinate declared within the scope of the last <block
name>. This detailed specification is not usually necessary. The action
taken at a breakpoint is
IF LENGTH (CONDITION) AND EVAL (CONDITION)
AND (COUNT _ COUNT-1)<0 AND LENGTH(ACTION)
THEN EVAL(ACTION);
EVAL(TTY)
********************************* COORD ********************************;
NUMBER _ COORD ("LOCATION")
Returns the coordinate number of the location given as its argument.
LOCATION has the same syntax as in BREAK.
DDT DEFINE HELP SETLEX
********************************** DDT *********************************;
DDT
This procedure transfers control to an assembly language debugging program
(if one was loaded).
********************************* DEFINE *******************************;
DEFINE (CHAR, "MACRO")
Macros from the source file(s) are not recognized at the present time.
There are 26 character macros definable, from "A" to "Z". DEFINE macros
substitute the given string for each occurrence of <alt><char> which is not
part of a string constant. If the operating system can send characters of
more than 7 bits to INCHWL (INTTY under TENEX) then any activation character
with high order bits will also activate the macro. Thus at SUAI <alt>P, P,
and P are all equivalent. In all cases the character is converted to
upper case before doing anything else. The macros G, P, S, and X are
predefined to be " !!GO;", " !!GO;", " !!STEP;", and " !!GSTEP;"
respectively.
********************************** HELP ********************************;
HELP
A list of options, including short descriptions of the procedures described
in this section, is printed. An input consisting of a question mark
followed by a carriage return is interpreted as a call to HELP.
********************************* SETLEX *******************************;
SETLEX (LEVEL)
Evaluating SETLEX(n) changes the static (lexical) scope to the scope of the
n-th entry in the dynamic scope list. SETLEX(0) is the scope of the
breakpoint; SETLEX(1) is the scope of the most recent procedure call
in the dynamic scope, etc.
SHOW TEXT TRACE TRAPS UNBREAK
********************************** SHOW ********************************;
"STR" _ SHOW (FIRST, LAST(0))
The text of the program from the source or listing file. If last is less
than first then set last to last+first. Return coordinates first through
last. SHOW (5, 3) gives coordinates 5, 6, 7, and 8; SHOW (5, 7) gives
coordinates 5, 6, and 7; SHOW (5) gives coordinate 5 only.
A plus sign ("+") following the coordinate number indicates that the values
of some variables have been carried over in accumulators from the previous
coordinate. Changing the value of variables might not be successful in such
a case, because BAIL will not change any accumulator value directly. The
MEMORY construct can be used to modify any location in a core image,
including the accumulators.
********************************** TEXT ********************************;
"STR" _ TEXT
The current static and dynamic scopes, with text from the source or listing
file.
********************************* TRACE ********************************;
TRACE ("PROCEDURE")
Special breakpoints are inserted at the beginning and end of the procedure
named. On entry, the procedure name and arguments are typed. On exit, the
name and value returned (if any) are typed.
********************************* TRAPS ********************************;
"STR" _ TRAPS
A list of the current breakpoints and traces.
******************************** UNBREAK *******************************;
UNBREAK ("LOCATION")
The breakpoint at the location specified is removed.
UNTRACE !!GO !!GSTEP !!STEP GOGTAB
******************************** UNTRACE *******************************;
UNTRACE ("PROCEDURE")
The breakpoints inserted by TRACE are removed.
********************************** !!GO ********************************;
!!GO
An immediate exit from the current instantiation of BAIL is taken and
execution of the program is resumed. !!GO is a reserved word (the only one)
in BAIL.
******************************** !!GSTEP *******************************;
!!GSTEP
Temporary breakpoints are inserted at all of the logical exits of the
current statement, and execution of the program is resumed. Logical exits
are the next statement and locations to which the current statement can
jump, excluding any procedure calls. All of the breakpoints which are
inserted will be removed as soon as one of them is encountered.
********************************* !!STEP *******************************;
!!STEP
Temporary breakpoints are inserted at all locations to which the current
statement can jump, including procedure calls, and execution of the program
is resumed.
********************************* GOGTAB *******************************;
EXTERNAL INTEGER ARRAY GOGTAB[0:n]
This array is the Sail user table, containing all kinds of magical
information. (The procedure USERCON was formerly the only way to access the
user table.) If you are a hacker then pick up a copy of SYS:GOGTAB.DEF
(<SAIL>GOGTAB.DEF on TENEX) and poke around. Do not change any values
unless you know what you are doing.
STRING TYPEOUT BLOCK STRUCTURE BAIL and DDT
STRING TYPEOUT
Strings are usually typed so that the output looks the same as the input,
i.e., a string is typed with surrounding quotation marks and doubled
internal quotation marks. For SHOW, ARGS, and TEXT this would ordinarily
create confusion, so they are handled specially. When these procedures are
evaluated they set a flag which inhibits quotation mark fiddling, provided
that no further evaluation takes place before the next typeout. Thus
SHOW (5, 3); will be typed plain, but STR _ SHOW (5, 3); will have quotation
marks massaged.
BLOCK STRUCTURE
Variables not in the current scope can be referenced by using the same
scheme used to describe locations to BREAK. If you have something of your
own named SHOW then you can access the BAIL SHOW function by using
$RUN$.SHOW (coord);. Warning: this mode assumes that you know what you are
doing.
BAIL and DDT
When BAIL is loaded by a non-TENEX system, it sets .JBDDT to the address of
one of its routines. (If you load both BAIL and DDT then the last module
loaded wins.) Under TENEX, BAIL sets .JBDDT at runtime, but only if it is
zero when BAIL looks. If BAIL is initialized in a core image which does
not have DDT or RAID then things will be set up so that the monitor
command DDT gets you into BAIL in the right way. That is, BAIL will be
your DDT. To enter BAIL from DDT (provided that the Sail initialization
sequence has already been performed), use
pushi P,<program counter>$X
JRST BAIL$X
For example, if .JBOPC contains the program counter,
PUSH P,.JBOPC$X
JRST BAIL$X
The entry B. provides a path from DDT to BAIL which works whether or not the
core image has been initialized. One use of this feature is to BREAK a
procedure in an existing production program without recompiling. For
example,
@; PROG compiled, loaded with BAIL and DDT, and SSAVEd
@GET PROG
@DD
B.$G
BAIL initialization
:
:
1:BREAK("procedure");
1:!!GO;
$G
WARNINGS !!GOTO !!UP (LEVEL) !!QUERY SETSCOPE
To enter DDT from BAIL, simply say DDT;. For operation under TENEX,
control-B is a pseudo-interrupt character which gets you into BAIL.
WARNINGS
Since BAIL is itself a Sail procedure, entering BAIL from the error
handler or DDT after a push-down overflow or a string garbage
collection error will get you into trouble.
SIMPLE procedures cause headaches for BAIL because they do not keep a
display pointer. BAIL tries to do the right thing, but occasionally it
gets lost. BAIL will try to warn you if it can. In general, looking at
value string parameters of SIMPLE procedures does not work.
)!!GOTO ("LOCATION")
(For wizards only.) The return address is set to the location specified, and
then a !!GO is done. Note that the location should be in the same lexical
scope as the most recent entry to BAIL, or the program will probably get
confused.
!!UP (LEVEL)
(For wizards only.) This procedure trims the runtime stack back to LEVEL,
then reenters BAIL. CLEANUPs and deallocations are performed for the
procedures thus killed. Level has the same interpretation as in SETLEX, and
in addition must not designate a SIMPLE procedure. Suppose you ask BAIL to
evaluate a procedure call, the procedure hits an error, and you want to get
back to where you were before the procedure was called. Then !!UP will do
the trick if the value of level is correct.
!!QUERY
(Declare as EXTERNAL STRING !!QUERY in your program.) Whenever BAIL wants
input, it checks this string first. If it is not NULL then !!QUERY is used
instead of asking the operating system for input from the terminal.
(!!QUERY is set to NULL each time this is done.) Thus a program can simulate
the effect of typing to its own input buffer by stuffing the text into
!!QUERY. In particular, file input to BAIL and various macro hacks can be
effected by using procedures which assign values to !!QUERY.
******************************** SETSCOPE ******************************;
SETSCOPE (ITEMVAR PITEM)
If you have processes then SETSCOPE can be used to peek around the world.
Specifically, the static and dynamic scopes are set to those of the process
for which PITEM is the process item. This will allow access to variables
and traceback from TEXT, but care must be exercised when calling procedures.
A call to a procedure which is not defined at the top level will probably
not work. Also, if the procedure does not return successfully then your
stacks will be hopelessly confused.
RESOURCES USED EXAMPLE
Note on processes: BAIL runs in the process which caused the break. Thus
stack space must be provided in each process. The minimum amount is
PSTACK(4)+STRINGSTACK(2). RESOURCES USED
At compile time one channel, a small amount of additional memory, and
approximately 0.3 seconds of KA10 CPU time per page are used. BAIL uses two
channels at runtime and a third during initialization. These channels are
obtained with GETCHAN. If the debugging recursion level exceeds 3 or 4 then
it will be necessary to increase the pushdown stacks (particularly
STRING!PDL) appropriately. BAIL uses 7 of the privileged breaktables,
obtaining them with GETBREAK. BAIL occupies 19.5 pages. Symbols require 5
words each with an additional 2 words for each block; one word for each 128
coordinates is also required. The disk space required for .SM1 and .BAI
files is generally one half that required for the .REL files.
EXAMPLE
@TYPE TEST1.SAI
; <REISER>TEST1.SAI;1 SAT 28-AUG-76 4:20PM PAGE 1
BEGIN "TEST"
EXTERNAL PROCEDURE BAIL;
INTEGER I, J, K; STRING A, B, C; REAL X, Y, Z;
INTEGER ARRAY FOO[0:15]; STRING ARRAY STRARR[1:5, 2:6];
INTEGER PROCEDURE ADD (INTEGER I, J); BEGIN "ADD"
OUTSTR ("
HI. GLAD YOU STOPPED BY."); RETURN (I+J) END "ADD";
FOR I_0 STEP 1 UNTIL 15 DO FOO[I]_I*I;
FOR I_1 STEP 1 UNTIL 5 DO
FOR J_2 STEP 1 UNTIL 6 DO STRARR[I, J]_64+8*I+J;
I_4; J_6; K_112; X_3.14159265; Y_0; Z_23.;
A_"BIG DEAL"; B_"QED"; C_"THE LAST PICASSO";
BAIL; ADD (7, 45); USERERR (0, 1, "THIS IS A TEST");
END "TEST";
^L
@SAIL.SAV;10
TENEX SAIL 8.1 8-28-76 (? FOR HELP)
*TEST1,_
**/27B
**
TEST1.SAI;1 1
END OF COMPILATION.
LOADING
LOADER 6+9K CORE
EXECUTION
$G
BAIL VER. 28-AUG-76
TEST1.SM1;2
TEST1.SAI;1
End of BAIL initialization.
1:45, 7.089, "SOME RANDOM STRING";
45 7.089000 "SOME RANDOM STRING"
1:'275, TRUE, FALSE, NULL;
189 -1 0 ""
1:J, X, I_46;
6 3.141593 46
1:I, I<J;
46 0
1:45*(89.4-53.06);
1635.300
1:ADD (3, 4);
HI. GLAD YOU STOPPED BY. 7
1:FOO;
<ARRAY>[ 0:15]
1:FOO[4];
16
1:STRARR[1 FOR 2, 4 TO 6];
"L" "M" "N" "T" "U" "V"
1:FOO[35];
SUBSCRIPTING ERROR.
INDEX VALUE MIN MAX
1 35 0 15 : FOO[35]
;
1:BREAK ("ADD");
1:ADD (3, 4);
2:ARGS;
3 4
2:!!GO;
HI. GLAD YOU STOPPED BY. 7
1:!!GO;
1:TEXT;
LEXICAL SCOPE, TOP DOWN:
$RUN$
TEST
ADD
ROUTINE TEXT
ADD #4 INTEGER PROCEDURE ADD (INTEGER I, J);
TEST #24 ADD (7, 45);
AT SETLEX(0);
CURRENT STATUS
1:UNBREAK ("ADD");
1:!!GO;
HI. GLAD YOU STOPPED BY.
THIS IS A TEST
CALLED FROM 642124 LAST SAIL CALL AT 400303
^B
1:TEXT;
LEXICAL SCOPE, TOP DOWN:
$RUN$
DYNAMIC SCOPE, MOST RECENT FIRST:
ROUTINE TEXT
.SIMPLE. '642124 %%% FILE NOT VIEWABLE
TEST #26 USERERR (0, 1, "THIS IS A TEST");
AT SETLEX(0);
1:I;
UNKNOWN ID: I
;
1:SETLEX (1);
LEXICAL SCOPE, TOP DOWN:
$RUN$
TEST
1:I;
64
1:!!GO;
END OF SAIL EXECUTION.
CURRENT STATUS
The state of the world is determined by the values of the accumulators
and the value of the Sail variable !SKIP!.
The run-time interpreter recognizes only the first 15 characters of
identifier names; the rest are discarded without comment. The
characters which are legal in identifiers are
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
0123456789!!#$\|
Notable for its absence: period.
LOCATION of a procedure does not work.
PROPS is read-only.
Bracketed triple items are not allowed.
A procedure call containing the name of a parametric procedure
(functional argument) is not handled properly.
Contexts are not recognized.
External linkage: If an identifier is never referenced by code (i.e., has
an empty fixup chain at the time fixups are put out to the loader)
then that identifier is not defined by Sail. Thus variables which are
never used do not take up space and a request to the loader is not
made for EXTERNALS which are not referenced. This feature of Sail. As
a result, the following DOES NOT WORK unless special precautions are
taken:
BEGIN
EXTERNAL PROCEDURE BAIL;
EXTERNAL PROCEDURE
PLOT (REAL X0, Y0, X1, Y1);
REQUIRE "CALCOM" LIBRARY;
BAIL END
PLOT will not be defined by Sail, hence BAIL will not know about it.
However if there are any references to PLOT (real or "dummy" calls) then
BAIL will know. The following trick can also be used, assuming that CALCOM
is a Sail-compiled library: Compile CALCOM with /10B, which says "make
the .SM1 file but don't automatically load SYS:BAIL.REL". Then the
above will win (due to BAIL recognizing things which are globally unique)
and programs which do not use BAIL will not have it loaded just because the
library was used. This same problem occurs with EXTERNAL RECORD!CLASS
declarations. Use of the field index information does not cause a
reference to the class name but NEW!RECORD does. Thus the same /10B trick
must be used if there are no NEW!RECORD calls.
BAIL and other language processors: If CALCOM in the paragraph above was
compiled by some processor other than Sail (e.g. FAIL, MACRO, BLISS, ...)
then further steps must be taken if BAIL is to know about the procedures
contained in the file. BAIL must have access to a procedure descriptor
in order to call any procedure (cf. the /4B switch). Thus a user who
wishes to use assembly language procedures with BAIL must provide
appropriate procedure descriptors. The file SUAISAILPD.FAI[S,AIL]
defines a FAIL macro which will generate a Sail procedure descriptor.
The procedure descriptors may reside in a separate load module if
desired; but they must be in the core image when BAIL is being used.
APPENDICES Characters Stanford (SUAI) Character Set
APPENDIX A
Characters
CHARACTER EQUIVALENT RESERVED WORD
AND
EQV
NOT
OR
XOR
INF
IN
| SUCH THAT
NEQ
LEQ
GEQ
{ SETO
~ SETC
UNION
INTER
` ASSOC
SWAP
! !
Stanford (SUAI) Character Set
The Stanford ASCII character set is displayed in the following table. The
three digit octal code for a character is composed of the number at the left
of its row plus the digit at the top of its column. For example, the code
for ``A'' is 100+1 or 101.
ASCII 0 1 2 3 4 5 6 7
000 NUL
010 TAB LF VT FF CR
SIXBIT 020
030 !
00 040 SP ! " # $ % & '
10 050 ( ) * + , - . /
20 060 0 1 2 3 4 5 6 7
30 070 8 9 : ; < = > ?
40 100 @ A B C D E F G
50 110 H I J K L M N O
60 120 P Q R S T U V W
70 130 X Y Z [ \ ] ^ _
140 ` a b c d e f g
150 h i j k l m n o
160 p q r s t u v w
170 x y z { | ALT ~ BS
The tables below display the standard ASCII codes, and the SOS
representation for entering the full ASCII character set from Teletypes or
similar terminals with restricted character sets. The obscure names for the
ASCII codes below 40 are listed just for confusion. Notes: ``DEL'' (177) is
the ASCII delete. ``ESC'' (33) is their alt mode. Codes 136 and 137 have
two different interpretations, as shown below. The SOS representation is so
called because it is provided by SOS, the Teletype editor. Certain other
programs also know about this representation, but it is not built into Sail
in any way.
Standard ASCII
0 1 2 3 4 5 6 7
000 NUL SOH STX ETX EOT ENQ ACK BEL
010 BS TAB LF VT FF CR SO SI
020 DLE DC1 DC2 DC3 DC4 NAK SYN ETB
030 CAN EM SUB ESC FS GS RS US
040 SP ! " # $ % & '
050 ( ) * + , - . /
060 0 1 2 3 4 5 6 7
070 8 9 : ; < = > ?
100 @ A B C D E F G
110 H I J K L M N O
120 P Q R S T U V W
130 X Y Z [ \ ] ^ !_
140 ` a b c d e f g
150 h i j k l m n o
160 p q r s t u v w
170 x y z { | ~ DEL
SOS Representation of Standard ASCII
0 1 2 3 4 5 6 7
000 --- ?! ?" ?# ?$ ?% ?& ?'
010 ?( TAB LF VT FF CR ?) ?*
020 ?+ ?, ?- ?. ?/ ?0 ?1 ?2
030 ?9 ?6 ?4 ?= ?< ?> ?7 ?8
040 SP ! " # $ % & '
050 ( ) * + , - . /
060 0 1 2 3 4 5 6 7
070 8 9 : ; < = > ??
100 @ A B C D E F G
110 H I J K L M N O
120 P Q R S T U V W
130 X Y Z [ \ ] ^ _
140 ?@ ?A ?B ?C ?D ?E ?F ?G
150 ?H ?I ?J ?K ?L ?M ?N ?O
160 ?P ?Q ?R ?S ?T ?U ?V ?W
170 ?X ?Y ?Z ?[ ?: ?] ?3 ?\
The Sail compiler automatically transliterates "!" to "!" before doing
anything else (outside of string constants, of course). It also believes
that BOTH '175 and '176 represent the right brace character "~".
Sail Reserved Words
APPENDIX B
Sail Reserved Words
ABS END
ACCESS ENDC
AFTER ENTRY
ALL EQV
ALLGLOBAL ERASE
AND ERROR!MODES
ANY!CLASS EVALDEFINE
APPLY EVALREDEFINE
ARG!LIST EXPR!TYPE
ARRAY EXTERNAL
ASH FAIL
ASSIGN FALSE
ASSIGNC FIRST
ASSOC FOR
BBPP FORC
BEFORE FOREACH
BEGIN FORGET
BIND FORLC
BOOLEAN FORTRAN
BUCKETS FORWARD
BUILT!IN FROM
CASE GEQ
CASEC GLOBAL
CAUSE GO
CHECK!TYPE GOTO
CLEANUP IBP
COMMENT IDPB
COMPILER!SWITCHES IF
CONOK IFC
CONTEXT IFCR
CONTINUE ILDB
COP IN
CPRINT IN!CONTEXT
CVI INF
CVLIST INITIALIZATION
CVMS INTEGER
CVN INTER
CVPS INTERNAL
CVSET INTERROGATE
DATUM ISTRIPLE
DECLARATION ITEM
DEFINE ITEM!START
DELETE ITEMVAR
DELIMITERS KILL!SET
DEPENDENTS LABEL
DIV LAND
DO LDB
DOC LEAP!ARRAY
DONE LENGTH
DPB LEQ
ELSE LET
ELSEC LIBRARY
LIST REQUIRE
LISTC RESERVED
LISTO RESTORE
LNOT RETURN
LOAD!MODULE ROT
LOCATION SAFE
LOP SAMEIV
LOR SECOND
LSH SEGMENT!FILE
MAKE SEGMENT!NAME
MATCHING SET
MAX SETC
MEMORY SETCP
MESSAGE SETIP
MIN SETO
MOD SHORT
NEEDNEXT SIMPLE
NEQ SOURCE!FILE
NEW SPROUT
NEW!ITEMS SPROUT!DEFAULTS
NEW!RECORD START!CODE
NEXT STEP
NIL STEPC
NOMAC STRING
NOT STRING!PDL
NOW!SAFE STRING!SPACE
NOW!UNSAFE SUCCEED
NULL SUCH
NULL!CONTEXT SWAP
NULL!DELIMITERS SYSTEM!PDL
NULL!RECORD THAT
OF THEN
OFC THENC
OR THIRD
OWN TO
PHI TRUE
PNAMES UNION
POLL UNSTACK!DELIMITERS
POLLING!INTERVAL UNTIL
PRELOAD!WITH UNTILC
PRESET!WITH VALUE
PRINT VERSION
PROCEDURE WHILE
PROCESSES WHILEC
PROTECT!ACS XOR
PUT
QUICK!CODE
REAL
RECORD!CLASS
RECORD!POINTER
RECURSIVE
REDEFINE
REF!ITEM
REFERENCE
REMEMBER
REMOVE
REPLACE!DELIMITERS
Sail Predeclared Identifiers
APPENDIX C
Sail Predeclared Identifiers
$PINT CVSTR RAN
$PITM CVXSTR REALIN
$PLST DDFINT REALSCAN
$PREC DEL!PNAME RELEASE
$PREL DFCPKT RENAME
$PRINT DFR1IN RESUME
$PSET DFRINT SCAN
$PSTR DISABLE SCANC
ACOS EDFILE SETBREAK
ANSWER ENABLE SETFORMAT
ARRBLT ENTER SETPL
ARRCLR EQU SETPRINT
ARRINFO ERMSBF SIN
ARRTRAN EVENT!TYPE SIND
ARRYIN EXP SINH
ARRYOUT FILEINFO SQRT
ASIN GETBREAK STDBRK
ASKNTC GETCHAN SUBSR
ATAN GETFORMAT SUBST
ATAN2 GETPRINT SUSPEND
BBPP. INCHRS TANH
BINDIT INCHRW TERMINATE
BREAKSET INCHSL TRIGINI
CALL INCHWL TTYIN
CALLER INPUT TTYINL
CAUSE1 INSTR TTYINS
CHNCDB INSTRL TTYUP
CLKMOD INSTRS TYPEIT
CLOSE INTIN URSCHD
CLOSIN INTMAP USERCON
CLOSO INTPRO USERERR
CLRBUF INTSCAN USETI
CODE INTSET USETO
COMPILER! INTTBL WORDIN
BANNER JOIN WORDOUT
COS LINOUT
COSD LISTX
COSH LOG
CV6STR LOOKUP
CVASC MAINPI
CVASTR MAINPR
CVD MKEVTT
CVE MTAPE
CVF MYPROC
CVFIL NEW!PNAME
CVG OPEN
SUAI ONLY TOPS-10 ONLY CMU ONLY TYMSHARE ONLY TENEX ONLY
CVIS OUT
CVO OUTCHR
CVOS OUTSTR
CVS POINT
CVSI PRISET
CVSIX PSTATUS
SUAI ONLY
GET!BIT PTCHRS PTYALL
GET!DATA PTCHRW PTYGET
GET!ENTRY PTIFRE PTYIN
GET!SET PTOCHS PTYREL
IFGLOBAL PTOCHW PTYSTR
ISSUE PTOCNT PUT!DATA
LODED PTOSTR QUEUE
TOPS-10 ONLY
BACKUP INOUT INTMOD
CHNCDB GETSTS TMPIN
ERENAME SETSTS TMPOUT
CMU ONLY
ARDINIT DOTVEC SEAINIT
ARDSTR INITSEA SEAREL
CHARSZ INVVEC SETPNT
CHRMOD MOUSES SVEC
CLEAR MOUSEW VISVEC
TYMSHARE ONLY
AUXCLR CALLI CHNIOV
AUXCLV CHNIOR IONEOU
TENEX ONLY
ASND INDEXFILE RTIW
ATI INTTY RUNPRG
BKJFN JFNS RUNTM
CFILE JFNSL RWDPTR
CHARIN KPSITIME SCHPTR
CHAROUT MTOPR SDSTS
CHFDB ODTIM SETCHAN
CLOSF OPENF SETEDIT
CNDIR OPENFILE SETINPUT
CVJFN PBIN SFCOC
DEVST PBOUT SFMOD
DEVTYPE PMAP SFPTR
DIRST PSIDISMS SINI
DTI PSIMAP SIZEF
DVCHR PSIRUNTM STDEF
ERSTR PSOUT STDIR
GDSTS RCHPTR STI
GJINF RDSEG STIW
GNJFN RELD STPAR
GTAD RFBSZ STSTS
GTFDB RFCOC STTYP
GTJFN RFMOD SWDPTR
GTRPW RFPTR UNDELETE
GTSTS RLJFN
IDTIM RNAMF
Indices for Interrupts
APPENDIX D
Indices for Interrupts
SUAI INTERRUPT SYSTEM
NAME NUMBER DESCRIPTION
INTSWW!INX 0 You will receive an interrupt when your job is about to be
swapped out.
INTSWD!INX 1 You will receive an interrupt when your job is swapped back
into core. If you are activated for interrupts for swap out
also, you will receive these two interrupts as a pair in the
expected order every time your job is swapped.
INTSHW!INX 2 You will receive an interrupt when your job is about to be
shuffled.
INTSHD!INX 3 You will receive an interrupt when your job has been
shuffled.
INTTTY!INX 4 You will receive an interrupt every time your program would
be activated due to the teletype if it were waiting for the
teletype. As long as you do not ask for more than there is in
the teletype buffer, you may read from the teletype at
interrupt level.
INTPTO!INX 5 You will be interrupted every time the PTY job goes into a
wait state waiting for you to sent it characters.
INTMAIL!INX 6 Interrupts whenever someone SENDs you mail (see [Frost]).
You may read the letter at interrupt level.
INTPTI!INX 8 You will be interrupted every time any job on a PTY you own
send you a character (or line).
INTPAR!INX 9 Interrupts you on parity errors in your core image.
INTCLK!INX 10You will be interrupted at every clock tick (1/60th of a
second).
INTINR!INX 11IMP interrupt by receiver.
INTINS!INX 12IMP interrupt by sender.
INTIMS!INX 13IMP status change interrupt.
INTINP!INX 14IMP input waiting.
INTTTI!INX 15You will be interrupted whenever <esc> I is typed on your
teletype.
INTPOV!INX 19Interrupts you on push-down overflow.
INTILM!INX 22Interrupts you on illegal memory references, that is,
references to memory outside of your core image.
INTNXM!INX 23You will receive an interrupt whenever your program
references non-existent memory.
INTFOV!INX 29Interrupts you on floating overflow.
INTOV!INX 32Interrupts you on arithmetic overflow.
Bits 33 through 35 are left to the user. REQUIRE "SYS:PROCES.DEF"
SOURCE!FILE to define the above names. NOTE: to program yourself for more
than one interrupt, you must execute two separate INTMAP statements.
TOPS-10 INTERRUPT SYSTEM
NAME NUMBER DESCRIPTION
INTPOV!APR 19Interrupts you on push-down stack overflow.
INTILM!APR 22Interrupts you on illegal memory references, that is,
references to memory outside of your core image.
INTNXM!APR 23You will receive an interrupt whenever your program
references non-existent memory.
INTFOV!APR 29Interrupts you on floating overflow.
INTOV!APR 32Interrupts you on arithmetic overflow.
TENEX PSI CHANNELS
CHANNEL USE
0-5 terminal character
6 APR integer overflow, no divide
7 APR floating overflow, exponent underflow
8 unused
9 pushdown overflow
10 file EOF
11 file data error
12 file, unassigned
13 file, unassigned
14 time of day
15 illegal instruction
16 illegal memory read
17 illegal memory write
18 illegal memory execute
19 subsidiary fork termination, forced freeze
20 machine size exceeded
21 SPACS trap to user
22 reference to non-existent page
23 unused
25-35 terminal character
Bit Names for Process Constructs SPROUT OPTIONS RESUME OPTIONS
APPENDIX E
Bit Names for Process Constructs
SPROUT OPTIONS
BITS NAME DESCRIPTION
14-17 QUANTUM(X) Q _ IF X=0 THEN 4 ELSE 2^X; The process will be given a
quantum of Q clock ticks, indicating that if the user is
using CLKMOD to handle clock interrupts, the process should
be run for at most Q clock ticks, before calling the
scheduler. (see about CLKMOD, page 214 for details on
making processes "time share").
18-21 STRINGSTACK(X) S _ IF X=0 THEN 16 ELSE X*32; The process will be
given S words of string stack.
22-27 PSTACK(X) P_IF X=0 THEN 32 ELSE X*32; The process will be given
P words of arithmetic stack.
28-31 PRIORITY(X) P _ IF X=0 THEN 7 ELSE X; The process will be given a
priority of P. 0 is the highest priority, and reserved for
the Sail system. 15 is the lowest priority. Priorities
determine which ready process the scheduler will next pick
to make running.
32 SUSPHIM If set, suspend the newly sprouted process.
33 Not used at present.
34 SUSPME If set, suspend the process in which this sprout statement
occurs.
35 RUNME If set, continue to run the process in which this sprout
statement occurs.
RESUME OPTIONS
33-32 READYME If 33-32 is 1, then the current process will not be
suspended, but be made ready.
KILLME If 33-32 is 2, then the current process will be terminated.
IRUN If 33-32 is 3, then the current process will not be
suspended, but be made running. The newly resumed process
will be made ready.
34 This should always be zero.
CAUSE OPTIONS INTERROGATE OPTIONS
35 NOTNOW If set, this bit makes the newly resumed process ready
instead of running. If 33-32 are not 3, then this bit
causes a rescheduling.
CAUSE OPTIONS
35 DONTSAVENever put the <event item> on the notice queue. If there is
no process on the wait queue, this makes the cause
statement a no-op.
34 TELLALL Wake all processes waiting for this event. Give them all
this item. The highest priority process will be made
running, others will be made ready.
33 RESCHEDULE Reschedule as soon as possible (i.e., immediately after
the cause procedure has completed executed).
INTERROGATE OPTIONS
35 RETAIN Leave the event notice on the notice queue, but still
return the notice as the value of the interrogate. If the
process goes into a wait state as a result of this
Interrogate, and is subsequently awakened by a Cause
statement, then the DONTSAVE bit in the Cause statement
will over ride the RETAIN bit in the Interrogate if both
are on.
34 WAIT If the notice queue is empty, then suspend the process
executing the interrogate and put its process item on the
wait queue.
33 RESCHEDULE Reschedule as soon as possible (i.e., immediately after
execution of the interrogate procedure).
32 SAY!WHICH Creates the association EVENT!TYPE <event notice>
<event type> where <event type> is the type of the event
returned. Useful with the set form of the Interrogate
construct.
Statement Counter System GENERAL DISCUSSION HOW TO GET COUNTERS PROFILE PROGRAM
APPENDIX F
Statement Counter System
GENERAL DISCUSSION
The statement counter system allows you to determine the number of times
each statement in your program was executed. Sail accomplishes this by
inserting an array of counters and placing AOS instructions at various
points in the object program (such as in loops and conditional statements).
Sail automatically calls K!ZERO to zero the counter array before your
program is entered and K!OUT to write the array before exiting to the
system. If your program does not exit by falling out the bottom, or you are
interested only in counts during specific periods, then you may declare
K!OUT and K!ZERO as external procedures and call them yourself.
Another program, called PROFIL, is used to merge the listing file produced
by the Sail compiler with the file of counters produced by the execution of
your program. The output of the PROFIL program is an indented
listing with execution counts in the right hand margin.
Since the AOS instructions access fixed locations, and they are placed
only where needed to determine program flow, they should not add much
overhead to the execution time. Although no large study has been made, the
counters seem to contribute about 2% to the execution time of the
profile program, which has a fairly deeply nested structure.
HOW TO GET COUNTERS
In order to use the counter system you must generate a listing and also
specify the /K switch. Specifying /K automatically selects /10F, since the
PROFIL program needs this listing format. The characters '002 and '003 in
the listing mark the location of counters.
At the end of each program (i.e. each separate compilation) is the block
of counters, preceded by a small data block used by K!ZERO and K!OUT. This
block contains the number of counters, the name of the list file, and a
link to other such blocks. The first counter location is given the
symbolic name .KOUNT, which is accessible from DDT, but cannot be
referenced by the Sail program itself.
K!OUT uses GETCHAN to find a spare channel, does a single dump mode
output which writes out all the counters for all the programs loaded having
counters, and then releases the channel. The file which it writes is
xxx.KNT, where xxx is the name of the list file of the first program loaded
having counters (usually the name of the Sail source file). If there
are no counters, K!OUT simply returns.
PROFILE PROGRAM
The program PROFIL is used to produce the program profile, i.e. the
SAMPLE RUN
listing complete with statement counts. It operates in the following
manner. First it reads in the file xxx.KNT created by the execution of
the user program. This file contains the values of the counters and the
names of the list files of the programs loaded which had counters. It
then reads the the list files and produces the profile.
The format of the listing is such that only statements executed the
same number of times are listed on a single line. In the case of
conditional statements, the statement is continued on a new line after the
word THEN. Conditional expressions and case expression, on the other
hand, are still listed on a single line. In order that you might know the
execution counts, they are inserted into the text surrounded by two
"brokets" (e.g. <<15>>).
PROFIL expects a command string of the form
<output>_<input> {switches~
where <input> is the name of the file containing the counters; extension
.KNT is assumed. If the output device is the DSK, the output file will
have a default extension of .PFL. Although the line spacing will
probably be different from the source, PROFIL makes an effort to keep any
page spacing that was in the source. The switches allowed by PROFIL are
/nB Indent n spaces for blocks (default 4)
/nC Indent n spaces for continuations (default 2)
/F Fill out every 4th line with "..." (default ON)
/I Ignore comments, strip them from the listing
/nK Make counter array of size n (default 200)
/nL Maximum line length of n (default 120)
/N Suppress /F feature
/S Stop after this profile
/T TTY mode = /1C/2B/F/80L
SAMPLE RUN
Suppose that you have a Sail program named FOO.SAI for which you desire a
profile. The following statements will give you one.
.EX /LIST FOO(K) (or TRY or DEB or what have you)
. . . any input to FOO . . .
EXIT
^C
.R PROFIL
*FOO_FOO/T/S
EXIT
^C
At this point, the file FOO.PFL contains the profile, suitable for typing on
the TTY or editing.
Array Implementation
APPENDIX G
Array Implementation
Let STRINGAR be 1 (TRUE) if the array in question is a String array, 0
(FALSE) otherwise. Then a Sail array of n dimensions has the following
format:
HEAD: DATAWD ; MEANS "POINTS AT"
HEAD-END-1
ARRHED: BASE!WORD ;SEE BELOW
LOWER!BD(n)
UPPER!BD(n)
MULT(n)
...
LOWER!BD(1)
UPPER!BD(1)
MULT(1)
NUM!DIMS,,TOTAL!SIZE
DATAWD: BLOCK TOTAL!SIZE
<sometimes a few extra words>
END: 400000,,HEAD
HEAD The first two words of each array, and the last, are control words
for the dynamic storage allocator. These words are always
present for an array. The array access code does not refer to
them.
ARRHED Each array is preceded by a block of 3*n+2 control words. The
BASE!WORD entry is explained later.
NUM!DIMS This is the dimensionality of the array. If STRINGAR, this value
is negated before storage in the left half.
DATAWD This is stored in the core location bearing the name of the array
(see symbols, page 247). If it is a string array, DATAWD+1 is
stored instead.
TOTAL!SIZE The total number of accessible elements (double if STRINGAR) in
the array.
BOUNDS The lower bound and upper bound for each dimension are stored in
this table, the left-hand index values occupying the higher
addresses (closest to the array data). If they are constants, the
compiler will remember them too and try for better code (i.e.
immediate operands).
MULT This number, for dimension m, is the product of the total number
of elements of dimensions m+1 through n. MULT for the last
dimension is always 1.
BASE!WORD This is DATAWD minus the sum of (STRINGAR+1) * LOWER!BD(m) *
MULT(m) for all m from 1 to n. If this is a string array then the
left half is -1.
The formula for calculating the address of A[I,J,K] is:
address(A[I,J,K]) =
address(DATAWD) +
(I-LOWER!BD(1))*MULT(1) +
(J-LOWER!BD(2))*MULT(2) +
(K-LOWER!BD(3))
This expands to
address(A[I,J,K]) =
address(DATAWD) +
I*MULT(1) + J*MULT(2) + K
-(LOWER!BD(1)*MULT(1) +
LOWER!BD(2)*MULT(2) +
LOWER!BD(3)
which is
BASE!WORD + I*MULT(1) + J*MULT(2) + K.
By pre-calculating the effects of the lower bounds, several instructions are
saved for each array reference.
The LOADER gets confused if BASE!WORD does not designate the same segment as
DATAWD. The difference between BASE!WORD and the address of any location in
the array should be less than '400000. Avoid constructs like INTEGER ARRAY
X[1000000:1000005]. Declare large static arrays last.
String Implementation STRING DESCRIPTORS INEXHAUSTIBLE STRING SPACE
APPENDIX H
String Implementation
STRING DESCRIPTORS
A Sail String has two distinct parts: the descriptor and the text. The
descriptor is unique and has the following format:
WORD1: CONST,,LENGTH
WORD2: BYTP
1)CONST. This entry is 0 if the String is a constant (the descriptor
will not be altered, and the String text is not in String space, is
therefore not subject to garbage collection), and non-zero otherwise.
2)LENGTH. This number is zero for any null String; otherwise it is the
number of text characters.
3)BYTP. If LENGTH is 0, this byte pointer is never checked (it need not
even be a valid byte pointer. Otherwise, an ILDB machine instruction
pointed at the BYTP word will retrieve the first text character of
the String. The text for a String may begin at any point in a word.
The characters are stored as LENGTH contiguous characters.
A Sail String variable contains the two word descriptor for that variable.
The identifier naming it points to WORD1 of that descriptor. If a String is
declared INTERNAL, a symbol is formed to reference WORD2 by taking all
characters from the original name (up to 5) and concatenating a "."
(OUTSTRING's second word would be labeled OUTST.).
When a String is passed by reference to a procedure, the address of WORD2 is
placed in the P-stack (see page 287). For VALUE Strings both descriptor
words are pushed onto the SP stack.
A String array is a block of 2-word String descriptors. The array descriptor
(see page 281) points at the second word of the first descriptor in the
array.
Information is generated by the compiler to allow the locations of all non-
constant strings to be found for purposes of garbage-collection and
initialization. All String variables and non-preloaded arrays are cleared
to NULL whenever a Sail program is started or restarted. The non-constant
strings in Preloaded arrays are also set to null by a restart.
INEXHAUSTIBLE STRING SPACE
The string garbage collector expands string space (using discontiguous
blocks) whenever necessary to satisfy the demand for places to put strings.
Here are some points of interest:
1) The initial string space size is settable via REQUIRE or the ALLOC
sequence. Each string-space increment will be the same as the original
size. The threshold (see below) for expansion is 1/8 the string
space size (increment size). One can modify these values with USERCON
or by storing directly into GOGTAB.
NAME VALUE
STINCR LH: # chars in increment
RH: 4+ # words in increment
STREQD LH: # chars in threshold
RH: # words in threshold
2) (the threshold) Assume that the garbage collector was called to make
room for R characters, and that after garbage collection M-1
discontiguous string spaces are full, with the M'th having N free
characters. If N is less than or equal to R+LH (STREQD) then expansion
to M+1 string spaces takes place. In other words, if STREQD is 1/8 the
size of the current space then that space will not be allowed to become
more than about 7/8 full. All but the current space are allowed to
become as full as possible, however.
3) Wizards may cause the garbage collector to keep some statistics by
setting SGCTIME to -1.
Save/Continue
APPENDIX I
Save/Continue
A (new) save/continue facility has been implemented in the Sail compiler.
This allows compiling header files, saving the state of the compiler, and
resuming compilation at a later time. The save/continue facility works with
files as the basic unit; compilation can be interrupted only at the end
of a file. The /X (eXtend) switch controls the new feature. The examples
shown here are for TOPS-10. Analogous commands work under TENEX, using
the TENEX RUN and SAVE commands. Example:
.R SAIL
*INTRMD.REL[PRJ,PRG]_A,B,C/X
A.SAI 1 etc.
SAVE ME FOR USE AS XSAIL.
EXIT
.SAVE XSAIL
JOB SAVED IN 25K
UPPER NOT SAVED!
.RU XSAIL
*FINAL_D,E,F
D.SAI
Copying DSK:INTRMD.REL[PRJ,PRG]
2 3 etc.
*^C
The above is equivalent to
.R SAIL
*FINAL_A,B,C,D,E,F
On TENEX, the user will want to save all of core when creating the
XSAIL.SAV file.
Information is saved in XSAIL.SAV and in the binary file from the first
"compilation" (in this case INTRMD.REL). When compilation is resumed,
the final binary file is initialized by copying the intermediate
file. Save/continue is not allowed if the file break occurs while scanning
false conditional compilation or actual parameters to a macro call.
A hint on using this feature: If the source term of your command string
consists of just one file, and this one file does REQUIREs of other source
files, the following setup works well.
Original file FOO.SAI:
BEGIN "FOO"
REQUIRE "[][]" DELIMITERS;
DEFINE !=[COMMENT];
REQUIRE "BAZ.SAI" SOURCE!FILE;
REQUIRE "MUMBLE.SAI" SOURCE!FILE;
:
<rest of file>
:
END "FOO"
New file FOO.SAI:
IFCR NOT DECLARATION(GARPLY) THENC
BEGIN "FOO"
REQUIRE "[][]" DELIMITERS;
DEFINE GARPLY=TRUE;
DEFINE !=[COMMENT];
REQUIRE "BAZ.SAI" SOURCE!FILE;
REQUIRE "MUMBLE.SAI" SOURCE!FILE;
ENDC;
:
<rest of file>
:
END "FOO"
New file FOO.HDR:
IFCR NOT DECLARATION(GARPLY) THENC
BEGIN "FOO"
REQUIRE "[][]" DELIMITERS;
DEFINE GARPLY=TRUE;
DEFINE !=[COMMENT];
REQUIRE "BAZ.SAI" SOURCE!FILE;
REQUIRE "MUMBLE.SAI" SOURCE!FILE;
ENDC;
Initial compilation:
.R SAIL
*FOO.INT[PRJ,PRG]_FOO.HDR/X
SAVE ME!
.SAV XSAIL
Now the command string
FOO_FOO
will work both in the case of .R SAIL and in the case .RU XSAIL.
Procedure Implementation STACK FRAME
APPENDIX J
Procedure Implementation
When a procedure is entered it places three words of control information
on the run time (P) stack. This "mark stack control packet" contains
pointers to the control packets for the procedure's dynamic and static
parents. Register F ('12) is set to point at this area. This pointer is
then used to access procedure parameters and other "in stack" objects, such
as the local variables of a recursive procedure. Many of the run-time
routines (including the string garbage collector) use rF to find vital
information. Therefore, THE USER MUST NOT HARM REGISTER '12. If you wish
to refer in assembly language to a procedure parameter, the safest way is
name it, and let Sail do the address arithmetic. (Similarly one may use the
ACCESS construct).
STACK FRAME
Shown here is the stack frame of a recursive procedure.
:.............................:
: parameter 1 :
:.............................:
: :
:.............................:
: parameter n :
:.............................:
: : ret. addr :
:.............................:
rF : : dynamic link : (old rF)
:.............................:
: proc desc : static link : (rF of static
:.............................: parent)
: old value of rSP :
:.............................:
: start of recursive locals :
:.............................:
: :
:.............................:
rP : end of recursive locals :_(rP points
:.............................: here after
: start of working storage : entry to a
:.............................: recursive
: : procedure)
:.............................:
If a formal parameter is a value parameter then the actual parameter value
is kept on the stack. If a formal parameter is a reference parameter, then
the address of the actual parameter is put on the stack. Non-own string
ACCESSING THINGS ON THE STACK
locals (to recursive procedures) and string value parameters are kept on
the string (SP = '16) stack. The stack frame for a non-recursive procedure
is the same except that there are no local variables on the stack. The
stack frame for a SIMPLE procedure consists only of the parameters and the
return address.
ACCESSING THINGS ON THE STACK
SIMPLE procedures access their parameters relative to the top-of-stack
pointers SP(for strings) and P (for everything else). Thus the the k'th (of
n) string value parameter would be accessed by
OP AC,2*k-2*n(SP) ;(SP='16)
and the j'th (of m) "arithmetic" -- i.e., not value string -- parameter
would be accessed by
OP AC,j-m-1(P) ;(P='17)
Non-SIMPLE procedures use rF (register '12) as a base for addressing
parameters and recursive locals. Thus the j'th parameter would be accessed
by
OP AC,j-m-2(rF)
or, in the case of a string, by
MOVE ACX,2(rF) ;points at top of
;string stack when
;proc was entered
OP ACY,2*k-2*m(ACX)
Similarly, recursive locals are addressed using positive displacements from
rF.
An up-level reference to a procedure's parent is made by executing the
instruction
HRRZ AC,1(rF) ;now AC points at
;stack frame of parent
and then using AC in the place of rF in the access sequences above,
iterating the process if need be to get at one's grandparent, or some more
distant lexical ancestor.
NOTE: When Sail compiled code needs to make such an up-level reference it
keeps track of any intermediate registers (called "display" registers) that
may have been loaded. Thus, if you use several up-level references
together, you only pay once for setting up the "display", unless some
intervening procedure call or the like should cause Sail to forget whatever
was in its accumulators. Note here that if a display register is thrown
ACTIONS IN THE PROLOGUE FOR NON-SIMPLE PROCEDURES
away, there is no attempt to save its value. At some future date this may
be done. It was felt, however, that the minimal (usually zero) gain in
speed was just not worth the extra hair that this would entail.
ACTIONS IN THE PROLOGUE FOR NON-SIMPLE PROCEDURES
The algorithm given here is that for a recursive procedure being declared
inside another procedure. The examples show how it is simplified when
possible.
1. Pick up proc descriptor address.
2. Push old rF onto the stack.
3. Calculate static link. (a). Must loop back through the static links
to grab it. (b). once calculated put together with the PDA and
put it on the stack.
4. Push current rSP onto the stack.
5. Increment stack past locals & check for overflow.
6. Zero out whatever you have to.
7. Set rF to point at the MSCP.
EXAMPLES:
1. A non-recursive entry (note: in this section only cases where F is needed
are considered).
PUSH P,rF ;SAVE DYNAMIC LINK
SKIPA AC,rF
MOVE AC,1(AC) ;GO UP STATIC LINK
HLRZ TEMP,1(AC) ;LOOK AT PDA IN STACK
CAIE TEMP,PPDA ;IS IT THE SAME AS PARENTS
JRST .-3 ;NO
HRLI AC,PDA ;PICK UP PROC DESC
PUSH P,AC ;SAVE STATIC LINK
PUSH P,SP
HRRZI rF,-2(P) ;NEW RF
In the case that the procedure is declared in the outer block we don't need
to worry about the static link and the prologue can look like
PUSH P,rF ;SAVE DYNAMIC LINK
PUSH P,[XWD PDA,0] ;STATIC LINK WORD
PUSH P,SP ;SAVE STRING STACK
HRRZI rF,-2(P) ;NEW F REGISTER
2. Recursive entry -- i.e one with locals in the stack.
ACTIONS AT THE EPILOGUE FOR NON-SIMPLE PROCEDURES
PUSH P,rF ;SAVE DYNAMIC LINK
SKIPA AC,rF
MOVE AC,1(AC) ;GO UP STATIC LINK
HLRZ TEMP,(AC) ;LOOK AT PDA IN STACK
CAIE TEMP,PPDA ;IS IT THE SAME AS PARENTS
JRST .-3 ;NO
HRLI AC,PDA ;PICK UP PROC DESC
PUSH P,AC ;SAVE STATIC LINK
PUSH P,SP
HRLZI TEMP,1(P) ;
HRRI TEMP,2(P) ;
ADD P,[XWD locals, locals] ;create space for
CAIL P,0 ;arith locals
<trigger pdl ov error>
SETZM -1(TEMP) ;zero out locals
BLT TEMP,(P) ;
HRLZI TEMP,1(SP)
HRRI TEMP,2(SP)
ADD SP,[XWD 2* string locals,2* string locals]
CAIL SP,0 ;check for pdl ov
<cause pdl ov error>
SETZM -1(TEMP)
BLT TEMP,(SP) ;zero out string locals
HRRZI rF,- locals-3(P)
The BLT of zeros is replaced by repeated pushes of zero if there are only a
few locals. Again, the loop is replaced by a simple push if the procedure
is declared in the outer block.
ACTIONS AT THE EPILOGUE FOR NON-SIMPLE PROCEDURES
1. If returning a value, set it into 1 or onto right spot in
the string stack.
2. Do any deallocations that need to be made.
4. Restore rF.
5. Roll back stack.
6. Return either via POPJ P, or by JRST @mumble(P)
EXAMPLES:
1. No parameters.
PROCEDURE DESCRIPTORS
<step 1>
<step 2>
MOVE rF,(rF)
SUB P,[XWD M+3,M+3] ;M= # LOCAL VARS
POPJ P,
2. n string parameters, m other parameters, k string locals on stack, j
other locals on stack.
<step 1>
<step 2>
MOVE rF,(rF)
SUB SP,[XWD 2*k+2*n,2*k+2*n]
SUB P,[XWD j+m+3,j+m+3] ;POP STACK
JRST @m+1(P)
SIMPLE procedures are similar, except that rF is never changed.
PROCEDURE DESCRIPTORS
Procedure descriptors are used by the storage allocation system, the
interpretive caller, BAIL, and various other parts of Sail. They are not
put out for SIMPLE procedures. The entries are shown as they are at the
present time. No promise is made that they will not be different tomorrow.
If you do not understand this page, do not worry too much about it.
-1: link for pd list
0: entry address
1: word1 of string for proc name
2: word2 of string for proc name
3: type info for procedure,,sprout defaults
4: # string params*2,,# arith params+1
5: + ss displ,, + as displ
6: lexic lev,,local var info
7: display level,,proc param stuff
10: pda,,0
11: pcnt at end of mksemt,,parent's pda
12: pcnt at prdec,,loc for jrst exit
13: type info for first argument,,0 (or default value)
:
type info for last argument,,0 (or default value)
lvi: byte (4)type(9)lexical-level(23)location
:
:
The type codes in the lvi (local variable info) block are as follows:
type = 0 end of procedure area
type = 1 arith array
type = 2 string array
type = 3 set or list
type = 4 set or list array
type = 5 foreach search control block
type = 6 list of all processes dependent on
this block.
type = 7 context
type = 10 a cleanup to be executed
type = 11 record pointer
type = 12 record pointer array
type = 17 block boundary. Location gives base
location of parents block's information.
local variable info for each block is organized as
info for var
:
info for var
17,lev,loc of parent block bbw
REFERENCES
BBNEXEC Bolt Beranek and Newman, TENEX Executive Manual, Cambridge,
Massachusetts, April 1973.
Feldman J.A. Feldman and P.D. Rovner, An Algol-Based Associative
Language, CACM 12, 8 (August 1969), 439-449.
J.A. Feldman, J.R. Low, D.C. Swinehart, and R.H. Taylor, Recent
Developments in SAIL, AFIPS FJCC 1972, 1193-1202.
Frost M. Frost, UUO Manual (Second Edition), Stanford Artificial
Intelligence Laboratory Operating Note 55.4 (July 1975).
Harvey B. Harvey (M. Frost, ed.), Monitor Command Manual, Stanford
Artificial Intelligence Laboratory Operating Note 54.5 (January
1976).
JSYS Bolt, Beranek, and Newman, TENEX JSYS Manual, Cambridge,
Massachusetts, September 1973.
vanLehn K. vanLehn, SAIL, SAILON 57.3, (June 1973).
MonCom [Harvey], [BBNEXEC], [OSCMA]
Nauer P. Nauer (ed.), Revised Report on the Algorithmic Language
ALGOL-60, CACM 6 (1963) 1-17.
OSCMA decsystem10 Operating System Commands Manual DEC-10-OSCMA-A-D,
Digital Equipment Corporation, Maynard, Massachusetts, May
1974.
Petit P. Petit (R. Finkel, ed.), RAID Manual, SAILON 58.2, (March
1975).
Reiser J.F. Reiser, BAIL--A Debugger for SAIL, Stanford Artificial
Intelligence Laboratory Memo AIM-270, Computer Science
Department Report STAN-CS-75-523, October 1975.
Savitzky S.R. Savitzky (L. Earnest, ed.) Son of Stopgap, SAILON 50.3,
March 1971.
SmithN N. Smith, Sail Tutorial, Stanford Aritifical Intelligence
Laboratory Memo AIM-290, Computer Science Department Report
STAN-CS-76-575, August 1976.
SmithR R. Smith, TENEX SAIL, Institute for Mathematical Studies in the
Social Sciences T.R. 248, Stanford University, January 1975.
Swinehart & SproullD.C. Swinehart and R.F. Sproull, SAIL, SAILON 57.2,
(January 1971).
SysCall [Frost], [JSYS], [TopHand]
TopHand decsystem10 Assembly Language Handbook DEC-10-NRZC-D, Digital
Equipment Corporation, Maynard, Massachusetts, 1973.
INDEX
(AND) 51
(NOT) 51
in substrings 54
, in list REMOVEs 162
(INTERSECTION) 179
(UNION) 179
(OR) 50
!!GO 256
!!GSTEP 256
!!STEP 256
% (integer or real division) 52
& (CONCATENATION), of strings 53
&, of lists 180
-, of sets 179
/ (real division) 52
<>= (RELATIONS) 51
?, Foreach itemvars 167
?, in Binding Booleans 165
?, Matching procedure formals 172
(intersection) 176
(union) 176
(EQV) 264
!ERRJ! 246
!ERRP! 246
!SKIP! 53, 63, 80, 81, 82, 89, 93, 129, 130, 131, 132, 133, 134, 136, 137,
138, 139, 140, 145, 148, 149, 262
$CLASS 121
$REC$ 122
$RECFN 122
$RECGC 120
$SPCAR 124
ABS 54
ACCESS 59
ACOS 95
ADJSP 237
AFTER 159, 162
algebraic variables 15
<algebraic!expression> 42
ALL 159, 162
allocation of variables and arrays 21
AND 51, 160, 264
ANSWER 200, 225
ANY 179
ANY!CLASS 118
ANY, in Binding Boolean 165
ANY, in Derived Sets 166
ANY, in Erase statement 164
ANY, in Foreach 170
APPLY 205
<apply!construct> 203
ARG!LIST 204
<arg!list!specifier> 203
ARGS 253
Array element designation 227
<array!declaration> 9
<array!list> 9
<array!type> 152
Arrays, allocation 21
Arrays, as parameters 17
Arrays, declaration 16
Arrays, initialization and reinitialization 22
Arrays, outer block 13, 16
Arrays, OWN 16
Arrays, PRELOADed 16
Arrays, SAFE declaration 16
Arrays, storage convention 16
ARRBLT 94
ARRCLR 94
ARRINFO 93
ARRTRAN 94
ARRYIN 75, 126
ARRYOUT 76, 126
ASCII 264
ASH 52
ASIN 95
ASKNTC 201, 224
ASND 129
ASSIGN 203
<assign!statement> 203
ASSIGNC 114
<assignc> 105
assignment expressions 49
Assignment statement, semantics 31
<assignment!expression> 42
<assignment!statement> 28
ASSOC 264
ASSOCIATIONS 157
Associations, ERASE 164
Associations, implementation 158
Associations, introduction 151
Associations, MAKE 163
Associations, searching for 164
associative booleans 180
associative context 168
Associative search 164
Associative search, controling hash 165
associative search, relative speeds 171
associative searches, introduction 151
associative store 151, 157
Associative store, searching 164
<associative!statement> 160
ATAN 95
ATAN2 95
ATI 208
attribute 165
AUXCLR 80
AUXCLV 80
<backtracking!statement> 182
Backtracking, introduction 182
BACKUP 80
BAIL 249
BEFORE 159, 162
BIND 165
Binding Boolean 165, 180
Binding Booleans, general considerations 165
<binding!list> 160
BINDIT 179
BINDIT, in Binding Boolean 166
BINDIT, in Derived Sets 166
BINDIT, in Foreach 170
BINDIT, in Foreaches 167
BINDIT, in Matching Procedures 172
BKJFN 130
Block names 6, 247
<block> 5
Boolean Expression <element> 169
<boolean!expression> 42
Boolean, declaration 15
bound 165
Bracketed Triple item 163
Bracketed Triple Item Retrieval 163
Bracketed Triple Item retrieval 166
Bracketed Triple item retrieval, general considerations 165
Bracketed Triple Items, ERASE 164
BREAK 253
BREAKSET 68
BRKERS 220
BRKMAK 220
BRKOFF 220
BUCKETS 165
BUILT!IN 113
Byte pointers, creation 92
CALL 90, 148
CALLER 193
CALLI 90
CASE expressions 49
CASE statement 36
<case!expression> 42
<case!statement> 29
CASEC 111
CAUSE 197
<cause!statement> 196
CAUSE, <options> 197, 277
CAUSE, user defined procedures for 200
CAUSE1 200, 224
Causing events, introduction 196
CFILE 128, 130
character codes 264
CHARIN 130, 146
CHAROUT 130, 146
CHECK!TYPE 112
CHECKED 156, 161
Checked, formal parameters 157
CHECKED, in associative searches 165
Checked, itemvar procedures 157
Checked, type checking 179
CHFDB 130
CHNCDB 95
CHNIOR 80
CHNIOV 80
CHNTAB 213
CLEANUP 23
<cleanup!declaration> 11
CLKMOD 214
CLOSE 66, 126
CLOSF 128, 130
CLOSIN 66, 126
CLOSO 66, 126
CLRBUF 80
CNDIR 148
CODE 89
<code!block> 56
command line 236
<command!line> 233
Comment 6
COMMENTS 229
compile time expressions 108
COMPILER!BANNER 114
COMPILER!SWITCHES 240
<compound!statement> 5
concatenation of lists 180
<cond!comp!statement> 104
conditional compilation 111
Conditional Statements, ambiguity 32
<conditional!expression> 42
<conditional!statement> 28
CONOK 113
Constants, arithmetic 229
Constants, octal 229
Constants, real 229
Constants, string 229
constructive item expressions 178
CONTEXT 183
Context elements 185
<context!declaration> 182
<context!element> 183
CONTINUE statement 38
Conversions, algebraic 46
COORD 253
COP 177, 222
coroutining with RESUMEs 192
COS 95
COSD 95
COSH 95
CPRINT 98
CTLOSW 145
CV6STR 88
CVASC 87
CVASTR 87
CVD 86
CVE 86
CVF 86
CVFIL 93
CVG 86
CVI 158, 219
CVIS 181, 220
CVJFN 131
CVLIST 219
CVMS 109, 110
CVN 158, 219
CVO 86
CVOS 86
CVPS 109
CVS 86
CVSET 219
CVSI 181, 221
CVSIX 88
CVSTR 87
CVXSTR 88
DATUM 154, 161, 226, 227
DATUM, type checking 179
DDT 246, 254
deallocation of variables and arrays 21
DECLARATION (a function) 112
<declaration> 8, 152
default parameters 18
DEFINE 103, 106, 110, 113, 254
<define> 103
DEFPRI 188
DEFPSS 188
DEFQNT 188
DEFSSS 188
DEL!PNAME 181, 221
DELETE 160, 162
DELF 131
delimited strings 107
delimited!anything 112
delimited!expr 112
Delimiters 105
DELIMITERS 105
DELIMITERS, NULL 106
Delimiters, null 106
DELNF 131
DEPENDENTS 189
Derived sets 180
Derived Sets, general considerations 165
<derived!set> 176
DEVST 131
DEVTYPE 132
DFCPKT 225
DFR1IN 208
DFRINT 208
DIRST 148
DISABLE 209
DIV 52
DO statement 35
<do!statement> 29
DOC 104
DONE statement 37
DONTSAVE 197, 277
DPB 92
DRYROT 231, 243
DSKIN 132
DSKOP 129
DSKOUT 132
DTI 208
DVCHR 132
EDFILE 91
EIR 213
<element!list> 160
<element> 160
<element>, Foreach 167
ELSE 28, 42
ELSEC 104
ENABLE 209
ENDC 104
ENTER 67, 126
ENTRY specification 25
EQU 88
EQV 52, 264
ERASE 164
ERASE, in a Foreach 171
ERENAME 68
ERMSBF 91
error messages 243
error procedures 245
ERROR!MODES 244
ERSTR 132
EVALDEFINE 114
EVALREDEFINE 114
event notices 196
Event type items, datums of 199
event types 196
<event!statement> 196
EVENT!TYPE 198, 277
Events, introduction 196
EXP 96
EXPR!TYPE 113
<expression> 42
EXTERNAL declaration 12, 26
EXTERNAL procedures 20, 25
FAIL 161, 172, 190
FALSE, definition 229
FILEINFO 93
FIRST 164, 222
fix (convert real to integer) 46
FIXR 47, 237
float (convert integer to real) 47
FLTR 47, 237
FOR (substringer) 44, 53
FOR statement 34
<for!statement> 28
FORC 111
FOREACH 160
Foreach <element>, Boolean Expression 169
Foreach <element>, List membership 168
Foreach <element>, Retrieval Triple 170
Foreach <element>, Set membership 168
Foreach <element>s 167
Foreach itemvars 167
Foreach searches, relative speeds 171
<foreach!statement> 160
FOREACH, execution of 168
FOREACH, general considerations 165
FOREACH, increase speed of 165
FOREACH, main discussion of 166
Foreach, Matching Procedure <element> 172
Foreach, satisfiers 168
FORGET 182, 185
FORLC 111
formal parameters, Leap 157
formals 17
FORTRAN procedures 20, 26, 40
FORTRAN, actual parameters 21
FORWARD declaration 12
FORWARD procedures 18
FROM 159
GDSTS 132
generation of symbols using macros 108
Gensym 108
GEQ 264
GETBREAK 71
GETCHAN 66, 126
GETFORMAT 85
GETPRINT 99
GETSTS 76, 126
GJINF 149
GLOBAL 156
GNJFN 133
Go To Statements, restrictions 32
GO TO, into a Foreach 167
<go!to!statement> 28
GOGTAB 90, 256
GTAD 149
GTFDB 133
GTJFN 133
GTJFNL 134
GTRPW 213
GTSTS 134
GTTYP 143
handler procedures, Record!class 122
HELP 254
IBP 92
<id!list> 8
identifiers 228
IDPB 92
IDTIM 149
IF expressions 48
IF statement 32
<if!statement> 28
IFC 111
IFCR 112
ILDB 92
ILL MEM REF 231
ILLEGAL UUO 231
IN 159, 162, 264
IN!CONTEXT 94
INCHRS 80, 146
INCHRW 80, 144, 146
INCHSL 80, 146
INCHWL 80, 146
INDEXFILE 134
INF 264
INIACS 92
initialization 22
INITIALIZATION 24
inner block 6
INOUT 76, 127
INPUT 72, 127, 146
INSTR 81, 146
INSTRL 81, 146
INSTRS 81, 146
INT...!APR 275
INT...!INX 273
integer constants 229
Integers, range 15
INTER 264
INTERNAL declaration 12, 26
INTERNAL procedures 20
INTERROGATE 198
<interrogate!construct> 196
INTERROGATE, <options> 198, 277
INTERROGATE, set form of 198
INTERROGATE, user defined procedures for 201
Interrupt codes 273
INTIN 77, 127, 146
INTMAP 209
INTPRO 217
INTRPT 191, 214
INTSCAN 79
INTSET 210
INTTBL 210
INTTY 145, 146
IRUN 193, 276
ISTRIPLE 222
ITEM 154
item booleans 180
<item!expression> 175
<item!primary> 175
ITEM!START 157
<item!type> 152
Item, <typed!item!expression> 226
Items & Itemvars, distinction between 155
Items, ANY 179
Items, BINDIT 179
Items, Bracketed Triple 163
items, creation of 154
Items, Datums of 154
Items, declared 154
Items, DELETE 162
Items, implementation 157
Items, internal & external 154
Items, internal &external 157
Items, introduction 151
Items, NEW 178
Items, Pnames 181
Items, props of 181
Items, scope 154
Items, type checking 179
Items, type of 154
Items, with array datums 154
ITEMVAR 155
<itemvar!type> 152
Itemvars & Items, distinction between 155
Itemvars, CHECKED 156
Itemvars, implementation 158
Itemvars, initialization 156
Itemvars, scope 156
Itemvars, type checking 156, 161
Itemvars, types of 155
JFNS 135
JFNSL 135
JOIN 194
K!OUT 278
K!ZERO 278
KAFIX 47
KIFIX 47, 237
KILLME 193, 276
KPSITIME 215
Label use 13
<label!declaration> 9
Labels, as actual parameters 21
Labels, restrictions 32
LAND 52
LDB 92
leap booleans 180
LEAP!ARRAY 113
<leap!expression> 175
<leap!relational> 177
<leap!statement> 159
Leap, introduction 151
LENGTH 88, 223
LEQ 264
LET 22
letters, legal Sail letters 228
LEVTAB 213
LIBRARY 23
Library, runtime 62
LINOUT 74, 127
LIST 156
list booleans 180
list element designator 227
List element designators 178
list expressions 180
List membership <element> 168
<list!expression> 175
<list!statement> 159
list, sublists 180
Lists, automatic conversion 161
lists, concatenation 180
lists, initialization 180
Lists, PUT 162
Lists,REMOVE 162
LISTX 221
LNOT 52
LOAD!MODULE 23
LOCATION 54
LODED 82
LOG 96
Logical expressions 52
LOOKUP 67, 127
loop block 37
LOP 89, 178, 222
LOR 52
LSH 52
Macro bodies 107
Macro bodies, concatenation in 109
macro body delimiters 105
macro declarations 106
Macro declarations, scope 107
macro parameter delimiters 105
<macro!body> 103
<macro!call> 103
Macros with parameters 110
Macros without parameters 106
MAKE 160, 163
MAKE, in a Foreach 171
Matching Procedures 172
Matching procedures, as processes 190
Matching Procedures, sharing memory 172
MAX 52
MEMORY 54
MESSAGE 114
MESSAGE procedures 156
MIN 52
MKEVTT 196, 219
MOD 52
MTAPE 77, 127
MTOPR 135
MULTIN 201
MYPROC 194
NEEDNEXT 38
NEQ 264
NEW 175, 178
NEW!ITEMS 178
NEW!PNAME 181, 221
NEW!RECORD 119
NEXT statement 38
NIL 180
No one to run 191
NOJOY 200
NOMAC 114
NOPOLL 191
NOT 51, 264
NOTCQ 199
notice queue 197
NOTNOW 193, 277
NOW!SAFE 41
NOW!UNSAFE 41
NULL DELIMITERS 106
null delimiters mode 106
NULL!CONTEXT 184
NULL!RECORD 118, 119
NULL, definition 229
object 165
ODTIM 149
OF 36, 42
OFC 104
OPEN 63, 127
OPENF 136
OPENFILE 136
operator precedence 50
OR 50, 264
OUT 74, 127, 145
OUTCHR 81, 144, 145
outer block 6
OUTSTR 81, 144, 145
OVERFLOW 96
OWN 14
Parameters, default values 18
parametric procedures 21
PBIN 144
PBOUT 144, 145
PBTIN 145, 146
PHI 180
PMAP 149
Pnames 181
PNAMES 181
POINT 92
POLL 191
Polling points 191
POLLING!INTERVAL 191
<preload!specification> 9
PRELOADed arrays 16
PRESET!WITH 17
PRINT 98
Printnames of items 181
PRIORITY 187(X)
PRIORITY(X) 276
PRISET 194
Procedure body, emptiness 13
Procedure Calls, actual parameters 39
Procedure Calls, semantics 39
<procedure!call> 30
<procedure!declaration> 10, 153
<procedure!head> 10
<procedure!type> 153
Procedures, as actual parameters 40
Procedures, assembly language 27
Procedures, declaration 17
Procedures, defaults in declarations 21
procedures, Leap 157
Procedures, parametric 21
Procedures, restrictions 21
Procedures, restrictions on formal parameters 17
Procedures, separately compiled 25
procedures, user error 245
process item 187
process procedure 187
Process procedures, Matching 190
Process procedures, recursive 190
<process!statement> 186
Processes, control of scheduling 190
processes, creation of 187
Processes, dependency of 189
Processes, inside recursive procedures 188
PROCESSES, introduction 186
Processes, resumption of 192
Processes, sharable memory 190
Processes, status of 186
Processes, suspension of 192
Processes, termination of 192
Program name, for DDT 6
PROPS 161, 181, 226, 227
PROTECT!ACS 58
Pseudo-teletype functions 82
PSIDISMS 214
PSIMAP 211
PSIRUNTM 215
PSOUT 144, 145
PSTACK 187(X)
PSTACK(X) 276
PSTATUS 195
PTY... 82
PUT 159, 162
QUANTUM 187(X)
QUANTUM(X) 276
question itemvars 172
QUICK!CODE 57
RAID 246
RAN 96
RCHPTR 137
RDSEG 150
ready 186
READYME 193, 276
real constants 229
REALIN 77, 127, 146
Reals, range 15
REALSCAN 79
RECORD!CLASS 117
RECORD!POINTER 117
RECURSIVE declaration 12
RECURSIVE procedures 19
REDEFINE 107
Reentering programs 242
REF!ITEM 204
<ref!item!construct> 203
REFERENCE 17, 21, 39
Reference items 204
RELBREAK 71
RELD 129
RELEASE 66, 127
REMEMBER 182, 184
REMOVE 159, 162
REMOVE, in Foreach 168
RENAME 67, 127
REPLACE!DELIMITERS 106
REQUIRE 23
REQUIRE - indexed by last word of the require statement 114
<require!specification> 11
REQUIREs, list of 11
RESCHEDULE 197, 198, 277
rescheduling of processes 190
RESERVED 113
Restarting programs 242
RESTORE 182, 184
RESUME 192
RESUME, <options> 276
RESUME, <return item> 192
RETAIN 198, 277
retrieval item expression 179
Retrieval Triple <element> 160, 170
RETURN 54
RETURN statement 36
RFBSZ 138
RFCOC 144
RFMOD 144
RFPTR 138
RGCOFF 120
RLJFN 138
RNAMF 138
ROT 52
RPGSW 241
RTIW 213
RUNME 188, 276
running 186
RUNPRG 150
RUNTM 150
RWDPTR 139
SAFE declaration 12
<safety!statement> 30
SAMEIV 223
satisfier group 168
SAY!WHICH 198, 199, 277
SCAN 73
SCANC 74
SCHEDULE!ON!CLOCK!INTERRUPTS 214
scheduling of processes 190
SCHPTR 137
scope, of variables 14
SDSTS 132
SECOND 164, 222
SEGMENT!FILE 24
SEGMENT!NAME 24
SET 156
set booleans 180
Set expressions 179
Set membership <element> 168
<set!expression> 176
<set!statement> 159
SETBREAK 71
SETC 264
SETCHAN 139
SETCP 200, 223
SETEDIT 143
SETFORMAT 85
SETINPUT 139
SETIP 201, 223
SETLEX 254
SETO 264
SETPL 75, 127
SETPRINT 99
Sets, automatic coercion 161
Sets, Derived Sets 180
Sets, initialization 180
Sets, PUT 162
Sets, REMOVE 162
SETSCOPE 258
SETSTS 76, 127
SFCOC 144
SFMOD 144
SFPTR 138
SHORT 8, 12, 15, 47
SHOW 255
SIMPLE declaration 12
simple expressions 49
SIMPLE procedures 19
<simple!formal!type> 153
<simple!type> 152
SIN 95
SIND 95
SINH 95
SINI 139, 146
SIR 213
SIZEF 140
SNAIL commands 233
SOS representation 265
SOURCE!FILE 23, 114
SPROUT 187
SPROUT DEFAULTS 188
<sprout!default!declaration> 186
SPROUT!DEFAULTS 186
<sprout!statement> 186
SPROUT, <options> 187, 276
SQRT 95
Stanford character set 264
START!CODE 57
START!CODE, calling procedures from 60
<statement> 5
STDBRK 72, 127
STDEV 131
STDIR 148
STEP 28
STEPC 104
STI 144
STIW 213
storage reallocation 242
STPAR 144
String constant, as comment 6
string constants 229
String descriptors 283
STRING!PDL 23
STRING!SPACE 23
String, declaration 15
STRINGSTACK 187(X)
STRINGSTACK(X) 276
STSTS 134
STTYP 143
SUBSR 89
SUBST 89
<substring!spec> 44
Substrings 53
<suc!fail!statement> 161
SUCCEED 161, 172, 190
SUCH THAT 160, 264
SUSPEND 192
suspended 186
SUSPHIM 187, 276
SUSPME 188, 276
SWAP 264
Swap statement 31
<swap!statement> 28
SWDPTR 139
switches, in command lines 237
symbols, automatic generation of 108
<synonym!declaration> 10
SYSTEM!PDL 23
TANH 95
TELLALL 197, 277
TERMINATE 192
terminated 186
TEXT 255
THAT 160
THEN 28, 32, 42
THENC 104
THIRD 164, 222
time sharing with processes 214
TMPIN 79, 127
TMPOUT 79, 127
TO 44, 53
TRACE 255
TRAPS 255
TRIGINI 96
Triple, Binding Boolean 165
<triple> 160
TRIPLES 157
Triples, introduction 151
TRUE, definition 229
TTYIN 81, 146
TTYINL 81, 146
TTYINS 81, 146
TTYUP 81, 146
type checking, itemvars 156
type conversions, algebraic 46
<type!qualifier> 8
typed!item!expression 226
<typed!item!expression> 226
TYPEIT 218
unbound 165
UNBREAK 255
UNDELETE 140
UNION 264
UNSTACK!DELIMITERS 105
UNTIL 28, 35
UNTILC 104
UNTRACE 256
URSCHD 191
USER1 199
USER2 199
USERCON 90
USERERR 91
USETI 77, 127
USETO 77, 127
UUOFIX 47
VALUE 17, 21, 39
value 165
<variable> 226
variables 227
Variables, allocation 21
variables, initialization 22
variables, scope 14
VERSION 24, 25
WAIT 198, 277
wait queue 197
WAITQ 199
WHILE 28
WHILE statement 35
<while!statement> 29
WHILEC 111
WORDIN 75, 127
WORDOUT 76, 127
XOR 52, 264