perm filename SAIL.PUB[DOC,AIL]23 blob sn#202935 filedate 1976-02-26 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00019 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00003 00002	.REQUIRE "PUBMAC[DOC,AIL]" SOURCE_FILE
C00006 00003	.SEC |NUMERICAL ROUTINES|
C00014 00004	.SEC |NEW PROCESS FEATURES|
C00020 00005	.SEC |ERROR HANDLING|
C00027 00006	.SEC |INEXHAUSTIBLE STRING SPACE|
C00032 00007	.SEC |RECORD STRUCTURES|
C00048 00008	.SS |INTERNAL REPRESENTATIONS |
C00062 00009	.SEC |PRINT|
C00081 00010	.SEC |SAVE/CONTINUE|
C00085 00011	.SEC |MISCELLANEOUS NEW FEATURES|
C00089 00012	.SS |ARRCLR|
C00093 00013	.SS|TTYUP|
C00097 00014	.ss|ASH|
C00101 00015	.ss BREAK TABLES
C00106 00016	.ss NEW SWITCHES
C00111 00017	.SS EDFILE
C00118 00018	.sec |MINOR CORRECTIONS TO AIM-204|
C00119 00019	.MAN_BACK
C00121 ENDMK
C⊗;
.REQUIRE "PUBMAC[DOC,AIL]" SOURCE_FILE;
.XGPCOL;
.TITLEPG←TRUE;
.ONECOL
.DOCNAME←"SAIL Addendum #1"
.STAND_FRONT
.SEC |INTRODUCTION|
The following short manual describes the changes that have
happened to SAIL since the publishing of the Manual in July 1973. 
It accurately reflects the state of SAIL, version 18, which was
last modified on November 23, 1975.  The reader should be warned that many
of these new features were designed for veteran SAIL hackers.

The reader may also want to refer to the following documents, which 
are usually kept updated.
.LIST(20,0)

MACLIE.WRU[DOC,AIL]\A summary of commonly made errors when using macros and
what to do about them.

TUTOR.DOC[DOC,AIL]\In introduction to LEAP.

LEAP.WRU[DOC,AIL]\A detailed description of the runtime environment of LEAP
for the hardy few who want to interface to LEAP in assembly language. 

SAIL.DOC[AIM,DOC]\The July '73 manual (AIM-204) in LPT form. Warning: this version
is the Stanford character set.  It is also almost 300 pages long.  You can
get a 120 page version, set in two columns of nice type from the National
Technical Information Service, Springfield, Virginia 22151.

LIES[DOC,AIL]\This file contains the know mistakes in the Manual. Soon it will
also contain the known mistakes in this document.
.end
.SEC |NUMERICAL ROUTINES|

A collection of numerical routines has been added to SAIL.  These
are pre-declared in the compiler, and are loaded
from the standard SAIL library.  The functions are quite standard;
following are the equivalent definitions:

.LIST(4,0)
1.\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.
.NOFILL

	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 NUMERATOR,DENOMINATOR);

.FILL

2.\The hyperbolic trigonometric functions.
.NOFILL

	REAL PROCEDURE SINH (REAL ARGUMENT);
	REAL PROCEDURE COSH (REAL ARGUMENT);
	REAL PROCEDURE TANH (REAL ARGUMENT);
.FILL
3. \The square-root function:
.NOFILL

	REAL PROCEDURE SQRT (REAL ARGUMENT);
.FILL
4.\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].
.NOFILL

	REAL PROCEDURE RAN (INTEGER SEED);

.FILL
5.\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 .4342944819.
.NOFILL

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


.END
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.

.SS |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:
.NOFILL

	TRIGINI ( LOCATION(simple-procedure-name) );
.FILL
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 in the
Manual for 'export' interrupt handlers, page 79.
If no TRIGINI call is done, the interrupt routine will simply
dismiss unexpected floating-point interrupts.

.SS |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.

.SS |OVERFLOW IMPLEMENTATION|

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

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 causes the interrupt if 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.

.SEC |NEW PROCESS FEATURES|

.SS|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 77 of the manual.)

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 and 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. Page 68 of the Manual contains the description of this protection
mechanism for non-APPLY Sprouts.

.SS|SPROUT_DEFAULTS|

SAIL  now provides  a mechanism  by which  the user  may specify  the
"default" options to be used when individual procedures are sprouted.

.EXA

Syntax:
	PROCEDURE <procid> ...
	 BEGIN
	 <some declarations>;
	 SPROUT_DEFAULTS <integer constant>;
	 <perhaps some more declarations>;
	 :
	 :
	 <statements>
	 :
	 END;

.ENDEXA

In other words, SPROUT_DEFAULTS is a declaration.

Semantics:

If  one of the "allocation" fields of  the options word passed to the
SPROUT routine -- i.e. QUANTUM,STRINGSTACK,PSTACK, or  PRIORITY -- is
zero,   then  SPROUT  will look  at  the corresponding  field of  the
specified <integer constant> for the procedure being sprouted. If the
field is  non-zero,   then  that value  will be  used; otherwise  the
current "system" default will be used.

NOTE:  SPROUT_DEFAULTS  only applies  to  "allocations", i.e.   the
process status control bits (e.g. SUSPME) are not affected.

.EXA
Example:

	RECURSIVE PROCEDURE FOO;
	 BEGIN
	 SPROUT_DEFAULTS STRINGSTACK(10);
	 INTEGER XXX;
	 :
	 :
	 END;
	:
	SPROUT(P1,FOO,STRINGSTACK(3));
	SPROUT(P2,FOO);
	COMMENT P1 will have a string stack of 3*32 words.
	 P2 will have a string stack of 10*32 words;

.ENDEXA
.SS |SUSPEND|

SUSPEND now behaves like RESUME in that it returns an item.
.nofill

	itm ← SUSPEND(<process item>)
.fill
Frequently, one is suspending some other process than the one that is executing the SUSPEND
statement. In this case, the item returned is ANY.
 However, in cases like:
.nofill

	X ← SUSPEND(MYPROC);
.fill
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
<return_item>  that was an argument to the RESUME.

.SS |FAIL AND SUCCEED|
FAIL and SUCCEED now behave like RESUME and SUSPEND in that they also return
an item.  The item returned is ANY unless the Matching Procedure containing
the FAIL or SUCCEED was (1) sprouted as a process, and (2) made running by a
RESUME construct.  In the latter case, the item returned is the <return_item>
that was an argument to the RESUME.  [Note that the only case in which a 
Matching Procedure can be reactivated at a FAIL is by being RESUMEd.]

.SEC |ERROR HANDLING|
.SS |ERROR MODES|
SAIL's error  handler has at  long last been  modified to do  what is
claimed it will do  in Section 20 of the manual (pgs 95 - 97), and in
the description of USERERR (pg 42).  In brief, it allows one to  have
error  messages automatically  sent  to  a "log"  file  while one  is
compiling, and to use USERERR as a trace statement.

