Trailing-Edge
-
PDP-10 Archives
-
decuslib20-01
-
decus/20-0002/sail.jfr
Click decus/20-0002/sail.jfr to
see without markup as text/plain
There is 1 other file named sail.jfr in the archive. Click here to see a list.
COMMENT VALID 00294 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00022 00002 Stanford Artificial Intelligence Laboratory August 1976
C00025 00003 PREFACE HISTORY OF THE LANGUAGE LEARNING ABOUT SAIL CHANGES IN THE LANGUAGE OPERATING SYSTEMS UNIMPLEMENTED CONSTRUCTS ACKNOWLEDGEMENTS
C00032 00004 TABLE OF CONTENTS
C00051 00005 PROGRAMS AND BLOCKS Syntax
C00053 00006 Semantics DECLARATIONS STATEMENTS BLOCK NAMES
C00057 00007 EXAMPLES
C00058 00008 ALGOL DECLARATIONS Syntax
C00060 00009
C00062 00010
C00064 00011 Restrictions
C00066 00012
C00067 00013 Examples
C00070 00014 Semantics SCOPE OF DECLARATIONS TYPE QUALIFIERS
C00073 00015 NUMERIC DECLARATIONS STRING DECLARATIONS ARRAY DECLARATIONS
C00078 00016 PRELOAD SPECIFICATIONS
C00084 00017 PROCEDURE DECLARATIONS FORMAL PARAMETERS
C00088 00018 DEFAULT PARAMETER VALUES FORWARD PROCEDURE DECLARATIONS
C00093 00019 RECURSIVE PROCEDURES SIMPLE PROCEDURES
C00097 00020 EXTERNAL PROCEDURES
C00102 00021 PARAMETRIC PROCEDURES DEFAULTS IN PROCEDURE DECLARATIONS RESTRICTIONS ON PROCEDURE DECLARATIONS ALLOCATION AND DEALLOCATION
C00106 00022 INITIALIZATION AND REINITIALIZATION SYNONYMS
C00110 00023 CLEANUP DECLARATIONS REQUIREMENTS
C00114 00024
C00119 00025 Separately Compiled Procedures
C00124 00026 FORTRAN PROCEDURES
C00129 00027 ASSEMBLY LANGUAGE PROCEDURES
C00132 00028 ALGOL STATEMENTS Syntax
C00134 00029
C00136 00030 Semantics
C00138 00031 ASSIGNMENT STATEMENTS SWAP ASSIGNMENT CONDITIONAL STATEMENTS
C00142 00032 AMBIGUITY IN CONDITIONAL STATEMENTS GO TO STATEMENTS
C00145 00033
C00149 00034 FOR STATEMENTS
C00152 00035 WHILE STATEMENT DO STATEMENT
C00155 00036 CASE STATEMENTS RETURN STATEMENT
C00159 00037 DONE STATEMENT
C00163 00038 NEXT STATEMENT CONTINUE STATEMENT
C00167 00039 PROCEDURE STATEMENTS
C00171 00040 PROCEDURES AS ACTUAL PARAMETERS FORTRAN PROCEDURES
C00176 00041 NOWSAFE and NOWUNSAFE
C00178 00042 ALGOL EXPRESSIONS Syntax
C00180 00043
C00182 00044
C00184 00045 Type Conversion
C00186 00046
C00190 00047
C00194 00048 Semantics CONDITIONAL EXPRESSIONS
C00197 00049 ASSIGNMENT EXPESSIONS CASE EXPRESSIONS SIMPLE EXPRESSIONS
C00201 00050 PRECEDENCE OF ALGEBRAIC OPERATORS EXPRESSION EVALUATION RULES OR
C00205 00051 AND NOT <>= RELATIONS
C00210 00052 MAX MIN +- ADDITION SUBTRACTION LAND LOR XOR EQV LNOT */% MULTIPLICATION DIVISION DIV MOD ASH LSH ROT
C00214 00053 & CONCATENATION ^ EXPONENTIATION SUBSTRINGS
C00219 00054 INFINITY SPECIAL LENGTH OPERATOR FUNCTION DESIGNATORS UNARY OPERATORS MEMORY AND LOCATION
C00223 00055 LOCATION
C00225 00056 ASSEMBLY LANGUAGE STATEMENTS Syntax
C00227 00057 Semantics DECLARATIONS IN CODE BLOCKS
C00230 00058 PROTECT ACS DECLARATION OPCODES THE <simple addr> FIELD
C00235 00059 STARTCODE VERSUS QUICKCODE
C00240 00060 ACCUMULATOR USAGE IN CODE BLOCKS CALLING PROCEDURES FROM INSIDE CODE BLOCKS
C00244 00061 BEWARE
C00246 00062 INPUT/OUTPUT ROUTINES Execution-time Routines in General SCOPE NOTATIONAL CONVENTIONS
C00250 00063 SKIP I/O Channels and Files OPEN
C00254 00064
C00259 00065
C00263 00066 CLOSE, CLOSIN, CLOSO GETCHAN RELEASE
C00267 00067 LOOKUP, ENTER RENAME
C00271 00068 ERENAME Break Characters BREAKSET
C00275 00069
C00279 00070
C00283 00071 SETBREAK GETBREAK, RELBREAK
C00286 00072 STDBRK I/O Routines INPUT
C00290 00073 SCAN
C00293 00074 SCANC OUT LINOUT
C00297 00075 SETPL WORDIN WARNING ABOUT DUMP MODE IO ARRYIN
C00301 00076 WORDOUT ARRYOUT INOUT GETSTS, SETSTS
C00304 00077 MTAPE USETI, USETO REALIN, INTIN
C00307 00078
C00310 00079 REALSCAN, INTSCAN TMPIN, TMPOUT
C00314 00080 AUXCLR, AUXCLV CHNIOR, CHNIOV TTY and PTY Routines TELETYPE I/O ROUTINES BACKUP CLRBUF INCHRS INCHRW INCHSL INCHWL
C00317 00081 INSTR INSTRL INSTRS IONEOU OUTCHR OUTSTR TTYIN TTYINL TTYINS TTYUP
C00321 00082 PSEUDO-TELETYPE FUNCTIONS LODED PTYALL PTCHRS PTCHRW PTOCHS PTOCHW PTOCNT PTIFRE PTOSTR PTYGET PTYGTL PTYIN
C00324 00083 PTYREL PTYSTL PTYSTR Example of TOPS-10 I/O
C00328 00084
C00331 00085 EXECUTION TIME ROUTINES Type Conversion Routines SETFORMAT GETFORMAT
C00334 00086 CVS CVD CVOS CVO CVE, CVF, CVG
C00337 00087 CVASC, CVASTR, CVSTR
C00341 00088 CV6STR, CVSIX, CVXSTR String Manipulation Routines EQU LENGTH
C00344 00089 LOP SUBSR, SUBST Liberation-from-Sail Routines CODE
C00347 00090 CALL CALLI USERCON GOGTAB
C00351 00091 USERERR ERMSBF EDFILE
C00355 00092 INIACS Byte Manipulation Routines LDB, DPB, etc. POINT
C00358 00093 Other Useful Routines CVFIL FILEINFO ARRINFO
C00361 00094 ARRBLT ARRTRAN ARRCLR INCONTEXT
C00364 00095 CHNCDB Numerical Routines SIN COS SIND COSD ASIN ACOS ATAN ATAN2 SINH COSH TANH SQRT
C00368 00096 RAN LOG EXP TRIGINI OVERFLOW ENTRY POINTS OVERFLOW IMPLEMENTATION
C00373 00097
C00375 00098 PRINT Syntax Semantics DEFAULT FORMATS
C00378 00099 DESTINATIONS SETPRINT, GETPRINT
C00382 00100 SIMPLE USE CAVEATS
C00386 00101 FOR WIZARDS ONLY
C00389 00102
C00390 00103 MACROS AND CONDITIONAL COMPILATION Syntax
C00392 00104
C00394 00105 Delimiters
C00398 00106 Macros
C00402 00107 SCOPE MACRO BODIES DELIMITED STRINGS
C00406 00108 INTEGER COMPILE TIME EXPRESSIONS STRING COMPILE TIME EXPRESSIONS
C00410 00109 HYBRID MACRO BODIES
C00413 00110 Macros with Parameters
C00417 00111 Conditional Compilation
C00421 00112 Type Determination at Compile Time DECLARATION CHECKTYPE EXPRTYPE
C00425 00113 BUILTIN LEAPARRAY RESERVED DEFINE CONOK
C00429 00114 Miscelaneous Features COMPILE TIME I/O EVALDEFINE, EVALREDEFINE ASSIGNC NOMAC COMPILERBANNER
C00434 00115 Hints
C00437 00116
C00440 00117 RECORD STRUCTURES Introduction Declaration Syntax Declaration Semantics
C00444 00118
C00448 00119 Allocation Fields
C00451 00120 Garbage Collection Internal Representations
C00455 00121 $CLASS RECRNG HNDLER RECSIZ TYPARR TXTARR
C00457 00122 Handler Procedures
C00460 00123
C00463 00124 More about Garbage Collection
C00466 00125
C00469 00126 TENEX ROUTINES Introduction TOPS-10 Style Input/Output
C00472 00127
C00475 00128 MAGTAPE I/O TENEX Style Input/Output OBTAINING ACCESS
C00479 00129 DATA TRANSFER RANDOM I/O ERROR HANDLING DIRECT DSK OPERATIONS ASND, RELD
C00483 00130 BKJFN CFILE CHARIN CHAROUT CHFDB CLOSF
C00486 00131 CVJFN DELF DELNF DEVST, STDEV
C00489 00132 DEVTYPE DSKIN, DSKOUT DVCHR ERSTR GDSTS, SDSTS
C00492 00133 GNJFN GTFDB GTJFN
C00495 00134 GTJFNL GTSTS, STSTS INDEXFILE
C00498 00135 JFNS JFNSL MTOPR
C00501 00136 OPENF OPENFILE
C00504 00137 RCHPTR, SCHPTR
C00507 00138 RFBSZ RFPTR, SFPTR RLJFN RNAMF
C00510 00139 RWDPTR, SWDPTR SETCHAN SETINPUT SINI
C00514 00140 SIZEF UNDELETE Terminal Handling THE TERMINAL AS A DEVICE
C00518 00141
C00523 00142
C00526 00143 SETEDIT TERMINAL MODE FUNCTIONS GTTYP, STTYP
C00529 00144 RFCOC, SFCOC RFMOD, SFMOD STPAR STI DATA TRANSFER
C00532 00145 INTTY PBTIN SUPPRESSING OUTPUT
C00536 00146
C00539 00147
C00541 00148 Utility TENEX System Calls CALL CNDIR DIRST, STDIR
C00544 00149 GJINF GTAD IDTIM, ODTIM PMAP
C00547 00150 RDSEG RUNPRG RUNTIM
C00550 00151 LEAP DATA TYPES Introduction
C00554 00152 Syntax
C00556 00153
C00558 00154 Semantics ITEM GENESIS SCOPE OF ITEMS DATUMS OF ITEMS
C00563 00155 ITEMVARS
C00568 00156 SETS AND LISTS
C00573 00157 ASSOCIATIONS PROCEDURES IMPLEMENTATION
C00578 00158
C00581 00159 LEAP STATEMENTS Syntax
C00583 00160
C00585 00161 Restrictions Semantics ASSIGNMENT STATEMENTS
C00589 00162 PUT REMOVE DELETE
C00594 00163 MAKE BRACKETED TRIPLE ITEMS
C00598 00164 ERASE Searching the Associative Store
C00602 00165 BINDING BOOLEANS
C00607 00166 DERIVED SETS BRACKETED TRIPLE ITEM RETRIEVAL THE FOREACH STATEMENT
C00611 00167
C00615 00168 THE LIST MEMBERSHIP <ELEMENT>
C00620 00169 THE BOOLEAN EXPRESSION <ELEMENT>
C00624 00170 THE RETRIEVAL TRIPLE <ELEMENT>
C00629 00171
C00633 00172 THE MATCHING PROCEDURE <ELEMENT>
C00639 00173
C00643 00174
C00644 00175 LEAP EXPRESSIONS Syntax
C00646 00176
C00648 00177 Semantics ITEM EXPRESSIONS
C00650 00178 NEW
C00655 00179 ANY BINDIT TYPES AGAIN SET AND LIST EXPRESSIONS
C00659 00180 DERIVED SETS BOOLEANS
C00663 00181 PNAMES PROPS
C00667 00182 BACKTRACKING Introduction Syntax
C00669 00183 Semantics THE CONTEXT DATA TYPE
C00672 00184 REMEMBER RESTORE
C00676 00185 FORGET INCONTEXT CONTEXT ELEMENTS
C00680 00186 PROCESSES Introduction Syntax Semantics STATUS OF A PROCESS
C00683 00187 SPROUTING A PROCESS
C00687 00188 SPROUTDEFAULTS MEMORY ACCESSIBLE TO A PROCESS
C00691 00189
C00695 00190 SPROUT APPLY SPROUTING MATCHING PROCEDURES SCHEDULING
C00701 00191 POLLING POINTS
C00706 00192 Process Runtimes TERMINATE SUSPEND RESUME
C00710 00193 CALLER
C00714 00194 DDFINT JOIN MYPROC PRISET
C00717 00195 PSTATUS URSCHD
C00721 00196 EVENTS Syntax Introduction
C00724 00197 Sail-defined Cause and Interrogate THE CAUSE STATEMENT
C00728 00198 THE INTERROGATE CONSTRUCT - SIMPLE FORM THE INTERROGATE CONSTRUCT - SET FORM
C00733 00199 User-defined Cause and Interrogate EVENT TYPE DATA STRUCTURE
C00737 00200 USER CAUSE PROCEDURES
C00741 00201 USER INTERROGATE PROCEDURES
C00745 00202
C00746 00203 PROCEDURE VARIABLES Syntax Semantics ASSIGN
C00749 00204 REFITEM ARGLIST
C00753 00205 APPLY
C00758 00206
C00761 00207 INTERRUPTS Introduction
C00766 00208 Interrupt Routines ATI, DTI DFR1IN DFRINT
C00769 00209 DISABLE, ENABLE INTMAP
C00773 00210 INTSET INTTBL
C00776 00211 PSIMAP Immediate Interrupts IN SUAI Sail
C00779 00212 IN TOPS-10
C00782 00213 IN TENEX Sail GTRPW RTIW, STIW
C00785 00214 Clock Interrupts PSIDISMS
C00789 00215 PSIRUNTM KPSITIME Deferred Interrupts
C00793 00216
C00796 00217 THE DEFERRED INTERRUPT PROCESS - INTPRO
C00798 00218 LEAP RUNTIMES Types and Type Conversion TYPEIT
C00801 00219 CVSET CVLIST CVN and CVI MKEVTT
C00804 00220 Make and Erase Breakpoints BRKERS, BRKMAK, BRKOFF Pname Runtimes CVIS
C00807 00221 CVSI DELPNAME NEWPNAME Other Useful Runtimes LISTX
C00810 00222 FIRST, SECOND, THIRD ISTRIPLE LOP COP
C00813 00223 LENGTH SAMEIV Runtimes for User Cause and Interrogate Procedures SETCP AND SETIP
C00817 00224 CAUSE1 ASKNTC
C00821 00225 ANSWER DFCPKT
C00824 00226 BASIC CONSTRUCTS Syntax
C00827 00227 Semantics VARIABLES DATUMS PROPS
C00832 00228 RECORD FIELDS IDENTIFIERS
C00837 00229 ARITHMETIC CONSTANTS STRING CONSTANTS COMMENTS
C00841 00230
C00842 00231 USING SAIL For TOPS-10 Beginners
C00846 00232 For TENEX Beginners The Complete use of Sail
C00850 00233 Compiling Sail Programs COMMAND LINE SYNTAX TOPS-10 COMMAND LINE SYNTAX
C00853 00234
C00855 00235 TENEX SAIL COMMAND LINE SYNTAX
C00857 00236 COMMAND LINE SEMANTICS
C00862 00237 SWITCHES
C00866 00238
C00871 00239
C00876 00240
C00880 00241 Loading Sail Programs Starting Sail Programs STARTING THE PROGRAM IN RPG MODE
C00885 00242 Storage Reallocation with REEnter
C00887 00243 DEBUGGING SAIL PROGRAMS Error Messages
C00891 00244 ERROR MODES
C00896 00245 STOPPING RUNAWAY COMPILATIONS EXECUTION TIME ERROR MESSAGES USER ERROR PROCEDURES
C00900 00246 Debugging
C00905 00247 SYMBOLS BLOCKS SAIL GENERATED SYMBOLS
C00909 00248 WARNINGS
C00913 00249 BAIL
C00919 00250 COMPILE-TIME ACTION
C00924 00251
C00929 00252 RUN-TIME ACTION DEBUGGING REQUESTS
C00934 00253 ARGS BREAK COORD
C00937 00254 DDT DEFINE HELP SETLEX
C00940 00255 SHOW TEXT TRACE TRAPS UNBREAK
C00943 00256 UNTRACE !!GO !!GSTEP !!STEP GOGTAB
C00946 00257 STRING TYPEOUT BLOCK STRUCTURE BAIL and DDT
C00950 00258 WARNINGS !!GOTO !!UP (LEVEL) !!QUERY SETSCOPE
C00955 00259 RESOURCES USED EXAMPLE
C00957 00260
C00959 00261
C00961 00262 CURRENT STATUS
C00963 00263
C00967 00264 APPENDICES Characters Stanford (SUAI) Character Set
C00970 00265
C00974 00266
C00975 00267 Sail Reserved Words
C00978 00268
C00981 00269
C00982 00270 Sail Predeclared Identifiers
C00985 00271 SUAI ONLY TOPS-10 ONLY CMU ONLY TYMSHARE ONLY TENEX ONLY
C00987 00272
C00988 00273 Indices for Interrupts
C00992 00274
C00994 00275
C00996 00276 Bit Names for Process Constructs SPROUT OPTIONS RESUME OPTIONS
C01000 00277 CAUSE OPTIONS INTERROGATE OPTIONS
C01003 00278 Statement Counter System GENERAL DISCUSSION HOW TO GET COUNTERS PROFILE PROGRAM
C01008 00279 SAMPLE RUN
C01012 00280
C01013 00281 Array Implementation
C01016 00282
C01018 00283 String Implementation STRING DESCRIPTORS INEXHAUSTIBLE STRING SPACE
C01022 00284
C01024 00285 Save/Continue
C01027 00286
C01029 00287 Procedure Implementation STACK FRAME
C01033 00288 ACCESSING THINGS ON THE STACK
C01037 00289 ACTIONS IN THE PROLOGUE FOR NON-SIMPLE PROCEDURES
C01040 00290 ACTIONS AT THE EPILOGUE FOR NON-SIMPLE PROCEDURES
C01043 00291 PROCEDURE DESCRIPTORS
C01046 00292
C01048 00293 REFERENCES
C01052 00294 INDEX
C01092 ENDMK
C;
Stanford Artificial Intelligence Laboratory August 1976
Memo AIM-289 Sailon 57.4
Computer Science Department
Report No. STAN-CS-76-574
SAIL
edited by
John F. Reiser
ABSTRACT
Sail is a high-level programming language for the PDP-10 computer. It
includes an extended ALGOL 60 compiler and a companion set of
execution-time routines. In addition to ALGOL, the language features: (1)
flexible linking to hand-coded machine language algorithms, (2) complete
access to the PDP-10 I/O facilities, (3) a complete system of compile-time
arithmetic and logic as well as a flexible macro system, (4) a high-level
debugger, (5) records and references, (6) sets and lists, (7) an
associative data structure, (8) independent processes (9) procedure
varaiables, (10) user modifiable error handling, (11) backtracking, and
(12) interrupt facilities.
This manual describes the Sail language and the execution-time routines
for the typical Sail user: a non-novice programmer with some knowledge of
ALGOL. It lies somewhere between being a tutorial and a reference manual.
This manual was supported by the Advanced Research Projects Agency under
Contract MDA 903-76-C-0206.
The views and conclusions contained in this document are those of the
author(s) and should not be interpreted as necessarily representing the
official policies, either expressed or implied, of Stanford University,
ARPA, NSF, or the United States Government.
We thank Bernard A. Goldhirsch and the Institute for Advancement of
Sailing for their kind permission to use the cover design of the August
1976 issue of SAIL magazine.
Reproduced in the U.S.A. Available from the National Technical
Information Service, Springfield, Virginia 22161.
PREFACE HISTORY OF THE LANGUAGE LEARNING ABOUT SAIL CHANGES IN THE LANGUAGE OPERATING SYSTEMS UNIMPLEMENTED CONSTRUCTS ACKNOWLEDGEMENTS
PREFACE
HISTORY OF THE LANGUAGE
The GOGOL III compiler, developed principally by Dan Swinehart at the
Stanford Artificial Intelligence Project, was the basis for the non-LEAP
portions of SAIL. Robert Sproull joined Swinehart in incorporating the
features of LEAP The first version of the language was released in November,
1969. SAIL's intermediate development was the responsibility of Russell
Taylor, Jim Low, and Hanan Samet, who introduced processes, procedure
variables, interrupts, contexts, matching procedures, a new macro system,
and other features. Most recently John Reiser, Robert Smith, and Russell
Taylor maintained and extended SAIL. They added a high-level debugger,
conversion to TENEX, a print statement, and records and references.
LEARNING ABOUT SAIL
A novice programmer (or one who is unfamiliar with ALGOL) should start with
the Sail Tutorial [SmithN]. An experienced programmer with a knowledge of
ALGOL should be able to use this Sail manual at once. Begin with Appendix
A, Characters; in this manual the symbol "" designates the character with
code '030. For the first reading, a light skim of sections 1, 2, 3, 4, and
8, followed by a careful perusal of subsection 21.1 should be adequate to
familiarize the new user with the differences between ALGOL and SAIL and
allow him to start writing programs in SAIL. The other sections of this
manual are relatively self contained, and can be read when one wants to know
about the features they describe. The exceptions to this rule are sections
12, 13, and 14. These describe the basics of the LEAP and are essential for
understanding of the following sections.
Special effort has gone into making the index more comprehensive than in
previous versions of this manual. Please use it.
CHANGES IN THE LANGUAGE
There are no known incompatibilities at the SAIL source level with the
language described in [vanLehn]. PRINT, BAIL, operation under TENEX, and
records are major additions to the language. Significant revisions to
[vanLehn] or points deserving emphasis are marked by vertical bars in the
margin. This paragraph is so marked, as an example.
OPERATING SYSTEMS
Sail runs under several operating systems. In this manual distinction is
drawn between the operating system at the Stanford Artificial Intelligence
Laboratory (SUAI), the TOPS-10 operating system from Digital Equipment
Corporation, the TENEX operating system from Bolt Beranek and Newman, and
the TYMSHARE operating system. The major distinction is between TENEX and
non-TENEX systems, although the differences between SUAI and TOPS-10 are
also significant. The TOPS-20 operating system from Digital Equipment
Corporation is the same as TENEX as far as Sail is concerned. TENEX users
should substitute "<SAIL>" for "SYS:" wherever the latter appears in a file
name (except when talking to the LOADER).
UNIMPLEMENTED CONSTRUCTS
The following items are described in the manual as if they existed. As the
manual goes to press, they are not implemented.
1. NEW (<contextvariable>). Creates a new item which has a datum that is
a context.
2. Using a <contextvariable> instead of a list of variables in any of the
REMEMBER, FORGET or RESTORE statements.
3. Using in the expression n of REMOVE n FROM list.
4. ANYANYANY searches in Leap (searches where no constraints at all are
placed on the triple returned.)
5. CHECKED itemvars (the dynamic comparison of the datum type of an item to
the datum type of the CHECKED itemvar to which the item is being
assigned.) It is currently the user's responsibility to insure that the
type of the item agrees with the type of the itemvar whenever DATUM is
used.
ACKNOWLEDGEMENTS
Les Earnest and Robert Smith assisted the editor in PUB wizardry and reading
drafts.
TABLE OF CONTENTS
T A B L E O F C O N T E N T S
SECTION PAGE
1 PROGRAMS AND BLOCKS
1 Syntax 5
2 Semantics 6
2 ALGOL DECLARATIONS
1 Syntax 8
2 Restrictions 12
3 Examples 13
4 Semantics 14
5 Separately Compiled Procedures 25
3 ALGOL STATEMENTS
1 Syntax 28
2 Semantics 31
4 ALGOL EXPRESSIONS
1 Syntax 42
2 Type Conversion 45
3 Semantics 48
5 ASSEMBLY LANGUAGE STATEMENTS
1 Syntax 56
2 Semantics 57
6 INPUT/OUTPUT ROUTINES
1 Execution-time Routines in General 62
2 I/O Channels and Files 63
3 Break Characters 68
4 I/O Routines 72
5 TTY and PTY Routines 80
6 Example of TOPS-10 I/O 83
7 EXECUTION TIME ROUTINES
1 Type Conversion Routines 85
2 String Manipulation Routines 88
3 Liberation-from-Sail Routines 89
4 Byte Manipulation Routines 92
5 Other Useful Routines 93
6 Numerical Routines 95
8 PRINT
1 Syntax 98
2 Semantics 98
9 MACROS AND CONDITIONAL COMPILATION
1 Syntax 103
2 Delimiters 105
3 Macros 106
4 Macros with Parameters 110
5 Conditional Compilation 111
6 Type Determination at Compile Time 112
7 Miscelaneous Features 114
8 Hints 115
10 RECORD STRUCTURES
1 Introduction 117
2 Declaration Syntax 117
3 Declaration Semantics 117
4 Allocation 119
5 Fields 119
6 Garbage Collection 120
7 Internal Representations 120
8 Handler Procedures 122
9 More about Garbage Collection 124
11 TENEX ROUTINES
1 Introduction 126
2 TOPS-10 Style Input/Output 126
3 TENEX Style Input/Output 128
4 Terminal Handling 140
5 Utility TENEX System Calls 148
12 LEAP DATA TYPES
1 Introduction 151
2 Syntax 152
3 Semantics 154
13 LEAP STATEMENTS
1 Syntax 159
2 Restrictions 161
3 Semantics 161
4 Searching the Associative Store 164
14 LEAP EXPRESSIONS
1 Syntax 175
2 Semantics 177
15 BACKTRACKING
1 Introduction 182
2 Syntax 182
3 Semantics 183
16 PROCESSES
1 Introduction 186
2 Syntax 186
3 Semantics 186
4 Process Runtimes 192
17 EVENTS
1 Syntax 196
2 Introduction 196
3 Sail-defined Cause and Interrogate 197
4 User-defined Cause and Interrogate 199
18 PROCEDURE VARIABLES
1 Syntax 203
2 Semantics 203
19 INTERRUPTS
1 Introduction 207
2 Interrupt Routines 208
3 Immediate Interrupts 211
4 Clock Interrupts 214
5 Deferred Interrupts 215
20 LEAP RUNTIMES
1 Types and Type Conversion 218
2 Make and Erase Breakpoints 220
3 Pname Runtimes 220
4 Other Useful Runtimes 221
5 Runtimes for User Cause and Interrogate Procedures 223
21 BASIC CONSTRUCTS
1 Syntax 226
2 Semantics 227
22 USING SAIL
1 For TOPS-10 Beginners 231
2 For TENEX Beginners 232
3 The Complete use of Sail 232
4 Compiling Sail Programs 233
5 Loading Sail Programs 241
6 Starting Sail Programs 241
7 Storage Reallocation with REEnter 242
23 DEBUGGING SAIL PROGRAMS
1 Error Messages 243
2 Debugging 246
3 BAIL 249
APPENDICES
A Characters 264
B Sail Reserved Words 267
C Sail Predeclared Identifiers 270
D Indices for Interrupts 273
E Bit Names for Process Constructs 276
F Statement Counter System 278
G Array Implementation 281
H String Implementation 283
I Save/Continue 285
J Procedure Implementation 287
REFERENCES 293
INDEX 294
PROGRAMS AND BLOCKS Syntax
SECTION 1
PROGRAMS AND BLOCKS
1.1 Syntax
<program>
::= <block>
<block>
::= <blockhead> ; <compoundtail>
<blockhead>
::= BEGIN <declaration>
::= BEGIN <blockname> <declaration>
::= <blockhead> ; <declaration>
<compoundtail>
::= <statement> END
::= <statement> END <blockname>
::= <statement> ; <compoundtail>
<compoundstatement>
::= BEGIN <compoundtail>
::= BEGIN <blockname> <compoundtail>
<statement>
::= <block>
::= <compoundstatement>
::= <requirespecification>
::= <assignment>
::= <swapstatement>
::= <conditionalstatement>
::= <ifstatement>
::= <gotostatement>
::= <forstatement>
::= <whilestatement>
::= <dostatement>
::= <casestatement>
::= <printstatement>
::= <returnstatement>
::= <donestatement>
::= <nextstatement>
Semantics DECLARATIONS STATEMENTS BLOCK NAMES
::= <continuestatement>
::= <procedurestatement>
::= <safetystatement>
::= <backtrackingstatement>
::= <codeblock>
::= <leapstatement>
::= <processstatement>
::= <eventstatement>
::= <stringconstant> <statement>
::= <labelidentifier> : <statement>
::= <empty>
1.2 Semantics
DECLARATIONS
Sail programs are organized in the traditional block structure of ALGOL-60
[Nauer].
Declarations serve to define the data types and dimensions of simple and
subscripted (array) variables (arithmetic variables, strings, sets, lists,
record pointers, and items). They are also used to describe procedures
(subroutines) and record classes, and to name program labels.
Any identifier referred to in a program must be described in some
declaration. An identifier may only be referenced by statements within the
scope (see page 14) of its declaration.
STATEMENTS
As in ALGOL, the statement is the fundamental unit of operation in the Sail
language. Since a statement within a block or compound statement may itself
be a block or compound statement, the concept of statement must be
understood recursively.
The block representing the program is known as the "outer block". All
blocks internal to this one will be referred to as "inner blocks".
BLOCK NAMES
The block name construct is used to describe the block structure of a Sail
program to a symbolic debugging routine (see page 246). The name of the
outer block becomes the title of the binary output file (not necessarily the
file name). In addition, if a block name is used following an END then the
compiler compares it with the block name which followed the corresponding
BEGIN. A mismatch is reported to the user as evidence of a missing (extra)
BEGIN or END somewhere.
The <stringconstant> <statement> construct is equivalent in action to the
<statement> alone; that is, the string constant serves only as a comment.
EXAMPLES
EXAMPLES
Given:
S is a statement,
Sc is a Compound Statement,
D is a Declaration,
B is a Block.
Then:
(Sc) BEGIN S; S; S; ... ; S END
(Sc) BEGIN "SORT" S; S; ... ;S END "SORT"
(B) BEGIN D; D; D; ... ; S; S; S; ... ; S END
(B) BEGIN "ENTER NEW INFO" D; D; ... ;
S; ... ;S END
are syntactically valid Sail constructs.
ALGOL DECLARATIONS Syntax
SECTION 2
ALGOL DECLARATIONS
2.1 Syntax
<idlist>
::= <identifier>
::= <identifier> , <idlist>
<declaration>
::= <typedeclaration>
::= <arraydeclaration>
::= <preloadspecification>
::= <labeldeclaration>
::= <proceduredeclaration>
::= <synonymdeclaration>
::= <requirespecification>
::= <contextdeclaration>
::= <leapdeclaration>
::= <recordclassdeclaration>
::= <protectacs declaration>
::= <cleanupdeclaration>
::= <typequalifier> <declaration>
<simpletype>
::= BOOLEAN
::= INTEGER
::= REAL
::= RECORDPOINTER ( <classidlist> )
::= STRING
<typequalifier>
::= EXTERNAL
::= FORTRAN
::= FORWARD
::= INTERNAL
::= OWN
::= RECURSIVE
::= SAFE
::= SHORT
::= SIMPLE
<typedeclaration>
::= <simpletype> <idlist>
::= <typequalifier> <typedeclaration>
<arraydeclaration>
::= <simpletype> ARRAY <arraylist>
::= <typequalifier> <array declaration>
<arraylist>
::= <arraysegment>
::= <arraylist> , <arraysegment>
<arraysegment>
::= <idlist> [ <boundpairlist> ]
<boundpairlist>
::= <boundpair>
::= <boundpairlist> , <boundpair>
<boundpair>
::= <lowerbound> : <upperbound>
<lowerbound>
::= <algebraicexpression>
<upperbound>
::= <algebraicexpression>
<preloadspecification>
::= PRELOADWITH <preloadlist>
::= PRESETWITH <preloadlist>
<preloadlist>
::= <preloadelement>
::= <preloadlist> , <preloadelement>
<preloadelement>
::= <expression>
::= [expression] <expression>
<labeldeclaration>
::= LABEL <idlist>
<proceduredeclaration>
::= PROCEDURE <identifier> <procedurehead>
<procedurebody>
::= <simpletype> PROCEDURE <identifier>
<procedurehead> <procedurebody>
::= <typequalifier> <proceduredeclaration>
<procedurehead>
::= <empty>
::= ( <formalparamdecl> )
<procedurebody>
::= <empty>
::= ; <statement>
<formalparamdecl>
::= <formalparameterlist>
::= <formalparameterlist> ;
<formalparamdecl>
<formalparameterlist>
::= <formaltype> <idlist>
::= <formaltype> <idlist> (
<defaultvalue> )
<formaltype>
::= <simpleformaltype>
::= REFERENCE <simpleformaltype>
::= VALUE <simpleformaltype>
<simpleformaltype>
::= <simpletype>
::= <simpletype> ARRAY
::= <simpletype> PROCEDURE
<synonymdeclaration>
::= LET <synonymlist>
<synonymlist>
::= <synonym>
::= <synonymlist> , <synonym>
Restrictions
<synonym>
::= <identifier> = <reservedword>
<cleanupdeclaration>
::= CLEANUP <procedureidentifierlist>
<requirespecification>
::= REQUIRE <requirelist>
<requirelist>
::= <requireelement>
::= <requirelist> , <requireelement>
<requireelement>
::= <constantexpression> <requirespec>
::= <procedurename> INITIALIZATION
::= <procedurename> INITIALIZATION [
<phase> ]
<requirespec>
::= STRINGSPACE
::= SYSTEMPDL
::= STRINGPDL
::= ITEMSTART
::= NEWITEMS
::= PNAMES
::= LOADMODULE
::= LIBRARY
::= SOURCEFILE
::= SEGMENTFILE
::= SEGMENTNAME
::= POLLINGINTERVAL
::= POLLINGPOINTS
::= VERSION
::= ERRORMODES
::= DELIMITERS
::= NULLDELIMITERS
::= REPLACEDELIMITERS
::= UNSTACKDELIMITERS
::= BUCKETS
::= MESSAGE
::= COMPILERSWITCHES
2.2 Restrictions
For simplicity, the typequalifiers are listed in only one syntactic class.
Although their uses are always valid when placed according to the above
syntax, most of them only have meaning when applied to particular subsets of
these productions:
SAFE is only meaningful in array declarations.
INTERNAL/EXTERNAL have no meaning in formal parameter declarations.
SIMPLE, FORWARD, RECURSIVE, and FORTRAN have meaning only in
procedure type specifications.
SHORT has meaning only when applied to INTEGER or REAL entities.
Examples
For array declarations in the outer block substitute <constantexpression>
for <algebraicexpression> in the productions for <lowerbound> and
<upperbound>.
A label must be declared in the innermost block in which the statement being
labeled appears (more information, page 32). The syntax for procedure
declarations requires semantic embellishment (see page 17) in order to make
total sense. In particular, a procedure body may be empty only in a
restricted class of declarations.
2.3 Examples
Let I, J, K, L, X, Y, and P be identifiers, and let S be a statement.
(<typedeclaration>)
INTEGER I, J, K
EXTERNAL REAL X, Y
INTERNAL STRING K
(<arraydeclaration>)
INTEGER ARRAY X [0:10, 0:10]
REAL ARRAY Y [X:P(L)]; Comment illegal
in outer block unless P is a macro
STRING ARRAY I [0:IF BIG THEN 30 ELSE 3]
(<labeldeclaration>)
LABEL L, X, Y
(<procedure declaration>)
PROCEDURE P; S
PROCEDURE P (INTEGER I, J;
REFERENCE REAL X; REAL Y); S
INTEGER PROCEDURE P (REAL PROCEDURE L;
STRING I, J; INTEGER ARRAY K); S
EXTERNAL PROCEDURE P (REAL X)
FORWARD INTEGER PROCEDURE X (INTEGER I)
Note that these sample declarations are all given without the semicolons
which would normally separate them from the surrounding declarations and
statements. Here is a sample block to bring it all together (again, let S
be any statement, D any declaration, and other identifiers as above):
Semantics SCOPE OF DECLARATIONS TYPE QUALIFIERS
BEGIN "SAMPLE BLOCK"
INTEGER I, J, K;
REAL X, Y;
STRING A;
INTEGER PROCEDURE P (REFERENCE REAL X);
BEGIN "P"
D; D; D; ... ;S; ... ; S
END "P";
REAL ARRAY DIPHTHONGS [0:10, 1:100];
S; S; S; S
END "SAMPLE BLOCK"
2.4 Semantics
SCOPE OF DECLARATIONS
Every block automatically introduces a new level of nomenclature. Any
identifier declared in a block's head is said to be LOCAL to that block.
This means that:
a. The entity represented by this identifier inside the block has no
existence outside the block.
b. Any entity represented by the same identifier outside the block is
completely inaccessible (unless it has been passed as a parameter)
inside the block.
An identifier occurring within an inner block and not declared within that
block will be nonlocal (global) to it; that is, the identifier will
represent the same entity inside the block and in the block or blocks within
which it is nested, up to and including the level in which the identifier is
declared.
The Scope of an entity is the set of blocks in which the entity is
represented, using the above rules, by its identifier. An entity may not be
referenced by any statement outside its scope.
TYPE QUALIFIERS
An array, variable, or procedure declared OWN will behave as if it were
declared globally to the current procedure; the OWN type qualifier on a
variable, etc. declared in a block not nested inside a procedure declaration
will have no effect. This means that in a second call of a procedure with
OWN locals (or a recursive call)
NUMERIC DECLARATIONS STRING DECLARATIONS ARRAY DECLARATIONS
the OWN variables will not be reinitialized; they will have the values that
they had when the first call of the procedure finished. Furthermore, OWN
arrays, etc. will not be deallocated upon exiting the procedure in which
they are declared.
INTERNAL and EXTERNAL procedures, variables, etc. let one link programs
that are loaded together but were compiled separately. See page 25 for
more information.
RECURSIVE, SHORT, FORTRAN, FORWARD, SIMPLE, and SAFE will be explained when
the data types they modify are discussed.
NUMERIC DECLARATIONS
Identifiers which appear in type declarations with types REAL or INTEGER can
subsequently be used to refer to numeric variables. An Integer variable may
take on values from -2^35 to 2^35-1 (-2^26 to 2^26-1 for SHORT INTEGERS).
A Real variable may take on positive and negative values from about 10^-38
to 10^38 with a precision of 27 bits (same range for SHORT REALs as for
SHORT INTEGERs). REAL and INTEGER variables (and constants) may be used in
the same arithmetic expressions; type conversions are carried out
automatically (see page 46) when necessary.
The advantage of SHORT reals and integers is that the conversion from
integer to real is sped by a factor of 8 if either the integer or the real
is SHORT. See page 46 for more information.
The BOOLEAN type is identical to INTEGER. BOOLEAN and algebraic expressions
are really equivalent syntactically. The syntactic context in which they
appear determines their meaning. Non-zero integers correspond to TRUE and 0
corresponds to FALSE. The declarator BOOLEAN is included for program
clarity.
STRING DECLARATIONS
A variable defined in a String declaration is a two-word descriptor
containing the information necessary to represent a Sail character string.
A String may be thought of as a variable-length, one-dimensional array of 7-
bit ASCII characters. Its descriptor contains a character count and a byte
pointer to the first character (see page 283). Strings originate as
constants at compile time (page 229), as the result of a String INPUT
operation from some device (see page 72), or from the concatenation or
decomposition of already existing strings (see page 53).
When strings appear in arithmetic operations or vice-versa, a somewhat
arbitrary conversion is performed to obtain the proper type (by arbitrary we
do not mean to imply random -- see page 46). For this reason arithmetic,
String, and Recordpointer variables are referred to as "algebraic
variables" and their corresponding expressions are called "algebraic
expressions" (to differentiate them them from the variables and expressions
of LEAP -- see page 151).
PRELOAD SPECIFICATIONS
ARRAY DECLARATIONS
In general, any data type which is applicable to a simple variable may be
applied in an Array declaration to an array of variables. The entity
represented by the name of an Array, qualified with subscript expressions to
locate a particular element (e.g. A[I, J]) behaves in every way like a
simple variable. Therefore, in the future we shall refer to both simple
variables and single elements of Arrays (subscripted variables) as
"variables". The formal syntax for <variable> can be found on page 227.
For an Array which is not qualified by the SAFE attribute, nor had a
NOWSAFE statement done on it (NowSafe - see page 41), each subscript
will be checked to ensure that it falls within the lower and upper bounds
given for the dimension it specifies. Subscripts outside the bounds trigger
an error message and job abortion. The SAFE declaration inhibits this
checking, resulting in faster, smaller, and bolder code.
Arrays which are allocated at compile time (OWN arrays and arrays in the
outer block) are restricted to 5 or fewer dimensions. There is no limit to
the number of dimensions allowed for an Array which is dynamically
allocated. However, the efficiency of Array references tends to decrease
for large dimensions. Avoid large dimensionality.
OWN Arrays are available in part. They must be declared with constant
bounds, since fixed storage is allocated. They are NOT initialized when
the program is started or restarted (except in preloaded Arrays, see page
16). A certain degree of extra efficiency is possible in accessing these
Arrays, since they may be assigned absolute core locations by the compiler,
eliminating some of the address arithmetic. Constant bounds always add a
little efficiency, even in inner blocks. Arrays declared in the outer
block must have constant bounds, since no variable may yet have been
assigned a value. They are thus automatically made OWN. For more details
concerning the internal structure of Arrays see page 246 and page 281.
PRELOAD SPECIFICATIONS
Any OWN arithmetic or String Array may be "pre-loaded" at compile time with
constant information by preceding its declaration with a
<preloadspecification>. This specification gives the values which are to
be placed in consecutive core locations of the Arrays declared immediately
following the <preloadspecification>. "Immediately", in this case, means
all identifiers up to and including one which is followed by boundpairlist
brackets (e.g. in REAL ARRAY X, Y, Z[0:10], W[1:5]; -- preloads X, Y, and Z;
not W). It is the user's responsibility to guarantee that the proper values
will be obtained under the subscript mapping, namely: arrays are stored by
rows; if A[I, J] is stored in location 10000, then A[I, J+1] is stored in
location 10001.
The current values of non-String pre-loaded Arrays will not be lost by
restarting the program; they will not be re-initialized or re-preloaded. For
preloaded String Arrays, the non-constant elements are set to NULL by a
restart.
PROCEDURE DECLARATIONS FORMAL PARAMETERS
Algebraic type conversions will be performed at compile-time to provide
values of the proper types to pre-loaded Arrays. All expressions in these
specifications must be constant expressions -- that is, they must contain
only constants and algebraic operators. The compiler will not allow you to
fill an Array beyond its capacity. You may, however, provide a number of
elements less than the total size of the Array; remaining elements will be
set to zero or to the null string.
Example:
PRELOADWITH [5] 0, 3, 4, [4] 6, 2;
INTEGER ARRAY TABL [1:4, 1:3];
The first five elements of TABL will be initialized to 0 (bracketed number
is used as a repeat argument). The next two elements will be 3 and 4,
followed by four 6's and a 2. The array will look like this:
1 2 3 (second subscript)
1 | 0 0 0
(first 2 | 0 0 3
subscript) 3 | 4 6 6
4 | 6 6 2
PRESETWITH is just like PRELOADWITH except that an array which is PRESET
is placed in the upper segment of a /H compilation. This allows constant
arrays to be in the shared portion of the code.
PROCEDURE DECLARATIONS
If a Procedure is typed then it may return a value (see page 36) of the
specified type. If formal parameters are specified then they must be
supplied with actual parameters in a one to one correspondence when they are
called (see page 54 and page 39).
FORMAL PARAMETERS
Formal parameters, when specified, provide information to the body
(executable portion) of the Procedure about the kinds of values which will
be provided as actual parameters in the call. The type and complexity
(simple or Array) are specified here. In addition, the formal parameter
indicates whether the value (VALUE) or address (REFERENCE) of the actual
parameter will be supplied. If the address is supplied then the variable
whose identifier is given as an actual parameter may be changed by the
Procedure. This is not the case if the value is given.
To pass a PROCEDURE by value has no readily determined meaning. ARRAYs
passed by value (requiring a complete copy operation) are not implemented.
Therefore these cases are noted as errors by the compiler.
The proper use of actual parameters is further discussed on page 39 and
page 54.
DEFAULT PARAMETER VALUES FORWARD PROCEDURE DECLARATIONS
DEFAULT PARAMETER VALUES
Default values for trailing parameters may be specified by enclosing the
desired value in parentheses following the parameter declaration.
PROCEDURE FOO (REAL X; INTEGER I (2);
STRING S ("FOO"); REAL Y (3.14159) );
If a defaulted parameter is left out of a procedure call then the compiler
fills in the default automatically. The following all compile the same
code:
FOO (A+B);
FOO (A+B, 2, "FOO");
FOO (A+B, 2, "FOO", 3.14159);
Only VALUE parameters may be defaulted, and the default values must be
constant expressions. A parameter may not be left out of the middle of the
parameter list; i.e., FOO (A+B, , "BAR") won't work. Finally, it should be
noted that the compiled code assumes that all parameters are actually
present in the call, so be careful about odd STARTCODE or INTERNAL-EXTERNAL
linkages. However, APPLY will fill in default values if not enough actual
parameters are supplied in an interpreted call. FORWARD PROCEDURE
DECLARATIONS
A Procedure's type and parameters must be described before the Procedure may
be called. Normally this is accomplished by specifying the procedure
declaration in the head of some block containing the call. If, however, it
is necessary to have two Procedures, declared in some block head, which are
both accessible to statements in the compound tail of that block and to each
other, then the FORWARD construct permits the definition of the parameter
information for one of these Procedures in advance of its declaration. The
Procedure body must be empty in a forward procedure declaration. When the
body of the Procedure described in the forward declaration is actually
declared, the types of the Procedure and of its parameters must be identical
in both declarations. The declarations must appear at the same level
(within the same block head). Example:
BEGIN "NEED FORWARD"
FORWARD INTEGER PROCEDURE T1 (INTEGER I);
COMMENT PARAMS DESCRIBED;
INTEGER PROCEDURE T2 (INTEGER J);
RETURN (T1 (J)+3); COMMENT CALL T1 ;
INTEGER PROCEDURE T1 (INTEGER I);
COMMENT ACTUALLY DEFINE T1;
RETURN (IF I=15 THEN I ELSE T2 (I-1));
COMMENT CALLS T2;
...
K_T1 (L); ... ; L_T2 (K); ...
END "NEED FORWARD";
Notice that the forward declaration is required only because BOTH Procedures
RECURSIVE PROCEDURES SIMPLE PROCEDURES
are called in the body of the block. These procedures should also be
declared RECURSIVE if recursive entrance is likely. If only T1 were called
from statements within the block then this example could be implemented as:
BEGIN "NO FORWARD"
RECURSIVE INTEGER PROCEDURE T1 (INTEGER I);
BEGIN
INTEGER PROCEDURE T2 (J);
RETURN (T1 (J)+3);
RETURN( IF I=15 THEN I
ELSE T2 (I-1));
END "T1";
...
K_T1 (L);
...
END "NO FORWARD";
RECURSIVE PROCEDURES
If a Procedure is to be entered recursively then the compiler must be
instructed to provide code for allocating new local variables when the
Procedure is called and deallocating them when it returns. Use the type-
qualifier RECURSIVE in the declaration of any recursive Procedure.
The compiler can produce much more efficient code for non-recursive
Procedures than for recursive ones. We feel that this gain in efficiency
merits the necessity for declaring Procedures to be recursive.
If a Procedure which has not been declared recursive is called recursively
then all its local variables (and temporary storage locations assigned by
the compiler) will behave as if they were global to the Procedure -- they
will not be reinitialized, and when the recursive call is complete, the
locals of the calling procedure will reflect the changes made to them during
the recursive call. Otherwise, no ill effects should be observed.
SIMPLE PROCEDURES
Standard procedures contain a short prologue that sets up some links on the
stack and a descriptor that is used by the storage allocation system, the
GOTO solver, and some other routines. For most procedures, this overhead
is insignificant. However, for small procedures that just do a few simple
statements and exit, this overhead is excessive and unneeded. To skip the
prologue, just include SIMPLE in the attribute list for the procedure.
RESTRICTIONS:
EXTERNAL PROCEDURES
1. Simple procedures may not be Recursive and may not be
SPROUTed or APPLYed.
2. ARRAY locals must be OWN.
3. Set and List locals must be OWN (Sets and list are part of
Leap, page 151).
4. Procedures declared local to a simple procedure must also be
of of type SIMPLE, and may not reference any of the parameters of
the outer simple procedure.
5. One may not GO TO a statement outside the body of the simple
procedure.
6. RECORDPOINTERs may not be declared or passed as arguments to
other procedures, and the code must not cause the compiler to
create RECORDPOINTER temporaries.
EXTERNAL PROCEDURES
A file compiled by Sail represents either a "main" program or a collection
of independent procedures to be called by the main program. The method for
preparing such a collection of Procedures is described in page 25. The
EXTERNAL and FORTRAN type-qualifiers allow description of the types of these
Procedures and their parameters. An EXTERNAL or FORTRAN procedure
declaration, like the FORWARD declaration, does not include a procedure
body. Both declarations instead result in requests to the loader to provide
the addresses of these Procedures to all statements which call them. This
means that an EXTERNAL Procedure declaration (or the declaration of any
External identifier) may be placed within any block head, thereby
controlling the scope of this External identifier within this program.
Any Sail Procedure which is referenced via these external declarations must
be an INTERNAL Procedure. That is, the type-qualifier INTERNAL must appear
in the actual declaration of the Procedure. Again, see page 25.
The type-qualifier FORTRAN is used to describe the type and name of an
external Procedure which is to be called using a Fortran calling sequence.
Either the old F40 or the new FORTRAN-10 calling sequence can be generated,
depending on the /A switch (page 237). All parameters to Fortran Procedures
are by reference. In fact, the procedure head part of the declaration need
not be included unless the types expected by the Procedure differ from those
provided by the actual parameters--the number of parameters supplied, and
their types, are presumed correct. Fortran Procedures are automatically
External Procedures. See page 21, page 39, page 54 for more information
about Fortran Procedures. Example:
FORTRAN PROCEDURE FPF;
Y_FPF (X, Z);
PARAMETRIC PROCEDURES DEFAULTS IN PROCEDURE DECLARATIONS RESTRICTIONS ON PROCEDURE DECLARATIONS ALLOCATION AND DEALLOCATION
PARAMETRIC PROCEDURES
The calling conventions for Procedures with Procedures as arguments, and for
the execution of these parametric Procedures, are described on page 39 and
page 54. Any Procedure PP which is to be used as a parameter to another
Procedure CP must not have any Procedure or array parameters, or any
parameters called by value. In other words, PP may only have simple
reference parameters. The number of parameters supplied in a call on PP
within CP, and their types, will be presumed correct, and should not be
specified in the procedure head. Example:
PROCEDURE CP (INTEGER PROCEDURE FP);
BEGIN INTEGER A, I; REAL X;
...
A_FP (I, X); COMMENT I AND X PASSED BY
REFERENCE, NO TYPE CONVERSION;
END "CP";
INTEGER PROCEDURE PP (REFERENCE INTEGER J;
REFERENCE REAL Y);
BEGIN ...
END "PP";
...
CP (PP);
DEFAULTS IN PROCEDURE DECLARATIONS
If no VALUE or REFERENCE qualification appears in the description then the
following qualifications are assumed:
VALUE Integer, String, Real, Recordpointer,
Set, List variables.
REFERENCEArrays, Contexts and Procedures.
RESTRICTIONS ON PROCEDURE DECLARATIONS
1) Fortran Procedures cannot handle String parameters. Nor can a
Fortran Procedure return a string as a result.
2) Labels may never be passed as arguments to Procedures.
3) Procedures may not have the type "CONTEXT".
4) Arrays and Context parameters must always be passed by reference.
ALLOCATION AND DEALLOCATION
All simple variables (integer, real, string, boolean, record pointer) are
allocated at compile time. Non-own simple variables that are local to a
recursive procedure are an exception to this and are allocated (on the
stack) upon instantiation of the procedure; they are deallocated when the
INITIALIZATION AND REINITIALIZATION SYNONYMS
instantiation is terminated. Simple variables which are declared but not
subsequently referenced are not allocated at all.
All outer-block and OWN arrays are allocated at compile time. All other
arrays are allocated when the block of their definition is entered, and
deallocated when it is exited.
INITIALIZATION AND REINITIALIZATION
Upon allocation, everything is initialized to 0 or the NULL string (except
preloaded arrays, which are initialized to their the values of their
PRELOAD). Nothing is reinitialized unless the program is restarted by
typing ^C and REEnter. This lack of reinitialization is noticeable when
one enters a block for the second time, and that block is not the body of a
recursive procedure. For example,
STRING PROCEDURE READIN;
BEGIN
INTEGER CHANNEL, BRTAB;
IF BRTAB=0 THEN BRTAB _ INIT (CHANNEL);
RETURN (INPUT (CHANNEL, BRTAB));
END;
will return a string from an input operation with every call. However, on
the first call, it will do some initialization of the I/O channel because
BRTAB is 0 then, whereas it is not for any of the other calls. If READIN
were a recursive procedure then CHANNEL and BRTAB would be allocated and
hence initialized with every call.
When one REEnters a program, some things are reinitialized and some are not.
Namely, strings and non-preloaded arrays will be reinitialized, but simple
variables will not. Preloaded arrays will not be re-preloaded.
SYNONYMS
The Sail Synonym ("LET") permits one to declare any identifier to act as a
reserved word. The effect of the reserved word is not changed; it may be
used as well as the new identifier. Synonyms follow the same scope rules
that identifiers used for variables, arrays, etc. do.
Since Sail permits one to declare almost any reserved word to be an
identifier for variables, procedures, etc. (see about restrictions on
identifiers, page 228), synonyms are used to keep the effect of the
reserved word available. For example,
CLEANUP DECLARATIONS REQUIREMENTS
LET BEG = BEGIN;
PROCEDURE BEGIN;
BEG
.....;
END;
...
IF OK THEN BEGIN;
...
CLEANUP DECLARATIONS
The CLEANUP declaration requires a list of procedure names following the
"CLEANUP" token. Each procedure specified must be SIMPLE and have no formal
parameters. The specified procedures will be called at the exit of the
block that the CLEANUP declaration occurs in. They will be called in the
order of their appearance on the list, and before any of the variables of
the block are deallocated. NOTE: If the block is part of a process (see
about processes, page 186) that is being terminated then the cleanup
procedures will be called before the terminate is completed.
Cleanup procedures are normally used in connection with processes to
"cleanup" a block by terminating the processes dependent on that block (it
is an error to leave active a process that depends on an exited block).
REQUIREMENTS
The user may, using the REQUIRE construct, specify to the compiler
conditions which are required to be true of the execution-time environment
of his programs. All requirements are legal at either declaration or
statement level. The requirements fall into three classifications,
described as follows:
Group 1 -- Space requirements -- STRINGSPACE, SYSTEMPDL, etc.
The inclusion of the specification "REQUIRE 1000 STRINGSPACE" will ensure
that at least 1000 words of storage will be available for storing (the text
characters of) Strings when the program is run. Similar provisions are made
for various push-down stacks used by the execution-time routines and the
compiled code. If a parameter is specified twice, or if separately compiled
procedures are loaded (see page 25) then the sum of all such specifications
will be used. These parameters could also be typed to the loaded program
just before execution (see page 242), but it is often more convenient to
specify differences from the standard sizes in the source program. Use
these specifications only if messages from the running program indicate that
the standard allocations are not sufficient.
Group 2 -- Other files -- LOADMODULE, LIBRARY, SOURCEFILE, etc.
The inclusion of the specification REQUIRE "PROCS1" LOADMODULE,
"HELIB[1,3]" LIBRARY; would inform the Loader that the file PROCS1.REL must
be loaded and the library HELIB.REL[1,3] searched whenever the program
containing the specification is loaded. The parameter for both features
should be a string constant of one of the above forms. The file extension
.REL is the only value permitted, and is therefore assumed; the device,
name, and ppn may be specified. TENEX users should note that the LOADER
restricts LOADMODULE and LIBRARY file names to 6 characters in the main
name and 3 characters in the extension.
LOADMODULES (.REL files to be loaded) may themselves contain requests for
other LOADMODULES and LIBRARYs. LIBRARYs may only contain requests for
other LIBRARYs. The LOADER may do strange things with files requested
twice.
Sail automatically places a request for the library SYS:LIBSAn (<SAIL>LIBSAn
on TENEX) [HLBSAn for /H compilations] in each main program, where n is the
version number of the current Sail library of runtime routines.
The inclusion of REQUIRE "PREAMB.SAI" SOURCEFILE will cause the compiler
to save the state of the current input file, then begin scanning from
PREAMB. When PREAMB is exhausted, Sail will resume scanning the original
file on the line directly following the REQUIRE. Commonly-used
declarations, particularly EXTERNAL declarations for libraries, are often
put in a separate file which is then REQUIREd.
Restrictions: A SOURCEFILE request must be followed by a semicolon (only
one per REQUIREment), and must be the last text on the line in which it
appears. SOURCEFILE switching must not be specified from within a DEFINE
body (see page 106). SOURCEFILEs may be nested to a depth of about 10
levels.
The SEGMENTNAME, SEGMENTFILE specifications are currently applicable only
to the SUAI "global model" users of Sail. They allow specification of the
name of a special non-sharable "HISEG", and the name of the file used to
create this HISEG. These specifications may, like the space REQUIREments,
be overridden by using the system REENTER command (see page 242).
Group 3 -- other - INITIALIZATION, VERSION
Before the execution of a program, Sail runs through an initialization
routine. The user can specify things that he wants done at initialization
time by declaring an outer-block Procedure without arguments, then saying
REQUIRE procedurename INITIALIZATION.
Require-initialization procedures are run just before the first executable
statement in the outer block of the program. They are run in order of
ascending phase number, and within each phase in the order the compiler saw
the REQUIREs. There are currently three user phases, numbered 0, 1, and 2.
Phase 1 is the default if no phase is specified. WARNING: you should not
Separately Compiled Procedures
Require initialization of a procedure which is declared inside another
procedure.
REQUIRE n VERSION (n a non-zero integer) will flag the resultant .REL file
as version n. When a program loaded from several such RELfiles is started,
the Sail allocation code will verify that all specified versions are equal.
A non-fatal error message is generated if any disagree. As much as will fit
of the version number is also stored in lh(.JBVER), where .JBVER is location
'137.
For other requirements, check the index under the specific condition being
Required.
COMMENT: You have probably noticed that a great deal of prior knowledge is
required for proper understanding of this section. For more information
about storage allocation, see page 242 below. The form and use of .REL
files and libraries are described in [TopHand].
2.5 Separately Compiled Procedures
When a program becomes extremely large it becomes useful to break it up into
several files which can be compiled separately. This can be done in Sail by
preparing one file as a main program, and one or more other files as
programs each of which contains one or more procedures to be called by the
main program. The main program must contain EXTERNAL declarations for each
of the procedures declared in the other files. (EXTERNAL declarations have
no procedure body.) The non-main program files must have the following
characteristics:
1) All procedures to be called from the main program (or procedures in
other files) must be qualified with the INTERNAL attribute when they
are declared. External procedure declarations with headings
identical to those of the actual declarations must appear in all
those programs which call these procedures.
2) These internal procedures must be uniquely identifiable by the first
six characters of their identifiers. In general, any two internal
procedure names (or any other Internal variables in the same core
image) with the same first six characters will cause incorrect
linkages when the programs are loaded.
3) The reserved word ENTRY, followed by a semi-colon, must be the first
item in the program (preceding even the BEGIN for its outer block).
No starting address will be issued for a program containing an Entry
Specification. Since no starting address is present for this file,
entry to code within it may only be to the procedures it contains.
The statements in the outer block, if any, can never be executed.
FORTRAN PROCEDURES
4) Should you desire your separatedly compiled procedures to be
collected into a user library, include a list of their identifiers
between the ENTRY and the semi-colon of the Entry Specification of
the program containing those procedure declarations. The format of
libraries is described in [TopHand]. The identifier(s) appearing in
the entry list may be any valid identifiers, but usually they will be
the names of the procedures contained in the file. No checking is
done to see if entry identifiers are ever really declared in the body
of the program.
5) Any variables (simple or array) which appear in the outer block of a
Separately Compiled Procedure program will be global to the
procedures in this program, but not available to the main program
(unless they are themselves connected to the main program by
Internal/External declarations -- see below). Non-LEAP arrays in
these outer blocks will always be zero when the program is first
loaded, but will never be cleared as others are by restarting your
program (see reinitialization, page 22).
Any variable, procedure or label may contain the attribute INTERNAL or
EXTERNAL in its declaration (ITEMS may not -- items are part of leap, page
151). The INTERNAL attribute does not affect the storage assignment of the
entity it represents, nor does it have any effect on the behavior of the
entity (or the scope of its identifier) in the file wherein it appears.
However, its address and (the first six characters of) its name are made
available to the loader for satisfying External requests. GOTO an external
label is for wizards only.
No space is ever allocated for an External declaration. Instead, a list of
references to each External identifier is made by the compiler. This list
is passed to the loader along with the first six characters of the
identifier name. (If there are no references then Sail ignores the External
declaration.) When a matching Internal name is found during loading, the
loader places the associated address in each of the instructions mentioned
on the list. No program inefficiency at all results from External/Internal
linkages (belay that -- references to External arrays are sometimes less
efficient).
The entity finally represented by an External identifier is only accessible
within the scope of the External declaration.
FORTRAN PROCEDURES
For a program written in either F40 or FORTRAN-10 to run in the Sail
environment, the following restrictions must be observed:
ASSEMBLY LANGUAGE PROCEDURES
1) It must be a SUBROUTINE or FUNCTION, not a main program.
2) It must not execute any FORTRAN I/O calls. The UUO structures of
the two languages are not compatible.
3) It must be declared as a Fortran Procedure (see page 40) in the
Sail program which calls it.
The type bits required in the argument addresses for Fortran arguments are
passed correctly to these routines.
The Sail compiler will not produce a procedure to be called from FORTRAN.
ASSEMBLY LANGUAGE PROCEDURES
The following rules should be observed:
1) The ENTRY, INTERNAL, and EXTERNAL pseudo-ops should be used to
obtain linkages for procedure names and "global" identifiers;
remember that only six characters are used for these linkage names.
2) Accumulators F (currently '12), P (currently '17) and SP ('16)
should be preserved over function calls. P may be used as a push-
down pointer for arithmetic values and return addresses. SP is the
string stack pointer. String results are returned on this stack.
Arithmetic results are returned in AC 1.
3) Those who wish to provide their own UUO handlers or to increase
their core size should read the code.
There are no other known processors which will produce Sail-compatible
programs.
ALGOL STATEMENTS Syntax
SECTION 3
ALGOL STATEMENTS
3.1 Syntax
<assignmentstatement>
::= <algebraicvariable> _
<algebraicexpression>
<swapstatement>
::= <variable> <variable>
::= <variable> SWAP <variable>
<conditionalstatement>
::= <ifstatement>
::= <ifstatement> ELSE <statement>
<ifstatement>
::= IF <booleanexpression> THEN <statement>
<gotostatement>
::= GO TO <labelidentifier>
::= GOTO <labelidentifier>
::= GO <labelidentifier>
<labelidentifier>
::= <identifier>
<forstatement>
::= FOR <algebraicvariable> _ <forlist>
DO <statement>
::= NEEDNEXT <forstatement>
<forlist>
::= <forlistelement>
::= <forlist> , <forlistelement>
<forlistelement>
::= <algebraicexpression>
::= <algebraicexpression> STEP
<algebraicexpression> UNTIL
<algebraicexpression>
::= <algebraicexpression> STEP
<algebraicexpression> WHILE
<booleanexpression>
<whilestatement>
::= WHILE <booleanexpression> DO
<statement>
::= NEEDNEXT <whilestatement>
<dostatement>
::= DO <statement> UNTIL
<booleanexpression>
<casestatement>
::= <casestatementhead> <statementlist>
<casestatementtail>
::= <casestatementhead>
<numberedstatelist>
<casestatementtail>
<casestatementhead>
::= CASE <algebraicexpression> OF BEGIN
::= CASE <algebraicexpression> OF BEGIN
<blockname>
<casestatementtail>
::= END
::= END <blockname>
<statementlist>
::= <statement>
::= <statementlist> ; <statement>
<numberedstatelist>
::= [ <integerconstant> ] <statement>
::= [ <integerconstant> ]
<numberedstatelist>
::= <numberedstatelist> ; [
<integerconstant> ] <statement>
<returnstatement>
Semantics
::= RETURN
::= RETURN ( <expression> )
<donestatement>
::= DONE
::= DONE <blockname>
<nextstatement>
::= NEXT
::= NEXT <blockname>
<continuestatement>
::= CONTINUE
::= CONTINUE <blockname>
<procedurestatement>
::= <procedurecall>
<procedurecall>
::= <procedureidentifier>
::= <procedureidentifier> (
<actualparameterlist> )
<actualparameterlist>
::= <actualparameter>
::= <actualparameterlist> ,
<actualparameter>
<actualparameter>
::= <expression>
::= <arrayidentifier>
::= <procedureidentifier>
<safetystatement>
::= NOWSAFE <idlist>
::= NOWUNSAFE <idlist>
ASSIGNMENT STATEMENTS SWAP ASSIGNMENT CONDITIONAL STATEMENTS
3.2 Semantics
ASSIGNMENT STATEMENTS
The assignment statement causes the value represented by an expression to be
assigned to the variable appearing to the left of the assignment symbol.
You will see later (page 49) that one value may be assigned to two or more
variables through the use of two or more assignment symbols. The operation
of the assignment statement proceeds in the following order:
a) The subscript expressions of the left part variable (if any - Sail
defines "variable" to include both array elements and simple
variables) are evaluated from left to right (see Expression
Evaluation Rules, page 50).
b) The expression is evaluated.
c) The value of the expression is assigned to the left part variable,
with subscript expressions, if any, having values as determined in
step a.
This ordering of operations may usually be disregarded. However it becomes
important when expression assignments (page 49) or function calls with
reference parameters appear anywhere in the statement. For example, in the
statements:
K_3;
A[K]_3+(K_1);
A[3] will receive the value 4 using the above algorithm. A[1] will not
change.
Any algebraic expression (REAL, INTEGER (BOOLEAN), or STRING) may be
assigned to any variable of algebraic type. The resultant type will be that
of the left part variable. The conversion rules for assignments involving
mixed types are identical to the conversion rules for combining mixed types
in algebraic expressions (see page 46).
SWAP ASSIGNMENT
The operator causes the value of the variable on the left hand side to be
exchanged with the value of the variable on the right hand side. Arithmetic
(REALINTEGER) type conversions are made, if necessary; any other type
conversions are invalid. Note that the operator may not be used in
assignment expressions.
CONDITIONAL STATEMENTS
These statements provide a means whereby the execution of a statement, or a
series of statements, is dependent on the logical value produced by a
Boolean expression.
A Boolean expression is an algebraic expression whose use implies that it is
AMBIGUITY IN CONDITIONAL STATEMENTS GO TO STATEMENTS
to be tested as a logical (truth) value. If the value of the expression is 0
or NULL then the expression is a FALSE boolean expression, otherwise it is
TRUE. See about type conversion, page 46.
IF STATEMENT - The statement following the operator THEN (the "THEN part")
is executed if the logical value of the Boolean expression is TRUE;
otherwise, that statement is ignored.
IF ... ELSE STATEMENT - If the Boolean expression is true, the "THEN part"
is executed and the statement following the operator ELSE (the "ELSE part")
is ignored. If the Boolean expression is FALSE, the "ELSE part" is executed
and the "THEN part" is ignored.
AMBIGUITY IN CONDITIONAL STATEMENTS
The syntax given here for conditional statements does not fully explain the
correspondences between THEN-ELSE pairs when conditional statements are
nested. An ELSE will be understood to match the immediately preceding
unmatched THEN. Example:
COMMENT DECIDE WHETHER TO GO TO WORK;
IF WEEKEND THEN
IF GIANTSONTV THEN BEGIN
PHONEEXCUSE ("GRANDMOTHER DIED");
ENJOY (GAME);
SUFFER (CONSCIENCEPANGS)
END
ELSE IF REALLYSICK THEN BEGIN
PHONEEXCUSE ("REALLY SICK");
ENJOY (0);
SUFFER (AGONY)
END
ELSE GO TO WORK;
GO TO STATEMENTS
Each of the three forms of the Go To statement (GO, GOTO, GO TO) means the
same thing -- an unconditional transfer is to be made to the "target"
statement labeled by the label identifier. The following rules pertain to
labels:
1) All label identifiers used in a program must be declared.
2) The declaration of a label must be local to the block immediately
surrounding the statement it identifies (see exception below). Note
that compound statements (BEGIN-END pairs containing no declarations)
are not blocks. Therefore the block
BEGIN "B1"
INTEGER I, J; LABEL L1;
...
IF BE3 THEN BEGIN "C1"
...
L1: ...
...
END "C1";
...
GO TO L1
END "B1"
is legal.
3) Rule 2 can be violated if the inner block(s) have no array
declarations. E.g.:
Legal Illegal
BEGIN "B1" BEGIN "B1"
INTEGER I, J; INTEGER I, J;
LABEL L1; LABEL L1;
... ...
BEGIN "B2" BEGIN "B2"
REAL X; REAL ARRAY X [1:10];
... ...
L1: ... L1: ...
... ...
END "B2"; END "B2";
GO TO L1; GO TO L1;
END "B1" END "B1"
4) No Go To statement may specify a transfer into a FOREACH statement
(FOREACH statements are part of LEAP -- page 151), or into complicated
For loops (those with For Lists or which contain a NEXT statement).
Labels will seldom be needed for debugging purposes. The block name feature
(see page 246) and the listing feature which associates with each source
line the octal address of its corresponding object code (see page 237)
should provide enough information to find things easily.
Many program loops coded with labels can be alternatively expressed as For
or While loops, augmented by DONE, NEXT, and CONTINUE statements. This
often results in a source program whose organization is somewhat more
transparent, and an object program which is more efficient.
FOR STATEMENTS
FOR STATEMENTS
For, Do and While statements provide methods for forming loops in a
program. They allow the repetitive execution of a statement zero or more
times. These statements will be described by means of Sail programs which
are functionally equivalent but which demonstrate better the actual order of
processing. Refer to these equations for any questions you might have about
what gets evaluated when, and how many times each part is evaluated.
Let VBL be any algebraic variable, AE1, ... , AE8 any algebraic
expressions, BE a Boolean expression, TEMP a temporary location, S a
statement. Then the following Sail statements are equivalent.
Using For Statements:
FOR VBL _ AE1, AE2, AE3 STEP
AE4 UNTIL AE5, AE6 STEP AE7 WHILE
BE, AE8 DO S;
Equivalent formulation without For Statements:
VBL_AE1;
S;
VBL_AE2;
S;
VBL_AE3; Comment STEP-UNTIL loop;
LOOP1: IF (VBL-AE5) * SIGN(AE4) 0 THEN
BEGIN
S;
VBL_VBL+AE4;
GO TO LOOP1
END;
VBL_AE6; Comment STEP-WHILE loop;
LOOP2: IF BE THEN BEGIN
S;
VBL_VBL+AE7;
GO TO LOOP2
END;
VBL_AE8;
S;
If AE4 (AE7) is an unsubscripted variable then changing its value within the
loop will cause the new value to be used for the next iteration. If AE4
(AE7) is a constant or an expression requiring evaluation of some operator
then the value used for the step element will remain constant throughout the
execution of the For Statement. If AE5 is an expression then it will be
evaluated before each iteration, so watch this possible source of
inefficiency.
WHILE STATEMENT DO STATEMENT
Now consider the For Statement:
FOR VBL_AE1 STEP CONST UNTIL AE2 DO S;
where const is a positive constant. The compiler will simplify this case
to:
VBL_AE1;
LOOP3: IF VBL AE2 THEN BEGIN
S;
VBL_VBL+CONST;
GO TO LOOP3
END;
If CONST is negative then the line at LOOP3 would be:
LOOP3: IF VBL AE2 THEN BEGIN
The value of VBL when execution of the loop is terminated, whether it be by
exhaustion of the For list or by execution of a DONE, NEXT or GO TO
statement (see page 37, page 38, page 32), is the value last assigned to
it using the algorithm above. This value is therefore always well-defined.
The statement S may contain assignment statements or procedure calls which
change the value of VBL. Such a statement behaves the same way it would if
inserted at the corresponding point in the equivalent loop described above.
WHILE STATEMENT
The statement:
WHILE BE DO S;
is equivalent to the statements:
LOOP: IF BE THEN BEGIN
S;
GO TO LOOP
END;
DO STATEMENT
The statement:
DO S UNTIL BE;
is equivalent to the sequence:
LOOP: S;
IF BE THEN GO TO LOOP;
CASE STATEMENTS RETURN STATEMENT
CASE STATEMENTS
The statement:
CASE AE OF BEGIN S0; S1; S2 ... Sn END
is functionally equivalent to the statements:
TEMP_AE;
IF TEMP<0 THEN ERROR
ELSE IF TEMP = 0 THEN S0
ELSE IF TEMP = 1 THEN S1
ELSE IF TEMP = 2 THEN S2
...
ELSE IF TEMP = n THEN Sn
ELSE ERROR;
For applications of this type the CASE statement form will give
significantly more efficient code than the equivalent If statements. Notice
that dummy statements may be inserted for those cases which will not occur
or for which no entries are necessary. For example,
CASE AE OF BEGIN S0; ; ; S3; ; ; S6; END
provides for no actions when AE is 1, 2, 4, 5, or 7. When AE is 0, 3, or 6
the corresponding statement will be executed. However, slightly more
efficient code may be generated with a second type of Case statement that
numbers each of its statement with [n] where n is an integer constant. The
above example using this type of Case statement is then:
CASE AE OF BEGIN [3] S3; [0] S0; [6] S6 END;
All the statements must be numbered, and the numbers must all be non-
negative integer constant expressions, although they may be in any order.
Multiple case numbers may precede each statement; the statement is executed
for any one of the numbers specified. The following two CASE statements are
equivalent:
CASE AE OF BEGIN [4] [1] S41; [2] [3] S23 END;
CASE AE OF BEGIN [1] S41; [2] S23;
[3] S23; [4] S41 END;
Block names (i.e. any string constant) may be used after the BEGIN and END
of a Case statement with the same effect as block names on blocks or
compound statements. (See about block names on page 6).
RETURN STATEMENT
This statement is invalid if it appears outside a procedure declaration. It
provides for an early return from a Procedure execution to the statement
calling the Procedure. If no return statement is executed then the
DONE STATEMENT
Procedure will return after the last statement representing the procedure
body is executed (see page 17).
An untyped Procedure (see page 39) may not return a value. The return
statement for this kind of Procedure consists merely of the word RETURN. If
an argument is given then it will cause the compiler to issue an error
message.
A typed Procedure (see page 54) must return a value as it executes a return
statement. If no argument is present an error message will be given. If
the Procedure has an algebraic type then any algebraic expression may be
returned as its value; type conversion will be performed in a manner
described on page 46.
If no RETURN statement is executed in a typed Procedure then the value
returned is undefined.
DONE STATEMENT
The statement containing only the word DONE may be used to terminate the
execution of a FOR, WHILE, or DO (also FOREACH - see page 166) loop
explicitly. Its operation can most easily be seen by means of an example.
The statement
FOR I_1 STEP 1 UNTIL n DO BEGIN
S;
...
IF BE THEN DONE;
...
END
is equivalent to the statement
FOR I_1 STEP 1 UNTIL n DO BEGIN
S;
...
IF BE THEN GO TO EXIT;
...
END;
EXIT:
In either case the value of I is well-defined after the statement has been
executed (see page 35).
The DONE statement will only cause an escape from the innermost loop in
which it appears, unless a block name follows "DONE". The block name must
be the name of a block or compound statement (a "Loop Block") which is the
object statement of some FOR, WHILE, or DO statement in which the current
one is nested. The effect is to terminate all loops out to (and including)
the Loop Block, continuing with the statement following this outermost loop.
For example:
NEXT STATEMENT CONTINUE STATEMENT
WHILE TRUE DO BEGIN "B1"
...
IF OK THEN DO BEGIN "B2"
...
FOR I_1 STEP 1 UNTIL K DO
IF A[I]=FLAGWORD THEN DONE "B1";
...
END "B2" UNTIL COWSCOMEHOME;
...
END "B1";
Here the block named "B1" is the "loop block".
NEXT STATEMENT
A Next statement is valid only in a For Statement or a While Statement (or
Foreach - see page 166). Processing of the loop statement is temporarily
suspended. When the NEXT statement appears in a For loop, the next value
is obtained from the For List and assigned to the controlled variable. The
termination test is then made. If the termination condition is satisfied
then control is passed to the statement following the For Statement. If
not, control is returned to the inner statement following the NEXT
statement. In While and Do loops, the termination condition is tested. If
it is satisfied, execution of the loop terminates. Otherwise it resumes at
the statement within the loop following the NEXT statement.
Unless a block name follows NEXT, the innermost loop containing the NEXT
statement is used as the "Loop Block" (see page 37). The terminating
condition for the loop block is checked. If the condition is met then all
inner loops are terminated (in DONE fashion) as well. If continuation is
indicated then no inner-loop FOR-variable or WHILE-condition will have been
affected by the NEXT code.
The reserved word NEEDNEXT must precede FOR or WHILE in the "Loop Block",
and must not appear between this block and the NEXT statement. Example:
NEEDNEXT WHILE EOF DO BEGIN
S_INPUT(1,1);
NEXT;
Comment check EOF and terminate if TRUE;
T_INPUT(1,3);
PROCESSINPUT(S,T);
END;
CONTINUE STATEMENT
The Continue statement is valid in only those contexts valid for the DONE
statement (see page 37); the "Loop Block" is determined in the same way
(i.e., implicitly or by specifying a block name). All loops out to the Loop
Block are terminated as if DONE had been requested. Control is transferred
to a point inside the loop containing the Loop Block, but after all
statements in the loop. Example:
PROCEDURE STATEMENTS
FOR I_1 STEP 1 UNTIL N DO BEGIN
...
CONTINUE;
...
END
is semantically equivalent to:
FOR I_1 STEP 1 UNTIL N DO BEGIN
LABEL CONT;
...
GO TO CONT;
...
CONT:
END
PROCEDURE STATEMENTS
A Procedure statement is used to invoke the execution of a Procedure (see
page 17). After execution of the Procedure, control returns to the
statement immediately following the Procedure statement. Sail does allow
you to use typed Procedures as procedure statements. The value returned
from the Procedure is simply discarded.
The actual parameters supplied to a Procedure must match the formal
parameters described in the procedure declaration, modulo Sail type
conversion. Thus one may supply an integer expression to a real formal, and
type conversion will be performed as on page 46.
If an actual parameter is passed by VALUE then only the value of the
expression is given to the Procedure. This value may be changed or examined
by the Procedure, but this will in no way affect any of the variables used
to evaluate the actual parameters. Any algebraic expression may be passed
by value. Neither Arrays nor Procedures may be passed by value (use ARRBLT,
page 94, to copy arrays). See the default declarations for parameters in
page 21.
If an actual parameter is passed by REFERENCE then its address is passed to
the Procedure. All accesses to the value of the parameter made by the
Procedure are made indirectly through this address. Therefore any change
the Procedure makes in a reference parameter will change the value of the
variable which was used as an actual parameter. This is sometimes useful.
However, if it is not intended, use of this feature can also be somewhat
confusing as well as moderately inefficient. Reference parameters should be
used only where needed.
Variables, constants, Procedures, Arrays, and most expressions may be passed
by reference. No String expressions (or String constants) may be reference
parameters.
PROCEDURES AS ACTUAL PARAMETERS FORTRAN PROCEDURES
If an expression is passed by reference then its value is first placed in a
temporary location; a constant passed by reference is stored in a unique
location. The address of this location is passed to the Procedure.
Therefore, any values changed by the Procedure via reference parameters of
this form will be inaccessible to the user after the Procedure call. If the
called program is an assembly language routine which saves the parameter
address, it is dangerous to pass expressions to it, since this address will
be used by the compiler for other temporary purposes. A warning message
will be printed when expressions are called by reference.
The type of each actual parameter passed by reference must match that of its
corresponding formal parameter, modulo Sail type conversion. The exception
is reference string formals, which must have string variables (or string
array elements) passed to them. If an algebraic type mismatch occurs the
compiler will create a temporary variable containing the converted value and
pass the address of this temporary as the parameter, and a warning message
will be printed. An exception is made for Fortran calls (see page 40).
PROCEDURES AS ACTUAL PARAMETERS
If an actual parameter to a Procedure PC is the name of a Procedure PR with
no arguments then one of three things might happen:
1)If the corresponding formal parameter requires a value of a type
matching that of PR (in the loose sense given above in page 39),
the Procedure is evaluated and its value is sent to the Procedure
PC.
2) If the formal parameter of PC requires a reference Procedure of
identical type, the address of PR is passed to PC as the actual
parameter.
3) If the formal parameter requires a reference variable, the
Procedure is evaluated, its result stored, and its address passed
(as with expressions in the previous paragraph) as the parameter.
If a Procedure name followed by actual parameters appears as an actual
parameter it is evaluated (see functions, page 54). Then if the
corresponding formal parameter requires a value, the result of this
evaluation is passed as the actual parameter. If the formal parameter
requires a reference to a value, it is called as a reference expression.
FORTRAN PROCEDURES
If the Procedure being called is a Fortran Procedure, all actual parameters
must be of type INTEGER (BOOLEAN) or REAL. All such parameters are passed
by reference, since Fortran will only accept that kind of call. For
convenience, any constant or expression used as an actual parameter to a
Fortran Procedure is stored in a temporary cell whose address is given as
the reference actual parameter.
It was explained in page 17 that formal parameters need not be described for
NOWSAFE and NOWUNSAFE
Fortran Procedures. This allows a program to call a Fortran Procedure with
varying numbers of arguments. No type conversion will be performed for such
parameters, of course. If type conversion is desired, the formal parameter
declarations should be included in the Fortran procedure declaration; Sail
will use them if they are present.
To pass an Array to Fortran, mention the address of its first element (e.g.
A[0], or B[1, 1]).
NOWSAFE and NOWUNSAFE
The NOWSAFE and NOWUNSAFE statements both take a list of Array names
(names only - no indices) following them. From a NOWSAFE until the end of
the program or the next NOWUNSAFE, the specified arrays will not have
bounds checking code emitted for them. If an array has had a NOWSAFE done
on it, or has been declared SAFE, NOWUNSAFE will cause bounds checking code
to be emitted until the array is made safe again (if ever). Note that
NOWSAFE and NOWUNSAFE are compile time statements. "IF BE THEN NOWSAFE
..." will not work.
ALGOL EXPRESSIONS Syntax
SECTION 4
ALGOL EXPRESSIONS
4.1 Syntax
<expression>
::= <simpleexpression>
::= <conditionalexpression>
::= <assignmentexpression>
::= <caseexpression>
<conditionalexpression>
::= IF <booleanexpression> THEN
<expression> ELSE <expression>
<assignmentexpression>
::= <variable> _ <expression>
<caseexpression>
::= CASE <algebraicexpression> OF (
<expressionlist> )
<expressionlist>
::= <expression>
::= <expressionlist> , <expression>
<simpleexpression>
::= <algebraicexpression>
::= <leapexpression>
<booleanexpression>
::= <expression>
<algebraicexpression>
::= <disjunctiveexpression>
::= <algebraicexpression>
<disjunctiveexpression>
::= <algebraicexpression> OR
<disjunctiveexpression>
<disjunctiveexpression>
::= <negatedexpression>
::= <disjunctiveexpression>
<negatedexpression>
::= <disjunctiveexpression> AND
<negatedexpression>
<negatedexpression>
::= <relationalexpression>
::= NOT <relationalexpression>
::= <relationalexpression>
<relationalexpression>
::= <algebraicrelational>
::= <leaprelational>
<algebraicrelational>
::= <boundedexpression>
::= <relationalexpression>
<relationaloperator>
<boundedexpression>
<relationaloperator>
::= <
::= >
::= =
::=
::=
::=
::= LEQ
::= GEQ
::= NEQ
<boundedexpression>
::= <addingexpression>
::= <boundedexpression> MAX
<addingexpression>
::= <boundedexpression> MIN
<addingexpression>
<addingexpression>
::= <term>
::= <addingexpression> <addoperator>
<term>
<addingoperator>
::= +
::= -
::= LAND
::= LOR
::= EQV
::= XOR
<term>
::= <factor>
::= <term> <multoperator> <factor>
<multoperator>
::= *
::= /
::= %
::= LSH
::= ASH
::= ROT
::= MOD
::= DIV
::= &
<factor>
::= <primary>
::= <primary> ^ <primary>
<primary>
::= <algebraicvariable>
::= - <primary>
::= LNOT <primary>
::= ABS <primary>
::= <stringexpression> [ <substringspec> ]
::=
::= INF
::= <constant>
::= <functiondesignator>
::= LOCATION ( <locspecifier> )
::= ( <algebraicexpression> )
<stringexpression>
::= <algebraicexpression>
<substringspec>
Type Conversion
::= <algebraicexpression> TO
<algebraicexpression>
::= <algebraicexpression> FOR
<algebraicexpression>
<functiondesignator>
::= <procedurecall>
<locspecifier>
::= <variable>
::= <arrayidentifier>
::= <procedureidentifier>
::= <labelidentifier>
<algebraicvariable>
::= <variable>
4.2 Type Conversion
Sail automatically converts between the data types Integer, Real, String and
Boolean. The following table illustrates by description and example these
conversions. The data type boolean is identical to integer under the
mapping TRUE0 and FALSE=0.
F |To
r |
o | INTEGER REAL STRING
m|
I | | Left justify | Make a string
N | | and raise to | of 1 character
T | | appropriate | with the low
E | | power. | 7 bits for its
G | | 13451.345@3 | ASCII code.
E | | -678-6.78@2 | 48 "0"
R|||
| | |
R | Take greatest | | Convert to in-
E | integer. | | teger, then to
A | 1.345@2 134 | | string.
L | -6.71@1 -68 | | 4.8@1 "0"
| 2.3@-2 0 | | 4.899@1 "0"
|||
| The ASCII code| Convert to in-|
S | for the first | teger then |
T | character of | to real. |
R | string. | |
I | "0SUM" 48 | "0SUM" 4.8@1 |
N | NULL 0 | NULL 0 |
G|||
NOTES: The NULL string is converted to 0, but 0 is converted to the one
character string with the ASCII code of 0. If an integer requires more than
27 bits of precision (2^27 = 134217728) then some low order significance
will be lost in the conversion to real; otherwise, conversion to real and
then back to integer will result in the same integer value. If a real
number has magnitude greater than 2^35 - 2^8 (=34359738112) then conversion
to integer will produce an invalid result. UUOFIX does no error checking
for this case; KIFIX and FIXR will set Overflow and Trap 1.
The default instruction compiled for a real to integer conversion is a UUO
which computes FLOOR (x), the greatest integer function. This can be
changed with the /A switch (page 237) to one of several other instructions.
For real to integer conversion the choices are UUOFIX(opcode 003),
KIFIX(122) and FIXR(126); the effect of each is shown in the following
table.
real UUOFIX KIFIX FIXR
1.4 1 1 1
1.5 1 1 2
1.6 1 1 2
-1.4 -2 -1 -1
-1.5 -2 -1 -1
-1.6 -2 -1 -2
UUOFIX is the default. In mathematical terms, UUOFIX (x)=FLOOR (x)=[x]
where [x] is the traditional notation for the greatest integer less than or
equal to x. This UUO requires execution of 18.125 instructions (32 memory
references) on the average. Many FORTRANs use the function implemented by
KIFIX; KIFIX (x)=SIGN (x)*FLOOR (ABS (x)). Many ALGOLs use FIXR;
FIXR (x)=FLOOR (x+0.5). Note that FIXR (-1.5) is not equal to -FIXR (1.5).
For integer to real conversion the choices are UUOFLOAT(002) and FLTR(127).
FLTR rounds while UUOFLOAT (the default) truncates. It only makes a
difference when the magnitude of the integer being converted is greater than
134217728. In such cases it is always true that UUOFLOAT (i)i and
FLTR (i)i. UUOFLOAT merely truncates after normalization, while FLTR adds
+0.5 lsb and then truncates. Most users will never see the difference.
UUOFLOAT takes 18.625 instructions (32 memory references) on the average.
[For integer to real conversion involving a SHORT quantity, FSC ac,233 is
used. At SUAI real to integer conversion involving a SHORT quantity uses
KAFIX ac,233000; as this manual went to press KAFIX was simulated by the
system and was very expensive.]
The binary arithmetic, logical, and String operations which follow will
accept combinations of arguments of any algebraic types. The type of the
result of such an operation is sometimes dependent on the type of its
arguments and sometimes fixed. An argument may be converted to a different
algebraic type before the operation is performed. The following table
describes the results of the arithmetic and logical operations given various
combinations of Real and Integer inputs. ARG1 and ARG2 represent the types
of the actual arguments. ARG1' and ARG2' represent the types of the
arguments after any necessary conversions have been made.
Beware: automatic type conversion can be a curse as well as a blessing.
Study the conversion rules carefully; note that Sail has three division
operators, %, DIV, and /.
Semantics CONDITIONAL EXPRESSIONS
OPERATIONARG1 ARG2 ARG1'ARG2'RESULT
+ - INT INT INT INT INT*
* ^ % REAL INT REAL REAL REAL
MAX MIN INT REAL REAL REAL REAL
REAL REAL REAL REAL REAL
LAND LOR INT INT INT INT INT
EQV XOR REAL INT REAL INT REAL
INT REAL INT REAL INT
REAL REAL REAL REAL REAL
LSH ROT INT INT INT INT INT
ASH REAL INT REAL INT REAL
INT REAL INT INT INT
REAL REAL REAL INT REAL
/ INT INT REAL REAL REAL
REAL INT REAL REAL REAL
INT REAL REAL REAL REAL
REAL REAL REAL REAL REAL
MOD DIV INT INT INT INT INT
REAL INT INT INT INT
INT REAL INT INT INT
REAL REAL INT INT INT
* For the operator ^, ARG2' and RESULT are REAL unless ARG2 is a positive
integer constant.
4.3 Semantics
CONDITIONAL EXPRESSIONS
A conditional expression returns one of two possible values depending on the
logical truth value of the Boolean expression. If the Boolean expression
(BE) is true, the value of the conditional expression is the value of the
expression following the delimiter THEN. If BE is false, the other value is
used. If both expressions are of an algebraic type, the precise type of the
entire conditional expression is that of the "THEN part". In particular,
the "ELSE part" will be converted to the type of the "THEN part" before
being returned as the value of the conditional expression. Reread and
understand the last sentence.
Unlike the nested If statement problem, there can be no ambiguity for
conditional expressions, since there is an ELSE part in every such
expression. Example:
ASSIGNMENT EXPESSIONS CASE EXPRESSIONS SIMPLE EXPRESSIONS
FOURTHDOWN (YARDSTOGO,YARDLINE,
IF YARDLINE < 70 THEN PUNT ELSE
IF YARDLINE < 90 THEN FIELDGOAL ELSE
RUNFORIT)
ASSIGNMENT EXPESSIONS
The somewhat weird syntax for an assignment expression (it is equivalent to
that for an assignment statement) is nonetheless accurate: the two function
identically as far as the new value of the left part variable is concerned.
The difference is that the value of this left part variable is also retained
as the value of the entire expression. Assuming that the assignment itself
is legal (following the rules given in page 31 above), the type of the
expression is that of the left part variable. This variable may now
participate in any surrounding expressions as if it had been given its new
value in a separate statement on the previous line. Only the _ operator is
valid in assignment expressions. The operator is valid only at statement
level. Example:
IF (K_K+1) < 30 THEN K_0 ELSE K_K+1;
CASE EXPRESSIONS
The expression
CASE AE OF (E0, E1, E2, ... , En)
is equivalent to:
IF AE=0 THEN E0
ELSE IF AE=1 THEN E1
ELSE IF AE=2 THEN E2
...
ELSE IF AE=n THEN En
ELSE ERROR
The type of the entire expression is therefore that of E0. If any of the
expressions E1 ... En cannot be fit into this mold an error message is
issued by the compiler. Case expressions differ from Case statements in
that one may not use the [n] construct to number the expressions. Example:
OUT (TTY, CASE ERRNO OF ("BAD DIRECTORY",
"IMPROPER DATA MODE",
"UNKNOWN I/O ERROR",
...
"COMPUTER IN BAD MOOD"));
SIMPLE EXPRESSIONS
Simple expressions are simple only in that they are not conditional, case,
or assignment expressions. There are in fact some exciting complexities to
be discussed with respect to simple expressions.
PRECEDENCE OF ALGEBRAIC OPERATORS EXPRESSION EVALUATION RULES OR
PRECEDENCE OF ALGEBRAIC OPERATORS
The binary operators in Sail generally follow "normal" precedence rules.
That is, exponentiations are performed before multiplications or divisions,
which in turn are performed before additions and subtractions, etc. The
bounding operators MAX and MIN are performed after these operations. The
logical connectives and , when they occur, are performed last ( before
). The order of operation can be changed by including parentheses at
appropriate points.
In an expression where several operators of the same precedence occur at the
same level, the operations are performed from left to right. See page 50
for special evaluation rules for logical connectives.
TABLE OF PRECEDENCE
^
* / % & MOD DIV LSH ROT ASH
+ - LAND LOR
MAX MIN
= < > LEQ GEQ NEQ
AND
OR
EXPRESSION EVALUATION RULES
Sail does not evaluate expressions in a strictly left-to-right fashion. If
we are not constrained to a left-to-right evaluation, (as is ALGOL 60), we
can in some cases produce considerably better code than a strict left-to-
right scheme could achieve. Intuitively, the essential features (and
pitfalls) of this evaluation rule can be illustrated by a simple example:
b _ 2.6 ;
c _ b + (b _ b/2);
The second statement is executed as follows: divide b by 2 and assign this
value (1.3) to b. Add this value to b and assign the sum to c. Thus c gets
2.6. If the expressions were evaluated in a strictly left-to-right manner,
c would get 2.6 + 1.3.
The evaluation scheme can be stated quite simply: code is generated for the
operation represented by a BNF production when the reduction of that BNF
production takes place. That is, b + (b _ b/2) isn't reduced until after
(b _ b/2) is reduced, so the smaller expression gets done first.
"" (OR)
If an algebraic expression has as its major connective the logical
connective "", the expression has the logical value TRUE (arithmetic value
some non-zero integer) if either of its conjuncts (the expressions
surrounding the "") is true; FALSE otherwise. The reserved word OR is
equivalent to the symbol "". AB does NOT produce the bit-wise Or of A
and B if they are algebraic expressions. Truth values combined by numeric
AND NOT <>= RELATIONS
operators will in general be meaningless (use the operators LOR and LAND for
bit operations).
The user should be warned that in an expression containing logical
connectives, only enough of the expression is evaluated (from left to right)
to uniquely determine its truth value. Thus in the expression
(J<3 (K_K+1) > 0),
K will not be incremented if J is less than 3 since the entire expression is
already known to be true. Conversely in the expression
(X 0 SQRT(X)>2)
there is never any danger of attempting to extract the square root of a
negative X, since the failure of the first test testifies to the falsity of
the entire expression -- the SQRT routine is not even called in this case.
"" (AND)
If a disjunctive expression has as its major connective the logical
connective "", the expression has the logical value TRUE if both of its
disjuncts are TRUE; FALSE otherwise. Again, if the first disjunct is FALSE
a logical value of FALSE is obtained for the entire expression without
further evaluation. The reserved word AND is equivalent to "".
"" (NOT)
The unary Boolean operator applied to an argument BE (a relational
expression, see Syntax) has the value TRUE if BE is false, and FALSE if BE
is true. Notice that A is not the bitwise complement of A, if A is an
algebraic value. If used as an algebraic value, A is simply 0 if A0 and
some non-zero Integer otherwise. The reserved word NOT is equivalent to
"".
"<>=" (RELATIONS)
If any of the binary relational operators is encountered, code is produced
to convert any String arguments to Integer numbers. Then type conversion is
done as it is for the + operations (see page 46). The values thus obtained
are compared for the indicated condition. A Boolean value TRUE or FALSE is
returned as the value of the expression. Of course, if this expression is
used in subsequent arithmetic operations, a conversion to integer is
performed to obtain an integer value. The reserved words LEQ, GEQ, NEQ are
equivalent to "", "", "" respectively.
The syntax E1 RELOP1 E2 RELOP2 E3 where E1, E2, and E3 are expressions and
RELOP1, RELOP2 are relational operators, is specially interpreted as (E1
RELOP1 (T_E2)) (T RELOP2 E3). The compiler can sometimes produce better
code when the special syntax is used. Thus a bounds check may be written IF
L<I<U THEN ... . RELOP1 and RELOP2 may be any relational operators, and need
not be in transitive order. The following are equivalent:
MAX MIN +- ADDITION SUBTRACTION LAND LOR XOR EQV LNOT */% MULTIPLICATION DIVISION DIV MOD ASH LSH ROT
IF A < X > B THEN ... and
IF X > (A MAX B) THEN ...
MAX MIN
A MAX B (where A and B are appropriate expressions -- see the Syntax) has
the value of the larger of A and B (in the algebraic sense). Type
conversions are performed as if the operator were `+'. `0 MAX X MIN 10' is
X if 0X10, 0 if X<0, 10 if X>10.
"+-" (ADDITION AND SUBTRACTION)
The + and - operators will do integer addition (subtraction) if both
arguments are integers (or converted to integers from strings); otherwise,
rounded Real addition or subtraction, after necessary conversions, is done.
LAND LOR XOR EQV LNOT
LAND, LOR, XOR, and EQV carry out bit-wise And, Or, Exclusive Or, and
Equivalence operations on their arguments. No type conversions are done
for these functions. The logical connectives and do not have this
effect -- they simply cause tests and jumps to be compiled. The type of the
result is that of the first operand. This allows expressions of the form X
LAND '777777777, where X is Real, if they are really desired.
The unary operator LNOT produces the bitwise complement of its (algebraic)
argument. No type conversions (except strings to integers) are performed on
the argument. The type of the result (meaningful or not) is the type of the
argument.
"*/%" (MULTIPLICATION AND DIVISION)
The operation * (multiplication), like + and -, represents Integer
multiplication only if both arguments are integers; Real otherwise. Integer
multiplication uses the IMUL machine instruction -- no double-length result
is available.
The / operator (division) always does rounded Real division, after
converting any Integer arguments to Real.
The % (division) operator has the same type table as +, -, and *. It
performs whatever division is appropriate.
DIV MOD
DIV and MOD force both arguments to be integers before dividing. X MOD Y is
the remainder after X DIV Y is performed:
X MOD Y = X - (X DIV Y)*Y .
ASH LSH ROT
LSH and ROT provide logical shift operations on their first arguments. If
the value of the second argument is positive, a shift or rotation of that
many bits to the left is performed. If it is negative, a right-shift or
& CONCATENATION ^ EXPONENTIATION SUBSTRINGS
rotate is done. ASH does an arithmetic shift. Assume that A is an integer.
If N is positive then the expression A ASH N is equal to A * 2^N. If N is
negative then A ASH N is equal to FLOOR (A / 2^(-N)).
"&" (CONCATENATION)
This operator produces a result of type String. It is the String with
length the sum of the lengths of its arguments, containing all the
characters of the second string concatenated to the end of all the
characters of the first. The operands will first be converted to strings if
necessary as described in page 46 above. Numbers can be converted to
strings representing their external forms (and vice-versa) through explicit
calls on execution time routines like CVS and CVD (see page 86 below).
NOTE: Concatenation of constant strings will be done at compile time where
possible. For example, if SS is a string variable, SS&'12&'15 will result
in two runtime concatenations, while SS&('12&'15) will result in one compile
time concatenation and one runtime concatenation.
"^" (EXPONENTIATION)
A factor is either a primary or a primary raised to a power represented by
another primary. As usual, evaluation is from left to right, so that A^B^C
is evaluated as (A^B)^C. In the factor X^Y, a suitable number of
multiplications and additions is performed to produce an "exact" answer if Y
is a positive integer. Otherwise a routine is called to approximate
ANTILOG (Y LOG X). The result has the type of X in the former case. It is
always of type Real in the latter.
SUBSTRINGS
A String primary which is qualified by a substring specification represents
a part of the specified string. The characters of a string STR are numbered
1, 2, 3, ..., LENGTH (STR). ST[X FOR Y] represents the substring which is Y
characters long and begins with character X. ST[X TO Y] represents the Xth
through Yth characters of ST.
Consider the ST[X TO Y] case. This is evaluated
SKIP_FALSE; XT_X; YT_Y;
IF YT > LENGTH (ST) THEN BEGIN
YT_LENGTH (ST); righthalf (SKIP)_TRUE END;
IF YT < 0 THEN COMMENT result will be NULL;
BEGIN YT_0; righthalf (SKIP)_TRUE END;
IF XT < 1 THEN
BEGIN XT _ 1; lefthalf (SKIP)_TRUE END;
IF XT > YT THEN COMMENT result will be NULL;
BEGIN XT _ YT+1; lefthalf (SKIP)_TRUE END;
<return the XTth through YTth characters of ST>
LENGTH returns the number of characters in a string (see page 88). The
ST[X FOR Y] operation is converted to the ST[X TO Y] case before the
substring operation is performed. The variable SKIP can be examined to
determine if the substring indices were "out of bounds".
INFINITY SPECIAL LENGTH OPERATOR FUNCTION DESIGNATORS UNARY OPERATORS MEMORY AND LOCATION
"" (SPECIAL LENGTH OPERATOR)
This special primary construct is valid only within substring brackets. It
is an algebraic value representing the length of the most immediate string
under consideration. The reserved word INF is equivalent to "".
Example:
A[-2 to ] yields the last 3 characters of A.
A[3 for B[-1 for 1]] uses the next to the last character of string
B as the number of characters for the A substring operation.
FUNCTION DESIGNATORS
A function designator defines a single value. This value is produced by the
execution of a typed user Procedure or of a typed execution-time routine
(See chapters 6 and 7 for execution-time routines). For a function
designator to be an algebraic primary, its Procedure must be declared to
have an algebraic type. Untyped Procedures may only be called as Procedure
statements (see page 39). The value obtained from a user-defined Procedure
is that provided by a Return Statement within that Procedure.
The rules for supplying actual parameters in a function designator are
identical to those for supplying parameters in a procedure statement (see
page 39).
UNARY OPERATORS
The unary operator ABS is valid only for algebraic quantities. It returns
the absolute value of its argument.
-X is equivalent to (0-X). No type conversions are performed.
X is the logical negation of X. MEMORY AND LOCATION
One's core image can be considered a giant one dimensional array, which may
be accessed with the MEMORY construct. You had better be a good sport, or
know what you are doing.
MEMORY [ <integer expression> ]
One can store and retrieve from the elements of MEMORY just as with any
other array. However, with MEMORY, one can control how the compiler
interprets the type of the accessed element by including type declarator
reserved words after the <integer expression>. For example:
..._ MEMORY[X, INTEGER]
MEMORY[X, REAL] _ ...
..._ MEMORY[X, ITEM]
COMMENT items and sets are part of Leap;
MEMORY[X, SET] _ ...
..._ MEMORY[X, INTEGER ITEMVAR]
Note that one can not specify the contents of memory to be an Array or a
String.
LOCATION
LOCATION is a predeclared Sail routine that returns the index in MEMORY of
the Sail construct furnished it. The following is a list of constructs it
can handle and what LOCATION will return.
CONSTRUCT xLOCATION (x) RETURNS
variable address of the variable
string variable -1,,address of word2
array name address of a word containing the the address of the first data
word of the array
array element address of that element
procedure name address of the procedure's entry code
labels address of the label
Simple example:
REAL X;
MEMORY [LOCATION (X), REAL] _ 2.0;
PRINT (X); COMMENT " 2.000000 ";
MEMORY [LOCATION (X)] _ 2.0; PRINT (X);
COMMENT " .0000000@-39", MEMORY is INTEGER
unless otherwise specified;
MEMORY [LOCATION (X), INTEGER] _ 2.0;
PRINT (X); COMMENT same as above;
ASSEMBLY LANGUAGE STATEMENTS Syntax
SECTION 5
ASSEMBLY LANGUAGE STATEMENTS
5.1 Syntax
<codeblock>
::= <codehead> <codetail>
<codehead>
::= <codebegin>
::= <codebegin> <blockname>
::= <codehead> <declaration> ;
<codebegin>
::= STARTCODE
::= QUICKCODE
<codetail>
::= <instruction> END
::= <instruction> END <blockname>
::= <instruction> ; <codetail>
<instruction>
::= <addresses>
::= <opcode>
::= <opcode> <addresses>
<addresses>
::= <address>
::= <acfield> ,
::= <acfield> , <address>
<acfield>
::= <constantexpression>
<address>
::= <indexedaddress>
::= @ <indexedaddress>
Semantics DECLARATIONS IN CODE BLOCKS
<indexedaddress>
::= <simpleaddress>
::= <simpleaddress> ( <indexfield> )
<simpleaddress>
::= <identifier>
::= <staticarrayname> [
<constantsubscriptlist> ]
::= <constantexpression>
::= <literal>
<literal>
::= [ <constantexpression> ]
<indexfield>
::= <constantexpression>
<opcode>
::= <constantexpression>
::= <PDP-10opcode>
5.2 Semantics
Within a STARTCODE (QUICKCODE) block, statements are processed by a small
and weak, but hopefully adequate, assembly language translator. Each
"instruction" places one instruction word into the output file. An
instruction consists of
<label>:<opcode> <acfield>, @<simpleaddr> (<index>)
or some subset thereof (see syntax). Each instruction must be followed by a
semi-colon.
DECLARATIONS IN CODE BLOCKS
A codeblock behaves like any other block with respect to block structure.
Therefore, all declarations are valid, and the names given in these
declarations will be available only to the instructions in the codeblock.
All labels must be declared as usual. Labels in codeblocks may refer to
instructions which will be executed, or to those which are not really
instructions, but data to be manipulated by these instructions (these latter
words must be bypassed in the code by jump instructions). The user may find
it easier to declare variables or SAFE arrays as data areas rather than
using labels and null statements. As noted below, identifiers of simple
variables are addresses of core locations. Identifiers of arrays are
PROTECT ACS DECLARATION OPCODES THE <simple addr> FIELD
addresses of the first word of the array header (see the appendix on array
implementation).
PROTECT ACS DECLARATION
PROTECTACS <ac #>, ... , <ac #>;
where <ac #> is an integer constant between 0 and '17, is a declaration.
Its effect is to cause Sail not to use the named accumulators in the code it
emits for the block in which the declaration occurred (only AFTER the
declaration). The most common use is with the ACCESS construct (see below);
if one is using accumulators 2, 3, and 4 in a code block, then one should
declare PROTECTACS 2, 3, 4 if one is going to use ACCESS. This way, the
code emitted by Sail for doing the ACCESS will not use accumulators 2, 3, or
4. WARNING: this does not prevent you from clobbering such ACs with
procedure calls (your own procedures or Sail's). However, most Sail
runtimes save their ACs and restore them after the call.
RESTRICTION: Accumulators P ('17), SP ('16), F ('12) and 1 are used for,
respectively, the system push down pointer, the string push down pointer,
the display pointer, and returning results from typed procedures and
runtimes. More about these acs on page 60. The protect mechanism will not
override these usages, so attempts to protect 1, '12, '16, or '17 will be
futile.
OPCODES
The Opcode may be a constant provided by the user, or one of the standard
(non I/O) PDP-10 operation codes, expressed symbolically. If a constant, it
should take the form of a complete PDP-10 instruction, expressed in octal
radix (e.g. DEFINE TTYUUO = "'51000000000";). Any bits appearing in fields
other than the opcode field (first 9 bits) will be OR'ed with the bits
supplied by other fields of instructions in which this opcode appears. In
TOPS-10 Sail the MUUOs (ENTER, LOOKUP, etc.) are available. In TENEX Sail
the JSYSes are available. Within a codeblock opcodes supersede all other
objects; a variable, macro, or procedure with the same name as an opcode
will be taken for the opcode instead.
The indirect, index, and AC fields have the same syntax and perform the same
functions as they do in the FAIL or MACRO languages.
THE <simple addr> FIELD
If the <address> in an instruction is a constant (constant expression), it
is assumed to be an immediate or data operand, and is not relocated.
If the <address> is an identifier, the machine address (relative to the
start of the compilation) is used, and will be relocated to the proper value
by the Loader.
If the <address> is an identifier which has been declared as a formal
parameter to a procedure, addressing arithmetic will be done automatically
STARTCODE VERSUS QUICKCODE
to get at the VALUE of the parameter. Hence if the <address> is a formal
reference parameter, the instruction will be of the form OP AC,@ -x('12)
where x depends on exactly where the parameter is in the stack. If the
formal was from a simple procedure, then '17 will be used as the index
register rather than '12. When computing x Sail will assume that the stack
pointer has not changed since the last procedure entry; if you use PUSH,
POP, etc. in a Simple Procedure then you must calculate x yourself.
If a literal is used, the address of the compiled constant will be placed in
the instruction.
Any reference to Strings will result in the address of the second descriptor
word (byte pointer) to be placed in the instruction (see the appendix on
string implementation for an explanation of string descriptors).
Accessing parameters of procedures global to the current procedure is
difficult. ACCESS (<expr>) may be used to return the address of such
parameters. ACCESS will in fact do all of the computing necessary to obtain
the value of the expression <expr>, then return the address of that value
(which might be a temporary). Thus, MOVE AC, ACCESS(GP) will put the value
of the variable GP in AC, while MOVEI AC, ACCESS(GP) will put the address of
the variable GP in AC. If the expression is an item expression (see Leap),
then the item's number will be stored in a temp, and that temp's address
will be returned. The code emitted for an Access uses any acs that Sail
believes are available, so one must include a PROTECTACS declaration in a
Code block that uses ACCESS if you want to protect certain acs from being
munged by the Access. WARNING: skipping over an Access won't do the right
thing. For example,
SKIPE FLAG;
MOVE '10,ACCESS ('777 LAND INTIN(CHAN));
MOVEI '10,0;
will cause the program to skip into the middle of the code generated by the
access if FLAG is 0.
STARTCODE VERSUS QUICKCODE
Before your instructions are parsed in a block starting with STARTCODE,
instructions are executed to leave all accumulators from 0 through '11 and
'13 through '15 available for your use. In this case, you may use a JRST to
transfer control out of the codeblock, as long as you do not leave (1) a
procedure, (2) a block with array declarations, (3) a Foreach loop, (4) a
loop with a For list, or (5) a loop which uses the NEXT construct. In a
QUICKCODE block, no accumulator-saving instructions are issued. Ac's '13
through '15 only are free. In addition, some recently used variables may be
given the wrong values if used as address identifiers (their current values
may be contained in Ac's 0-'11); and control should not leave the codeblock
except by "falling through".
ACCUMULATOR USAGE IN CODE BLOCKS CALLING PROCEDURES FROM INSIDE CODE BLOCKS
ACCUMULATOR USAGE IN CODE BLOCKS
Although we have said that accumulators are "freed" for your use, this does
not imply a carte blanche. Usually this means the compiler saves values
currently stored in the ACs which it wants to remember (the values of
variables mostly), and notes that when the code block is finished, these ACs
will have values in them that it doesn't care about. However, this is not
the case with the following accumulators, which are not touched at all by
the entrance and exit of code blocks:
NAME NUMBER USAGE
P '17 The system push down list pointer. All procedures are
called with a PUSHJ P, PROC and exited (usually) with a
POPJ P. Use this as your PDL pointer in the code block, but be
sure that its back to where it was on entrance to the block by
the time you exit.
SP '16 The string push down stack pointer. Used in all string
operations. For how to do your own string mangling, read the
code.
F '12 This is used to maintain the "display" structure of
procedures. DO NOT HARM AC F!! Disaster will result. A more
exact description of its usage may be found in the appendix on
procedures and by reading the code.
CALLING PROCEDURES FROM INSIDE CODE BLOCKS
To call a procedure (say, PROT) from inside a code block, use PUSHJ P, PROT.
If the procedure requires parameters, PUSH P them in order before you
PUSHJ P (i.e. the first one first, the second one next, etc.). If the
formal is a reference, push the address of the actual onto the P stack. If
the formal is a value string, push onto the SP stack the two words of the
string descriptor (see the appendix on string implementation for an
explanation of string descriptors). If the formal is a reference string,
simply PUSH P the address of the second word of the string descriptor. If
the procedure is typed, it will return is value in AC 1, except that STRING
procedures return their values as the top element of the SP stack. More
information can be found in the appendix on procedure implementation.
Example:
BEWARE
INTEGER K; STRING S, SS;
INTEGER PROCEDURE PROT (REAL T; REFERENCE
INTEGER TT; STRING TTT; REFERENCE
STRING TTTT);
BEGIN COMMENT BODY; END;
DEFINE P = '17, SP = '16;
STARTCODE
PUSH P, [3.14159];
MOVEI1, K;
PUSH P, 1;
MOVEI 1, S;
PUSH SP, -1(1);COMMENT if Sail allowed address
arithmetic in Startcode, you
could have said PUSH SP, S-1;
PUSH SP, S;
MOVEI1, SS;
PUSH P, 1;
PUSHJ P,PROT;
END;
gives the same effect as
PROT (3.14159, K, S, SS);
NOTE: procedures will change your accumulators unless the procedure takes
special pains to save and restore them.
BEWARE
The Sail <code block> assembler is not FAIL or MACRO. Read the syntax!
Address arithmetic is not permitted. All integer constants are decimal
unless specified explicitly as octal (e.g., '120). Each instruction is a
separate <statement> and must be separated from surrounding statements by a
semicolon. If you want comments then use COMMENT just like anywhere else in
Sail. QUICKCODE is for wizards.
INPUT/OUTPUT ROUTINES Execution-time Routines in General SCOPE NOTATIONAL CONVENTIONS
SECTION 6
INPUT/OUTPUT ROUTINES
6.1 Execution-time Routines in General
SCOPE
A large set of predeclared, built-in procedures and functions have been
compiled into a library permanently resident on the system disk area
(SYS:LIBSAn.REL or <SAIL>LIBSAn.REL - n is the current version number;
HLBSAn for /H compilations), and optionally into a special sharable write-
protected high segment. The library also contains programs for managing
storage allocation and initialization, and for certain String functions. If
a user calls one of these procedures, a request is automatically made to the
loader to include the procedure, and any other routines it might need, in
the core image (or to link to the high segment). These routines provide
input/output (I/O) facilities, Arithmetic-String conversion facilities,
array-handling procedures and miscellaneous other interesting functions.
The remainder of this section and the next describes the calling sequences
and functions of these routines.
NOTATIONAL CONVENTIONS
A short-hand is used in these descriptions for specifying the types (if any)
of the execution-time routines and of their parameters. Before the
description of each routine there is a sample call of the form
VALUE _ FUNCTION (ARG1, ARG2, ... ARGn)
If VALUE is omitted, the procedure is an untyped one, and may only be called
at statement level (page 39).
The types of VALUE and the arguments may be determined using the following
scheme:
1) If " characters surround the sample identifier (which is usually
mnemonic in nature) a String argument is expected. Otherwise the
argument is Integer or Real. If it is important which of the types
Integer or Real must be presented, it will be made clear in the
description of the function. Otherwise the compiler assumes
Integer arguments (for those functions which are predeclared). The
user may pass Real arguments to these routines by re-declaring them
in the blocks in which the Real arguments are desired.
2) If the @ character precedes the sample identifier, the argument
will be called by reference. Otherwise it is a value parameter.
Example:
SKIP I/O Channels and Files OPEN
"RESULT" _ SCAN (@"SOURCE", BREAKTABLE, @BRCHAR)
is a predeclared procedure with the implicit declaration:
EXTERNAL STRING PROCEDURE SCAN
(REFERENCE STRING SOURCE;
INTEGER BREAKTABLE;
REFERENCE INTEGER BRCHAR);
SKIP
Some routines return secondary values by storing them in SKIP. Declare
EXTERNAL INTEGER SKIP if you want to examine these values. In FAIL or DDT
the spelling is ".SKIP.".
6.2 I/O Channels and Files
********************************** OPEN ********************************;
OPEN (CHANNEL, "DEVICE", MODE,
NUMBEROFINPUTBUFFERS,
NUMBEROFOUTPUTBUFFERS,
@COUNT, @BRCHAR, @EOF);
Sail input/output operates at a very low level in the following sense: the
operations necessary to obtain devices, open and close files, etc., are
almost directly analogous to the system calls used in assembly language.
OPEN is used to associate a channel number (0 to '17) with a device, to
determine the data mode of the I/O to occur on this channel (character mode,
binary mode, dump mode, etc.), to specify storage requirements for the data
buffers used in the operations, and to provide the system with information
to be used for input operations. See page 4 for an example of TOPS-10 I/O
programming.
CHANNEL is a user-provided channel number which will be used in subsequent
I/O operations to identify the device. CHANNEL may range from 0 to 15
('17). A RELEASE will be performed before the OPEN is executed.
DEVICE must be a String (i.e. "TTY", "DSK") which is recognizable by the
system as a physical or logical device name.
MODE is the data mode for the I/O operation. MODE 0 will always work for
characters (see INPUT, page 72 and OUT, page 74). Modes 8 ('10)
and 15 ('17) are applicable for binary and dump-mode operations using
the functions WORDIN, WORDOUT, ARRYIN, or ARRYOUT (see page 75 and
following). For other data modes, see [SysCall]. If any of bits 18-
21 are on in the MODE word, the I-O routines will not print error
messages when data errors occur which present the corresponding bits
as a response to the GETSTS UUO. Instead, the GETSTS bits will be
reported to the user as described under EOF below. If bit 23 is on,
no error message will be printed if an invalid file name
specification is presented to LOOKUP, ENTER, or RENAME, a code
identifying the problem will be returned (see page 67 ff. for
details). If you don't understand any of this, leave all non-mode
bits off in the MODE word.
NUMBEROF{INPUT/OUTPUT~BUFFERS specifies the number of buffers to be
reserved for the I/O operations. At least one buffer must be
specified for input if any input is to be done in modes other than
'17; similarly for output. If data is only going one direction, the
other buffer specification should be 0. Two buffers give reasonable
performance for most devices (1 is sufficient for a TTY, more are
required for DSK if rapid operation is desired). The left half of
the BUFFER parameter, if non-zero, specifies the buffer size (mod
'7777) for the I/O buffers. Use this only if you desire non-standard
sizes.
The remaining arguments are applicable only for INPUT (String input). They
will be ignored for any other operations (although their values may be
changed by the Open function).
COUNT designates a variable which will contain the maximum number of
characters to be read from "DEVICE" in a given INPUT call (see page
72, page 68). Fewer characters may be read if a break character
is encountered or if an end of file is detected. The count should be
a variable or constant (not an expression), since its address is
stored, and the temporary storage for an expression may be re-used.
BRCHAR designates a variable into which the break character (see INPUT and
BREAKSET again) will be stored. This variable can be tested to
determine which of many possible characters terminated the read
operation.
EOF designates a variable to be used for two purposes:
1) Error handling when OPEN is called. If the system call used by
OPEN succeeds then EOF is set to zero and OPEN returns. If the
system call fails then OPEN looks at the EOF variable; if it is
nonzero then OPEN returns. If EOF is zero then the user is given
the option of retrying or continuing without the device. If a
retry is successful then EOF is zeroed. If the user proceeds
(gives up) then EOF is set to nonzero. The net effect is that the
program may interpret EOF=0 as a successful OPEN and EOF0 as an
unsuccessful OPEN.
2) Error handling for subsequent I/O operations. EOF will be made
non-zero (TRUE) if an end of file condition, or any error
condition among those enabled (see MODE, above) is detected during
any Sail input/output operation. It will be 0 (FALSE) on return
to the user otherwise. Subsequent inputs after an EOF return will
return non-zero values in EOF and a null String result for INPUT.
For ARRYIN, a 0 is returned as the value of the call after end of
file is detected. If EOF is TRUE after such an operation, it will
contain the entire set (18 bits) of GETSTS information in the left
half. The EOF bit is '20000, and is the only one you'll ever see
if you haven't specially enabled for others.
Here are the error bits for SUAI and TOPS-10; TENEX Sail uses the ERSTR
error number instead.
400000 improper mode (a catchall)
200000 parity error
100000 data error
40000 record number out of bounds
20000 end of file (input only)
You are always enabled for bit 20000 (EOF). However, to be allowed to
handle any of the others, you must turn on the corresponding bit in the
right half of the MODE word. In addition, the 10000 bit is used to enable
user handling of invalid file specifications to ENTER, LOOKUP, and RENAME.
'7500017 in the MODE parameter would enable a dump mode file for user
handling of ALL I/O errors on the channel. If you are not enabled for a
given error, an error message (which may or may not be fatal) will be
printed, and the error code word set as indicated. In addition, the number
of words actually transferred is stored in the right half of the EOF
variable for ARRYIN, ARRYOUT.
Assembly Language Approximation to OPEN:
INIT CHANNEL, MODE
SIXBIT /DEVICE/
XWD OHED,IHED
JRST <handle error condition>
JUMPE <NUMBEROFOUTPUTBUFFERS>, GETIN
<allocate buffer space>
OUTBUF CHANNEL, NUMBEROFOUTPUTBUFFERS
GETIN: JUMPE <NUMBEROFINPUTBUFFERS>, DONE
<allocate buffer space>
INBUF CHANNEL, NUMBEROFINPUTBUFFERS
DONE: <mark channel open -- internal bookkeeping>
<return>
OHED: BLOCK 3
IHED: BLOCK 3
CLOSE, CLOSIN, CLOSO GETCHAN RELEASE
************************** CLOSE, CLOSIN, CLOSO ************************;
CLOSE (CHANNEL, BITS(0));
CLOSIN (CHANNEL, BITS(0));
CLOSO (CHANNEL, BITS(0))
The input (CLOSIN) or output (CLOSO) side of the specified channel is
closed: all output is forced out (CLOSO); the current file name is
forgotten. However the device is still active; no OPEN need be done again
before the next LOOKUP/ENTER operation. Always CLOSE output files: Sail
exit code will deassign the device, but does not force out any remaining
output; you must do a CLOSE when writing on a disk file to have the new file
(or a newly edited old file) entered on your User File Directory. No INPUT,
OUT, etc., may be given to a directory device until an ENTER, LOOKUP, or
RENAME has been issued for the channel.
CLOSE is equivalent to the execution of both CLOSIN and CLOSO for the
channel. BITS specifies the close inhibit bits, which default to zero. See
[SysCall] for the interpretation of the bits.
******************************** GETCHAN *******************************;
VALUE _ GETCHAN
GETCHAN returns the number of some channel which Sail believes is not
currently open. The value -1 is returned if all channels are busy.
******************************** RELEASE *******************************;
RELEASE (CHANNEL, BITS(0))
If an OPEN has been executed for this channel, a CLOSE is now executed for
it. The device is dissociated from the channel and returned to the resource
pool (unless it has been assigned by the monitor ASSIGN command). No I/O
operation may refer to this channel until another OPEN denoting it has been
executed. BITS specifies the CLOSE inhibit bits; see [SysCall].
Release is always valid. If the channel mentioned is not currently open,
the command is simply ignored.
LOOKUP, ENTER RENAME
***************************** LOOKUP, ENTER ****************************;
LOOKUP (CHANNEL, "FILE", @FLAG);
ENTER (CHANNEL, "FILE", @FLAG)
Before input or output operations may be performed for a directory device
(DECtape or DSK) a file name must be associated with the channel on which
the device has been opened (see page 63). LOOKUP names a file which is to
be read. ENTER names a file which is to be created or extended (see
[SysCall]). It is recommended that an ENTER be performed after every OPEN
of an output device so that output not normally directed to the DSK can be
directed there for later processing if desired. The format for a file name
string is
"NAME", or
"NAME.EXT", or
"NAME[P,PN]", or
"NAME.EXT[P,PN]", or
"NAME.EXT[P,PN"
See [MonCom] for the meaning of these things if you do not immediately
understand.
Sail is not as choosy about the characters it allows as some processors are.
Any character which is not a comma, period, right square bracket, or left
square bracket will be passed on. Up to 6 characters from NAME, 3 from EXT,
P, or PN will be used -- the rest are ignored.
If the LOOKUP or ENTER operation fails then variable FLAG may be examined
to determine the cause. The left half of FLAG will be set to '777777 (Flag
has the logical value TRUE). The right half will contain the code returned
by the system giving the cause of the failure. An invalid file
specification will return a code of '10. In this case, if the appropriate
bit (bit 23, see OPEN) was OFF in the MODE parameter of the OPEN, an error
message will be printed; otherwise, the routine just returns without
performing the UUO.
If the LOOKUP or ENTER succeeds, FLAG will be set to zero (FALSE).
********************************* RENAME *******************************;
RENAME (CHANNEL, "FILE-SPEC",
PROTECTION, @FLAG)
The file open on CHANNEL is renamed to FILESPEC (a NULL file-name will
delete the file) with read/write protection as specified in PROTECTION (nine
bits, described in [SysCall]). FLAG is set as in LOOKUP and ENTER.
ERENAME Break Characters BREAKSET
******************************** ERENAME *******************************;
ERENAME (CHANNEL,"FILE-SPEC",
PROTECTION, DATE, TIME,
MODE, @FLAG)
(Not on TENEX.) This extended version of RENAME allows complete
specification of all the data which may be changed by a RENAME.
6.3 Break Characters
******************************** BREAKSET ******************************;
BREAKSET (TABLE, "BREAKCHARS", MODE)
Character input/output is done using the String features of Sail. In fact,
I/O is the chief justification for the existence of strings in the language.
String input presents a problem not present in String output. The length of
an output String can be used to determine the number of characters written.
However it is often awkward to require an absolute count for input. Quite
often one would like to terminate input, or "break", when one of a specified
set of characters is encountered in the input stream. In Sail, this
capability is implemented by means of the BREAKSET, INPUT, TTYIN, and SCAN
functions. The value of TABLE may range from -17 to 54, but tables -17
through -1 are reserved for use by the runtime system. Thus up to 54
different sets of user break specifications may exist at once. Which set
will be used is determined by the TABLE parameter in an INPUT or SCAN
function call. Breaktables are dynamically allocated in blocks of 18 (1-18,
19-36, 37-54).
BREAKSET merely modifies the existing settings in TABLE; use GETBREAK (which
returns a virgin table) if you want to achieve an absolute known state.
The function of a given BREAKSET command depends on the MODE, an integer
which is interpreted as a right-justified ASCII character whose value is
intended to be vaguely mnemonic. BREAKSET commands can be partitioned into
4 groups according to mode:
GROUP 0 -- Conversion specifications
MODE FUNCTION
"K" (Konvert) The minuscule letters (a-z) will be converted to
majuscule (A-Z) before doing anything else.
"F" (Full character set) Undoes the effect of "K". Mode "F" is the
default.
"Z" (Zero bytes) Believe the breaktable when INPUT reads a zero byte.
INPUT automatically omits zero characters otherwise. Mode "Z" is
turned off by both mode "I" and mode "X".
GROUP 1 -- Break character specifications
MODE FUNCTION
"I" (by Inclusion) The characters in the BREAKCHARS String comprise
the set of characters which will terminate an INPUT (or SCAN).
"X" (by eXclusion) Only those characters (of the possible 128 ASCII
characters) which are NOT contained in the String BREAKCHARS will
terminate an input when using this table.
"O" (Omit) The characters in "BREAKCHARS" will be omitted (deleted)
from the input string.
Any "I" or "X" command completely specifies the break character set for its
table (i.e., the table is reset before these characters are stored in it).
Neither will destroy the omitted character set currently specified for this
table. Any "O" command completely specifies the set of omitted characters,
without altering the break characters for the table in question. If a
character is a break-character, any role it might play as an omitted
character is sacrificed.
The next group of MODEs determines the disposition of break characters in
the input stream. The "BREAKCHARS" argument is ignored in these commands,
and may in fact be NULL:
GROUP 2 -- Break character disposition
MODE FUNCTION
"S" (Skip -- default mode) After execution of an "S" command the break
character will not appear either in the resultant String or in
subsequent INPUTs or SCANs-- the character is "skipped". Its value
may be determined after the INPUT by examination of the break
character variable (see page 63).
"A" (Append) The break character (if there is one -- see page 63 and
page 72) is appended, or concatenated to the end of the input
string. It will not appear again in subsequent inputs.
"R" (Retain) The break character does not appear in the resultant INPUT
or SCAN String, but will be the first character processed in the
next operation referring to this input source (file or SCAN
String).
Text files containing line numbers present a special problem. A line number
is a word containing 5 ASCII characters representing the number in bits 0-
34, with a "1" in bit 35. No other words in the file contain 1's in bit 35.
Since String manipulations provide no way for distinguishing line numbers
from other characters, there must be a way to warn the user that line
numbers are present, or to allow him to ignore them entirely.
The next group of MODEs determines the disposition of these line numbers.
Again, the "BREAKCHARS" argument is ignored:
Group 3 -- Line number disposition
MODE FUNCTION
"P" (Pass -- default) Line numbers are treated as any other characters.
Their identity is lost; they simply appear in the result string.
"N" (No numbers) No line number (or the TAB which always follows it in
standard files) will appear in the result string. They are simply
discarded.
"L" (Line no. break) The result String will be terminated early if a
line number is encountered. The characters comprising the line
number and the associated TAB will appear as the next 6 characters
read or scanned from this character source. The user's break
character variable (see page 63 and page 72) will be set to -1 to
indicate a line number break.
"E" (Lee Erman's very own mode) The result String is terminated on a
line number as with "L", but neither the line number nor the TAB
following it will appear in subsequent inputs. The line number
word, negated, is returned in the user's (integer) BRCHAR variable.
"D" (Display) obsolete
Once a break table is set up, it may be referenced in an INPUT, TTYIN or
SCAN call to control the scanning operation.
Example: To delimit a "word", a program might wish to input characters until
a blank, a TAB, a line feed, a comma, or a semicolon is encountered,
ignoring line numbers. Assume also that carriage returns are to be ignored,
and that the break character is to be retained in the character source for
the next scanning operation:
SETBREAK GETBREAK, RELBREAK
BREAKSET (DELIMS, " , ;"&TAB&LF, "I");
Comment break on any of these;
BREAKSET (DELIMS, '15, "O");
Comment ignore carriage return;
BREAKSET (DELIMS, NULL, "N");
Comment ignore line numbers;
BREAKSET (DELIMS, NULL, "R");
Comment save break char for next time;
Breaktable 0 is builtin as equivalent to SETBREAK (0, NULL, NULL, "I").
This is break-on-count for INPUT and returns the whole string from SCAN.
****************************** SETBREAK ******************************;
SETBREAK (TABLE, "BREAKCHARS",
"OMITCHARS", "MODES")
SETBREAK is logically equivalent to the Sail statement:
BEGIN "SETBREAK"
INTEGER I;
IF LENGTH (OMITCHARS) > 0 THEN
BREAKSET (TABLE, OMITCHARS, "O");
FOR I_1 STEP 1 UNTIL LENGTH (MODES) DO
BREAKSET (TABLE, BREAKCHARS, MODES[I FOR 1])
END "SETBREAK"
*************************** GETBREAK, RELBREAK *************************;
TABLE _ GETBREAK;
RELBREAK (TABLE)
GETBREAK finds an unreserved breaktable, reserves it, sets it to a
completely virgin state, and returns the number of the table. GETBREAK
returns -18 if there are no free tables. Breaktables are reserved by
GETBREAK, SETBREAK, BREAKSET, and STDBRK. RELBREAK returns a table to the
available list.
STDBRK I/O Routines INPUT
********************************* STDBRK *******************************;
STDBRK (CHANNEL)
Eighteen breakset tables have been selected as representative of the more
common input scanning operations. The function STDBRK initializes the
breakset tables by opening the file SYS:BKTBL.BKT on CHANNEL and reading
in these tables. The user may then reset those tables which he does not
like to something he does like.
The eighteen tables are described here by giving the SETBREAKs which would
be required for the user to initialize them:
DELIMS _ '15 & '12 & '40 & '11 & '14;
Comment carriage return, line feed, space,
tab, form feed;
LETTS _ "ABC ... Zabc ... z";
DIGS _ "0123456789";
SAILID _ LETTS&DIGS;
SETBREAK ( 1, '12, '15, "INS");
SETBREAK ( 2, '12, NULL, "INA");
SETBREAK ( 3, DELIMS, NULL, "XNR");
SETBREAK ( 4, SAILID, NULL, "INS");
SETBREAK ( 5, SAILID, NULL, "INR");
SETBREAK ( 6, LETTS, NULL, "XNR");
SETBREAK ( 7, DIGS, NULL, "XNR");
SETBREAK ( 8, DIGS, NULL, "INS");
SETBREAK ( 9, DIGS, NULL, "INR");
SETBREAK (10, DIGS&"+-@.", NULL, "XNR");
SETBREAK (11, DIGS&"+-@.", NULL, "INS");
SETBREAK (12, DIGS&"+-@.", NULL, "INR");
SETBREAK (13-18, NULL, NULL, NULL);
6.4 I/O Routines
********************************* INPUT ********************************;
"RESULT" _ INPUT (CHANNEL, BREAKTABLE)
A string of characters is obtained for the file open on CHANNEL, and is
returned as the result. The INPUT operation is controlled by BREAKTABLE
(see page 68) and the reference variables BRCHAR, EOF, and COUNT which are
provided by the user in the OPEN function for this channel (see page 63).
Zero bytes are automatically omitted (text editor convention) unless mode
"Z" was specified for the breaktable. Input may be terminated in several
SCAN
ways. The exact reason for termination can be obtained by examining BRCHAR
and EOF:
EOF BRCHAR
0 0 End of file or an error (if enabled, see page 63) occurred
while reading. The result is a String containing all non-
omitted characters which remained in the file when INPUT was
called.
0 0 No break characters were encountered. The result is a String
of length equal to the current COUNT specifications for the
CHANNEL (see page 63).
0 <0 A line number was encountered and the break table specified
that someone wanted to know. The result String contains all
characters up to the line number. If mode "L" was specified in
the Breakset setting up this table, bit 35 is turned off in
the line number word so that it will be input next time. -1 is
placed in BRCHAR. If mode "E" was specified, the line number
will not appear in the next input String, but its negated ASCII
value, complete with low-order line number bit, will be found
in BRCHAR.
0 >0 A break character was encountered. The break character is
stored in BRCHAR (an INTEGER reference variable, see page 63)
as a right-justified 7-bit ASCII value. It may also be tacked
on to the end of the result String or saved for next time,
depending on the BREAKSET mode (see page 68).
If break table 0 is specified, the only criteria for termination are end of
file or COUNT exhaustion.
********************************** SCAN ********************************;
"RESULT" _ SCAN (@"SOURCE",
BREAKTABLE, @BRCHAR)
SCAN functions identically to INPUT with the following exceptions:
SCANC OUT LINOUT
1. The source is not a data file but the String SOURCE, called by
reference. The String SOURCE is truncated from the left to produce
the same effect as one would obtain if SOURCE were a data file. The
disposition of the break character is the same as it is for INPUT.
2. BRCHAR is directly specified as a parameter. INPUT gets its break
character variable from a table set up by page 63.
3. Line number considerations are irrelevant.
********************************* SCANC ********************************;
"RESULT" _ SCANC ("SOURCE",
"BREAK", "OMIT", "MODE");
This routine is equivalent to the following Sail code:
STRING PROCEDURE SCANC (STRING ARG, BRK,
OMIT, MODE);
BEGIN "SCANC" INTEGER TBL, BRCHAR; STRING RSLT;
TBL_GETBREAK; SETBREAK (TBL, BRK, OMIT, MODE);
RSLT_SCAN (ARG, TBL, BRCHAR);
RELBREAK (TBL);
RETURN (RSLT) END "SCANC";
Note that the arguments are all value parameters, so that SCANC will be
called at compile time if the arguments are constants. It is intended that
SCANC be used with ASSIGNC in macros and conditional compilation. For
scanning at execution time, it is much more efficient to use SCAN directly.
********************************** OUT *********************************;
OUT (CHANNEL, "STRING")
STRING is output to the file open on CHANNEL. If the device is a TTY, the
String will be typed immediately. Buffered mode text output is employed for
this operation. The data mode specified in the OPEN for this channel must
be 0 or 1. The EOF variable will be set non-zero as described in page 63 if
an error is detected and the program is enabled for it; 0 otherwise.
********************************* LINOUT *******************************;
LINOUT (CHANNEL, NUMBER)
SETPL WORDIN WARNING ABOUT DUMP MODE IO ARRYIN
ABS (NUMBER) mod 100,000 is converted to a 5 character ASCII string. These
characters are placed in a single word in the output file designated by
CHANNEL with the low-order bit (line-number bit) turned on. A tab is
inserted after the line number. Mode 0 or 1 must have been specified in the
OPEN (page 63) for the results to be anywhere near satisfactory. EOF is set
as in OUT.
********************************* SETPL ********************************;
SETPL (CHANNEL, @LINNUM,
@PAGNUM, @SOSNUM)
This routine allows one to keep track of the string input from CHANNEL.
Whenever a '12 is encountered, LINNUM is incremented. Whenever a '14 is
encountered, PAGNUM is incremented and LINNUM is zeroed. Whenever an SOS
line number is encountered it is placed into SOSNUM.
********************************* WORDIN *******************************;
VALUE _ WORDIN (CHANNEL)
The next word from the file open on CHANNEL is returned. A zero is returned,
and EOF (see page 63, page 72) set, when end of file or error is
encountered. This operation is performed in buffered mode or dump mode,
depending on the mode specification in the OPEN.
WARNING ABOUT DUMP MODE IO
Dump Mode (mode '15, '16, or '17) is sufficiently device and system
dependent that you should consult [SysCall] and be extremely careful.
********************************* ARRYIN *******************************;
ARRYIN (CHANNEL, @LOC, HOWMANY)
HOWMANY words are read from the device and file open on CHANNEL, and
deposited in memory starting at location LOC. Buffered-mode input is done
if MODE (see page 63) is '10 or '14. Dump-mode input is done if MODE is '16
or '17. Other modes are illegal. See the warning about Dump Mode IO above.
If an end of file or enabled error condition occurs before HOWMANY words
are read in buffered mode then the EOF variable (see page 63) is set to the
enabled bits in its left half, as usual. Its right half contains the number
of words actually read. EOF will be 0 if the full request is satisfied. No
indication of how many words were actually read is given if EOF is
encountered while reading a file in DUMP mode.
WORDOUT ARRYOUT INOUT GETSTS, SETSTS
******************************** WORDOUT *******************************;
WORDOUT (CHANNEL, VALUE)
VALUE is placed in the output buffer for CHANNEL. An OUTPUT is done when
the buffer is full or when a CLOSE or RELEASE is executed for this channel.
Dump mode output will be done if dump mode is specified in the OPEN (see
page 63). EOF is set as in OUT. See the warning about Dump Mode IO above.
******************************** ARRYOUT *******************************;
ARRYOUT (CHANNEL, @LOC, HOWMANY)
HOWMANY words are written from memory, starting at location LOC, onto the
device and file open on channel CHANNEL. The valid modes are again '10,
'14, '16, and '17. The EOF variable is set as in ARRYIN, except that the
EOF bit itself will never occur.
********************************* INOUT ********************************;
INOUT (INCHAN, OUTCHAN, HOWMANY)
INOUT reads HOWMANY words from channel INCHAN and writes them out on channel
OUTCHAN. Each channel must be open in a mode between 8 and 12. On return,
the EOF variables for the two channels will be the same as if ARRYIN &
ARRYOUT had been used. If HOWMANY is less than zero, then transfer of data
will cease only upon end of file or a device error. INOUT is not available
in TENEX Sail.
***************************** GETSTS, SETSTS ***************************;
SETSTS (CHAN, NEWSTATUS);
issues a SETSTS uuo on channel CHAN with the status value NEWSTATUS.
STATUS _ GETSTS (CHAN)
returns the results of a GETSTS uuo on channel CHAN.
These functions do not exist in TENEX Sail. Instead, see GTSTS, GDSTS,
STSTS, and SDSTS for analogous features.
MTAPE USETI, USETO REALIN, INTIN
********************************* MTAPE ********************************;
MTAPE (CHANNEL, MODE)
MTAPE is ignored unless the device associated with CHANNEL is a magnetic
tape drive. It performs tape actions as follows:
MODE FUNCTION
"A" Advance past one tape mark (or file)
"B" Backspace past one tape mark
"E" Write tape mark
"F" Advance one record
"I" Set 'IBM compatible' mode
"R" Backspace one record
"S" Write 3 inches of blank tape
"T" Advance to logical end of tape
"U" Rewind and unload
"W" Rewind tape
NULL Wait until all activity ceases
****************************** USETI, USETO ****************************;
USETI (CHANNEL, VALUE);
USETO (CHANNEL, VALUE)
These routines are for random file access (see [SysCall]).
***************************** REALIN, INTIN ****************************;
VALUE _ REALIN (CHANNEL);
VALUE _ INTIN (CHANNEL)
Number input may be obtained using the functions REALIN or INTIN, depending
on whether a Real number or an Integer is required. Both functions use the
same free field scanner, and take as argument a channel number.
Free field scanning works as follows: characters are scanned one at a time
from the input channel, ignoring everything until a digit or decimal point
is encountered. Then a number is scanned according to this syntax, with
zero bytes, line numbers, and carriage returns (but not linefeeds) ignored:
<number>
::= <sign> <real number>
<real number>
::= <decimal number>
::= <decimal number> <exponent>
::= <exponent>
<decimal number>
::= <integer>
::= <integer> .
::= <integer> . <integer>
::= . <integer>
<integer>
::= <digit>
::= <integer> <digit>
<exponent>
::= @ <sign> <integer>
::= E <sign> <integer>
<sign>
::= +
::= -
::= <empty>
If the digit is not part of a number an error message will be printed and
the program will halt. Typing a carriage return will cause the input
function to return zero.
On input, leading zeros are ignored. The ten most significant digits are
used to form the number. A check for overflow and underflow is made and an
error message printed if this occurs. When using INTIN any exponent is
removed by scaling the Integer number. Rounding is used in this process.
All numbers are accurate to one half of the least significant bit.
After scanning the number the last delimiter is replaced on the input string
and is returned as the break character for the channel. If no number is
found, a zero is returned, and the break variable is set to -1; If an end of
file or enabled error is sensed this is also returned in the appropriate
channel variable. The maximum character count appearing in the OPEN call is
ignored.
REALSCAN, INTSCAN TMPIN, TMPOUT
*************************** REALSCAN, INTSCAN **************************;
VALUE _ REALSCAN (@"NUMBERSTRING",
@BRCHAR);
VALUE _ INTSCAN (@"NUMBERSTRING",
@BRCHAR)
These functions are identical in function to REALIN and INTIN. Their
inputs, however, are obtained from their NUMBERSTRING arguments. These
routines replace NUMBERSTRING by a string containing all characters left
over after the number has been removed from the front.
***************************** TMPIN, TMPOUT ****************************;
"RESULT" _ TMPIN ("FILE", @ERRFLAG);
TMPOUT ("FILE", "TEXT", @ERRFLAG)
These routines do input and output to tmpcor files (simulated files kept in
core storage--see [SysCall]).
TMPIN returns a string consisting of the entire contents of the tmpcor file
of the specified name. Only the first three characters in the file name are
significant. If the input fails for some reason (most likely: no tmpcor
file with the specified name) then ERRFLAG is set to true and NULL is
returned. Otherwise ERRFLAG is set to false.
TMPOUT writes its string argument into the specified tmpcor file. The
ERRFLAG has the same function as in TMPIN; in case of error, the tmpcor file
is not written. Likely causes for error are running out of tmpcor space
(currently, the sum of the sizes of all the tmpcor files for a single job
may not exceed =256 words) or attempting to write a null tmpcor file (i.e.,
calling TMPOUT with the string argument NULL).
TMPIN executes a TMPCOR uuo with code 1, and hence does not delete the
specified tmpcor file. The length of the returned string will always be a
multiple of five, since words rather than characters are actually being
transferred. TMPOUT executes a TMPCOR uuo with code 3. The last word of
the string is padded with nulls if necessary before the data transfer is
done.
Neither function is available in TENEX Sail.
AUXCLR, AUXCLV CHNIOR, CHNIOV TTY and PTY Routines TELETYPE I/O ROUTINES BACKUP CLRBUF INCHRS INCHRW INCHSL INCHWL
***************************** AUXCLR, AUXCLV ***************************;
RSLT _ AUXCLR (PORT, @ARG, FUNCTION);
RSLT _ AUXCLV (PORT, ARG, FUNCTION)
(TYMSHARE only.) These functions perform AUXCAL system calls; the only
difference is whether ARG is by reference or by value. SKIP is set.
***************************** CHNIOR, CHNIOV ***************************;
RSLT _ CHNIOR (CHAN, @ARG, FUNCTION);
RSLT _ CHNIOV (CHAN, ARG, FUNCTION)
(TYMSHARE only.) These functions perform CHANIO system calls; the only
difference is whether ARG is by reference or by value. SKIP is set.
6.5 TTY and PTY Routines
************************* TELETYPE I/O ROUTINES ************************;
Each of the I/O functions uses the TTCALL UUO's to do direct TTY I/O.
BACKUP
The system attempts to back up its TTY input buffer pointer to the
beginning of the last "line", thus allowing you to reread it. In
general this cannot possibly work, so do not use BACKUP.
CLRBUF
Flushes the input buffer.
CHAR _ INCHRS
Returns a negative value if no characters have been typed;
otherwise it is INCHRW.
CHAR _ INCHRW
Waits for a character to be typed and returns that character.
"STR" _ INCHSL (@FLAG)
Returns NULL with FLAG 0 if no lines have been typed. Otherwise
it sets FLAG to 0 and performs INCHWL.
"STR" _ INCHWL
Waits for a line to be typed and returns a string containing all
INSTR INSTRL INSTRS IONEOU OUTCHR OUTSTR TTYIN TTYINL TTYINS TTYUP
characters up to (but not including) the activation character.
The activation character is put into SKIP. If the activation
character is CR then the next character is discarded (on the
assumption that it is LF).
"STR" _ INSTR (BRCHAR)
Returns as a string all characters up to, but not including, the
first instance of BRCHAR. The BRCHAR instance is lost.
"STR" _ INSTRL (BRCHAR)
Waits for a line to be typed, then performs INSTR.
"STR" _ INSTRS (@FLAG, BRCHAR)
Is INCHSL if no lines are waiting; INSTRL otherwise.
IONEOU (CHAR)
(TYMSHARE only.) The low-order 8 bits of CHAR are sent to the TTY
in image mode.
OUTCHR (CHAR)
Types its character argument (right-justified in an integer
variable).
OUTSTR ("STR")
Types its string argument until the end of the string or a null
character is reached.
"STR" _ TTYIN (TABLE, @BRCHAR)
Uses the break table features described in page 68 and page 72 to
return a string and break character. Mode "R" is illegal; line
number modes are irrelevant. The input count (see page 63) is set
at 100.
"STR" _ TTYINL (TABLE, @BRCHAR)
Waits for a line to be typed, then does TTYIN.
"STR" _ TTYINS (TABLE, @BRCHAR)
Sets BRCHAR to 0 and returns NULL if no lines are waiting.
Otherwise it is TTYINL.
OLDVAL _ TTYUP (NEWVAL)
Causes conversion of lower case characters (a-z) to their upper
case equivalents for strings read by any of the Sail teletype
routines that do not use break tables. If NEWVAL is TRUE then
conversion will take place on all subsequent inputs until
TTYUP(FALSE) is called. OLDVAL will be set to the previous value
of the conversion flag. If TTYUP has never been called, then no
conversions will take place, and the first call to TTYUP will
return FALSE. In TENEX, TTYUP sets the system parameter using the
STPAR jsys to convert to upper case.
PSEUDO-TELETYPE FUNCTIONS LODED PTYALL PTCHRS PTCHRW PTOCHS PTOCHW PTOCNT PTIFRE PTOSTR PTYGET PTYGTL PTYIN
*********************** PSEUDO-TELETYPE FUNCTIONS **********************;
Pseudo-teletype functions are available at SUAI only.
LODED ("STR")
Loads the line editor with the string argument. PTOSTR should be
used rather than LODED if possible, since LODED works only on a DD
or III, while PTOSTR works on all terminals.
"STR" _ PTYALL (LINE)
Returns whatever is in the PTY's output buffer. No waiting is
done.
CHAR _ PTCHRS (LINE)
Reads a character from the PTY if there is one, returns -1 if
none.
CHAR _ PTCHRW (LINE)
Waits for a character from the PTY and returns it.
PTOCHS (LINE, CHAR)
Tries to send a character to a PTY. If the attempt was
successful, the global variable SKIP is -1, otherwise 0.
PTOCHW (LINE, CHAR)
Sends a character to a PTY, waiting if necessary.
NUMBER _ PTOCNT (LINE)
Returns the number of free characters in the PTY output buffer.
NUMBER _ PTIFRE (LINE)
Returns the number of free characters in the PTY input buffer.
PTOSTR (LINE, "STR")
Sends the string to the PTY, waiting if necessary. PTOSTR (0,
"STR") sends the string to your TTY.
LINE _ PTYGET
Gets a new pseudo-teletype line number and returns it. The global
variable SKIP is -1 if the attempt to get a PTY was successful,
and 0 otherwise.
CHARACTERISTICS _ PTYGTL (LINE)
Returns line characteristics for the PTY.
"STR" _ PTYIN (LINE, BKTBL, @BRCHAR)
Reads from the PTY (waiting if necessary) according to break table
conventions. The break character is stored in BRCHAR.
PTYREL PTYSTL PTYSTR Example of TOPS-10 I/O
PTYREL (LINE)
Releases PTY identified by LINE.
PTYSTL (LINE, CHARACTERISTICS)
Sets line characteristics for the PTY specified by LINE.
"STR" _ PTYSTR (LINE, BRCHAR)
Reads characters from the PTY, waiting if necessary, until a
character equal to BRCHAR is seen. All but the break character is
returned as the string. If the break character was '15 (carriage
return), the following character is snarfed (on the assumption
that it is a linefeed).
6.6 Example of TOPS-10 I/O
BEGIN "COPY"
COMMENT copies a text file, inserting a semicolon at the
beginning of each line, deleting SOS line numbers and
zero bytes, if any. Prints the page number as it goes;
REQUIRE "[][]" DELIMITERS;
DEFINE CRLF=[('15&'12)],LF=['12], FF=['14];
INTEGER COLONTAB;
RECORDCLASS $FILE (STRING DEVICE, NAME;
INTEGER CHANNEL, MODE, IBUF, OBUF,
COUNT, BRCHAR, EOF, LINNUM, PAGNUM, SOSNUM);
RECORDPOINTER($FILE) PROCEDURE OPENUP
(STRING FILNAM; INTEGER MODE, IBUF, OBUF);
BEGIN "OPENUP"
STRING T; RECORDPOINTER ($FILE) Q; INTEGER BRK;
Q_NEWRECORD ($FILE); T_SCAN (FILNAM, COLONTAB, BRK);
$FILE:DEVICE[Q]_(IF BRK=":" THEN T ELSE "DSK");
$FILE:NAME[Q]_(IF BRK=":" THEN FILNAM ELSE T);
$FILE:MODE[Q]_MODE; $FILE:IBUF[Q]_IBUF;
$FILE:OBUF[Q]_OBUF; OPEN ($FILE:CHANNEL[Q]_GETCHAN,
$FILE:DEVICE[Q], MODE, IBUF, OBUF, $FILE:COUNT[Q],
$FILE:BRCHAR[Q], $FILE:EOF[Q]_-1);
IF NOT($FILE:EOF[Q]) THEN BEGIN
SETPL ($FILE:CHANNEL[Q], $FILE:LINNUM[Q],
$FILE:PAGNUM[Q], $FILE:SOSNUM[Q]);
IF IBUF THEN
LOOKUP ($FILE:CHANNEL[Q], $FILE:NAME[Q], $FILE:EOF[Q]);
IF OBUF AND NOT ($FILE:EOF[Q]) THEN
ENTER ($FILE:CHANNEL[Q], $FILE:NAME[Q], $FILE:EOF[Q]);
END;
$FILE:PAGNUM[Q]_1;
IF $FILE:EOF[Q] THEN RELEASE($FILE:CHANNEL[Q]);
RETURN(Q)
END "OPENUP";
COMMENT Sail I/O should be rewritten to do this ^;
RECORDPOINTER ($FILE) PROCEDURE GETFILE
(STRING PROMPT; INTEGER MODE, I, O);
BEGIN "GETFILE"
RECORDPOINTER ($FILE) F; INTEGER REASON;
WHILE TRUE DO BEGIN "try"
PRINT (PROMPT);
IF (REASON_$FILE:EOF[F_OPENUP (INCHWL,
MODE, I, O)])=0 THEN RETURN (F);
IF REASON=-1 THEN
PRINT ("Device ", $FILE:DEVICE[F], " not available.")
ELSE PRINT("Error, ", CASE (0 MAX REASON MIN 4) OF
("no such file ", "illegal PPN ", "protection ",
"busy ", "??? "), $FILE:NAME[F], CRLF);
END "try";
END "GETFILE";
RECORDPOINTER ($FILE) SRC, SNK;
INTEGER FFLFTAB;
SETBREAK (COLONTAB_GETBREAK, ":", "", "ISN");
WHILE TRUE DO BEGIN "big loop"
STRING LINE;
SRC_GETFILE ("Copy from:", 0, 5, 0);
$FILE:COUNT[SRC]_200;
SNK_GETFILE (" to:", 0, 0, 5);
SETBREAK (FFLFTAB_GETBREAK, FF&LF, "", "INA");
WHILE TRUE DO BEGIN "a line"
LINE_INPUT ($FILE:CHANNEL[SRC], FFLFTAB);
IF $FILE:EOF[SRC] THEN DONE;
IF $FILE:BRCHAR[SRC]=FF THEN BEGIN
PRINT (" ", $FILE:PAGNUM[SRC]);
LINE_LINE&
INPUT ($FILE:CHANNEL[SRC], FFLFTAB) END;
CPRINT ($FILE:CHANNEL[SNK], ";", LINE)
END "a line";
RELEASE ($FILE:CHANNEL[SRC]);
RELEASE ($FILE:CHANNEL[SNK])
END "big loop";
END "COPY"
EXECUTION TIME ROUTINES Type Conversion Routines SETFORMAT GETFORMAT
SECTION 7
EXECUTION TIME ROUTINES
Please read Execution Time Routines in General, page 62, if you are
unfamiliar with the format used to describe runtime routines.
7.1 Type Conversion Routines
******************************* SETFORMAT ******************************;
SETFORMAT (WIDTH, DIGITS)
This function allows specification of a minimum width for strings created by
the functions CVS, CVOS, CVE, CVF, and CVG (see page 86 and following). If
WIDTH is positive then enough blanks will be inserted in front of the
resultant string to make the result at least WIDTH characters long. The
sign, if any, will appear after the blanks. If WIDTH is negative then
leading zeroes will be used in place of blanks. The sign, of course, will
appear before the zeroes. The parameter WIDTH is initialized by the system
to zero.
In addition, the DIGITS parameter allows one to specify the number of digits
to appear following the decimal point in strings created by CVE, CVF, and
CVG. This number is initially 7. See the writeups on these functions for
details.
NOTE: All type conversion routines, including those that SETFORMAT applies
to, are performed at compile time if their arguments are constants.
However, Setformat does not have its effect until execution time.
Therefore, CVS, CVOS, CVE, CVF, and CVG of constants will have no leading
zeros and 7 digits (if any) following the decimal point.
***************************** GETFORMAT ******************************;
GETFORMAT (@WIDTH, @DIGITS)
The WIDTH and DIGIT settings specified in the last SETFORMAT call are
returned in the appropriate reference parameters.
CVS CVD CVOS CVO CVE, CVF, CVG
********************************** CVS *********************************;
"ASCIISTRING" _ CVS (VALUE);
The decimal Integer representation of VALUE is produced as an ASCII String
with leading zeroes omitted (unless WIDTH has been set by SETFORMAT to some
negative value). "-" will be concatenated to the String representing the
decimal absolute value of VALUE if VALUE is negative.
********************************** CVD *********************************;
VALUE _ CVD ("ASCIISTRING")
ASCIISTRING should be a String of decimal ASCII characters perhaps
preceded by plus and/or minus signs. Characters with ASCII values SPACE
('40) are ignored preceding the number. Any character not a digit will
terminate the conversion (with no error indication). The result is a
(signed) integer.
********************************** CVOS ********************************;
"ASCIISTRING" _ CVOS (VALUE)
The octal Integer representation of VALUE is produced as an ASCII String
with leading zeroes omitted (unless WIDTH has been set to some negative
value by SETFORMAT. No "-" will be used to indicate negative numbers. For
instance, -5 will be represented as "777777777773".
********************************** CVO *********************************;
VALUE _ CVO ("ASCIISTRING")
This function is the same as CVD except that the input characters are deemed
to represent Octal values.
***************************** CVE, CVF, CVG ****************************;
"STRING" _ CVE (VALUE);
"STRING" _ CVF (VALUE);
"STRING" _ CVG (VALUE)
Real number output is facilitated by means of one of three functions CVE,
CVASC, CVASTR, CVSTR
CVG, or CVF, corresponding to the E, G, and F formats of FORTRAN IV. Each
of these functions takes as argument a real number and returns a string.
The format of the string is controlled by another function SETFORMAT
(WIDTH, DIGITS) (see page 85) which is used to change WIDTH from zero and
DIGITS from 7, their initial values. WIDTH specifies the minimum string
length. If WIDTH is positive leading blanks will be inserted and if
negative leading zeros will be inserted.
The following table indicates the strings returned for some typical numbers.
indicates a space and it is assumed that WIDTH_10 and DIGITS_3.
CVF CVE CVG
.000 .100@-3 .100@-3
.001 .100@-2 .100@-2
.010 .100@-1 .100@-1
.100 .100 .100
1.000 .100@1 1.00
10.000 .100@2 10.0
100.000 .100@3 100.
1000.000 .100@4 .100@4
10000.000 .100@5 .100@5
100000.000 .100@6 .100@6
1000000.000 .100@7 .100@7
-1000000.000 -.100@7 -.100@7
The first character ahead of the number is either a blank or a minus sign.
With WIDTH_-10 plus and minus 1 would print as:
CVF CVE CVG
00001.000 0.100@1 01.00
-00001.000 -0.100@1 -01.00
All numbers are accurate to one unit in the eighth digit. If DIGITS is
greater than 8, trailing zeros are included; if less than eight, the number
is rounded.
************************** CVASC, CVASTR, CVSTR ************************;
VALUE _ CVASC ("STRING");
"STRING" _ CVASTR (VALUE);
"STRING" _ CVSTR (VALUE)
These routines convert between a Sail String and an integer containing 5
ASCII characters left justified in a 36-bit word; the extra bit is made zero
(CVASC) or ignored (CVASTR, CVSTR). CVASC converts from String to ASCII.
Both CVSTR and CVASTR convert from a word of ASCII to a string. CVSTR
always returns a string of length five, while CVASTR stops converting at the
first null ('0) character.
CV6STR, CVSIX, CVXSTR String Manipulation Routines EQU LENGTH
CVASTR (CVASC ("ABC")) is "ABC"
CVSTR (CVASC ("ABC")) is "ABC" & 0 & 0
************************* CV6STR, CVSIX, CVXSTR ************************;
"STRING" _ CV6STR (VALUE);
VALUE _ CVSIX ("STRING");
"STRING" _ CVXSTR (VALUE)
The routines CV6STR, CVSIX, and CVXSTR are the SIXBIT analogues of CVASTR,
CVASC, and CVSTR, respectively. The character codes are converted, ASCII in
the String SIXBIT in the integer. CVXSTR always returns a string of
length six, while CV6STR stops converting upon reaching a null character.
CV6STR (CVSIX ("XYZ")) is "XYZ", not "XYZ ".
CV6STR (CVSIX ("X Y Z")) is "X", not "X Y Z" or "XYZ".
7.2 String Manipulation Routines
********************************** EQU *********************************;
VALUE _ EQU ("STR1", "STR2")
The value of this function is TRUE if STR1 and STR2 are equal in length and
have identically the same characters in them (in the same order). The value
of EQU is FALSE otherwise.
********************************* LENGTH *******************************;
VALUE _ LENGTH ("STRING")
LENGTH is always an integer-valued function. If the argument is a String,
its length is the number of characters in the string. The length of an
algebraic expression is always 1 (see page 46). LENGTH is usually compiled
in line.
LOP SUBSR, SUBST Liberation-from-Sail Routines CODE
********************************** LOP *********************************;
VALUE _ LOP (@STRINGVAR)
The LOP operator applied to a String variable removes the first character
from the String and returns it in the form given in page 46 above. The
String no longer contains this character. LOP applied to a null String has
a zero value. LOP is usually compiled in line. LOP may not appear as a
statement.
****************************** SUBSR, SUBST ****************************;
"RSLT" _ SUBSR ("STRING", LEN, FIRST);
"RSLT" _ SUBST ("STRING", LAST, FIRST)
These routines are the ones used for performing substring operations.
SUBSR (STR, LEN, FIRST) is STR[FIRST FOR LEN] and SUBST (STR, LAST, FIRST)
is STR[FIRST TO LAST].
7.3 Liberation-from-Sail Routines
********************************** CODE ********************************;
RESULT _ CODE (INSTR, @ADDR)
This function is equivalent to the FAIL statements:
EXTERNAL .SKIP. ;DECLARE AS SKIP IN SAIL
SETOM .SKIP. ;ASSUME SKIP
MOVE 0,INSTR
ADDI 0,@ADDR
XCT 0
SETZM .SKIP. ;DIDN'T SKIP
RETURN (1)
In other words, it executes the instruction formed by adding the address of
the ADDR variable (passed by reference) to the number INSTR. Before the
operation is carried out, AC1 is loaded from a special cell (initially 0).
AC1 is returned as the result, and also stored back into the special cell
after the instruction is executed. The global variable SKIP (.SKIP. in
DDT or FAIL) is FALSE (0) after the call if the executed instruction did not
skip; TRUE (currently -1) if it did. Declare this variable as
EXTERNAL INTEGER SKIP if you want to use it.
CALL CALLI USERCON GOGTAB
********************************** CALL ********************************;
RESULT _ CALL (VALUE, "FUNCTION")
This function is equivalent to the FAIL statements:
EXTERNAL .SKIP.
SETOM .SKIP.
MOVE 1,VALUE
CALL 1,[SIXBIT /FUNCTION/]
SETZM .SKIP. ;DID NOT SKIP
RETURN (REGISTER 1)
TENEX users should see more on CALL, page 148.
****************************** CALLI ********************************;
RESULT _ CALLI (VALUE, FUNCTION)
(TYMSHARE only.) Like CALL, only CALLI.
******************************** USERCON *******************************;
USERCON (@INDEX, @VALUE, FLAG)
This function allows inspection and alteration of the "User Table". The
user table is always loaded with your program and contains many interesting
variables. Declare an index you are interested in as an External Integer
(e.g., EXTERNAL INTEGER REMCHR). This will, when loaded, give an address
which is secretly a small Integer index into the User Table. When passed by
reference, this index is available to USERCON. The names and meanings of
the various User Table indices can be found in the file HEAD, wherever Sail
compiler program text files are sold.
USERCON always returns the current value of the appropriate User Table entry
(the Global Upper Segment Table is used if FLAG is negative and your system
knows about such things). If FLAG is odd, the contents of VALUE before the
call replaces the old value in the selected entry of the selected table.
By now the incredible danger of this feature must be apparent to you. Be
sure you understand the ramifications of any changes you make to any User
Table value. GOGTAB
Direct access to the user table can be gained by declaring EXTERNAL INTEGER
ARRAY GOGTAB[0:n]; The clumsy USERCON linkage is obsolete.
The symbolic names of all GOGTAB entries can be obtained by requiring
USERERR ERMSBF EDFILE
SYS:GOGTAB.DEF (<SAIL>GOGTAB.DEF on TENEX) as a source file. This file
contains DEFINEs for all of the user table entries.
******************************** USERERR *******************************;
USERERR (VALUE, CODE, "MSG",
"RESPONSE"(NULL))
USERERR generates an error message. See page 243 for a description of the
error message format. MSG is the error message that is printed on the
teletype or sent to the log file. If CODE = 2, VALUE is printed in decimal
on the same line. Then on the next line the "Last SAIL call" message may be
typed which indicates where in the user program the error occurred. If CODE
is 1 or 2, a "" will be typed and execution will be allowed to continue.
If it is 0, a "?" is typed, and no continuation will be permitted. The
string RESPONSE, if included in the USERERR call, will be scanned before the
input buffer is scanned. In fact, if the string RESPONSE satisfies the
error handler, the input buffer will not be scanned at all. Examples:
USERERR (0, 1, "LINE TOO LONG"); Gives
error message and allows continuation.
USERERR (0, 1, NULL, "QLA"); Resets mode
of error handler to Quiet, Logging, and
Automatic continuation. Then continues.
****************************** ERMSBF *******************************;
ERMSBF (NEWSIZE)
This routine insures that error messages of NEWSIZE characters can be
handled. The error message buffer is initially 256 characters, which is
sufficient for any Sail-generated error. USERERR can generate longer
messages, however.
********************************* EDFILE *******************************;
EDFILE ("FILENAME", LINE, PAGE, BITS(0))
(Not on TENEX.) Exits to an editor. Which editor is determined by the bits
which are on in the second parameter, LINE. If bit 0 or bit 1 (600000,,0
bits) is on, then LINE is assumed to be ASCID and SOS is called. If neither
of these bits is on, then LINE is assumed to be of the form attach
count,,sequential line number and E is called. PAGE is the binary page
number. BITS defaults to zero and controls the editing mode.
INIACS Byte Manipulation Routines LDB, DPB, etc. POINT
0 edit
1 no directory (as in /N)
2 readonly (as in /R)
4 create (as in /C)
In addition, the accumulators are set up from INIACS (see below) so that the
E command X RUN will run the dump file from which the current program was
gotten. [Accumulators 0 (file name), 1 (extension), and 6 (device) are
loaded from the corresponding values in INIACS.]
********************************* INIACS *******************************;
The contents of locations 0-'17 are saved in block INIACS when the core
image is started for the first time. Declare INIACS as an external integer
and use STARTCODE or MEMORY[LOCATION(INIACS)+n] to reference this block.
7.4 Byte Manipulation Routines
***************************** LDB, DPB, etc. ***************************;
VALUE _ LDB (BYTEPOINTER);
VALUE _ ILDB (@ BYTEPOINTER);
DPB (BYTE, BYTEPOINTER);
IDPB (BYTE, @ BYTEPOINTER);
IBP (@ BYTEPOINTER)
LDB, ILDB, DPB, IDPB, and IBP are Sail constructs used to invoke the PDP-10
byte loading instructions. The arguments to these functions are expressions
which are interpreted as byte pointers and bytes. In the case of ILDB,
IDPB, and IBP, you are required to use an algebraic variable as argument as
the bytepointer, so that the byte pointer (i.e. that algebraic variable)
may be incremented.
********************************* POINT ********************************;
VALUE _ POINT (BYTE SIZE,
@EFFECTIVE ADDRESS, LAST BIT NUMBER)
POINT returns a byte pointer (hence it is of type integer). The three
arguments correspond exactly to the three arguments to the POINT pseudo-op
in FAIL.
Other Useful Routines CVFIL FILEINFO ARRINFO
7.5 Other Useful Routines
********************************* CVFIL ********************************;
VALUE _ CVFIL ("FILESPEC", @EXTEN, @PPN)
FILESPEC has the same form as a file name specification for LOOKUP or
ENTER. The SIXBIT for the file name is returned in VALUE. SIXBIT values
for the extension and project-programmer numbers are returned in the
respective reference parameters. Any unspecified portions of the FILESPEC
will result in zero values. The global variable SKIP will be 0 if no
errors occurred, non-zero if an invalid file name specification is
presented.
******************************** FILEINFO ******************************;
FILEINFO (@INFOARRAY)
FILEINFO fills the 6-word array INFOARRAY with the following six words from
the most recent LOOKUP, ENTER, or RENAME:
FILENAME
EXT,,(2)hidate2 (15)date1
(9)prot (4)Mode (11)time (12)lodate2
negative swapped word count
0 (unless opened in magic mode)
0
See [SysCall]; TENEX users should use JFNS instead.
******************************** ARRINFO *******************************;
VALUE _ ARRINFO (ARRAY, PARAMETER)
ARRINFO (ARRAY, -1) is the number of dimensions for the array. This
number is negative for String arrays.
ARRINFO (ARRAY, 0) is the total size of the array in words.
ARRINFO (ARRAY, 1) is the lower bound for the first dimension.
ARRBLT ARRTRAN ARRCLR INCONTEXT
ARRINFO (ARRAY, 2) is the upper bound for the first dimension.
ARRINFO (ARRAY, 3) is the lower bound for the second dimension.
ARRINFO (... etc.
********************************* ARRBLT *******************************;
ARRBLT (@DEST, @SOURCE, NUM)
NUM words are transferred (using BLT) from consecutive locations starting at
SOURCE to consecutive locations starting at DEST. No bounds checking is
performed. This function does not work well for String Arrays (nor set nor
list arrays).
******************************** ARRTRAN *******************************;
ARRTRAN (DESTARR, SOURCEARR)
This function copies information from SOURCEARR to DESTARR. The transfer
starts at the first data word of each array. The minimum of the sizes of
SOURCEARR and DESTARR is the number of words transferred.
********************************* ARRCLR *******************************;
ARRCLR (ARRAY, VALUE(0))
This routine stores VALUE into each element of ARRAY. The most common use
is with VALUE omitted, which clears the array; i.e., arithmetic arrays get
filled with zeros, string arrays with NULLs, itemvar arrays with ANYs,
recordpointer arrays with NULLRECORD. One may use ARRCLR with set and
list arrays, but the set and list space will be lost (i.e., un-garbage-
collectible). Do not supply anything other than 0 (0, NULL, PHI, NIL,
NULLRECORD) for VALUE when clearing a string, set, list, or recordpointer
array unless you know what you are doing. Using a real value for an itemvar
array is apt to cause strange results. (If you use an integer then ARRAY
will be filled with CVI (value).)
******************************* INCONTEXT *****************************;
VALUE _ INCONTEXT (VARI, CONTXT)
CHNCDB Numerical Routines SIN COS SIND COSD ASIN ACOS ATAN ATAN2 SINH COSH TANH SQRT
INCONTEXT is a boolean which tells one if the specified variable is in the
specified context. VARI may be any variable, array element, array name, or
Leap variable. If that variable, element or array was REMEMBERed in that
context, INCONTEXT will return True. INCONTEXT will also return true if
VARI is an array element and the whole array was Remembered in that context
(by using REMEMBER <arrayname>). On the other hand, if VARI is an array
name, then INCONTEXT will return true only if one has Remembered that array
with a REMEMBER <arrayname>.
********************************* CHNCDB *******************************;
VALUE _ CHNCDB (CHANNEL)
(Not on TENEX.) This integer procedure returns the address of the block of
storage which Sail uses to keep track of the specified channel. It is
provided for the benefit of assembly language procedures that may want to
do I/O inside some fast inner loop, but which may want to live in a Sail
core image & use the Sail OPEN, etc.
7.6 Numerical Routines
These numerical routines are new as predeclared runtimes in Sail. The
routines themselves are quite standard.
The standard trigonometric functions. ASIN, ACOS, ATAN and ATAN2 return
results in radians. The ATAN2 call takes arc-tangent of the quotient of its
arguments; in this way, it correctly preserves sign information.
REAL PROCEDURE SIN (REAL RADIANS);
REAL PROCEDURE COS (REAL RADIANS);
REAL PROCEDURE SIND (REAL DEGREES);
REAL PROCEDURE COSD (REAL DEGREES);
REAL PROCEDURE ASIN (REAL ARGUMENT);
REAL PROCEDURE ACOS (REAL ARGUMENT);
REAL PROCEDURE ATAN (REAL ARGUMENT);
REAL PROCEDURE ATAN2 (REAL NUM, DEN)
The hyperbolic trigonometric functions.
REAL PROCEDURE SINH (REAL ARGUMENT);
REAL PROCEDURE COSH (REAL ARGUMENT);
REAL PROCEDURE TANH (REAL ARGUMENT)
The square-root function:
REAL PROCEDURE SQRT (REAL ARGUMENT)
RAN LOG EXP TRIGINI OVERFLOW ENTRY POINTS OVERFLOW IMPLEMENTATION
A pseudo-random number generator. The argument specifies a new value for
the seed (if the argument is 0, the old seed value is used. Thus to get
differing random numbers, this argument should be zero.) Results are
normalized to lie in the range [0,1].
REAL PROCEDURE RAN (INTEGER SEED)
Logarithm and exponentiation functions. These functions are the same ones
used by the Sail exponentiation operator. The base is e (2.71828182845904).
The logarithm to the base 10 of e is 0.4342944819.
REAL PROCEDURE LOG (REAL ARGUMENT);
REAL PROCEDURE EXP (REAL ARGUMENT)
These functions may occasionally be asked to compute numbers that lie
outside the range of legal floating-point numbers on the PDP-10. In these
cases, the routines issue sprightly error messages that are continuable.
OVERFLOW
In order to better perform their tasks, these routines enable the system
interrupt facility for floating-point overflow and underflow errors. If an
underflow is detected, the results are set to 0 (a feat not done by the PDP-
10 hardware, alas). Be aware that such underflow fixups will be done to
every underflow that occurs in your program. For further implementation
details, see the section below.
If you would like to be informed of any numerical exceptions, you can call
the runtime:
TRIGINI (LOCATION (simple-procedure-name))
Every floating-point exception that is not expected by the interrupt handler
(the numerical routines use a special convention to indicate that arithmetic
exception was expected) will cause the specified simple procedure to be
called. This procedure may look around the world as described for 'export'
interrupt handlers, page 212. If no TRIGINI call is done, the interrupt
routine will simply dismiss unexpected floating-point interrupts.
ENTRY POINTS
In order to avoid confusion (by the loader) with older trig packages, the
entry points of the Sail arithmetic routines all have a "$" appended to the
end. Thus, SIN has the entry point SIN$, etc. WARNING: If a program plans
to use the Sail intrinsic numerical routines, it should NOT include external
declarations to them, since this will probably cause the FORTRAN library
routines to be loaded.
OVERFLOW IMPLEMENTATION
This section may be skipped by all but those interested in interfacing
number crunching assembly code (where overflow and underflow are expected to
be a problem) with Sail routines.
The Sail arithmetic interrupt routines first check to see if the interrupt
was caused by floating exponent underflow. If it was, then the result is
set to zero, be it in an accumulator, memory, or both. Then if the
arithmetic instruction that caused the interrupt is followed by a JFCL, the
AC field of the JFCL is compared with the PC flag bits to see if the JFCL
tests for any of the flags that are on. If it does, those flags are cleared
and the program proceeds at the effective address of the JFCL (i.e., the
hardware is simulated in that case). Note that no instructions may
intervene between the interrupt-causing instruction and the JFCL or the
interrupt routines will not see the JFCL. They only look one instruction
ahead. Note that in any case, floating exponent underflow always causes the
result to be set to zero. There is no way to disable that effect.
PRINT Syntax Semantics DEFAULT FORMATS
SECTION 8
PRINT
8.1 Syntax
<printstatement>
::= PRINT ( <expressionlist> )
::= CPRINT ( <integerexpression> ,
<expressionlist> )
8.2 Semantics
The new constructs PRINT and CPRINT are conveniences for handling character
output. Code which formerly looked like
OUTSTR ("The values are " & CVS (I) & " and " &
CVG (X) & " for item " & CVIS (IT, JUNK));
may now be written
PRINT ("The values are ", I, X, " for item ", IT);
The first expression in <expressionlist> is evaluated, formatted as a
string, and routed to the appropriate destination. Then the second
expression is evaluated, formatted, and dispatched; etc. (If an expression
is an assignment expression or a procedure call then side effects may
occur.)
DEFAULT FORMATS
String expressions are simply sent to the output routine. Integer
expressions are first sent to CVS, and Real expressions are passed to CVG;
the current SETFORMAT parameters are used. Item expressions use the print
name for the item if one exists, otherwise ITEM!nnnn, where nnnn is the item
number. Sets and lists show their item components separated by commas.
Sets are surrounded by single braces and lists by double braces. PHI and
NIL are printed for the empty set and empty list respectively. Record
pointers are formatted as the name of the record class, followed by a ".",
followed by the (decimal) address of the record. NULL!RECORD is printed for
the empty record.
If the default format is not satisfactory then the user may give a function
call as an argument. For example,
PRINT (CVOS (I));
DESTINATIONS SETPRINT, GETPRINT
will print I in octal, since CVOS is called first. (The expression CVOS (I)
is of course a String expression.) Wizards may also change the default
formatting function for a given syntactic type.
DESTINATIONS
CPRINT interprets <integerexpression> as a Sail channel number and sends
all output to that channel. The following two statements are functionally
equivalent:
CPRINT (CHAN, "The values are ", I, " and ", X);
OUT (CHAN, "The values are "&CVS (I)&" and "&CVG (X));
PRINT initially sends all output to the terminal but can also direct output
to a file or any combination of terminal and/or file. The modes of PRINT
are (dynamically) established and queried by SETPRINT and GETPRINT.
*************************** SETPRINT, GETPRINT *************************;
SETPRINT ("FILENAME", "MODE");
"MODE" _ GETPRINT
Here MODE is a single character which represents the destination of PRINT
output.
MODE MEANING
"T" the Terminal gets all PRINT output. If an output file is open
then close it. "T" is the mode in which PRINT is initialized.
"F" File gets PRINT output. If no file is open then open one as
described below.
"B" Both terminal and file get PRINT output. If no file is open then
open one as described below.
"N" Neither the file nor the terminal gets any output. If a file is
open then close it.
"S" Suppress all output, but open a file if none is open.
"O" a file is Open, but the terminal is getting all output. If no
file is open then open one as described below.
"C" the terminal gets output, but ignore whether or not a file is
open and whether or not it is getting output.
"I" terminal does not get output. Ignore whether or not a file is
open and whether or not file is getting any output.
SIMPLE USE CAVEATS
The first 6 possibilities represent the logical states of the PRINT system
and are the characters which GETPRINT can return. The "C" and "I" modes
turn terminal output on and off without disturbing anything else. The PRINT
statement is initialized to mode "T" -- print to Terminal. Modes "T", "F",
and "B" are probably the most useful. The other modes are included for
completeness and allow the user to switch between various combinations
dynamically.
If SETPRINT is called in such a way that a file has to be opened -- e.g.,
mode "F" and no file is open -- then FILENAME will be used as the name of
the output file. If FILENAME is NULL then the filename will be obtained
from the terminal.
SETPRINT (NULL, "F");
first types the message
File for PRINT output *
and uses the response as the name of a file to open. On TENEX, GTJFN with
recognition is used; on TOPS-10 and its variants the filename is read with
INCHWL. The file opened by SETPRINT will be closed when the program
terminates by falling through the bottom. It will also be closed if the
user calls SETPRINT with some mode that closes the file -- e.g., "T" will
close an output file if one is open.
SETPRINT and GETPRINT are related only to PRINT; they have no effect on
CPRINT.
SIMPLE USE
Here are a few examples of common output situations.
1) PRINT to TERMINAL. Simply use PRINT; do not bother with SETPRINT.
2) PRINT to FILE. Call SETPRINT (NULL, "F"); and type the name of the
output file when it asks.
3) PRINT to FILE and TERMINAL. At the beginning of the program call
SETPRINT (NULL, "B"); and type the name when asked.
4) PRINT to FILE always and sometimes also to TERMINAL. Use SETPRINT
(NULL, "B"); and give the name of the file when it asks. This sets
output to both the terminal and the file. Then to ignore the terminal
(leaving the file alone), call SETPRINT (NULL, "I"); To resume output at
the terminal use SETPRINT (NULL, "C"); This is useful for obtaining a
cleaned-up printout on the file with error messages going to the
terminal.
CAVEATS
Trying to exploit the normal Sail type conversions will probably lead to
FOR WIZARDS ONLY
trouble with PRINT and CPRINT. Printing single ASCII characters is a
particular problem.
OUTSTR ('14);
prints a form-feed onto the terminal , but
PRINT ('14);
prints "12". The reason, of course, is the default formatting of integers
by PRINT or CPRINT. This problem is particularly severe with macros that
have been defined with an integer to represent an ASCII character. For
example,
DEFINE TAB="'11";
PRINT (TAB);
will print "9". The solution is to define the macro so that it expands to a
STRING constant rather than an integer.
DEFINE TAB=" "; or
DEFINE TAB=('11 & NULL);
Also, remember that the first argument to CPRINT is the channel number.
FOR WIZARDS ONLY
All output going to either the PRINT or CPRINT statements can be trapped by
setting user table entry $$PROU to the address of a SIMPLE procedure that
has one string and one integer argument.
SIMPLE PROCEDURE MYPRINT
(INTEGER CHAN; STRING S);
BEGIN ... END;
GOGTAB[$$PROU] _ LOCATION (MYPRINT);
The CHAN argument is either the CHAN argument for CPRINT, or -1 for PRINT.
If this trap is set then all output from PRINT and CPRINT goes through the
user routine and is not printed unless the user invokes OUT or OUTSTR from
within the trap routine itself.
To trap the formatting function for any syntactic type the user should set
the appropriate user table address to the location of a function that
returns a string and takes as an argument the syntactic type in question.
To print integers in octal , preceded by "'", use
SIMPLE STRING PROCEDURE MYCVOS (INTEGER I);
RETURN ("'" & CVOS (I));
GOGTAB[$$FINT] _ LOCATION (MYCVOS);
The names for the addresses in the user table associated with each
formatting function are:
INDEXTYPE
$$FINT INTEGER
$$FREL REAL
$$FITM ITEM
$$FSET SET
$$FLST LIST
$$FSTR STRING
$$FREC RECORDPOINTER
To restore any formatting function to the default provided by the PRINT
system, zero the appropriate entry of the user table.
MACROS AND CONDITIONAL COMPILATION Syntax
SECTION 9
MACROS AND CONDITIONAL COMPILATION
9.1 Syntax
<define>
::= DEFINE <deflist> ;
::= REDEFINE <deflist> ;
::= EVALDEFINE <deflist> ;
::= EVALREDEFINE <deflist> ;
<deflist>
::= <def>
::= <deflist> , <def>
<def>
::= <identifier> = <macrobody>
::= <identifier> ( <idlist> ) =
<macrobody>
::= <identifier> <stringconstant> =
<macrobody>
::= <identifier> ( <idlist> )
<stringconstant> = <macrobody>
<macrobody>
::= <delimitedstring>
::= <constantexpression>
::= <macrobody> & <macrobody>
<macrocall>
::= <macroidentifier>
::= <macroidentifier> (
<macroparamlist> )
::= <macroidentifier> <stringconstant>
( <macroparamlist> )
<macroidentifier>
::= <identifier>
<macroparamlist>
::= <macroparam>
::= <macroparamlist> , <macroparam>
<condcompstatement>
::= <conditionalc.c.s.>
::= <whilec.c.s.>
::= <forc.c.s.>
::= <forlistc.c.s.>
::= <casec.c.s.>
<conditionalc.c.s.>
::= IFC <constantexpression> THENC
<anything> ENDC
::= IFC <constantexpression> THENC
<anything> ELSEC <anything> ENDC
::= IFCR <constantexpression> THENC
<anything> ENDC
::= IFCR <constantexpression> THENC
<anything> ELSEC <anything> ENDC
<whilec.c.s.>
::= WHILEC <delimitedexpr> DOC
<delimitedanything> ENDC
<forc.c.s.>
::= FORC <identifier> _
<constantexpression> STEPC
<constantexpression> UNTILC
<constantexpression> DOC
<delimitedanything> ENDC
<forlistc.c.s.>
::= FORLC <identifier> _ (
<macroparamlist> ) DOC
<delimitedanything> ENDC
<casec.c.s.>
::= CASEC <constantexpression> OFC
<delimitedanythinglist> ENDC
<delimitedanythinglist>
::= <delimitedanything>
::= <delimitedanythinglist> ,
<delimitedanything>
Delimiters
<assignc>
::= ASSIGNC <identifier> = <macrobody> ;
<delimitedstring>, <macroparam>, <delimitedexpr>, <anything> and
<delimitedanything> are explained in the following text.
9.2 Delimiters
There are two types of delimiters used by the Sail macro scanner: macro body
delimiters and macro parameter delimiters. Their usage will be precisely
defined in the sections on Macro Bodies and Parameters to Macros. Here we
will discuss their declaration and scope, which is very important when using
source files with different delimiters (see page 24 to find out about source
files).
Sail initializes both left and right delimiters of both body and parameter
delimiters to the double quote ("). One may change delimiters by saying
REQUIRE "<>" DELIMITERS.
In this example, the left and right body delimiters become "" and "",
while the left and right parameter delimiters become "<" and ">". Require
Delimiters may appear wherever a statement or declaration is legal. One
should Require Delimiters whenever all but the most simple macros are going
to be used. The first Require Delimiters will initialize the macro
facility; if this is not done, some of the following conveniences will not
exist and only very simple macros like defining CRLF = "('12 & '15 )" may be
done.
Delimiters do not follow block structure. They persist until changed.
Furthermore, each time new delimiters are Required, they are stacked on a
special "delimiters stack". The old delimiters may be revived by saying
REQUIRE UNSTACKDELIMITERS
Thus, each source file with macros should begin with a Require delimiters,
and end with an Unstackdelimiters. It is impossible to Unstack off the
bottom of the stack. The bottom element of the stack is the double quote
delimiters that Sail initialized the program to. If you Unstack from these,
the Unstack will become a no-op, and the double quote delimiters remain the
delimiters of your program.
One may circumvent the delimiter stacking feature by saying
REQUIRE "<>" REPLACEDELIMITERS
Macros
instead of REQUIRE "<>" DELIMITERS. This doesn't deactivate the stacking
feature, it merely changes the active delimiters without stacking them.
To revert to the primitive, initial delimiter mode where double quotes are
the active delimiters, one may say
REQUIRE NULL DELIMITERS
Null delimiters are stacked in the delimiter stack in the ordinary REQUIRE
"<>" DELIMITERS way. In null delimiters mode, the double quote character
may be included in the macro body or macro parameter by using two double
quotes:
DEFINE SOR = "OUTSTR(""SORRY"");";
The Null Delimiters mode is essentially the macro facility of ancient
versions of Sail where " was the only delimiter. Programs written ancient
in Sail versions will run in Null Delimiters mode. Null delimiters mode has
all the rules and quirks of the prehistoric Sail macro system (the old Sail
macro facility is described in [Swinehart & Sproull], Section 13).
Compatibility with the ancient Sail is the only reason for Null Delimiters.
9.3 Macros
We will delay the discussion of macros with parameters until the next
section. A macro without parameters is declared by saying:
DEFINE <macroname> = <macrobody> ;
where <macroname> is some legal identifier name (see page 228 for a
definition of a legal identifier name). <macrobody>s can be simply a
sequence of Ascii characters delimited by macro body delimiters, or they can
be quite complex. Once the macro has been defined, the macro body is
substituted for every subsequent appearance of the macro name. Macros may
be called in this way at any point in a Sail program, except inside a
Comment or a string constant.
Macro declarations may also appear virtually anywhere in a Sail program.
When the word DEFINE is scanned by Sail, the scanner traps to a special
production. The Define is parsed, and the scanner returns to its regular
mode as if there had been no define there at all. Thus things like
I _ J + 5 + DEFINE CON = '777; K^2;....
are perfectly acceptable. However, don't put a Define in a string constant
or a Comment.
SCOPE MACRO BODIES DELIMITED STRINGS
SCOPE
Macros obey block structure. Each DEFINE serves both as a declaration and
an assignment of a macro body to the newly declared symbol. Two DEFINEs of
the same symbol in the at the same lexical level will be flagged as an
error. However, it is possible to change the macro body assigned to a macro
name without redeclaring the name by using saying REDEFINE instead of
DEFINE. For example,
BEGIN
...
BEGIN
...
DEFINE SQUAK = OUTSTR("OUTER BLOCK");;
...
BEGIN
...
REDEFINE SQUAK = OUTSTR("INNER BLOCK");;
...
END;
...
SQUAK COMMENT Here the program types
"INNER BLOCK";
END; COMMENT Here SQUAK is undefined.
If SQUAK were included here, you'd
get the error message
"UNDEFINED IDENTIFIER:SQUAK";
END
REDEFINE of a name that has not been declared in a DEFINE will act as a
DEFINE. That is, it will also declared the macro name as well as assigning
a body to it. MACRO BODIES
A Macro Body may be
1. A sequence of Ascii characters preceded by a left macro body
delimiter and followed by a right macro body delimiter.
2. An integer expression that may be evaluated at compile time.
3. A string expression that may be evaluated at compile time.
4. Concatenations of the above.
WARNING: Source file switching inside macros will not work.
DELIMITED STRINGS
Any sequence of Ascii characters, including " may be used as a macro body if
they are properly delimited. The macro body scanner keeps a count of the
number of left and right delimiters seen and will terminate its scan only
when it has seen the same number of each. This lets the macro body
delimiters "nest" so that one may include DEFINEs inside a macro body. For
example,
INTEGER COMPILE TIME EXPRESSIONS STRING COMPILE TIME EXPRESSIONS
DEFINE DEF =
DEFINE SYM = SYMBOL; SYM ;
One may temporarily override the active delimiters by including a two
character string before the "=" of the Define statement. For example:
DEFINE LES "&%" = & 0X<BIGGEST Y>X %;
The first character of the two character string becomes the left delimiter,
and the second becomes the right delimiter.
INTEGER COMPILE TIME EXPRESSIONS
Sail tries to do as much arithmetic as it can at compile time. In
particular, if you have an arithmetic expression of constants, such as
91.504 + (3.1415*8^(9-7))
% "Sail can convert strings"
then the whole expression will be evaluated at compile time and the
resultant constant, in this case 93.9263610, will be used in your code
instead of the constant expression. Runtime functions of constants will be
done at compile time too, if possible. EQU and the conversion routines
(CVS, CVO, etc.) will work.
When an integer compile time expression is scanned as part of a macro body,
it is immediately evaluated. The integer constant which results is
converted to a character string, and that character string used for the
place in the macro body of the integer expression. Thus,
DEFINE TTYUUO = '51 LSH 27 ;
will cause '51 LSH 27 to be evaluated, and the resulting constant,
5502926848, will be converted to the character string 5502926848, and that
character string assigned to the macro name TTYUUO.
STRING COMPILE TIME EXPRESSIONS
If a compile time expression has the type string (constant), the macro
scanner will evaluate the expression immediately. However, the string
constant that results will not be converted to the character string that
represents that constant, but to the character string with the same
characters that the string constant had. Thus, the way to use a macro for
string constants is to delimit the string constant like this:
DEFINE STRINCON = "Very long
complex string that is hard
to type more than once" ;
However, the automatic conversion of string constants to character strings
is helpful and indeed essential for automatic generation of identifiers:
HYBRID MACRO BODIES
DEFINE N = 1;
COMMENT we will use this like a variable;
DEFINE GENSYM =
DEFINE SYM = TEMP & CVS(N);
COMMENT SYM is defined to be the character
string TEMP# where # is an number;
REDEFINE N = N+1;
COMMENT This increments N;
SYM ;
COMMENT At the call of SYM, the character
string is read like program text. E.g...;
INTEGER GENSYM, GENSYM, GENSYM, GENSYM;
REAL GENSYM, GENSYM;
COMMENT We have generated 6 identifiers with
unique names, and declared 4 as integers,
2 as reals;
To convert a macro body to a string constant, one may use CVMS. Similarly,
a macro parameter is converted to a string constant by CVPS.
<string constant> _ CVMS (<macro name>);
<string constant> _ CVPS (<macro parameter name>)
A string that has the exact same characters as the macro body will be
returned. For example:
DEFINE A = B & C;
DEFINE ABC = CVMS (A) & & D;
COMMENT ABC now stands for the text B & C & D;
HYBRID MACRO BODIES
When two delimited strings are concatenated, the result is a longer
delimited string. "&" in compile time expression behaves the same way it
behaves in any expression. When a compile time expression is concatenated
to a delimited character string in a macro body, the result is exactly the
result one would get if the delimited character string were a string
constant, except that the result is a delimited character string. For
example:
DEFINE N = 1;
DEFINE M = 2;
DEFINE SYM = CVS(N*M + N^2) & -SQRT(N*M+1);
DEFINE SYM1 = 3-SQRT(N*M+1);
Here SYM is exactly the same as SYM1.
Macros with Parameters
9.4 Macros with Parameters
One defines a macro with parameters by specifying the formal parameters in a
list following the macro name:
DEFINE MAC (A, B) = IF A THEN B ELSE ERR_1;;
One calls a macro with parameters by including a list of delimited character
strings that will be substituted for each occurrence of the corresponding
formal in the macro body. For example,
COMMENT we assume that "<" and ">" are the
parameter delimiters at this point;
MAC (<BYTES LAND (BITMASK + '2000)>, <
BEGIN
WWDAT _ FETCH (BYTES, ENVIRON);
COLOR[WWDAT] _ '2000;
END >)
expands to
IF BYTES LAND (BITMASK + '2000) THEN
BEGIN
WWDAT _ FETCH (BYTES, ENVIRON);
COLOR[WWDAT] _ '2000;
END
ELSE ERR_1;
Parameter delimiters nest. Furthermore, if no delimiters are used about a
parameter, nesting counts are kept of "()", "[]", and "{~" character pairs.
The parameter scan will not terminate until the nesting counts of each of
the three pairs is zero. One may temporarily override the active parameter
delimiters by including a two character string ahead of the parameter list
in the macro call:
MAC "" (BYTES > '2000, MATCH(BYTES))
Formal parameters may not appear in compile time expressions that are used
to specify macro bodies. This is quite natural: compile time expressions
must be evaluated as they are scanned, but the value of a formal parameter
isn't known until later. However, if the macro body is a hybrid of
expressions and delimited character strings, then formal parameters may
appear in the delimited string parts.
When doing a CVMS on a macro with parameters, use only the macro name in the
call; the parameters are unnecessary. The string returned will have the two
character strings "1", "2", etc. (here stands for the Ascii character
'177) where the formal parameters were in the macro body. A "1" will
appear wherever the first formal parameter of the formal parameter list
appear in the macro body, a "2" will appear wherever the second parameter
Conditional Compilation
appeared, etc. The unfortunate appearance of the Ascii character '177 in
CVMS-generated strings is a product of the representation of macro bodies as
strings ending in '177, '0 (which CVMS removes), having '177, n for each
appearance of the nth formal parameter in the body.
9.5 Conditional Compilation
The compile time equivalents of the Sail IF, WHILE, FOR and CASE statements
are
IFC <CT expr> THENC <anything> ENDC
IFC <CT expr> THENC <anything> ELSEC
<anything> ENDC
WHILEC <CT expr> DOC <anything> ENDC
FORC <CT variable> _ <CT expr> STEPC <CT expr>
UNTILC <CT expr> DOC <anything> ENDC
FORLC <CT variable> _ (<macro param>, ... ,
<macro param>) DOC <anything> ENDC
CASEC <CT expr> OFC <anything>, <anything>,
... , <anything> ENDC
where <CT expr> is any compile time expression. <CT expr> could itself
include IFCs, FORCs or whatever. <CT variable> is a macro name such as N
from a define such as DEFINE N = MUMBLE; <macro param> is anything that is
delimited like a macro parameter. <anything> can be anything one could want
in his program at that point, including Defines and other conditional
compilation statements. The usual care must be taken with nested IFCs so
that the ELSECs match the desired THENCs. The "" and "" characters above
are to stand for the current MACRO BODY DELIMITER pair.
The semantics are exactly those of the corresponding runtime statements,
with one exception. When the list to a FORLC is null (i.e. it looks like "(
)" ), then the <anything> is inserted in the compilation once, with the <CT
variable> assigned to the null macro body.
Situations frequently occur where the false part of an IFC must have the
macros in it expanded in order to delimit the false part correctly. For
example,
Type Determination at Compile Time DECLARATION CHECKTYPE EXPRTYPE
DEFINE DEBUGSELECT =
IFC DEBNUM = 2 THENC ;
DEFINE DEBUGEND =
ELSEC OUTSTR ("DEBUG POINT") ENDC;
Debugselect
OUTSTR ("DEBUG POINT #" & CVS (DBN));
Debugend
If DEBNUM is not 2, then the program must expand the macro Debugend in
order to pick up the ELSEC that terminates the false part of the
conditional. The expansion is only to pick up such tokens -- the text of
the false part is not sent to the scanner as the true part is. In order to
avoid such expansion, one may use IFCR (the R stands for "recursive")
instead of IFC.
As an added feature, when delimiters are required about an <anything> in the
above (such constructs are named <delimitedanything> in the BNF), one may
substitute a concatenation of constant expressions and delimited strings.
This is just like a macro body, except the concatenation MUST contain at
least one delimited string, thereby forcing the result of the concatenation
to be a delimited string, rather than a naked expression.
As a further added feature,
IFC <CT expr> THENC <anything> ELSEC
<anything> ENDC
may be substituted in FORCs, FORLCs, and WHILECs for the <anything>
following DOC.
NOTE: In a WHILEC, the expression must be delimited with the appropriate
macro body delimiters (hence the construct <delimitedexpr> in the BNF).
9.6 Type Determination at Compile Time
To ascertain the type of an identifier at compile time, one may use the
integer function DECLARATION (<identifier>). This returns an integer with
bits turned on to represent the type of identifier. Exactly what the bits
represent is a dark secret and changes periodically anyway. The best way to
decode the integer returned by Declaration is to compare it to the integer
returned by CHECKTYPE (<a string of Sail declarators>). A Sail declarator
is any of the reserved words used an a declaration. Furthermore, the
declarators must be listed in a legal order, namely, an order that is legal
in declarations (i.e. ARRAY INTEGER won't work). One may include as
arguments to CHECKTYPE the following special tokens:
BUILTIN LEAPARRAY RESERVED DEFINE CONOK
TOKEN EFFECT
BUILTIN The bit that is on when a procedure is known to preserve ACs
0-'11 (except AC1 if returning a value) is returned. Sail
does not clear the ACs when compiling a call on a BUILTIN
procedure.
LEAPARRAY The bit that is on when an identifier is an item or itemvar
with a declared array datum is returned (the discussion of
Leap starts on page 151).
RESERVED The bit that is on for a reserved word is returned.
DEFINE The bit that indicates the identifier is a macro name is
returned (note: a macro name as the argument to DECLARATION
will not be expanded).
CONOK The bit which says "this procedure will be evaluated at
compile time if all its arguments are constant expressions"
is returned.
Examples:
DECLARATION (FOO) = CHECKTYPE (INTEGER)
This is an exact compare. Only if Foo is
an integer variable will equality hold.
DECLARATION (A) LAND CHECKTYPE (ARRAY)
This is not an exact compare. If A is any
kind of an array, the LAND will be non-zero.
DECLARATION (CVS) = CHECKTYPE(EXTERNAL CONOK
OWN BUILTIN FORWARD STRING PROCEDURE)
The equality holds. FORWARD so that you can
redeclare it without complaints; OWN as a hack
which saves space in the compiler.
DECLARATION (BEG) LAND CHECKTYPE (RESERVED)
This is non-zero only if one has said
LET BEG = BEGIN. DEFINE BEG = BEGIN
will only turn the Define bit of BEG on.
NOTE: if the <identifier> of DECLARATION has not yet been declared or was
declared in an inner block, then 0 is returned -- it is undeclared so it has
no type.
EXPRTYPE returns the same bits that DECLARATION does, except that the
argument to EXPRTYPE may be an expression and not just an identifier.
Miscelaneous Features COMPILE TIME I/O EVALDEFINE, EVALREDEFINE ASSIGNC NOMAC COMPILERBANNER
9.7 Miscelaneous Features
COMPILE TIME I/O
Compile time input is handled by the REQUIRE "<filename>" SOURCEFILE
construct. <filename> can be any legal file, including TTY: and MTA0: and
of course disk files. (MTA does not work for TENEX.) The file will be read
until the its end of file delimiter is scanned (<ctrl>Z for TTYs or
<meta><ctrl><lf> at SUAI), and its text will replace the REQUIRE statement
in the main file.
Compile time output is limited to typing a message on the user's teletype.
To do this say REQUIRE <stringconstant> MESSAGE, and the <stringconstant>
will appear on your teletype when the compilation hits that point in your
file.
EVALDEFINE, EVALREDEFINE
The reserved word EVALDEFINE may be used in place of the word DEFINE if one
would like the identifier that follows to be expanded. When one follows a
DEFINE with a macro name, the macro is not expanded, but rather the macro
name is declared at the current lexical level and assigned the specified
macro body. EVALDEFINE gets you around that. Helps with automatic
generation of macro names. EVALREDEFINE is also available.
ASSIGNC
The following compile time construct makes recursive macros easier.
ASSIGNC <name1> = <macrobody>;
<name1> must be a formal to a macro, and <macrobody> may be any macro body.
Thereafter, whenever <name1> is instantiated, the body corresponding to
<macrobody> is used in the expansion rather than the text passed to the
formal at the macro call.
RESTRICTION: ASSIGNC may only appear in the body of the macro that <name1>
is a formal of. If it appears anywhere else, the <name1> will be expanded
like any good formal, and that text used in the ASSIGNC as <name1>. Unless
you're being very clever, this is probably not what you want.
NOMAC
Preceding anything by the token NOMAC will inhibit the expansion of that
thing should that thing turn out to be a macro.
COMPILERBANNER
This is a predefined macro which expands to a string constant containing the
text of the two-line banner which would appear at the top of the current
page if a listing file were being made. This string contains the date,
time, name and page of the source file, the value of all compiler switches,
the name of the outer block, and the name of the current block. Thus you
can automatically include the date of compilation in a program by using
COMPILERBANNER[n TO m] for appropriate n and m. Try REQUIRE
COMPILERBANNER MESSAGE; or look at a listing for the exact format.
Hints
9.8 Hints
The following is a set of hints and aids in debugging programs with macros.
Unless otherwise stated array brackets "[]" are the macro body delimiters.
IFC and friends will not trigger at the point of macro definition, in a
macro actual parameter list, or inside a string constant.
DEFINE FOO = [IFC A THENC B ELSEC D ENDC];
which is not the same as
DEFINE FOO = IFC A THENC [B] ELSEC [D] ENDC;
which is the same as
IFC A THENC DEFINE FOO = [B]
ELSEC DEFINE FOO = [D] ENDC;
DEFINE BAZ (A) = [OUTSTR ("A");];
BAZ (IFC B THENC C ELSEC D ENDC)
will result in the following string typed
on your terminal:
IFC B THENC C ELSEC D ENDC
STRING A;
A_"IFC WILL NOT TRIGGER HERE";
Macros will not be expanded in strings, but macro formal parameters will be
expanded when they occur in strings within macro bodies as seen in the
second example above.
DEFINE FOO = [BAZ];
OUTSTR ("FOO");
which will type out the string FOO on your terminal rather than BAZ.
Caution should be employed when using letters (specifically ) as
delimiters. This may lead to problems when defining macros within macros.
DEFINE MAC(A) "" = REDEFINE FOO =A;;
Inside the macro body of MAC, A will not be recognized as a formal since the
scanner has scanned A as an identifier by virtue of being internally
represented as letters so that they could be defined to mean BEGIN and END
respectively (also as COMMENT). More justification for this feature is
seen by the following example:
DEFINE MAC(ABC) "AC" = A V_ABC; C;
We want ABC in the text to be the parameter and not B if we were to ignore
the macro delimiters.
When scanning lists of actual parameters, macros are not expanded.
DEFINE FOO = [A,B];
MAC (FOO) will not have the result MAC(A,B). However,
DEFINE FOO = [(A, B)];
followed by MAC FOO will have the same effect as
MAC (A, B).
The same reasoning holds for parameter lists to FORLC.
DEFINE FOO = [A, B, C];
FORLC I = (FOO) DOC [OUTSTR ("I");] ENDC
will result in FOO typed out on your terminal.
DEFINE FOO = [(A, B, C)];
FORLC I = FOO DOC [OUTSTR ("I");] ENDC
will have the desired result ABC typed out.
In order to take advantage of the nestable character feature in the
parameters to a macro call, one must be in REQUIRE DELIMITERS mode.
Otherwise scanning will break upon seeing a comma or a right parenthesis.
BEGIN
DEFINE FOO(A) = "A";
INTEGER ARRAY ABC[1:10, 1:10];
FOO (ABC[1, 2])_3;
END;
This is identical to:
BEGIN
INTEGER ARRAY ABC[1:10, 1:10];
ABC[1_3; Comment illegal;
END;
However, if the original program had included a REQUIRE DELIMITERS statement
prior to the macro call, as below, then the desired effect would have
resulted - i.e., ABC[1, 2]_3 .
BEGIN
REQUIRE "{~%$" DELIMITERS;
DEFINE FOO (A) = {A~;
INTEGER ARRAY ABC[1:10, 1:10];
FOO (ABC[1, 2])_3;
END;
RECORD STRUCTURES Introduction Declaration Syntax Declaration Semantics
SECTION 10
RECORD STRUCTURES
10.1 Introduction
Record structures are new to Sail. They provide a means by which a number
of closely related variables may be allocated and manipulated as a unit,
without the overhead or limitations associated with using parallel arrays
and without the restriction that the variables all be of the same data type.
In the current implementation, each record is an instance of a user-
defined record class, which serves as a template describing the various
fields of the record. Internally, records are small blocks of storage which
contain space for the various fields and a pointer to a class
descriptor record. Fields are allocated one per word and are accessed
by constant indexing off the record pointer. Deallocation is performed
automatically by a garbage collector or manually through explicit calls to a
deallocation procedure.
10.2 Declaration Syntax
<recordclassdeclaration>
::= RECORDCLASS <classid> (
<fielddeclarations> )
::= RECORDCLASS <classid> (
<fielddeclarations> ) [ <handler> ]
<recordpointerdeclaration>
::= RECORDPOINTER ( <classidlist> )
<idlist>
::= RECORDPOINTER ( ANYCLASS )
<idlist>
10.3 Declaration Semantics
The <fielddeclarations> have the same form as the <formalparamdecl> of
a procedure, except that the words VALUE and REFERENCE should not be
used, and default values are ignored. Each record class declaration is
compiled into a record descriptor (which is a record of constant record
class $CLASS) and is used by the runtime system for allocation,
deallocation, garbage collection, etc. At runtime record pointer variables
contain either the value NULLRECORD (internally, zero) or else a pointer to
a record. The <classid list> is used to make a compile-time check on
assignments and field references. The pseudo-class ANYCLASS matches all
classes, and effectively disables this compile-time check.
For instance,
RECORDCLASS VECTOR (REAL X, Y, Z);
RECORDCLASS CELL
(RECORDPOINTER (ANYCLASS) CAR, CDR);
RECORDCLASS TABLEAU
(REAL ARRAY A, B, C; INTEGER N, M);
RECORDCLASS FOO (LIST L; ITEMVAR A);
RECORDPOINTER (VECTOR) V1,V2;
RECORDPOINTER (VECTOR, TABLEAU) T1,T2;
RECORDPOINTER (ANYCLASS) R;
RECORDPOINTER (FOO, BAR) FB1, FB2;
RECORDPOINTER (FOO) FB3;
RECORDPOINTER (CELL) C;
RECORDPOINTER (ANYCLASS) RP;
COMMENT the following are all ok syntactically;
C _ NEWRECORD (CELL);
RP _ C;
FB2 _ NEWRECORD (FOO);
FB1 _ FB3;
FB3 _ RP; COMMENT This is probably a runtime bug
since RP will contain a cell record. Sail
won't catch it, however;
CELL:CAR[RP] _ FB1;
CELL:CAR[RP] _ FB1;
COMMENT The compiler will complain about these: ;
FB1 _ C;
FB3 _ NEWRECORD (CELL);
RP _ CELL:CAR[FB3];
NO runtime class information is kept with the record pointer variables, and
no runtime class checks are made on record assignment or field access.
Record pointer variables are allocated quantities, and should not appear
inside SIMPLE procedures. They resemble lists in that they are not given
any special value upon block entry and they are set to a null value
(NULLRECORD) when the block in which they are declared is exited. (This is
so that any records referred to only in that block can be reclaimed by the
garbage collector.)
Record pointers are regular Sail data types, just like integers or strings;
Allocation Fields
record pointer procedures, arrays, and items all work in the normal way. As
indicated earlier, the constant NULLRECORD produces a null reference.
10.4 Allocation
Records are allocated by
NEWRECORD (<classid>)
which returns a new record of the specified class. All fields of the new
record are set to the null or zero value for that field; i.e., real and
integer fields will be set to 0, itemvar fields to ANY, lists to NIL, etc.
Note that entry into a block with local record pointer variables does NOT
cause records to be allocated and assigned to those variables.
10.5 Fields
Record fields are referenced by
<classid> : <fieldid> [ <record pointer expression> ]
and may be used wherever an array element may be used. For example
RECORDPOINTER (VECTOR) V;
RECORDPOINTER (CELL) C;
RECORDPOINTER (FOO) F;
VECTOR:X[V] _ VECTOR:Y[V];
CELL:CAR[C _ NEWRECORD (CELL)] _ V;
VECTOR:Z[V] _ VECTOR:X[CELL:CAR[C]];
SUBLIS _ FOO:L[F][1 TO 3];
If the <record pointer expression> gives a null record, then a runtime error
message will be generated. This is the only runtime check that is made at
present. I.e., no runtime checks are made to verify that the <classid> in
the field statement matches the class of the record whose field is being
extracted.
An array field may be used as an array name, as in
RECORDPOINTER (TABLEAU) T;
TABLEAU:A[T][I,J] _ 2.5;
provided that a valid array descriptor has been stored into the field.
Unfortunately, Sail does not provide any clean way to do this. One unclean
way is
Garbage Collection Internal Representations
EXTERNAL INTEGER PROCEDURE ARMAK
(INTEGER LB, UB, #DIMS);
COMMENT returns address of first data word of new
array. For String arrays set #DIMS to -1,,n.
For higher dimensions declare with more LB, UB pairs;
EXTERNAL PROCEDURE ARYEL (INTEGER ARR);
COMMENT deallocates an array. ARR is the address of
the first data word;
RECORDCLASS FUBAR (INTEGER ARRAY A);
RECORDPOINTER (FUBAR) FB;
MEMORY[LOCATION (FUBAR:A[FB])] _ ARMAK (1, 100, 1);
ARYEL (MEMORY[LOCATION (FUBAR:A[FB])]);
(Warning: the above advice is primarily intended for hackers. No promises
are made that it will always work, although this particular trick is
unlikely to be made obsolete in the forseeable future.)
10.6 Garbage Collection
The Sail record service routines allocate records as small blocks from
larger buffers of free storage obtained from the normal Sail free storage
system. (The format of these records will be discussed in a later section.)
From time to time a garbage collector is called to reclaim the storage for
records which are no longer accessible by the user's program (i.e., no
variables or accessible records point to them). The garbage collector may
be called explicitly from Sail programs as external procedure $RECGC, and
automatic invocation of the garbage collection may be inhibited by setting
user table entry RGCOFF to TRUE. (In this case, Sail will just keep
allocating more space, with nothing being reclaimed until RGCOFF is set back
to FALSE or $RECGC is called explicitly). In addition, Sail provides a
number of hooks that allow a user to control the automatic invocation of the
garbage collector. These are discussed later.
10.7 Internal Representations
Each record has the following form:
-1: <ptrs to ring of all records of class>
0: <garbage collector ptr>,,<ptr to class descriptor>
+1: <first field>
: :
+n: <last field>
$CLASS RECRNG HNDLER RECSIZ TYPARR TXTARR
Record pointer variables point at word 0 of such records. A
String field contains the address of word2 of a string descriptor, like the
string was a REFERENCE parameter to a procedure. The string descriptors are
also dynamically allocated.
The predefined record class $CLASS defines all record classes, and is itself
a record of class $CLASS.
RECORDCLASS $CLASS
(INTEGER RECRNG, HNDLER, RECSIZ;
INTEGER ARRAY TYPARR; STRING ARRAY TXTARR);
RECRNG is a ring (bidirectional linked list) of all records of the
particular class.
HNDLER is a pointer to the handler procedure for the class (default $REC$).
RECSIZ is the number of fields in the class.
TYPARR is an array of field descriptors for each field of the class.
TXTARR is an array of field names for the class.
The normal value for the handler procedure is $REC$, which is the
standard procedure for such functions as allocation, deallocation, etc.
TYPARR and TXTARR are indexed [0:RECSIZ]. TXTARR[0] is the name of the
record class. TYPARR[0] contains type bits for the record class.
Handler Procedures
Example:
RECORDCLASS FOO (LIST L; ITEMVAR A);
The record class descriptor for FOO contain:
FOO-1: <ptrs for ring of all records of $CLASS>
FOO: <ptr to $CLASS>
FOO+1: <ptrs for ring of all records of class FOO;
initialized to <FOO+2,,FOO+2> >.
FOO+2: <ptr to handler procedure $REC$>
FOO+3: 2
FOO+4 <ptr to TYPARR>
FOO+5: <ptr to TXTARR>
The fields of FOO are:
$CLASS:RECRNG[FOO] = <initialized to null ring,
i.e., xwd(loc(FOO)+2,loc(FOO)+2)>
$CLASS:HNDLER[FOO] = $REC$
$CLASS:RECSIZ[FOO] = 2
$CLASS:TXTARR[FOO] [0] = "FOO"
$CLASS:TXTARR[FOO] [1] = "L"
$CLASS:TXTARR[FOO] [2] = "A"
$CLASS:TYPARR[FOO] [0] = <bits for garbage collector>
$CLASS:TYPARR[FOO] [1] = <descriptor for LIST>
$CLASS:TYPARR[FOO] [2] = <descriptor for ITEMVAR>
10.8 Handler Procedures
Sail uses a single runtime routine $RECFN (OP, REC) to handle such system
functions as allocation, deallocation, etc. The code compiled for r _
NEWRECORD (foo) is
PUSH P,[1]
PUSH P,[foo]
PUSHJ P,$RECFN
MOVEM 1,r
$RECFN performs some type checking and then jumps to the handler procedure
for the class. The normal value for this handler procedure is $REC$. It is
possible to substitute another handler procedure for a given class of
records by including the procedure name in brackets after the record class
declaration. The handler must have the form
RECORDPOINTER (ANYCLASS) PROCEDURE <procid>
(INTEGER OP; RECORDPOINTER (ANYCLASS) R);
Here OP will be a small integer saying what is to be done. The current
assignments for OP are:
value meaning
0 invalid
1 allocate a new record of record class R
2 not used
3 not used
4 mark all fields of record R
5 delete all space for record R
At SUAI, macro definitions for these functions may be found in the file
SYS:RECORD.DEF, which also includes EXTERNAL declarations for $CLASS, $REC$,
and $RECFN.
$REC$ (1, R) allocates a record of the record class specified by R, which
must be a record of class $CLASS. All fields (except string) are
initialized to zero. String fields are initialized to a pointer to a string
descriptor with length zero (null string).
$REC$ (4, R) is used by the garbage collector to mark all record fields of
R.
$REC$ (5, R) deallocates record R, and deallocates all string and array
fields of record R. Care must be exercised to prevent multiple pointers to
string and array fields; i.e., DO NOT store the location of an array in
fields of two different records unless extreme caution is taken to handle
deletion. This can be accomplished through user handler procedures which
zero array fields (without actually deleting the arrays) prior to the call
on $REC$ (5, R).
NOTE: When an alternate handler procedure is supplied it must perform all
the necessary functions. One good way to do this is to test for those OPs
performed by the alternate handler and call $REC$ for the others. If $REC$
is used to allocate space for the record then it should also be used to
release the space. These points are illustrated by the following example:
More about Garbage Collection
FORWARD RECORDPOINTER (ANYCLASS) PROCEDURE
FOOH (INTEGER OP;
RECORDPOINTER (ANYCLASS) R);
RECORDCLASS FOO (ITEMVAR IV) [FOOH];
RECORDPOINTER (ANYCLASS) PROCEDURE FOOH
(INTEGER OP; RECORDPOINTER (ANYCLASS) R);
BEGIN
PRINT("CALLING FOOH. OP = ", OP);
IF OP = 1 THEN
BEGIN
RECORDPOINTER (FOO) F;
F _ $REC$ (1,R);
FOO:IV[F] _ NEW;
RETURN (F);
END
ELSE IF OP = 5 THEN
DELETE (FOO:IV[R]);
RETURN ($REC$ (OP, R));
END;
10.9 More about Garbage Collection
The information used by the system to decide when to call $RECGC on its own
is accessible through the global array $SPCAR. In general, $SPCAR[n] points
at a descriptor block used to control the allocation of small blocks of n
words. This descriptor includes the following fields:
BLKSIZ number of words per block in this space
TRIGGERa counter controlling time of garbage collection
TGRMIN described below
TUNUSEDnumber of unused blocks on the free list
TINUSE total number of blocks in use for this space
CULPRITthe number of times this space has caused
collection
The appropriate macro definitions for access to these fields may be found in
the source file SUAISYS:RECORD.DEF. The decision to invoke the garbage
collector is made as part of the block allocation procedure, which works
roughly as follows:
INTEGER spc,size;
size _ $CLASS:RECSIZ[classid]+2;
IF size>16 THEN return a CORGET block;
spc _ $SPCAR[size];
L1:
IF (MEMORY[spc+TRIGGER]
_ MEMORY[spc+TRIGGER]-1) <0
THEN BEGIN
IF MEMORY[GOGTAB+RGCOFF] THEN BEGIN
MEMORY[spc+CULPRIT] _ MEMORY[spc+CULPRIT]+1;
$RECGC;
GO TO L1;
END END;
<allocate the block from space spc,
update counters, etc.>
Once $RECGC has returned all unused records to the free lists associated
with their respective block sizes, it must adjust the trigger levels in the
various spaces. To do this, it first looks to see if the user has specified
the location of an adjustment procedure in TGRADJ(USER). If this cell is
non-zero then $RECGC calls that procedure (which must have no parameters).
Otherwise it calls a default system procedure that works roughly like this:
<set all TRIGGER levels to -1>
FOR size _ 3 STEP 1 UNTIL 16 DO BEGIN
spc _ $SPCAR[size];
IF MEMORY[spc+TRIGGER]<0 THEN BEGIN
t_MEMORY[spc+TINUSE]*RGCRHO(USER);
t_MAX(t, MEMORY[spc+TUNUSED],
MEMORY[spc+TGRMIN]);
END END;
RGCRHO(USER) is a real number currently initialized by the system to 0.33.
Thus the behavior of Sail's automatic garbage collection system may be
modified by
Setting RGCOFF(USER).
Supplying a procedure in TGRADJ(USER).
Modifying RGCRHO(USER).
Modifying the TGRMIN entries in the space descriptors.
One word of caution: User procedures that set trigger levels must set the
trigger level of the space that caused garbage collection to some positive
value. If not then a runtime error message will be generated.
Look at the file SUAIRECAUX.SAI[CSP,SYS], which contains a number of
useful examples and auxilliary functions.
TENEX ROUTINES Introduction TOPS-10 Style Input/Output
SECTION 11
TENEX ROUTINES
11.1 Introduction
This section describes routines which interface Sail with the TENEX
operating system. Routines for file input/output, terminal handling, and
miscellaneous system calls are described here. For TENEX-specific details
of other routines (such as interrupts) consult the appropriate chapter.
11.2 TOPS-10 Style Input/Output
"Standard" Sail programs written using TOPS-10 I/O routines such as OPEN,
LOOKUP, etc., will run under TENEX with little or no conversion necessary.
The TENEX Sail routines simulate most of the effects of the TOPS-10 I/O
calls without using the PA-1050 emulator.
In TENEX Sail the non-zero values of error flags returned by routines such
as LOOKUP are ERSTR JSYS error numbers. The interpretation of zero/nonzero
is the same as with the TOPS-10 I/O routines, but the specific nonzero
values are probably different.
Here are the TOPS-10 I/O routines and the differences, if any, under TENEX.
ARRYIN TENEX dump mode implies a single DUMPI JSYS.
ARRYOUT similar to ARRYIN.
CLOSE The close inhibit bits have no effect.
CLOSIN same as CLOSE.
CLOSO same as CLOSE.
ENTER no differences.
GETCHAN In TOPS-10, GETCHAN returns the number of a channel for which no
OPEN is currently in effect. Thus successive GETCHANs without
intervening OPENs will return the same channel number. In TENEX
Sail, GETCHAN returns the number of a channel for which no OPEN or
GETCHAN is currently in effect; thus successive GETCHANs will return
different channel numbers.
GETSTS not available; see GDSTS, GTSTS.
INOUT not available.
INPUT assumes 200 characters maximum if no length variable has been
associated with the channel.
INTIN no differences.
LINOUT no differences.
LOOKUP no differences.
MTAPE Options "I" and NULL are not available.
OPEN MODE is mostly ignored (exception: dump mode on a dectape ignores
the directory). The number of input and output buffers serves only
to indicate whether reading or writing is desired.
OUT no differences.
REALIN no differences.
RELEASE The close inhibit bits have no effect.
RENAME Changing the protection does not work. See GTFDB and CHFDB.
SETPL The routines CHARIN and SINI do not update the variables associated
with the channel by SETPL.
SETSTS not available; see SDSTS, STSTS.
STDBRK no differences.
TMPIN not available.
TMPOUT not available.
USETI works only on those devices where the SFPTR JSYS works. On a
dectape the MTOPR JSYS is used, and may not produce the same results
as on a TOPS-10 system. USETI takes effect immediately (the
nondeterminancy of the standard TOPS-10 (not SUAI) USETI is not
simulated). Equivalent to SFPTR (chan, (N-1)*'200);
USETO same as USETI. TENEX has only one file pointer, so in fact USETI
and USETO are EXACTLY the same function.
WORDIN no differences.
WORDOUT no differences.
MAGTAPE I/O TENEX Style Input/Output OBTAINING ACCESS
MAGTAPE I/O
The user is warned that there are serious limitations in TENEX regarding
magtapes. While TENEX is supposed to have device-independent I/O, the
magtape code in TENEX (as of v. 1-31) is minimal, allowing only dump mode
transfers. Further, end of file markers must be written explicitly, and it
is sometimes necessary to do an MTOPR operation 0 to reset the magtape
status bits.
TENEX Sail has been designed to handle some of these things in a way that
makes features available on a standard TOPS-10 system available in a
transparent way. For example, string input and output functions work, with
Sail assuming 128-word records on the tape. ARRYIN and ARRYOUT cause the
DUMPI and DUMPO JSYSes to be executed for the specified word counts. TENEX
Sail does not actually open tapes for write until a write operation is
requested. A CLOSF or CFILE on a tape will write two EOF'S (MTAPE (ch,
"E")) and backspace over one of them, if and only if the file has been
opened. Do not rewind a tape unless it has been closed. The user who wants
to write magtape code for operations other than the above is hereby warned
that the TENEX magtape code is fraught with peril. TENEX Sail certainly
allows full access to TENEX in this regard, however.
11.3 TENEX Style Input/Output
The following functions satisfy most Sail and TENEX needs:
ARRYIN Read in an array (36-bit words)
ARRYOUT Write an array
CFILE Release a file
CPRINT Write a string
INPUT Read in a string
JFNS Read file name
OPENFILEObtain a file
OUT Write a string
SETINPUTSet parameters for input
OBTAINING ACCESS
The main procedure for obtaining access to files is OPENFILE. In terms of
JSYSes, OPENFILE does a GTJFN and OPENF. Additional routines provide
support to OPENFILE, including SETINPUT, INDEXFILE, and CFILE.
DATA TRANSFER RANDOM I/O ERROR HANDLING DIRECT DSK OPERATIONS ASND, RELD
DATA TRANSFER
The TENEX routines for transferring data are generally the same as the TOPS-
10 routines. One improvement in TENEX Sail is that characters and words can
be mixed in reading or writing to a file, provided the file is on the disk.
Such I/O is called "data mixed I/O".
The following interpretation is given to data mixed I/O. There is one
logical character pointer into the file. When a character is read or
written the routines access the byte designated by the pointer and then
increment the pointer. There is only one pointer for both input and output.
When a word is read or written, the next full word in the file is accessed.
Accessing a word advances the character pointer to the next full word in the
file, where five 7-bit ASCII characters occupy one 36-bit word. If a read
passes the end of the file then the EOF variable (specified by SETINPUT or
OPEN) and the external integer SKIP are set to -1. If a write passes the
end of file then the end of file is advanced.
RANDOM I/O
The routines RCHPTR, SCHPTR, RWDPTR, and SWDPTR give access to the file
pointer. USETI and USETO are equivalent to SWDPTR (chan, (N-1)*'200);.
ERROR HANDLING
When errors occur the runtime routines will sometimes trap the errors
themselves. This practice is held to a minimum since the error itself may
be information that the user is interested in seeing. Usually the routines
(as marked) put the TENEX error code in SKIP, which may be examined by the
program. The TENEX error numbers do not always make good sense, but for the
cases that they do the ERSTR routine will print out on the terminal the
message associated with a given error number.
DIRECT DSK OPERATIONS
The routines DSKIN and DSKOUT do direct DSK operations in TENEX Sail, using
the DSKOP JSYS. These routines relate only to the IMSSS version of TENEX-
Sail.
******************************* ASND, RELD *****************************;
SUCCESS _ ASND (DEVICEDESCRIPTOR);
SUCCESS _ RELD (DEVICEDESCRIPTOR)
DEVICEDESCRIPTOR (in the TENEX sense) is assigned to or deassigned from the
job. If DEVICEDESCRIPTOR is -1 when calling RELD then all devices assigned
to the job are deassigned. TENEX error codes are returned in SKIP, which
is zero if no errors occurred.
BKJFN CFILE CHARIN CHAROUT CHFDB CLOSF
********************************* BKJFN ********************************;
BKJFN (CHAN)
Does the BKJFN JSYS on CHAN. TENEX error codes are returned in SKIP, which
is zero if no errors occurred. This function is escape from Sail.
********************************* CFILE ********************************;
SUCCESS _ CFILE (CHAN)
This routine closes the file (CLOSF) and releases the CHAN (RLJFN). This is
the ordinary way to dispense with a file. CFILE returns TRUE if CHAN is
legal and released; it returns FALSE otherwise.
********************************* CHARIN *******************************;
CHAR _ CHARIN (CHAN)
The next character from CHAN is returned. Zero is returned if the file is at
the end.
******************************** CHAROUT *******************************;
CHAROUT (CHAN, CHAR)
The single character CHAR is written to CHAN.
********************************* CHFDB ********************************;
CHFDB (CHAN, DISPLACEMENT,
MASK, CHANGEDBITS)
This routine performs the CHFDB JSYS on CHAN, with DISPLACEMENT, MASK, and
CHANGEDBITS as described in the JSYS manual.
********************************* CLOSF ********************************;
CLOSF (CHAN)
CVJFN DELF DELNF DEVST, STDEV
This routine does a CLOSF on CHAN. CHAN is not released. If the device is
a magtape open for output then 2 file marks are written and a backspace is
performed. This writes a standard end-of-file on the tape.
********************************* CVJFN ********************************;
REALJFN _ CVJFN (CHAN)
The full TENEX JFN (including flags in the left half) corresponding to Sail
channel CHAN is returned. Only a hacker will ever need this.
********************************** DELF ********************************;
DELF (CHAN)
The file on CHAN (which must NOT be open) is deleted. TENEX error codes are
returned in SKIP, which is zero if no errors occurred.
********************************* DELNF ********************************;
DELETED _ DELNF (CHAN, KEPT)
This routine deletes all but KEPT versions of the file on CHAN, which must
have had a CLOSF done on it first. If KEPT=0 then all versions of the file
are deleted. If KEPT=1 then all versions except the most recent are
deleted. The number of files actually deleted is returned as the value of
DELNF.
****************************** DEVST, STDEV ****************************;
"DEVICENAME" _ DEVST (DEVICEDESIGNATOR);
DEVICEDESIGNATOR _ STDEV ("DEVICENAME")
These routines convert between string DEVICENAMEs (such as "DTA0") and
TENEX DEVICEDESIGNATORs. TENEX does not believe that lower case letters
are equivalent to upper case letters in STDEV. TENEX error codes are
returned in SKIP, which is zero if no errors occurred.
DEVTYPE DSKIN, DSKOUT DVCHR ERSTR GDSTS, SDSTS
******************************** DEVTYPE *******************************;
DEVICETYPE _ DEVTYPE (CHAN)
The DVCHR JSYS is used to return the device type of the device open on CHAN.
***************************** DSKIN, DSKOUT ****************************;
DSKIN (MODULE, RECNO, COUNT, @LOC);
DSKOUT (MODULE, RECNO, COUNT, @LOC)
[IMSSS only.] These routines do direct DSK I/O. MODULEs 4-7 are legal for
everyone; other modules require enabled status. The routines transfer COUNT
('1000) words, starting at location LOC in memory and at record RECNO in
MODULE. TENEX error codes are returned in SKIP, which is zero if no
errors occurred. WARNING: No bounds checking is performed to see if the
LOC is a legal Sail array.
********************************* DVCHR ********************************;
DEVICECHAR _ DVCHR (CHAN, @AC1, @AC3)
The DEVCHR JSYS is performed. The flags from AC2 are returned as the value
of the call, and AC1 and AC3 get the contents of ac's 1 and 3.
********************************* ERSTR ********************************;
ERSTR (ERRNO, FORK)
Using the ERSTR JSYS, this routine types on the console the TENEX error
string associated with ERRNO for fork FORK ('400000 for the current fork).
Parameters (in the sense of the ERSTR JSYS) are expanded. Types
ERSTR: UNDEFINED ERROR NUMBER (and sets SKIP to -1) if something is wrong
with ERRNO or FORK.
****************************** GDSTS, SDSTS ****************************;
STATUS _ GDSTS (CHAN, @WORDCOUNT);
SDSTS (CHAN, NEWSTATUS)
The status of the device on CHAN is returned or changed. For GDSTS,
@WORDCOUNT is set to the contents of AC3.
GNJFN GTFDB GTJFN
Remark: some magtape statuses (such as EOF) are set by MTOPR and not by
SDSTS. Ordinarily the Sail runtime system takes care of this, but it is
worth mentioning since so many users have run into this poorly documented
fact about TENEX.
********************************* GNJFN ********************************;
MOREFILES _ GNJFN (CHAN)
Does the GNJFN JSYS. A file that is open cannot have GNJFN applied to it.
INDEXFILE should normally be used instead of GNJFN. An exception is if
files are being indexed without actually being opened (i.e., without an
OPENF JSYS), which is a sensible way of performing operations such as
counting the number of files in a group.
********************************* GTFDB ********************************;
GTFDB (CHAN, @BUF)
The entire FDB of CHAN is read into the array BUF. No bounds checking is
performed, so BUF should be at least '25 words.
********************************* GTJFN ********************************;
CHAN _ GTJFN ("NAME", FLAGS)
Does a GTJFN. If NAME is non-null then it is used, otherwise the terminal
is queried for a filename. Any error code is returned in SKIP. The Sail
channel number obtained is returned as the value of GTJFN.
The following values for FLAGS will be translated by Sail before doing the
JSYS:
value translated to
0 '100001000000 (ordinary input)
1 '600001000000 (ordinary output)
Other values are taken literally.
Ordinarily OPENFILE will be used rather than GTJFN. The routines GTJFN,
OPENF, GNJFN, CLOSF, RLJFN, and DVCHR are all in the category of being
included only for completeness; they are not necessary in most programs.
GTJFNL GTSTS, STSTS INDEXFILE
********************************* GTJFNL *******************************;
CHAN _ GTJFNL ("ORIGSTR", FLAGS, JFNJFN,
"DEV", "DIR", "NAM", "EXT",
"PROT", "ACCOUNT", DESIREDJFN)
Does the long form of the GTJFN JSYS (and does not do an OPENF). The
arguments are put into the accumulators and locations in the table accepted
by the long form of the GTJFN JSYS. These arguments are given below, where
"AC X" means an accumulator and "E+X" means in the Xth address of the table.
Argument Where placed What
"ORIGSTR"AC 2 Partial or complete string
FLAGS E+0 Flags to GTJFN
JFNJFN E+1 xwd input JFN, output JFN
"DEV" E+2 device
"DIR" E+3 directory
"NAME" E+4 name
"EXT" E+5 extension
"PROT" E+6 protection
"ACCOUNT"E+7 account
DESIREDJFN E+'10 desired JFN if B11 on
****************************** GTSTS, STSTS ****************************;
STATUS _ GTSTS (CHAN);
STSTS (CHAN, NEWSTATUS)
These routines examine and change the file status using the JSYSes. TENEX
error codes are returned in SKIP, which is zero if no errors occurred.
WARNING: The results of GTSTS are not necessarily appropriate for
determining end-of-file if the file is being page-mapped by Sail. Look at
the EOF variable instead. See SETINPUT.
******************************* INDEXFILE ******************************;
ANOTHER _ INDEXFILE (CHAN)
If CHAN was opened with the "*" option by OPENFILE then INDEXFILE will try
to get the next file in the "*" group. INDEXFILE returns TRUE as long as
another file can be found on CHAN. Example:
JFNS JFNSL MTOPR
JFN _ OPENFILE ("<JONES>*.SAI;*", "RO*");
COMMENT Read all of Jones's Sail programs;
SETINPUT (JFN, 200, 0, EOF);
DO BEGIN "INDEX"
DO BEGIN "READ FILE"
STRING S;
S _ INPUT (JFN, BREAKTABLE);
COMMENT process ...;
END "READ FILE" UNTIL EOF;
END "INDEX" UNTIL NOT INDEXFILE (JFN);
The "*" option takes the place of reading the MFD and UFD on a TOPS-10
system. INDEXFILE clears the EOF, LINNUM, SOSNUM, and PAGNUM variables
associated with CHAN if these have been set by SETINPUT and SETPL.
********************************** JFNS ********************************;
"NAME" _ JFNS (CHAN, FLAGS)
The name of the file associated with CHAN is returned. FLAGS are for
accumulator 3 as described in the JSYS manual. Zero is a reasonable value
for FLAGS.
********************************* JFNSL ********************************;
"NAME" _ JFNSL (CHAN, FLAGS, LHFLAGS)
(This routine corrects a deficiency in the JFNS function.) The name of the
file associated with CHAN is returned, using FLAGS for accumulator 3 and
putting LHFLAGS into the left half of accumulator 2 as described in the JSYS
manual. If LHFLAGS is -1 then the value returned by GTJFN is used.
********************************* MTOPR ********************************;
MTOPR (CHAN, FUNCTION, VALUE)
The MTOPR JSYS is executed with FUNCTION placed into AC2 and VALUE into AC3.
The TOPS-10 style MTAPE function may be more comfortable. [(Stupid!) IMSSS
and SUMEX: skip to end of tape does not work.]
OPENF OPENFILE
********************************* OPENF ********************************;
OPENF (CHAN, FLAGS)
Does the OPENF JSYS on CHAN with FLAGS as the contents of accumulator 2.
TENEX error codes are returned in SKIP, which is zero if no errors
occurred. The following values for FLAGS will be translated by Sail before
setting AC2:
value translated to
0 '070000200000 (input characters)
1 '070000100000 (output characters)
2 '440000200000 (input words)
3 '440000100000 (output words)
4 '447400200000 (dump read)
5 '447400100000 (dump write)
Values 6-10 are reserved for expansion; other values are taken literally.
Best results are obtained by opening a TTY in 7-bit mode, the DSK or DTA in
36-bit mode, and a magtape in 36-bit dump mode.
******************************** OPENFILE ******************************;
CHAN _ OPENFILE ("NAME", "OPTIONS")
NAME is the name of the file to be opened. If it is null then OPENFILE gets
the filename from the terminal using TENEX filename recognition. CHAN, the
value returned by OPENFILE, is a Sail channel number. This is not
necessarily the same as the TENEX JFN (see CVJFN). All TENEX Sail functions
(except SETCHAN) require Sail channel numbers for arguments. OPTIONS is one
or more characters specifying the kind of access desired. The legal
characters are
Read or write:
R read
W write
A append
Version numbering, old-new:
O old file
N new file
T temporary file
* index with INDEXFILE routine
Independent bits to be set:
C require confirmation
D ignore deleted bit
H "thawed" access
RCHPTR, SCHPTR
Error handling:
E return errors to user in the external
integer SKIP. TENEX error codes are used.
(CHAN will be released in this case.)
If an error occurs and mode "E" was not specified then OPENFILE gives an
error message and attempts to obtain a file name from the terminal. If an
error occurs when "E" was specified then OPENFILE will return -1 for CHAN
and the TENEX error code will be put into SKIP.
Examples:
COMMENT get a filename from the terminal
and write the file;
BEGIN
INTEGER JFN;
OUTSTR (CRLF & "FILE NAME* ");
JFN _ OPENFILE (NULL, "WC");
COMMENT write, confirm name;
CPRINT (JFN, "text
");
CFILE (JFN); COMMENT close the file;
END;
COMMENT read a known file;
BEGIN
STRING S;
INTEGER JFN, BRCHAR, EOF;
SETBREAK (1, '12, '15&'14, "IN");
JFN _ OPENFILE ("<JONES>SECRET.DATA", "RCO");
SETINPUT (JFN, 200, BRCHAR, EOF);
DO BEGIN
S _ INPUT (JFN, 1);
END UNTIL EOF;
CFILE (JFN);
END;
Wizards: The OPENF is for 36-bit transfers; except that TTY, LPT, and a
device for which a 36-bit OPENF fails get 7-bit mode.
***************************** RCHPTR, SCHPTR ***************************;
PTR _ RCHPTR (CHAN);
SCHPTR (CHAN, NEWPTR)
The number of the byte which will be accessed next by character I/O is
returned or set. The first character of a file is character number 0. If
NEWPTR=-1 for SCHPTR then the pointer is set to end of file. Setting the
RFBSZ RFPTR, SFPTR RLJFN RNAMF
pointer beyond end of file will change the length of the file if it is being
written. TENEX error codes are returned in SKIP, which is zero if no
errors occurred.
********************************* RFBSZ ********************************;
BYTESIZE _ RFBSZ (CHAN)
The bytesize of the file open on CHAN is returned. This function is escape
from Sail.
****************************** RFPTR, SFPTR ****************************;
PTR _ RFPTR (CHAN);
SFPTR (CHAN, NEWPTR)
These routines perform JSYSes and are escape from Sail. TENEX error codes
are returned in SKIP, which is zero if no errors occurred.
********************************* RLJFN ********************************;
RLJFN (CHAN)
This routine does the RLJFN JSYS.
********************************* RNAMF ********************************;
SUCCESS _ RNAMF (EXISTINGCHAN, NEWCHAN)
The RNAMF JSYS is performed, renaming the file on EXISTINGCHAN to the name
of the (vestigial) file on NEWCHAN. It is necessary that
CLOSF(EXISTINGCHAN) be done before RNAMF and that OPENF be done afterwards.
The TOPS-10 style RENAME is sometimes more convenient to use than RNAMF,
since RENAME performs the GTJFN and OPENFs necessary for the renaming
operation. However, the actual JFN associated with CHAN is changed by
RENAME.
RWDPTR, SWDPTR SETCHAN SETINPUT SINI
***************************** RWDPTR, SWDPTR ***************************;
PTR _ RWDPTR (CHAN);
SWDPTR (CHAN, NEWPTR)
The number of the word which will be accessed next by word I/O is returned
or set. The first word of a file is word number 0. If NEWPTR=-1 for SWDPTR
then the pointer is set to end of file. Setting the pointer beyond end of
file will change the length of the file if it is being written.
******************************** SETCHAN *******************************;
CHAN _ SETCHAN (REALJFN,
GTJFNFLAGS, OPENFFLAGS)
This function is liberation from Sail I/O. It is provided for doing Sail
I/O on a JFN that is obtained from some means other than the Sail file-
opening routines -- for example, a JFN passed from a superior fork.
REALJFN is a 36-bit JFN (or JFN substitute, such as a Teletype number),
GTJFNFLAGS and OPENFFLAGS are the flags that should be recorded describing
how the GTJFN and OPENF were accomplished. REALJFN need not be open. The
value returned by SETCHAN is the Sail channel number which should be used
for subsequent Sail I/O. SETCHAN is the only function in TENEX Sail that
takes an actual JFN as an argument.
******************************** SETINPUT ******************************;
SETINPUT (CHAN, @COUNT, @BRCHAR, @EOF)
This function relates the COUNT, BRCHAR, and EOF variables to channel CHAN
in the same way that OPEN does. The INPUT function (page 72) uses 200 for
the default value of COUNT if no location has been associated with CHAN.
All I/O transfer routines also set SKIP to indicate end-of-file and I/O
errors. For example, on return from INPUT SKIP will be -1 if an end-of-
file occurred, a TENEX error number if an error occurred, and zero
otherwise.
********************************** SINI ********************************;
"STRING" _ SINI (CHAN, MAXLENGTH, BRCHAR)
A string of characters terminated by BRCHAR or by reaching MAXLENGTH
SIZEF UNDELETE Terminal Handling THE TERMINAL AS A DEVICE
characters, whichever happens first, is read from CHAN. SINI sets SKIP to
-1 if the string was terminated for count; otherwise SKIP will be set to
BRCHAR. To determine end-of-file, examine the EOF variable for the channel
(see SETINPUT).
********************************* SIZEF ********************************;
SIZE _ SIZEF (CHAN)
The size in pages of the file open on CHAN is returned. TENEX error codes
are returned in SKIP, which is zero if no errors occurred.
******************************** UNDELETE ******************************;
UNDELETE (CHAN)
The file open on CHAN is undeleted. TENEX error codes are returned in
SKIP, which is zero if no errors occurred.
11.4 Terminal Handling
The simplest way to write strings on the terminal is with PRINT. See page
98. The simplest way to read strings from the terminal is with INTTY. See
page 145. The following detailed discussion about terminal handling will
normally be of interest only to advanced programmers. The rest of this
section is new.
THE TERMINAL AS A DEVICE
We first discuss some of the problems in using the terminal as a device
(i.e., when device "TTY:" is opened by OPENFILE or a similar function).
Since Sail has various functions for reading strings, reals, and integers
from an arbitrary device, this can be a useful feature.
TENEX provides quite general teletype service. However, the lack of a
default system line editor creates some problems. Note the proliferation of
line editors in the many commonly used TENEX programs. Some of them, such
as the INTERLISP editor, are carefully and cleanly written. Most TENEX
utility programs, however, work quite poorly and inconsistently with regard
to the controlling terminal.
The TOPS-10 system has a simple line editor. On a standard Teletype device,
the standard TOPS-10 editor activates on a carriage return, altmode,
control-G, or control-Z. ASCII DEL ('177) deletes the previous character;
control-U deletes the current line; control-R retypes the current line; and
control-Z signifies end-of-file when the terminal is INITted as a device.
(The SUAI display line editor also has character insertion, deletion,
searching, kill-to-character, and settable activation characters.) The great
virtue of this is that programs can be written in a device-independent
manner. When the terminal is accessed as a device the system handles line
editing.
Many TOPS-10 programs take advantage of this device-independence, using the
INPUT, REALIN and INTIN functions to access the system line editor. TENEX
has had no system line editor; while IMSSS and SUMEX have had a line editor
in their TENEX for some time, it is not in general use.
Therefore, the features of a "system" line editor have been put into the
TENEX Sail runtime system. Several schemes have been implemented in TENEX
Sail as of this writing. When a channel is opened to the controlling
terminal, three kinds of line editing are available: 1) a TOPS-10 style
line editor, 2) a TENEX-style line editor, and 3) no line editor at all.
The TOPS-10 style editor is the default with a channel opened via OPEN; the
TENEX-style editor is the default when a TENEX function (such as OPENFILE or
GTJFN) is used to obtain the channel. The function SETEDIT can be used to
change which convention is used. More detailed description of these three
kinds of editing follows.
TOPS-10 Style Editor. The OPEN function to the controlling terminal,
usually "TTY" in the second argument, gets the following editing conventions
for functions INPUT, INTIN and REALIN:
'25 (control-U) deletes the entire line and echoes control-G (BEL) CR LF
to the terminal.
'32 (control-Z) means end-of-file, after all previous input is read in.
'33 (ESC, altmode) activates and is sent to the program as '33. This is
consistent with current TOPS-10 practice. Over the years there have
been several altmodes: '33, '175, and '176. On terminals that TENEX
believes to be a model 33 teletype, the characters '175 and '176 are
transliterated to '33 by TENEX before the Sail runtime system sees
them.
'37 (US, TENEX EOL), which is found in the input buffer when CR is typed
at the terminal, is transliterated to a '15 '12 (CRLF) sequence.
'177 (DEL, rubout) deletes the last character; consecutive deleted
characters are echoed, surrounded by backslashes "\". (At IMSSS and
SUMEX the deleted characters are removed from the screen with the
DELCH JSYS, which is not supported by BBN.)
The editor activates on line feed, altmode, control-G, and control-Z.
All this means that programs written for the TOPS-10 system, accessing the
controlling terminal with INPUT et al, should work with regard to teletype
input. The above is also a description of the operation of INCHWL, except
that control-Z is simply a break character to INCHWL.
TENEX-Style Editor. The OPENFILE, GTJFN, and GTJFNL functions to the
controlling terminal set the TENEX Sail line editor to the following
conventions:
IMSSS and SUMEX. These sites use the PSTIN JSYS for line editing in TENEX,
with the following conventions:
'12 (linefeed) allows input to continue on the next line.
'22 (control-R) retypes the current line.
'27 (control-W) deletes a "word" (up to the next space). This prints as
"___" on the terminal.
'30 (control-X) deletes the entire line.
'32 (control-Z) signifies end of file.
'37 (TENEX EOL) is transliterated to a '15 '12 sequence.
'177 (rubout) or '1 (control-A) deletes the last character, using the
DELCH JSYS to remove it from the display (if any).
The PSTIN JSYS transliterates '175 and '176 to '33.
The editor activates on the characters defined by the PSTIN JSYS (q.v.);
these include linefeed ('12 after EOL), escape ('33), control-G,
control-Z.
Sites other than IMSSS and SUMEX have the following editing conventions when
the channel is opened with the TENEX routines OPENFILE, GTJFN, etc.:
'22 (control-R) retypes the current contents of the buffer.
'30 (control-X) deletes the entire line and echoes CR LF to the terminal.
'32 (control-Z) signifies end-of-file.
'37 (TENEX EOL) is transliterated to a '15 '12 sequence.
'177 (rubout) or '1 (control-A) deletes the last character. Consecutive
deleted characters are echoed surrounded by backslashes.
The editor activates on line feed ('12), escape ('33), control-G (7) and
control-Z ('32).
SETEDIT TERMINAL MODE FUNCTIONS GTTYP, STTYP
This is also the action of the INTTY routine, except that control-Z is
simply a break character to INTTY.
The third mode is the BBN standard mode. In this mode all characters are
simply passed through. In particular, control-Z does not signify end of
file, typing a rubout gives a '177, ESC gives a '33, CR gives a '37, etc.
No editing is done by the system. This is the mode in which a terminal
other than the controlling terminal is accessed using any of the functions.
******************************** SETEDIT *******************************;
"OLDMODE" _ SETEDIT (CHAN, "NEWMODE")
If CHAN is not the controlling terminal then SETEDIT is a no-op. Otherwise,
it sets the line editing mode to NEWMODE" and returns OLDMODE, both
according to the following code:
MODE Meaning
"D" TOPS-10 mode, as above
"T" TENEX mode, as above
"B" (BBN bag)Byte(ing) mode, no editing
Notes:
(1) MODE SETTINGS. SETEDIT does not change or access the parameters set by
such functions as SFMOD, SFCOC, STPAR, TTYUP, etc. Changes made with
these latter functions will affect editing.
(2) NON-CONTROLLiNG TERMINALS. Terminals other than the controlling
terminal will have byte mode -- no editing.
(3) INCHWL no longer transliterates '33 to '175. Previous versions of
TENEX Sail transliterated '33 to '175.
TERMINAL MODE FUNCTIONS
The routines in this section really refer to terminals only in the "mini-
system" version of TENEX. The argument CHAN may be either a Sail channel
number associated with a terminal, or a terminal specifier (such as '100 or
'101 for the controlling terminal).
****************************** GTTYP, STTYP ****************************;
TERMINALTYPE _ GTTYP (CHAN, @BUFFERS);
STTYP (CHAN, AC2)
RFCOC, SFCOC RFMOD, SFMOD STPAR STI DATA TRANSFER
The indicated JSYS is performed. In GTTYP the additional values returned
from accumulator 2 are stored into reference parameter BUFFERS.
****************************** RFCOC, SFCOC ****************************;
RFCOC (CHAN, @AC2, @AC3);
SFCOC (CHAN, AC2, AC3)
The indicated JSYS is performed.
****************************** RFMOD, SFMOD ****************************;
MODEWORD _ RFMOD (CHAN);
SFMOD (CHAN, AC2)
A file's mode word is queried or altered using the JSYS. WARNING: some
features, such as upper case conversion, that are advertised by BBN as being
accomplished with the SFMOD JSYS are actually accomplished with the STPAR
JSYS.
********************************* STPAR ********************************;
STPAR (CHAN, AC2)
Does the STPAR JSYS, setting to AC2.
********************************** STI *********************************;
STI (CHAN, CHAR)
Does the STI jsys (Simulate Terminal Input) to channel CHAN (usually the
controlling terminal), inserting byte CHAR into the input stream.
DATA TRANSFER
The usual Sail routines for teletype I/O (see page 80) are available. In
addition, PBIN, PBOUT, and PSOUT have been added, although they execute
exactly the same code as INCHRW, OUTCHR, and OUTSTR respectively.
INTTY PBTIN SUPPRESSING OUTPUT
********************************* INTTY ********************************;
"STRING" _ INTTY
INTTY does a TENEX-style input. (Note that INCHWL does a TOPS-10 style
input.) Up to 200 characters are transfered. The activation character is
not appended to the string, but is put into SKIP. The value -1 is placed
into SKIP if the input is terminated for exceeding the 200 character
limit.
The normal activation characters are EOL, ESC, control-Z, and control-G;
however, see the section regarding line editing in TENEX Sail. At IMSSS and
SUMEX this routine uses the PSTIN JSYS with the standard system break
characters; no timing is available.
********************************* PBTIN ********************************;
CHAR _ PBTIN (SECONDS)
[IMSSS only.] Executes the PBTIN JSYS with timing of SECONDS.
SUPPRESSING OUTPUT
This new section is for advanced Sail users only, and supposes a knowledge
of the pseudo-interrupt system; see the JSYS manual and the interrupt
section of this manual.
The TOPS-10 system allows the user to type a control-O and suspend program
output to the terminal until either another control-O is typed or program
input is requested. (See [MonCom] for a complete description.) TENEX does
not have this at the system level, but pseudo-interrupts provide an
alternative with which the program can receive control and abort processing
as well as flush output.
TENEX Sail has complete access to the TENEX pseudo-interrupt system. In
order to facilitate handling of control-O an EXTERNAL INTEGER CTLOSW has
been added to the TENEX Sail runtime system. If CTLOSW is TRUE then any
output to the controlling terminal (device "TTY") is flushed by the
following functions:
PBOUT
PSOUT
OUT to a channel open to "TTY", or to '101
OUTCHR
OUTSTR
CTLOSW is likewise made FALSE when input is requested by any of the
following:
INCHRS INPUT INTIN TTYIN
INCHRW INSTR INTTY TTYINS
INCHSL INSTRL PBTIN TTYINL
INCHWL INSTRS REALIN TTYUP
Note: functions SINI, CHARIN and CHAROUT are not affected. CTLOSW may be
accessed by declaring it as an EXTERNAL INTEGER.
Here is an example of a control-O handler.
ENTRY; BEGIN
REQUIRE "<><>" DELIMITERS;
DEFINE !=<COMMENT>;
! This program sets up a control-O interrupt
using PSI channel 0, level 0.
;
EXTERNAL INTEGER CTLOSW,PS1ACS;
SIMPLE PROCEDURE CTLO; BEGIN
INTEGER USERPC,PSL1,USERINST,AC1,SAVEADDR;
LABEL LEAVE;
DEFINE PSOUTJSYS=<'104000000076>,
SOUTJSYS=<'104000000053>;
SIMPLE INTEGER PROCEDURE DEV (INTEGER JFN);
STARTCODE
HRRZ 2,JFN; ! THE JFN;
SETZ 4,;
HRROI 1,4; ! PUT STRING IN 4;
MOVSI 3,'200000; ! ONLY THE DEVICE;
JFNS; ! GET THE STRING;
MOVEM 4,1; ! CVASC("DEV");
END;
! this is Sail immediate interrupt level.
No dynamic strings are accessed.;
IF CTLOSW THEN
BEGIN
CTLOSW _ FALSE; ! TOGGLE IT;
RETURN; ! AND RETURN;
END;
STARTCODE
MOVEI 1,'101;
CFOBF;
END;
OUTSTR("^O
");
CTLOSW _ TRUE; ! NO MORE OUTPUT;
! get user PC and address into LEVTAB;
STARTCODE
MOVEI 1,'400000;
RIR;
HLRZ 2,2; ! LEVTAB ADDRESS;
MOVE 2,(2); ! PC FOR LEVEL 1;
MOVEM 2,PSL1;
MOVE 2,(2); ! USER PC;
MOVEM 2,USERPC;
END;
! return if user mode;
IF (USERPC LAND '010000000000) THEN RETURN;
! in monitor. Return if not in the middle
of a PSOUT or (SOUT to '101);
IF NOT (
(USERINST _ MEMORY[USERPC-1])=PSOUTJSYS
OR (USERINST=SOUTJSYS AND
((AC1 _ MEMORY[LOCATION(PS1ACS) + 1])
= '101 OR DEV(AC1)=CVASC("TTY"))))
THEN RETURN;
! modify return so that output stops;
SAVEADDR _ (MEMORY[PSL1] LAND '777777000000)
+ LOCATION(LEAVE);
MEMORY[PSL1] SWAP SAVEADDR;
RETURN; ! to Sail interrupt handler;
STARTCODE LEAVE: JRST @SAVEADDR; END;
END;
INTERNAL PROCEDURE INITIALIZE;
BEGIN
PSIMAP(0,CTLO,0,1);
ENABLE(0);
ATI(0,"O"-'100);
END;
REQUIRE INITIALIZE INITIALIZATION;
END;
Utility TENEX System Calls CALL CNDIR DIRST, STDIR
11.5 Utility TENEX System Calls
An effort has been made to provide calls that read and write strings which
may be inconvenient to perform from STARTCODE. Note that the TENEX Sail
compiler has the TENEX JSYS mnemonics defined in STARTCODE. In STARTCODE
these definitions take precedence over the function calls of the same name.
********************************** CALL ********************************;
RESULT _ CALL (ACARG, "FUNCTION")
A limited set of CALLs is simulated by TENEX Sail. Those available are
EXIT
DATE
DATSAV [IMSSS only.]
GETINF [IMSSS only.]
GETPPN
LOGOUT
MSTIME
PJOB
PUTINF [IMSSS only.]
RANDOM [IMSSS only.]
RUN
RUNTIM
TIMER
If any other FUNCTION is specified then a continuable error message is
given.
********************************* CNDIR ********************************;
CNDIR (DIRNO, "PASSWORD")
Does the CNDIR jsys, connecting to DIRNO with password "PASSWORD". If
"PASSWORD" is null then the user must have connect privileges. TENEX error
codes are returned in SKIP, which is zero if no errors occurred.
****************************** DIRST, STDIR ****************************;
"DIRECTORY" _ DIRST (DIRNO);
DIRNO _ STDIR ("DIRECTORY", DORECOGNITION)
These routines convert between TENEX directory numbers and strings. TENEX
GJINF GTAD IDTIM, ODTIM PMAP
error codes are returned in SKIP, which is zero if no errors occurred.
For STDIR the error codes in SKIP are
1 string does not match
2 string is ambiguous.
Note that DIRECTORY must be in uppercase for the STDIR JSYS.
********************************* GJINF ********************************;
JOBNO _ GJINF (@LOGDIR, @CONDIR, @TTYNO)
The job number is returned as the value of the call. Reference values are:
the number of the logged directory (LOGDIR), the connected directory
(CONDIR), and the TENEX Teletype number (TTYNO).
********************************** GTAD ********************************;
DT _ GTAD
The current date and time (in TENEX representation) is returned.
****************************** IDTIM, ODTIM ****************************;
DT _ IDTIM ("DATIME");
"DATIME" _ ODTIM (DT, FORMAT)
These routines convert between TENEX internal representation DT and string
representation DATIME. If DT is -1 in ODTIM then the current date and time
is used. If FORMAT is -1 then the format used is "TUESDAY, APRIL 16, 1974
16:33:32". For IDTIM, TENEX error codes are returned in SKIP, which is
zero if no errors occurred. WARNING: the IDTIM JSYS is nearly an inverse
to the ODTIM JSYS; however, the format returned by ODTIM with FORMAT=-1 will
NOT be recognized by IDTIM unless the day ("TUESDAY, ") is first removed.
Blame BBN.
********************************** PMAP ********************************;
PMAP (AC1, AC2, AC3)
Does the PMAP JSYS, using the accumulators for the arguments.
RDSEG RUNPRG RUNTIM
********************************* RDSEG ********************************;
RDSEG (@SEGPAGES, @BUFPAGES)
This function returns the pages which are specially used by the Sail runtime
system. The starting and ending pages of the runtime segment are returned
in the left and right halves, respectively, of SEGPAGES. The first and last
pages used for bufferring are returned in the left and right halves of
BUFPAGES. This function is escape from Sail.
Memory map, in general:
pages contents
(Compile time)
0-n impure data
400-450 compiler code
600-604 STARTCODE table, if needed
640-670 runtime system
770-m UDDT
(Run time)
0-n impure data
400-m code and pure data
600-637 I/O buffers
640-677 runtime system
770-p UDDT
********************************* RUNPRG *******************************;
RUNPRG ("PROGRAM", INCREMENT, NEWFORK)
This does two entirely different things depending on the value of NEWFORK.
If NEWFORK is true then a new fork is created, capabilities are transmitted,
and PROGRAM is run in the new fork (with the current fork suspended by a
WFORK). INCREMENT is added to the entry vector location. If NEWFORK is
false then the current fork is replaced with PROGRAM. In this case RUNPRG
is like the TOPS-10 RUN UUO; if the INCREMENT is 1 then the program is
started at the CCL address. If RUNPRG returns at all then there was a
problem with the file. Remember to say .SAV as the PROGRAM extension.
********************************* RUNTIM *******************************;
RUNNING _ RUNTM (FORK, @CONSOLE)
The running time in milliseconds for FORK is returned and the console
connect time is returned in CONSOLE.
LEAP DATA TYPES Introduction
SECTION 12
LEAP DATA TYPES
12.1 Introduction
In addition to the standard algol-like statements and expressions, Sail
contains an associative data store and auxiliary facilities called LEAP.
Sail's version of LEAP is based on the associative components of the LEAP
language implemented by J. Feldman and P. Rovner as described in [Feldman].
An associative store allows the retrieval of data based on the partial
specification of that data. LEAP stores associative data in the form of
ASSOCIATIONS, which are ordered three-tuples of ITEMS. Associations are
frequently called TRIPLES. Associations are placed in the associative store
by MAKE statements and removed from the store by ERASE statements. The
associative searches allow us to specify items and their position in the
triple and then have the LEAP interpreter search for triples in the
associative store which have the same items in the same positions. The
interpreter will extract the items from such triples, which correspond to
the positions left unspecified in the original search request. For example
say we had triples representing the binary relation Fatherof, and we had
"made" associations of the form
Fatherof John Tom
Fatherof Tom Harry,
Fatherof Jerry Tom,
where Fatherof, John, Tom, Harry, and Jerry are names of items. We could
then perform searches to find the sons of Tom by specifying to the leap
search routines that we wanted to find triples whose first component was
Fatherof and whose third component was Tom. Associative searches
inherently produce multiple values (i.e., both Jerry and John are sons of
Tom). To deal with multiple values, Leap has SETs and LISTs of items.
Items are constants. They may be created by declaration or by the function
NEW. Items may have a single algebraic variable, set, list or array
associated with them which is accessible by use of the DATUM construct.
Declared items have names which may be used to identify them in
expressions, etc. The simple variable whose value is an item is called an
ITEMVAR.
Syntax
12.2 Syntax
The following syntax is meant to REPLACE not supplement the syntax of
algebraic declarations, except where noted.
<declaration>
::= <typedeclaration>
::= <arraydeclaration>
::= <preloadspecification>
::= <labeldeclaration>
::= <proceduredeclaration>
::= <synonymdeclaration>
::= <requirespecification>
::= <contextdeclaration>
::= <recordclassdeclaration>
::= <protectacs declaration>
::= <cleanupdeclaration>
::= <typequalifier> <declaration>
::= <sproutdefaultdeclaration>
<simpletype>
::= BOOLEAN
::= INTEGER
::= LIST
::= REAL
::= RECORDPOINTER ( <classidlist> )
::= SET
::= STRING
<itemvartype>
::= ITEMVAR
::= <simpletype> ITEMVAR
::= <arraytype> ARRAY ITEMVAR
::= CHECKED <itemvartype>
::= GLOBAL <itemvartype>
<itemtype>
::= ITEM
::= <simpletype> ITEM
<arraytype>
::= <simpletype>
::= <itemvartype>
::= <itemtype>
<typedeclaration>
::= <simpletype> <identifierlist>
::= <itemvartype> <identifierlist>
::= <itemtype> <identifierlist>
::= <arraytype> ARRAY <arraylist>
::= <arraytype> ARRAY ITEM <arraylist>
::= <typequalifier> <typedeclaration>
<arraylist> -- as on page 9
<proceduredeclaration>
::= PROCEDURE <identifier> <procedurehead>
<procedurebody>
::= <proceduretype> PROCEDURE <identifier>
<procedurehead> <procedurebody>
::= <typequalifier> <proceduredeclaration>
<proceduretype>
::= <simpletype>
::= <itemvartype>
::= MATCHING <proceduretype>
::= MESSAGE <proceduretype>
<procedurehead> and <procedurebody> -- as on page 10 except:
<simpleformaltype>
::= <simpletype>
::= <itemvartype>
::= ? <itemvartype>
::= <simpletype> ARRAY
::= <itemvartype> ARRAY
::= <simpletype> PROCEDURE
::= <itemvartype> PROCEDURE
<preloadspecification>, <synonymdeclaration>, <labeldeclaration>,
and <requirespecification> as on page 9
<contextdeclaration> as on page 182
Semantics ITEM GENESIS SCOPE OF ITEMS DATUMS OF ITEMS
12.3 Semantics
ITEM GENESIS
Although items are constants, they must be created before they can be used.
Items may be created in three ways:
1) A Declared Item may created by declaration of an identifier to be
of type ITEM.
2) An item may be created with the NEW construct (see page 178).
3) A bracketed triple item is created by the MAKEing of a bracketed
triple (see MAKE, page 163).
Items of type 1 and 2 are the same except those of type 1 may be referred
to by the identifier that is associated with them. For example one may say
... ITEM DAD; ... X_DAD; .... NOTE: DAD is the name of an item, not a
variable! Saying DAD_X is just as illegal as saying 15_X.
Items of type 3 are different from those of type 1 and 2. Discussion of
them will be left until the creation of associations with the MAKE statement
is discussed (page 163).
SCOPE OF ITEMS
Items do not obey the traditional Algol scope rules. All declared items are
allocated in the outer block. All other items are allocated dynamically.
All items exist until a DELETE (<item expression>) is done on them (see page
162 for the details of DELETE), or until the outer block is exited at the
end of the program. HOWEVER, the identifiers of declared items (type 1
above) DO obey scope rules. After exiting the block in which item X was
declared, it will be impossible to refer to X by its declared name.
However, X may have been stored in an itemvar, associations, etc. and thus
still be retrieved and used.
Warning: items in recursive procedures behave differently from variables in
recursive procedures. At each recursive call of a procedure, the local
variables are reinstantiated (unless they were declared OWN). Items are
constants. There is never more than one instantiation of an item around at
a time.
DATUMS OF ITEMS
An item of type 1 or 2 may have an associated variable, called its DATUM.
The Datum of an item is like any variable; it may be declared to have any
type that a variable may have, except the type Itemvar. Because an item may
have only one datum from its creation until its death, we frequently will
say the "type of an item" referring to the type of the datum. RESTRICTIONS:
It is currently impossible to make either items or their datums either
Internal or External. However, the effect of External items can be
duplicated by manipulating the order in which items are declared (see page
157). OWN is not applicable as items are constants, not variables. Items
ITEMVARS
of type ARRAY must be declared with constant bounds since they are allocated
upon entering the outer block.
Example declarations of items with datums:
INTEGER ITEM FATHEROF;
STRING ITEM FOO;
INTEGER ARRAY ITEM NAMES [1:4, 1:8]; COMMENT note
the specification of the array's dimensions;
SHORT REAL ITEM POINT;
EXTERNAL ITEM BLAT; COMMENT illegal;
ITEMVAR ITEM BLAT; COMMENT illegal;
STRING ITEMVAR ITEM BLAT; COMMENT illegal;
REAL PROCEDURE ITEM BLAT; COMMENT illegal;
PROCEDURE ITEM BLAT; COMMENT illegal,
use ASSIGN;
The syntax for variable includes the Datum construct. That is, if AGE is a
declared an Integer Item, then DATUM (AGE) behaves exactly like an Integer
variable. If ARR is declared as
STRING ARRAY ITEM ARR [2:4, 1:9+2]
then DATUM (ARR) is a string array with two dimensions of the declared size.
A new array may not be assigned to the Datum of ARR, though of course the
individual elements of the array may be changed. Datums obey the same type
checking and type conversion rules that the algebraic variables of Sail do.
For example, when a string is assigned to an integer datum, the integer
stored in the integer datum is the ASCII of the first character of the
string. ITEMVARS
An Itemvar is a variable whose value is an Item. Just as the statements
"X_3; Y_X" and "Y_3" are equivalent with respect to Y, the statements
"X_DAD; Y_X" and "Y_DAD" are equivalent with respect to Y, if X and Y are
itemvars, DAD an item. The distinction between itemvars and items is
identical to the distinction between integer variables and integers. An
integer variable may only contain an integer and a variable declared ITEMVAR
may only contain an item. This may be confusing since historically, integer
variables have always been called INTEGER rather than INTEGERVAR.
Properly speaking, one should have INTEGERVAR ARRAYs instead of INTEGER
ARRAYs. Originally, Sail only allowed ITEMVAR ARRAYs. However, so many
people found this confusing that now one may say ITEM ARRAY, and it will be
interpreted to mean ITEMVAR ARRAY. Similarly, an Item procedure is exactly
the same as an Itemvar procedure.
An itemvar may contain items of any type. However, when one says
DATUM (ITMVR) where ITMVR is an itemvar, the compiler must know the type of
the datum of the item (i.e. the type of the item) contained in the itemvar
so that the the correct conversions, etc. may be done. Thus, one may
SETS AND LISTS
declare itemvars to have the same types that are legal for items. If one
has declared STRING ITEMVAR ITMVR, then the compiler assumes that you have
stored an string item in ITMVR, and and will treat DATUM (ITMVR) as a string
variable.
An Itemvar may be declared CHECKED if the user desires the type of itemvar
checked against the type of the datum of the item expressions assigned to
it. That is, only a string item could be stored in a Checked String
Itemvar. If the itemvar is not declared Checked, it may have an item of any
type assigned to it and their types need not match at all. This can be very
dangerous. For example, an integer array item might be assigned to a string
itemvar. When the datum of this itemvar is later assigned to an integer
variable, say, Sail will try to treat the array header as a string pointer
and get very confused. The runtime routine TYPEIT, page 218, returns a code
for the type of its argument, and can be useful for avoiding type matching
errors with un-checked itemvars.
GLOBAL itemvars are a special kind for SUAI global model users. Global
model operation allows several jobs to share a data segment, and GLOBAL
itemvars are used to build the data structures in this segment. MESSAGE
procedures are also related to global model operations. These features have
fallen into disuse.
EXTERNAL, OWN and INTERNAL Itemvars are legal. SAFE applies to either the
array of an array itemvar, the array of an itemvar array, or both arrays of
an array itemvar array.
Itemvars obey traditional Algol block structure. Upon exiting the block of
their declaration, their names are unavailable and their storage is
reallocated. However, the item stored in an itemvar is not affected -- it
continues to exist until DELETEd or until the end of the program.
Itemvars are initialized to the special item ANY at the beginning of one's
program.
SETS AND LISTS
Sets and Lists are collections of items. There are two distinctions between
Sets and Lists: a list may contain multiple occurrences of any item while a
set contains at most a single instance of an item. Second, the order in
which items appear within a list is completely within the control of the
user program, while with a set, the order is fixed by the internal
representation of the items. Lists and Sets do not care what type if any
the datums of their members are.
List and Set Arrays, Itemvars, Items, and Procedures are all legal, as well
as External, Own and Internal Sets and Lists. Like itemvars, the scope of
Set and List variables is the block they were declared in. Exiting that
block does not destroy the items stored in the departed sets or lists.
ASSOCIATIONS PROCEDURES IMPLEMENTATION
ASSOCIATIONS
Perhaps the most important form of storage of items is the Association, or
TRIPLE. Triples of items may be written into or retrieved from a special
store, the associative store. The method of storage of these triples is
designed to facilitate fast and flexible retrieval. Sail uses
approximately two words of storage for each triple in the associative store.
There is at most one copy of a triple in the store at any time. Once a
triple has been stored in the associative store, its component items can not
be changed, although an approximation to this can be obtained by erasing
the association then making a new association with the altered components.
You will note there is no syntax for declaring a triple. Triples can only
be created with the MAKE statement. In the examples which follow, a triple
is represented by :
A O V
where A, O, and V represent the items stored in the association. The
associative store is accessed by the FOREACH statement, derived sets, and
binding triples (see Searching the Associative Store, page 164).
PROCEDURES
Itemvar, Item, List, and Set procedures all exist. Itemvar procedures may
be CHECKED if one desires the item RETURNed to have the same type as the
type of the Itemvar procedure. Otherwise, the compiler only checks to see
that the value returned to an itemvar procedure is an item.
Every type except Item may be used in formal parameter declarations; items
are constants yet parameters always have something assigned to them in the
procedure call. Since you can't assign something to a constant, you can't
have item parameters.
WARNING: when using Checked Reference Itemvar formals, no type checking is
performed as the actual is assigned to the formal at the procedure call.
However, type checking will only be done during the procedure, and when the
formal is assigned to the actual upon the (normal) exit of the procedure.
IMPLEMENTATION
Each Item is represented by a unique integer in the compiler. The numbers
are assigned in the order the items are declared, e.g. the first declared
item gets 1, the second gets 2, etc. (Actually, Sail has already declared 8
items that it needs, so user item numbers start with 9. REQUIRE n
ITEMSTART changes the number at which user items start (only useful for
SUAI global model users). Lexical nesting is not observed; it is only the
sequence in which the declarations are scanned that determines their
numbers. The NEW function does not affect this assignment of numbers.
Items created by the New function are assigned the next available number at
the time of the execution of the New.
Those who use separately compiled procedures (see page 25) may wish to have
declared items common to both programs. However, Internal and External
items do not exist. The same effect may be achieved by carefully declaring
the desired items in the same order in both programs so that their numbers
match. The message "Warning -- two programs with items in them." will be
issued at the begining of execution, and may be ignored if you are certain
the items are declared in the same relative positions. No checking of
names, types, arrays bounds, etc. is done, so be very careful.
Items occupy no space (neither does the constant integer 15). The numbers
ascribed to items are stored in Itemvars and Associations. Itemvars are
simply a word of storage. An association is two words of storage, one
with three 12 bit bytes, each containing the number of one of the items of
the association, and a second word containing two pointers relating the
association to the associative search structure. Since the number of an
item must fit in 12 bits, the number of items is limited to about 4090.
The number of an item may be retrieved from the item as a integer with the
predeclared function CVN (<itemexpression>). The item represented by a
certain integer may be retrieved by the predeclared function
CVI (<algebraicexpression>). CVN and CVI should only be used by those who
know what they're doing and have kept themselves up to date on changes in
Leap.
LEAP STATEMENTS Syntax
SECTION 13
LEAP STATEMENTS
13.1 Syntax
<leapstatement>
::= <leapassignmentstatement>
::= <leapswapstatement>
::= <setstatement>
::= <liststatement>
::= <associativestatement>
::= <foreachstatement>
::= <sucfailstatement>
<leapassignmentstatement>
::= <itemvarvariable> _ <itemexpression>
::= <setvariable> _ <setexpression>
::= <listvariable> _ <listexpression>
<leapswapstatement>
::= <itemvarvariable> <itemvarvariable>
::= <setvariable> <setvariable>
::= <listvariable> <listvariable>
<setstatement>
::= PUT <itemexpression> IN <setvariable>
::= REMOVE <itemexpression> FROM
<setvariable>
<liststatement>
::= PUT <itemexpression> IN <listvariable>
<locationspecification>
::= REMOVE <itemexpression> FROM
<listvariable>
::= REMOVE ALL <itemexpression> FROM
<listvariable>
<locationspecification>
::= BEFORE <elementlocation>
::= AFTER <elementlocation>
<elementlocation>
::= <itemexpression>
::= <algebraicexpression>
<associativestatement>
::= DELETE ( <itemexpression> )
::= MAKE <triple>
::= ERASE <triple>
<triple>
::= <itemexpression> <itemexpression>
<itemexpression>
<foreachstatement>
::= FOREACH <bindinglist> SUCH THAT
<elementlist> DO <statement>
::= NEEDNEXT <foreachstatement>
<bindinglist>
::= <itemvarvariable>
::= <bindinglist> , <itemvarvariable>
<elementlist>
::= <element>
::= <elementlist> AND <element>
<element>
::= <itemexpression> IN
<listexpression>
::= ( <booleanexpression> )
::= <retrievaltriple>
::= <matchingprocedurecall>
<retrievaltriple>
::= <rettripelement> <rettripelement>
<rettripelement>
<rettripelement>
::= <itemexpression>
::= <derivedset>
Restrictions Semantics ASSIGNMENT STATEMENTS
<matchingprocedurecall>
::= <procedurecall>
<sucfailstatement>
::= SUCCEED
::= FAIL
13.2 Restrictions
SUCCEED and FAIL statements must be lexically nested inside a matching
procedure to be legal.
13.3 Semantics
ASSIGNMENT STATEMENTS
Assignment statements in Leap are similar to those in Algol. Itemvars, Set
variables, and List variables may be assigned item, set and list
expressions, respectively. Only one automatic coercion is done: a set
expression may be assigned to a list variable. NOTE: lists may not be
assigned to set variables (use CVSET).
The type of an itemvar is checked against the type of the item expression
assigned to it if and only if the itemvar is declared Checked. If a typed
item is assigned to an un-Checked itemvar of different or no type, the datum
is not affected. Assign an integer item to a string itemvar and the string
itemvar will now contain an item with an integer datum. Sail will not know
that you have in effect switched the type of the datum and will get very
confused if you later try to use the datum of the itemvar; it will treat the
integer as a pointer to a two word string descriptor in this case.
DATUM (X) is legal only when X is a typed item expression, namely an item
expression that the compiler can discover the type of (not COP (<set>) for
example). See page 226 for the BNF of typed item expressions. DATUM (X) is
syntactically a variable. It has the type of the typed item expression, X.
If X has an array type, then DATUM (X) should be followed by
[<subscriptlist>]. Appropriate coercions will be done (i.e., string to
integer, integer to real, etc.) just as with regular variables in
expressions. NOTE: the user is responsible for seeing that the datum of an
item expression really is the type that Datum thinks it is (i.e., Datum of a
Real Itemvar that has had a string item stored in it will give you garbage).
PROPS (X), where X is an item expression, is legal regardless of the type of
X. X may even evaluate to a bracketed triple item, procedure item, or
PUT REMOVE DELETE
event item. PROPS (X) is syntactically an integer variable. It is limited
to integers n where 0 n 4095. If negative (i.e. two's complement)
integers or integers larger than 4095 are assigned to a PROPS, only the
right 12 bits are stored. The rest of the integer is lost.
PUT
Sets and lists are initially empty. One may put items in them with the PUT
statement. "PUT <item expression> IN <set variable>" does exactly what it
says.
"PUT <item expression> IN <list variable> BEFORE <algebraic expression>"
evaluates the item expression, evaluates the algebraic expression and
coerces it into an integer, say n, then puts the item into the list at the
nth position, bumping the old nth item to the n+1th position, and so on down
the list. This increases the length of the list by one. "PUT item IN list
AFTER n" places the item in the n+1th position and bumps the old n+1th item
down to the n+2th position, and so on. If n < 0 or n > (1 + length-of-
list), then an error message is given. The special token "" may be used in
the expression for n to stand for the length of the list.
"PUT <item expression 1> IN <list variable> BEFORE <item expression 2>"
cause a search to be made of the list for the item of <item expression 2>.
If it is found, the item of <item expression 1> is placed in the list
immediately ahead of the item found by the search. "PUT item IN list AFTER
item" proceeds the same way, but puts the first item in the list immediately
following the second item. If the second item is not an element of the
list, a BEFORE will put the first item at the begining of the list, while an
AFTER will put it at the end of the list.
REMOVE
To remove an item from a set or list, one may use REMOVE. "REMOVE item FROM
set" does just what it says. If the item to be removed from the set does
not occur in the set, this statement is a no-op.
"REMOVE n FROM list" removes the nth item from the list. The old n+1th item
becomes the nth, and so forth. An error is indicated if n 0 or
n > length-of-list. As before, should stand for the length of the list.
However,
"REMOVE item FROM list" removes the first occurrence of the item from the
list. If the item is not found, this statement is a no-op.
"REMOVE ALL item FROM list" removes all occurrences of the item from the
list. DELETE
Items are represented by unique integer numbers in Sail. Due to the
overwhelming desire to store an association in one word of storage, these
unique numbers are limited to 12 bits. Thus the total number of items is
limited to 4090. The DELETE statement allows one to free numbers for reuse.
It is also the only way to get rid of an item short of exiting the program.
WARNING: The Delete statement in no way alters the instances of the Deleted
MAKE BRACKETED TRIPLE ITEMS
items which are present in sets, lists, associations, or itemvars. The user
should be sure that there are no instances of the Deleted item occurring in
itemvars, sets, lists or associations. Even saying DELETE (ITMVR) where
ITMVR is an itemvar with an item to be deleted in it will not remove the
item from ITMVR; one must be careful to change the contents of ITMVR before
using it again.
MAKE
The MAKE statement is the only way to create Associations (Triples) and add
them to the associative store. If the association already exists in the
store, no alterations are made. The argument to the Make statement is a
triple of item expressions:
MAKE item1 item2 item3
MAKE item1 itemvar1 NEW
MAKE itemvararray[23] item1 itemvar2
The component item expressions are evaluated left to right. The three items
that the three expressions evaluate to are then formed into an association,
and the association is hashed into the associative store. The item
expressions must be constructive, that is, one may use the NEW function but
not the ANY or BINDIT items (see NEW, page 178, ANY, page 179, and BINDIT,
page 179).
BRACKETED TRIPLE ITEMS
Items may be created by declaration, by the NEW function, or by using
BRACKETED TRIPLEs in Make statements. A Bracketed Triple item may not have
a datum, but may have a PROPS or a PNAME (see page 220 for pnames, page 161
for props). Instead, a Bracketed Triple item has an Association connected
to it. One creates a Bracketed Triple item by executing a Make statement:
MAKE item1 [item2item3item4] item5
where the itemN are item expressions. "[item2item3item4]" is the
Bracketed Triple item, and of course need not always be the second component
of the association. The association connected to the Bracketed Triple item
is "item2 item3 item4". The above Make statement actually creates
two triples and one item. Namely, the associations
item1 itemXX item5
item2 item3 item4
and the item "itemXX" which is a Bracketed Triple item and has the second
association connected to it. One can access a Bracket Triple item, with
the an associative search called the Bracketed Triple Item Retrieval:
itmvar _ [itm2 itm3 itm4];
COMMENT itmvar now contains itmXX;
The Bracket Triple construct may be used in any expression. See page 166.
ERASE Searching the Associative Store
Having "itmXX", one may access the items of the association connected to
with the predeclared functions FIRST, SECOND, and THIRD (see page 222 for
more information on these runtime functions):
FIRST (itemXX) is item2
SECOND (itemXX) is item3
THIRD (itemXX) is item4
ERASE
The way to remove an association from the associative store and destroy it
is to ERASE it:
ERASE item1 item2 item3
where the itemN are item expressions. The item expressions must be
retrieval item expressions; that is, one may use the ANY item but not the
NEW function or the BINDIT item (see ANY, page 179, and NEW, page 178, and
BINDIT page 179). Using ANY as one, two, or three of the item expressions
allows many associations to be erased in one statement. If the association
to be erased does not exist, Erase is a no-op.
Whenever one Erases an association, none of the items of the association are
deleted. In particular, when one Erases an association that has a
Bracketed Triple item as one of its components, the Bracketed Triple item is
not deleted. Furthermore, the association connected to the Bracketed Triple
item is not automatically erased by erasing an association containing a
Bracketed Triple item. The following Erase erases only one association:
ERASE item1 [item2item3item4] item5
However, erasing the association connected to a Bracketed Triple deletes the
item. Deleting the Bracketed Triple item DOES NOT erase the association
connected to it.
13.4 Searching the Associative Store
Flexible searching and retrieval are the main motivations for using an
associative store. It follows that this is the most important section of
the Leap part of this manual. It is a rare Leap program that does not use
at least one of the searches described below.
Four methods of searching the associative store exist in Sail:
Binding Booleans
Derived Sets
Bracketed Triple item retrieval
Foreach Statements
BINDING BOOLEANS
The first three are properly part of the discussion of Leap Expressions in
the next chapter, but are included here for completeness.
Throughout this section we will use the following notation for an
association:
A O V
where A, O and V stand for the "attribute", "object" and "value" items of an
association.
The terms "bound" and "unbound" will find heavy use in this section. Bound
describes an itemvar that has an item assigned to it. Unbound describes an
itemvar that, at this time in the execution of the program, has no item
bound to it. The object of searching the associative store is usually to
bind unbound itemvars to specific, but unknown, items. If the itemvar to be
bound was declared Checked, then type checking will be done, and the
appropriate error message will be issue if the binding item does not have
the same type as the itemvar.
Throughout this section, references to item expressions will always mean
retrieval item expressions. Do not use NEW in such expressions.
A hashing algorithm is used in storing and retrieving associations in Leap.
The user can increase the speed of associative searching or decrease his
core image by using the REQUIRE n BUCKETS construct to control the size of
his associative search hash table to reflect the number of associations he
will be using. A hash table will be allocated with (2^m) hash codes where m
is the smallest integer such that (2^m) n. Sail initializes the hash size
to '1000.
BINDING BOOLEANS
A Binding Boolean searches the associative store for a specified triple,
returning true if one can be found, and false otherwise. A Binding Boolean
is a triple:
itm1 itm2 itm3
where "itmN" is one of three things: an item expression, or the reserved
word "BIND" followed by an itemvar, or the token "?" followed by an itemvar.
An item expression as a component of the Binding Boolean means that
component of the triple that the boolean finds must be the item specified by
the item expression (unless the item expression evaluates to the item ANY,
which specifies that any item is okay). If a "BIND" itemvar is the A, O or
V of the triple, then the Binding Boolean will attempt to find an
association which meets the constraints imposed by the item expression A, O
or V components, and then binds to the "BIND" itemvar the items occuring in
the corresponding positions of the association that the Binding Boolean
found. If no such association can be found, then the Binding Boolean
returns FALSE and leaves the "BIND" itemvars with their previous values. If
DERIVED SETS BRACKETED TRIPLE ITEM RETRIEVAL THE FOREACH STATEMENT
"?" precedes an itemvar, then the itemvar will behave like a "BIND" itemvar
if it is currently contains BINDIT, but will behave like an item expression
if it is bound to some other item than BINDIT. Example:
IF Father ?Son ANY THEN PUT Son IN Sonset;
IF Father BIND Son Bob THEN CHILDLESS (Bob);
ERCHEK _ Father COP(Sonset) ANY;
DERIVED SETS
Derived Sets are quite simple: "Foo Garp" where Foo and Garp are item
expressions, is the set of all items X such that Foo Garp X exists.
"Garp Sister" is the set of all items X such that
X Garp Sister exists. "Foo ' Sister" is the set of all items X such
that Foo X Sister exists. Examples:
Dadset _ Father ANY;
Danson _ Father ` Dan;
News _ (Son Dad) attset;
ANY specifies "I don't care" to the search. BINDIT has no special meaning
to the search, and behaves like any other items. Since BINDIT can never
appear in an association, this means the set returned will always be the
empty set PHI.
BRACKETED TRIPLE ITEM RETRIEVAL
A Bracketed Triple item can be referenced by specifying the association it
is connected to. For example,
Itmvar _ [itm1 itm2 ANY]
PUT [ANY ANY ANY] IN Bracset
IF Foo Garp [itm1 itm2 ANY] THEN ...
Itmvar _ [itm1 [itm2 itm3 itm4] itm5]
where itmN is any item expression not containing NEW or BINDIT. ANY means
you don't care what item occupies that component. If the designated
Bracketed Triple is not found then BINDIT is returned and no error message
is given. THE FOREACH STATEMENT
This statement is the heart of Leap. It is similar to the FOR statement of
Algol in that a statement is executed once for each binding of a variable.
In this semi-schematic example,
FOREACH X SUCH THAT <element> AND ... AND
<element> DO <statement>;
the <statement> is executed once for each binding of the itemvar X. The
<element>s in the element list (i.e. <element> AND...AND <element>)
determine the bindings of the itemvar, and hence how many times the
<statement> is executed. If the <element>s are such that there is no
binding possible for X, then the <statement> is never executed. Like a Sail
FOR statement, one may use DONE, NEXT, and CONTINUE within the <statement>.
As before, when one uses a NEXT inside the loop, the word NEEDNEXT must
precede the FOREACH of the Foreach that one wants checked and possibly
terminated. See pages 37, 38, and 38 for more information about Done, Next,
and Continue.
Restriction: Jumping (i.e. with a GO TO) into a Foreach is illegal.
However, it is legal to jump out of a Foreach, or to jump around within the
same Foreach.
Foreach statements differ from For statements in that more than one itemvar
may be included to be given bindings:
FOREACH X, Y, Z SUCH THAT <element>....
X, Y, and Z are called Foreach itemvars. Just as one must declare the
integer I before using it in the Sail For statement
FOR I _ 1 STEP 2 UNTIL 21 DO...
so must one declare Foreach itemvars before using them in Foreaches.
Foreach itemvars are no more than normal itemvars receiving special
assignments; they may have any type. If a Foreach itemvar that has been
declared Checked is assigned an item by the search that has a different type
than the Checked itemvar, an error message will result.
Foreach itemvars differ from For variables in a more radical way. It is
possible to specify to the Foreach that a certain Foreach itemvar be a
variable to the search only on the condition that that the itemvar contains
the special item BINDIT at the time the Foreach is called. One precedes
such itemvars with the "?" token. For example:
FOREACH ?X, ? Y, Z SUCH THAT <element>....
If X contains BINDIT but Y does not when this Foreach starts execution, then
the search will be conducted exactly as if the statement
FOREACH X,Z SUCH THAT <element>....
were the Foreach specified. The itemvar X will then act just like an
ordinary, non-foreach itemvar that was bound previous to the Foreach. All
Foreach itemvars may be "?" itemvars if this is desired.
There are four different types of <element> that may be used in foreach
element lists:
Set Membership
Boolean Expressions
Retrieval Triples
Matching Procedures
THE LIST MEMBERSHIP <ELEMENT>
The order of the <element>s in the element list is very important, as we
shall see.
Terminology: we say that a certain binding of the the Foreach itemvars
"satisfies" an <element>. If that binding satisfies each <element> of the
element list, then we say it "satisfies the associative context". A fancy
way of refering to the element list is "associative context". We also refer
to the collection of bindings that satisfy the associative context as the
"satisfier group" of the Foreach.
The execution of a Foreach proceeds as follows. After initialization, the
Foreach proceeds with a search specified by the first <element> of the
element list. If a binding can be found that satisfies the first <element>,
the Foreach proceeds forward to the new <element> of the list and tries to
satisfy it, and so on. When the Foreach can not satisfy an <element>, it
"backs up" to the previous element and tries to get a different binding. If
it can't find satisfaction there, it backs up again and tries again to get a
different binding. When a Foreach proceeds forward off the end of the
element list (i.e. the associative context is satisfied) then the
<statement> is executed, and the Foreach backs up to the last <element> of
the element list. When the Foreach backs up off the left end of the element
list, the Foreach is exited.
When a Foreach is exited by backing up off the left, the Foreach itemvars
are restored to the last satisfier group bound to them, regardless of what
the <statement> may have done. If the associative context was never
satisfied, then the Foreach itemvars have the values that they had before
the Foreach. When a Foreach is exited with a GO TO, DONE, or RETURN, the
Foreach leave the itemvars with the bindings they had at the GO TO, or
whatever, including any modifications that the <statement> may have made to
them.
THE LIST MEMBERSHIP <ELEMENT>
[In the following, one may also read "set" for "list"; Sail automatically
coerces set expressions into list expressions.] This <element> does not
search the associative store to bind an itemvar, but merely binds it with an
item of a specified list. In the Foreach,
FOREACH X | X IN L DO <statement>;
(here we have used the Sail synonym "|" for "SUCH THAT"), the Foreach
itemvar X is bound successively to each element of the set L, starting at
the beginning of the list. If an item occurs n times in L, then X will be
bound to that item n times in the course of the Foreach. Thus, the number
of satisfiers to the above Foreach is LENGTH (L).
In the current implementation of Leap, there is a difficulty that should be
pointed out. If inside the <statement>, one changes L by list assignment,
Removes, etc. in such a way as to remove the next item of the list that the
THE BOOLEAN EXPRESSION <ELEMENT>
Foreach itemvar would have been bound to, Leap may go crazy. Foreach
searches look one ahead and save a pointer to the next items to be bound to
the Foreach itemvars. This allows one to remove the items of the current
bindings of the Foreach itemvars from lists or whatever, but makes other
removals hazardous. For example,
FOREACH X | X IN L DO REMOVE X FROM L;
will work, but
PUT V IN L BEFORE FOO;
FOREACH X | X IN L DO REMOVE V FROM L;
will probably fail. No error checking is done.
Whenever the Foreach itemvar of a list <element> has been bound previously,
the list element behaves like a boolean. It does not rebind the itemvar but
only checks to see that it is in the list. For example,
FOREACH X | X IN L AND X IN LL DO <statement>;
X is bound by the <element> "X IN L". <element> "X IN LL" is satisfied if
the item contained in the itemvar X is in the list LL.
If two different Foreach itemvars are used with two different lists, i.e.
FOREACH X,Y | X IN L AND Y IN LL
DO <statement>;
then after execution of the <statement>, the Foreach will go back the last
<element> that searches for bindings, in this case "Y IN LL" and gets a new
binding for Y. It is only on failure of this search that the Foreach goes
back to the first <element>, "X IN S", and gets a new binding for X. Thus
the <statement> will be executed once for each possible X,Y pair. In the
Foreach,
FOREACH X,Y | X IN L AND Y IN L ...;
X and Y will be bound to all possible pairs of elements in L. This
includes pairs with duplicate elements, like (a,a). Different orderings of
the same elements will NOT be ignored. Thus, pairs like (a,b) and (b,a)
will each be a satisfier group sometime during the Foreach. Furthermore, if
the list L contains duplications of the same item, identical pairs will
occur in proportion to the number of duplications. That is, regardless of
the duplications within the list, the number of satisfier groups to the
Foreach above is LENGTH (L)^2. THE BOOLEAN EXPRESSION <ELEMENT>
Any Sail boolean expression may be used as an <element> in the Associative
Context of a Foreach if it is inclosed by parentheses. A Boolean Expression
<element> is satisfied if it is TRUE. Note that the boolean expression must
have parentheses around it.
THE RETRIEVAL TRIPLE <ELEMENT>
WARNING: Foreach itemvars can not be bound by a Boolean Expression
<element>. Therefore, all itemvars used in a Boolean Expression <element>
must be bound by previous <element>s in the element list. A Boolean
Expression <element> with unbound Foreach itemvars in it causes an error
message. THE RETRIEVAL TRIPLE <ELEMENT>
To search the associative store with a Foreach, one uses the Retrieval
Triple <element>. A Retrieval Triple is satisfied if a binding of the
Foreach itemvars can be found such that the triple is an extant association.
If all of the itemvars of the Retrieval Triple <element> were bound previous
to the execution of the Retrieval Triple <element>, then the Triple does no
further binding; it is satisfied if the specified triple is in the
associative store. For example,
FOREACH X | FATHER TOM X AND
X IN PTASET DO <statement>;
FOREACH X | X IN PTASET AND
FATHER TOM X DO <statement>;
The two Foreaches have the same effect. However, in the first case, X is
bound by a search of the associative store for any triple that has FATHER
as its attribute component, and TOM as its object component. When such a
triple is found, X is bound to the item that is the value component. Then,
if X is in the PTASET, the Foreach lets the statement execute. If X is not
in PTASET, then the Foreach backs up and tries to find another triple with
FATHER as its attribute and TOM as its value. In the second Foreach, X is
bound with an item from PTASET, then the associative store is checked to
see that the triple FATHERTOMx, where x is the binding of X, is in the
store. If it is, the <statement> is executed, otherwise the Foreach backs
up and gets a different item from PTASET and binds that to X. Assuming
that Tom has only one father, the first search is much faster.
Using ANY in a Retrieval Triple indicated that you don't care what item
occupies that position. For instance, in
FOREACH X | FATHER ANY X DO <statement>;
X is bound successively to all fathers. However, if the associative store
included the following three associations,
FATHER KAREN PAUL
FATHER LYNN PAUL
FATHER TERRY PAUL
then X would be bound to PAUL only once, not thrice. BINDIT has no special
meaning to the search. Since BINDIT can never appear in an association, a
Retrieval Triple containing it will cause the search to always fail.
Different kinds of associative searches proceed with different efficiencies.
Listed below in order of decreasing efficiency are the various forms of
Retrieval Triple <element>s that are legal. A, O, and V represent either
bound Foreach itemvars or items from explicit item expressions in the
triple. x, y, and z represent unbound Foreach itemvars or the item ANY.
(note that x x V is really x O V, and so on). The two forms
of the List Membership <element> are included for comparison.
x IN L All items x in the list L.
A O x Only the value is free.
x y V Attribute and object are free.
A IN L Verification that item A is in list L.
A O V Verification that the triple
is in the store.
A x V Only the object is free.
x O V Only the attribute is free.
A x y Object and value are free.
x O y Attribute and value are free.
x y z Attribute, value and object are free.
Note that MAKEing an association inside a Foreach may or may not affect
subsequent bindings. For example, in
FOREACH X,Y | Link X Y DO
MAKE Link X Newlink;
it is uncertain whether Y will ever receive Newlink as its binding or not.
The A, O, and V used in a Retrieval Triple of a Foreach may be a derived set
expressions as well as item expressions. For example,
FOREACH X, Y | Link (FatherY) X DO ...;
ERASE in the <statement> of a Foreach that binds any of its itemvars with
Retrieval Triples may cause problems. This is similar to REMOVE used in
Foreaches with List Membership <element>s controling some bindings. ERASE
can only be guaranteed to to work safely if the association erased is the
one we just got a binding from, e.g.
FOREACH X | A O X DO ERASE A O X;
or if the association erased could not possible be used for a binding of a
Foreach itemvar, such as,
FOREACH X | Link X Node DO
ERASE Node X ANY;
Foreaches look one ahead to the next binding of its itemvars, and leaves a
pointer to those associations. If you Erase any of those associations, the
Foreach gets lost in the boondocks. No error checking is done.
However, as long as the associative store is not changed during the
THE MATCHING PROCEDURE <ELEMENT>
execution of the Foreach, a Retrieval Triple will not itself repeat a
particular set of bindings that it bound before. THE MATCHING PROCEDURE
<ELEMENT>
Matching Procedures are the most general search mechanism in Leap. They
also provide a convenient method of writing coroutines.
A MATCHING Procedure is very similar to a boolean procedure (in fact
outside of Foreach associative contexts, it behaves like a boolean procedure
and may be called within expressions, etc.). It must be declared type
MATCHING. It may not be declared SIMPLE. The formal parameters of a
Matching Procedure may include zero or more "?" itemvars (pronounced
"question itemvars") which may have any datum type but may not be VALUE or
REFERENCE. These parameters correspond roughly to either call by value or
call by reference, depending on the actual parameter when the procedure is
called. When the actual parameter is an item expression or a bound itemvar
the parameter is equivalent to a value parameter. However, if the actual
parameter is an unbound Foreach itemvar, then the parameter is treated as a
reference parameter, and on entry is is initialized to the special item
BINDIT.
Matching Procedures are exited by SUCCEED and FAIL statements instead of
RETURN statements. When used outside of an associative context, SUCCEED
corresponds to RETURN(TRUE) and FAIL corresponds to RETURN(FALSE) [this is
not strictly true when the matching procedure is sprouted as a process --
see page 190]. Inside an associative context, Succeed and Fail determine
whether the Foreach is to proceed to the next <element> of the element list
or to backup to the previous <element> of the element list. When the
Foreach backs up into a Matching Procedure, the procedure is not recalled,
but resumed at the statement following the last Succeed executed. On the
other hand, when a Foreach proceeds forward into a Matching Procedure, the
procedure is called, not resumed.
When a Matching Procedure is the last <element> of the associative context,
Succeeding will cause the <statement> to be executed; the Foreach then
backs up into the Matching Procedure, and the Matching Procedure is resumed
at the statement following the Succeed. When a Matching Procedure is the
first <element> of an associative context, Failing will exit the Foreach.
WARNING: Matching procedures are implemented as processes and two calls of
the same matching procedure may share the same memory unless the procedure
is declared RECURSIVE. See Memory Accessible to a Process, page 188.
If a Matching Procedure is explicitly SPROUTed as a process then the
Matching Procedure can be made running by a RESUME. In such a case the item
sent by RESUME is returned as the value of the SUCCEED or FAIL statement
which suspended the Matching Procedure, just as though SUCCEED or FAIL were
an item procedure. (In fact Succeed and Fail always return an item value,
but the value is ANY except in this special case.) Being Resumed is the
only was in which a Matching Procedure can be reactivated after a FAIL.
When a Matching Procedure is used exterior to the associative context of a
Foreach, one may use "BIND" in the call preceding those actuals which one
wishes bound regardless of their current binding. Preceding the actual with
"?" will have the save effect as "BIND" if the current value of the itemvar
is BINDIT, and will have no effect otherwise (the procedure will not
attempt to find it a binding).
That is all there is to Matching Procedures. Their power lies in the using
them cleverly. The following program illustrates techniques one may use
with matching procedures by simulating the List Membership and Retrieval
Triple <element>s with matching procedures.
RECURSIVE MATCHING PROCEDURE INLIST
(? ITEMVAR X; LIST L);
BEGIN "INLIST"
COMMENT THIS PROCEDURE SIMULATES THE CONSTRUCT
X L FOR ALL CASES EXCEPT THE SIMPLE
PREDICATE BINDITL;
IF X BINDIT THEN
BEGIN WHILE LENGTH (L) DO IF X = LOP (L)
THEN BEGIN SUCCEED; DONE END;
FAIL
END;
WHILE LENGTH (L) DO BEGIN X_LOP (L);
SUCCEED END;
END "INLIST";
MATCHING PROCEDURE TRIPLE (? ITEMVAR A, O, V);
BEGIN "TRIPLE"
DEFINE BINDING (A)="(A=BINDIT)";
SET SET1; INTEGER INDX;
RECURSIVE PROCEDURE SUCCSET (REFERENCE
ITEMVAR X; SET S1);
WHILE LENGTH (S1) DO BEGIN X_LOP (S1);
SUCCEED END;
INDX _ 0;
IF BINDING (A) THEN INDX _ 1;
IF BINDING (O) THEN INDX _ INDX + 2;
IF BINDING (V) THEN INDX _ INDX + 4;
CASE INDX OF
BEGIN [0] "AOV" IF AOV THEN SUCCEED;
[1] "?OV" SUCCSET (A, OV);
[2] "A?V" SUCCSET (O, A`V);
[3] "??V" BEGIN SET1 _ ANY V;
WHILE (LENGTH (SET1)) DO
BEGIN A _ LOP (SET1);
SUCCSET (O, A`V) END END;
[4] "AO?" SUCCSET (V, AV);
[5] "?O?" BEGIN SET1 _ O ANY;
WHILE (LENGTH (SET1)) DO
BEGIN A _ LOP (SET1);
SUCCSET (V, AO) END END;
[6] "A??" BEGIN SET1 _ A ` ANY;
WHILE (LENGTH (SET1)) DO
BEGIN O _ LOP (SET1);
SUCCSET (V, AO) END END;
[7] "???"
USERERR(0, 1, "ANYANYANY IS IN BAD TASTE")
END;
END "TRIPLE";
LEAP EXPRESSIONS Syntax
SECTION 14
LEAP EXPRESSIONS
14.1 Syntax
<leapexpression>
::= <itemexpression>
::= <setexpression>
::= <listexpression>
<itemexpression>
::= <itemprimary>
::= [ <itemprimary> <itemprimary>
<itemprimary> ]
<itemprimary>
::= NEW
::= NEW ( <algebraicexpression> )
::= NEW ( <setexpression> )
::= NEW ( <listexpression> )
::= NEW ( <arrayname> )
::= ANY
::= BINDIT
::= <itemidentifier>
::= <itemvarvariable>
::= <listexpression> [
<algebraicexpression> ]
::= <itemvarprocedurecall>
::= <resumeconstruct>
::= <interrogateconstruct>
<itemvarprocedurecall>
::= <procedurecall>
<listexpression>
::= <listprimary>
::= <listexpression> & <listexpression>
<listprimary>
::= NIL
::= <listvariable>
::= {{ <itemexprlist> ~~
::= ( <listexpression> )
::= <listprimary> [ <substringspec> ]
::= <setprimary>
<itemexprlist>
::= <itemexpression>
::= <itemexprlist> , <itemexpression>
<setexpression>
::= <setterm>
::= <setexpression> <setterm>
<setterm>
::= <setfactor>
::= <setterm> <setfactor>
<setfactor>
::= <setprimary>
::= <setfactor> - <setprimary>
<setprimary>
::= PHI
::= <setvariable>
::= {itemexprlist~
::= ( <setexpression> )
::= <derivedset>
<derivedset>
::= <itemexpression> <associativeoperator>
<itemexpression>
<associativeoperator>
::=
::= `
::=
<itemvarvariable>
::= <variable>
<setvariable>
::= <variable>
Semantics ITEM EXPRESSIONS
<listvariable>
::= <variable>
<leaprelational>
::= <itemexpression> IN
<setexpression>
::= <itemexpression> IN
<listexpression>
::= <itemexpression>
<itemrelationaloperator>
<itemexpression>
::= <setexpression>
<setrelationaloperator>
<setexpression>
::= <listexpression>
<listrelationaloperator>
<listexpression>
::= <triple>
<itemrelationaloperator>
::= =
::=
<setrelationaloperator>
::= =
::=
::= <
::= >
::=
::=
<listrelationaloperator>
::= =
::=
14.2 Semantics
ITEM EXPRESSIONS
Itemvars and itemvar arrays may be used in item expressions just as
algebraic variables and algebraic arrays are used in algebraic expressions.
Itemvars and itemvar arrays are initialized to the special Sail item ANY.
Items may be retrieved from sets and lists with the Sail functions COP and
NEW
LOP. COP (<set expression or list expression>) yields the item which is the
first element of the set or list that the set or list expression evaluated
to. LOP also yields the first item of the set or list, but removes that
item from the set or list. Because LOP changes the contents of the set or
list that is its argument, it can only accept set or list variables, not
expressions. See page 89.
List element designators may be used as itemvars in expressions. For
example, if RECORD is a list, and ITMVR an itemvar,
RECORD[5] _ ITMVR;
ITMVR _ RECORD[-1];
RECORD[] _ RECORD[1];
are all legal. The special token "" means the length of the list when used
in this context. The contents of the square brackets may be any algebraic
expression as long as it evaluates to an integer n where
1 n LENGTH (list).
<listexpression> [<algebraicexpression>] returns a particular element of a
list, but may not appear on the left of an assignment expression, because
assignment must be to variables.
NEW
The function NEW creates an item at execution time. Since space must be
allocated at loading for various tables, one must indicate approximately
how may NEW items he will create (the compiler counts the declared items for
you). Therefore, one should say "REQUIRE n NEWITEMS" where n is some
integer less than 4090 (the maximum number of items allowed in Sail). n may
be larger than the actual number of New items created, but the excess will
be wasted space. If 0 < n < 50, you get tables for 50 New items anyway.
NEW may take an argument. In this case, the datum of the created item is
preloaded with the value passed as argument. If this argument is algebraic,
set or list, then the datum will be of the same type. No type conversions
are done when passing the algebraic argument. NEW will also accept an array
name as argument. In this case, the created item will be of the type array.
In fact, the array cited as argument will be copied into the newly created
array. The new array will have the same bounds and number of dimensions as
the array cited as argument. This array will not disappear even if the
block that the original array was declared in is exited. It will only be
deallocated if the item is deleted.
NEW in an item expression makes that item expression a "constructive item
expression". Constructive item expressions are illegal in some places,
namely anywhere that attempts to gets an item from an existing structure
(i.e., ERASE, REMOVE, and Associative searches). It is usually clear
whether or not a constructive item expression is illegal.
ANY BINDIT TYPES AGAIN SET AND LIST EXPRESSIONS
ANY
Some associative searches may need only partial specification. The ANY item
is used to specify exactly which parts of the specification are
"don't cares"'s. Examples:
FOREACH X SUCH THAT Father X ANY DO ...
IF Father BIND X ANY THEN ...
ANY in an item expression makes that item expression a "retrieval item
expression". This is the opposite of a constructive item expression, and is
illegal anywhere the statement is creating new structure, namely, a MAKE
statement. Thus, ANY is legal everywhere items are, except a MAKE
statement.
BINDIT
Like ANY, BINDIT specifies no constraints on the associative search.
However, BINDIT has a special meaning to some searches, namely the Binding
Boolean and Matching Procedures (depending on how they're written). An
itemvar containing BINDIT will be bound by the search to an item of the
association that the search found. For example:
X _ BINDIT;
IF Father ? X Bob
THEN PUT X IN Bobfatherset;
Like ANY, BINDIT is illegal in MAKE statements. In certain associative
searches, namely the ERASE statement, the Bracketed Triple Item retrieval
expression, and the Retrieval Triple <element> of a Foreach, inclusion of
BINDIT will cause the search to always fail, because BINDIT can appear in no
association.
TYPES AGAIN
The compiler can determine the type of items when the item expression is a
typed itemvar, a typed itemvar procedure, a declared item with a type, a
typed itemvar array, or a NEW with an argument. When the compiler can
determine the type of the item expression, then and only then is it legal to
use the Datum construct on the item expression or to assign the item
expression to a Checked itemvar. For example, the following are ILLEGAL:
DATUM (COP (<set>))
DATUM (RECORD[]); COMMENT RECORD is a list;
CHEC _ NEW; COMMENT CHEC is a Checked itemvar;
SET AND LIST EXPRESSIONS
Three rather standard operations are implemented for use with sets. These
are union (), intersection (), and subtraction (-). These operators have
the standard mathematical interpretations. The only possible confusion
pertains to subtractions: if we perform the set operation
set1 - set2
DERIVED SETS BOOLEANS
and if there is an instance of an item x in set2 but not in set1, the
subtraction proceeds and no error message is given.
If one considers a list to be a string of items, then concatenation and
taking sublists suggest themselves as likely list operations. The syntax
and semantics for sublisting and list concatenation are identical with those
of strings, with the natural exception that the results are lists, and not
strings. There is also a difference in that if the indices to the
substringer do not make sense, an error message is generated rather than
setting of the SKIP variable. Examples:
LISTVAR _ LISTVAR[2 TO -1];
LISTVAR _ LISTVAR[9 FOR 2*N];
LISTVAR _ LISTVAR[1 FOR 2] & LISTVAR[3 TO ];
One may generate sets with
{item1, item2, item3~
and may generate lists with
{{item1, item1, item2, item3~~.
Sets are initialized to the empty set, PHI. Lists are initialized to the
null list, NIL. Initialization occurs at the beginning of the execution of
the program. Sets and list are reinitialized on entering the blocks of
their declaration only when such blocks are in recursive procedures.
DERIVED SETS
Derived sets are really sets of answers to questions which search the
associative memory. The conventions are:
a b -- the set of all x such that a b x
a b -- the set of all x such that x a b
a ` b -- the set of all x such that a x b
BOOLEANS
Several boolean primaries are implemented for comparing sets, lists, and
items. In the following discussion, "ix" means item expression, "se" means
set expressions, and "le" means list expression. These are:
1) Set and List Membership. The boolean "ix IN se" evaluates the set
or list expression, and returns TRUE if the item value specified by
the item expression is a member of the set or list.
2) Association Existence. The binding boolean "ix ix ix", where
the ix are item expressions or itemvars preceded by ? or BIND,
returns TRUE if a binding of the BIND itemvars (and ? itemvars that
contained BINDIT) can be found such that the association exists in
the associative store. See page 165 for more information on
binding booleans.
PNAMES PROPS
3) Relations.
ix = ix obvious interpretation
ix ix obvious interpretation
se1 < se2 true if se1 is a proper
subset of se2
se1 se2 true if se1 is identical to
se2 or is a proper subset of se2
se1 = se2 obvious interpretation
se1 se2 obvious interpretation
se1 > se2 equivalent to se2 < se1
se1 se2 equivalent to se2 se1
le1 = le2 obvious interpretation
le1 le2 obvious interpretation
PNAMES
For those desire them, each item may have a string, called its PNAME, linked
with it. This is completely independent of the Datum construct. New
items and Bracketed Triple items are created with NULL strings as their
Pnames. One may delete an item's Pname with the DELPNAME function which
takes an item expression as its argument. One may give a Pnameless item a
Pname with the NEWPNAME procedure, which takes an item expression and a
string as its arguments. CVSI will give you the Pname of an item, and CVIS
with give you the item with the specified Pname. No two items may have the
same Pname. Pnames do not follow Algol scope rules. See page 220 to find
out how to use the above four functions.
If you would like your declared items to have Pnames that are the same as
the identifier used in their declaration, say "REQUIRE PNAMES" or "REQUIRE n
PNAMES" before their declaration at the beginning of the program. The n is
an estimate of the number of dynamically created items with pnames you will
use -- this causes tables for n pnames to be allocated at compile time
rather than runtime, thus making your program more efficient.
PROPS
Any item may have a PROPS. This is an extra 12 bits of storage (frequently
used for bits). PROPS (X) where X is an item expression is exactly an
integer variable in its syntax. See page 161 for further information on
props.
BACKTRACKING Introduction Syntax
SECTION 15
BACKTRACKING
15.1 Introduction
Backup or backtracking is the ability to "back up" execution to a previous
point. Sail facilitates backtracking by allowing one to REMEMBER, FORGET,
or RESTORE variables in the data type CONTEXT.
15.2 Syntax
<contextdeclaration>
::= CONTEXT <idlist>
::= CONTEXT ARRAY <arraylist>
::= CONTEXT ITEM <idlist>
::= CONTEXT ITEMVAR <idlist>
<backtrackingstatement>
::= <remkeyword> <variablelist>
<rempreposition> <contextvariable>
<remkeyword>
::= REMEMBER
::= FORGET
::= RESTORE
<rempreposition>
::= IN
::= FROM
<variablelist>
::= <varilist>
::= ( <varilist> )
::= ALL
::= <contextvariable>
<varilist>
::= <vari>
::= <varilist> , <vari>
Semantics THE CONTEXT DATA TYPE
<vari>
::= <variable>
::= <arrayidentifier>
<contextvariable>
::= <variable>
<arrayidentifier>
::= <identifier>
<contextelement>
::= <contextvariable> : <variable>
15.3 Semantics
THE CONTEXT DATA TYPE
A context is essentially a storage place of undefined capacity. When we
REMEMBER a variable in a context, we remember the name of the variable along
with its current value (if an array, values). If we remember a value which
we have already remembered in the named context, we destroy the old value we
had remembered and replace it with the current value of the variable.
Values can be given back to variables with the RESTORE statement.
Context variables are just like any other variables with respect to scope.
Also, at execution time, context variables are destroyed when the block in
which they were declared is exited in order to reclaim their space. Context
arrays, items, and itemvars are legal (items and itemvars are part of Leap).
NEW( <context variable> ) is legal (NEW is also part of Leap).
RESTRICTIONS:
1. Context procedures do not exist. Use context itemvar procedures
instead.
2. Context variables may only be passed by reference to procedures (i.e.,
contexts are not copied).
3. Contexts may not be declared "GLOBAL" (shared between jobs - SUAI
only).
4. +, *, /, and all other arithmetic operators have no meaning when
applied to Context variables. Therefore, context variable expressions
always consist only of a context variable.
REMEMBER RESTORE
The empty context is NULLCONTEXT. Context variables are initialized to
NULLCONTEXT at program entry.
REMEMBER
To save the current values of variables, list them, with or without
surrounding parentheses, in the remember statement. All of an array will be
remembered if subscripts of an array are not used, otherwise, only the value
indicated will be remembered. If a variable has already been remembered in
context, its value is replaced by the current value. If one wants to update
all the variables so far remembered in this context, one may say
REMEMBER ALL IN <context>.
If you have several contexts active,
REMEMBER CNTXT1 IN CNTXT2
will note the variables Remembered in CNTXT1, and automatically Remember
their CURRENT values in CNTXT2.
RESTORE
To restore the values of variables that were saved in a context, list them
(with or without surrounding parentheses) in a restore statement.
Restoring an array without using subscripts causes as much of the array that
was remembered to be restored magically to the right locations in the array.
You can remember a whole array, then restore all or selected parts (e.g.
RESTORE A[1, 2] FROM IX;). If you remembered only A[1, 2], then restoring A
will only update A[1, 2]. RESTORE ALL IN IX will of course restore all the
variables from IX. RESTORE CNTXT1 FROM CNTXT2 will act like a list of the
variables in CNTXT1 was presented to the Restore instead of the identifier
CNTXT1.
Astute Leap users will have noted that the syntax for variables includes
Datum(typed itemvar) and similar things. If one executes REMEMBER DATUM
(typeditemexpression1) IN CNTXT, then RESTORE DATUM (<itemexpression2>)
FROM CNTXT will give an error message unless the <typeditemexpression2>
returns the same item as <typeditemexpression1>.
WARNING!!! Restoring variables that have been destroyed by block exits will
give you garbage. For example, the following will blow up:
BEGIN "BLOWS UP"
CONTEXT J1;
INTEGER J;
BEGIN INTEGER ARRAY L[1:J];
REMEMBER J, L IN J1;
END;
RESTORE ALL FROM J1;
END "BLOWS UP";
FORGET INCONTEXT CONTEXT ELEMENTS
FORGET
The forget statement just deletes the variable from the context without
touching the current variable's value. Variables remembered in a context
should be forgotten before the block in which the variables were declared
is exited. FORGET ALL FROM X1 and FORGET CNTXT1 FROM CNTXT2 work just as
the similar Restore statements work, only the variables are Forgotten
instead of Restored.
INCONTEXT
The runtime boolean INCONTEXT returns true if the specified variable is in
the specified context. For details, see page 95.
CONTEXT ELEMENTS
Context elements provide a convenient method of accessing a variable that is
being remembered in a context. Examples of context elements:
CNTXTVARI : SOMEVARI
DATUM (CNTXTITEM) : SOMEVARI
CNTXTAR[2,3] : ARRY[4]
DATUM (CNTXTVARI : ITMVR)
CNTXTVARI : DATUM(ITMVR)
A context element is syntactically and semantically equivalent to a variable
of the same type as the variable following the colon. For the complete
syntax of variables, see page 226. Assignments to context elements change
the Remembered value (i.e., X_5; REMEMBER X IN C; C:X_6; RESTORE X FROM C;
will leave X with the value 6).
As with the Restore statement, one may not use Context Elements of variables
destroyed by block exits.
RESTRICTIONS: (1) One may not Remember Context Elements. (2) Passing
Context Elements by reference to procedures that change contexts is
dangerous. Namely, if the procedure Forgets the element that was passed to
it by reference, then the user is left with a dangling pointer. A more
subtle variation of this disaster occurs when the Context element passed is
an array element. If the procedure Remembers the array that that array
element was a part of, the formal that had the array element Context Element
passed to it is left with a dangling pointer.
PROCESSES Introduction Syntax Semantics STATUS OF A PROCESS
SECTION 16
PROCESSES
16.1 Introduction
A PROCESS is a procedure call that may be run independently of the main
program. Several processes may "run" concurrently. When dealing with a
multi-process system, it is not quite correct to speak of "the main
program". The main program is actually a process itself, the main process.
This section will deal with the creation, control, and destruction of
processes, as well as define the memory accessible to a process. The
following section will describe communication between processes.
16.2 Syntax
<processstatement>
::= <sproutstatement>
<sproutstatement>
::= SPROUT ( <itemexpression> ,
<procedurecall> ,
<algebraicexpression> )
::= SPROUT ( <itemexpression> ,
<procedurecall> )
::= SPROUT ( <itemexpression> ,
<applyconstruct> )
<sproutdefaultdeclaration>
::= SPROUTDEFAULTS <integerconstant>
16.3 Semantics
STATUS OF A PROCESS
A process can be in one of four states: terminated, suspended, ready, or
running. A terminated process can never be run again. A suspended process
can be run again, but it must be explicitly told to run by some process that
is running. Since Sail is currently implemented on a single processor
SPROUTING A PROCESS
machine, one cannot really execute two procedures simultaneously. Sail uses
a scheduler to swap processes from ready to running status. A running
process is actually executing, while a ready process is one which may be
picked by the scheduler to become the running process. The user may
retrieve the status of a process with the execution time routine PSTATUS,
page 195. SPROUTING A PROCESS
One creates a process with the SPROUT statement:
SPROUT (<item>, <procedure call>, <options>)
SPROUT (<item>, <procedure call>)
<item> is a construction item expression (i.e. do not use ANY or BINDIT).
Such an item will be called a process item. The item may be of any type;
however, its current datum will be writen over by the SPROUT statement, and
its type will be changed to "process item" (see TYPEIT, page 218).
RESTRICTION: A user must never modify the datum of a process item.
<procedure call> is any procedure call on a regular or recursive procedure,
but not a simple procedure. This procedure will be called the process
procedure for the new process.
<options> is an integer that may be used to specify special options to the
SPROUTer. If <options> is left out, 0 will be used. The different fields
of the word are as follows:
BITS NAME DESCRIPTION
14-17 QUANTUM (X) Q _ IF X=0 THEN 4 ELSE 2^X; The process will be given
a quantum of Q clock ticks, indicating that if the user is
using CLKMOD to handle clock interrupts, the process should
be run for at most Q clock ticks, before calling the
scheduler. (see about CLKMOD, page 214 for details on making
processes "time share").
18-21 STRINGSTACK (X) S _ IF X=0 THEN 16 ELSE X*32; The process will be
given S words of string stack.
22-27 PSTACK (X) P_IF X=0 THEN 32 ELSE X*32; The process will be given P
words of arithmetic stack.
28-31 PRIORITY (X) P _ IF X=0 THEN 7 ELSE X; The process will be given a
priority of P. 0 is the highest priority, and reserved for
the Sail system. 15 is the lowest priority. Priorities
determine which ready process the scheduler will next pick to
make running.
32 SUSPHIMIf set, suspend the newly sprouted process.
33 Not used at present.
SPROUTDEFAULTS MEMORY ACCESSIBLE TO A PROCESS
34 SUSPME If set, suspend the process in which this sprout statement
occurs.
35 RUNME If set, continue to run the process in which this sprout
statement occurs.
The names are defined in the file SUAISYS:PROCES.DEF, which one may
require as a source file. Options words may be assembled by simple
addition, e.g. RUNME + PRIORITY (3) + PSTACK (2).
DEFAULT STATUS: If none of bits 32, 34, or 35 are set, then the process in
which the sprout statement occurs will revert to ready status, and the newly
sprouted process will become the running process.
The default values of QUANTUM, STRINGSTACK, PSTACK, and PRIORITY are stored
in the system variables DEFQNT, DEFSSS, DEFPSS, and DEFPRI respectively.
These values may be changed. The variables are declared EXTERNAL INTEGERs
in SUAISYS:PROCES.DEF.
SPROUTDEFAULTS
If one of the "allocation" fields of the options word passed to the SPROUT
routine -- i.e., QUANTUM, STRINGSTACK, PSTACK, or PRIORITY -- is zero,
then SPROUT will look at the corresponding field of the specified
<integerconstant> of the SPROUTDEFAULTS for the procedure being sprouted.
If the field is non-zero then that value will be used; otherwise the
current "system" default will be used.
NOTE: SPROUTDEFAULTS only applies to "allocations", i.e., the process
status control bits (e.g. SUSPME) are not affected. Example:
RECURSIVE PROCEDURE FOO;
BEGIN
SPROUTDEFAULTS STRINGSTACK (10);
INTEGER XXX;
:
:
END;
:
SPROUT (P1, FOO, STRINGSTACK (3));
SPROUT (P2, FOO);
COMMENT P1 will have a string stack of 3*32 words.
P2 will have a string stack of 10*32 words;
MEMORY ACCESSIBLE TO A PROCESS
A process has access to the same global variables as would a "normal" call
of the process procedure at the point of the SPROUT statement. For example,
suppose you Sprouted a process in the first instantiation of a recursive
procedure and immediately suspended it. Then in another instantiation of
the procedure, you resumed the process. Since each recursive instantiation
of a procedure creates and initializes new instances of its local variables,
the process uses the instances of the recursive procedure's locals that were
current at the time of the SPROUT, namely those of the first instantiation.
Sail will give you an error message whenever the global variables of a
process are deallocated but the process still exists. Usually, this means
that when the block in which the process procedure was declared is exited,
the corresponding process must be terminated (one can insure this by using a
small Cleanup procedure that will TERMINATE the fated process or JOIN it to
the current one -- see about Cleanup, page 23, Terminate, page 192, and
Join, page 194). When the process procedure has been declared inside a
recursive procedure, things become a bit more complex. As mentioned above,
the process takes its globals from the context of the Sprout statement.
Therefore, it is only in the instantiation of the recursive procedure that
executed the Sprout that trouble can occur. For example,
RECURSIVE PROCEDURE TENLEVEL (INTEGER I);
BEGIN "TROUBLE"
PROCEDURE FOO;
; COMMENT does nothing;
IF I=5 THEN SPROUT (NEW, FOO, SUSPHIM);
COMMENT sprouts FOO on the 5th
instantiation of TENLEVEL, then
immediately suspends it;
IF I<10 THEN TENLEVEL (I+1);
RETURN;
COMMENT assuming TENLEVEL is called
with I=0, it will do 10 instantiations,
then come back up;
END "TROUBLE";
TENLEVEL will nest 10 deep, then start returning. This means "TROUBLE" will
be exited five times will no ill effects. However, when Sail attempts to
exit "TROUBLE" a sixth time, it will be exiting a block in which a process
was sprouted and declared. It will generate the error message,
"Unterminated process dependent on block exited".
The construct DEPENDENTS (<blockname>), where <blockname> is a string
constant, produces a set of process items. The process items are those of
all the processes which depend on the current instance of the named block --
i.e. all processes whose process procedures obtain their global variables
from that block (via the position of the process procedure's declaration, or
occasionaly via the location of the Sprout in a nest of recursive procedure
instantiations). This construct may be used together with a CLEANUP
procedure (see page 23) to avoid having a block exit before all procedures
dependent on it have been terminated.
SPROUT APPLY SPROUTING MATCHING PROCEDURES SCHEDULING
If one Sprouts the same non-recursive procedure more than once (with
different process items, of course), the local variables of the procedure
are not copied. In other words, "X_5" in process A will store 5 in the same
location that "X_10" in process B would store 10. If such sharing of memory
is undesirable, declare the process procedure RECURSIVE, and then new
instances of the local variables of the procedure will be created with each
Sprout involving that procedure. Then "X" in process A will refer to a
different memory location than "X" in process B.
SPROUT APPLY
The <procedure call> in a SPROUT statement may be an APPLY construct. In
this case SPROUT will do the "right" thing about setting up the static
link for the APPLY. That is, "up-level" references by the process will be
made to the same variable instances that would be used if the APPLY did not
occur in a SPROUT statement. (See page 206.)
However, there is a glitch. The sprout mechanism is not yet smart enough to
find out the block of the declaration of the procedure used to define the
procedure item. It would be nice if it did, since then it could warn the
user when that block was exited and yet the process was still alive, and
thus potentially able to refer to deallocated arrays, etc. What the sprout
does instead is assume the procedure was declared in the outer block. This
may be fixed eventually, but in the meantime some extra care should be taken
when using apply in sprouts to avoid exiting a block with dependents.
Similarly, be warned that the "DEPENDENTS (<blockid>)" construct may not
give the "right" result for sprout applies.
SPROUTING MATCHING PROCEDURES
When a matching procedure is the object of a Sprout statement, the FAIL and
SUCCEED statements are interpreted differently than they would be were the
matching procedure called in a Foreach or as a regular procedure. FAIL is
equivalent to RESUME (CALLER (MYPROC), CVI (0)). SUCCEED is equivalent to
RESUME (CALLER (MYPROC), CVI (-1)).
SCHEDULING
One may change the status of a process between terminated, suspended and
ready/running with the TERMINATE, SUSPEND, RESUME, and JOIN constructs
discussed above, and the CAUSE and INTERROGATE constructs discussed in the
next chapter. This section will describe how the the status of processes
may change between ready and running.
Whenever the currently running process performs some action that causes its
status to change (to ready, terminated, or suspended) without specifying
which process is to be run next, the Sail process scheduler will be invoked.
It chooses a process from the pool of ready processes. The process it
chooses will be made the next running process. The scheduling algorithm is
essentially round robin within priority class. In other words, the
scheduler finds the highest priority class that has at least one ready
process in it. Each class has a list of processes associated with it, and
the scheduler choses the first ready process on the list. This process then
POLLING POINTS
becomes the running process and is put on the end of the list. If no
processes have ready status, the scheduler looks to see if the program is
enabled for any interrupts (see Interrupts, page 207). If the program is
enabled for some kind of interrupt that might still happen (not arithmetic
overflow, for instance), then the scheduler puts the program in interrupt
wait. After the interrupt is dismissed, the scheduler tries again to find a
ready process. If no interrupts that may still happen are enabled, and
there are no ready processes, the error message "No one to run" is issued.
The rescheduling operation may be explicitly invoked by calling the runtime
routine URSCHD, which has no parameters.
POLLING POINTS
Polling points are located at "clean" or "safe" points in the program;
points where a process may change from running to ready and back with no bad
effects. Polling points cause conditional rescheduling. A polling point is
an efficient version of the statement:
IF INTRPT NOPOLL THEN
BEGIN INTRPT_0; URSCHD END;
INTRPT is an external integer that is used to request rescheduling at the
next polling point. It is commonly set by the deferred interrupt routine
DFRINT (for all about deferred interrupts, see page 215) and by the clock
interrupt routine CLKMOD (for how to make processes time share, see page
214). The user may use INTRPT for his own purposes (carefully, so as not to
interfere with DFRINT or CLKMOD) by including the declaration "EXTERNAL
INTEGER INTRPT", then assigning INTRPT a non-zero value any time he desires
the next polling point to cause rescheduling. NOPOLL is another external
integer that is provided to give the user a means of dynamically inhibiting
polling points. For example, suppose one is time sharing using CLKMOD. In
one of the processes, a point is reached where it becomes important that the
processes not be swapped out until a certain tight loop is finished up. By
assigning NOPOLL (which was declared an EXTERNAL INTEGER) a non-zero value,
the polling points in the loop are efficiently ignored. Zeroing NOPOLL
restores normal time sharing.
A single polling point can be inserted with the statement POLL. The
construct
REQUIRE n POLLINGINTERVAL
where n is a positive integer, causes polling points to be inserted at safe
points in the code, namely: at the start of every statement provided that at
least n instructions have been emitted since the last polling point, after
every label, and at the end of every loop. If n 0 then no further polling
points will be put out until another Require n (n>0) PollingInterval is
seen.
Process Runtimes TERMINATE SUSPEND RESUME
16.4 Process Runtimes
******************************* TERMINATE ******************************;
TERMINATE (PROCITM)
The process for which PROCITM is the process item is terminated. It is
legal to terminate a terminated process. A terminated process is truly
dead. The item may be used over for anything you want, but after you have
used it for something else, you may not do a terminate on it. Termination
of a process causes all blocks of the process to be exited.
******************************** SUSPEND *******************************;
ITM _ SUSPEND (PROCITM)
The process for which PROCITM is the process item is suspended. If the
process being suspended is not the currently running process then the item
returned is ANY. In cases such as
X _ SUSPEND (MYPROC);
where the process suspends itself, it might happen that this process is made
running by a RESUME from another process. If so, then X receives the
SENDITM that was an argument to the RESUME.
One may suspend a suspended process. Suspending a terminated process will
cause an error message. If the process being suspended is the currently
running process (i.e. the process suspends itself), then the scheduler will
be called to find another process to run. A process may also be suspended
as the result of RESUME or JOIN.
********************************* RESUME *******************************;
RETITM _ RESUME (PROCITM,
SENDITM, OPTIONS(0))
RESUME provides a means for one process to restore a suspended process to
ready/running status while at the same time communicating an item to the
awakened process. It may also specify what its own status should be. It
may be used anywhere that an itemvar procedure is syntactically correct.
When a process which has suspended itself by means of a RESUME is
subsequently awakened by another resume, the SENDITM of the awakening
RESUME is used as the RETITM of the RESUME that caused the suspension. For
example, suppose that process A has suspended itself:
CALLER
STARTINFO _ RESUME (Z, NEEDTOOL);
If later a process B executes the statement
INFOFLAG _ RESUME (A, HAMMER);
then B will suspend itself and A will become the running process. A's
process information will be updated to remember that it was awakened by B
(so than the runtime routine CALLER can work). Finally, A's RESUME will
return the value HAMMER, which will be assigned to STARTINFO. If A had been
suspended by SUSPEND or JOIN then the SENDITM of B's RESUME is ignored.
A process that has been suspended in any manner will run from the point of
suspension onward when it is resumed.
OPTIONS is an integer used to change the effect of the RESUME on the current
process (MYPROC) and the newly resumed process.
BITS NAME DESCRIPTION
33-32 READYMEIf 33-32 is 1, then the current process will not be
suspended, but be made ready.
KILLME If 33-32 is 2, then the current process will be terminated.
IRUN If 33-32 is 3, then the current process will not be
suspended, but be made running. The newly resumed process
will be made ready.
34 This should always be zero.
35 NOTNOW If set, this bit makes the newly resumed process ready
instead of running. If 33-32 are not 3, then this bit causes
a rescheduling.
DEFAULT: If none of bits 35 to 32 are set, then the current process will be
suspended and the newly resumed process will be made running. At SUAI
include a REQUIRE "SYS:PROCES.DEF" SOURCEFILE in your program to get the
above bit names defined. Options may then be specified by simple addition,
e.g. KILLME + NOTNOW.
********************************* CALLER *******************************;
PROCITEM _ CALLER (PROCITEM2)
CALLER returns the process item of the process that most recently resumed
the process referred to PROCITEM2. PROCITEM2 must be the process item of an
DDFINT JOIN MYPROC PRISET
unterminated process, otherwise an error message will be issued. If
PROCITEM2's process has never been called, then the process item of the
process that sprouted PROCITEM2 is returned.
********************************* DDFINT *******************************;
DDFINT
A polling point is SKIPE INTRPT; PUSHJ P, DDFINT. DDFINT suspends the
current process (but leaves it ready to run), then calls the scheduler;
DDFINT is like SUSPEND (MYPROC, IRUN+NOTNOW).
********************************** JOIN ********************************;
JOIN (SETOFPROCESSITEMS)
The current process (the one with the JOIN statement in it) is suspended
until all of the processes in the set are terminated. WARNING: Be very
careful; you can get into infinite wait situations.
1. Do not join to the current process; since the current process is
now suspended, it will never terminate of its own accord.
2. Do not suspend any of the joined processes unless you are assured
they will be resumed.
3. Do not do an interrogate-wait in any of the processes unless you
are sure that the event it is waiting for will be caused (page
196).
****************************** MYPROC *******************************;
PROCITEM _ MYPROC
MYPROC returns the process item of the process that it is executed in. If
it is executed not inside a process, then MAINPI (the item for the main
process) is returned.
********************************* PRISET *******************************;
PRISET (PROCITM, PRIORITY)
PSTATUS URSCHD
PRISET sets the priority of the process specified by PROCITM (an item
expression that must evaluate to the process item of a non-terminated
process) to the priority specified by the integer expression PRIORITY.
Meaningful priorities are the integer between 1, the highest priority, to
15, the lowest priority. Whenever a rescheduling is called for, the
scheduler finds the highest priority class that has at least one ready
process in it, and makes the first process on that list the running process.
See about the scheduler, page 190.
******************************** PSTATUS *******************************;
STATUS _ PSTATUS (PROCITM)
PSTATUS returns an integer indicating the status of the process specified by
the item expression PROCITM.
-1 running
0 suspended
1 ready
2 terminated
********************************* URSCHD *******************************;
URSCHD
URSCHD is essentially the Sail Scheduler. When one calls URSCHD, the
scheduler finds the highest priority class that has at least one Ready
process in it. Each class has a list of processes associated with it, and
the scheduler choses the first ready process on the list. This process then
becomes the running process and is put on the end of the list. If no
processes have ready status, the scheduler looks to see if the program is
enabled for any interrupts. If the program is enabled for some kind of
interrupt that may still happen (not arithmetic overflow, for instance),
then the scheduler puts the program into interrupt wait. After the
interrupt is dismissed, the scheduler tries again to find a ready process.
If no interrupts that may still happen are enabled, and there are no ready
processes, the error message "No one to run" is issued.
EVENTS Syntax Introduction
SECTION 17
EVENTS
17.1 Syntax
<eventstatement>
::= <causestatement>
::= <interruptstatement>
<causestatement>
::= CAUSE ( <itemexpression> ,
<itemexpression> ,
<algebraicexpression> )
::= CAUSE ( <itemexpression> ,
<itemexpression> )
<interrogateconstruct>
::= INTERROGATE ( <itemexpression> ,
<algebraicexpression> )
::= INTERROGATE ( <itemexpression> )
::= INTERROGATE ( <listexpression> ,
<algebraicexpression> )
::= INTERROGATE ( <listexpression> )
17.2 Introduction
The Sail event mechanism is really a general message processing system which
provides a means by which an occurrence in one process can influence the
flow of control in other processes. The mechanism allows the user to
classify the messages, or "event notices", into distinct types ("event
types") and specify how each type is to be handled.
Any leap item may be used as an event notice. An event type is an item
which has been given a special runtime data type and datum by means of the
runtime routine:
MKEVTT (et)
where et is any item expression (except ANY or BINDIT). With each such
event type Sail associates:
Sail-defined Cause and Interrogate THE CAUSE STATEMENT
1. a "notice queue" of items which have been "caused" for this event
type.
2. a "wait queue" of processes which are waiting for an event of this
type.
3. procedures for manipulating the queues.
The principle actions associated with the event system are the CAUSE
statement and the INTERROGATE construct. Ordinarily these statements cause
standard Sail runtime routines to be invoked. However, the user may
substitute his own procedures for any event type (see User Defined Cause
and Interrogate procedures, page 199). The Cause and Interrogate statements
are here described in terms of the Sail system supplied procedures.
17.3 Sail-defined Cause and Interrogate
THE CAUSE STATEMENT
CAUSE (<event type>, <event notice>, <options>)
CAUSE (<event type>, <event notice> )
<event type> is an item expression, which must yield an event type item.
<event notice> is an item expression, and can yield any legal item.
<options> is an integer expression. If <options> is left out, 0 is used.
The Cause statement causes the wait queue of <event type> to be examined.
If it is non-empty, then the system will give the <event notice> to the
first process waiting on the queue (see about the WAIT bit in Interrogate,
below). Otherwise, <event notice> will be placed at the end of the notice
queue for <event type>.
The effect of Cause may be modified by the appropriate bits being set in the
options word:
BITS NAME DESCRIPTION
35 DONTSAVE Never put the <event item> on the notice queue. If
there is no process on the wait queue, this makes the cause
statement a no-op.
34 TELLALLSet the status of all processes waiting for this event to
READY.
33 RESCHEDULE Reschedule as soon as possible (i.e., immediately after
the cause procedure has completed executed).
DEFAULT: If bits 35 to 33 are 0, then the either a single process is
THE INTERROGATE CONSTRUCT - SIMPLE FORM THE INTERROGATE CONSTRUCT - SET FORM
awakened from the wait queue, or the event is placed on the notice queue.
The process doing the Cause continues to run. At SUAI, REQUIRE
"SYS:PROCES.DEF" SOURCEFILE to get the above bit names defined. Options
can then be constructed with simple addition, e.g. DONTSAVE + TELLALL. THE
INTERROGATE CONSTRUCT - SIMPLE FORM
<itemvar> _ INTERROGATE (<event type>, <options>)
<itemvar> _ INTERROGATE (<event type>)
<event type> is an item expression, which must yield an event type item.
<options> is an integer expression. If <options> is left out, 0 is used.
The notice queue of <event type> is examined. If it is non-empty, then the
first element is removed and returned as the value of the Interrogate.
Otherwise, the special item BINDIT is returned.
<options> modifies the effect of the interrogate statement as follows:
BITS NAME DESCRIPTION
35 RETAIN Leave the event notice on the notice queue, but still return
the notice as the value of the interrogate. If the process
goes into a wait state as a result of this interrogate, and
is subsequently awakened by a Cause, then the DONTSAVE bit
in the Cause statement will override the RETAIN bit in the
Interrogate if both are on.
34 WAIT If the notice queue is empty, then suspend the process
executing the interrogate and put its process item on the
wait queue.
33 RESCHEDULE Reschedule as soon as possible (i.e., immediately after
execution of the interrogate procedure).
32 SAYWHICH Creates the association EVENTTYPE <event notice>
<event type> where <event type> is the type of the event
returned. Useful with the set form of the Interrogate
construct, below.
DEFAULT: If bits 35 to 32 are 0, then the interrogate removes an event from
the event queue, and returns it. If the event queue is empty, BINDIT is
returned and no waiting is done; the process continues to run. At SUAI, use
a REQUIRE "SYS:PROCES.DEF" SOURCEFILE to get the names defined; use simple
addition to form options, e.g. RETAIN + WAIT. THE INTERROGATE CONSTRUCT -
SET FORM
<itemvar> _ INTERROGATE (<event type set>)
<itemvar> _ INTERROGATE (<event type set>, <options>)
<event type set> is a set of event type items. <options> is an integer
expression. If it is left out, 0 will be used.
User-defined Cause and Interrogate EVENT TYPE DATA STRUCTURE
The set form of interrogate allows the user to examine a whole set of
possible event types. This form of interrogate will first look at the
notice queues, in turn, of each event type in <event type set>. If one of
these notice queues is non-empty, then the first notice in that queue will
be remved and that notice will be returned as the value of the Interrogate.
If all the notice queues are empty, and WAITing is not specified in the
options word, then BINDIT will be returned. When the WAIT bit is set, the
process doing the interrogate gets put at the end of the wait queues of each
event type in <event type set>. Then, when a notice is finally available,
the process is removed from all of the wait queues before returning the
notice. Note that the option SAYWHICH provides a means for determining
which event type produced the returned notice.
17.4 User-defined Cause and Interrogate
By executing the appropriate runtime routine, the user can specify that some
non-standard action is to be associated with CAUSE or INTERROGATE for a
particular event type. Such user specified cause or interrogate procedures
may then manipulate the event data structure directly or by themselves
invoking the primitives used by the Sail Cause and Interrogate constructs.
User defined Cause and Interrogate are not for novice programmers (this is
an understatement).
EVENT TYPE DATA STRUCTURE
The datum of an event type item points to a six word block of memory. This
block contains the following information:
WORD NAME TYPE DESCRIPTION
0 NOTCQ LIST The list of all notices pending for this event type.
1 WAITQ LIST The list of all processes currently waiting for a
notice of this type.
2 --- --- Procedure specifier for the user specified cause
procedure (zero if system procedure is to be used).
3 --- --- Procedure specifier for the user specified
interrogate procedure (zero if system procedure is to be
used).
4 USER1 INTEGER Reserved for user.
5 USER2 INTEGER Reserved for user.
The appropriate macro definitions for these names (e.g. WAITQ (et) =
"MEMORY[ DATUM (et)+1, LIST ]" ) are included in the file
SUAISYS:PROCES.DEF.
USER CAUSE PROCEDURES
USER CAUSE PROCEDURES
A procedure to be used as a Cause procedure must have three formal value
parameters corresponding to the event type, event notice, and options of the
Cause. Such a procedure is associated with an event type by means of the
runtime SETCP:
SETCP (<event type>, <procedure specifier>);
where <event type> must yield an event type item and <procedure specifier>
is either a procedure name or DATUM (<procedure item>). For example:
PROCEDURE CX (ITEMVAR ET, EN; INTEGER OPT);
BEGIN
PRINT ("Causing ", EN,
" as an event of type ", ET);
CAUSE1 (ET, EN, OPT);
END;
...
SETCP (FOO, CX);
Now,
CAUSE (FOO, BAZ);
would cause CX (FOO, BAZ) to be called. This procedure would print out
"Causing BAZ as an event of type FOO" and then call CAUSE1. The runtime
CAUSE1 (ITEMVAR etype, enot; INTEGER opt) is the Sail runtime routine that
does all the actual work of causing a particular notice, enot, as an
instance of event type etype. It is essentially this procedure which is
replaced by a user specified cause procedure.
CAUSE1 uses an important subroutine which is also available to the user.
The integer runtime ANSWER (ITEMVAR evtype, evnot, processitem) is used
to wake up a process that has suspended itself with an interrogate. If the
process named by processitem is suspended, it will be set to ready status
and be removed from any wait queues it may be on. ANSWER will return as its
value the options bits from the interrogate that caused the process to
suspend itself. If the named process was not suspended, then ANSWER
returns an integer word with bit 18 (the '400000 bit in the right half =
NOJOY in SUAISYS:PROCES.DEF) set to 1. The evtype and evnot must be
included in case the SAYWHICH bit was on in the interrogate which caused
the suspension. ANSWER has no effect on the notice queue of evtype.
Frequently one may wish to use a cause procedure to re-direct some notices
to other event types. For instance:
USER INTERROGATE PROCEDURES
PROCEDURE CXX (ITEMVAR ET, EN; INTEGER OPT);
BEGIN ITEMVAR OTH; LABEL C;
IF redirecttest(ET, EN) THEN
FOREACH OTH | OTHERCAUSEETOTH DO
C: CAUSE1 (ET, EN, OPT)
ELSE CAUSE1 (ET, EN, OPT);
END;
In order to avoid some interesting race conditions, the implementation will
not execute the causes at C immediately. Rather, it will save ET, EN and
OPT, then, when the procedure CXX is finally exited, any such deferred
causes will be executed in the order in which they were requested. USER
INTERROGATE PROCEDURES
A user specified interrogate procedure must have two value formal parameters
corresponding to the two arguments to INTERROGATE and should return an item
as the value. The statement
SETIP (<event type>, <procedure specifier>);
where <event type> is an event type item, and <procedure specifier> is
either a procedure name or DATUM (<procedure item>), will make the specified
procedure become the new interrogate procedure for <event type>. For
instance:
ITEMVAR PROCEDURE IX (ITEMVAR ET; INTEGER OPT);
BEGIN ITEMVAR NOTI;
NOTI _ ASKNTC (ET, OPT);
PRINT ("Notice ", NOTI, " returned
from interrogation of ", ET);
RETURN (NOTI);
END;
...
SETIP (FOO, IX);
Now,
... _ INTERROGATE (FOO);
would cause NOTI to be set to the value of ASKNTC (FOO, 0). Then the
message "Notice BAZ returned from interrogate of FOO" would be printed and
IX would return NOTI as its value.
The runtime ASKNTC (ITEMVAR etype; INTEGER opt) is the Sail system routine
for handling the interrogation of a single event type. Essentially it is
the procedure being replaced by the user interrogate procedure.
In the case of multiple interrogations, Sail sets a special bit (bit 19 =
'200000 in the right half = MULTIN in SUAISYS:PROCES.DEF) in the options
word before doing any of the interrogates specified by the event type items
in the event type set. The effect of this bit, which will also be set in
the options word passed to a user interrogate procedure, is to cause ASKNTC
always to return BINDIT instead of ever waiting for an event notice. Then,
if ASKNTC returns BINDIT for all event types, Sail will cause the
interrogating process to Wait until its request is satisfied. If multin is
not set, then ASKNTC will do the WAIT if it is told to.
PROCEDURE VARIABLES Syntax Semantics ASSIGN
SECTION 18
PROCEDURE VARIABLES
18.1 Syntax
<assignstatement>
::= ASSIGN ( <itemexpr> ,
<procedurename> )
::= ASSIGN ( <itemexpr> , DATUM (
<itemexpr> ) )
<refitemconstruct>
::= REFITEM ( <expression> )
::= REFITEM ( VALUE <expression> )
::= REFITEM ( BIND <itemvar> )
::= REFITEM ( ? <itemvar> )
<applyconstruct>
::= APPLY ( <procedurename> )
::= APPLY ( <procedurename> ,
<arglistspecifier> )
::= APPLY ( DATUM ( <item> ) )
::= APPLY ( DATUM ( <item> ) ,
<arglistspecifier> )
<arglistspecifier>
::= <listexpression>
::= ARGLIST ( <exprlist> )
18.2 Semantics
ASSIGN
One may give an item a procedure "datum" using the ASSIGN statement.
ASSIGN accepts as its first argument an item expression (do not use ANY
or BINDIT). To this is bound the procedure identified by its name or
to the "datum" of another procedure item. The procedure may be any
type. However, the value it returns will only be accessible if the
procedure is an itemvar or item procedure. Apply assumes that whatever the
procedure left in AC 1, (the register used by all non-string procedures to
return a value) on exiting is an item number. Warning: a procedure is
REFITEM ARGLIST
no ordinary datum. Using DATUM on a procedure item except in the above
context will not work. Use APPLY instead.
REFITEM
Reference items are created at run time by the REFITEM construct and are
used principally in argument lists for the APPLY construct. The datum of a
reference item contains a pointer to a data object, together with type
information about that object. To create a reference item one executes
itm _ REFITEM (<expression>)
A NEW item is created. If the expression is (a) a simple variable or an
array element, then the address will be saved in the item's datum. If the
expression is (b) a constant or "calculated" expression, then Sail will
dynamically allocate a cell into which the value of the expression will be
saved, and the address of that cell will be saved in the datum of the item.
The item is then noted as having the datum type "reference" and returned as
the value of the REFITEM construct. One can slightly modify this
procedure by using one of the following variations.
itm _ REFITEM (VALUE <expression>)
In this case, a temp cell will always be allocated. Thus X_3;
XI_REFITEM (VALUE X); X_4; would cause the datum of XI to point at a cell
containing 3.
itm _ REFITEM (? itmvr)
itm _ REFITEM (BIND itmvr)
where itmvr must be an itemvar or an element of an itemvar array, will cause
the reference item's datum to contain information that Apply can use to
obtain the effect of using "? itmvr" or "BIND itmvr" as an actual parameter
in a procedure call.
ARGLIST
The ARGLIST construct assembles a list of "temporary" reference items that
will be deleted by APPLY after the applied procedure returns. Arguments to
ARGLIST may be anything legal for REFITEM. Thus
APPLY (proc, ARGLIST (foo, bar, VALUE baz))
is roughly equivalent to
tmplst _ {{REFITEM (foo), REFITEM (bar),
REFITEM (VALUE baz)~~;
APPLY (proc, tmplst);
WHILE LENGTH (tmplst) DO DELETE (LOP (tmplst));
but is somewhat easier to type. Note that the reference items created by
ARGLIST are just like those created by REFITEM, except that they are
marked so that APPLY will know to kill them.
APPLY
APPLY
APPLY uses the items in the <arglistspecifier>, together with the
environment information from the procedure item (or from the current
environment, if the procedure is named explicitly) to make the appropriate
procedure call. <arglistspecifier> is an ordinary list expression, except
that each element of the list must be a reference item. The elements of the
list will be used as the actuals in the procedure call. There must be at
least as many list elements as there are formals in the procedure. The
reference items must refer to an object of the same type as the
corresponding formal parameter in the procedure being called. (EXCEPTION:
if the formal parameter is an untyped itemvar or untyped itemvar array, then
the reference item may refer to a typed itemvar or itemvar array,
respectively.) At present, type checking (but not type coercion) is done.
If the formal parameter is a reference parameter, then a reference to the
object pointed to by the reference item is passed. If the formal parameter
is a value parameter, then the value of the object pointed to by the
reference item is used. Similarly, "?" formals are handled appropriately
when the reference item contains a "?" or "BIND" reference. If the
procedure to be called has no parameters, the <arglistspecifier> may be
left out.
Apply may be used wherever an itemvar procedure call is permitted. The
value returned will be whatever value would normally be returned by the the
applied procedure, but Apply will treat it as an item number. Care should
therefore be taken when using the result of Apply when the procedure being
invoked is not itself an itemvar procedure, since this may cause an invalid
item number to be used as a valid item (for instance, in a MAKE). Recall
that when a typed procedure (or an Apply) is called at statement level, the
value it returns is ignored. Here is an example of the use of APPLY.
BEGIN
LIST L;INTEGER XX;
INTEGER ITEMVAR YY;ITEMVAR ZZ;
REAL ARRAY AA[1:2];
PROCEDURE FOO (INTEGER X;
ITEMVAR Y,Z; REAL ARRAY A);
BEGIN
Y _ NEW (X);
Z _ NEW (A);
A[X]_3;
END;
XX_0;
L _ {{REFITEM (XX), REFITEM (YY),
REFITEM (ZZ), REFITEM (AA)~~;
XX _ 2; AA[1] _ AA[2] _ 1;
APPLY (FOO, L);
COMMENT Y now contains an item whose
datum is 2, Z contains an item whose
datum is the array (1.0, 1.0),
A[1]=1.0, and A[2]=3.0.;
END;
The variables accessed by a procedure called with APPLY may not always
be what you would think they were. Temporary terminology: the "environment"
of a procedure is the collection of variables, arrays and procedures
accessible to it. "Environment" is not meant to include the state of
the associative store or the universe of items. The environment of a
procedure item is the environment of the ASSIGN, and that environment will
be used regardless of the position of the APPLY. Since procedure items
are untouched by block exits, yet environments are, it is possible to
Apply a procedure item when its environment is gone; Sail catches most of
these situations and gives an error message. Consider the following
example:
BEGIN
ITEM P; LABEL L;
RECURSIVE PROCEDURE FOO (INTEGER J);
BEGIN "FOO"
INTEGER I;
PROCEDURE BAZ;
PRINT ("J=", J, " I=", I);
IF J=1 THEN
BEGIN
I_2;
ASSIGN (P, BAZ);
FOO (-1);
END
ELSE APPLY (DATUM (P));
END "FOO";
FOO (1);
L: APPLY (DATUM (P)); COMMENT will cause a
runtime error -- see discussion below;
END
The effect of the program is to Assign Baz to P on the first
instantiation of Foo, then Apply P on the second (recursive)
instantiation. However, the environment at the time of the Assign
includes {I=2, J=1~ but the environment at the time of the Apply
includes {I=0, J=-1~ instead. At the time of the Apply, Baz is
executed with the environment from the time of the Assign, and will print
out
J=1 I=2
The Apply at L will cause a runtime error message because the
environment of the Assign has been destroyed by the exiting of Foo.
INTERRUPTS Introduction
SECTION 19
INTERRUPTS
19.1 Introduction
The interrupt facilities of Sail are based on the interrupt facilities
provided by the operating system under which Sail is running. For programs
running at SUAI or on TENEX this results in satisfactory interrupt
operation. TOPS-10 programs are at a distinct disadvantage because the
operating system does not prevent interrupt handlers from being interrupted
themselves. At SUAI the Sail system uses new-style interrupts [Frost];
programs may also enable for old-style interrupts and the two will work
together provided that the same condition is not enabled under both kinds.
On TENEX the pseudointerrupt (PSI) system is used; programs may use the
interrupt system independently of Sail. Only interrupt functions pertaining
to the current fork are provided. TOPS-10 interrupts are directly tied to
the APRENB system; Sail and non-Sail use do not mix.
Sail gives control to the user program as soon as the operating system
informs the Sail interrupt handler. This can be dangerous because the Sail
runtime system may be in the middle of core allocation or garbage
collection. Therefore Sail provides a special runtime DFRINT which can
receive control in the restricted environment of an interrupt. DFRINT
records the fact that an interrupt happened and that a particular user
procedure is to be run at the next polling point (page 191), when the
integrity of all runtime data structures is (normally) assured. If the Sail
interrupt handler passes control to DFRINT then the user procedure (which is
run at the next polling point) is called a "deferred interrupt procedure",
even though the only connection it has with interrupts is the special status
and priority given to it by the Sail Process machinery. If DFRINT is not
used then the user procedure to which the Sail interrupt handler passes
control is called an "immediate interrupt procedure". (This is orthogonal
to the TENEX distinction between immediate and deferred TTY interrupts.)
To use interrupts a program must first tell Sail what procedure(s) to run
when an interrupt happens. The routines INTMAP and PSIMAP perform this
task. Deferred interrupts use the Sail process machinery (page 186), so
INTSET is used to sprout the interrupt process. Then the operating system
must be told to activate (and deactivate) interrupts for the desired
conditions. ENABLE and DISABLE are used by the program to tell Sail, which
tells the operating system.
A good knowledge of the interrupt structure of the operating system which
you are trying to use should be considered a prerequisite for this chapter.
Interrupt Routines ATI, DTI DFR1IN DFRINT
19.2 Interrupt Routines
******************************** ATI, DTI ******************************;
ATI (PSICHAN, CODE);
DTI (PSICHAN, CODE)
(TENEX only.) CODE is associated or dissociated with PSICHAN, using the
appropriate JSYS. Executing ATI is an additional step (beyond ENABLE) which
is necessary to receive TENEX TTY interrupts.
********************************* DFR1IN *******************************;
DFR1IN (AOBJNPTR)
DFR1IN is the procedure used by DFRINT to record the interrupt and the
AOBJNPTR. Thus DFRINT is (partially) equivalent to
SIMPLE PROCEDURE DFRINT; BEGIN
DFR1IN (<AOBJNPTR specified
to INTMAP>) END;
To have more than one procedure run (deferred) as the result of an
interrupt, a program may use DFR1IN to record the AOBJNPTRs explicitly.
Example:
SIMPLE PROCEDURE ZORCH;
BEGIN
DFR1IN (<AOBJN pointer for FOO call>);
DFR1IN (<AOBJN pointer for BAZ call>);
END;
...
INTMAP (INTTTYINX, ZORCH, 0);
ENABLE (INTTTYINX);
Both FOO and BAZ will be run (deferred) as the result of INTTTYINX
interrupt.
********************************* DFRINT *******************************;
DFRINT
DFRINT is a predeclared simple procedure which handles the queueing of
deferred interrupts. Specify DFRINT to INTMAP for each interrupt which
DISABLE, ENABLE INTMAP
will be run as a Sail deferred interrupt. When run as the result of an
interrupt, DFRINT grabs the AOBJNPTR pointer specified to INTMAP (or
PSIMAP) and copies the block along with other useful information into the
circular deferred interrupt buffer. (See INTTBL.) DFRINT then changes the
status of the interrupt process INTPRO from suspend to ready, and turns on
the global integer INTRPT.
**************************** DISABLE, ENABLE ***************************;
DISABLE (INDEX);
ENABLE (INDEX)
Sail tells the operating system to ignore (DISABLE) or to send to the
program (ENABLE) interrupts for the condition specified by INDEX. INDEX is
a bit number (0-35) which varies from system to system; consult [SysCall].
INDEX is sometimes called a "PSI channel" on TENEX.
********************************* INTMAP *******************************;
INTMAP (INDEX, PROC, AOBJNPTR)
(TENEX users should see PSIMAP.) The routine INTMAP specifies that the
simple procedure PROC is to be run whenever the Sail interrupt handler
receives an interrupt corresponding to the condition specified by INDEX. A
separate INTMAP must be executed for each interrupt condition. If the same
INDEX is specified on two calls to INTMAP then the most recent call is the
one in effect. PROC must be a simple procedure with no formal parameters.
If PROC is a user procedure then PROC is run as a Sail immediate interrupt.
AOBJNPTR should be zero unless DFRINT is specified for PROC. If PROC is
DFRINT (and thus will be a Sail deferred interrupt) then AOBJNPTR gives the
length and location of a block of memory describing a procedure call. Such
a block has the form
<number of words in the block>
<1st parameter to the procedure>
<second parameter to the procedure>
...
<last parameter to the procedure>
-1,,<address of the procedure>
and an AOBJNPTR to it has the form
-<number of words>,,<starting address>.
Here is an example in which FOO (I, J, K) is to be called as a deferred
interrupt.
INTSET INTTBL
PROCEDURE FOO (INTEGER i, j, k); ...;
...
SAFE INTEGER ARRAY FOOBLK [1:5];
ITEMVAR IPRO; COMMENT for process item of INTPRO;
...
FOOBLK [1] _ 5;
FOOBLK [2] _ I;
FOOBLK [3] _ J;
FOOBLK [4] _ K;
FOOBLK [5] _ (-1 LSH 18)+LOCATION (FOO);
...
INTSET (IPRO _ NEW, 0); COMMENT sprout INTPRO;
INTMAP (INTTTIINX, DFRINT,
(-5 LSH 18) + LOCATION (FOOBLK[1]));
ENABLE(INTTTIINX)
NOTE: The procedure (FOO in this case) must not be declared inside any
process except the main program. Otherwise, its environment will not be
available when INTPRO runs. However, there is a rather complex way to get
around this by using <environment>,,PDA as the last word of the calling
block. See a Sail hacker if you must do this and don't know what
<environment> or PDA mean.
********************************* INTSET *******************************;
INTSET (ITM, OPTIONS)
INTSET sprouts the interrupt process INTPRO with process options OPTIONS;
see page 187. The default priority of INTPRO is zero; this is the highest
possible priority and no other process may have priority zero. Thus INTPRO
is sure to be run first at any polling point. ITM must be an item; it will
become the process item of INTPRO, the interrupt process. INTSET must be
called before any deferred interrupts are used.
********************************* INTTBL *******************************;
INTTBL (NEWSIZE)
The buffer used to queue deferred interrupts is initially 128 locations
long. The queue has not been know to overflow except for programs which do
not POLL very often. INTTBL changes the buffer size to NEWSIZE. Do not
call INTTBL if there are any deferred interrupts pending; wait until they
have all been executed.
PSIMAP Immediate Interrupts IN SUAI Sail
********************************* PSIMAP *******************************;
PSIMAP (PSICHAN, PROC,
AOBJNPTR, LEVEL)
(TENEX only.) This routine is the same as INTMAP except that LEVEL may be
specified. ROUTINE is executed at interrupt level LEVEL. (TENEX INTMAP is
equivalent to PSIMAP ( , , ,3).) PROC and AOBJNPTR have the same meaning as
for INTMAP.
19.3 Immediate Interrupts
Do not access, create, or destroy strings, records, arrays, sets, or lists.
If these data structures are needed then use deferred interrupts.
To set up an immediate interrupt say
INTMAP (<index>, <simple procedure name>, 0);
ENABLE (<index>)
or on TENEX,
PSIMAP (<PSIchan>, <simple procedure name>, 0, <PSIlev>);
ENABLE (<PSIchan>)
where <index> is a code for the interrupt condition. To turn off an
interrupt use
DISABLE (<index>)
The system will not provide user interrupts for the specified condition
until another ENABLE statement is executed.
IN SUAI Sail
A procedure specified by an INTMAP statement will be executed at user
interrupt level. A program operating in this mode will not be interrupted,
but must finish whatever it is doing within 8/60 ths of a second. It may
not do any UUOs that can cause it to be rescheduled. Also, the accumulators
will not be the same ones as those that were in use by the regular program.
Certain locations are set up as follows:
ACs 1-6Set up by the system as in [Frost].
AC '15 (USER) Address of the Sail user table.
AC '16 (SP) A temporary string push down stack pointer (for the
foolhardy who chose to disregard the warnings about strings in
immediate interrupts).
AC '17 (P) A temporary push down stack pointer.
IN TOPS-10
XJBCNI (declared in SYS:PROCES.DEF as an external integer.) Bit mask
with a bit on corresponding to the current condition.
XJBTPC (declared in SYS:PROCES.DEF as an external integer.) Full PC
word of regular user level program.
The interrupt will be dismissed and the user program resumed when the
interrupt procedure is exited. For more information on interrupt level
programming consult [Frost].
IN TOPS-10
The interrupt handler again will decode the interrupt condition and call the
appropriate procedure. Since there is no "interrupt level", the interrupt
procedure must not itself generate any interrupt conditions, since this will
cause Sail to lose track of where in the user program it was interrupted
(trapped).
Also, the Sail interrupt module sets up some temporary accumulators and
JOBTPC:
AC '10 index of the interrupt condition.
AC '15 (USER) Address of the Sail user table.
AC '16 (SP) A temporary string push down list. Beware.
AC '17 (P) A temporary push down pointer.
JOBTPC (an external integer) Full PC word of regular user program.
The "real" acs -- i.e., the values of all accumulators at the time the trap
occurred -- are stored in locations APRACS to APRACS+17. Thus you can get
at the value of accumulator x by declaring APRACS as an external integer and
referring to MEMORY [LOCATION (APRACS)+x]. When the interrupt procedure is
exited the acs are restored from APRACS to APRACS+17 and the Sail interrupt
handler jumps to the location stored in JOBTPC (which was set by the
operating system to the location at which the trap occurred). Thus, if you
want to transfer control to some location in your user program, a way to do
it is to have an interrupt routine like:
IN TENEX Sail GTRPW RTIW, STIW
SIMPLE PROCEDURE IROUT;
BEGIN
EXTERNAL INTEGER JOBTPC;
:
JOBTPC_LOCATION (GTFOO);
COMMENT GTFOO is a non-simple procedure
that contains a GO TO FOO, where FOO
is the location to which control
is to be passed. This allows the
"go to solver" to be called and clean
up any unwanted procedure activations.;
END;
WARNING: this does not work very well if you were interrupted at a bad time.
IN TENEX Sail
Sail initialization does a SIR, setting up the tables to external integers
LEVTAB and CHNTAB, then an EIR to turn on the interrupt system. PSIMAP
fills the appropriate CHNTAB location with XWD LEV,LEVROU, where LEVROU is
the address of the routine that handles the interrupts for level LEV.
LEVROU saves the accumulators in blocks PS1ACS, PS2ACS, and PS3ACS, which
are external integers, for levels 1 through 3 respectively. Thus for a
level 3 interrupt accumulator x can be accessed by MEMORY [LOCATION (PS3ACS)
+ x]. The PC can be obtained by reading the LEVTAB address with the RIR
JSYS. Temporary stacks are set up for both immediate and deferred
interrupts.
See page 145 for an example of TENEX immediate interrupts. The functions
GTRPW, RTIW, STIW provide for some of the information set up in ACs under
SUAI or TOPS-10.
********************************* GTRPW ********************************;
STATUS _ GTRPW (FORK);
The trap status of FORK is returned, using the GTRPW JSYS.
******************************* RTIW, STIW *****************************;
AC1 _ RTIW (PSICHAN, @AC2);
STIW (PSICHAN, AC2, AC3);
The indicated JSYS is performed.
Clock Interrupts PSIDISMS
19.4 Clock Interrupts
(This feature is currently available only in SUAI Sail and TENEX Sail.)
Clock interrupts are a kind of immediate interrupt used to approximate time
sharing among processes. Every time the scheduler decides to run a process
it copies the procedure's time quantum (see all about quantums of processes,
page 187) into the Sail user table location TIMER. Consider the following
procedure, which is roughly equivalent to the one predeclared in Sail:
SIMPLE PROCEDURE CLKMOD;
IF (TIMER_TIMER-1) 0 THEN INTRPT_-1;
To time share several ready processes one should include polling points in
the relevant process procedures and should execute the following statements:
INTMAP (INTCLKINX, CLKMOD, 0);
ENABLE (INTCLKINX);
or on TENEX
PSIMAP (1, CLKMOD, 0, 3);
ENABLE (1);
PSIDISMS (1, 1000/60);
The macro SCHEDULEONCLOCKINTERRUPTS defined in SUAISYS:PROCES.DEF is
equivalent to these statements. When the time quantum of a process is
exceeded by the number of clock ticks since it began to run, the integer
INTRPT is set, and this causes the next polling point in the process to
cause a rescheduling (see about rescheduling and INTRPT on page 190). The
current running process will be made ready, and the scheduling algorithm
chooses a ready process to run.
In TENEX Sail clock interrupts are handled differently. Since TENEX does
not directly provide for interrupting user processes on clock ticks, an
inferior fork is created which periodically generates the interrupts.
******************************** PSIDISMS ******************************;
PSIDISMS (PSICHAN, MSTIME)
An inferior fork is created which interrupts the current fork every MSTIME
milliseconds of real time. The inferior is approximately
WAIT: MOVE 1,MSTIME ;HOW LONG
DISMS ;GO AWAY
MOVEI 1,-1 ;HANDLE TO SUPERIOR
MOVE 2,[bit mask];SELECTED CHANNEL
IIC ;CAUSE AN INTERRUPT
JRST WAIT ;CONTINUE
PSIRUNTM KPSITIME Deferred Interrupts
******************************** PSIRUNTM ******************************;
PSIRUNTM (PSICHAN, MSTIME)
The current fork is interrupted every MSTIME milliseconds of runtime. The
inferior is approximately
WAIT: MOVE 1,MSTIME ;HOW LONG
DISMS
MOVEI 1,-1 ;SUPERIOR FORK
RUNTM ;RUNTIME OF SUPERIOR
CAMGE 1,NEXTTIME ;READY?
JRST WAIT ;NO
ADD 1,MSTIME
MOVEM 1,NEXTTIME ;RECHARGE
MOVEI 1,-1 ;SUPERIOR
MOVE 2,[bit mask];SELECTED CHANNEL
IIC ;CAUSE INTERRUPT
JRST WAIT
******************************** KPSITIME ******************************;
KPSITIME (PSICHAN)
Discontinues clock interrupts on PSICHAN.
Several channels can be interrupted by PSIRUNTM or PSIDISMS, each with
different timing interval.
19.5 Deferred Interrupts
Deferred interrupts use the Sail Process machinery (page 186) to synchronize
the Sail runtime system with the running of user procedures in response to
interrupts. The routine INTSET sprouts the interrupt process INTPRO, the
process which eventually does the calling of deferred interrupt procedures.
This process is special because it is (ordinarily) guaranteed to be the
first process run after a rescheduling. (See page 191 and page 195 for
information on rescheduling.) When DFRINT runs as the result of an
interrupt, it copies the calling block (specified to INTMAP with the
AOBJNPTR) into the deferred interrupt buffer and turns on the global
integer INTRPT. At the next polling point the process supervisor will
suspend the current process and run INTPRO. INTPRO calls the procedures
specified by the calling blocks in the deferred interrupt buffer, turns off
INTRPT, and suspends itself. The process scheduler then runs the process of
highest priority.
One very common use of deferred interrupts is to cause an event soon after
some asynchronous condition (say, TTY activation) occurs. This effect may
be obtained by the following sequence:
INTSET (IPRO_NEW, 0); COMMENT this will cause
the interrupt process to be sprouted and
assigned to IPRO. This process will execute
procedure INTPRO and will have priority zero
(the highest possible).;
:
INTMAP (<index>, DFRINT,
DFCPKT (0, <event type>, <event notice>,
<cause options>));
:
ENABLE (<index>);
In SUAISYS:PROCES.DEF is the useful macro
DEFERREDCAUSEONINTERRUPT (<index>,
<event type>, <notice>, <options>)
which may be used to replace the INTMAP statement.
The following program illustrates how deferred interrupts on TENEX can be
accomplished.
BEGIN REQUIRE 1 NEWITEM;
ITEMVAR IPRO; COMMENT for process item;
PROCEDURE FOO (INTEGER I, J);
PRINT ("HI ", I, " ", J);
INTEGER ARRAY FOOBLK[1:4];
FOOBLK[1] _ 4; COMMENT # words;
FOOBLK[2] _ 12; COMMENT arguments;
FOOBLK[3] _ 13;
FOOBLK[4] _ -1 LSH 18 + LOCATION (FOO);
INTSET (IPRO _ NEW, 0);
PSIMAP (1, DFRINT,
-4 LSH 18 + LOCATION (FOOBLK[1]), 3);
ENABLE (1); ATI (1, "Q"-'100);
DO BEGIN OUTCHR ("."); POLL; END UNTIL FALSE;
END;
The program prints dots, interspersed with "HI 12 13" for each control-Q
typed on the console. Whenever a control-Q is typed, DFRINT buffers the
request and makes INTPRO ready to run; then DFRINT DEBRKs (in the sense of
the DEBRK JSYS) back to the interrupted code. At Sail user level the POLL
THE DEFERRED INTERRUPT PROCESS - INTPRO
statement causes the process scheduler to run INTPRO, where the deferred
interrupt calling block (which was copied by DFRINT) is used to call FOO.
THE DEFERRED INTERRUPT PROCESS - INTPRO
INTPRO first restores the following information which was stored by DFRINT
at the time of the interrupt.
LOCATION CONTENTS
USER The base of the user table (GOGTAB).
AC 1 Status of spacewar buttons.
AC 2 Your job status word (JBTSTS). See [Frost].
IJBCNI(USER) XJBCNI (i.e., JOBCNI) at time of interrupt.
IJBTPC(USER) XJBTPC (i.e., JOBTPC) at time of interrupt.
IRUNNR(USER) Item number of running process at time of interrupt.
Then INTPRO calles the procedure described by the calling block. When the
procedure is finished, INTPRO looks to see if the deferred interrupt buffer
has any more entries left. If it does, INTPRO handles them in the same
manner. Otherwise INTPRO suspends itself and the highest priority ready
process takes over.
LEAP RUNTIMES Types and Type Conversion TYPEIT
SECTION 20
LEAP RUNTIMES
We will follow the same conventions for describing Leap execution time
routines as were used in describing the runtimes of the Algol section of
Sail (see page 62).
20.1 Types and Type Conversion
********************************* TYPEIT *******************************;
CODE _ TYPEIT (ITM);
The type of the datum linked to an item is called the type of an item. An
item without a datum is called untyped. TYPEIT is an integer function which
returns an integer CODE for the type of the item expression ITM that is its
argument. The codes are:
0 - item deleted or never allocated
1 - untyped
2 - Bracketed Triple item
3 - string
4 - real
5 - integer
6 - set
7 - list
8 - procedure item
9 - process item
10 - event item
11 - context item
12 - reference item
13 - record pointer
14 - label
15 - record class
23 - string array
24 - real array
25 - integer array
26 - set array
27 - list array
31 - context array
33 - record pointer array
37 - error (the runtime screwed up)
The user is encouraged to use TYPEIT. It requires the execution of only a
few machine instructions and can save considerable debugging time.
CVSET CVLIST CVN and CVI MKEVTT
********************************* CVSET ********************************;
SET _ CVSET (LIST)
CVSET returns a set given a list expression by removing duplicate
occurrences of items in the list, and reordering the items into the order of
their internal integer representations.
********************************* CVLIST *******************************;
LIST _ CVLIST (SET)
CVLIST returns a list given a set expression. It executes no machine
instructions, but merely lets you get around Sail type checking at compile
time.
****************************** CVN and CVI *****************************;
INTEGR _ CVN (ITM);
ITM _ CVI (INTEGR)
CVN returns the integer that is the internal representation of the item that
is the the value of the item expression ITM. CVI returns the item that is
represented by the integer expression INTEGR that is its argument. Legal
item numbers are between (inclusively) 1 and 4095, but you'll get in trouble
if you CVI when no item has been created with that integer as its
representation. Absolutely no error checking is done. CVI is for daring
men. See about item implementation, page 157, for more information about
the internal representations of items.
********************************* MKEVTT *******************************;
MKEVTT (ITEM)
MKEVTT will convert its item argument to an event type item. The old datum
will be overwritten. The type of the item will now be "event type". Any
item except an event type item may be converted to an event type item by
MKEVTT.
Make and Erase Breakpoints BRKERS, BRKMAK, BRKOFF Pname Runtimes CVIS
20.2 Make and Erase Breakpoints
************************* BRKERS, BRKMAK, BRKOFF ***********************;
BRKMAK (BREAKPTPROC);
BRKERS (BREAKPTPROC);
BRKOFF
In order to give the programmer some idea of what is going on in the
associative store, there is a provision to interrupt each MAKE and ERASE
operation, and enter a breakpoint procedure. The user can then do whatever
he wants with the three items of the association being created or destroyed.
ERASE Foo ANY ANY will cause the breakpoint procedure to be activated
once for each association that matches the pattern. MAKE it1 it2 [it3
it4 it5] will cause the breakpoint procedure to be activated twice.
The user's breakpoint procedures must have the form:
PROCEDURE Breakptproc (ITEMVAR a, o, v )
If the association being made or erased is AOV, then directly before doing
the Make or Erase, Breakptproc is called with the items A, O, and V for the
formals a, o, and v.
To make the procedure Breakptproc into a breakpoint procedure for MAKE,
call BRKMAK with Breakptproc as a parameter. To make the procedure
Breakptproc into a breakpoint procedure for ERASE, call BRKERS with
Breakptproc as its parameter. To turn off both breakpoint procedures, call
BRKOFF with no parameters.
NOTE: BRKMAK, BRKERS and BRKOFF are not predeclared. The user must include
the declarations:
EXTERNAL PROCEDURE BRKERS (PROCEDURE BP);
EXTERNAL PROCEDURE BRKMAK (PROCEDURE BP);
EXTERNAL PROCEDURE BRKOFF
20.3 Pname Runtimes
********************************** CVIS ********************************;
"PNAME" _ CVIS (ITEM, @FLAG)
The print name of ITEM is returned as a string. Items have print names only
CVSI DELPNAME NEWPNAME Other Useful Runtimes LISTX
if one includes a REQUIRE n PNAMES statement in his program, where n is an
estimate of the number of pnames the program will use. An Item's print name
is the identifier used to declare it, or that pname explicitly given it by
the NEWPNAME function (see below). FLAG is set to False (0) if the
appropriate string is found. Otherwise it is set to TRUE (-1), and one
should not put great faith in the string result.
********************************** CVSI ********************************;
ITEM _ CVSI ("PNAME", @FLAG)
The Item whose pname is the same as the string argument PNAME is returned
and FLAG is set to FALSE if such an ITEM exists. Otherwise, something very
random is returned, and FLAG is set to TRUE.
******************************* DELPNAME ******************************;
DELPNAME (ITEM)
This function deletes any string PNAME associates with this ITEM.
******************************* NEWPNAME ******************************;
NEWPNAME (ITEM, "STRING")
This function assigns to the Item the name "STRING". Don't perform this
twice for the same Item without first deleting the previous one. The
corresponding name or Item may be retrieved using CVIS or CVSI (see above).
The NULL string is prohibited as the second argument.
20.4 Other Useful Runtimes
********************************* LISTX ********************************;
VALUE _ LISTX (LIST, ITEM, N)
The value of this integer function is 0 if the ITEM (an item expression)
does not occur in the list at least N (an integer expression) different
times in the LIST (a list expression). Otherwise LISTX is the index of the
Nth occurrence of ITEM in LIST. For example,
FIRST, SECOND, THIRD ISTRIPLE LOP COP
LISTX ({{Foo, Baz, Garp, Baz~~, Baz, 2) is 4.
************************** FIRST, SECOND, THIRD ************************;
ITEM _ FIRST (BRACTRIPITEM);
ITEM _ SECOND (BRACTRIPITEM);
ITEM _ THIRD (BRACTRIPITEM)
The Item which is the FIRST, SECOND, or THIRD element of the association
connected to a bracketed triple item (BRACTRIPITEM) is returned. If the
item expression BRACTRIPITEM does not evaluate to a bracketed triple, an
error messages issues forth.
******************************** ISTRIPLE ******************************;
RSLT _ ISTRIPLE (ITM)
If ITM is a bracketed triple item then ISTRIPLE returns TRUE; otherwise it
returns FALSE. ISTRIPLE (ITM) is equivalent to (TYPEIT (ITM) = 2).
********************************** LOP *********************************;
ITEM _ LOP (@SETVARIABLE);
ITEM _ LOP (@LISTVARIABLE)
LOP will remove the first item of a set or list from the set or list, and
return that item as its value. Note that the argument must be a variable
because the contents of the set or list is changed. If one LOPs an empty
set or a null list, an error message will be issued.
********************************** COP *********************************;
ITEM _ COP (SETEXPR);
ITEM _ COP (LISTEXPR)
COP will return the first item of the set or list just as LOP (above) will.
However, it will NOT remove that item from the set or list. Since the set
or list will be unchanged, COP's argument may be a set or list expression.
As with LOP, an error message will be returned if one COPs an empty set or a
null list.
LENGTH SAMEIV Runtimes for User Cause and Interrogate Procedures SETCP AND SETIP
********************************* LENGTH *******************************;
VALUE _ LENGTH (SETEXPR);
VALUE _ LENGTH (LISTEXPR)
LENGTH will return the number of items in that set or list that is its
argument. LENGTH (S) = 0 is a much faster test for the null set or list
that S = PHI or S = NIL.
********************************* SAMEIV *******************************;
VALUE _ SAMEIV (ITMVAR1, ITMVAR2)
SAMEIV is useful in Matching Procedures to solve a particular problem that
arises when a Matching Procedure has at least two ? itemvar arguments. An
example will demonstrate the problem:
FOREACH X | Matchingproc ( X, X ) DO ...;
FOREACH X, Y | Matchingproc ( X, Y ) DO ... ;
Clearly, the matching procedure with both arguments the same may want to do
something different from the matching procedure with two different Foreach
itemvars as its arguments. However, there is no way inside the body of the
matching procedure to differentiate the two cases since in both cases both
itemvar formals have the value BINDIT. SAMEIV will return True only in the
first case, namely 1) both of its arguments are ? itemvar formals to a
matching procedure, 2) both had the same Foreach itemvar passed by reference
to them. It will return False under all other conditions, including the
case where the Foreach itemvar is bound at the time of the call (so it is
not passed by reference, but its item value is passed by value to both
formals).
20.5 Runtimes for User Cause and Interrogate Procedures
**************************** SETCP AND SETIP ***************************;
SETCP (ETYPE, PROCNAME);
SETCP (ETYPE, DATUM (PROCITEM));
SETIP (ETYPE, PROCNAME);
SETIP (ETYPE, DATUM (PROCITEM))
SETCP and SETIP associate with the event type specified by the item
expression ETYPE a procedure specified by its name or the datum of a
procedure item expression.