The description given in the manual differs from reality in two ways:
"Keep" mode has not  been implemented (the error handler  will flush
all type-ahead except a <lf>);
  all of the  other modes ("Quiet",  "Logging", and
"Numbers") are implemented ONLY  IN THE COMPILER.   However, one  can
get the effect of error modes at runtime by using a brand new feature
called user error procedures.

.SS ALTMODE RESPONSE
After an "E" or "T" response to the error handler, an altmode
will return to the question loop; i.e., you may change your
mind if you do not wish to edit.  Normal monitor line editing
of the file name is allowed.

.SS |USER ERROR PROCEDURES|
A  user error  procedure is a  user procedure  that is run  before or
instead of  the  SAIL error  handler  everytime  an error  occurs  at
runtime.  This  includes all array errors, IO  errors, Leapish errors
and all USERERRs.  It does not include system errors, such as Ill Mem
Ref or Ill UUO. 

The  procedure one uses  for a  user error procedure  must be  of the
following type: 

.CENTER

SIMPLE INTEGER PROCEDURE proc (INTEGER loc; STRING msg, rsp);

.FILL
Only the names  proc, loc,  msg, and rsp  may vary  from the  example
above, except  that one  may declare  the procedure  INTERNAL if  one
wishes to use it across files.

Whenever  the external integer _ERRP_  is loaded with LOCATION(proc),
the error handler will  call proc before it  does anything else.   It
will set loc to  the core location of the call  to the error handler.
Msg  will be  the message  that it  would have  printed.  
Rsp will be non-NULL only if the error was from a USERERR which had
response string argument.
Proc can do anything that  a simple procedure can do.  When it exits,
it should  return an  integer which  tells the  error handler  if  it
should do anything more.  If the integer is 0, the error handler will
(1)  print the  message, (2)  print the location, and (3) query the tty
and dispatch on the response character (i.e ask for a <cr>, <lf>, etc.).
If the right half of the integer is non-zero, it is taken as the ascii for
a character to dispatch upon.
The left half may have two bits to control printing.
 If bit 17 in the integer is on,
message printing is inhibited.  If bit 16 is on, then the location printing is
inhibited. 
For example, "X"+(1 LSH 18) will cause the location to be printed and the
program exited. "C"+(3 LSH 18) will cause the error handler to continue
without printing anything.


Note that simple  procedures can not do  a non-local GOTO.   However,
the  effect of  a non-local  GOTO  can be  achieved in  a  user error
procedure by loading the external integer _ERRJ_ with the LOCATION of
a label. 
The label should be a on a call to a non-simple procedure which does the desired
GOTO.
The error handler clears _ERRJ_ before calling the procedure
in  _ERRP_.  If _ERRJ_  is non-zero when the  user procedure returns, and continuing
was specified, 
then the error handler's exit  consists of a simple transfer to  that
location.  Note that for this simple transfer to work properly, the place where
the error occurred (or the call to USERERR) must be in the same static (lexical)
scope as the label whose LOCATION is in _ERRJ_.  If this is really important
to you, see a SAIL hacker.

WARNING! Handling errors from strange places like the string garbage
collector and the core management routines will get you into deep 
trouble. 
.SEC |INEXHAUSTIBLE STRING SPACE|

The string garbage collector has been modified to expand string space
(using discontiguous blocks) whenever necessary to satisfy the demand
for places to put strings. To take advantage  of  this  feature,  one
need not change his programs.

Here are some points which might be of interest, however:

.LIST(4,0); 
1)\Although  we  are  going  to  provide  user control over all size
parameters eventually, currently only  the  initial  string  space
size  is  settable  by  the  user, either via REQUIRE or the ALLOC
sequence, as before.  The size of each string space increment will
be  the same as the original size, which need not be monstrous any
more unless you  know that all runs of your program will  need  a
monstrous  string  space  anyhow.   The  threshold (see below) for
expanding will be set at 1/8  the  string  space  size  (increment
size).

2)\One can, in his program, modify these values independently, if he
is willing to use the USERCON function, and to follow this format:
.NOFILL; LPTFONT;

     USER TABLE ENTRY NAME 		VALUE
	STINCR		lh: number of chars. in increment
			rh: number of words in increment + 4

	STREQD		lh: number of chars. in threshold
			rh: number of words in threshold.

.FILL; SELECT 1;
3)\The threshold.
After garbage collection, let us say that M-1 discontiguous string
spaces  are  full,  and  the  M'th  has  n  free  characters in it
(available for new strings).  The  garbage  collector  was  called
because  some routine wanted to create a string R characters long,
and there were not that many  available (free).     After  garbage
collection,  the new algorithm requires that N be greater than
R+LH(STREQD).  If it is not, expansion takes place (to M+1 spaces),
to  satisfy  this requirement.    In other words, if STREQD is 1/8
the size of the current space, that space will not be  allowed  to
become  more  than  about  7/8  full.   This helps avoid frequent,
nearly useless calls on the garbage collector when  space is about
gone.   All but the current space are allowed to become as full as
possible, however.

4)\New statistics are maintained by the  garbage  collector.     Soon
there  will  be a built-in routine you can use to print them.  For
now, you may look at them using USERCON, although this  document  does
not  say  what  they  are.     In  order to activate timing of the
garbage collector (slows it down), set SGCTIME in the  user  table
to -1.

5)\Future plans.
The  new  structure  not only allows expansion of string space, it
also will  allow  for  partial  garbage  collections  (no  visible
benefits  except  increased speed, since a partial collection will
be almost as effective as a complete one, and  much  faster),  and
the  ability  to  move  string  space  blocks, in order to compact
memory.  Push on your local representative  to  get  these  things
done.
.END
.SEC |RECORD STRUCTURES|

.SS |INTRODUCTORY REMARKS|

Record structures are a fairly recent addition to SAIL.  Essentially,
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 subfields
of the record.  Internally, records are small blocks of storage which
contain space for  the various  subfields and  a pointer  to a  class
descriptor record.   Subfields  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.

Records were originally added  to SAIL to  fullfill a number of  very
specific needs at Stanford, and  were subsequently generalized to the
form  seen  here.    The structures  described  in  this  section are
implemented and, so far as is known, work correctly.  (They have been
used successfully by several different people
to produce a number of sizable programs).  Readers are strongly urged
to look at the  file RECAUX.SAI[CSP,SYS], which contains a  number of
useful examples and auxilliary functions. 

.SS |RECORD CLASS DECLARATIONS|
.NOFILL

	RECORD_CLASS <classid> (<subfield declarations>)

For instance,

	RECORD_CLASS VECTOR (REAL X,Y,Z);
	RECORD_CLASS CELL (RECORD_POINTER(ANY_CLASS) CAR,CDR);
	RECORD_CLASS TABLEAU (REAL ARRAY A,B,C;INTEGER N,M);
	RECORD_CLASS FOO(LIST L;ITEMVAR A);

.FILL
Generally,  the <subfield  declarations>  have  the  same form  as  a
procedure's  formal paramter  list, except that  the words  VALUE and
REFERENCE should  not  be used.    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. 

.SS |RECORD POINTER DECLARATIONS|
.NOFILL

	RECORD_POINTER(<classid list>) <id list>
	RECORD_POINTER(ANY_CLASS) <id list>

For instance,

	RECORD_POINTER(VECTOR) V1,V2;
	RECORD_POINTER(VECTOR,TABLEAU) T1,T2;
	RECORD_POINTER(ANY_CLASS) R;

.FILL
At runtime, these variables contain either the value NULL_RECORD
(internally, zero) or else a pointer to a record.
The <classid list> is used to make a compile-time check on
assignments and subfield references.  The pseudo-class ANY_CLASS
matches all classes, and effectively disables this compile-time
check.  For instance:
.nofill

	RECORD_POINTER(FOO,BAR) FB1,FB2;
	RECORD_POINTER(FOO) FB3;
	RECORD_POINTER(CELL) C;
	RECORD_POINTER(ANY_CLASS) RP;
	:
	COMMENT the following are all ok syntactically;
	C←NEW_RECORD(CELL); 
	RP←C;	
	FB2←NEW_RECORD(FOO);
	FB1←FB3;
	FB3←RP; COMMENT Note that this is most likely a runtime bug
			Since RP will contain a cell record.  SAIL
			won't catch it, however;
	CELL:CAR[RP]←FB1;
	CELL:CAR[RP]←FB1;

	COMMENT The compiler will complain about these: ;
	FB1←C;
	FB3←NEW_RECORD(CELL);
	RP←CELL:CAR[FB3];
	
.FILL
NO runtime class information is kept with the variable, and no runtime
class checks are made on record assignment or subfield access.  
Record pointer variables are allocated quantities, and should not appear
inside SIMPLE procedures.   They resemble lists in that they are not
given any special value upon block entry and they are set to a null value
(NULL_RECORD) when the block in which they are declared is exited.
(This is so that any records referred to only in that block can be
reclaimed by the garbage collector.)  

Record pointers are regular SAIL data types, just like integers or
strings; record pointer procedures, arrays, and items all work in
the "normal" way.  As was indicated earlier, the constant NULL_RECORD
produces a null reference.  

.SS |ALLOCATION|
Records are allocated by means of the construct
.NOFILL

	NEW_RECORD(<classid>)

.FILL
which returns a new record of the specified class.  All subfields of
the new record are set to the "null" or "zero" value for that subfield
-- i.e., real & integer subfields will be set to 0, itemvar subfields
will be set to ANY, lists will be set to PHI, etc.
Again, note that entry into a 
block with local record pointer variables does NOT cause records
to be allocated and assigned to those variables.

.SS |SUBFIELDS|

Record subfields are referenced by means of the construct
.nofill

	<classid>:<fieldid>[<record pointer expression>]

.FILL
and may be used wherever an array element may be used.
For example
.nofill

	RECORD_POINTER(VECTOR) V;
	RECORD_POINTER(CELL) C;
	RECORD_POINTER(FOO) F;

	:
	VECTOR:X[V]←VECTOR:Y[V];
	CELL:CAR[C←NEW_RECORD(CELL)]←V;
	VECTOR:Z[V]←VECTOR:X[CELL:CAR[C]];
	SUBLIS ← FOO:L[F][1 TO 3];
	:

.fill
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 subfield statement matches the class of the
record whose subfield is being extracted.

An array subfield may be used as an array name, as in
.nofill

	RECORD_POINTER(TABLEAU) T;
	:
	TABLEAU:A[T][I,J] ← 2.5;

.fill
provided that you have stored a valid array descriptor into the subfield.
Unfortunately, SAIL does not provide any clean way to do this.  One unclean
way is
.nofill
	INTEGER PROCEDURE NEWARY(INTEGER LB,UB);
		BEGIN
		INTEGER ARRAY A[LB:UB];
		INTEGER AA;
		AA←MEMORY[LOCATION(A)];
		MEMORY[LOCATION(A)]←0;
			 COMMENT defeats deallocation;
		RETURN(AA);
		END;
	:
	RECORD_CLASS FUBAR(INTEGER ARRAY A);
	RECORD_POINTER(FUBAR) FB;
	:
	MEMORY[LOCATION(FUBAR:A[FB])]←NEWARY(1,100);
.fill

(Warning: the above "advice" is primarily intended for hackers; we
make no promisses that it will never get you into trouble, although
this particular trick is unlikely to be made obsolete in the forseeable
future).

.SS |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., are not pointed to by any variables, aren't
pointed to by any accessible records, etc.).  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 in a subsequent section.

.SS |INTERNAL REPRESENTATIONS |

.fill
Each record has the following form:
.nofill

       -1:  <pointers to ring of all records of class>
        0:  <garbage collector pointer>,,<pntr to class descriptor>
        +1:     <first subfield>
                      :
        +n:     <last subfield>
.fill

Record  pointer   variables  point  at   word  0  of   such  records.

The predefined record class $CLASS is used to define all record classes,
and is itself a record of class $CLASS.  
.nofill

	RECORD_CLASS $CLASS (INTEGER RECRNG,HNDLER,RECSIZ;
				INTEGER ARRAY TYPARR;
				STRING ARRAY TXTARR);
.fill

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 subfields in the class.

TYPARR is an array of subfield descriptors for each subfield of the
class.

TXTARR is an array of subfield 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.

.nofill
Example:

        RECORD_CLASS FOO(LIST L;ITEMVAR A);

The record class descriptor for FOO would contain:

	FOO-1:	<pointers for ring of all records of $CLASS>
	FOO:	<pointer to $CLASS>
	FOO+1:	<pointers for ring of all records of class FOO
			initialized to <FOO+2,,FOO+2> >.
	FOO+2:	<pointer to handler procedure: $REC$>
	FOO+3:	2
	FOO+4	<pointer to TYPARR>
	FOO+5:	<pointer to TXTARR>

The subfields of FOO will be:

        $CLASS:RECRNG[FOO] = <initialized to null ring,
                              i.e., xwd(loc(FOO)+2,loc(FOO)+22)>
	$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] = <magic bits for garbage collector>
	$CLASS:TYPARR[FOO] [1] = <descriptor for LIST>
	$CLASS:TYPARR[FOO] [2] = <descriptor for ITEMVAR>


.SS |HANDLER PROCEDURES|

.fill
SAIL uses a single runtime routine $RECFN(OP,REC) to handle such system
functions as record allocation, deallocation, etc.  For instance, the
construct 
.nofill
	r ← NEW_RECORD(foo) 
is compiled as
	PUSH	P,[1]
	PUSH	P,[foo]
	PUSHJ	P,$RECFN
	MOVEM	1,r

.fill
$RECFN performs a certain amount of type checking and then jumps to the 
appropriate handler procedure for the class being operated on.
The normal value for this handler procedure is $REC$.
It is possible for a user to substitute his own handler procedure for
a given class of records by including the procedure name  in brackets
after the record class declaration:

.nofill
        RECORD_CLASS <id> (<subfields>) [<handler>]

This handler must have the form

        RECORD_POINTER(ANY_CLASS) PROCEDURE <procid>
                        (INTEGER OP;RECORD_POINTER(ANY_CLASS) R);

.fill
Where OP  will be a  small integer  saying what is  to be  done.  The
current assignments for OP are:
.nofill

        OP value        meaning

        0               invalid
        1               allocate a new record of record class R
        2               not used
        3               not used
        4               Mark all subfields of record R 
        5               Delete all space for record R 

.fill
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 subfields (except string) are initialized to zero.
String subfields 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
subfields of R.  

$REC$(5,R) deallocates record R, and deallocates all string and array
subfields of record R.  Care must be exercised to prevent multiple 
pointers to string and array subfields, ie. DO NOT store the location
of an array in subfields of two different records unless extreme caution
is taken to handle deletion.  This can be accomplished through user handler
procedures which zero array subfields (without actually deleting the arrays)
prior to the call on $REC$(5,R).  

NOTE:  When a user wishes to supply his own handler procedure, he must
be careful to furnish all the necessary functions.  One good way to do this
is to test for those OPs that he wishes to handle and then call $REC$
for all the rest.  Also, if $REC$ was 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:
.NOFILL

	RECORD_CLASS FOO(ITEMVAR IV)[FOOH];
	RECORD_POINTER(ANY_CLASS) PROCEDURE FOOH(INTEGER OP;
					RECORD_POINTER(ANY_CLASS) R);
		BEGIN
		OUTSTR("CALLING FOOH.  OP = "&CVS(OP));
		IF OP = 1 THEN
			BEGIN
			RECORD_POINTER(FOO) F;
			F←$REC$(1,R);
			FOO:IV[F]←NEW;
			RETURN(F);
			END
		ELSE IF OP = 5 THEN
			BEGIN
			DELETE(FOO:IV[R]);
			END;
		RETURN($REC$(OP,R));
		END;
.fill

.SS |STRING SUBFIELDS|

String subfields presented an implementation difficulty due to the fact that
string discriptors require 2 words, but record subfields are exactly one word.
This problem was solved by making string subfields contain a pointer
to the descriptor for the string (like REFERENCE STRING formal parameters).

When a record with string subfields is allocated by a call to NEW_RECORD,
the string descriptor blocks (2 words each) are allocated from a linked
list of free string descriptors.  Likewise, when a record is deallocated
either explicitly or by the garbage collector, the string subfield descriptors
are released to the free list.  The free list is automatically expanded when
exhausted.

.SS |MORE ABOUT GARBAGE COLLECTION|

The information used by the system to decide when to call $RECGC on its
own is accessible through 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:
.NOFILL

	BLKSIZ	-- number of words per block in this "space"
	TRIGGER	-- a counter used to control when to garbage collect
	TGRMIN	-- described below.
	TUNUSED	-- number of unused blocks on the "free list"
	TINUSE 	-- total number of blocks "in use" for this space.
	CULPRIT	-- the number of times this space has caused 
		   collection.

.FILL
The appropriate macro definitions for access of these fields may be found
in the source file "SYS:RECORD.DEF".  As one might expect, the decision
to invoke the garbage collector is  made as part of the block allocation
procedure, which works (roughly) as follows:
.nofill

	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.>

.FILL
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:
.nofill

	<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;

.FILL
RGCRHO(USER) is a real number currently initialized by the system to 0.33.
Thus, users can modify the behavior or SAIL's automatic garbage collection by
some combination of:
.nofill

	(1) Setting RGCOFF(USER).
	(2) Supplying their own procedure in TGRADJ(USER).
	(3) Modifying RGCRHO(USER).
	(4) Modifying the TGRMIN entries in the space descriptors.

.FILL
One word of caution: User procedures that set trigger levels must be sure
not to leave the trigger level of the space that caused collection to
be set to zero.  This will cause a runtime error message to be generated.

.SEC |PRINT|
SAIL has traditionally had very flexible input and output that
allowed the user essentially complete access to the time-sharing
system.  Unfortunately, some of the simple and common things that
the user wants to do have been too difficult -- an example being
printing output.  Typically, the user has done his own explicit
conversions in order to print the various SAIL data types.
Thus, the following is an example of the coding that has
been common.
.nofill

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

.fill
The PRINT statement solves this by printing out each
value according to the printing format for each argument and allowing
an indefinite number of arguments, each of which is separately printed
without concatenation.  Thus, the above becomes:
.nofill

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

.fill
The PRINT construction is both clearer and more efficient.
The first example has the disadvantage of doing unneeded concatenation,
in the sense that the string really doesn't have to be created;
PRINT avoids unnecessary concatenation and generally compiles less
in-line code as well.
.ss Default Formats for PRINT
Each syntactic type is formatted by PRINT in a standard way that
will hopefully satisfy most users.  Strings are simply printed out.
Integers use the function CVS, with the current SETFORMAT positions.
Reals use CVG, also with the current SETFORMAT.

Item expressions use the print name for the item if one exists, otherwise
ITEM!xxx, where xxx is the item number.  Sets and lists are printed
out showing 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 printed with the name of the record class, followed
by a ".", followed by the address in decimal of the record.  NULL!RECORD
is printed for the empty record.

If the user wants some other format for a given syntactic type, it
is easy to make a function call as the argument to the PRINT statement.
For example,

PRINT(CVOS(I));

will print out I in octal, since CVOS is first called.  (The syntactic
type of the expression CVOS(I) is of course STRING.)

There is also a way to entirely change the default formatting function
for a given syntactic type.  It is described below, but is considered
to be for wizards only.
.ss SETPRINT and GETPRINT
The SETPRINT function sets the destination of the output from the
PRINT statement.  Initially, the PRINT statement is set to output
to the user's terminal.  This may be changed to be to a file or
to both the terminal and a file.

The call to SETPRINT is as follows:
.skip 1
.once center
SETPRINT("FILE!NAME", "MODE")
.skip 1
The file name is a (possibly empty) name for a file to which the PRINTed
text is to be printed.  "MODE" is a single character indicating how the
PRINT statement is to function.  SETPRINT may be called many times
-- it is a procedure with dynamic effects, not a declaration.
.nofill

   Character	Meaning
   ---------------------------
	T	the Terminal gets all PRINT output.  if an
		output file is open, it is to be closed.  T is
		the default mode in which PRINT is initialized.
	F	File gets PRINT output.  If no file is open, it
		will be opened, as described below.
	B	Both terminal and file get PRINT output.  if
		no file is open, it should be opened.
	N	Neither the file nor the terminal is getting any
		output.  if a file is open, it will be closed.
	S	suppress all output, but open a file if none is
		open.  
	O	a file is Open, but the terminal is getting all
		output.  opens a file if none is open.
	C	terminal gets output, but ignore whether or not
		file is open and whether or not file 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.
.fill

The first 6 possibilities represent the logically possible states in
which the PRINT system can be.  These are therefore the letters returned
by GETPRINT below.  The "C" and "I" modes are added for convenience, so
that it is possible to terminal output on and off without bothering to 
check, with GETPRINT, the current state of the output.

The PRINT statement is initialized to mode "T" -- print to Terminal.
The user will probably find modes "T", "F" and "B" to be the most
useful.  The other modes are included for completeness of the system,
and to allow the user to switch between various combinations dynamically.

Should SETPRINT be called in such a way that a file has to be opened -- e.g.,
mode "F" and no file is open -- then the first argument to SETPRINT will
be used ss the name for the output file.  If the filename argument is
NULL, then the filename will be obtained from the terminal.
Thus, the statement:
.nofill

SETPRINT(NULL,"F");

.fill
first types out the message

.nofill

File for PRINT output  *

.fill
and then waits for the user to type a filename, and then opens such a file.  
On TENEX, GTJFN with
filename recognition is used here.  On the DEC system and its variants,
the filename is read in with the INCHWL routine.

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.

To determine the current mode of the PRINT system, the user can
call GETPRINT, which returns a letter indicating which of the
modes is currently set, using the same coding as SETPRINT above.  Thus, the call is
.nofill

	"MODE" ← GETPRINT;

.fill
SETPRINT and GETPRINT are related only to the PRINT statement and
do not effect CPRINT below.
.ss Simple Uses of PRINT and SETPRINT
We provide a few examples of common output situations.

1)  PRINT to TERMINAL.  Just call PRINT with your output, don't bother
with SETPRINT.

2)  PRINT to FILE.  Call
.nofill

SETPRINT(NULL,"F");

.fill
and type the name of the output file, at runtime, when it asks.

3)  PRINT to FILE and TERMINAL.  Call, at the beginning of your program,
.nofill

SETPRINT(NULL,"B");


.fill
and type the name when asked.

4)  PRINT to FILE always and sometimes also to TERMINAL.  Call, at the beginning
.nofill

SETPRINT(NULL,"B");

.fill
and give the name of the file when it asks.  This sets output to both
the terminal and the file.  Now, to ignore the terminal (leaving the file
alone), call

.nofill

SETPRINT(NULL,"I");

.FILL
Likewise, to turn the teletype back on, call

.nofill

SETPRINT(NULL,"C");

.FILL
This is useful for obtaining a cleaned-up printout on the file, with
error messages going to the terminal.

The use of GETPRINT to determine the current state allows more complex
possibilities.

.ss CPRINT -- Printing to a Channel
CPRINT is similar to PRINT
except that the first argument is assumed to be a SAIL channel
number.  CPRINT
then dispatches to the file the appropriate printed formats of the rest of the
arguments.
Thus, the following are functionally equivalent:
.nofill

CPRINT(CHAN,"The value are ",I," and ",X);

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

.fill
The user is responsible for obtaining and releasing the channel for
the CPRINT statement.  As a very simple example on the DEC system:

.nofill

OUTSTR("Type output file name  *");
OPEN(CHAN ← GETCHAN,"DSK",0,0,2,0,0,0);
ENTER(CHAN,INCHWL,FLAG);
IF FLAG THEN USERERR(0,0,"CANNOT GET FILE ON DISK");

.FILL
or for TENEX:
.NOFILL

OUTSTR("Type output file name  *");
CHAN ← OPENFILE(NULL,"W");

.FILL
The statement RELEASE(CHAN) on DEC or CFILE(CHAN) on TENEX will
close and release the device/file open on CHAN.
.ss |Design of PRINT|
The PRINT statement was designed
for those who miss the ALGOL W WRITE and WRITEON statements, especially
for new SAIL users who currently have to learn too much about 
type conversion and file manipulation to write simple SAIL programs.
Notice that SAIL doesn't need both WRITE and WRITEON, since manipulating
lines can be done from SAIL by printing out formatting characters.  
We recommend the following macro

.nofill

DEFINE CRLF="('15 & '12)";

.FILL
which the user may define to mean "carriage return - line feed".  Thus,

.NOFILL

PRINT(CRLF,"Hi There");

is equivalent to the ALGOL W 

WRITE("Hi There");

.FILL
A READ statement was also considered for implementation.
The difficulty lies in
defining how strings are input.  SAIL has very extensive
input facilities, including break tables, which do many of the
common scanning operation.

Further consideration was given to a template-driven printing function.
This proved to be both inefficient in implementation, and an added overhead
to an already large runtime system.
.ss CAVEATS of PRINT and CPRINT
A problem with the PRINT construction, especially for experienced
SAIL users, is likely to occur when the user trys to exploit the
normal SAIL type conversions in the printing of strings.  This is
especially common when the user wants to print out ASCII characters.
One often uses a number (i.e., an INTEGER expression) that, upon default
type conversion, becomes a one-character string.  Thus,
.nofill

OUTSTR(12)

.fill
prints a form-feed onto the terminal, whereas 

.nofill

PRINT(12)

.FILL
prints "12" onto the terminal.  The reason, of course, is the
default formatting for integers under PRINT or CPRINT.

This problem is especially confusing with macros that have been
defined with an integer to represent an ASCII character.  For
example,
.NOFILL

DEFINE TAB="'11";

.FILL
will present the same problem.  The solution is to define the macro
so that it expands to a STRING constant rather than an integer.

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

.ss |Hooks into the PRINT System|
[This section is for wizards only.]

As is traditional with SAIL, there are user hooks to get hold of the
operation of the PRINT system.  These hooks are obtained by setting
certain cells of the user table to the addresses of SIMPLE PROCEDURES
that the user has written.

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, e.g.,
.nofill

SIMPLE PROCEDURE MYPRINT(INTEGER CHAN; STRING S)

.FILL
The user could set this up by the assignment
.nofill

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

.fill
(See below in this manual for a description of the GOGTAB array as a 
way of accessing the user table.)

The CHAN argument is then either the CHAN argument for CPRINT, or -1 for
PRINT.  If this trap is set up, 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.  For example, if the user wanted to trap integer printing
and print all integers out in octal, preceded by a "'", he could do the
following:
.nofill

SIMPLE STRING PROCEDURE MYCVOS(INTEGER I);
RETURN("'" & CVOS(I));


GOGTAB[$$FINT] ← LOCATION(MYCVOS);

.fill
The names for the addresses in the user table associated with each
formatting function are:
.nofill

User table index	for syntactic type
-----------------------------------------

$$FINT			INTEGER
$$FREL			REAL
$$FITM			ITEM
$$FSET			SET
$$FLST			LIST
$$FSTR			STRING
$$FREC			RECORD!POINTER

.fill
To restore any formatting function to the default
provided by the PRINT system, the user should zero the appropriate address of the
user table.
.SEC |SAVE/CONTINUE|
A save/continue facility has been implemented in the SAIL compiler.
This allows compiling header files, saving the state of the compiler,
and resuming compilation at a later time.  The save/continue facility
works with files as the basic unit; compilation can be interrupted
only at the end of a file.  The /X (eXtend) switch controls the new
feature.  The examples shown here are for the DEC timesharing system.
Analogous commands work under TENEX, using the TENEX RUN and SAVE commands.
The dialog is similar, except that the TENEX command language format is
followed on TENEX.
Example:
.NOFILL
α.R SAIL
*INTRMD.REL[PRJ,PRG]←A,B,C/X
 A.SAI 1 etc.

SAVE ME FOR USE AS XSAIL.
EXIT
α.SAVE XSAIL
JOB SAVED IN 25K
UPPER NOT SAVED!

α.RU XSAIL
*FINAL←D,E,F
 D.SAI
Copying DSK:INTRMD.REL[PRJ,PRG]
 2 3 etc.

*↑C


The above is equivalent to 
α.R SAIL
*FINAL←A,B,C,D,E,F

.FILL
On TENEX, the user will want to save core from 20 to 577777
in creating the XSAIL.SAV file.  

Information is saved in XSAIL.SAV and in the binary file from the
first "compilation" (in this case INTRMD.REL).  When compilation is
resumed, the final binary file is initialized by copying the
intermediate file.

Save/continue is not allowed if the file break occurs while scanning
false conditional compilation or actual parameters to a macro call.

A hint on using this feature:  If the source term of your command string
consists of just one file, and this one file does REQUIREs of other
source files, the following setup works well.
.nofill

Original file FOO.SAI:
	BEGIN "FOO"
	  REQUIRE "[][]" DELIMITERS;
	  DEFINE !=[COMMENT];
	  REQUIRE "BAZ.SAI" SOURCE_FILE;
	  REQUIRE "MUMBLE.SAI" SOURCE_FILE;
	  :
	  <rest of file>
	  :
	END "FOO"


New file FOO.SAI:
    IFCR NOT DECLARATION(GARPLY) THENC
	BEGIN "FOO"
	  REQUIRE "[][]" DELIMITERS;
    DEFINE GARPLY=TRUE;
	  DEFINE !=[COMMENT];
	  REQUIRE "BAZ.SAI" SOURCE_FILE;
	  REQUIRE "MUMBLE.SAI" SOURCE_FILE;
    ENDC;
	  :
	  <rest of file>
	  :
	END "FOO"


New file FOO.HDR:
    IFCR NOT DECLARATION(GARPLY) THENC
	BEGIN "FOO"
	  REQUIRE "[][]" DELIMITERS;
    DEFINE GARPLY=TRUE;
	  DEFINE !=[COMMENT];
	  REQUIRE "BAZ.SAI" SOURCE_FILE;
	  REQUIRE "MUMBLE.SAI" SOURCE_FILE;
    ENDC;


Initial compilation: 
    .R SAIL
    *FOO.INT[PRJ,PRG]←FOO.HDR/X
    SAVE ME!
    .SAV XSAIL


Now the command string
    FOO←FOO
will work both in the case of .R SAIL and in the case .RU XSAIL.
.FILL
.SEC |MISCELLANEOUS NEW FEATURES|
.SS|NEW MTAPE OPTIONS|
.nofill

	MTAPE(chan,NULL)

.fill
will cause an MTAPE 0 to be issued for channel chan.  For mag. tapes,
this will cause you to wait until all activity ceases.  For other
devices, various random things can happen, depending on the device
and system.

In export SAIL, MTAPE(chnl,"I") sets the 'IBM compatible'
mode for a tape drive.  (It does an MTAPE chnl,101.)

These features do not work in TENEX SAIL.  Full access to TENEX magtapes
is obtained through the MTOPR, GDSTS and SDSTS routines, which are
TENEX-only runtimes.

.ss|INITIALIZATION PHASES|

User initializations  are  now done  in successive  phases, with  all
initializations   required   for   one   phase   being  done   before
initializations required for the next phase. 
.NOFILL

Syntax:

	REQUIRE <procid> INITIALIZATION;
	REQUIRE <procid> INITIALIZATION [<phase no>];

where <phase no> is an integer constant. 

Semantics:
.FILL

<phase no> specifies  the number  of the user  initialization
phase.  If  it is left out,  then one is used.   Currently, there are
three  phases, numbered 0, 1, and  2.  If the  demand is great enough,
additional phases may  be added later.   (Note for assembly  language
hackers: internally, user phases are numbered '400000, '400001, etc.)

.SS|CHNCDB|

	val←CHNCDB(channel)

This integer procedure returns a pointer to the three word block used
to open 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. 

This feature does not work in TENEX SAIL.  See a hacker if you want
these features.

.SS|OVERFLOW|

The overflow-underflow handling routines have been upgraded to deal
with JFCL properly. See the section on trigonometric routines.
.SS |ARRCLR|
.nofill

	ARRCLR(arry)
.FILL
This new runtime routine clears any kind of array. That is, arthmetic 
arrays get filled with zeros, string arrays with NULLs, and itemvar
arrays with ANYs.  
One may use ARRCLR with set and list arrays, but the set and list
space will be lost (i.e. un-garbage-collectable). 
The alternative form:
.nofill

	ARRCLR(arry,val)

.fill
where val is either an integer or a real number, will fill arry with
that value.  Do not do this to string or list arrays unless you do
not care whether or not your program works.  Also using a real val for
an itemvar array is apt to cause strange results. (If you use an integer,
arry will be filled with CVI(val).)

.SS |SETPL|
.nofill

	SETPL(channel, @linnum, @pagnum, @sosnum)
.fill
This new runtime 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.  When fully
implemented (soon), this will work on the INPUT, INTIN, and REALIN functions
as well.

.SS |EVALREDEFINE|
EVALREDEFINE bears the same relationship to REDEFINE as EVALDEFINE does to DEFINE.
See pages 47 and 50 of the Manual.

.SS|CVPS|
CVPS(<macro_parameter>) converts <macro_parameter> to a string and returns
the string. See about macro parameters on page 48 of the manual.

.SS|EXPRESSIONS IN REQUIRES|
Previously, all REQUIRE constructs had to have only constants in them. Now
SAIL allows compile time expressions as well. See about compile time 
expressions on page 47 of the Manual. 

.SS|RELEASE|
RELEASE now takes an optional second argument, the CLOSE inhibit bits.
These are described in the UUO manual (Stanford System). These are defaulted
to zero when not specified so that 
old programs which did not specify them will work as before. 

The CLOSE inhibit feature only works at SU-AI.
.SS|TTYUP|
.nofill

	oldval←TTYUP(newval)

.fill
This routine casuse 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 is called with newval
FALSE.  Oldval will always get set to the value of newval used in the
previous call. (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.

.SS|BREAKSET MODES ⊗≡⊗K⊗≡⊗ AND ⊗≡⊗F⊗≡⊗|
A "K" specification as a BREAKSET mode will cause lower to upper case
conversion when that break table is used.  Conversion takes place before
each character is checked for breaking or omission.  An "F" specification
turns off the conversion -- i.e. it undoes the effects of "K".

.SS|INOUT|
.nofill

	INOUT(inchan,outchan,howmany)

.fill
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.

(note: INOUT uses BLTs to transfer data directly from one set of buffers
to the other)

INOUT is not available in TENEX SAIL.
.SS|GETSTS & SETSTS|
.nofill

	SETSTS(chan,new_status)

issues a SETSTS uuo on channel chan with the status value new_status.

	status←GETSTS(chan)

.fill
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.

.SS|CHANGES TO ⊗≡⊗OPEN⊗≡⊗ ERROR HANDLING|
If the EOF variable supplied to OPEN is non-zero and the device name
is invalid, then OPEN will fail without giving the error message
"INVALID DEVICE NAME FOR OPEN", and the EOF value will be unchanged.
If a device is unavailable, and EOF=0, then the user is now given the
options of trying again or going on without opening the device, in which
case EOF will be set to non-zero as usual.

.ss|ASH|
ASH has been added as an arithmetic operator.  Its syntax is just like that
of LSH, and it generates similar code (except for putting out a
PDP-10 ASH instruction instead of a LSH).

.SS|ARG_LIST|
.nofill

	ARG_LIST(<arg1>,...,<argn>)

.fill
where each <arg> may be any valid argument to the REF_ITEM construct,
assembles a list of "temporary" reference items that will be deleted
by APPLY after the applied procedure returns.  Thus
.nofill

	APPLY(proc,ARG_LIST(foo,bar,VALUE baz))

is roughly equivalent to

.begin verbatim
	tmplst←{{REF_ITEM(foo),REF_ITEM(bar),REF_ITEM(VALUE baz)}};
	APPLY(proc,tmplst);
	WHILE LENGTH(tmplst) DO DELETE(LOP(tmplst));
.end

.FILL
but is somewhat easier to type.  Note that the reference items created
by ARG_LIST are just like those created by REF_ITEM, except that they
are marked so that APPLY will know to kill them.

.ss CLOSE


The CLOSE function now has an additional optional argument
that allows the CLOSE inhibit bits to be turned on.  The second argument
is the bit pattern for the inhibition, and it is defaulted 0.  See the
UUO manual for details.  

This feature is available only at SU-AI, and is not available in 
the TENEX version of SAIL.
.ss TYPEIT
The number returned by the TYPEIT procedure (which identifies
the type of the datum of its item expression argument) is now changed
for arrays.  Now it is '24 plus the scalar value of the type (e.g., INTEGER),
whereas before it was '15 plus the scalar value.

Additional types added to TYPEIT are:  '16 for labels, '17 for
record classes.
.ss COMPARISON OF .REL FILES
Starting with version 18, the compiler emits information
into each .REL file containing the version of the compiler used.  Then,
during the SAIL initialization sequence, these versions are compared, and
a warning message is issued if necessary.  Thus, the problem of converting from one
version of the compiler to a new version is lessened.  This feature
may not work as well in the  current changeover as it will in the future.
.ss SCAN Optimizations
The SCAN function has been modified to return true substrings
instead of copies in those cases that the break table in question was
not omitting any characters.  This should be an improvement in the
efficiency of those routines.
.ss BREAK TABLES
There are now 54 user-setable break tables available to the
SCAN, INPUT, TTYINL functions, as well as SETBREAK and BREAKSET.  These
tables are allocated and deallocated dynamically.

In addition, GETBREAK now returns the value of the smallest
unallocated breaktable, and RELBREAK releases its table argument.
The implicit declarations of these new runtime routines are:
.begin center

INTEGER PROCEDURE GETBREAK
PROCEDURE RELBREAK(INTEGER TABLE)

.end
NOTE:  a breaktable is allocated by either GETBREAK, SETBREAK, BREAKSET or STDBRK.
A breaktable is de-allocated (and thereby made available to GETBREAK) by
RELBREAK.  Breaktables are stored in groups of 18, so it is more efficient
to use all the tables in a given group before going to another group.  The
use of GETBREAK is particularly recommended for load-modules and the like,
so that conflicts will be minimzed.  Breaktable zero is special.  It is
predeclared to produce the same results as SETBREAK(0,NULL,NULL,"I").  This
results in break-on-count for calls to INPUT, and always returns the whole
string for SCAN.  Breaktable zero is stored along with breaktables 1 to 18,
and thus takes up no additional space.

HACKERS:  Actually there are 17 additional system breaktables which are
reserved for
use by the SAIL runtime system.  These tables are numbers -17 through -1, and
are ordinarily not available to the user.  They will be used by the debugger
and by additional SAIL system software.  

The user can obtain access to breaktables -17 through -1 by setting
BRKPRV(USER) to -1, for example by the USERCON function which accesses the
SAIL user table.  WARNING:  absolutely no guarantees are made to the user
who accesses these breaktables.  This information is intended for completeness
only; the use of these special breaktables by the user is not recommended.

.ss CV6STR
CV6STR does what you always wished CVXSTR did.  I.e., it stops converting
as soon as it sees a blank rather than always returning a six-character
string.  Example:
.nofill
	CV6STR(CVSIX("XYZ"))="XYZ", not "XYZ   ".

Beware, however, since

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

.ss TENEX RUNTIMES
The TENEX version of SAIL now has these additional teletype-oriented
routines:  STTYP, GTTYP, STPAR, STI, and DELNF, all named after jsyses.  
See TENEX-specific
documentation.

.ss CVASTR
CVASTR is the "correct" inverse function to CVASC.  I.e., it stops on
a null character.  For instance,
.nofill
	CVASTR(CVASC("ABC")) is "ABC"

whereas

	CVSTR(CVASC("ABC")) is "ABC"&0&0
.fill
.ss NEW SWITCHES
/V  forces loader link blocks and constant string texts into
the low segment; intended for overlay systems in which code
is overlaid but data is not.

/X  controls compiler save/continue; see the section on save/continue.

/W  generates additional (suppressed) DDT symbols.  These symbols
are designed to serve as comments to a programmer or processor
rummaging though the generated code.  Symbols generated by this
switch all begin with a percent sign (%), and many come in pairs.
A %$ symbol points to the first word of an area and a %. symbol
points to the first word beyond the area.  Thus the length of an
area is the difference of its %. and %$ symbols.  The symbols are:
	
.nofill
	%$ADCN	%.ADCN	address constant area
	%$LIT	%.LIT	literal area
	%$RLIT	%.RLIT	reference literal area
	%$SCOD	%.SCOD	START!CODE or QUICK!CODE area
	%$STRC	%$STRC	string variable area
	%$VARS	%.VARS	simple variable area
	%ALSTO		registers are about to be cleared
	%$ARRY		first data word of a fixed array
	%$FORE		FOREACH satisfier block
	%$SUCC		SUCCEED/FAIL return block
.fill

/W tends to increase the number of DDT symbols by a factor of 2 or 3.

/F Two new values, /40F and /100F, have been added. Characters are
temporarily inhibited from going to the listing file if the 40 bit
is on (see COMPILER_SWITCHES below for what good this will do you).
If the 100 bit is on then the two-line banner normally put at the
top of each page of the listing is suppressed.  Using the 100 bit
is a way to have SAIL "permanently" expand macros; a /110F listing
can be used as a SAIL source file.

/D, /P, /Q, /R now accept values.  If a non-zero value appears,
it is interpreted as decimal and the stack is set to that size. 
If the value is zero or absent, then the stack size is doubled
as before.  Thus /35P/P first sets the P stack to 35 and then
doubles it to 70.

.ss COMPILER_SWITCHES
The statement REQUIRE "chars" COMPILER_SWITCHES; can be used
to change the settings of the compiler switches.  "chars"
must be a string constant which is a legitimate switch string,
containing none of the characters "(/)"; e.g.,
.nofill
    REQUIRE "20F" COMPILER_SWITCHES;
.fill
The string of characters is merely passed to the switch processor,
and it may be possible to cause all sorts of problems depending
on the switches you try to modify.
The switches which set stack sizes (D, P, Q, R) or string space (S)
should be avoided. Switches which control the format of files (B, F)
should only be used if you have such a file open.

.SS COMPILER_BANNER
This is a predefined macro which expands to a string constant
containing the text of the two-line banner which would appear
at the top of the current page if a listing file were being made.
This string contains the date, time, name and page of the source file,
the value of all compiler switches, the name of the outer block,
and the name of the current block.  Thus you can automatically
include the date of compilation in a program by using
COMPILER_BANNER[n TO m] for appropriate n and m. Try
REQUIRE COMPILER_BANNER MESSAGE; or look at a listing for the exact format.
.SS EDFILE
.NOFILL
	EDFILE("filename",line,page,bits[0])
.FILL
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:
.NOFILL
	    0  edit
	    1  no directory (as in /N)
	    2  readonly (as in /R)
	    4  create (as in /C)
.FILL
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 (extention), and 6 (device) are loaded from the corresponding
values in INIACS.]

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

.ss 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 SYS:GOGTAB.DEF (<SAIL>GOGTAB.DEF on TENEX) as
a source file.  This file contains DEFINEs for all of the
user table entries.
.SS ARERR
The effective address of an ARERR UUO (op code 007) may be
either word 1 or word 2 of a string descriptor.  This allows
ARERR 1,["array name"] from START_CODE.

.SS COORD
.ONCE VERBATIM
INTEGER PROCEDURE COORD(STRING LOCATION);

(A  new BAIL feature).
Returns the coordinate number of the location given as its argument.  LOCATION
has the same syntax as in BREAK.

.SS DEFAULT PARAMETER VALUES
Actually, this is an old feature which, for some reason, was never documented.
The SAIL compiler allows you to specify default values to any trailing
parameters.  This is done by enclosing the desired value in parentheses
following the parameter declaration.  For instance,
.nofill
	PROCEDURE FOO(REAL X;INTEGER I(2);STRING S("FOO");REAL Y(3.14159));
.FILL
Then, if a defaulted parameter is left out of a procedure call, the compiler
fills in the default automatically.  Thus, the following would all compile
the same:
.unfill
	FOO(A+B);
	FOO(A+B,2,"FOO");
	FOO(A+B,2,"FOO",3.14159);
.fill
Only VALUE parameters may be defaulted, and the default values must all be
recognized by the SAIL compiler as constants.  Also, you cannot leave out
a parameter from 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
funny START_CODE or INTERNAL-EXTERNAL linkages.  However, APPLY will fill in
default values if not enough actual parameters are supplied in an
interpreted call.

.SS PRESET_WITH
(This feature has also been around for a while.) PRESET_WITH is just like
PRELOAD_WITH except that an array which is PRESET is placed in the upper
segment if the /H switch was specified to the compiler.
This allows constant arrays to be in the shared portion of the code.


.SS RELATIONAL OPERATORS
(This feature is not exactly new, either.)  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)) AND (T RELOP2 E3).
In addition, the compiler can often 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 code IF A<X>B THEN is equivalent to
IF X>(A MAX B) THEN .
.sec |MINOR CORRECTIONS TO AIM-204|

.turn on "%"
%4
.require "lies[doc,ail]" source_file
%*
.MAN_BACK
.EVERY HEADING (,,);
.PORTION TITLEPAGE
.turn on "→{↑"
.SELECT 1
STANFORD ARTIFICIAL INTELLIGENCE LABORATORY→{(↑MONTH)} {YEAR}
.SKIP 6
.SELECT 2; CENTER
SAIL USER MANUAL
.SELECT 1;
.SKIP 1
UPDATE
.SKIP 3
James R. Low
John F. Reiser
Hanan J. Samet
Robert L. Smith
Robert F. Sproull
Daniel C. Swinehart
Russell H. Taylor
Kurt A. VanLehn
.SKIP 10
ABSTRACT
.FILL;CRSPACE;INDENT 0,0;ADJUST
.SKIP 1;
This document describes recent changes to the SAIL language
since the "new" manual (AIM-204) was published in July 1973.
It reflects the various new features implemented 
as of 20 November, 1975 for SAIL version 18 and
corrects a number of minor errors in the earlier manual.