diff --git a/docs/medley-irm/01-INTRO.TEDIT b/docs/medley-irm/01-INTRO.TEDIT index 176f6df1..1f57223a 100644 Binary files a/docs/medley-irm/01-INTRO.TEDIT and b/docs/medley-irm/01-INTRO.TEDIT differ diff --git a/docs/medley-irm/02-LITATOM.TEDIT b/docs/medley-irm/02-LITATOM.TEDIT index c6830886..7749ab2f 100644 Binary files a/docs/medley-irm/02-LITATOM.TEDIT and b/docs/medley-irm/02-LITATOM.TEDIT differ diff --git a/docs/medley-irm/03-LISTS.TEDIT b/docs/medley-irm/03-LISTS.TEDIT new file mode 100644 index 00000000..d1009d1c --- /dev/null +++ b/docs/medley-irm/03-LISTS.TEDIT @@ -0,0 +1,582 @@ +INTERLISP-D REFERENCE MANUAL +LISTS +"3"(3.% % LISTS% + (CHAPTER) NIL NIL NIL NIL)3. LISTS +3 + +One of the most useful datatypes in Lisp is the list cell, a data structure that contains pointers to two other objects, called the CAR and the CDR of the list cell. You can build very complicated structures out of list cells, including lattices and trees, but most often they're used to represent simple linear lists of objects. +The following functions are used to manipulate individual list cells: +(CONS(CONS (Function) NIL NIL ("3") 1) X Y) [Function] +CONS is the primary list construction function. It creates and returns a new list cell containing pointers to X and Y. If Y is a list, this returns a list with X added at the beginning of Y. +(LISTP(LISTP (Function) NIL NIL ("3") 1) X) [Function] +Returns X if X is a list cell, e.g., something created by CONS; NIL otherwise. +(LISTP NIL) = NIL +(NLISTP(NLISTP (Function) NIL NIL ("3") 1) X) [Function] +The same as (NOT (LISTP X)). Returns T if X is not a list cell, NIL otherwise. However, (NLISTP NIL) = T +(CAR(CAR (Function) NIL NIL ("3") 1) X) [Function] +Returns the first element of the list X. CAR of NIL is always NIL. For all other nonlists (e.g., symbols, numbers, etc.), the value returned is controlled by CAR/CDRERR (below). +(CDR(CDR (Function) NIL NIL ("3") 1) X) [Function] +Returns all but the first element of the list X. CDR of NIL is always NIL. The value of CDR for other nonlists is controlled by CAR/CDRERR (below). +CAR/CDRERR(CAR/CDRERR (Variable) NIL NIL ("3") 1) [Variable] +The variable CAR/CDRERR controls the behavior of CAR and CDR when they are passed non-lists (other than NIL). +If CAR/CDRERR = NIL (the current default), then CAR or CDR of a non-list (other than NIL) return the string "{car of non-list}" or "{cdr of non-list}". If CAR/CDRERR = T, then CAR and CDR of a non-list (other than NIL) causes an error. +If CAR/CDRERR = ONCE, then CAR or CDR of a string causes an error, but CAR or CDR of anything else returns the string "{car of non-list}" or "{cdr of non-list}" as above. This catches loops which repeatedly take CAR or CDR of an object, but it allows one-time errors to pass undetected. +If CAR/CDRERR = CDR, then CAR of a non-list returns "{car of non-list}" as above, but CDR of a non-list causes an error. This setting is based on the observation that nearly all infinite loops involving non-lists occur from taking CDRs, but a fair amount of careless code takes CAR of something it has not tested to be a list. +(CAAR(CAAR (Function) NIL NIL ("3") 2) X) (CADR(CADR (Function) NIL NIL ("3") 2) X) ((CDDR (Function) NIL NIL ("3") 2)CDDR X) etc. (CAAAR (Function) NIL NIL ("3") 2) (CAADR (Function) NIL NIL ("3") 2) (CADAR (Function) NIL NIL ("3") 2) (CADDR (Function) NIL NIL ("3") 2) (CDAAR (Function) NIL NIL ("3") 2) (CDADR (Function) NIL NIL ("3") 2) (CDDAR (Function) NIL NIL ("3") 2) (CDDDR (Function) NIL NIL ("3") 2) (CAAAAR (Function) NIL NIL ("3") 2) (CAAADR (Function) NIL NIL ("3") 2) (CAADAR (Function) NIL NIL ("3") 2) (CAADDR (Function) NIL NIL ("3") 2) (CADAAR (Function) NIL NIL ("3") 2) (CADADR (Function) NIL NIL ("3") 2) (CADDAR (Function) NIL NIL ("3") 2) (CADDDR (Function) NIL NIL ("3") 2) (CDAAAR (Function) NIL NIL ("3") 2) (CDAADR (Function) NIL NIL ("3") 2) (CDADAR (Function) NIL NIL ("3") 2) (CDADDR (Function) NIL NIL ("3") 2) (CDDAAR (Function) NIL NIL ("3") 2) (CDDADR (Function) NIL NIL ("3") 2) (CDDDAR (Function) NIL NIL ("3") 2) (CDDDDR (Function) NIL NIL ("3") 2) [Function] +Often, combinations of CAR and CDR are used to extract parts of complex list structures. Functions of the form C...R may be used for some of these combinations: +(CAAR X) ==> (CAR (CAR X)) +(CADR X) ==> (CAR (CDR X)) +(CDDDDR X) ==> (CDR (CDR (CDR (CDR X)))) +All 30 combinations of nested CARs and CDRs up to 4 deep are included in the system. +(RPLACD(RPLACD (Function) NIL NIL ("3") 2) X Y) [Function] +Replaces the CDR of the list cell X with Y. This physically changes the internal structure of X, as opposed to CONS, which creates a new list cell. You can make a circular list by using RPLACD to place a pointer to the beginning of a list at the end of the list. +The value of RPLACD is X. An attempt to RPLACD NIL will cause an error, Attempt to RPLACD NIL (except for (RPLACD NIL NIL)). An attempt to RPLACD any other non-list will cause an error, Arg not list. +(RPLACA(RPLACD (Function) NIL NIL ("3") 2) X Y) [Function] +Like RPLACD, but replaces the CAR of X with Y. The value of RPLACA is X. An attempt to RPLACA NIL will cause an error, Attempt to RPLACA NIL, (except for (RPLACA NIL NIL)). An attempt to RPLACA any other non-list will cause an error, Arg not list. +(RPLNODE(RPLNODE (Function) NIL NIL ("3") 2) X A D) [Function] +Performs (RPLACA X A), (RPLACD X D), and returns X. +(RPLNODE2(RPLNODE2 (Function) NIL NIL ("3") 2) X Y) [Function] +Performs (RPLACA X (CAR Y)), (RPLACD X (CDR Y)) and returns X. +(FRPLACD(FRPLACD (Function) NIL NIL ("3") 2) X Y) [Function] +(FRPLACA(FRPLACA (Function) NIL NIL ("3") 2) X Y) [Function] +(FRPLNODE(FRPLNODE (Function) NIL NIL ("3") 2) X A D) [Function] +(FRPLNODE2(FRPLNODE2 (Function) NIL NIL ("3") 2) X Y) [Function] +Faster versions of RPLACD, etc. +Usually, you don't use list cells alone, but in structures called lists. A list is represented by a list cell whose CAR is the first element of the list, and whose CDR is the rest of the list. That's normally another list cell (with another element of the list) or the empty list, NIL, marking the list's end. List elements may be any Lisp objects, including other lists. +You type in a list as a sequence of Lisp data objects (symbols, numbers, other lists, etc.) enclosed in parentheses or brackets. Note that () is read as the symbol NIL. +Sometimes, you won't want your list to end in NIL, but just with the final element. To indicate that, type a period (with spaces on both sides) in front of the final element. This makes CDR of the list's final cell be the element immediately following the period, e.g. (A . B) or (A B C . D). Note that a list needn't end in NIL. It is simply a structure composed of one or more list cells. The input sequence (A B C . NIL) is equivalent to (A B C), and (A B . (C D)) is equivalent to (A B C D). Note, however, that (A B . C D) will create a list containing the five symbols A, B, %., C, and D. +Lists are printed by printing a left parenthesis, and then printing the first element of the list, a space, the second element, etc., until the final list cell is reached. The individual elements of a list are printed by PRIN1, if the list is being printed by PRIN1, and by PRIN2 if the list is being printed by PRINT or PRIN2. Lists are considered to terminate when CDR of some node is not a list. If CDR of this terminal node is NIL (the usual case), CAR of the last node is printed followed by a right parenthesis. If CDR of the terminal node is not NIL, CAR of the last node is printed, followed by a space, a period, another space, CDR of the last node, and the right parenthesis. A list input as (A B C . NIL) will print as (A B C), and a list input as (A B . (C D)) will print as (A B C D). PRINTLEVEL affects the printing of lists (see the PRINTLEVEL section of Chapter 25), and that carriage returns may be inserted where dictated by LINELENGTH (see the Output Functions section of Chapter 25). +Note: Be careful when testing the equality of list structures. EQ will be true only when the two lists are the exact same list. For example, + (SETQ A '(1 2)) +(1 2) + (SETQ B A) +(1 2) + (EQ A B) +T +(SETQ C '(1 2)) +(1 2) +(EQ A C) +NIL +(EQUAL A C) +T +In the example above, the values of A and B are the exact same list, so they are EQ. However, the value of C is a totally different list, although it happens to have the same elements. EQUAL should be used to compare the elements of two lists. In general, one should notice whether list manipulation functions use EQ or EQUAL for comparing lists. This is a frequent source of errors. +Creating Lists +1 + +(LIST(MKLIST (Function) NIL NIL ("3") 3) X1 X2 ... XN) [NoSpread Function] +Returns a list of its arguments, e.g. +(LIST 'A 'B '(C D)) => (A B (C D)) +(LIST*(LIST* (Function) NIL NIL ("3") 3) X1 X2 ... XN) [NoSpread Function] +Returns a list of its arguments, using the last argument for the tail of the list. This is like an iterated CONS: (LIST* A B C) == (CONS A (CONS B C)). For example, +(LIST* 'A 'B 'C) => (A B . C) +(LIST* 'A 'B '(C D)) => (A B C D) +(APPEND(APPEND (Function) NIL NIL ("3") 4) X1 X2 ... XN) [NoSpread Function] +Copies the top level of the list X1 and appends this to a copy of the top level of the list X2 appended to ... appended to XN, e.g., +(APPEND '(A B) '(C D E) '(F G)) => (A B C D E F G) +Only the first N-1 lists are copied. However N = 1 is treated specially; (APPEND X) copies the top level of a single list. To copy a list to all levels, use COPY. +The following examples illustrate the treatment of non-lists: +(APPEND '(A B C) 'D) => (A B C . D) +(APPEND 'A '(B C D)) => (B C D) +(APPEND '(A B C . D) '(E F G)) => (A B C E F G) +(APPEND '(A B C . D)) => (A B C . D) +(NCONC(NCONC (Function) NIL NIL ("3") 4) X1 X2 ... XN) [NoSpread Function] +Returns the same value as APPEND, but modifies the list structure of X1 ... Xn-1. +NCONC cannot change NIL to a list: +(SETQ FOO NIL) +NIL +(NCONC FOO '(A B C)) +(A B C) +FOO +NIL +Although the value of the NCONC is (A B C), FOO has not been changed. The problem is that while it is possible to alter list structure with RPLACA and RPLACD, there is no way to change the non-list NIL to a list. +(NCONC1(NCONC1 (Function) NIL NIL ("3") 4) LST X) [Function] +Adds X to the end of LST: (NCONC LST (LIST X)) +(ATTACH(ATTACH (Function) NIL NIL ("3") 4) X L) [Function] +Attaches X to the front of L by doing a RPLACA and RPLACD. The value is EQUAL to (CONS X L), but EQ to L, which it physically changes (except if L is NIL). (ATTACH X NIL) is the same as (CONS X NIL). Otherwise, if L is not a list, an error is generated, Arg not list. +(MKLIST(MKLIST (Function) NIL NIL ("3") 4) X) [Function] +Make List. If X is a list or NIL, returns X; Otherwise, returns (LIST X). +Building Lists From Left to Right +1 + +(TCONC(TCONC (Function) NIL NIL ("3") 4) PTR X) [Function] +TCONC is similar to NCONC1; it is useful for building a list by adding elements one at a time at the end. Unlike NCONC1, TCONC does not have to search to the end of the list each time it is called. Instead, it keeps a pointer to the end of the list being assembled, and updates this pointer after each call. This can be considerably faster for long lists. The cost is an extra list cell, PTR. (CAR PTR) is the list being assembled, (CDR PTR) is (LAST (CAR PTR)). TCONC returns PTR, with its CAR and CDR appropriately modified. +PTR can be initialized in two ways. If PTR is NIL, TCONC will create and return a PTR. In this case, the program must set some variable to the value of the first call to TCONC. After that, it is unnecessary to reset the variable, since TCONC physically changes its value. Example: +(SETQ FOO (TCONC NIL 1)) +((1) 1) +(for I from 2 to 5 do (TCONC FOO I)) +NIL +FOO +((1 2 3 4 5) 5) +If PTR is initially (NIL), the value of TCONC is the same as for PTR = NIL. but TCONC changes PTR. This method allows the program to initialize the TCONC variable before adding any elements to the list. Example: +(SETQ FOO (CONS)) +(NIL) +(for I from 1 to 5 do (TCONC FOO I)) +NIL +FOO +((1 2 3 4 5) 5) +(LCONC(LCONC (Function) NIL NIL ("3") 5) PTR X) [Function] +Where TCONC is used to add elements at the end of a list, LCONC is used for building a list by adding lists at the end, i.e., it is similar to NCONC instead of NCONC1. Example: +(SETQ FOO (CONS)) +(NIL) +(LCONC FOO '(1 2)) +((1 2) 2) +(LCONC FOO '(3 4 5)) +((1 2 3 4 5) 5) +(LCONC FOO NIL) +((1 2 3 4 5) 5) +LCONC uses the same pointer conventions as TCONC for eliminating searching to the end of the list, so that the same pointer can be given to TCONC and LCONC interchangeably. Therefore, continuing from above, +(TCONC FOO NIL) +((1 2 3 4 5 NIL) NIL) +(TCONC FOO '(3 4 5)) +((1 2 3 4 5 NIL (3 4 5)) (3 4 5)) +The functions DOCOLLECT and ENDCOLLECT also let you build lists from left-to-right like TCONC, but without the overhead of an extra list cell. The listis kept as a circular list. DOCOLLECT adds items; ENDCOLLECT replaces the tail with its second argument, and returns the full list. +(DOCOLLECT(DOCOLLECT (Function) NIL NIL ("3") 6) ITEM LST) [Function] +Adds ITEM to the end of LST. Returns the new circular list. Note that LST is modified, but it is not EQ to the new list. The new list should be stored and used as LST to the next call to DOCOLLECT. +(ENDCOLLECT(DOCOLLECT (Function) NIL NIL ("3") 6) LST TAIL) [Function] +Takes LST, a list returned by DOCOLLECT, and returns it as a non-circular list, adding TAIL as the terminating CDR. +Here is an example using DOCOLLECT and ENDCOLLECT. HPRINT is used to print the results because they are circular lists. Notice that FOO has to be set to the value of DOCOLLECT as each element is added. +(SETQ FOO NIL] +NIL +(HPRINT (SETQ FOO (DOCOLLECT 1 FOO] +(1 . {1}) +(HPRINT (SETQ FOO (DOCOLLECT 2 FOO] +(2 1 . {1}) +(HPRINT (SETQ FOO (DOCOLLECT 3 FOO] +(3 1 2 . {1}) +(HPRINT (SETQ FOO (DOCOLLECT 4 FOO] +(4 1 2 3 . {1}) +(SETQ FOO (ENDCOLLECT FOO 5] +(1 2 3 4 . 5) +The following two functions are useful when writing programs that reuse a scratch list to collect together some result(s) (both of these compile open): +(SCRATCHLIST(SCRATCHLIST (Function) NIL NIL ("3") 6) LST X1 X2 ... XN) [NLambda NoSpread Function] +SCRATCHLIST sets up a context in which the value of LST is used as a scratch list. The expressions X1, X2, ... XN are evaluated in turn. During the course of evaluation, any value passed to ADDTOSCRATCHLIST will be saved, reusing CONS cells from the value of LST. If the value of LST is not long enough, new CONS cells will be added onto its end. If the value of LST is NIL, the entire value of SCRATCHLIST will be new (i.e., no CONS cells will be reused). +(ADDTOSCRATCHLIST(ADDTOSCRATCHLIST (Function) NIL NIL ("3") 6) VALUE) [Function] +For use under calls to SCRATCHLIST. VALUE is added on to the end of the list of things being collected by SCRATCHLIST. When SCRATCHLIST returns, its value is a list containing all of the things added by ADDTOSCRATCHLIST. +Copying Lists +1 + +(COPY(COPY (Function) NIL NIL ("3") 6) X) [Function] +Creates and returns a copy of the list X. All levels of X are copied down to non-lists, so that if X contains arrays and strings, the copy of X will contain the same arrays and strings, not copies. COPY is recursive in the CAR direction only, so very long lists can be copied. +To copy just the top level of X, do (APPEND X). +(COPYALL(COPYALL (Function) NIL NIL ("3") 7) X) [Function] +Like COPY, but it copies down to atoms. Arrays, hash-arrays, strings, user data types, etc., are all copied. Analagous to EQUALALL (see the Equality Predicates section of Chapter 9). This will not work if given a data structure with circular pointers; in this case, use HCOPYALL. +(HCOPYALL(HCOPYALL (Function) NIL NIL ("3") 7) X) [Function] +Like COPYALL, but it will work even if the data structure contains circular pointers. +Extracting Tails of Lists +1 + +(NTH(NTH (Function) NIL NIL ("3") 7) X N) [Function] +Returns the tail of X beginning with the Nth element. Returns NIL if X has fewer than N elements. This is different from Common Lisp's NTH. Examples: +(NTH '(A B C D) 1) => (A B C D) +(NTH '(A B C D) 3) => (C D) +(NTH '(A B C D) 9) => NIL +(NTH '(A . B) 2) => B +For consistency, if N = 0, NTH returns (CONS NIL X): +(NTH '(A B) 0) => (NIL A B) +(FNTH(FNTH (Function) NIL NIL ("3") 7) X N) [Function] +Faster version of NTH that terminates on a null-check. +(LAST(LAST (Function) NIL NIL ("3") 7) X) [Function] +Returns the last list cell in the list X. Returns NIL if X is not a list. Examples: +(LAST '(A B C)) => (C) +(LAST '(A B . C)) => (B . C) +(LAST 'A) => NIL +(FLAST(FLAST (Function) NIL NIL ("3") 7) X) [Function] +Faster version of LAST that terminates on a null-check. +(NLEFT(NLEFT (Function) NIL NIL ("3") 7) L N TAIL) [Function] +NLEFT returns the tail of L that contains N more elements than TAIL. If L does not contain N more elements than TAIL, NLEFT returns NIL. If TAIL is NIL or not a tail of L, NLEFT returns the last N list cells in L. NLEFT can be used to work backwards through a list. Example: +(SETQ FOO '(A B C D E)) +(A B C D E) +(NLEFT FOO 2) +(D E) +(NLEFT FOO 1 (CDDR FOO)) +(B C D E) +(NLEFT FOO 3 (CDDR FOO)) +NIL +(LASTN(LASTN (Function) NIL NIL ("3") 8) L N) [Function] +Returns (CONS X Y), where Y is the last N elements of L, and X is the initial segment, e.g., +(LASTN '(A B C D E) 2) => ((A B C) D E) +(LASTN '(A B) 2) => (NIL A B) +Returns NIL if L is not a list containing at least N elements. +(TAILP(TAILP (Function) NIL NIL ("3") 8) X Y) [Function] +Returns X, if X is a tail of the list Y; otherwise NIL. X is a tail of Y if it is EQ to 0 or more CDRs of Y. +Note: If X is EQ to 1 or more CDRs of Y, X is called a proper tail. +Counting List Cells +1 + +(LENGTH(LENGTH (Function) NIL NIL ("3") 8) X) [Function] +Returns the length of the list X, where length is defined as the number of CDRs required to reach a non-list. Examples: +(LENGTH '(A B C)) => 3 +(LENGTH '(A B C . D)) => 3 +(LENGTH 'A) => 0 +(FLENGTH(FLENGTH (Function) NIL NIL ("3") 8) X) [Function] +Faster version of LENGTH that terminates on a null-check. +(EQLENGTH(EQLENGTH (Function) NIL NIL ("3") 8) X N) [Function] +Equivalent to (EQUAL (LENGTH X) N), but more efficient, because EQLENGTH stops as soon as it knows that X is longer than N. EQLENGTH is safe to use on (possibly) circular lists, since it is bounded by N. +(COUNT(COUNT (Function) NIL NIL ("3") 8) X) [Function] +Returns the number of list cells in the list X. Thus, COUNT is like a LENGTH that goes to all levels. COUNT of a non-list is 0. Examples: +(COUNT '(A)) => 1 +(COUNT '(A . B)) => 1 +(COUNT '(A (B) C)) => 4 +In this last example, the value is 4 because the list (A X C) uses three list cells for any object X, and (B) uses another list cell. +(COUNTDOWN(COUNT (Function) NIL NIL ("3") 8) X N) [Function] +Counts the number of list cells in X, decrementing N for each one. Stops and returns N when it finishes counting, or when N reaches 0. COUNTDOWN can be used on circular structures since it is bounded by N. Examples: +(COUNTDOWN '(A) 100) => 99 +(COUNTDOWN '(A . B) 100) => 99 +(COUNTDOWN '(A (B) C) 100) => 96 +(COUNTDOWN (DOCOLLECT 1 NIL) 100) => 0 +(EQUALN(EQUALN (Function) NIL NIL ("3") 9) X Y DEPTH) [Function] +Like EQUAL, for use with (possibly) circular structures. Whenever the depth of CAR recursion plus the depth of CDR recursion exceeds DEPTH, EQUALN does not search further along that chain, and returns the symbol ?. If recursion never exceeds DEPTH, EQUALN returns T if the expressions X and Y are EQUAL; otherwise NIL. +(EQUALN '(((A)) B) '(((Z)) B) 2) => ? +(EQUALN '(((A)) B) '(((Z)) B) 3) => NIL +(EQUALN '(((A)) B) '(((A)) B) 3) => T +Set Operations +1 + +(INTERSECTION(INTERSECTION (Function) NIL NIL ("3") 9) X Y) [Function] +Returns a list whose elements are members of both lists X and Y (using EQUAL to do compares). +Note that (INTERSECTION X X) gives a list of all members of X without duplicates. +(UNION(INTERSECTION (Function) NIL NIL ("3") 9) X Y) [Function] +Returns a (new) list consisting of all elements included on either of the two original lists (using EQUAL to compare elements). It is more efficient for X to be the shorter list. +The value of UNION is Y with all elements of X not in Y CONSed on the front of it. Therefore, if an element appears twice in Y, it will appear twice in (UNION X Y). Since (UNION '(A) '(A A)) = (A A), while (UNION '(A A) '(A)) = (A), UNION is non-commutative. +(LDIFFERENCE(LDIFFERENCE (Function) NIL NIL ("3") 9) X Y) [Function] +List Difference. Returns a list of the elements in X that are not members of Y (using EQUAL to compare elements). +Note: If X and Y share no elements, LDIFFERENCE returns a copy of X. +(LDIFF(INTERSECTION (Function) NIL NIL ("3") 9) LST TAIL ADD) [Function] +TAIL must be a tail of LST, i.e., EQ to the result of applying some number of CDRs to LST. (LDIFF LST TAIL) returns a list of all elements in LST up to TAIL. +If ADD is not NIL, the value of LDIFF is effectively (NCONC ADD (LDIFF LST TAIL)), i.e., the list difference is added at the end of ADD. +If TAIL is not a tail of LST, LDIFF generates an error, LDIFF: not a tail. LDIFF terminates on a null-check, so it will go into an infinite loop if LST is a circular list and TAIL is not a tail. +Example: +(SETQ FOO '(A B C D E F)) +(A B C D E F) +(CDDR FOO) +(C D E F) +(LDIFF FOO (CDDR FOO)) +(A B) +(LDIFF FOO (CDDR FOO) '(1 2)) +(1 2 A B) +(LDIFF FOO '(C D E F)) +LDIFF: not a tail +(C D E F) +Note that the value of LDIFF is always new list structure unless TAIL = NIL, in which case the value is LST itself. +Searching Lists +1 + +(MEMB(MEMB (Function) NIL NIL ("3") 10) X Y) [Function] +Determines if X is a member of the list Y. If there is an element of Y EQ to X, returns the tail of Y starting with that element. Otherwise, returns NIL. Examples: +(MEMB 'A '(A (W) C D)) => (A (W) C D) +(MEMB 'C '(A (W) C D)) => (C D) +(MEMB 'W '(A (W) C D)) => NIL +(MEMB '(W) '(A (W) C D)) => NIL +(FMEMB(FMEMB (Function) NIL NIL ("3") 10) X Y) [Function] +Faster version of MEMB that terminates on a null-check. +(MEMBER(MEMBER (Function) NIL NIL ("3") 10) X Y) [Function] +Identical to MEMB except that it uses EQUAL instead of EQ to check membership of X in Y. Examples: +(MEMBER 'C '(A (W) C D)) => (C D) +(MEMBER 'W '(A (W) C D)) => NIL +(MEMBER '(W) '(A (W) C D)) => ((W) C D) +(EQMEMB(EQMEMB (Function) NIL NIL ("3") 10) X Y) [Function] +Returns T if either X is EQ to Y, or else Y is a list and X is an FMEMB of Y. +Substitution Functions +1 + +(SUBST(SUBST (Function) NIL NIL ("3") 10) NEW OLD EXPR) [Function] +Returns the result of substituting NEW for all occurrences of OLD in the expression EXPR. Substitution occurs whenever OLD is EQUAL to CAR of some subexpression of EXPR, or when OLD is atomic and EQ to a non-NIL CDR of some subexpression of EXPR. For example: +(SUBST 'A 'B '(C B (X . B))) => (C A (X . A)) +(SUBST 'A '(B C) '((B C) D B C)) => (A D B C) not (A D . A) +SUBST returns a copy of EXPR with the appropriate changes. Furthermore, if NEW is a list, it is copied at each substitution. +(DSUBST(DSUBST (Function) NIL NIL ("3") 11) NEW OLD EXPR) [Function] +Like SUBST, but it does not copy EXPR, but changes the list structure EXPR itself. Like SUBST, DSUBST substitutes with a copy of NEW. More efficient than SUBST. +(LSUBST(LSUBST (Function) NIL NIL ("3") 11) NEW OLD EXPR) [Function] +Like SUBST, but NEW is substituted as a segment of the list EXPR rather than as an element. For instance, +(LSUBST '(A B) 'Y '(X Y Z)) => (X A B Z) +If NEW is not a list, LSUBST returns a copy of EXPR with all OLD's deleted: +(LSUBST NIL 'Y '(X Y Z)) => (X Z) +(SUBLIS(SUBLIS (Function) NIL NIL ("3") 11) ALST EXPR FLG) [Function] +ALST is a list of pairs: +((OLD1 . NEW1) (OLD2 . NEW2) ... (OLDN . NEWN)) +Each OLDi is an atom. SUBLIS returns the result of substituting each NEWi for the corresponding OLDi in EXPR, e.g., +(SUBLIS '((A . X) (C . Y)) '(A B C D)) => (X B Y D) +If FLG = NIL, new structure is created only if needed, so if there are no substitutions, the value is EQ to EXPR. If FLG = T, the value is always a copy of EXPR. +(DSUBLIS(DSUBLIS (Function) NIL NIL ("3") 11) ALST EXPR FLG) [Function] +Like SUBLIS, but it changes the list structure EXPR itself instead of copying it. +(SUBPAIR(SUBPAIR (Function) NIL NIL ("3") 11) OLD NEW EXPR FLG) [Function] +Like SUBLIS, but elements of NEW are substituted for corresponding atoms of OLD in EXPR, e.g., +(SUBPAIR '(A C) '(X Y) '(A B C D)) => (X B Y D) +As with SUBLIS, new structure is created only if needed, or if FLG = T, e.g., if FLG = NIL and there are no substitutions, the value is EQ to EXPR. +If OLD ends in an atom other than NIL, the rest of the elements on NEW are substituted for that atom. For example, if OLD = (A B . C) and NEW = (U V X Y Z), U is substituted for A, V for B, and (X Y Z) for C. Similarly, if OLD itself is an atom (other than NIL), the entire list NEW is substituted for it. Examples: +(SUBPAIR '(A B . C) '(W X Y Z) '(C A B B Y)) => ((Y Z) W X X Y) +SUBST, DSUBST, and LSUBST all substitute copies of the appropriate expression, whereas SUBLIS, and DSUBLIS, and SUBPAIR substitute the identical structure (unless FLG = T). For example: + (SETQ FOO '(A B)) +(A B) + (SETQ BAR '(X Y Z)) +(X Y Z) + (DSUBLIS (LIST (CONS 'X FOO)) BAR) +((A B) Y Z) + (DSUBLIS (LIST (CONS 'Y FOO)) BAR T) +((A B) (A B) Z) + (EQ (CAR BAR) FOO) +T + (EQ (CADR BAR) FOO) +NIL +Association Lists and Property Lists +1 + +It is often useful to associate a set of property names (NAME1, NAME2, etc.), with a set of property values (VALUE1, VALUE2, etc.). Two list structures commonly used to store such associations are called property lists and association lists. A list in association list format is a list where each element is a call whose CAR is a property name, and whose CDR is the value: +( (NAME1 . VALUE1) (NAME2 . VALUE2) ...) +A list in property list format is a list where the first, third, etc. elements are the property names, and the second, forth, etc. elements are the associated values: +( NAME1 VALUE1 NAME2 VALUE2 ...) +Another data structure that offers some of the advantages of association lists and property lists is the hash array (see the first page of Chapter 6). +The functions below provide facilities for searching and changing lists in property list or association list format. +Note: Property lists are used in many Medley system datatypes. There are special functions that can be used to set and retrieve values from the property lists of symbols (see the Property Lists section of Chapter 2), from properties of windows (see the Window Properties section of Chapter 28), etc. +(ASSOC(ASSOC (Function) NIL NIL ("3") 12) KEY ALST) [Function] +ALST is a list of lists. ASSOC returns the first sublist of ALST whose CAR is EQ to KEY. If such a list is not found, ASSOC returns NIL. Example: +(ASSOC 'B '((A . 1) (B . 2) (C . 3))) => (B . 2) +(FASSOC(FASSOC (Function) NIL NIL ("3") 12) KEY ALST) [Function] +Faster version of ASSOC that terminates on a null-check. +(SASSOC(SASSOC (Function) NIL NIL ("3") 12) KEY ALST) [Function] +Same as ASSOC, but uses EQUAL instead of EQ when searching for KEY. +(PUTASSOC(PUTASSOC (Function) NIL NIL ("3") 12) KEY VAL ALST) [Function] +Searches ALST for a sublist CAR of which is EQ to KEY. If one is found, the CDR is replaced (using RPLACD) with VAL. If no such sublist is found, (CONS KEY VAL) is added at the end of ALST. Returns VAL. If ALST is not a list, generates an error, Arg not list. +The argument order for ASSOC, PUTASSOC, etc. is different from that of LISTGET, LISTPUT, etc. +(LISTGET(LISTGET (Function) NIL NIL ("3") 13) LST PROP) [Function] +Searches LST two elements at a time, by CDDR, looking for an element EQ to PROP. If one is found, returns the next element of LST, otherwise NIL. Returns NIL if LST is not a list. Example: +(LISTGET '(A 1 B 2 C 3) 'B) => 2 +(LISTGET '(A 1 B 2 C 3) 'W) => NIL +(LISTPUT(LISTGET (Function) NIL NIL ("3") 13) LST PROP VAL) [Function] +Searches LST two elements at a time, by CDDR, looking for an element EQ to PROP. If PROP is found, replaces the next element of LST with VAL. Otherwise, PROP and VAL are added to the end of LST. If LST is a list with an odd number of elements, or ends in a non-list other than NIL, PROP and VAL are added at its beginning. Returns VAL. If LST is not a list, generates an error, Arg not list. +(LISTGET1(LISTGET1 (Function) NIL NIL ("3") 13) LST PROP) [Function] +Like LISTGET, but searches LST one CDR at a time, i.e., looks at each element. Returns the next element after PROP. Examples: +(LISTGET1 '(A 1 B 2 C 3) 'B) => 2 +(LISTGET1 '(A 1 B 2 C 3) '1) => B +(LISTGET1 '(A 1 B 2 C 3) 'W) => NIL +(LISTPUT1(LISTPUT1 (Function) NIL NIL ("3") 13) LST PROP VAL) [Function] +Like LISTPUT, but searches LST one CDR at a time. Returns the modified LST. Example: +(SETQ FOO '(A 1 B 2)) +(A 1 B 2) +(LISTPUT1 FOO 'B 3) +(A 1 B 3) +(LISTPUT1 FOO 'C 4) +(A 1 B 3 C 4) +(LISTPUT1 FOO 1 'W) +(A 1 W 3 C 4) +FOO +(A 1 W 3 C 4) +If LST is not a list, no error is generated. However, since a non-list cannot be changed into a list, LST is not modified. In this case, the value of LISTPUT1 should be saved. Example: +(SETQ FOO NIL) +NIL +(LISTPUT1 FOO 'A 5) +(A 5) +FOO +NIL +Sorting Lists +1 + +(SORT(SORT (Function) NIL NIL ("3") 14) DATA COMPAREFN) [Function] +DATA is a list of items to be sorted using COMPAREFN, a predicate function of two arguments which can compare any two items on DATA and return T if the first one belongs before the second. If COMPAREFN is NIL, ALPHORDER is used; thus (SORT DATA) will alphabetize a list. If COMPAREFN is T, CAR's of items that are lists are given to ALPHORDER, otherwise the items themselves; thus (SORT A-LIST T) will alphabetize an assoc list by the CAR of each item. (SORT X 'ILESSP) will sort a list of integers. +The value of SORT is the sorted list. The sort is destructive and uses no extra storage. The value returned is EQ to DATA but elements have been switched around. There is no safe way to interrupt SORT. If you abort a call to SORT by any means, you may loose elements from the list beeing sorted. The algorithm used by SORT is such that the maximum number of compares is N*log2N, where N is (LENGTH DATA). +Note: If (COMPAREFN A B) = (COMPAREFN B A), then the ordering of A and B may or may not be preserved. +For example, if (FOO . FIE) appears before (FOO . FUM) in X, (SORT X T) may or may not reverse the order of these two elements. +(MERGE(MERGE (Function) NIL NIL ("3") 14) A B COMPAREFN) [Function] +A and B are lists which have previously been sorted using SORT and COMPAREFN. Value is a destructive merging of the two lists. It does not matter which list is longer. After merging both A and B are equal to the merged list. (In fact, (CDR A) is EQ to (CDR B)). +(ALPHORDER(ALPHORDER (Function) NIL NIL ("3") 14) A B CASEARRAY) [Function] +A predicate function of two arguments, for alphabetizing. Returns a non-NIL value if its arguments are in lexicographic order, i.e., if B does not belong before A. Numbers come before literal atoms, and are ordered by magnitude (using GREATERP). Literal atoms and strings are ordered by comparing the character codes in their print names. Thus (ALPHORDER 23 123) is T, whereas (ALPHORDER 'A23 'A123) is NIL, because the character code for the digit 2 is greater than the code for 1. +Atoms and strings are ordered before all other data types. If neither A nor B are atoms or strings, the value of ALPHORDER is always T. +If CASEARRAY is non-NIL, it is a casearray (see the Random Access File Operations section of Chapter 25) that the characters of A and B are translated through before being compared. Numbers are not passed through CASEARRAY. +Note: If either A or B is a number, the value returned in the true case is T. Otherwise, ALPHORDER returns either EQUAL or LESSP to discriminate the cases of A and B being equal or unequal strings/atoms. +Note: ALPHORDER does no UNPACKs, CHCONs, CONSes or NTHCHARs. It is several times faster for alphabetizing than anything that can be written using these other functions. +(UALPHORDER(UALPHORDER (Function) NIL NIL ("3") 15) A B) [Function] +Defined as (ALPHORDER A B UPPERCASEARRAY). UPPERCASEARRAY maps every lowercase character into the corresponding uppercase character. For more information on UPPERCASEARRAY see Chapter 25. +(MERGEINSERT(MERGEINSERT (Function) NIL NIL ("3") 15) NEW LST ONEFLG) [Function] +LST is NIL or a list of partially sorted items. MERGEINSERT tries to find the best place to (destructively) insert NEW, e.g., +(MERGEINSERT 'FIE2 '(FOO FOO1 FIE FUM)) => (FOO FOO1 FIE FIE2 FUM) +Returns LST. MERGEINSERT is undoable. +If ONEFLG = T and NEW is already a member of LST, MERGEINSERT does nothing and returns LST. +MERGEINSERT is used by ADDTOFILE (see the Functions for Manipulating File Command Lists section of Chapter 17) to insert the name of a new function into a list of functions. The algorithm is essentially to look for the item with the longest common leading sequence of characters with respect to NEW, and then merge NEW in starting at that point. +Other List Functions +1 + +(REMOVE(REMOVE (Function) NIL NIL ("3") 15) X L) [Function] +Removes all top-level occurrences of X from list L, returning a copy of L with all elements EQUAL to X removed. Example: +(REMOVE 'A '(A B C (A) A)) => (B C (A)) +(REMOVE '(A) '(A B C (A) A)) => (A B C A) +(DREMOVE(DREMOVE (Function) NIL NIL ("3") 15) X L) [Function] +Like REMOVE, but uses EQ instead of EQUAL, and actually modifies the list L when removing X, and thus does not use any additional storage. More efficient than REMOVE. +DREMOVE cannot change a list to NIL: +(SETQ FOO '(A)) +(A) +(DREMOVE 'A FOO) +NIL +FOO +(A) +The DREMOVE above returns NIL, and does not perform any CONSes, but the value of FOO is still (A), because there is no way to change a list to a non-list. See NCONC. +(REVERSE(REVERSE (Function) NIL NIL ("3") 15) L) [Function] +Reverses (and copies) the top level of a list, e.g., +(REVERSE '(A B (C D))) => ((C D) B A) +If L is not a list, REVERSE just returns L. +(DREVERSE(DREVERSE (Function) NIL NIL ("3") 16) L) [Function] +Value is the same as that of REVERSE, but DREVERSE destroys the original list L and thus does not use any additional storage. More efficient than REVERSE. +(COMPARELISTS(COMPARELISTS (Function) NIL NIL ("3") 16) X Y) [Function] +Compares the list structures X and Y and prints a description of any differences to the terminal. If X and Y are EQUAL lists, COMPARELISTS simply prints out SAME. Returns NIL. +COMPARELISTS prints a terse description of the differences between the two list structures, highlighting the items that have changed. This printout is not a complete and perfect comparison. If X and Y are radically different list structures, the printout will not be very useful. COMPARELISTS is meant to be used as a tool to help users isolate differences between similar structures. +When a single element has been changed for another, COMPARELISTS prints out items such as (A -> B), for example: +(COMPARELISTS '(A B C D) '(X B E D)) +(A -> X) (C -> E) +NIL +When there are more complex differences between the two lists, COMPARELISTS prints X and Y, highlighting differences and abbreviating similar elements as much as possible. & is used to signal a single element that is present in the same place in the two lists; -- signals an arbitrary number of elements in one list but not in the other; -2-, -3-, etc. signal a sequence of two, three, etc. elements that are the same in both lists. Examples: +(COMPARELISTS '(A B C D) '(A D)) +(A B C --) +(A D) +(COMPARELISTS '(A B C D E F G H) '(A B C D X)) +(A -3- E F --) +(A -3- X) +(COMPARELISTS '(A B C (D E F (G) H) I) '(A B (G) C (D E F H) I)) +(A & & (D -2- (G) &) &) +(A & (G) & (D -2- &) &) +(NEGATE(NEGATE (Function) NIL NIL ("3") 16) X) [Function] +For a form X, returns a form which computes the negation of X . For example: +(NEGATE '(MEMBER X Y)) => (NOT (MEMBER X Y)) +(NEGATE '(EQ X Y)) => (NEQ X Y) +(NEGATE '(AND X (NLISTP X))) => (OR (NULL X) (LISTP X)) +(NEGATE NIL) => T + + +[This page intentionally left blank] +(SEQUENCE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "3-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "3-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (ALTERNATE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "3-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "3-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "3-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "3-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))))1$6$EVENT2$TE$ PAGEHEADING RIGHTPAGE1lx$1ll$1ll$1HH$506$T1~~$1l~$1l~$2 +T1$7`x$x1ll$1l~$1ll$2$T1ll$1l~$1l~$76Z$Z506$T406$406$1$7$D$ PAGEHEADINGLEFTBACKMODERN +GACHA +PALATINO TITAN TITAN TITAN +CLASSIC +TITAN PALATINO MODERN +PALATINO PALATINO CLASSIC +TITAN CLASSIC + +TIMESROMAN +MODERN HELVETICA + + IM.CHAP.GETFN)IM.INDEX.GETFN + HRULE.GETFN F !IM.INDEX.GETFN k% "IM.INDEX.GETFN,   #IM.INDEX.GETFN     IM.INDEX.GETFN & ^ + +  IM.INDEX.GETFN . % + + +'IM.INDEX.GETFN   +,  "$   4A  ,. !IM.INDEX.GETFN!IM.INDEX.GETFN!IM.INDEX.GETFN "IM.INDEX.GETFN "IM.INDEX.GETFN "IM.INDEX.GETFN "IM.INDEX.GETFN "IM.INDEX.GETFN "IM.INDEX.GETFN "IM.INDEX.GETFN "IM.INDEX.GETFN #IM.INDEX.GETFN #IM.INDEX.GETFN #IM.INDEX.GETFN #IM.INDEX.GETFN #IM.INDEX.GETFN #IM.INDEX.GETFN #IM.INDEX.GETFN #IM.INDEX.GETFN #IM.INDEX.GETFN #IM.INDEX.GETFN #IM.INDEX.GETFN #IM.INDEX.GETFN #IM.INDEX.GETFN #IM.INDEX.GETFN #IM.INDEX.GETFN #IM.INDEX.GETFN N- )+ #IM.INDEX.GETFN  5HG  (   #IM.INDEX.GETFN   '    $IM.INDEX.GETFN   %IM.INDEX.GETFN   $IM.INDEX.GETFN $IM.INDEX.GETFN  %IM.INDEX.GETFN  &IM.INDEX.GETFN w-uY.P #T    0# !*!A L?    +( +U +3@.   +  +  +  + + +  +$&N}<  + HRULE.GETFN #IM.INDEX.GETFN & % "IM.INDEX.GETFN m$ $ #IM.INDEX.GETFN !9   5 +K> &"2' "IM.INDEX.GETFN %    +  +  + X)  #IM.INDEX.GETFN  #IM.INDEX.GETFN   ) '   + #IM.INDEX.GETFN  + + ! + HRULE.GETFN + "IM.INDEX.GETFN X  +  %V>) + & +  + 4< + & +  + "IM.INDEX.GETFN  ' $   + + + + +&\4 + +"  +2X   +H  &IM.INDEX.GETFN -=   +&IM.INDEX.GETFN  0  +L   + % +  % +  % + % +  +  (IM.INDEX.GETFN  )/N4  -IM.INDEX.GETFN  A  D  + HRULE.GETFN + !IM.INDEX.GETFN '**83  $IM.INDEX.GETFN s %IM.INDEX.GETFNJ  + HRULE.GETFN  IM.INDEX.GETFN1  "    !IM.INDEX.GETFN " !IM.INDEX.GETFN '   "IM.INDEX.GETFN" "IM.INDEX.GETFN  9  +   +  + +  + "IM.INDEX.GETFN +  * "   "IM.INDEX.GETFN    + +  + HRULE.GETFN + #IM.INDEX.GETFN-+ $IM.INDEX.GETFN" %IM.INDEX.GETFN G "IM.INDEX.GETFN-   6&  "IM.INDEX.GETFN#"$  = !#) #IM.INDEX.GETFN FB  (*(  + HRULE.GETFN  )IM.INDEX.GETFN8 +   )IM.INDEX.GETFNd1 B   (IM.INDEX.GETFN6 +  )IM.INDEX.GETFN *#3D   +  + +  +  + +  + + +%   + HRULE.GETFN + "IM.INDEX.GETFN1 (" " #IM.INDEX.GETFN!  $IM.INDEX.GETFN   " ( $IM.INDEX.GETFN  +  + HRULE.GETFN + #IM.INDEX.GETFN #  + +.. 0/ $IM.INDEX.GETFN ! $IM.INDEX.GETFN  )++ + $ $IM.INDEX.GETFN )6Z  %IM.INDEX.GETFN $ %IM.INDEX.GETFN,21 .1  #@>,  +  + % +  ' +  +  + $ + HRULE.GETFN +9(w ) #IM.INDEX.GETFN   3 $IM.INDEX.GETFN" $IM.INDEX.GETFN +  &IM.INDEX.GETFN     $  ! %IM.INDEX.GETFN 0  #% %IM.INDEX.GETFN  (L&#   &IM.INDEX.GETFN I $$& &IM.INDEX.GETFN "  + +  + +  +  +  +a. +  +  +  + HRULE.GETFN + "IM.INDEX.GETFN' K 1   ( '' `LZ0     +  +   +; #IM.INDEX.GETFN 2  p)   'IM.INDEX.GETFN I=Jg +MF$  + iO  7 '   p  +(IM.INDEX.GETFN  +e  )IM.INDEX.GETFN% : C       + HRULE.GETFN + $IM.INDEX.GETFN%  *, %IM.INDEX.GETFN  !E   +  +  + ? %IM.INDEX.GETFN5 ( &IM.INDEX.GETFND  *IM.INDEX.GETFNB   Q ]4  & + +? SYLc ! +  + 0 + + + B + + $IM.INDEX.GETFN 0- 8%DATE:i z \ No newline at end of file diff --git a/docs/medley-irm/04-STRINGS.TEDIT b/docs/medley-irm/04-STRINGS.TEDIT index 8bc603c2..a17c1e68 100644 --- a/docs/medley-irm/04-STRINGS.TEDIT +++ b/docs/medley-irm/04-STRINGS.TEDIT @@ -1,76 +1,172 @@ - INTERLISP-D REFERENCE MANUAL STRINGS 4. STRINGS(4.% % STRINGS (Chapter) NIL NIL NIL NIL) 3 A string represents a sequence of characters. Interlisp strings are a subtype of Common Lisp strings. Medley provides functions for creating strings, concatenating strings, and creating sub-strings of a string; all accepting or producing Common Lisp-acceptable strings. A string is typed as a double quote ("), followed by a sequence of any characters except double quote and %, terminated by a double quote. To include % or " in a string, type % in front of them: "A string" "A string with %" in it, and a %%." "" ; an empty string Strings are printed by PRINT and PRIN2 with initial and final double quotes, and %s inserted where necessary for it to read back in properly. Strings are printed by PRIN1 without the double quotes and extra %s. The null string is printed by PRINT and PRIN2 as "". (PRIN1 "") doesn't print anything. Internally, a string is stored in two parts: a string header and the sequence of characters. Several string headers may refer to the the same character sequence, so a substring can be made by creating a new string header, without copying any characters. Functions that refer to strings actually manipulate string headers. Some functions take an old string argument, and re-use the string pointer. (STRINGP X) [Function] Returns X if X is a string, NIL otherwise. (STREQUAL X Y) [Function] Returns T if X and Y are both strings and they contain the same sequence of characters, otherwise NIL. EQUAL uses STREQUAL. Note that strings may be STREQUAL without being EQ. For instance, (STREQUAL "ABC" "ABC") => T (EQ "ABC" "ABC") => NIL STREQUAL returns T if X and Y are the same string pointer, or two different string pointers which point to the same character sequence, or two string pointers which point to different character sequences which contain the same characters. Only in the first case would X and Y be EQ. (STRING-EQUAL X Y) [Function] Returns T if X and Y are either strings or symbols, and they contain the same sequence of characters, ignoring case. For instance, (STRING-EQUAL "FOO" "Foo") => T (STRING-EQUAL "FOO" 'Foo) => T This is useful for comparing things that might want to be considered equal even though they're not both symbols in a consistent case, such as file names and user names. (STRING.EQUAL X Y) [Function] Returns T if the print names of X and Y contain the same sequence of characters, ignoring case. For instance, (STRING-EQUAL "320" 320) => T (STRING-EQUAL "FOO" 'Foo) => T This is like STRING-EQUAL, but handles numbers, etc., where STRING-EQUAL doesn't. (ALLOCSTRING N INITCHAR OLD FATFLG) [Function] Creates a string of length N characters of INITCHAR (which can be either a character code or something coercible to a character). If INITCHAR is NIL, it defaults to character code 0. if OLD is supplied, it must be a string pointer, which is modified and returned. If FATFLG is non-NIL, the string is allocated using full 16-bit NS characters (see Chapter 2) instead of 8-bit characters. This can speed up some string operations if NS characters are later inserted into the string. This has no other effect on the operation of the string functions. (MKSTRING X FLG RDTBL) [Function] If X is a string, returns X. Otherwise, creates and returns a string containing the print name of X. Examples: (MKSTRING "ABC") => "ABC" (MKSTRING '(A B C)) => "(A B C)" (MKSTRING NIL) => "NIL" Note that the last example returns the string "NIL", not the symbol NIL. If FLG is T, then the PRIN2-name of X is used, computed with respect to the readtable RDTBL. For example, (MKSTRING "ABC" T) => "%"ABC%"" (NCHARS X FLG RDTBL) [Function] Returns the number of characters in the print name of X. If FLG=T, the PRIN2-name is used. For example, (NCHARS 'ABC) => 3 (NCHARS "ABC" T) => 5 Note: NCHARS works most efficiently on symbols and strings, but can be given any object. (SUBSTRING(SUBSTRING (Function) NIL NIL ("4") 3) X N M OLDPTR) [Function] Returns the substring of X consisting of the Nth through Mth characters of X. If M is NIL, the substring contains the Nth character thru the end of X. N and M can be negative numbers, which are interpreted as counts back from the end of the string, as with NTHCHAR (Chapter 2). SUBSTRING returns NIL if the substring is not well defined, (e.g., N or M specify character positions outside of X, or N corresponds to a character in X to the right of the character indicated by M). Examples: (SUBSTRING "ABCDEFG" 4 6) => "DEF" (SUBSTRING "ABCDEFG" 3 3) => "C" (SUBSTRING "ABCDEFG" 3 NIL) => "CDEFG" (SUBSTRING "ABCDEFG" 4 -2) => "DEF" (SUBSTRING "ABCDEFG" 6 4) => NIL (SUBSTRING "ABCDEFG" 4 9) => NIL If X is not a string, it is converted to one. For example, (SUBSTRING '(A B C) 4 6) => "B C" SUBSTRING does not actually copy any characters, but simply creates a new string pointer to the characters in X. If OLDPTR is a string pointer, it is modified and returned. (GNC(GNC (Function) NIL NIL ("4") 3) X) [Function] Get Next Character. Returns the next character of the string X (as a symbol); also removes the character from the string, by changing the string pointer. Returns NIL if X is the null string. If X isn't a string, a string is made. Used for sequential access to characters of a string. Example: (SETQ FOO "ABCDEFG") "ABCDEFG" (GNC FOO) A (GNC FOO) B FOO "CDEFG" Note that if A is a substring of B, (GNC A) does not remove the character from B. (GLC(GLC (Function) NIL NIL ("4") 3) X) [Function] Get Last Character. Returns the last character of the string X (as a symbol); also removes the character from the string. Similar to GNC. Example: (SETQ FOO "ABCDEFG") "ABCDEFG" (GLC FOO) G (GLC FOO) F FOO "ABCDE" (CONCAT(CONCAT (Function) NIL NIL ("4") 4) X1 X2 ... XN) [NoSpread Function] Returns a new string which is the concatenation of (copies of) its arguments. Any arguments which are not strings are transformed to strings. Examples: (CONCAT "ABC" 'DEF "GHI") => "ABCDEFGHI" (CONCAT '(A B C) "ABC") => "(A B C)ABC" (CONCAT) returns the null string, "" (CONCATLIST(CONCATLIST (Function) NIL NIL ("4") 4) L) [Function] L is a list of strings and/or other objects. The objects are transformed to strings if they aren't strings. Returns a new string which is the concatenation of the strings. Example: (CONCATLIST '(A B (C D) "EF")) => "AB(C D)EF" (RPLSTRING(RPLSTRING (Function) NIL NIL ("4") 4) X N Y) [Function] Replaces the characters of string X beginning at character position N with string Y. X and Y are converted to strings if they aren't already. N may be positive or negative, as with SUBSTRING. Characters are smashed into (converted) X. Returns the string X. Examples: (RPLSTRING "ABCDEF" -3 "END") => "ABCEND" (RPLSTRING "ABCDEFGHIJK" 4 '(A B C)) => "ABC(A B C)K" Generates an error if there is not enough room in X for Y, i.e., the new string would be longer than the original. If Y was not a string, X will already have been modified since RPLSTRING does not know whether Y will fit without actually attempting the transfer. Warning: In some implementations of Interlisp, if X is a substring of Z, Z will also be modified by the action of RPLSTRING or RPLCHARCODE. However, this is not guaranteed to be true in all cases, so programmers should not rely on RPLSTRING or RPLCHARCODE altering the characters of any string other than the one directly passed as argument to those functions. (RPLCHARCODE(RPLCHARCODE (Function) NIL NIL ("4") 4) X N CHAR) [Function] Replaces the Nth character of the string X with the character code CHAR. N may be positive or negative. Returns the new X. Similar to RPLSTRING. Example: (RPLCHARCODE "ABCDE" 3 (CHARCODE F)) => "ABFDE" (STRPOS(STRPOS (Function) NIL NIL ("4") 4) PAT STRING START SKIP ANCHOR TAIL CASEARRAY BACKWARDSFLG) [Function] STRPOS is a function for searching one string looking for another. PAT and STRING are both strings (or else they are converted automatically). STRPOS searches STRING beginning at character number START, (or 1 if START is NIL) and looks for a sequence of characters equal to PAT. If a match is found, the character position of the first matching character in STRING is returned, otherwise NIL. Examples: (STRPOS "ABC" "XYZABCDEF") => 4 (STRPOS "ABC" "XYZABCDEF" 5) => NIL (STRPOS "ABC" "XYZABCDEFABC" 5) => 10 SKIP can be used to specify a character in PAT that matches any character in STRING. Examples: (STRPOS "A&C&" "XYZABCDEF" NIL '&) => 4 (STRPOS "DEF&" "XYZABCDEF" NIL '&) => NIL If ANCHOR is T, STRPOS compares PAT with the characters beginning at position START (or 1 if START is NIL). If that comparison fails, STRPOS returns NIL without searching any further down STRING. Thus it can be used to compare one string with some portion of another string. Examples: (STRPOS "ABC" "XYZABCDEF" NIL NIL T) => NIL (STRPOS "ABC" "XYZABCDEF" 4 NIL T) => 4 If TAIL is T, the value returned by STRPOS if successful is not the starting position of the sequence of characters corresponding to PAT, but the position of the first character after that, i.e., the starting position plus (NCHARS PAT). Examples: (STRPOS "ABC" "XYZABCDEFABC" NIL NIL NIL T) => 7 (STRPOS "A" "A" NIL NIL NIL T) => 2 If TAIL = NIL, STRPOS returns NIL, or a character position within STRING which can be passed to SUBSTRING. In particular, (STRPOS "" "") => NIL. However, if TAIL = T, STRPOS may return a character position outside of STRING. For instance, note that the second example above returns 2, even though A has only one character. If CASEARRAY is non-NIL, this should be a casearray like that given to FILEPOS (Chapter 25). The casearray is used to map the string characters before comparing them to the search string. If BACKWARDSFLG is non-NIL, the search is done backwards from the end of the string. (STRPOSL(STRPOSL (Function) NIL NIL ("4") 5) A STRING START NEG BACKWARDSFLG) [Function] STRING is a string (or is converted automatically to a string), A is a list of characters or character codes. STRPOSL searches STRING beginning at character number START (or 1 if START = NIL) for one of the characters in A. If one is found, STRPOSL returns as its value the corresponding character position, otherwise NIL. Example: (STRPOSL '(A B C) "XYZBCD") => 4 If NEG = T, STRPOSL searches for a character not on A. Example: (STRPOSL '(A B C) "ABCDEF" NIL T) => 4 If any element of A is a number, it is assumed to be a character code. Otherwise, it is converted to a character code via CHCON1. Therefore, it is more efficient to call STRPOSL with A a list of character codes. If A is a bit table, it is used to specify the characters (see MAKEBITTABLE below) If BACKWARDSFLG is non-NIL, the search is done backwards from the end of the string. STRPOSL uses a bit table data structure to search efficiently. If A is not a bit table, it is converted to a bit table using MAKEBITTABLE. If STRPOSL is to be called frequently with the same list of characters, a considerable savings can be achieved by converting the list to a bit table once, and then passing the bit table to STRPOSL as its first argument. (MAKEBITTABLE(MAKEBITTABLE (Function) NIL NIL ("4") 6) L NEG A) [Function] Returns a bit table suitable for use by STRPOSL. L is a list of characters or character codes, NEG is the same as described for STRPOSL. If A is a bit table, MAKEBITTABLE modifies and returns it. Otherwise, it will create a new bit table. Note: If NEG = T, STRPOSL must call MAKEBITTABLE whether A is a list or a bit table. To obtain bit table efficiency with NEG=T, MAKEBITTABLE should be called with NEG=T, and the resulting inverted bit table should be given to STRPOSL with NEG=NIL. [This page intentionally left blank] (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "4-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "4-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "4-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "4-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "4-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "4-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))5,1EVENT-T,l~,~~,l~,,@ PAGEHEADING RIGHTPAGE? PAGEHEADINGLEFTBACK,ll,HH306 -T, -TIMESROMAN -PALATINO PALATINO PALATINO TITAN TITAN TITAN TITAN CLASSIC -TITAN + INTERLISP-D REFERENCE MANUAL +STRINGS +"4" (4.% % STRINGS (CHAPTER) NIL NIL NIL NIL)4. STRINGS +3 + +A string represents a sequence of characters. Interlisp strings are a subtype of Common Lisp strings. Medley provides functions for creating strings, concatenating strings, and creating sub-strings of a string; all accepting or producing Common Lisp-acceptable strings. +A string is typed as a double quote ("), followed by a sequence of any characters except double quote and %, terminated by a double quote. To include % or " in a string, type % in front of them: + "A string" + "A string with %" in it, and a %%." + "" ; an empty string +Strings are printed by PRINT and PRIN2 with initial and final double quotes, and %s inserted where necessary for it to read back in properly. Strings are printed by PRIN1 without the double quotes and extra %s. The null string is printed by PRINT and PRIN2 as "". (PRIN1 "") doesn't print anything. +Internally, a string is stored in two parts: a string header and the sequence of characters. Several string headers may refer to the the same character sequence, so a substring can be made by creating a new string header, without copying any characters. Functions that refer to strings actually manipulate string headers. Some functions take an old string argument, and re-use the string pointer. +(STRINGP X) [Function] +Returns X if X is a string, NIL otherwise. +(STREQUAL X Y) [Function] +Returns T if X and Y are both strings and they contain the same sequence of characters, otherwise NIL. EQUAL uses STREQUAL. Note that strings may be STREQUAL without being EQ. For instance, +(STREQUAL "ABC" "ABC") => T +(EQ "ABC" "ABC") => NIL +STREQUAL returns T if X and Y are the same string pointer, or two different string pointers which point to the same character sequence, or two string pointers which point to different character sequences which contain the same characters. Only in the first case would X and Y be EQ. +(STRING-EQUAL X Y) [Function] +Returns T if X and Y are either strings or symbols, and they contain the same sequence of characters, ignoring case. For instance, +(STRING-EQUAL "FOO" "Foo") => T +(STRING-EQUAL "FOO" 'Foo) => T +This is useful for comparing things that might want to be considered equal even though they're not both symbols in a consistent case, such as file names and user names. +(STRING.EQUAL X Y) [Function] +Returns T if the print names of X and Y contain the same sequence of characters, ignoring case. For instance, +(STRING-EQUAL "320" 320) => T +(STRING-EQUAL "FOO" 'Foo) => T +This is like STRING-EQUAL, but handles numbers, etc., where STRING-EQUAL doesn't. +(ALLOCSTRING N INITCHAR OLD FATFLG) [Function] +Creates a string of length N characters of INITCHAR (which can be either a character code or something coercible to a character). If INITCHAR is NIL, it defaults to character code 0. if OLD is supplied, it must be a string pointer, which is modified and returned. +If FATFLG is non-NIL, the string is allocated using full 16-bit NS characters (see Chapter 2) instead of 8-bit characters. This can speed up some string operations if NS characters are later inserted into the string. This has no other effect on the operation of the string functions. +(MKSTRING X FLG RDTBL) [Function] +If X is a string, returns X. Otherwise, creates and returns a string containing the print name of X. Examples: +(MKSTRING "ABC") => "ABC" +(MKSTRING '(A B C)) => "(A B C)" +(MKSTRING NIL) => "NIL" +Note that the last example returns the string "NIL", not the symbol NIL. +If FLG is T, then the PRIN2-name of X is used, computed with respect to the readtable RDTBL. For example, +(MKSTRING "ABC" T) => "%"ABC%"" +(NCHARS X FLG RDTBL) [Function] +Returns the number of characters in the print name of X. If FLG=T, the PRIN2-name is used. For example, +(NCHARS 'ABC) => 3 +(NCHARS "ABC" T) => 5 +Note: NCHARS works most efficiently on symbols and strings, but can be given any object. +(SUBSTRING(SUBSTRING (Function) NIL NIL ("4") 3) X N M OLDPTR) [Function] +Returns the substring of X consisting of the Nth through Mth characters of X. If M is NIL, the substring contains the Nth character thru the end of X. N and M can be negative numbers, which are interpreted as counts back from the end of the string, as with NTHCHAR (Chapter 2). SUBSTRING returns NIL if the substring is not well defined, (e.g., N or M specify character positions outside of X, or N corresponds to a character in X to the right of the character indicated by M). Examples: +(SUBSTRING "ABCDEFG" 4 6) => "DEF" +(SUBSTRING "ABCDEFG" 3 3) => "C" +(SUBSTRING "ABCDEFG" 3 NIL) => "CDEFG" +(SUBSTRING "ABCDEFG" 4 -2) => "DEF" +(SUBSTRING "ABCDEFG" 6 4) => NIL +(SUBSTRING "ABCDEFG" 4 9) => NIL +If X is not a string, it is converted to one. For example, +(SUBSTRING '(A B C) 4 6) => "B C" +SUBSTRING does not actually copy any characters, but simply creates a new string pointer to the characters in X. If OLDPTR is a string pointer, it is modified and returned. +(GNC(GNC (Function) NIL NIL ("4") 3) X) [Function] +Get Next Character. Returns the next character of the string X (as a symbol); also removes the character from the string, by changing the string pointer. Returns NIL if X is the null string. If X isn't a string, a string is made. Used for sequential access to characters of a string. Example: +(SETQ FOO "ABCDEFG") +"ABCDEFG" +(GNC FOO) +A +(GNC FOO) +B +FOO +"CDEFG" +Note that if A is a substring of B, (GNC A) does not remove the character from B. +(GLC(GLC (Function) NIL NIL ("4") 3) X) [Function] +Get Last Character. Returns the last character of the string X (as a symbol); also removes the character from the string. Similar to GNC. Example: +(SETQ FOO "ABCDEFG") +"ABCDEFG" +(GLC FOO) +G +(GLC FOO) +F +FOO +"ABCDE" +(CONCAT(CONCAT (Function) NIL NIL ("4") 4) X1 X2 ... XN) [NoSpread Function] +Returns a new string which is the concatenation of (copies of) its arguments. Any arguments which are not strings are transformed to strings. Examples: +(CONCAT "ABC" 'DEF "GHI") => "ABCDEFGHI" +(CONCAT '(A B C) "ABC") => "(A B C)ABC" +(CONCAT) returns the null string, "" +(CONCATLIST(CONCATLIST (Function) NIL NIL ("4") 4) L) [Function] +L is a list of strings and/or other objects. The objects are transformed to strings if they aren't strings. Returns a new string which is the concatenation of the strings. Example: +(CONCATLIST '(A B (C D) "EF")) => "AB(C D)EF" +(RPLSTRING(RPLSTRING (Function) NIL NIL ("4") 4) X N Y) [Function] +Replaces the characters of string X beginning at character position N with string Y. X and Y are converted to strings if they aren't already. N may be positive or negative, as with SUBSTRING. Characters are smashed into (converted) X. Returns the string X. Examples: +(RPLSTRING "ABCDEF" -3 "END") => "ABCEND" +(RPLSTRING "ABCDEFGHIJK" 4 '(A B C)) => "ABC(A B C)K" +Generates an error if there is not enough room in X for Y, i.e., the new string would be longer than the original. If Y was not a string, X will already have been modified since RPLSTRING does not know whether Y will fit without actually attempting the transfer. +Warning: In some implementations of Interlisp, if X is a substring of Z, Z will also be modified by the action of RPLSTRING or RPLCHARCODE. However, this is not guaranteed to be true in all cases, so programmers should not rely on RPLSTRING or RPLCHARCODE altering the characters of any string other than the one directly passed as argument to those functions. +(RPLCHARCODE(RPLCHARCODE (Function) NIL NIL ("4") 4) X N CHAR) [Function] +Replaces the Nth character of the string X with the character code CHAR. N may be positive or negative. Returns the new X. Similar to RPLSTRING. Example: +(RPLCHARCODE "ABCDE" 3 (CHARCODE F)) => "ABFDE" +(STRPOS(STRPOS (Function) NIL NIL ("4") 4) PAT STRING START SKIP ANCHOR TAIL CASEARRAY BACKWARDSFLG) [Function] +STRPOS is a function for searching one string looking for another. PAT and STRING are both strings (or else they are converted automatically). STRPOS searches STRING beginning at character number START, (or 1 if START is NIL) and looks for a sequence of characters equal to PAT. If a match is found, the character position of the first matching character in STRING is returned, otherwise NIL. Examples: +(STRPOS "ABC" "XYZABCDEF") => 4 +(STRPOS "ABC" "XYZABCDEF" 5) => NIL +(STRPOS "ABC" "XYZABCDEFABC" 5) => 10 +SKIP can be used to specify a character in PAT that matches any character in STRING. Examples: +(STRPOS "A&C&" "XYZABCDEF" NIL '&) => 4 +(STRPOS "DEF&" "XYZABCDEF" NIL '&) => NIL +If ANCHOR is T, STRPOS compares PAT with the characters beginning at position START (or 1 if START is NIL). If that comparison fails, STRPOS returns NIL without searching any further down STRING. Thus it can be used to compare one string with some portion of another string. Examples: +(STRPOS "ABC" "XYZABCDEF" NIL NIL T) => NIL +(STRPOS "ABC" "XYZABCDEF" 4 NIL T) => 4 +If TAIL is T, the value returned by STRPOS if successful is not the starting position of the sequence of characters corresponding to PAT, but the position of the first character after that, i.e., the starting position plus (NCHARS PAT). Examples: +(STRPOS "ABC" "XYZABCDEFABC" NIL NIL NIL T) => 7 +(STRPOS "A" "A" NIL NIL NIL T) => 2 +If TAIL = NIL, STRPOS returns NIL, or a character position within STRING which can be passed to SUBSTRING. In particular, (STRPOS "" "") => NIL. However, if TAIL = T, STRPOS may return a character position outside of STRING. For instance, note that the second example above returns 2, even though A has only one character. +If CASEARRAY is non-NIL, this should be a casearray like that given to FILEPOS (Chapter 25). The casearray is used to map the string characters before comparing them to the search string. +If BACKWARDSFLG is non-NIL, the search is done backwards from the end of the string. +(STRPOSL(STRPOSL (Function) NIL NIL ("4") 5) A STRING START NEG BACKWARDSFLG) [Function] +STRING is a string (or is converted automatically to a string), A is a list of characters or character codes. STRPOSL searches STRING beginning at character number START (or 1 if START = NIL) for one of the characters in A. If one is found, STRPOSL returns as its value the corresponding character position, otherwise NIL. Example: +(STRPOSL '(A B C) "XYZBCD") => 4 +If NEG = T, STRPOSL searches for a character not on A. Example: +(STRPOSL '(A B C) "ABCDEF" NIL T) => 4 +If any element of A is a number, it is assumed to be a character code. Otherwise, it is converted to a character code via CHCON1. Therefore, it is more efficient to call STRPOSL with A a list of character codes. +If A is a bit table, it is used to specify the characters (see MAKEBITTABLE below) +If BACKWARDSFLG is non-NIL, the search is done backwards from the end of the string. +STRPOSL uses a bit table data structure to search efficiently. If A is not a bit table, it is converted to a bit table using MAKEBITTABLE. If STRPOSL is to be called frequently with the same list of characters, a considerable savings can be achieved by converting the list to a bit table once, and then passing the bit table to STRPOSL as its first argument. +(MAKEBITTABLE(MAKEBITTABLE (Function) NIL NIL ("4") 6) L NEG A) [Function] +Returns a bit table suitable for use by STRPOSL. L is a list of characters or character codes, NEG is the same as described for STRPOSL. If A is a bit table, MAKEBITTABLE modifies and returns it. Otherwise, it will create a new bit table. +Note: If NEG = T, STRPOSL must call MAKEBITTABLE whether A is a list or a bit table. To obtain bit table efficiency with NEG=T, MAKEBITTABLE should be called with NEG=T, and the resulting inverted bit table should be given to STRPOSL with NEG=NIL. + + +[This page intentionally left blank] +(SEQUENCE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "4-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "4-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (ALTERNATE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "4-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "4-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "4-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "4-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))))1$6$EVENT2$TE$ PAGEHEADING RIGHTPAGE1HH$506 +$T1l~$1ll$1~~$1l~$1$1$1$7$D$ PAGEHEADINGLEFTBACKMODERN +GACHA +PALATINO TITAN TITAN PALATINO PALATINO TITAN CLASSIC -MODERN -MODERNMODERN -   -  )IM.INDEX.GETFN  HRULE.GETFNMODERN %D,8 +T%" -  - - -    -  -  N    -  - -  p" !  -  - -  H  !  #  - -  -  - S'K  - -  - H  #  .   1" -  -   6  M - &IM.INDEX.GETFNTITAN - -  -  c  .(,% # ) & # # 8$ e3 - IM.INDEX.GETFNTITAN - - - @ec  -   $ - IM.INDEX.GETFNTITAN - - - @H  -   -#IM.INDEX.GETFNTITAN - -   - + *  - -'IM.INDEX.GETFNTITAN - - - 0 - &IM.INDEX.GETFNTITAN - - - "! 3& + , 8 2>' 6 ,(  ^  i - - (IM.INDEX.GETFNTITAN - - - /  2#IM.INDEX.GETFNMODERN -    >? - 2R " & ( ' * ,  -+ - $7. * [W 3 &  ! ,K  0o  ; -$IM.INDEX.GETFNMODERN -  - :. - -F #  ) h+ ;   ; >: $ - )IM.INDEX.GETFNMODERN -  - (- F     3 < -%.{-z \ No newline at end of file +TITAN TITAN CLASSIC +MODERN + +TIMESROMAN +MODERN   IM.CHAP.GETFN)IM.INDEX.GETFN HRULE.GETFN  %D, 8 +T%" +  +   +  N  + p"! + H !  #  + + S'K + + H #.  1" +   6M +&IM.INDEX.GETFN  c  .(,%#)&##8$ e3 + IM.INDEX.GETFN@ec  + +   +   +  $ + IM.INDEX.GETFN@H  + +   +   +  +#IM.INDEX.GETFN     +*  + +'IM.INDEX.GETFN0 +&IM.INDEX.GETFN"! 3& + ,82>' 6,(  ^  i +(IM.INDEX.GETFN /  2 +#IM.INDEX.GETFN   >? + 2R "&(' *, ++ + $7.*[W + 3& !  +,K 0o ; +$IM.INDEX.GETFN :. + +F # )h+;  ;>: $ +)IM.INDEX.GETFN (- F    3 <%DATE:i.,z \ No newline at end of file diff --git a/docs/medley-irm/08-RECORDPACKAGE.TEDIT b/docs/medley-irm/08-RECORDPACKAGE.TEDIT index cfb215b3..0b1b2c32 100644 Binary files a/docs/medley-irm/08-RECORDPACKAGE.TEDIT and b/docs/medley-irm/08-RECORDPACKAGE.TEDIT differ diff --git a/docs/medley-irm/10-FUNC-DEF.TEDIT b/docs/medley-irm/10-FUNC-DEF.TEDIT index 141f28e3..cc1d1a55 100644 --- a/docs/medley-irm/10-FUNC-DEF.TEDIT +++ b/docs/medley-irm/10-FUNC-DEF.TEDIT @@ -1,1177 +1,469 @@ -INTERLISP-D REFERENCE MANUAL FUNCTION DEFINITION, MANIPULATION AND EVALUATION "10"FUNCTION DEFINITION, MANIPULATION AND EVALUATION 3 Medley is designed to help you define and debug functions. Developing an applications program with Medley involves defining a number of functions in terms of the system primitives and other user-defined functions. Once defined, your functions may be used exactly like Interlisp primitive functions, so the programming process can be viewed as extending the Interlisp language to include the required functionality. A function's definition specifies if the function has a fixed or variable number of arguments, whether these arguments are evaluated or not, the function argument names, and a series of forms which define the behavior of the function. For example: (LAMBDA (X Y) (PRINT X) (PRINT Y)) This function has two evaluated arguments, X and Y, and it will execute (PRINT X) and (PRINT Y) when evaluated. Other types of function definitions are described below. A function is defined by putting an expr definition in the function definition cell of a symbol. There are a number of functions for accessing and setting function definition cells, but one usually defines a function with DEFINEQ (see the Defining Functions section below). For example: (DEFINEQ (FOO (LAMBDA (X Y) (PRINT X) (PRINT Y))))(FOO) The expression above will define the function FOO to have the expr definition (LAMBDA (X Y) (PRINT X) (PRINT Y)). After being defined, this function may be evaluated just like any system function: (FOO 3 (IPLUS 3 4)) 3 7 7 Not all function definition cells contain expr definitions. The compiler (see the first page of Chapter 18) translates expr definitions into compiled code objects, which execute much faster. Interlisp provides a number of function type functions which determine how a given function is defined, the number and names of function arguments, etc. See the Function Type Functions section below. Usually, functions are evaluated automatically when they appear within another function or when typed into Interlisp. However, sometimes it is useful to envoke the Interlisp interpreter explicitly to apply a given functional argument to some data. There are a number of functions which will apply a given function repeatedly. For example, MAPCAR will apply a function (or an expr definition) to all of the elements of a list, and return the values returned by the function: (MAPCAR '(1 2 3 4 5) '(LAMBDA (X) (ITIMES X X)) (1 4 9 16 25) When using functional arguments, there are a number of problems which can arise, related to accessing free variables from within a function argument. Many times these problems can be solved using the function FUNCTION to create a FUNARG object. The macro facility provides another way of specifying the behavior of a function (see the Macros section below). Macros are very useful when developing code which should run very quickly, which should be compiled differently than when it is interpreted, or which should run differently in different implementations of Interlisp. Function Types 1 Interlisp functions are defined using list expressions called expr definitions. An expr definition is a list of the form (LAMBDA-WORD ARG-LIST FORM1 ... FORMN). LAMBDA-WORD determines whether the arguments to this function will be evaluated or not. ARG-LIST determines the number and names of arguments. FORM1 ... FORMN are a series of forms to be evaluated after the arguments are bound to the local variables in ARG-LIST. If LAMBDA-WORD is the symbol LAMBDA, then the arguments to the function are evaluated. If LAMBDA-WORD is the symbol NLAMBDA, then the arguments to the function are not evaluated. Functions which evaluate or don't evaluate their arguments are therefore known as lambda or nlambda functions, respectively. If ARG-LIST is NIL or a list of symbols, this indicates a function with a fixed number of arguments. Each symbol is the name of an argument for the function defined by this expression. The process of binding these symbols to the individual arguments is called spreading the arguments, and the function is called a spread function. If the argument list is any symbol other than NIL, this indicates a function with a variable number of arguments, known as a nospread function. If ARG-LIST is anything other than a symbol or a list of symbols, such as (LAMBDA "FOO" ...), attempting to use this expr definition will generate an Arg not symbol error. In addition, if NIL or T is used as an argument name, the error Attempt to bind NIL or T is generated. These two parameters (lambda/nlambda and spread/nospread) may be specified independently, so there are four nain function types, known as lambda-spread, nlanbda-spread, lanbda-nospread, and nlambda-nospread functions. Each one has a different form and is used for a different purpose. These four function types are described more fully below. For lambda-spread, lanbda-nospread, or nlambda-spread functions, there is an upper limit to the number of arguments that a function can have, based on the number of arguments that can be stored on the stack on any one function call. Currently, the limit is 80 arguments. If a function is called with more than that many arguments, the error Too many arguments occurs. However, nlambda-nospread functions can be called with an arbitrary number of arguments, since the arguments are not individually saved on the stack. Lambda-Spread Functions(LAMBDA-SPREAD% FUNCTIONS NIL Lambda-Spread% Functions NIL ("10") 2) Lambda-spread functions take a fixed number of evaluated arguments. This is the most common function type. A lambda-spread expr definition has the form: (LAMBDA (ARG1 ... ARGM) FORM1 ... FORMN) The argument list (ARG1 ... ARGM) is a list of symbols that gives the number and names of the formal arguments to the function. If the argument list is ( ) or NIL, this indicates that the function takes no arguments. When a lambda-spread function is applied to some arguments, the arguments are evaluated, and bound to the local variables ARG1 ... ARGM. Then, FORM1 ... FORMN are evaluated in order, and the value of the function is the value of FORMN. (DEFINEQ (FOO (LAMBDA (X Y) (LIST X Y)))) (FOO) (FOO 99 (PLUS 3 4)) (99 7) In the above example, the function FOO defined by (LAMBDA (X Y) (LIST X Y)) is applied to the arguments 99 and (PLUS 3 4). These arguments are evaluated (giving 99 and 7), the local variable X is bound to 99 and Y to 7, (LIST X Y) is evaluated, returning (99 7), and this is returned as the value of the function. A standard feature of the Interlisp system is that no error occurs if a spread function is called with too many or too few arguments. If a function is called with too many argumnents, the extra arguments are evaluated but ignored. If a function is called with too few arguments, the unsupplied ones will be delivered as NIL. In fact, a spread function cannot distinguish between being given NIL as an argument, and not being given that argument, e.g., (FOO) and (FOO NIL) are exactly the same for spread functions. If it is necessary to distinguish between these two cases, use an nlambda function and explicitly evaluate the arguments with the EVAL function. Nlambda-Spread Functions(NLAMBDA-SPREAD% FUNCTIONS NIL Nlambda-Spread% Functions NIL ("10") 3) Nlambda-spread functions take a fixed number of unevaluated arguments. An nlambda-spread expr definition has the form: (NLAMBDA (ARG1 ... ARGM) FORM1 ... FORMN) Nlambda-spread functions are evaluated similarly to lanbda-spread functions, except that the arguments are not evaluated before being bound to the variables ARG1 ... ARGM. (DEFINEQ (FOO (NLAMBDA (X Y) (LIST X Y)))) (FOO) (FOO 99 (PLUS 3 4)) (99 (PLUS 3 4)) In the above example, the function FOO defined by (NLAMBDA (X Y) (LIST X Y)) is applied to the arguments 99 and (PLUS 3 4). These arguments are unevaluated to X and Y. (LIST X Y) is evaluated, returning (99 (PLUS 3 4)), and this is returned as the value of the function. Functions can be defined so that all of their arguments are evaluated (lambda functions) or none are evaluated (nlambda functions). If it is desirable to write a function which only evaluates some of its arguments (e.g., SETQ), the functions should be defined as an nlambda, with some arguments explicitly evaluated using the function EVAL. If this is done, the user should put the symbol EVAL on the property list of the function under the property INFO. This informs various system packages, such as DWIM, CLISP, and Masterscope, that this function in fact does evaluate its arguments, even though it is an nlambda. Warning: A frequent problem that occurs when evaluating arguments to nlambda functions with EVAL is that the form being evaluated may reference variables that are not accessible within the nlambda function. This is usually not a problem when interpreting code, but when the code is compiled, the values of local variables may not be accessible on the stack (see Chapter 18). The system nlambda functions that evaluate their arguments (such as SETQ) are expanded in-line by the compiler, so this is not a problem. Using the macro facility is recommended in cases where it is necessary to evaluate some arguments to an nlambda function. Lambda-Nospread Functions(LAMBDA-NOSPREAD% FUNCTIONS NIL Lambda-Nospread% Functions NIL ("10") 3) Lambda-nospread functions take a variable number of evaluated arguments. A lambda-nospread expr definition has the form: (LAMBDA VAR FORM1 ... FORMN) VAR may be any symbol, except NIL and T. When a lambda-nospread function is applied to some arguments, each of these arguments is evaluated and the values stored on the stack. VAR is then bound to the number of arguments which have been evaluated. For example, if FOO is defined by (LAMBDA X ...), when (FOO A B C) is evaluated, A, B, and C are evaluated and X is bound to 3. VAR should never be reset The following functions are used for accessing the arguments of lambda-nospread functions. (ARG(ARG (Function) NIL NIL ("10") 4) VAR M) [NLambda Function] Returns the Mth argument for the lambda-nospread function whose argument list is VAR. VAR is the name of the atomic argument list to a lambda-nospread function, and is not evaluated. M is the number of the desired argument, and is evaluated. The value of ARG is undefined for M less than or equal to 0 or greater than the value of VAR. (SETARG(SETARG (Function) NIL NIL ("10") 4) VAR M X) [NLambda Function] Sets the Mth argument for the lambda-nospread function whose argument list is VAR to X. VAR is not evaluated; M and X are evaluated. M should be between 1 and the value of VAR. In the example below, the function FOO is defined to collect and return a list of all of the evaluated arguments it is given (the value of the for statement). (DEFINEQ (FOO (LAMBDA X (for ARGNUM from 1 to X collect (ARG X ARGNUM)] (FOO) (FOO 99 (PLUS 3 4)) (99 7) (FOO 99 (PLUS 3 4)(TIMES 3 4))) (99 7 12) NLambda-Nospread Functions(NLAMBDA-NOSPREAD% FUNCTIONS NIL NLambda-Nospread% Functions NIL ("10") 4) Nlambda-nospread functions take a variable number of unevaluated arguments. An nlambda-nospread expr definition has the form: (NLAMBDA VAR FORM1 ... FORMN) VAR may be any symbol, except NIL and T. Though similar in form to lambda-nospread expr definitions, an nlambda-nospread is evaluated quite differently. When an nlambda-nospread function is applied to some arguments, VAR is simply bound to a list of the unevaluated arguments. The user may pick apart this list, and evaluate different arguments. In the example below, FOO is defined to return the reverse of the list of arguments it is given (unevaluated): (DEFINEQ (FOO (NLAMBDA X (REVERSE X)))) (FOO) (FOO 99 (PLUS 3 4)) ((PLUS 3 4) 99) (FOO 99 (PLUS 3 4)(TIMES 3 4)) (TIMES 3 4)(PLUS 3 4) 99) The warning about evaluating arguments to nlambda functions also applies to nlambda-nospread function. Compiled Functions(COMPILED% FUNCTIONS NIL Compiled% Functions NIL ("10") 5) Functions defined by expr definitions can be compiled by the Interlisp compiler (see Chapter 18). The compiler produces compiled code objects (of data type CCODEP) which execute more quickly than the corresponding expr definition code. Functions defined by compiled code objects may have the same four types as expr definitions (lambda/nlambda, spread/nospread). Functions created by the compiler are referred to as compiled functions. Function Type Functions There are a variety of functions used for examining the type, argument list, etc. of functions. These functions may be given either a symbol (in which case they obtain the function definition from the definition cell), or a function definition itself. (FNTYP(FNTYP (Function) NIL NIL ("10") 5) FN) [Function] Returns NIL if FN is not a function definition or the name of a defined function. Otherwise, FNTYP returns one of the following symbols, depending on the type of function definition. EXPR Lambda-spread expr definition CEXPR Lambda-spread compiled definition FEXPR Nlambda-spread expr definition CFEXPR Nlambda-spread compiled definition EXPR* Lambda-nospread expr definition CEXPR* Lambda-nospread compiled definition FEXPR* Nlambda-nospread expr definition CFEXPR* Nlambda-nospread compiled definition FUNARG FNTYP returns the symbol FUNARG if FN is a FUNARG expression. EXP, FEXPR, EXPR*, and FEXPR* indicate that FN is defined by an expr definition. CEXPR, CFEXPR, CEXPR*, and CFEXPR* indicate that FN is defined by a compiled definition, as indicated by the prefix C. The suffix * indicates that FN has an indefinite number of arguments, i.e., is a nospread function. The prefix F indicates unevaluated arguments. Thus, for example, a CFEXPR* is a compiled nospread nlambda function. (EXPRP(EXPRP (Function) NIL NIL ("10") 5) FN) [Function] Returns T if (FNTYP FN) is EXPR, FEXPR, EXPR*, or FEXPR*; NIL otherwise. However, (EXPRP FN) is also true if FN is (has) a list definition, even if it does not begin with LAMBDA or NLAMBDA. In other words, EXPRP is not quite as selective as FNTYP. (CCODEP(CCODEP (Function) NIL NIL ("10") 5) FN) [Function] Returns T if (FNTYP FN) is either CEXPR, CFEXPR, CEXPR*, or CFEXPR*; NIL otherwise. (ARGTYPE(ARGTYPE (Function) NIL NIL ("10") 6) FN) [Function] FN is the name of a function or its definition. ARGTYPE returns 0, 1, 2, or 3, or NIL if FN is not a function. ARGTYPE corresponds to the rows of FNTYPs. The interpretation of this value is as follows: 0 Lambda-spread function (EXPR, CEXPR) 1 Nlambda-spread function (FEXPR, CFEXPR) 2 Lambda-nospread function (EXPR*, CEXPR*) 3 Nlambda-nospread function (FEXPR*, CFEXPR*) (NARGS(NARGS (Function) NIL NIL ("10") 6) FN) [Function] Returns the number of arguments of FN, or NIL if FN is not a function. If FN is a nospread function, the value of NARGS is 1. (ARGLIST(ARGLIST (Function) NIL NIL ("10") 6) FN) [Function] Returns the argument list for FN. Note that the argument list is a symbol for nospread functions. Since NIL is a possible value for ARGLIST, the error Args not available is generated if FN is not a function. If FN is a compiled function, the argument list is constructed, i.e., each call to ARGLIST requires making a new list. For functions defined by expr definitions, lists beginning with LAMBDA or NLAMBDA, the argument list is simply CADR of GETD. If FN has an expr definition, and CAR of the definition is not LAMBDA or NLAMBDA, ARGLIST will check to see if CAR of the definition is a member of LAMBDASPLST (see Chapter 20). If it is, ARGLIST presumes this is a function object the user is defining via DWIMUSERFORMS, and simply returns CADR of the definition as its argument list. Otherwise ARGLIST generates an error as described above. (SMARTARGLIST(SMARTARGLIST (Function) NIL NIL ("10") 6) FN EXPLAINFLG TAIL) [Function] A smart version of ARGLIST that tries various strategies to get the arglist of FN. First SMARTARGLIST checks the property list of FN under the property ARGNAMES. For spread functions, the argument list itself is stored. For nospread functions, the form is (NIL ARGLIST1 . ARGLIST2), where ARGLIST1 is the value SMARTARGLIST should return when EXPLAINFLG = T, and ARGLIST2 the value when EXPLAINFLG = NIL. For example, (GETPROP 'DEFINEQ 'ARGNAMES) = (NIL (X1 Xl ... XN) . X). This allows the user to specify special argument lists. Second, if FN is not defined as a function, SMARTARGLIST attempts spelling correction on FN by calling FNCHECK (see Chapter 20), passing TAIL to be used for the call to FIXSPELL. If unsuccessful, the FN Not a function error will be generated. Third, if FN is known to the file package (see Chapter 17) but not loaded in, SMARTARGLIST will obtain the arglist information from the file. Otherwise, SMARTARGLIST simply returns (ARGLIST FN). SMARTARGLIST is used by BREAK (see Chapter 15) and ADVISE with EXPLAINFLG = NIL for constructing equivalent expr definitions, and by the TTYIN in-line command ?= (see Chapter 26), with EXPLAINFLG = T. Defining Functions 1 Function definitions are stored in a function definition cell associated with each symbol. This cell is directly accessible via the two functions PUTD and GETD (see below), but it is usually easier to define functions with DEFINEQ: (DEFINEQ(DEFINEQ (Function) NIL NIL ("10") 7) X1 X2 ... XN) [NLambda NoSpread Function] DEFINEQ is the function normally used for defining functions. It takes an indefinite number of arguments which are not evaluated. Each Xi must be a list defining one function, of the form (NAME DEFINITION). For example: (DEFINEQ (DOUBLE (LAMBDA (X) (IPLUS X X)))) The above expression will define the function DOUBLE with the expr definition (LAMBDA (X) (IPLUS X X)). Xi may also have the form (NAME ARGS . DEF-BODY), in which case an appropriate lambda expr definition will be constructed. Therefore, the above expression is exactly the same as: (DEFINEQ (DOUBLE (X) (IPLUS X X))) Note that this alternate form can only be used for lambda functions. The first form must be used to define an nlambda function. DEFINEQ returns a list of the names of the functions defined. (DEFINE(DEFINE (Function) NIL NIL ("10") 7) X %) [Function] Lambda-spread version of DEFINEQ. Each element of the list X is itself a list either of the form (NAME DEFINITION) or (NAME ARGS . DEF-BODY). DEFINE will generate an error, Incorrect defining form on encountering an atom where a defining list is expected. DEFINE and DEFINEQ operate correctly if the function is already defined and BROKEN, ADVISED, or BROKEN-IN. For expressions involving type-in only, if the time stamp facility is enabled (see the Time Stamps section of Chapter 16), both DEFINE and DEFINEQ stamp the definition with your initials and date. UNSAFE.TO.MODIFY.FNS(UNSAFE.TO.MODIFY.FNS (Variable) NIL NIL ("10") 7) [Variable] Value is a list of functions that should not be redefined, because doing so may cause unusual bugs (or crash the system!). If you try to modify a function on this list (using DEFINEQ, TRACE, etc), the system prints Warning: XXX may be unsafe to modify -- continue? If you type Yes, the function is modified, otherwise an error occurs. This provides a measure of safety for novices who may accidently redefine important system functions. You can add your own functions onto this list. By convention, all functions starting with the character backslash (\) are system internal functions, which you should never redefine or modify. Backslash functions are not on UNSAFE.TO.MODIFY.FNS, so trying to redefine them will not cause a warning. DFNFLG(DFNFLG (Variable) NIL NIL ("10") 8) [Variable] DFNFLG is a global variable that affects the operation of DEFINEQ and DEFINE. If DFNFLG=NIL, an attempt to redefine a function FN will cause DEFINE to print the message (FN REDEFINED) and to save the old definition of FN using SAVEDEF (see the Functions for Manipulating Typed Definitions section of Chapter 17) before redefining it (except if the old and new definitions are EQUAL, in which case the effect is simply a no-op). If DFNFLG=T, the function is simply redefined. If DFNFLG=PROP or ALLPROP, the new definition is stored on the property list under the property EXPR. ALLPROP also affects the operation of RPAQQ and RPAQ (see the Functions Used Within Source Files section of Chapter 17). DFNFLG is initially NIL. DFNFLG is reset by LOAD (see the Loading Files section of Chapter 17) to enable various ways of handling the defining of functions and setting of variables when loading a file. For most applications, the user will not reset DFNFLG directly. Note: The compiler does not respect the value of DFNFLG when it redefines functions to their compiled definitions (see the first page of Chapter 18). Therefore, if you set DFNFLG to PROP to completely avoid inadvertantly redefining something in your running system, you must use compile mode F, not ST. Note that the functions SAVEDEF and UNSAVEDEF (see the Functions for Manipulating Typed Definitions section of Chapter 17) can be useful for saving and restoring function definitions from property lists. (GETD(GETD (Function) NIL NIL ("10") 8) FN) [Function] Returns the function definition of FN. Returns NIL if FN is not a symbol, or has no definition. GETD of a compiled function constructs a pointer to the definition, with the result that two successive calls do not necessarily produce EQ results. EQP or EQUAL must be used to compare compiled definitions. (PUTD(PUTD (Function) NIL NIL ("10") 8) FN DEF %) [Function] Puts DEF into FN's function cell, and returns DEF. Generates an error, Arg not symbol, if FN is not a symbol. Generates an error, Illegal arg, if DEF is a string, number, or a symbol other than NIL. (MOVD(MOVD (Function) NIL NIL ("10") 8) FROM TO COPYFLG %) [Function] Moves the definition of FROM to TO, i.e., redefines TO. If COPYFLG = T, a COPY of the definition of FROM is used. COPYFLG =T is only meaningful for expr definitions, although MOVD works for compiled functions as well. MOVD returns TO. COPYDEF (see the Functions for Manipulating Typed Definitions section of Chapter 17) is a higher-level function that not only moves expr definitions, but works also for variables, records, etc. (MOVD?(MOVD? (Function) NIL NIL ("10") 9) FROM TO COPYFLG %) [Function] If TO is not defined, same as (MOVD FROM TO COPYFLG). Otherwise, does nothing and returns NIL. Function Evaluation 1 Usually, function application is done automatically by the Interlisp interpreter. If a form is typed into Interlisp whose CAR is a function, this function is applied to the arguments in the CDR of the form. These arguments are evaluated or not, and bound to the funcion parameters, as determined by the type of the function, and the body of the function is evaluated. This sequence is repeated as each form in the body of the function is evaluated. There are some situations where it is necessary to explicitly call the evaluator, and Interlisp supplies a number of functions that will do this. These functions take functional arguments, which may either be symbols with function definitions, or expr definition forms such as (LAMBDA (X)...), or FUNARG expressions. (APPLY(APPLY (Function) NIL NIL ("10") 9) FN ARGLIST %) [Function] Applies the function FN to the arguments in the list ARGLIST, and returns its value. APPLY is a lambda function, so its arguments are evaluated, but the individual elements of ARGLIST are not evaluated. Therefore, lambda and nlambda functions are treated the same by APPLY%lambda functions take their arguments from ARGLIST without evaluating them. For example: (APPLY 'APPEND '((PLUS 1 2 3)(4 5 6))) (PLUS 1 2 3 4 5 6) Note that FN may explicitly evaluate one or more of its arguments itself. For example, the system function SETQ is an nlambda function that explicitly evaluates its second argument. Therefore, (APPLY 'SETQ '(FOO (ADD1 3)))will set FOO to 4, instead of setting it to the expression (ADD1 3). APPLY can be used for manipulating expr definitions. For example: (APPLY '(LAMBDA (X Y)(ITIMES X Y)) '(3 4))) 12 (APPLY*(APPLY* (Function) NIL NIL ("10") 9) FN ARG1 ARG2 ... ARGN ) [NoSpread Function] Nospread version of APPLY. Applies the function FN to the arguments ARG1 ARG2 ... ARGN. For example: (APPLY 'APPEND '(PLUS 1 2 3)(4 5 6)) (PLUS 1 2 3 4 5 6) (EVAL(EVAL (Function) NIL NIL ("10") 10) X%) [Function] EVAL evaluates the expression X and returns this value, i.e., EVAL provides a way of calling the Interlisp interpreter. Note that EVAL is itself a lambda function, so its argument is first evaluated, e.g.: (SETQ FOO 'ADD1 3))) (ADD1 3) (EVAL FOO) 4 (EVAL 'FOO) (ADD1 3) (QUOTE(QUOTE (Function) NIL NIL ("10") 10) X) [Nlambda NoSpread Function] QUOTE prevents its arguments from being evaluated. Its value is X itself, e.g., (QUOTE FOO) is FOO. Interlisp functions can either evaluate or not evaluate their arguments. QUOTE can be used in those cases where it is desirable to specify arguments unevaluated. The single-quote character (') is defined with a read macro so it returns the next expression, wrapped in a call to QUOTE (see Chapter 25). For example, 'FOO reads as (QUOTE FOO). This is the form used for examples in this manual. Since giving QUOTE more than one argument is almost always a parenthese error, and one that would otherewise go undetected, QUOTE itself generates an error in this case, Parenthesis error. (KWOTE(KWOTE (Function) NIL NIL ("10") 10) X) [Function] Value is an expression which, when evaluated, yields X. If X is NIL or a number, this is X itself. Otherwise (LIST (QUOTE QUOTE) X). For example: (KWOTE 5) => 5 (KWOTE (CONS 'A 'B)) => (QUOTE (A.B)) (NLAMBDA.ARGS(NLAMBDA.ARGS (Function) NIL NIL ("10") 10) X) [Function] This function interprets its argument as a list of unevaluated nlambda arguments. If any of the elements in this list are of the form (QUOTE...), the enclosing QUOTE is stripped off. Actually, NLAMBDA.ARGS stops processing the list after the first non-quoted argument. Therefore, whereas (NLAMBDA.ARGS '((QUOTE FOO) BAR)) -> (FOO BAR), (NLAMBDA.ARGS '(FOO (QUOTE BAR))) -> (FOO (QUOTE BAR)). NLAMBDA.ARGS is alled by a number of nlambda functions in the system, to interpret their arguments. For instance, the function BREAK calls NLAMBDA.ARGS so that (BREAK 'FOO) will break the function FOO, rather than the function QUOTE. (EVALA(EVALA (Function) NIL NIL ("10") 10) X A) [Function] Simulates association list variable lookup. X is a form, A is a list of the form: ((NAME1 . VAL1) (NAME2 . VAL2)... (NAMEN . VALN)) The variable names and values in A are spread on the stack, and then X is evaluated. Therefore, any variables appearing free in X that also appears as CAR of an element of A will be given the value on the CDR of that element. (DEFEVAL(DEFEVAL (Function) NIL NIL ("10") 11) TYPE FN) [Function] Specifies how a datum of a particular type is to be evaluated. Intended primarily for user-defined data types, but works for all data types except lists, literal atoms, and numbers. TYPE is a type name. FN is a function object, i.e., name of a function or a lambda expression. Whenever the interpreter encounters a datum of the indicated type, FN is applied to the datum and its value returned as the result of the evaluation. DEFEVAL returns the previous evaling function for this type. If FN = NIL, DEFEVAL returns the current evaling function without changing it. If FN = T, the evaling functions is set back to the system default (which for all data types except lists is to return the datum itself). COMPILETYPELST (see Chapter 18) permits the user to specify how a datum of a particular type is to be compiled. (EVALHOOK(EVALHOOK (Function) NIL NIL ("10") 11) FORM EVALHOOKFN) [Function] EVALHOOK evaluates the expression FORM, and returns its value. While evaluating FORM, the function EVAL behaves in a special way. Whenever a list other than FORM itself is to be evaluated, whether implicitly or via an explicit call to EVAL, EVALHOOKFN is invoked (it should be a function), with the form to be evaluated as its argument. EVALHOOKFN is then responsible for evaluating the form. Whatever is returned is assume to be the result of evaluating the form. During the execution of EVALHOOKFN, this special evaluation is turned off. (Note that EVALHOOK does not affect the evaluations of variables, only of lists). Here is an example of a simple tracing routine that uses the EVALHOOK feature: (DEFINEQ (PRINTHOOK (FORM) (printout T "eval: "FORM T) (EVALHOOK FORM (FUNCTION PRINTHOOK (PRINTHOOK) Using PRINTHOOK, one might see the following interaction: (EVALHOOK '(LIST (CONS 1 2)(CONS 3 4)) 'PRINTHOOK) eval: (CONS 1 2) eval: (CONS 3 4) ((1.2)(3.4)) Iterating and Mapping Functions 1 The functions below are used to evaluate a form or apply a function repeatedly. RPT, RPTQ, and FRPTQ evaluate an expression a specified number of time. MAP, MAPCAR, MAPLIST, etc., apply a given function repeatedly to different elements of a list, possibly constructing another list. These functions allow efficient iterative computations, but they are difficult to use. For programming iterative computations, it is usually better to use the CLISP Iterative Statement facility (see Chapter 9), which provides a more general and complete facility for expressing iterative statements. Whenever possible, CLISP transltes iterative statements into expressions using the functions below, so there is no efficiency loss. (RPT(RPT (Function) NIL NIL ("10") 12) N FORM) [Function] Evaluates the expression FORM, N times. Returns the value of the last evaluation. If N is less than or equal to 0, FORM is not evaluated, and RPT returns NIL. Before each evaluation, the local variable RPTN is bound to the number of evaluations yet to take place. This variable can be referenced within FORM. For example, (RPT 10 '(PRINT RPTN)) will print the numbers 10, 9...1, and return 1. (RPTQ(RPTQ (Function) NIL NIL ("10") 12) N FORM1 FORM2... FORMN) [NLambda NoSpread Function] Nlambda-nospread version of RPT: N is evaluated, FORMi are not. Returns the value of the last evaluation of FORMN. (FRPTQ(FRPTQ (NLambda NoSpread Function) NIL NIL ("10") 12) N FORM1 FORM2... FORMN) [NLambda NoSpread Function] Faster version of RPTQ. Does not bind RPTN. (MAP(MAP (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] If MAPFN2 is NIL, MAP applies the function MAPFN1 to successive tails of the list MAPX. That is, first it computes (MAPFN1 MAPX), and then (MAPFN1 (CDR MAPX)), etc., until MAPX becomes a non-list. If MAPFN2 is provided, (MAPFN2 MAPX) is used instead of (CDR MAPX) for the next call for MAPFN1, e.g., if MAPFN2 were CDDR, alternate elements of the list would be skipped. MAP returns NIL. (MAPC(MAPC (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] Identical to MAP, except that (MAPFN1 (CAR MAPX)) is computed at each iteration instead of (MAPFN1 MAPX), i.e., MAPC works on elements, MAP on tails. MAPC returns NIL. (MAPLIST(MAPLIST (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] Successively computes the same values that MAP would compute, and returns a list consisting of those values. (MAPCAR(MAPCAR (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] Computes the same values that MAPC would compute, and returns a list consisting of those values, e.g., (MAPCAR X 'FNTYP) is a list of FNTYPs for each element on X. (MAPCON(MAPCON (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] Computes the same values that MAP and MAPLIST but NCONCs these values to form a list which it returns. (MAPCONC(MAPCONC (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] Computes the same values that MAPC and MAPCAR, but NCONCs the values to form a list which it returns. Note that MAPCAR creates a new list which is a mapping of the old list in that each element of the new list is the result of applying a function to the corresponding element on the original list. MAPCONC is used when there are a variable number of elements (including none) to be inserted at each iteration. Examples: (MAPCONC '(A B C NIL D NIL) '(LAMBDA (Y)(if (NULL Y) then NIL else (LIST Y)))) = > (A B C D) This MAPCONC returns a list consisting of MAPX with all NILs removed. (MAPCONC '((A B) C (D E F)(G) H I) '(LAMBDA (Y)(if (LISP Y) then Y else NIL))) = > (A B D E F G) This MAPCONC returns a linear list consisting of all the lists on MAPX. Since MAPCONC uses NCONC to string the corresponding lists together, in this example the original list will be altered to be ((A B C D E F G) C (D E F G)(G) H I). If this is an undesirable side effect, the functional argument to MAPCONC should return instead a top level copy of the lists, i.e., (LAMBDA (Y) (if (LISTP Y) then (APPERND Y) else NIL))). (MAP2C(MAP2C (Function) NIL NIL ("10") 13) MAPX MAPY MAPFN1 MAPFN2) [Function] Identical to MAPC except MAPFN1 is a function of two arguments, and (MAPFN1 (CAR MAPX)(CAR MAPY)) is computed at each iteration. Terminates when either MAPX or MAPY is a non-list. MAPFN2 is still a function of one argument, and is applied twice on each iteration; (MAPFN2 MAPX) gives the new MAPX, (MAPFN2 MAPY) the new MAPY. CDR is used if MAPFN2 is not supplied, i.e., is NIL. (MAP2CAR(MAP2CAR (Function) NIL NIL ("10") 13) MAPX MAPY MAPFN1 MAPFN2) [Function] Identical to MAPCAR except MAPFN1 is a function of two arguments, and (MAPFN1 (CAR MAPX)(CAR MAPY)) is used to assemble the new list. Terminates when either MAPX or MAPY is a non-list. (SUBSET(SUBSET (Function) NIL NIL ("10") 13) MAPX MAPFN1 MAPFN2) [Function] Applies MAPFN1 to elements of MAPX and returns a list of those elements for which this application is non-NIL, e.g.: (SUBSET '(A B 3 C 4) 'NUMBERP) = (3 4) MAPFN2 plays the same role as with MAP, MAPC, et al. (EVERY(EVERY (Function) NIL NIL ("10") 13) EVERYX EVERYFN1 EVERYFN2) [Function] Returns T if the result of applying EVERYFN1 to each element in EVERYX is true, otherwise NIL. For example, (EVERY '(X Y Z) 'ATOM) => T. EVERY operates by evaluating (EVERYFN1 (CAR EVERYX) EVERYX). The second argument is passed to EVERYFN1 so that it can look at the next element on EVERYX if necessary. If EVERYFN1 yields NIL, EVERY immediately returns NIL. Otherwise, EVERY computes (EVERYFN2 EVERYX), or (CDR EVERYX) if EVERYFN2 = NIL, and uses this as the new EVERYX, and the process continues. For example (EVERY X 'ATOM 'CDDR) is true if every other element of X is atomic. (SOME(SOME (Function) NIL NIL ("10") 14) SOMEX SOMEFN1 SOMEFN2) [Function] Returns the tail of SOMEX beginning with the first element that satisfies SOMEFN1, i.e., for which SOMEFN1 applied to that element is true. Value is NIL if no such element exists. (SOME X '(LAMBDA (Z) (EQUAL Z Y))) is equivalent to (MEMBER Y X). SOME operates analogously to EVERY. At each stage, (SOMEFN1 (CAR SOMEX) SOMEX) is computed, and if this not NIL, SOMEX is returned as the value of SOME. Otherwise, (SOMEFN2 SOMEX) is computed, or (CDR SOMEX) if SOMEFN2 = NIL, and used for the next SOMEX. (NOTANY(NOTANY (Function) NIL NIL ("10") 14) SOMEX SOMEFN1 SOMEFN2) [Function] (NOT (SOME SOMEX SOMEFN1 SOMEFN2)). (NOTEVERY(NOTEVERY (Function) NIL NIL ("10") 14) EVERYX EVERYFN1 EVERYFN2) [Function] (NOT (EVERY EVERYX EVERYFN1 EVERYFN2)). (MAPRINT(MAPRINT (Function) NIL NIL ("10") 14) LST FILE LEFT RIGHT SEP PFN LISPXPRINTFLG) [Function] A general printing function. For each element of the list LST, applies PFN to the element, and FILE. If PFN is NIL, PRIN1 is used. Between each application MAPRINT performs PRIN1 of SEP (or "" if SEP = NIL). If LEFT is given, it is printed (using PRIN1) initially; if RIGHT is given, it is printed (using PRIN1) at the end. For example, (MAPRINT X NIL '%( '%)) is equivalent to PRIN1 for lists. To print a list with commas between each element and a final . one could use (MAPRINT X T NIL '%. '%,). If LISPXPRINTFLG = T, LISPXPRIN1 (see Chapter 13) is used instead of PRIN1. Functional Arguments 1 The functions that call the Interlisp-D evaluator take functional arguments, which may be symbols with function definitions, or expr definition forms such as (LAMBDA (X) ...). The following functions are useful when one wants to supply a functional argument which will always return NIL, T, or 0. Note that the arguments X1 ... XN to these functions are evaluated, though they are not used. (NILL(NILL (NoSpread Function) NIL NIL ("10") 14) X1 ... XN ) [NoSpread Function] Returns NIL. (TRUE(TRUE (NoSpread Function) NIL NIL ("10") 14) X1 ... XN ) [NoSpread Function] Returns T. (ZERO(ZERO (NoSpread Function) NIL NIL ("10") 15) X1 ... XN ) [NoSpread Function] Returns 0. When using expr definitions as function arguments, they should be enclosed within the function FUNCTION rather than QUOTE, so that they will be compiled as separate functions. (FUNCTION(FUNCTION (NLambda Function) NIL NIL ("10") 15) FN ENV ) [NLambda Function] If ENV = NIL, FUNCTION is the same as QUOTE, except that it is treated differently when compiled. Consider the function definition: (DEFINEQ (FOO (LST)(FIE LST (FUNCTION (LAMBDA (Z)(ITIMES Z Z))] FOO calls the function FIE with the value of LST and the expr definition (LAMBDA (Z)(LIST (CAR Z))). If FOO is run interpreted, it does not make any difference whether FUNCTION or QUOTE is used. However, when FOO is compiled, if FUNCTION is used the compiler will define and compile the expr definition as an auxiliary function (see Chapter 18). The compiled expr definition will run considerably faster, which can make a big difference if it is applied repeatedly. Compiling FUNCTION will not create an auxiliary function if it is a functional argument to a function that compiles open, such as most of the mapping functions (MAPCAR, MAPLIST, etc.). If ENV is not NIL, it can be a list of variables that are (presumably) used freely by FN. ENV can also be an atom, in which case it is evaluated, and the value interpreted as described above. Macros 1 Macros provide an alternative way of specifying the action of a function. Whereas function definitions are evaluated with a function call, which involves binding variables and other housekeeping tasks, macros are evaluated by translating one Interlisp form into another, which is then evaluated. A symbol may have both a function definition and a macro definition. When a form is evaluated by the interpreter, if the CAR has a function definition, it is used (with a function call), otherwise if it has a macro definition, then that is used. However, when a form is compiled, the CAR is checked for a macro definition first, and only if there isn't one is the function definition compiled. This allows functions that behave differently when compiled and interpreted. For example, it is possible to define a function that, when interpreted, has a function definition that is slow and has a lot of error checks, for use when debugging a system. This function could also have a macro definition that defines a fast version of the function, which is used when the debugged system is compiled. Macro definitions are represented by lists that are stored on the property list of a symbol. Macros are often used for functions that should be compiled differently in different Interlisp implementations, and the exact property name a macro definition is stored under determines whether it should be used in a particular implementation. The global variable MACROPROPS contains a list of all possible macro property names which should be saved by the MACROS file package command. Typical macro property names are DMACRO for Interlisp-D, 10MACRO for Interlisp-10, VAXMACRO for Interlisp-VAX, JMACRO for Interlisp-Jerico, and MACRO for implementation independent macros. The global variable COMPILERMACROPROPS is a list of macro property names. Interlisp determines whether a symbol has a macro definition by checking these property names, in order, and using the first non-NIL property value as the macro definition. In Interlisp-D this list contains DMACRO and MACRO in that order so that DMACROs will override the implementation-independent MACRO properties. In general, use a DMACRO property for macros that are to be used only in Interlisp-D, use 10MACRO for macros that are to be used only in Interlisp-10, and use MACRO for macros that are to affect both systems. Macro definitions can take the following forms: (LAMBDA ...) (NLAMBDA ...) A function can be made to compile open by giving it a macro definition of the form (LAMBDA ...) or (NLAMBDA ...), e.g., (LAMBDA (X) (COND ((GREATERP X 0) X) (T (MINUS X)))) for ABS. The effect is as if the macro definition were written in place of the function wherever it appears in a function being compiled, i.e., it compiles as a lambda or nlambda expression. This saves the time necessary to call the function at the price of more compiled code generated in-line. (NIL EXPRESSION) (LIST EXPRESSION) Substitution macro. Each argument in the form being evaluated or compiled is substituted for the corresponding atom in LIST, and the result of the substitution is used instead of the form. For example, if the macro definition of ADD1 is ((X) (IPLUS X 1)), then, (ADD1 (CAR Y)) is compiled as (IPLUS (CAR Y) 1). Note that ABS could be defined by the substitution macro ((X) (COND ((GREATERP X 0) X) (T (MINUS X)))). In this case, however, (ABS (FOO X)) would compile as (COND ((GREATERP (FOO X) 0) (FOO X)) (T (MINUS (FOO X)))) and (FOO X) would be evaluated two times. (Code to evaluate (FOO X) would be generated three times.) (OPENLAMBDA ARGS BODY) This is a cross between substitution and LAMBDA macros. When the compiler processes an OPENLAMBDA, it attempts to substitute the actual arguments for the formals wherever this preserves the frequency and order of evaluation that would have resulted from a LAMBDA expression, and produces a LAMBDA binding only for those that require it. Note: OPENLAMBDA assumes that it can substitute literally the actual arguments for the formal arguments in the body of the macro if the actual is side-effect free or a constant. Thus, you should be careful to use names in ARGS which don't occur in BODY (except as variable references). For example, if FOO has a macro definition of (OPENLAMBDA (ENV) (FETCH (MY-RECORD-TYPE ENV) OF BAR)) then (FOO NIL) will expand to (FETCH (MY-RECORD-TYPE NIL) OF BAR) T When a macro definition is the atom T, it means that the compiler should ignore the macro, and compile the function definition; this is a simple way of turning off other macros. For example, the user may have a function that runs in both Interlisp-D and Interlisp-10, but has a macro definition that should only be used when compiling in Interlisp-10. If the MACRO property has the macro specification, a DMACRO of T will cause it to be ignored by the Interlisp-D compiler. This DMACRO would not be necessary if the macro were specified by a 10MACRO instead of a MACRO. (= . OTHER-FUNCTION) A simple way to tell the compiler to compile one function exactly as it would compile another. For example, when compiling in Interlisp-D, FRPLACAs are treated as RPLACAs. This is achieved by having FRPLACA have a DMACRO of (= . RPLACA). (LITATOM EXPRESSION) If a macro definition begins with a symbol other than those given above, this allows computation of the Interlisp expression to be evaluated or compiled in place of the form. LITATOM is bound to the CDR of the calling form, EXPRESSION is evaluated, and the result of this evaluation is evaluated or compiled in place of the form. For example, LIST could be compiled using the computed macro: [X (LIST 'CONS (CAR X)(AND (CDR X)(CONS 'LIST (CDR X] This would cause (LIST X Y Z) to compile as (CONS X (CONS Y (CONS Z NIL))). Note the recursion in the macro expansion. If the result of the evaluation is the symbol IGNOREMACRO, the macro is ignored and the compilation of the expression proceeds as if there were no macro definition. If the symbol in question is normally treated specially by the compiler (CAR, CDR, COND, AND, etc.), and also has a macro, if the macro expansion returns IGNOREMACRO, the symbol will still be treated specially. In Interlisp-10, if the result of the evaluation is the atom INSTRUCTIONS, no code will be generated by the compiler. It is then assumed the evaluation was done for effect and the necessary code, if any, has been added. This is a way of giving direct instructions to the compiler if you understand it. It is often useful, when constructing complex macro expressions, to use the BQUOTE facility (see the Read Macros section of Chapter 25). The following function is quite useful for debugging macro definitions: (EXPANDMACRO(EXPANDMACRO (Function) NIL NIL ("10") 18) EXP QUIETFLG % %) [Function] Takes a form whose CAR has a macro definition and expands the form as it would be compiled. The result is prettyprinted, unless QUIETFLG=T, in which case the result is simply returned. Note: EXPANDMACRO only works on Interlisp macros. Use CL:MACROEXPAND-1 to expand Interlisp macros visible to the Common Lisp interpreter and compliler. DEFMACRO Macros defined with the function DEFMACRO are much like computed macros (see the above section), in that they are defined with a form that is evaluated, and the result of the evaluation is used (evaluated or compiled) in place of the macro call. However, DEFMACRO macros support complex argument lists with optional arguments, default values, and keyword arguments as well as argument list destructuring. (DEFMACRO(DEFMACRO (Function) NIL NIL ("10") 18) NAME ARGS FORM) [NLambda NoSpread Function] Defines NAME as a macro with the arguments ARGS and the definition form FORM (NAME, ARGS, and FORM are unevaluated). If an expression starting with NAME is evaluated or compiled, arguments are bound according to ARGS, FORM is evaluated, and the value of FORM is evaluated or compiled instead. The interpretation of ARGS is described below. Note: Like the function DEFMACRO in Common Lisp, this function currently removes any function definition for NAME. ARGS is a list that defines how the argument list passed to the macro NAME is interpreted. Specifically, ARGS defines a set of variables that are set to various arguments in the macro call (unevaluated), that FORM can reference to construct the macro form. In the simplest case, ARGS is a simple list of variable names that are set to the corresponding elements of the macro call (unevaluated). For example, given: (DEFMACRO FOO (A B) (LIST 'PLUS A B B)) The macro call (FOO X (BAR Y Z)) will expand to (PLUS X (BAR Y Z) (BAR Y Z)). &-keywords (beginning with the character &) that are used to set variables to particular items from the macro call form, as follows: &OPTIONAL Used to define optional arguments, possibly with default values. Each element on ARGS after &OPTIONAL until the next &-keyword or the end of the list defines an optional argument, which can either be a symbol or a list, interpreted as follows: VAR If an optional argument is specified as a symbol, that variable is set to the corresponding element of the macro call (unevaluated). (VAR DEFAULT) If an optional argument is specified as a two element list, VAR is the variable to be set, and DEFAULT is a form that is evaluated and used as the default if there is no corresponding element in the macro call. (VAR DEFAULT VARSETP) If an optional argument is specified as a three element list, VAR and DEFAULT are the variable to be set and the default form, and VARSETP is a variable that is set to T if the optional argument is given in the macro call, NIL otherwise. This can be used to determine whether the argument was not given, or whether it was specified with the default value. For example, after (DEFMACRO FOO (&OPTIONAL A (B 5) (C 6 CSET)) FORM) expanding the macro call (FOO) would cause FORM to be evaluated with A set to NIL, B set to 5, C set to 6, and CSET set to NIL. (FOO 4 5 6) would be the same, except that A would be set to 4 and CSET would be set to T. &REST &BODY Used to get a list of all additional arguments from the macro call. Either &REST or &BODY should be followed by a single symbol, which is set to a list of all arguments to the macro after the position of the &-keyword. For example, given (DEFMACRO FOO (A B &REST C) FORM) expanding the macro call (FOO 1 2 3 4 5) would cause FORM to be evaluated with A set to 1, B set to 2, and C set to (3 4 5). If the macro calling form contains keyword arguments (see &KEY below), these are included in the &REST list. &KEY Used to define keyword arguments, that are specified in the macro call by including a keyword (a symbol starting with the character :) followed by a value. Each element on ARGS after &KEY until the next &-keyword or the end of the list defines a keyword argument, which can either be a symbol or a list, interpreted as follows: VAR (VAR) ((KEYWORD VAR)) If a keyword argument is specified by a single symbol VAR, or a one-element list containing VAR, it is set to the value of a keyword argument, where the keyword used is created by adding the character : to the front of VAR. If a keyword argument is specified by a single-element list containing a two-element list, KEYWORD is interpreted as the keyword (which should start with the letter :), and VAR is the variable to set. (VAR DEFAULT) ((KEYWORD VAR) DEFAULT) (VAR DEFAULT VARSETP) ((KEYWORD VAR) DEFAULT VARSETP) If a keyword argument is specified by a two- or three-element list, the first element of the list specifies the keyword and variable to set as above. Similar to &OPTIONAL (above), the second element DEFAULT is a form that is evaluated and used as the default if there is no corresponding element in the macro call, and the third element VARSETP is a variable that is set to T if the optional argument is given in the macro call, NIL otherwise. For example, the form (DEFMACRO FOO (&KEY A (B 5 BSET) ((:BAR C) 6 CSET)) FORM) Defines a macro with keys :A, :B (defaulting to 5), and :BAR. Expanding the macro call (FOO :BAR 2 :A 1) would cause FORM to be evaluated with A set to 1, B set to 5, BSET set to NIL, C set to 2, and CSET set to T. &ALLOW-OTHER-KEYS It is an error for any keywords to be supplied in a macro call that are not defined as keywords in the macro argument list, unless either the &-keyword &ALLOW-OTHER-KEYS appears in ARGS, or the keyword :ALLOW-OTHER-KEYS (with a non-NIL value) appears in the macro call. &AUX Used to bind and initialize auxiliary varables, using a syntax similar to PROG (see the PROG and Associated Control Functions section of Chapter 9). Any elements after &AUX should be either symbols or lists, interpreted as follows: VAR Single symbols are interpreted as auxiliary variables that are initially bound to NIL. (VAR EXP) If an auxiliary variable is specified as a two element list, VAR is a variable initially bound to the result of evaluating the form EXP. For example, given (DEFMACRO FOO (A B &AUX C (D 5)) FORM) C will be bound to NIL and D to 5 when FORM is evaluated. &WHOLE Used to get the whole macro calling form. Should be the first element of ARGS, and should be followed by a single symbol, which is set to the entire macro calling form. Other &-keywords or arguments can follow. For example, given (DEFMACRO FOO (&WHOLE X A B) FORM) Expanding the macro call (FOO 1 2) would cause FORM to be evaluated with X set to (FOO 1 2), A set to 1, and B set to 2. DEFMACRO macros also support argument list destructuring, a facility for accessing the structure of individual arguments to a macro. Any place in an argument list where a symbol is expected, an argument list (in the form described above) can appear instead. Such an embedded argument list is used to match the corresponding parts of that particular argument, which should be a list structure in the same form. In the simplest case, where the embedded argument list does not include &-keywords, this provides a simple way of picking apart list structures passed as arguments to a macro. For example, given (DEFMACRO FOO (A (B (C . D)) E) FORM) Expanding the macro call (FOO 1 (2 (3 4 5)) 6) would cause FORM to be evaluated with with A set to 1, B set to 2, C set to 3, D set to (4 5), and E set to 6. Note that the embedded argument list (B (C . D)) has an embedded argument list (C . D). Also notice that if an argument list ends in a dotted pair, that the final symbol matches the rest of the arguments in the macro call. An embedded argument list can also include &-keywords, for interpreting parts of embedded list structures as if they appeared in a top-level macro call. For example, given (DEFMACRO FOO (A (B &OPTIONAL (C 6)) D) FORM) Expanding the macro call (FOO 1 (2) 3) would cause FORM to be evaluated with with A set to 1, B set to 2, C set to 6 (because of the default value), and D set to 3. Warning: Embedded argument lists can only appear in positions in an argument list where a list is otherwise not accepted. In the above example, it would not be possible to specify an embedded argument list after the &OPTIONAL keyword, because it would be interpreted as an optional argument specification (with variable name, default value, set variable). However, it would be possible to specify an embedded argument list as the first element of an optional argument specification list, as so: (DEFMACRO FOO (A (B &OPTIONAL ((X (Y) Z) '(1 (2) 3))) D) FORM) In this case, X, Y, and Z default to 1, 2, and 3, respectively. Note that the default value has to be an appropriate list structure. Also, in this case either the whole structure (X (Y) Z) can be supplied, or it can be defaulted (i.e., is not possible to specify X while letting Y default). Interpreting Macros When the interpreter encounters a form CAR of which is an undefined function, it tries interpreting it as a macro. If CAR of the form has a macro definition, the macro is expanded, and the result of this expansion is evaluated in place of the original form. CLISPTRAN (see the Miscellaneous Functions and Variables section of Chapter 21) is used to save the result of this expansion so that the expansion only has to be done once. On subsequent occasions, the translation (expansion) is retrieved from CLISPARRAY the same as for other CLISP constructs. Note: Because of the way that the evaluator processes macros, if you have a macro on FOO, then typing (FOO 'A 'B) will work, but FOO(A B) will not work. [This page intentionally left blank] (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "10-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "10-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "10-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "10-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (102 612 510 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "10-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "10-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))(5~5/HH306T,-l~T0<NT0<<T55555,HH,,xx,Zl,l~,~~,l~/<N/NN/<<1EVENT-T@ PAGEHEADING RIGHTPAGE0 -T0 -T,NN,<</,-TA PAGEHEADING RIGHTPAGET,<</HH,5@ PAGEHEADINGLEFTBACKT/HHTITAN TITANTITANTITAN -TIMESROMAN -MODERNPALATINO PALATINO PALATINO PALATINO PALATINO TITAN CLASSIC -MODERN -CLASSIC -CLASSIC -CLASSIC - HELVETICACLASSIC +INTERLISP-D REFERENCE MANUAL +FUNCTION DEFINITION, MANIPULATION AND EVALUATION +"10"10. FUNCTION DEFINITION, MANIPULATION AND EVALUATION +3 + + +Medley is designed to help you define and debug functions. Developing an applications program with Medley involves defining a number of functions in terms of the system primitives and other user-defined functions. Once defined, your functions may be used exactly like Interlisp primitive functions, so the programming process can be viewed as extending the Interlisp language to include the required functionality. +A function's definition specifies if the function has a fixed or variable number of arguments, whether these arguments are evaluated or not, the function argument names, and a series of forms which define the behavior of the function. For example: +(LAMBDA (X Y) (PRINT X) (PRINT Y)) +This function has two evaluated arguments, X and Y, and it will execute (PRINT X) and (PRINT Y) when evaluated. Other types of function definitions are described below. +A function is defined by putting an expr definition in the function definition cell of a symbol. There are a number of functions for accessing and setting function definition cells, but one usually defines a function with DEFINEQ (see the Defining Functions section below). For example: + (DEFINEQ (FOO (LAMBDA (X Y) (PRINT X) (PRINT Y))))(FOO) +The expression above will define the function FOO to have the expr definition (LAMBDA (X Y) (PRINT X) (PRINT Y)). After being defined, this function may be evaluated just like any system function: + (FOO 3 (IPLUS 3 4)) +3 +7 +7 +Not all function definition cells contain expr definitions. The compiler (see the first page of Chapter 18) translates expr definitions into compiled code objects, which execute much faster. Interlisp provides a number of function type functions which determine how a given function is defined, the number and names of function arguments, etc. See the Function Type Functions section below. +Usually, functions are evaluated automatically when they appear within another function or when typed into Interlisp. However, sometimes it is useful to envoke the Interlisp interpreter explicitly to apply a given functional argument to some data. There are a number of functions which will apply a given function repeatedly. For example, MAPCAR will apply a function (or an expr definition) to all of the elements of a list, and return the values returned by the function: + (MAPCAR '(1 2 3 4 5) '(LAMBDA (X) (ITIMES X X)) +(1 4 9 16 25) +When using functional arguments, there are a number of problems which can arise, related to accessing free variables from within a function argument. Many times these problems can be solved using the function FUNCTION to create a FUNARG object. +The macro facility provides another way of specifying the behavior of a function (see the Macros section below). Macros are very useful when developing code which should run very quickly, which should be compiled differently than when it is interpreted, or which should run differently in different implementations of Interlisp. +Function Types +1 + +Interlisp functions are defined using list expressions called expr definitions. An expr definition is a list of the form (LAMBDA-WORD ARG-LIST FORM1 ... FORMN). LAMBDA-WORD determines whether the arguments to this function will be evaluated or not. ARG-LIST determines the number and names of arguments. FORM1 ... FORMN are a series of forms to be evaluated after the arguments are bound to the local variables in ARG-LIST. +If LAMBDA-WORD is the symbol LAMBDA, then the arguments to the function are evaluated. If LAMBDA-WORD is the symbol NLAMBDA, then the arguments to the function are not evaluated. Functions which evaluate or don't evaluate their arguments are therefore known as lambda or nlambda functions, respectively. +If ARG-LIST is NIL or a list of symbols, this indicates a function with a fixed number of arguments. Each symbol is the name of an argument for the function defined by this expression. The process of binding these symbols to the individual arguments is called spreading the arguments, and the function is called a spread function. If the argument list is any symbol other than NIL, this indicates a function with a variable number of arguments, known as a nospread function. +If ARG-LIST is anything other than a symbol or a list of symbols, such as (LAMBDA "FOO" ...), attempting to use this expr definition will generate an Arg not symbol error. In addition, if NIL or T is used as an argument name, the error Attempt to bind NIL or T is generated. +These two parameters (lambda/nlambda and spread/nospread) may be specified independently, so there are four nain function types, known as lambda-spread, nlanbda-spread, lanbda-nospread, and nlambda-nospread functions. Each one has a different form and is used for a different purpose. These four function types are described more fully below. +For lambda-spread, lanbda-nospread, or nlambda-spread functions, there is an upper limit to the number of arguments that a function can have, based on the number of arguments that can be stored on the stack on any one function call. Currently, the limit is 80 arguments. If a function is called with more than that many arguments, the error Too many arguments occurs. However, nlambda-nospread functions can be called with an arbitrary number of arguments, since the arguments are not individually saved on the stack. +Lambda-Spread Functions(LAMBDA-SPREAD% FUNCTIONS NIL Lambda-Spread% Functions NIL ("10") 2) +Lambda-spread functions take a fixed number of evaluated arguments. This is the most common function type. A lambda-spread expr definition has the form: +(LAMBDA (ARG1 ... ARGM) FORM1 ... FORMN) +The argument list (ARG1 ... ARGM) is a list of symbols that gives the number and names of the formal arguments to the function. If the argument list is ( ) or NIL, this indicates that the function takes no arguments. When a lambda-spread function is applied to some arguments, the arguments are evaluated, and bound to the local variables ARG1 ... ARGM. Then, FORM1 ... FORMN are evaluated in order, and the value of the function is the value of FORMN. + (DEFINEQ (FOO (LAMBDA (X Y) (LIST X Y)))) +(FOO) + (FOO 99 (PLUS 3 4)) +(99 7) +In the above example, the function FOO defined by (LAMBDA (X Y) (LIST X Y)) is applied to the arguments 99 and (PLUS 3 4). These arguments are evaluated (giving 99 and 7), the local variable X is bound to 99 and Y to 7, (LIST X Y) is evaluated, returning (99 7), and this is returned as the value of the function. +A standard feature of the Interlisp system is that no error occurs if a spread function is called with too many or too few arguments. If a function is called with too many argumnents, the extra arguments are evaluated but ignored. If a function is called with too few arguments, the unsupplied ones will be delivered as NIL. In fact, a spread function cannot distinguish between being given NIL as an argument, and not being given that argument, e.g., (FOO) and (FOO NIL) are exactly the same for spread functions. If it is necessary to distinguish between these two cases, use an nlambda function and explicitly evaluate the arguments with the EVAL function. +Nlambda-Spread Functions(NLAMBDA-SPREAD% FUNCTIONS NIL Nlambda-Spread% Functions NIL ("10") 3) +Nlambda-spread functions take a fixed number of unevaluated arguments. An nlambda-spread expr definition has the form: +(NLAMBDA (ARG1 ... ARGM) FORM1 ... FORMN) +Nlambda-spread functions are evaluated similarly to lanbda-spread functions, except that the arguments are not evaluated before being bound to the variables ARG1 ... ARGM. + (DEFINEQ (FOO (NLAMBDA (X Y) (LIST X Y)))) +(FOO) + (FOO 99 (PLUS 3 4)) +(99 (PLUS 3 4)) +In the above example, the function FOO defined by (NLAMBDA (X Y) (LIST X Y)) is applied to the arguments 99 and (PLUS 3 4). These arguments are unevaluated to X and Y. (LIST X Y) is evaluated, returning (99 (PLUS 3 4)), and this is returned as the value of the function. +Functions can be defined so that all of their arguments are evaluated (lambda functions) or none are evaluated (nlambda functions). If it is desirable to write a function which only evaluates some of its arguments (e.g., SETQ), the functions should be defined as an nlambda, with some arguments explicitly evaluated using the function EVAL. If this is done, the user should put the symbol EVAL on the property list of the function under the property INFO. This informs various system packages, such as DWIM, CLISP, and Masterscope, that this function in fact does evaluate its arguments, even though it is an nlambda. +Warning: A frequent problem that occurs when evaluating arguments to nlambda functions with EVAL is that the form being evaluated may reference variables that are not accessible within the nlambda function. This is usually not a problem when interpreting code, but when the code is compiled, the values of local variables may not be accessible on the stack (see Chapter 18). The system nlambda functions that evaluate their arguments (such as SETQ) are expanded in-line by the compiler, so this is not a problem. Using the macro facility is recommended in cases where it is necessary to evaluate some arguments to an nlambda function. +Lambda-Nospread Functions(LAMBDA-NOSPREAD% FUNCTIONS NIL Lambda-Nospread% Functions NIL ("10") 3) +Lambda-nospread functions take a variable number of evaluated arguments. A lambda-nospread expr definition has the form: +(LAMBDA VAR FORM1 ... FORMN) +VAR may be any symbol, except NIL and T. When a lambda-nospread function is applied to some arguments, each of these arguments is evaluated and the values stored on the stack. VAR is then bound to the number of arguments which have been evaluated. For example, if FOO is defined by (LAMBDA X ...), when (FOO A B C) is evaluated, A, B, and C are evaluated and X is bound to 3. VAR should never be reset +The following functions are used for accessing the arguments of lambda-nospread functions. +(ARG(ARG (Function) NIL NIL ("10") 4) VAR M) [NLambda Function] +Returns the Mth argument for the lambda-nospread function whose argument list is VAR. VAR is the name of the atomic argument list to a lambda-nospread function, and is not evaluated. M is the number of the desired argument, and is evaluated. The value of ARG is undefined for M less than or equal to 0 or greater than the value of VAR. +(SETARG(SETARG (Function) NIL NIL ("10") 4) VAR M X) [NLambda Function] +Sets the Mth argument for the lambda-nospread function whose argument list is VAR to X. VAR is not evaluated; M and X are evaluated. M should be between 1 and the value of VAR. +In the example below, the function FOO is defined to collect and return a list of all of the evaluated arguments it is given (the value of the for statement). + (DEFINEQ (FOO + (LAMBDA X (for ARGNUM from 1 to X collect (ARG X ARGNUM)] +(FOO) + (FOO 99 (PLUS 3 4)) +(99 7) + (FOO 99 (PLUS 3 4)(TIMES 3 4))) +(99 7 12) +NLambda-Nospread Functions(NLAMBDA-NOSPREAD% FUNCTIONS NIL NLambda-Nospread% Functions NIL ("10") 4) +Nlambda-nospread functions take a variable number of unevaluated arguments. An nlambda-nospread expr definition has the form: +(NLAMBDA VAR FORM1 ... FORMN) +VAR may be any symbol, except NIL and T. Though similar in form to lambda-nospread expr definitions, an nlambda-nospread is evaluated quite differently. When an nlambda-nospread function is applied to some arguments, VAR is simply bound to a list of the unevaluated arguments. The user may pick apart this list, and evaluate different arguments. +In the example below, FOO is defined to return the reverse of the list of arguments it is given (unevaluated): + (DEFINEQ (FOO (NLAMBDA X (REVERSE X)))) +(FOO) + (FOO 99 (PLUS 3 4)) +((PLUS 3 4) 99) + (FOO 99 (PLUS 3 4)(TIMES 3 4)) +(TIMES 3 4)(PLUS 3 4) 99) +The warning about evaluating arguments to nlambda functions also applies to nlambda-nospread function. +Compiled Functions(COMPILED% FUNCTIONS NIL Compiled% Functions NIL ("10") 5) +Functions defined by expr definitions can be compiled by the Interlisp compiler (see Chapter 18). The compiler produces compiled code objects (of data type CCODEP) which execute more quickly than the corresponding expr definition code. Functions defined by compiled code objects may have the same four types as expr definitions (lambda/nlambda, spread/nospread). Functions created by the compiler are referred to as compiled functions. +Function Type Functions +There are a variety of functions used for examining the type, argument list, etc. of functions. These functions may be given either a symbol (in which case they obtain the function definition from the definition cell), or a function definition itself. +(FNTYP(FNTYP (Function) NIL NIL ("10") 5) FN) [Function] +Returns NIL if FN is not a function definition or the name of a defined function. Otherwise, FNTYP returns one of the following symbols, depending on the type of function definition. + EXPR Lambda-spread expr definition + CEXPR Lambda-spread compiled definition + FEXPR Nlambda-spread expr definition + CFEXPR Nlambda-spread compiled definition + EXPR* Lambda-nospread expr definition + CEXPR* Lambda-nospread compiled definition + FEXPR* Nlambda-nospread expr definition + CFEXPR* Nlambda-nospread compiled definition + FUNARG FNTYP returns the symbol FUNARG if FN is a FUNARG expression. +EXP, FEXPR, EXPR*, and FEXPR* indicate that FN is defined by an expr definition. CEXPR, CFEXPR, CEXPR*, and CFEXPR* indicate that FN is defined by a compiled definition, as indicated by the prefix C. The suffix * indicates that FN has an indefinite number of arguments, i.e., is a nospread function. The prefix F indicates unevaluated arguments. Thus, for example, a CFEXPR* is a compiled nospread nlambda function. +(EXPRP(EXPRP (Function) NIL NIL ("10") 5) FN) [Function] +Returns T if (FNTYP FN) is EXPR, FEXPR, EXPR*, or FEXPR*; NIL otherwise. However, (EXPRP FN) is also true if FN is (has) a list definition, even if it does not begin with LAMBDA or NLAMBDA. In other words, EXPRP is not quite as selective as FNTYP. +(CCODEP(CCODEP (Function) NIL NIL ("10") 5) FN) [Function] +Returns T if (FNTYP FN) is either CEXPR, CFEXPR, CEXPR*, or CFEXPR*; NIL otherwise. +(ARGTYPE(ARGTYPE (Function) NIL NIL ("10") 6) FN) [Function] +FN is the name of a function or its definition. ARGTYPE returns 0, 1, 2, or 3, or NIL if FN is not a function. ARGTYPE corresponds to the rows of FNTYPs. The interpretation of this value is as follows: + 0 Lambda-spread function (EXPR, CEXPR) + 1 Nlambda-spread function (FEXPR, CFEXPR) + 2 Lambda-nospread function (EXPR*, CEXPR*) + 3 Nlambda-nospread function (FEXPR*, CFEXPR*) +(NARGS(NARGS (Function) NIL NIL ("10") 6) FN) [Function] +Returns the number of arguments of FN, or NIL if FN is not a function. If FN is a nospread function, the value of NARGS is 1. +(ARGLIST(ARGLIST (Function) NIL NIL ("10") 6) FN) [Function] +Returns the argument list for FN. Note that the argument list is a symbol for nospread functions. Since NIL is a possible value for ARGLIST, the error Args not available is generated if FN is not a function. +If FN is a compiled function, the argument list is constructed, i.e., each call to ARGLIST requires making a new list. For functions defined by expr definitions, lists beginning with LAMBDA or NLAMBDA, the argument list is simply CADR of GETD. If FN has an expr definition, and CAR of the definition is not LAMBDA or NLAMBDA, ARGLIST will check to see if CAR of the definition is a member of LAMBDASPLST (see Chapter 20). If it is, ARGLIST presumes this is a function object the user is defining via DWIMUSERFORMS, and simply returns CADR of the definition as its argument list. Otherwise ARGLIST generates an error as described above. +(SMARTARGLIST(SMARTARGLIST (Function) NIL NIL ("10") 6) FN EXPLAINFLG TAIL) [Function] +A smart version of ARGLIST that tries various strategies to get the arglist of FN. +First SMARTARGLIST checks the property list of FN under the property ARGNAMES. For spread functions, the argument list itself is stored. For nospread functions, the form is (NIL ARGLIST1 . ARGLIST2), where ARGLIST1 is the value SMARTARGLIST should return when EXPLAINFLG = T, and ARGLIST2 the value when EXPLAINFLG = NIL. For example, (GETPROP 'DEFINEQ 'ARGNAMES) = (NIL (X1 Xl ... XN) . X). This allows the user to specify special argument lists. +Second, if FN is not defined as a function, SMARTARGLIST attempts spelling correction on FN by calling FNCHECK (see Chapter 20), passing TAIL to be used for the call to FIXSPELL. If unsuccessful, the FN Not a function error will be generated. +Third, if FN is known to the file package (see Chapter 17) but not loaded in, SMARTARGLIST will obtain the arglist information from the file. +Otherwise, SMARTARGLIST simply returns (ARGLIST FN). +SMARTARGLIST is used by BREAK (see Chapter 15) and ADVISE with EXPLAINFLG = NIL for constructing equivalent expr definitions, and by the TTYIN in-line command ?= (see Chapter 26), with EXPLAINFLG = T. +Defining Functions +1 + +Function definitions are stored in a function definition cell associated with each symbol. This cell is directly accessible via the two functions PUTD and GETD (see below), but it is usually easier to define functions with DEFINEQ: +(DEFINEQ(DEFINEQ (Function) NIL NIL ("10") 7) X1 X2 ... XN) [NLambda NoSpread Function] +DEFINEQ is the function normally used for defining functions. It takes an indefinite number of arguments which are not evaluated. Each Xi must be a list defining one function, of the form (NAME DEFINITION). For example: +(DEFINEQ (DOUBLE (LAMBDA (X) (IPLUS X X)))) +The above expression will define the function DOUBLE with the expr definition (LAMBDA (X) (IPLUS X X)). Xi may also have the form (NAME ARGS . DEF-BODY), in which case an appropriate lambda expr definition will be constructed. Therefore, the above expression is exactly the same as: +(DEFINEQ (DOUBLE (X) (IPLUS X X))) +Note that this alternate form can only be used for lambda functions. The first form must be used to define an nlambda function. +DEFINEQ returns a list of the names of the functions defined. +(DEFINE(DEFINE (Function) NIL NIL ("10") 7) X %) [Function] +Lambda-spread version of DEFINEQ. Each element of the list X is itself a list either of the form (NAME DEFINITION) or (NAME ARGS . DEF-BODY). DEFINE will generate an error, Incorrect defining form on encountering an atom where a defining list is expected. +DEFINE and DEFINEQ operate correctly if the function is already defined and BROKEN, ADVISED, or BROKEN-IN. +For expressions involving type-in only, if the time stamp facility is enabled (see the Time Stamps section of Chapter 16), both DEFINE and DEFINEQ stamp the definition with your initials and date. +UNSAFE.TO.MODIFY.FNS(UNSAFE.TO.MODIFY.FNS (Variable) NIL NIL ("10") 7) [Variable] +Value is a list of functions that should not be redefined, because doing so may cause unusual bugs (or crash the system!). If you try to modify a function on this list (using DEFINEQ, TRACE, etc), the system prints Warning: XXX may be unsafe to modify -- continue? If you type Yes, the function is modified, otherwise an error occurs. This provides a measure of safety for novices who may accidently redefine important system functions. You can add your own functions onto this list. +By convention, all functions starting with the character backslash (\) are system internal functions, which you should never redefine or modify. Backslash functions are not on UNSAFE.TO.MODIFY.FNS, so trying to redefine them will not cause a warning. +DFNFLG(DFNFLG (Variable) NIL NIL ("10") 8) [Variable] +DFNFLG is a global variable that affects the operation of DEFINEQ and DEFINE. If DFNFLG=NIL, an attempt to redefine a function FN will cause DEFINE to print the message (FN REDEFINED) and to save the old definition of FN using SAVEDEF (see the Functions for Manipulating Typed Definitions section of Chapter 17) before redefining it (except if the old and new definitions are EQUAL, in which case the effect is simply a no-op). If DFNFLG=T, the function is simply redefined. If DFNFLG=PROP or ALLPROP, the new definition is stored on the property list under the property EXPR. ALLPROP also affects the operation of RPAQQ and RPAQ (see the Functions Used Within Source Files section of Chapter 17). DFNFLG is initially NIL. +DFNFLG is reset by LOAD (see the Loading Files section of Chapter 17) to enable various ways of handling the defining of functions and setting of variables when loading a file. For most applications, the user will not reset DFNFLG directly. +Note: The compiler does not respect the value of DFNFLG when it redefines functions to their compiled definitions (see the first page of Chapter 18). Therefore, if you set DFNFLG to PROP to completely avoid inadvertantly redefining something in your running system, you must use compile mode F, not ST. +Note that the functions SAVEDEF and UNSAVEDEF (see the Functions for Manipulating Typed Definitions section of Chapter 17) can be useful for saving and restoring function definitions from property lists. +(GETD(GETD (Function) NIL NIL ("10") 8) FN) [Function] +Returns the function definition of FN. Returns NIL if FN is not a symbol, or has no definition. +GETD of a compiled function constructs a pointer to the definition, with the result that two successive calls do not necessarily produce EQ results. EQP or EQUAL must be used to compare compiled definitions. +(PUTD(PUTD (Function) NIL NIL ("10") 8) FN DEF %) [Function] +Puts DEF into FN's function cell, and returns DEF. Generates an error, Arg not symbol, if FN is not a symbol. Generates an error, Illegal arg, if DEF is a string, number, or a symbol other than NIL. +(MOVD(MOVD (Function) NIL NIL ("10") 8) FROM TO COPYFLG %) [Function] +Moves the definition of FROM to TO, i.e., redefines TO. If COPYFLG = T, a COPY of the definition of FROM is used. COPYFLG =T is only meaningful for expr definitions, although MOVD works for compiled functions as well. MOVD returns TO. +COPYDEF (see the Functions for Manipulating Typed Definitions section of Chapter 17) is a higher-level function that not only moves expr definitions, but works also for variables, records, etc. +(MOVD?(MOVD? (Function) NIL NIL ("10") 9) FROM TO COPYFLG %) [Function] +If TO is not defined, same as (MOVD FROM TO COPYFLG). Otherwise, does nothing and returns NIL. +Function Evaluation +1 + +Usually, function application is done automatically by the Interlisp interpreter. If a form is typed into Interlisp whose CAR is a function, this function is applied to the arguments in the CDR of the form. These arguments are evaluated or not, and bound to the funcion parameters, as determined by the type of the function, and the body of the function is evaluated. This sequence is repeated as each form in the body of the function is evaluated. +There are some situations where it is necessary to explicitly call the evaluator, and Interlisp supplies a number of functions that will do this. These functions take functional arguments, which may either be symbols with function definitions, or expr definition forms such as (LAMBDA (X)...), or FUNARG expressions. +(APPLY(APPLY (Function) NIL NIL ("10") 9) FN ARGLIST %) [Function] +Applies the function FN to the arguments in the list ARGLIST, and returns its value. APPLY is a lambda function, so its arguments are evaluated, but the individual elements of ARGLIST are not evaluated. Therefore, lambda and nlambda functions are treated the same by APPLY%lambda functions take their arguments from ARGLIST without evaluating them. For example: + (APPLY 'APPEND '((PLUS 1 2 3)(4 5 6))) +(PLUS 1 2 3 4 5 6) +Note that FN may explicitly evaluate one or more of its arguments itself. For example, the system function SETQ is an nlambda function that explicitly evaluates its second argument. Therefore, (APPLY 'SETQ '(FOO (ADD1 3)))will set FOO to 4, instead of setting it to the expression (ADD1 3). +APPLY can be used for manipulating expr definitions. For example: + (APPLY '(LAMBDA (X Y)(ITIMES X Y)) '(3 4))) +12 +(APPLY*(APPLY* (Function) NIL NIL ("10") 9) FN ARG1 ARG2 ... ARGN ) [NoSpread Function] +Nospread version of APPLY. Applies the function FN to the arguments ARG1 ARG2 ... ARGN. For example: + (APPLY 'APPEND '(PLUS 1 2 3)(4 5 6)) +(PLUS 1 2 3 4 5 6) +(EVAL(EVAL (Function) NIL NIL ("10") 10) X%) [Function] +EVAL evaluates the expression X and returns this value, i.e., EVAL provides a way of calling the Interlisp interpreter. Note that EVAL is itself a lambda function, so its argument is first evaluated, e.g.: + (SETQ FOO 'ADD1 3))) +(ADD1 3) +(EVAL FOO) +4 +(EVAL 'FOO) +(ADD1 3) +(QUOTE(QUOTE (Function) NIL NIL ("10") 10) X) [Nlambda NoSpread Function] +QUOTE prevents its arguments from being evaluated. Its value is X itself, e.g., (QUOTE FOO) is FOO. +Interlisp functions can either evaluate or not evaluate their arguments. QUOTE can be used in those cases where it is desirable to specify arguments unevaluated. +The single-quote character (') is defined with a read macro so it returns the next expression, wrapped in a call to QUOTE (see Chapter 25). For example, 'FOO reads as (QUOTE FOO). This is the form used for examples in this manual. +Since giving QUOTE more than one argument is almost always a parenthese error, and one that would otherewise go undetected, QUOTE itself generates an error in this case, Parenthesis error. +(KWOTE(KWOTE (Function) NIL NIL ("10") 10) X) [Function] +Value is an expression which, when evaluated, yields X. If X is NIL or a number, this is X itself. Otherwise (LIST (QUOTE QUOTE) X). For example: +(KWOTE 5) => 5 +(KWOTE (CONS 'A 'B)) => (QUOTE (A.B)) +(NLAMBDA.ARGS(NLAMBDA.ARGS (Function) NIL NIL ("10") 10) X) [Function] +This function interprets its argument as a list of unevaluated nlambda arguments. If any of the elements in this list are of the form (QUOTE...), the enclosing QUOTE is stripped off. Actually, NLAMBDA.ARGS stops processing the list after the first non-quoted argument. Therefore, whereas (NLAMBDA.ARGS '((QUOTE FOO) BAR)) -> (FOO BAR), (NLAMBDA.ARGS '(FOO (QUOTE BAR))) -> (FOO (QUOTE BAR)). +NLAMBDA.ARGS is alled by a number of nlambda functions in the system, to interpret their arguments. For instance, the function BREAK calls NLAMBDA.ARGS so that (BREAK 'FOO) will break the function FOO, rather than the function QUOTE. +(EVALA(EVALA (Function) NIL NIL ("10") 10) X A) [Function] +Simulates association list variable lookup. X is a form, A is a list of the form: +((NAME1 . VAL1) (NAME2 . VAL2)... (NAMEN . VALN)) +The variable names and values in A are spread on the stack, and then X is evaluated. Therefore, any variables appearing free in X that also appears as CAR of an element of A will be given the value on the CDR of that element. +(DEFEVAL(DEFEVAL (Function) NIL NIL ("10") 11) TYPE FN) [Function] +Specifies how a datum of a particular type is to be evaluated. Intended primarily for user-defined data types, but works for all data types except lists, literal atoms, and numbers. TYPE is a type name. FN is a function object, i.e., name of a function or a lambda expression. Whenever the interpreter encounters a datum of the indicated type, FN is applied to the datum and its value returned as the result of the evaluation. DEFEVAL returns the previous evaling function for this type. If FN = NIL, DEFEVAL returns the current evaling function without changing it. If FN = T, the evaling functions is set back to the system default (which for all data types except lists is to return the datum itself). +COMPILETYPELST (see Chapter 18) permits the user to specify how a datum of a particular type is to be compiled. +(EVALHOOK(EVALHOOK (Function) NIL NIL ("10") 11) FORM EVALHOOKFN) [Function] +EVALHOOK evaluates the expression FORM, and returns its value. While evaluating FORM, the function EVAL behaves in a special way. Whenever a list other than FORM itself is to be evaluated, whether implicitly or via an explicit call to EVAL, EVALHOOKFN is invoked (it should be a function), with the form to be evaluated as its argument. EVALHOOKFN is then responsible for evaluating the form. Whatever is returned is assume to be the result of evaluating the form. During the execution of EVALHOOKFN, this special evaluation is turned off. (Note that EVALHOOK does not affect the evaluations of variables, only of lists). +Here is an example of a simple tracing routine that uses the EVALHOOK feature: +(DEFINEQ (PRINTHOOK (FORM) +(printout T "eval: "FORM T) +(EVALHOOK FORM (FUNCTION PRINTHOOK +(PRINTHOOK) +Using PRINTHOOK, one might see the following interaction: +(EVALHOOK '(LIST (CONS 1 2)(CONS 3 4)) 'PRINTHOOK) +eval: (CONS 1 2) +eval: (CONS 3 4) +((1.2)(3.4)) +Iterating and Mapping Functions +1 + +The functions below are used to evaluate a form or apply a function repeatedly. RPT, RPTQ, and FRPTQ evaluate an expression a specified number of time. MAP, MAPCAR, MAPLIST, etc., apply a given function repeatedly to different elements of a list, possibly constructing another list. +These functions allow efficient iterative computations, but they are difficult to use. For programming iterative computations, it is usually better to use the CLISP Iterative Statement facility (see Chapter 9), which provides a more general and complete facility for expressing iterative statements. Whenever possible, CLISP transltes iterative statements into expressions using the functions below, so there is no efficiency loss. +(RPT(RPT (Function) NIL NIL ("10") 12) N FORM) [Function] +Evaluates the expression FORM, N times. Returns the value of the last evaluation. If N is less than or equal to 0, FORM is not evaluated, and RPT returns NIL. +Before each evaluation, the local variable RPTN is bound to the number of evaluations yet to take place. This variable can be referenced within FORM. For example, (RPT 10 '(PRINT RPTN)) will print the numbers 10, 9...1, and return 1. +(RPTQ(RPTQ (Function) NIL NIL ("10") 12) N FORM1 FORM2... FORMN) [NLambda NoSpread Function] +Nlambda-nospread version of RPT: N is evaluated, FORMi are not. Returns the value of the last evaluation of FORMN. +(FRPTQ(FRPTQ (NLambda NoSpread Function) NIL NIL ("10") 12) N FORM1 FORM2... FORMN) [NLambda NoSpread Function] +Faster version of RPTQ. Does not bind RPTN. +(MAP(MAP (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] +If MAPFN2 is NIL, MAP applies the function MAPFN1 to successive tails of the list MAPX. That is, first it computes (MAPFN1 MAPX), and then (MAPFN1 (CDR MAPX)), etc., until MAPX becomes a non-list. If MAPFN2 is provided, (MAPFN2 MAPX) is used instead of (CDR MAPX) for the next call for MAPFN1, e.g., if MAPFN2 were CDDR, alternate elements of the list would be skipped. MAP returns NIL. +(MAPC(MAPC (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] +Identical to MAP, except that (MAPFN1 (CAR MAPX)) is computed at each iteration instead of (MAPFN1 MAPX), i.e., MAPC works on elements, MAP on tails. MAPC returns NIL. +(MAPLIST(MAPLIST (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] +Successively computes the same values that MAP would compute, and returns a list consisting of those values. +(MAPCAR(MAPCAR (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] +Computes the same values that MAPC would compute, and returns a list consisting of those values, e.g., (MAPCAR X 'FNTYP) is a list of FNTYPs for each element on X. +(MAPCON(MAPCON (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] +Computes the same values that MAP and MAPLIST but NCONCs these values to form a list which it returns. +(MAPCONC(MAPCONC (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] +Computes the same values that MAPC and MAPCAR, but NCONCs the values to form a list which it returns. +Note that MAPCAR creates a new list which is a mapping of the old list in that each element of the new list is the result of applying a function to the corresponding element on the original list. MAPCONC is used when there are a variable number of elements (including none) to be inserted at each iteration. Examples: +(MAPCONC '(A B C NIL D NIL) '(LAMBDA (Y)(if (NULL Y) then NIL + else (LIST Y)))) = > (A B C D) +This MAPCONC returns a list consisting of MAPX with all NILs removed. +(MAPCONC '((A B) C (D E F)(G) H I) '(LAMBDA (Y)(if (LISP Y) then Y + else NIL))) = > (A B D E F G) +This MAPCONC returns a linear list consisting of all the lists on MAPX. +Since MAPCONC uses NCONC to string the corresponding lists together, in this example the original list will be altered to be ((A B C D E F G) C (D E F G)(G) H I). If this is an undesirable side effect, the functional argument to MAPCONC should return instead a top level copy of the lists, i.e., (LAMBDA (Y) (if (LISTP Y) then (APPERND Y) else NIL))). +(MAP2C(MAP2C (Function) NIL NIL ("10") 13) MAPX MAPY MAPFN1 MAPFN2) [Function] +Identical to MAPC except MAPFN1 is a function of two arguments, and (MAPFN1 (CAR MAPX)(CAR MAPY)) is computed at each iteration. Terminates when either MAPX or MAPY is a non-list. +MAPFN2 is still a function of one argument, and is applied twice on each iteration; (MAPFN2 MAPX) gives the new MAPX, (MAPFN2 MAPY) the new MAPY. CDR is used if MAPFN2 is not supplied, i.e., is NIL. +(MAP2CAR(MAP2CAR (Function) NIL NIL ("10") 13) MAPX MAPY MAPFN1 MAPFN2) [Function] +Identical to MAPCAR except MAPFN1 is a function of two arguments, and (MAPFN1 (CAR MAPX)(CAR MAPY)) is used to assemble the new list. Terminates when either MAPX or MAPY is a non-list. +(SUBSET(SUBSET (Function) NIL NIL ("10") 13) MAPX MAPFN1 MAPFN2) [Function] +Applies MAPFN1 to elements of MAPX and returns a list of those elements for which this application is non-NIL, e.g.: +(SUBSET '(A B 3 C 4) 'NUMBERP) = (3 4) +MAPFN2 plays the same role as with MAP, MAPC, et al. +(EVERY(EVERY (Function) NIL NIL ("10") 13) EVERYX EVERYFN1 EVERYFN2) [Function] +Returns T if the result of applying EVERYFN1 to each element in EVERYX is true, otherwise NIL. For example, (EVERY '(X Y Z) 'ATOM) => T. +EVERY operates by evaluating (EVERYFN1 (CAR EVERYX) EVERYX). The second argument is passed to EVERYFN1 so that it can look at the next element on EVERYX if necessary. If EVERYFN1 yields NIL, EVERY immediately returns NIL. Otherwise, EVERY computes (EVERYFN2 EVERYX), or (CDR EVERYX) if EVERYFN2 = NIL, and uses this as the new EVERYX, and the process continues. For example (EVERY X 'ATOM 'CDDR) is true if every other element of X is atomic. +(SOME(SOME (Function) NIL NIL ("10") 14) SOMEX SOMEFN1 SOMEFN2) [Function] +Returns the tail of SOMEX beginning with the first element that satisfies SOMEFN1, i.e., for which SOMEFN1 applied to that element is true. Value is NIL if no such element exists. (SOME X '(LAMBDA (Z) (EQUAL Z Y))) is equivalent to (MEMBER Y X). SOME operates analogously to EVERY. At each stage, (SOMEFN1 (CAR SOMEX) SOMEX) is computed, and if this not NIL, SOMEX is returned as the value of SOME. Otherwise, (SOMEFN2 SOMEX) is computed, or (CDR SOMEX) if SOMEFN2 = NIL, and used for the next SOMEX. +(NOTANY(NOTANY (Function) NIL NIL ("10") 14) SOMEX SOMEFN1 SOMEFN2) [Function] +(NOT (SOME SOMEX SOMEFN1 SOMEFN2)). +(NOTEVERY(NOTEVERY (Function) NIL NIL ("10") 14) EVERYX EVERYFN1 EVERYFN2) [Function] +(NOT (EVERY EVERYX EVERYFN1 EVERYFN2)). +(MAPRINT(MAPRINT (Function) NIL NIL ("10") 14) LST FILE LEFT RIGHT SEP PFN LISPXPRINTFLG) [Function] +A general printing function. For each element of the list LST, applies PFN to the element, and FILE. If PFN is NIL, PRIN1 is used. Between each application MAPRINT performs PRIN1 of SEP (or "" if SEP = NIL). If LEFT is given, it is printed (using PRIN1) initially; if RIGHT is given, it is printed (using PRIN1) at the end. +For example, (MAPRINT X NIL '%( '%)) is equivalent to PRIN1 for lists. To print a list with commas between each element and a final . one could use (MAPRINT X T NIL '%. '%,). +If LISPXPRINTFLG = T, LISPXPRIN1 (see Chapter 13) is used instead of PRIN1. +Functional Arguments +1 + +The functions that call the Interlisp-D evaluator take functional arguments, which may be symbols with function definitions, or expr definition forms such as (LAMBDA (X) ...). +The following functions are useful when one wants to supply a functional argument which will always return NIL, T, or 0. Note that the arguments X1 ... XN to these functions are evaluated, though they are not used. +(NILL(NILL (NoSpread Function) NIL NIL ("10") 14) X1 ... XN ) [NoSpread Function] +Returns NIL. +(TRUE(TRUE (NoSpread Function) NIL NIL ("10") 14) X1 ... XN ) [NoSpread Function] +Returns T. +(ZERO(ZERO (NoSpread Function) NIL NIL ("10") 15) X1 ... XN ) [NoSpread Function] +Returns 0. +When using expr definitions as function arguments, they should be enclosed within the function FUNCTION rather than QUOTE, so that they will be compiled as separate functions. +(FUNCTION(FUNCTION (NLambda Function) NIL NIL ("10") 15) FN ENV ) [NLambda Function] +If ENV = NIL, FUNCTION is the same as QUOTE, except that it is treated differently when compiled. Consider the function definition: +(DEFINEQ (FOO (LST)(FIE LST (FUNCTION (LAMBDA (Z)(ITIMES Z Z))] +FOO calls the function FIE with the value of LST and the expr definition (LAMBDA (Z)(LIST (CAR Z))). +If FOO is run interpreted, it does not make any difference whether FUNCTION or QUOTE is used. However, when FOO is compiled, if FUNCTION is used the compiler will define and compile the expr definition as an auxiliary function (see Chapter 18). The compiled expr definition will run considerably faster, which can make a big difference if it is applied repeatedly. +Compiling FUNCTION will not create an auxiliary function if it is a functional argument to a function that compiles open, such as most of the mapping functions (MAPCAR, MAPLIST, etc.). +If ENV is not NIL, it can be a list of variables that are (presumably) used freely by FN. ENV can also be an atom, in which case it is evaluated, and the value interpreted as described above. +Macros +1 + +Macros provide an alternative way of specifying the action of a function. Whereas function definitions are evaluated with a function call, which involves binding variables and other housekeeping tasks, macros are evaluated by translating one Interlisp form into another, which is then evaluated. +A symbol may have both a function definition and a macro definition. When a form is evaluated by the interpreter, if the CAR has a function definition, it is used (with a function call), otherwise if it has a macro definition, then that is used. However, when a form is compiled, the CAR is checked for a macro definition first, and only if there isn't one is the function definition compiled. This allows functions that behave differently when compiled and interpreted. For example, it is possible to define a function that, when interpreted, has a function definition that is slow and has a lot of error checks, for use when debugging a system. This function could also have a macro definition that defines a fast version of the function, which is used when the debugged system is compiled. +Macro definitions are represented by lists that are stored on the property list of a symbol. Macros are often used for functions that should be compiled differently in different Interlisp implementations, and the exact property name a macro definition is stored under determines whether it should be used in a particular implementation. The global variable MACROPROPS contains a list of all possible macro property names which should be saved by the MACROS file package command. Typical macro property names are DMACRO for Interlisp-D, 10MACRO for Interlisp-10, VAXMACRO for Interlisp-VAX, JMACRO for Interlisp-Jerico, and MACRO for implementation independent macros. The global variable COMPILERMACROPROPS is a list of macro property names. Interlisp determines whether a symbol has a macro definition by checking these property names, in order, and using the first non-NIL property value as the macro definition. In Interlisp-D this list contains DMACRO and MACRO in that order so that DMACROs will override the implementation-independent MACRO properties. In general, use a DMACRO property for macros that are to be used only in Interlisp-D, use 10MACRO for macros that are to be used only in Interlisp-10, and use MACRO for macros that are to affect both systems. +Macro definitions can take the following forms: + (LAMBDA ...) + (NLAMBDA ...) A function can be made to compile open by giving it a macro definition of the form (LAMBDA ...) or (NLAMBDA ...), e.g., (LAMBDA (X) (COND ((GREATERP X 0) X) (T (MINUS X)))) for ABS. The effect is as if the macro definition were written in place of the function wherever it appears in a function being compiled, i.e., it compiles as a lambda or nlambda expression. This saves the time necessary to call the function at the price of more compiled code generated in-line. + (NIL EXPRESSION) + (LIST EXPRESSION) Substitution macro. Each argument in the form being evaluated or compiled is substituted for the corresponding atom in LIST, and the result of the substitution is used instead of the form. For example, if the macro definition of ADD1 is ((X) (IPLUS X 1)), then, (ADD1 (CAR Y)) is compiled as (IPLUS (CAR Y) 1). + Note that ABS could be defined by the substitution macro ((X) (COND ((GREATERP X 0) X) (T (MINUS X)))). In this case, however, (ABS (FOO X)) would compile as + (COND ((GREATERP (FOO X) 0) + (FOO X)) + (T (MINUS (FOO X)))) + and (FOO X) would be evaluated two times. (Code to evaluate (FOO X) would be generated three times.) + (OPENLAMBDA ARGS BODY) This is a cross between substitution and LAMBDA macros. When the compiler processes an OPENLAMBDA, it attempts to substitute the actual arguments for the formals wherever this preserves the frequency and order of evaluation that would have resulted from a LAMBDA expression, and produces a LAMBDA binding only for those that require it. + Note: OPENLAMBDA assumes that it can substitute literally the actual arguments for the formal arguments in the body of the macro if the actual is side-effect free or a constant. Thus, you should be careful to use names in ARGS which don't occur in BODY (except as variable references). For example, if FOO has a macro definition of + (OPENLAMBDA (ENV) (FETCH (MY-RECORD-TYPE ENV) OF BAR)) + then (FOO NIL) will expand to + (FETCH (MY-RECORD-TYPE NIL) OF BAR) + T When a macro definition is the atom T, it means that the compiler should ignore the macro, and compile the function definition; this is a simple way of turning off other macros. For example, the user may have a function that runs in both Interlisp-D and Interlisp-10, but has a macro definition that should only be used when compiling in Interlisp-10. If the MACRO property has the macro specification, a DMACRO of T will cause it to be ignored by the Interlisp-D compiler. This DMACRO would not be necessary if the macro were specified by a 10MACRO instead of a MACRO. + (= . OTHER-FUNCTION) A simple way to tell the compiler to compile one function exactly as it would compile another. For example, when compiling in Interlisp-D, FRPLACAs are treated as RPLACAs. This is achieved by having FRPLACA have a DMACRO of (= . RPLACA). + (LITATOM EXPRESSION) If a macro definition begins with a symbol other than those given above, this allows computation of the Interlisp expression to be evaluated or compiled in place of the form. LITATOM is bound to the CDR of the calling form, EXPRESSION is evaluated, and the result of this evaluation is evaluated or compiled in place of the form. For example, LIST could be compiled using the computed macro: + [X (LIST 'CONS (CAR X)(AND (CDR X)(CONS 'LIST (CDR X] + This would cause (LIST X Y Z) to compile as (CONS X (CONS Y (CONS Z NIL))). Note the recursion in the macro expansion. + If the result of the evaluation is the symbol IGNOREMACRO, the macro is ignored and the compilation of the expression proceeds as if there were no macro definition. If the symbol in question is normally treated specially by the compiler (CAR, CDR, COND, AND, etc.), and also has a macro, if the macro expansion returns IGNOREMACRO, the symbol will still be treated specially. + In Interlisp-10, if the result of the evaluation is the atom INSTRUCTIONS, no code will be generated by the compiler. It is then assumed the evaluation was done for effect and the necessary code, if any, has been added. This is a way of giving direct instructions to the compiler if you understand it. + It is often useful, when constructing complex macro expressions, to use the BQUOTE facility (see the Read Macros section of Chapter 25). + The following function is quite useful for debugging macro definitions: +(EXPANDMACRO(EXPANDMACRO (Function) NIL NIL ("10") 18) EXP QUIETFLG % %) [Function] +Takes a form whose CAR has a macro definition and expands the form as it would be compiled. The result is prettyprinted, unless QUIETFLG=T, in which case the result is simply returned. +Note: EXPANDMACRO only works on Interlisp macros. Use CL:MACROEXPAND-1 to expand Interlisp macros visible to the Common Lisp interpreter and compliler. +DEFMACRO +Macros defined with the function DEFMACRO are much like computed macros (see the above section), in that they are defined with a form that is evaluated, and the result of the evaluation is used (evaluated or compiled) in place of the macro call. However, DEFMACRO macros support complex argument lists with optional arguments, default values, and keyword arguments as well as argument list destructuring. +(DEFMACRO(DEFMACRO (Function) NIL NIL ("10") 18) NAME ARGS FORM) [NLambda NoSpread Function] +Defines NAME as a macro with the arguments ARGS and the definition form FORM (NAME, ARGS, and FORM are unevaluated). If an expression starting with NAME is evaluated or compiled, arguments are bound according to ARGS, FORM is evaluated, and the value of FORM is evaluated or compiled instead. The interpretation of ARGS is described below. +Note: Like the function DEFMACRO in Common Lisp, this function currently removes any function definition for NAME. +ARGS is a list that defines how the argument list passed to the macro NAME is interpreted. Specifically, ARGS defines a set of variables that are set to various arguments in the macro call (unevaluated), that FORM can reference to construct the macro form. +In the simplest case, ARGS is a simple list of variable names that are set to the corresponding elements of the macro call (unevaluated). For example, given: +(DEFMACRO FOO (A B) (LIST 'PLUS A B B)) +The macro call (FOO X (BAR Y Z)) will expand to (PLUS X (BAR Y Z) (BAR Y Z)). +&-keywords (beginning with the character &) that are used to set variables to particular items from the macro call form, as follows: + &OPTIONAL Used to define optional arguments, possibly with default values. Each element on ARGS after &OPTIONAL until the next &-keyword or the end of the list defines an optional argument, which can either be a symbol or a list, interpreted as follows: + VAR + If an optional argument is specified as a symbol, that variable is set to the corresponding element of the macro call (unevaluated). + (VAR DEFAULT) + If an optional argument is specified as a two element list, VAR is the variable to be set, and DEFAULT is a form that is evaluated and used as the default if there is no corresponding element in the macro call. + (VAR DEFAULT VARSETP) + If an optional argument is specified as a three element list, VAR and DEFAULT are the variable to be set and the default form, and VARSETP is a variable that is set to T if the optional argument is given in the macro call, NIL otherwise. This can be used to determine whether the argument was not given, or whether it was specified with the default value. + For example, after (DEFMACRO FOO (&OPTIONAL A (B 5) (C 6 CSET)) FORM) expanding the macro call (FOO) would cause FORM to be evaluated with A set to NIL, B set to 5, C set to 6, and CSET set to NIL. (FOO 4 5 6) would be the same, except that A would be set to 4 and CSET would be set to T. + &REST + &BODY Used to get a list of all additional arguments from the macro call. Either &REST or &BODY should be followed by a single symbol, which is set to a list of all arguments to the macro after the position of the &-keyword. For example, given + (DEFMACRO FOO (A B &REST C) FORM) + expanding the macro call (FOO 1 2 3 4 5) would cause FORM to be evaluated with A set to 1, B set to 2, and C set to (3 4 5). + If the macro calling form contains keyword arguments (see &KEY below), these are included in the &REST list. + &KEY Used to define keyword arguments, that are specified in the macro call by including a keyword (a symbol starting with the character :) followed by a value. + Each element on ARGS after &KEY until the next &-keyword or the end of the list defines a keyword argument, which can either be a symbol or a list, interpreted as follows: + VAR + (VAR) + ((KEYWORD VAR)) + If a keyword argument is specified by a single symbol VAR, or a one-element list containing VAR, it is set to the value of a keyword argument, where the keyword used is created by adding the character : to the front of VAR. If a keyword argument is specified by a single-element list containing a two-element list, KEYWORD is interpreted as the keyword (which should start with the letter :), and VAR is the variable to set. + (VAR DEFAULT) + ((KEYWORD VAR) DEFAULT) + (VAR DEFAULT VARSETP) + ((KEYWORD VAR) DEFAULT VARSETP) + If a keyword argument is specified by a two- or three-element list, the first element of the list specifies the keyword and variable to set as above. Similar to &OPTIONAL (above), the second element DEFAULT is a form that is evaluated and used as the default if there is no corresponding element in the macro call, and the third element VARSETP is a variable that is set to T if the optional argument is given in the macro call, NIL otherwise. + For example, the form + (DEFMACRO FOO (&KEY A (B 5 BSET) ((:BAR C) 6 CSET)) FORM) + Defines a macro with keys :A, :B (defaulting to 5), and :BAR. Expanding the macro call (FOO :BAR 2 :A 1) would cause FORM to be evaluated with A set to 1, B set to 5, BSET set to NIL, C set to 2, and CSET set to T. + &ALLOW-OTHER-KEYS It is an error for any keywords to be supplied in a macro call that are not defined as keywords in the macro argument list, unless either the &-keyword &ALLOW-OTHER-KEYS appears in ARGS, or the keyword :ALLOW-OTHER-KEYS (with a non-NIL value) appears in the macro call. + &AUX Used to bind and initialize auxiliary varables, using a syntax similar to PROG (see the PROG and Associated Control Functions section of Chapter 9). Any elements after &AUX should be either symbols or lists, interpreted as follows: + VAR + Single symbols are interpreted as auxiliary variables that are initially bound to NIL. + (VAR EXP) + If an auxiliary variable is specified as a two element list, VAR is a variable initially bound to the result of evaluating the form EXP. + For example, given + (DEFMACRO FOO (A B &AUX C (D 5)) FORM) + C will be bound to NIL and D to 5 when FORM is evaluated. + &WHOLE Used to get the whole macro calling form. Should be the first element of ARGS, and should be followed by a single symbol, which is set to the entire macro calling form. Other &-keywords or arguments can follow. For example, given + (DEFMACRO FOO (&WHOLE X A B) FORM) + Expanding the macro call (FOO 1 2) would cause FORM to be evaluated with X set to (FOO 1 2), A set to 1, and B set to 2. + DEFMACRO macros also support argument list destructuring, a facility for accessing the structure of individual arguments to a macro. Any place in an argument list where a symbol is expected, an argument list (in the form described above) can appear instead. Such an embedded argument list is used to match the corresponding parts of that particular argument, which should be a list structure in the same form. In the simplest case, where the embedded argument list does not include &-keywords, this provides a simple way of picking apart list structures passed as arguments to a macro. For example, given + (DEFMACRO FOO (A (B (C . D)) E) FORM) + Expanding the macro call (FOO 1 (2 (3 4 5)) 6) would cause FORM to be evaluated with with A set to 1, B set to 2, C set to 3, D set to (4 5), and E set to 6. Note that the embedded argument list (B (C . D)) has an embedded argument list (C . D). Also notice that if an argument list ends in a dotted pair, that the final symbol matches the rest of the arguments in the macro call. + An embedded argument list can also include &-keywords, for interpreting parts of embedded list structures as if they appeared in a top-level macro call. For example, given + (DEFMACRO FOO (A (B &OPTIONAL (C 6)) D) FORM) + Expanding the macro call (FOO 1 (2) 3) would cause FORM to be evaluated with with A set to 1, B set to 2, C set to 6 (because of the default value), and D set to 3. + Warning: Embedded argument lists can only appear in positions in an argument list where a list is otherwise not accepted. In the above example, it would not be possible to specify an embedded argument list after the &OPTIONAL keyword, because it would be interpreted as an optional argument specification (with variable name, default value, set variable). However, it would be possible to specify an embedded argument list as the first element of an optional argument specification list, as so: + (DEFMACRO FOO (A (B &OPTIONAL ((X (Y) Z) + '(1 (2) 3))) D) FORM) + In this case, X, Y, and Z default to 1, 2, and 3, respectively. Note that the default value has to be an appropriate list structure. Also, in this case either the whole structure (X (Y) Z) can be supplied, or it can be defaulted (i.e., is not possible to specify X while letting Y default). +Interpreting Macros +When the interpreter encounters a form CAR of which is an undefined function, it tries interpreting it as a macro. If CAR of the form has a macro definition, the macro is expanded, and the result of this expansion is evaluated in place of the original form. CLISPTRAN (see the Miscellaneous Functions and Variables section of Chapter 21) is used to save the result of this expansion so that the expansion only has to be done once. On subsequent occasions, the translation (expansion) is retrieved from CLISPARRAY the same as for other CLISP constructs. +Note: Because of the way that the evaluator processes macros, if you have a macro on FOO, then typing (FOO 'A 'B) will work, but FOO(A B) will not work. + + +[This page intentionally left blank] + +(SEQUENCE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "10-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "10-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (ALTERNATE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "10-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "10-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (102 612 510 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "10-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "10-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))))))))1$16$EVENT2$TE$ PAGEHEADING RIGHTPAGE1$2 +T7777771HH1l~$506$T2 +T1HH$1HH1$1$1xx$2l~$T1Zl$1l~$1~~$7~71HH1<N2<NT1<<$1<<1NN2<<T1NN$1<<$1$2$TF$ PAGEHEADING RIGHTPAGETE$ PAGEHEADINGLEFTBACKTPALATINO GACHA +PALATINO TITANMODERN MODERN -'"1! IM.CHAP.GETFNPALATINO 1 HRULE.GETFNMODERN! -% - -# + - - -  - -J - -; -: . - -"V -  - X - -2 - - - H - - HRULE.GETFN - | -  - M - 0 - _ - -  -  -8 -  - -  - -P -  -a -  -? -: -  - -( - - Y - W - - DIM.INDEX.GETFN -  -w - - - - F - -,# - - - - -) - - - - - - - - -5 -B -E -: - -  - -FIM.INDEX.GETFNx - - --# - - - - -& - - -  -5 - -n -3 -9 - - V -_ - -HIM.INDEX.GETFNz -  - - -V - - -  - - - - - - -[ - !IM.INDEX.GETFNTITAN -$ -D - -_ -H - -6 - - $IM.INDEX.GETFNTITAN -$ -D - - - - - -& - -# -y -#<" -JIM.INDEX.GETFN -   - - - - -V -+!i -:IM.INDEX.GETFN - - - #IM.INDEX.GETFNTITAN -$ - -M -U -   -  " -   - $ -   -  $ - -   $ -     -   -   -  -$ - - - -$ - - - - -A - - -Q -8 -* - #IM.INDEX.GETFNTITAN$ - - -  - - - - - -  -< - - - - - $IM.INDEX.GETFNTITAN$ - - - - - - - - %IM.INDEX.GETFNTITAN -$/ - - - - - - - - -5 - -  - - - - - #IM.INDEX.GETFNTITAN -$# - - - -& - - - %IM.INDEX.GETFNTITAN -$ -L - - - - -$ -N -^ - - - - - - - - - -" -  -= -  -4 -( - *IM.INDEX.GETFNTITAN -$ -5 - -$ -  - -c -  - -  - - - - - - - -7; -$ - - ! - - - - - -$ - -B - 4 -$ -  -  -$  - - - - -: - - - - - - - HRULE.GETFN - - -@ - - %IM.INDEX.GETFNCLASSIC - -$ -3 - -,$. - - - - -#$ -$6 - -  $IM.INDEX.GETFNCLASSIC - -$ - -% - - - -< -  -: - - -  - - -3 - 2IM.INDEX.GETFNCLASSIC - -$ - - -2 - -$ -7 - $IM.INDEX.GETFNCLASSIC - -$4 - - - - - - - -# - - -3 -( -  -G - - - -F - - -$ - - -$  - -v - -T - - - -$ - -  - "IM.INDEX.GETFNCLASSIC - -$# - - -( -$ - - -. - - "IM.INDEX.GETFNCLASSIC -  -$ - - - -  -' -  -- - - - "IM.INDEX.GETFNCLASSIC -  -$ - - - - - - - - -3 -( - - -$ - - - #IM.INDEX.GETFNCLASSIC -  - -$ - -& - - - HRULE.GETFN - { -A - -  - - - #IM.INDEX.GETFNTITAN - -$ - - -V -U -[ -P -,$ - -` -S - - -* - -$> -/ $IM.INDEX.GETFNTITAN - - - -$ - - - -( #IM.INDEX.GETFNTITAN -$ - -A -H -     $IM.INDEX.GETFNTITAN - - - -$< - -  - -$J -T -$ -5 -! - - 6 -$ -j -) - - $IM.INDEX.GETFNTITAN - -$5 - - - - - -& +IM.INDEX.GETFNTITAN -$ - - - - S -/ -8$ t - -  -  - - - $IM.INDEX.GETFNTITAN -$- - - -$! -% -; - - - - - &IM.INDEX.GETFNTITAN -$ - - -R -: - - -? - - -$b - - 'IM.INDEX.GETFNTITAN - -$ -+ - -7 -J - - -W - - - -5 -? -$= - - -# $ - + -4  - HRULE.GETFN - Q - - -5 - - -o -  - "IM.INDEX.GETFNTITAN -$ - -7 - - - - -$+ -b - - - - - - - #IM.INDEX.GETFNTITAN - - -$ - - -6 - - 5IM.INDEX.GETFNTITAN - - - -$ - - - "IM.INDEX.GETFNTITAN -$ - - - -! - - - - - - - - - -4 - - - #IM.INDEX.GETFNTITAN  -$ - - -* - - - - - - &IM.INDEX.GETFNTITAN - -$+ -@ - %IM.INDEX.GETFNTITAN  - -$ -D - - - - %IM.INDEX.GETFNTITAN -  -$ - - -/ - &IM.INDEX.GETFNTITAN -  - -$ - - -- - - - -t ->   - - - - - -C  -6 - -  - -d -%E -< -6 - $IM.INDEX.GETFNTITAN  - -$ - -% - - -8 - - -(O - - - - - - - - &IM.INDEX.GETFNTITAN -  - -$ - -% -; - - - %IM.INDEX.GETFNTITAN -  - -$ - -H - -'$ - - - $IM.INDEX.GETFNTITAN - -$ - - - - - -$ -$ -, - - - - - - - - - - - -* -# - - #IM.INDEX.GETFNTITAN -  -$ -0 - -, - -# -  - - - - - - - - - - %IM.INDEX.GETFNTITAN - -$  - - 'IM.INDEX.GETFNTITAN - - -$  - &IM.INDEX.GETFNTITAN* -  -$; - - - - - - -$ - - - - - - - - - - - - -$ - -J - - -$ -  - - -% - - - HRULE.GETFN - - - k - - - -< - ,IM.INDEX.GETFNTITAN - - - - -( ,IM.INDEX.GETFNTITAN - - - -( ,IM.INDEX.GETFNTITAN - - - - -( _ - -7 - - /IM.INDEX.GETFNTITAN - - - - - -$ - - - -Z -@$ - - - -$ -= - - - - -$ - - - - - -$ - -E - -c - - HRULE.GETFN - - ; - z - - - g - -S -9 - - - - -? - -L - - -/ - -B -> -- - 0 -  -  - S -  -  -5 -# -   - -  -{ -k - - - - -+ -. - -     -1 -" -  -  * -) - - - -) -  -  - - - -3 - - 9  - - - &  $ -D -) - -@ -9 - - -   - - - - -  -   -V - P - - - -n -, - 8  -  -. - 0 -  - - - -> - . - ? -  - -  - -  -' -  -  - - *IM.INDEX.GETFNTITAN - -$ -k - -/ -$  - & -R - ! - - - 'IM.INDEX.GETFNTITAN -$ - - - - - -3 -< - - -: - -$  -M - -$B - -d -, -$ - -($ - - -$) -\ -  S - -  -~ -  - -   > - -m -  @ - -6 - -6 - -  -3 - - - - - - - - - - -  - - - - -   - L - -v - - $  - - - - - - - - < -# - -   -  - - -| -    7 -# -~ -^ -M - -         -  - - -6 - -  - ;   - - - - - - - - - - - - - - - -  - - - - -! -  J - - -M -< -  - T - -  ? -D - -  - )  - - - - - -  J -c -6 - %  -  - - -  - - - - -  -[ - (  - - - - - - - - - - - -( -  - - - - - 0  - - - - - - - -% - - -  - -  - +&  - - - - - - - -K - - - ' -M - -  - - - - R - -  -  - - -% - -۩ z \ No newline at end of file +CLASSIC +TITAN TITAN PALATINO CLASSIC +CLASSIC +CLASSIC +TITANPALATINO TITANPALATINO CLASSIC + +TIMESROMAN +MODERN HELVETICA)(1' IM.CHAP.GETFN5 HRULE.GETFN'&%#+  +J;%:."V%$$$X%2$  H HRULE.GETFN|  M 0 _ 8 P a?: (YW DIM.INDEX.GETFN! w  F#,"#"#  +)  +5BE:  FIM.INDEX.GETFNx! !-!#  +& 5n39V_HIM.INDEX.GETFNz  V  [ !IM.INDEX.GETFN  D_H6 $IM.INDEX.GETFN  D&#y <" +JIM.INDEX.GETFN  V+!i:IM.INDEX.GETFN #IM.INDEX.GETFN  MU"$$ $ $AQ8* #IM.INDEX.GETFN   < $IM.INDEX.GETFN    %IM.INDEX.GETFN  / 5  #IM.INDEX.GETFN  #& %IM.INDEX.GETFN   L N^" = 4( *IM.INDEX.GETFN  5 c  +   + +7;  !  +B 4      + +: + HRULE.GETFN@ %IM.INDEX.GETFN   3,.#6  &IM.INDEX.GETFN  +%<: 3 2IM.INDEX.GETFN 2 7 $IM.INDEX.GETFN 4 + +  #3( GF   +vT +  "IM.INDEX.GETFN  # ( . $IM.INDEX.GETFN  ' - $IM.INDEX.GETFN  3(  + %IM.INDEX.GETFN & HRULE.GETFN{A #IM.INDEX.GETFN +VU[P, +`S *>/ $IM.INDEX.GETFN      +( #IM.INDEX.GETFNAH     $IM.INDEX.GETFN< JT5!  6 j) $IM.INDEX.GETFN5& +IM.INDEX.GETFN + S/8 t    $IM.INDEX.GETFN - !%;  &IM.INDEX.GETFNR:?b + 'IM.INDEX.GETFN+7J +W + +5?= +#  +4  HRULE.GETFNQ5o "IM.INDEX.GETFN7 +b  #IM.INDEX.GETFN6 5IM.INDEX.GETFN "IM.INDEX.GETFN    !   4  #IM.INDEX.GETFN     * +   &IM.INDEX.GETFN    +@ %IM.INDEX.GETFN  D  %IM.INDEX.GETFN  / &IM.INDEX.GETFN - +t>  + +C6d%E<6 $IM.INDEX.GETFN  %8O   &IM.INDEX.GETFN  %; %IM.INDEX.GETFN  H'  $IM.INDEX.GETFN  $, +*#  #IM.INDEX.GETFN 0,#  %IM.INDEX.GETFN   + 'IM.INDEX.GETFN   &IM.INDEX.GETFN* ; +$ +   J  +% HRULE.GETFNk< ,IM.INDEX.GETFN   ,IM.INDEX.GETFN   ,IM.INDEX.GETFN  _ 7 + /IM.INDEX.GETFN  +Z@= + + Ec HRULE.GETFN +;zg +S9?L/ B>-0  +   S  5#   +   +{k +. +  1" +  *) +) +3 +9 + +& $D)@9    +V +P +n, +8 .0 > .?  '  ,IM.INDEX.GETFN  k/ &R! 'IM.INDEX.GETFN 3< :MB d,()\ S ~ + + + > m +@663       Lv +$ <# | +  7#~^M +       6  +;      ! J +M< + +T +?D +) Jc6 +%   [ +( ( - +0 %   ++   +K 'M  + R   %DATE:i5p۷ z \ No newline at end of file diff --git a/docs/medley-irm/16-SEDIT.TEDIT b/docs/medley-irm/16-SEDIT.TEDIT index 756a5f26..ac9813fd 100644 Binary files a/docs/medley-irm/16-SEDIT.TEDIT and b/docs/medley-irm/16-SEDIT.TEDIT differ diff --git a/docs/medley-irm/23-STREAMS.TEDIT b/docs/medley-irm/23-STREAMS.TEDIT index f32d621e..67fedb22 100644 Binary files a/docs/medley-irm/23-STREAMS.TEDIT and b/docs/medley-irm/23-STREAMS.TEDIT differ diff --git a/docs/porter-irm/003-LOF.TEDIT b/docs/porter-irm/003-LOF.TEDIT new file mode 100644 index 00000000..f334c0fd Binary files /dev/null and b/docs/porter-irm/003-LOF.TEDIT differ diff --git a/docs/porter-irm/003-TOC.TEDIT b/docs/porter-irm/003-TOC.TEDIT new file mode 100644 index 00000000..4c308842 --- /dev/null +++ b/docs/porter-irm/003-TOC.TEDIT @@ -0,0 +1,13 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual TABLE OF CONTENTS 1 TABLE OF CONTENTS 1 TABLE of CONTENTS 6 Preface xvii Volume 1 - Lanuage Reference 1. Introduction 1 Operating System Conventions 1 System Components 1 NoteCards Device-Naming Conventions 2 Notation Conventions 2 Font 2 Prompts 3 Keyboard Conventions 3 2. Litatoms (Symbols) 2-1 Using Symbols as Variables 2-1 Function Definition Cells 2-3 Property Lists 2-4 Print Names 2-5 Characters and Character Codes 2-9 3. Lists 3-1 Creating Lists 3-3 Building Lists from Left to Right 3-4 Copying Lists 3-6 Extracting Tails of Lists 3-6 Counting List Cells 3-8 Logical Operations 3-9 Searching Lists 3-10 Substitution Functions 3-10 Association Lists and Property Lists 3-11 Sorting Lists 3-13 Other List Functions 3-15 4. Strings 4-1 5. Arrays 5-1 6. Hash Arrays 6-1 Hash Overflow 6-3 User-Specified Hashing Functions 6-3 7. Numbers and Arithmetic Functions 7-1 Generic Arithmetic 7-2 Integer Arithmetic 7-3 Logical Arithmetic Functions 7-6 Floating-Point Arithmetic 7-8 Other Arithmetic Functions 7-10 8. Record Package 8-1 FETCH and REPLACE 8-1 CREATE 8-2 TYPE? 8-3 WITH 8-4 Record Declarations 8-4 Record Types 8-5 Optional Record Specifications 8-10 Defining New Record Types 8-12 Record Manipulation Functions 8-12 Changetran 8-13 Built-in and User Data Types 8-15 9. Conditionals and Iterative Statements 9-1 Data Type Predicates 9-1 Equality Predicates 9-2 Logical Predicates 9-3 COND Conditional Function 9-3 The IF Statement 9-4 Selection Functions 9-5 PROG and Associated Control Functions 9-6 The Iterative Statement 9-7 I.s. Types 9-8 Iterative Variable I.s.oprs 9-9 Condition I.s.oprs 9-12 Other I.s.oprs 9-13 Miscellaneous Hints on I.s.oprs 9-13 Errors in Iterative Statements 9-15 Defining New Iterative Statement Operators 9-15 10. Function Definition, Manipulation, and Evaluation 10-1 Function Types 10-2 Lambda-Spread Functions 10-2 Nlambda-Spread Functions 10-3 Lambda-Nospread Functions 10-4 Nlambda-Nospread Functions 10-4 Compiled Functions 10-5 Function Type Functions 10-5 Defining Functions 10-7 Function Evaluation 10-1 Iterating and Mapping Functions 10-1 Function Arguments 10-1 Macros 10-1 DEFMACRO 10-15 Interpreting Macros 10-15 11. Variable Binds and the Interlisp Stack 11-1 Spaghetti Stack 11-2 Stack Functions 11-3 Searching the Stack 11-4 Variable Binds in Stack Frames 11-5 Evaluating Expressions in Stack Frames 11-6 Altering Flow of Control 11-6 Releasing and Reusing Stack Pointers 11-7 Backtrace Functions 11-8 Other Stack Functions 11-10 The Stack and the Interpreter 11-10 Generators 11-12 Coroutines 11-14 Possibilities Lists 11-15 12. Miscellaneous 12-1 Greeting and Initialization Files 12-1 Idle Mode 12-3 Saving Virtual Memory State 12-5 System Version Information 12-9 Date and Time Functions 12-11 Timers and Duration Functions 12-13 Resources 12-15 A Simple Example 12-16 Trade-offs in More Complicated Cases 12-18 Macros for Accessing Resources 12-18 Saving Resources in a File 12-19 Pattern Matching 12-19 Pattern Elements 12-20 Element Patterns 12-20 Segment Patterns 12-21 Assignments 12-23 Place-Markers 12-23 Replacements 12-24 Reconstruction 12-24 Examples 12-25 Volume 2 - Environment Reference 13. Interlisp Executive 13-1 Input Formats 13-3 Programmer's Assistant Commands 13-4 Event Specification 13-4 Commands 13-6 P.A. Commands Applied to P.A. Commands 13-15 Changing the Programmer's Assistant 13-16 Undoing 13-19 Undoing Out of Order 13-20 SAVESET 13-21 UNDONLSETQ and RESETUNDO 13-22 Format and Use of the History List 13-23 Programmer's Assistant Functions 13-26 The Editor and the Programmer's Assistant 13-32 14. Errors and Breaks 14-1 Breaks 14-1 Break Windows 14-2 Break Commands 14-3 Controlling When to Break 14-10 Break Window Variables 14-11 Creating Breaks with BREAK1 14-12 Signalling Errors 14-14 Catching Errors 14-16 Changing and Restoring System State 14-18 Error List 14-20 15. Breaking, Tracing, and Advising 15-1 Breaking Functions and Debugging 15-1 Advising 15-7 Implementation of Advising 15-7 Advise Functions 15-8 16. List Structure Editor 16-1 DEdit 16-1 Calling DEdit 16-1 Selecting Objects and Lists 16-4 Typing Characters to DEdit 16-4 Copy-Selection 16-5 DEdit Commands 16-5 Multiple Commands 16-8 DEdit Idioms 16-8 DEdit Parameters 16-9 Local Attention-Changing Commands 16-10 Commands That Search 16-14 Search Algorithm 16-15 Search Commands 16-16 Location Specification 16-18 Commands That Save and Restore the Edit Chain 16-21 Commands That Modify Structure 16-22 Implementation 16-23 The A, B, and : Commands 16-24 Form Oriented Editing and the Role of UP 16-26 Extract and Embed 16-26 The MOVE Command 16-28 Commands That Move Parentheses 16-30 TO and THRU 16-31 The R Command 16-34 Commands That Print 16-35 Commands for Leaving the Editor 16-37 Nested Calls to Editor 16-39 Manipulating the Characters of an Atom or String 16-39 Manipulating Predicates and Conditional Expressions 16-40 History Commands in the Editor 16-41 Miscellaneous Commands 16-41 Commands That Evaluate 16-43 Commands That Test 16-45 Edit Macros 16-46 Undo 16-48 EDITDEFAULT 16-50 Editor Functions 16-51 Time Stamps 16-57 17. File Package 17-1 Loading Files 17-3 Storing Files 17-8 Remaking a Symbolic File 17-12 Loading Files in a Distributed Environment 17-13 Marking Changes 17-13 Noticing Files 17-15 Distributing Change Information 17-16 File Package Types 17-16 Functions for Manipulating Typed Definitions 17-19 Defining New File Package Types 17-23 File Package Commands 17-25 Functions and Macros 17-26 Variables 17-27 Litatom Properties 17-29 Miscellaneous File Package Commands 17-30 DECLARE: 17-31 Exporting Definitions 17-33 FileVars 17-34 Defining New File Package Commands 17-35 Functions for Manipulating File Command Lists 17-37 Symbolic File Format 17-38 Copyright Notices 17-40 Functions Used Within Source Files 17-42 File Maps 17-42 18. Compiler 18-1 Compiler Printout 18-2 Global Variables 18-3 Local Variables and Special Variables 18-4 Constants 18-5 Compiling Function Calls 18-6 FUNCTION and Functional Arguments 18-7 Open Functions 18-8 COMPILETYPELST 18-8 Compiling CLISP 18-9 Compiler Functions 18-9 Block Compiling 18-12 Block Declarations 18-13 Block Compiling Functions 18-15 Compiler Error Messages 18-16 19. Masterscope 19-1 Command Languages 19-3 Commands 19-3 Relations 19-6 Set Specifications 19-8 Set Determiners 19-10 Set Types 19-10 Conjunctions of Sets 19-10 SHOW PATHS 19-11 Error Messages 19-13 Macro Expansion 19-13 Affecting Masterscope Analysis 19-13 Database Updating 19-16 Masterscope Entries 19-16 Noticing Changes that Require Recompiling 19-18 Implementation Notes 19-19 20. DWIM 20-1 Spelling Correction Protocol 20-3 Parentheses Errors Protocol 20-4 Undefined Function T Errors 20-4 DWIM Operation 20-5 DWIM Correction: Unbound Atoms 20-6 Undefined CAR of Form 20-7 Undefined Function in APPLY 20-8 DWIMUSERFORMS 20-8 DWIM Functions and Variables 20-10 Spelling Correction 20-11 Synonyms 20-12 Spelling Lists 20-12 Generators for Spelling Correction 20-14 Spelling Corrector Algorithm 20-14 Spelling Corrector Functions and Variables 20-15 21. CLISP 21-1 CLISP Interaction with User 21-4 CLISP Character Operators 21-5 Declarations 21-9 CLISP Operation 21-10 CLISP Translations 21-12 DWIMIFY 21-13 CLISPIFY 21-16 Miscellaneous Functions and Variables 21-18 CLISP Internal Conventions 21-20 22. Performance Issues 22-1 Storage Allocation and Garbage Collection 22-1 Variable Bindings 22-4 Performance Measuring 22-5 BREAKDOWN 22-7 GAINSPACE 22-9 Using Data Types Instead of Records 22-9 Using Incomplete File Names 22-10 Using "Fast" and "Destructive" Functions 22-10 23. Processes 23-1 Creating and Destroying Processes 23-1 Process Control Constructs 23-4 Events 23-5 Monitors 23-7 Global Resources 23-8 Typein and the TTY Process 23-9 Switing the TTY Process 23-9 Handling of Interrupts 23-11 Keeping the Mouse Alive 23-12 Process Status Window 23-12 Non-Process Compatibility 23-14 Volume 3 - I/O Reference 24. Streams and Files 24-1 Opening and Closing File Streams 24-1 File Names 24-4 Incomplete File Names 24-7 Version Recognition 24-9 Using File Names Instead of Streams 24-10 File Name Efficiency Considerations 24-11 Obsolete File Opening Functions 24-11 Converting Old Programs 24-11 Using Files with Processes 24-12 File Attributes 24-12 Closing and Reopening Files 24-15 Local Hard Disk Device 24-16 Floppy Disk Device 24-18 I/O Operations To and From Strings 24-22 Temporary Files and the CORE Device 24-23 NULL Device 24-24 Deleting, Copying, and Renaming Files 24-24 Searching File Directories 24-24 Listing File Directories 24-25 File Servers 24-28 PUP File Server Protocols 24-28 Xerox NS File Server Protocols 24-28 Operating System Designations 24-29 Logging In 24-30 Abnormal Conditions 24-31 25. Input/Output Functions 25-1 Specifying Streams for Input/Output Functions 25-1 Input Functions 25-2 Output Functions 25-6 PRINTLEVEL 25-8 Printing Numbers 25-10 User Defined Printing 25-12 Printing Unusual Data Structures 25-13 Random Access File Operations 25-14 Input/Output Operations with Characters and Bytes 25-17 PRINTOUT 25-17 Horizontal Spacing Commands 25-19 Vertical Spacing Commands 25-20 Special Formatting Controls 25-20 Printing Specifications 25-20 Paragraph Format 25-21 Right-Flushing 25-21 Centering 25-22 Numbering 25-22 Escaping to Lisp 25-23 User-Defined Commands 25-23 Special Printing Functions 25-24 READFILE and WRITEFILE 25-25 Read Tables 25-25 Read Table Functions 25-26 Syntax Classes 25-26 Read Macros 25-29 26. User Input/Output Packages 26-1 Inspector 26-1 Calling the Inspector 26-1 Multiple Ways of Inspecting 26-2 Inspect Windows 26-3 Inspect Window Commands 26-3 Interaction with Break Windows 26-4 Controlling the Amount Displayed During Inspection 26-4 Inspect Macros 26-4 INSPECTWs 26-5 PROMPTFORWORD 26-7 ASKUSER 26-9 Format of KEYLST 26-10 Options 26-12 Operation 26-13 Completing a Key 26-14 Special Keys 26-15 Startup Protocol and Typeahead 26-16 TTYIN Typein Editor 26-17 Entering Input with TTYIN 26-17 Mouse Commands (Interlisp-D Only) 26-19 Display Editing Commands 26-19 Using TTYIN for Lisp Input 26-22 Useful Macros 26-23 Programming with TTYIN 26-23 Using TTYIN as a General Editor 26-25 ?= Handler 26-26 Read Macros 26-27 Assorted Flags 26-28 Special Responses 26-29 Display Types 26-30 Prettyprint 26-31 Comment Feature 26-33 Comment Pointers 26-34 Converting Comments to Lowercase 26-35 Special Prettyprint Controls 26-36 27. Graphics Output Operations 27-1 Primitive Graphics Concepts 27-1 Positions 27-1 Regions 27-1 Bitmaps 27-2 Textures 27-5 Opening Image Streams 27-6 Accessing Image Stream Fields 27-8 Current Position of an Image Stream 27-10 Moving Bits Between Bitmaps with BITBLT 27-11 Drawing Lines 27-13 Drawing Curves 27-14 Miscellaneous Drawing and Printing Operations 27-15 Drawing and Shading Grids 27-17 Display Streams 27-18 Fonts 27-19 Font Files and Font Directories 27-24 Font Profiles 27-24 Image Objects 27-27 IMAGEFNS Methods 27-28 Registering Image Objects 27-30 Reading and Writing Image Objects on Files 27-31 Copying Image Objects Between Windows 27-31 Implementation of Image Streams 27-32 28. Windows and Menus 28-1 Using the Window System 28-1 Changing the Window System 28-6 Interactive Display Functions 28-7 Windows 28-9 Window Properties 28-10 Creating Windows 28-10 Opening and Closing Windows 28-11 Redisplaying Windows 28-12 Reshaping Windows 28-13 Moving Windows 28-14 Exposing and Burying Windows 28-16 Shrinking Windows into Icons 28-16 Coordinate Systems, Extents, and Scrolling 28-18 Mouse Activity in Windows 28-21 Terminal I/O and Page Holding 28-22 TTY Process and the Caret 28-23 Miscellaneous Window Functions 28-24 Miscellaneous Window Properties 28-25 Example: A Scrollable Window 28-26 Menus 28-28 Menu Fields 28-29 Miscellaneous Menu Functions 28-32 Examples of Menu Use 28-32 Attached Windows 28-34 Attaching Menus to Windows 28-37 Attached Prompt Windows 28-38 Window Operations and Attached Windows 28-39 Window Properties of Attached Windows 28-41 29. Hardcopy Facilities 29-1 Hardcopy Functions 29-1 Low-Level Hardcopy Variables 29-4 30. Terminal Input/Output 30-1 Interrupt Characters 30-1 Terminal Tables 30-4 Terminal Syntax Classes 30-4 Terminal Control Functions 30-5 Line-Buffering 30-7 Dribble Files 30-10 Cursor and Mouse 30-10 Changing the Cursor Image 30-11 Flashing Bars on the Cursor 30-13 Cursor Position 30-13 Mouse Button Testing 30-14 Low-Level Mouse Functions 30-15 Keyboard Interpretation 30-15 Display Screen 30-18 Miscellaneous Terminal I/O 30-19 31. Ethernet 31-1 Ethernet Protocols 31-1 Protocol Layering 31-1 Level Zero Protocols 31-2 Level One Protocols 31-2 Higher Level Protocols 31-3 Connecting Networks: Routers and Gateways 31-3 Addressing Conflicts with Level Zero Mediums 31-3 References 31-4 Higher-Level PUP Protocol Functions 31-4 Higher-Level NS Protocol Functions 31-5 Name and Address Conventions 31-5 Clearinghouse Functions 31-7 NS Printing 31-9 SPP Stream Interface 31-9 Courier Remote Procedure Call Protocol 31-11 Defining Courier Programs 31-11 Courier Type Definitions 31-12 Pre-defined Types 31-13 Constructed Types 31-13 User Extensions to the Type Language 31-15 Performing Courier Transactions 31-16 Expedited Procedure Call 31-17 Expanding Ring Broadcast 31-18 Using Bulk Data Transfer 31-18 Courier Subfunctions for Data Transfer 31-19 Level One Ether Packet Format 31-20 PUP Level One Functions 31-21 Creating and Managing Pups 31-21 Sockets 31-22 Sending and Receiving Pups 31-23 Pup Routing Information 31-23 Miscellaneous PUP Utilities 31-24 PUP Debugging Aids 31-24 NS Level One Functions 31-28 Creating and Managing XIPs 31-28 NS Sockets 31-28 Sending and Receiving XIPs 31-29 NS Debugging Aids 31-29 Support for Other Level One Protocols 31-29 The SYSQUEUE Mechanism 31-31 Glossary GLOSSARY-1 Index INDEX-1 [This page intentionally left blank](LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (LOWERROMAN "" "") STARTINGPAGE# 3) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY MODERN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (LOWERROMAN "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 723) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (LOWERROMAN "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY MODERN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (LOWERROMAN "" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (LOWERROMAN "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY MODERN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (LOWERROMAN "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))50008000H,3 T222 +;000H`2 ,"-TF PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR HELVETICA HELVETICA HELVETICA +MODERN MODERNMODERN + HRULE.GETFNMODERN + HRULE.GETFNMODERN +   HRULE.GETFNMODERN +   HRULE.GETFNMODERN + HRULE.GETFNMODERN   + &   # &*  +% $!     +%#" )*!&%16 !%  +%-+$ '! $,&" +! %.* )'0   "* $&!  "!(4% 0&&7:%  1&4'+*4* +'! %0 "!!&"#*$2 +!,! /)"/ '     &*+'!")*,!!&% 3($8#!#" "%9 &!) "'($ ##*.4  &!2-& # #$$2!%!&'$ $".-"!!#!! 03)(#."!-(!!!/$""#"",   $7"z \ No newline at end of file diff --git a/docs/porter-irm/04-STRINGS.TEDIT b/docs/porter-irm/04-STRINGS.TEDIT new file mode 100644 index 00000000..ed4c61c7 --- /dev/null +++ b/docs/porter-irm/04-STRINGS.TEDIT @@ -0,0 +1,51 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 4. STRINGS 1 4. STRINGS 1 4. STRINGS 6 A string is an object which represents a sequence of characters. Interlisp provides functions for creating strings, concatenating strings, and creating sub-strings of a string. The input syntax for a string is a double quote ("), followed by a sequence of any characters except double quote and %, terminated by a double quote. The % and double quote characters may be included in a string by preceding them with the character %. Strings are printed by PRINT and PRIN2 with initial and final double quotes, and %s inserted where necessary for it to read back in properly. Strings are printed by PRIN1 without the delimiting double quotes and extra %s. A "null string" containing no characters is input as "". The null string is printed by PRINT and PRIN2 as "". (PRIN1 "") doesn't print anything. Internally a string is stored in two parts; a "string pointer" and the sequence of characters. Several string pointers may reference the same character sequence, so a substring can be made by creating a new string pointer, without copying any characters. Functions that refer to "strings" actually manipulate string pointers. Some functions take an "old string" argument, and re-use the string pointer. (STRINGP X) [Function] Returns X if X is a string, NIL otherwise. (STREQUAL X Y) [Function] Returns T if X and Y are both strings and they contain the same sequence of characters, otherwise NIL. EQUAL uses STREQUAL. Note that strings may be STREQUAL without being EQ. For instance, (STREQUAL "ABC" "ABC") => T (EQ "ABC" "ABC") => NIL STREQUAL returns T if X and Y are the same string pointer, or two different string pointers which point to the same character sequence, or two string pointers which point to different character sequences which contain the same characters. Only in the first case would X and Y be EQ. (STRING-EQUAL X Y) [Function] Returns T if X and Y are either strings or litatoms, and they contain the same sequence of characters, ignoring case. For instance, (STRING-EQUAL "FOO" "Foo") => T (STRING-EQUAL "FOO" 'Foo) => T This is useful for comparing things that might want to be considered "equal" even though they're not both litatoms in a consistent case, such as file names and user names. (ALLOCSTRING N INITCHAR OLD FATFLG) [Function] Creates a string of length N characters of INITCHAR (which can be either a character code or something coercible to a character). If INITCHAR is NIL, it defaults to character code 0. if OLD is supplied, it must be a string pointer, which is modified and returned. If FATFLG is non-NIL, the string is allocated using full 16-bit NS characters (see Chapter 2) instead of 8-bit characters. This can speed up some string operations if NS characters are later inserted into the string. This has no other effect on the operation of the string functions. (MKSTRING X FLG RDTBL) [Function] If X is a string, returns X. Otherwise, creates and returns a string containing the print name of X. Examples: (MKSTRING "ABC") => "ABC" (MKSTRING '(A B C)) => "(A B C)" (MKSTRING NIL) => "NIL" Note that the last example returns the string "NIL", not the atom NIL. If FLG is T, then the PRIN2-name of X is used, computed with respect to the readtable RDTBL. For example, (MKSTRING "ABC" T) => "%"ABC%"" (NCHARS X FLG RDTBL) [Function] Returns the number of characters in the print name of X. If FLG=T, the PRIN2-name is used. For example, (NCHARS 'ABC) => 3 (NCHARS "ABC" T) => 5 Note: NCHARS works most efficiently on litatoms and strings, but can be given any object. (SUBSTRING(SUBSTRING (Function) NIL NIL NIL 2) X N M OLDPTR) [Function] Returns the substring of X consisting of the Nth through Mth characters of X. If M is NIL, the substring contains the Nth character thru the end of X. N and M can be negative numbers, which are interpreted as counts back from the end of the string, as with NTHCHAR (Chapter 2). SUBSTRING returns NIL if the substring is not well defined, e.g., N or M specify character positions outside of X, or N corresponds to a character in X to the right of the character indicated by M). Examples: (SUBSTRING "ABCDEFG" 4 6) => "DEF" (SUBSTRING "ABCDEFG" 3 3) => "C" (SUBSTRING "ABCDEFG" 3 NIL) => "CDEFG" (SUBSTRING "ABCDEFG" 4 -2) => "DEF" (SUBSTRING "ABCDEFG" 6 4) => NIL (SUBSTRING "ABCDEFG" 4 9) => NIL If X is not a string, it is converted to one. For example, (SUBSTRING '(A B C) 4 6) => "B C" SUBSTRING does not actually copy any characters, but simply creates a new string pointer to the characters in X. If OLDPTR is a string pointer, it is modified and returned. (GNC(GNC (Function) NIL NIL NIL 2) X) [Function] "Get Next Character." Returns the next character of the string X (as an atom); also removes the character from the string, by changing the string pointer. Returns NIL if X is the null string. If X isn't a string, a string is made. Used for sequential access to characters of a string. Example: (SETQ FOO "ABCDEFG") "ABCDEFG" (GNC FOO) A (GNC FOO) B FOO "CDEFG" Note that if A is a substring of B, (GNC A) does not remove the character from B. (GLC(GLC (Function) NIL NIL NIL 3) X) [Function] "Get Last Character." Returns the last character of the string X (as an atom); also removes the character from the string. Similar to GNC. Example: (SETQ FOO "ABCDEFG") "ABCDEFG" (GLC FOO) G (GLC FOO) F FOO "ABCDE" (CONCAT(CONCAT (Function) NIL NIL NIL 3) X1 X2 ... XN) [NoSpread Function] Returns a new string which is the concatenation of (copies of) its arguments. Any arguments which are not strings are transformed to strings. Examples: (CONCAT "ABC" 'DEF "GHI") => "ABCDEFGHI" (CONCAT '(A B C) "ABC") => "(A B C)ABC" (CONCAT) returns the null string, "" (CONCATLIST(CONCATLIST (Function) NIL NIL NIL 3) L) [Function] L is a list of strings and/or other objects. The objects are transformed to strings if they aren't strings. Returns a new string which is the concatenation of the strings. Example: (CONCATLIST '(A B (C D) "EF")) => "AB(C D)EF" (RPLSTRING(RPLSTRING (Function) NIL NIL NIL 3) X N Y) [Function] Replaces the characters of string X beginning at character position N with string Y. X and Y are converted to strings if they aren't already. N may be positive or negative, as with SUBSTRING. Characters are smashed into (converted) X. Returns the string X. Examples: (RPLSTRING "ABCDEF" -3 "END") => "ABCEND" (RPLSTRING "ABCDEFGHIJK" 4 '(A B C)) => "ABC(A B C)K" Generates an error if there is not enough room in X for Y, i.e., the new string would be longer than the original. If Y was not a string, X will already have been modified since RPLSTRING does not know whether Y will "fit" without actually attempting the transfer. Warning: In some implementations of Interlisp, if X is a substring of Z, Z will also be modified by the action of RPLSTRING or RPLCHARCODE. However, this is not guaranteed to be true in all cases, so programmers should not rely on RPLSTRING or RPLCHARCODE altering the characters of any string other than the one directly passed as argument to those functions. (RPLCHARCODE(RPLCHARCODE (Function) NIL NIL NIL 4) X N CHAR) [Function] Replaces the Nth character of the string X with the character code CHAR. N may be positive or negative. Returns the new X. Similar to RPLSTRING. Example: (RPLCHARCODE "ABCDE" 3 (CHARCODE F)) => "ABFDE" (STRPOS(STRPOS (Function) NIL NIL NIL 4) PAT STRING START SKIP ANCHOR TAIL CASEARRAY BACKWARDSFLG) [Function] STRPOS is a function for searching one string looking for another. PAT and STRING are both strings (or else they are converted automatically). STRPOS searches STRING beginning at character number START, (or 1 if START is NIL) and looks for a sequence of characters equal to PAT. If a match is found, the character position of the first matching character in STRING is returned, otherwise NIL. Examples: (STRPOS "ABC" "XYZABCDEF") => 4 (STRPOS "ABC" "XYZABCDEF" 5) => NIL (STRPOS "ABC" "XYZABCDEFABC" 5) => 10 SKIP can be used to specify a character in PAT that matches any character in STRING. Examples: (STRPOS "A&C&" "XYZABCDEF" NIL '&) => 4 (STRPOS "DEF&" "XYZABCDEF" NIL '&) => NIL If ANCHOR is T, STRPOS compares PAT with the characters beginning at position START (or 1 if START is NIL). If that comparison fails, STRPOS returns NIL without searching any further down STRING. Thus it can be used to compare one string with some portion of another string. Examples: (STRPOS "ABC" "XYZABCDEF" NIL NIL T) => NIL (STRPOS "ABC" "XYZABCDEF" 4 NIL T) => 4 If TAIL is T, the value returned by STRPOS if successful is not the starting position of the sequence of characters corresponding to PAT, but the position of the first character after that, i.e., the starting position plus (NCHARS PAT). Examples: (STRPOS "ABC" "XYZABCDEFABC" NIL NIL NIL T) => 7 (STRPOS "A" "A" NIL NIL NIL T) => 2 If TAIL=NIL, STRPOS returns NIL, or a character position within STRING which can be passed to SUBSTRING. In particular, (STRPOS "" "") => NIL. However, if TAIL=T, STRPOS may return a character position outside of STRING. For instance, note that the second example above returns 2, even though "A" has only one character. If CASEARRAY is non-NIL, this should be a casearray like that given to FILEPOS (Chapter 25). The casearray is used to map the string characters before comparing them to the search string. If BACKWARDSFLG is non-NIL, the search is done backwards from the end of the string. (STRPOSL(STRPOSL (Function) NIL NIL NIL 4) A STRING START NEG BACKWARDSFLG) [Function] STRING is a string (or else it is converted automatically to a string), A is a list of characters or character codes. STRPOSL searches STRING beginning at character number START (or else 1 if START=NIL) for one of the characters in A. If one is found, STRPOSL returns as its value the corresponding character position, otherwise NIL. Example: (STRPOSL '(A B C) "XYZBCD") => 4 If NEG=T, STRPOSL searches for a character not on A. Example: (STRPOSL '(A B C) "ABCDEF" NIL T) => 4 If any element of A is a number, it is assumed to be a character code. Otherwise, it is converted to a character code via CHCON1. Therefore, it is more efficient to call STRPOSL with A a list of character codes. If A is a bit table, it is used to specify the characters (see MAKEBITTABLE below) If BACKWARDSFLG is non-NIL, the search is done backwards from the end of the string. STRPOSL uses a "bit table" data structure to search efficiently. If A is not a bit table, it is converted to a bit table using MAKEBITTABLE. If STRPOSL is to be called frequently with the same list of characters, a considerable savings can be achieved by converting the list to a bit table once, and then passing the bit table to STRPOSL as its first argument. (MAKEBITTABLE(MAKEBITTABLE (Function) NIL NIL NIL 5) L NEG A) [Function] Returns a bit table suitable for use by STRPOSL. L is a list of characters or character codes, NEG is the same as described for STRPOSL. If A is a bit table, MAKEBITTABLE modifies and returns it. Otherwise, it will create a new bit table. Note: If NEG=T, STRPOSL must call MAKEBITTABLE whether A is a list or a bit table. To obtain bit table efficiency with NEG=T, MAKEBITTABLE should be called with NEG=T, and the resulting "inverted" bit table should be given to STRPOSL with NEG=NIL. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))2l3H` +T-T222l,,,,3H` +T,ll3H +T,HH,HH,2F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR2 CLASSIC +CLASSIC +CLASSIC +TITAN + HELVETICA +CLASSIC +MODERN +MODERNMODERN + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC +  HRULE.GETFNCLASSIC +  HRULE.GETFNCLASSIC +  HRULE.GETFNMODERN + 1D%^+T05!   +      N     + q"!    S'K  +    H  +# .   1"    6 +N +$IM.INDEX.GETFN  +   c  -(,%#)&## 8$ e3 IM.INDEX.GETFN +  @dd +  +   +    $ IM.INDEX.GETFN  @G  + +  +   +    !IM.INDEX.GETFN   ++*    %IM.INDEX.GETFN   +0 +$IM.INDEX.GETFN   "! 3& +  +,8 2>' 6 3(  ^  j &IM.INDEX.GETFN  /   +2 !IM.INDEX.GETFNMODERN +8  >? + 2R  +"&( '  +*,  ++ + $7 +.* [W  +3&  ! ,K  0o  3  "IM.INDEX.GETFNMODERN +  B. +F  +#   +) h+ ;   ; >: $ 'IM.INDEX.GETFNMODERN +  (- F +    3 <-z \ No newline at end of file diff --git a/docs/porter-irm/05-ARRAY.TEDIT b/docs/porter-irm/05-ARRAY.TEDIT new file mode 100644 index 00000000..94827af7 Binary files /dev/null and b/docs/porter-irm/05-ARRAY.TEDIT differ diff --git a/docs/porter-irm/06-HASHARRAYS.TEDIT b/docs/porter-irm/06-HASHARRAYS.TEDIT new file mode 100644 index 00000000..9902f12f --- /dev/null +++ b/docs/porter-irm/06-HASHARRAYS.TEDIT @@ -0,0 +1,52 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 6. HASH ARRAYS 1 6. HASH ARRAYS 1 6. HASH ARRAYS 6 Hash arrays provide a mechanism for associating arbitrary lisp objects ("hash keys") with other objects ("hash values"), such that the hash value associated with a particular hash key can be quickly obtained. A set of associations could be represented as a list or array of pairs, but these schemes are very inefficient when the number of associations is large. There are functions for creating hash arrays, putting a hash key/value pair in a hash array, and quickly retrieving the hash value associated with a given hash key. By default, the hash array functions use EQ for comparing hash keys. This means that if non-atoms are used as hash keys, the exact same object (not a copy) must be used to retrieve the hash value. However, you can override this default for any hash array by specifying the functions used to compare hash keys and to "hash" a hash key to a number. This can be used, for example, to create hash arrays where EQUAL but non-EQ strings will hash to the same value. Specifying alternative hashing algorithms is described below. In the description of the functions below, the argument HARRAY should be a value of the function HASHARRAY, which is used to create hash arrays. For convenience in interactive program development, it may also be NIL, in which case a hash array (SYSHASHARRAY) provided by the system is used; you must watch out for confusions if this form is used to associate more than one kind of value with the same key. Note: For backwards compatibility, the hash array functions will accept a list whose CAR is a hash array, and whose CDR is the "overflow method" for the hash array (see below). However, hash array functions are guaranteed to perform with maximum efficiency only if a direct value of HASHARRAY is given. (HASHARRAY(HASHARRAY (Function) NIL NIL NIL 1) MINKEYS OVERFLOW HASHBITSFN EQUIVFN) [Function] Creates a hash array containing at least MINKEYS hash keys, with overflow method OVERFLOW. See discussion of overflow behavior below. If HASHBITSFN and EQUIVFN are non-NIL, they specify the hashing function and comparison function used to interpret hash keys. This is described in the section on user-specified hashing functions below. If HASHBITSFN and EQUIVFN are NIL, the default is to hash EQ hash keys to the same value. (HARRAY(HARRAY (Function) NIL NIL NIL 1) MINKEYS) [Function] Provided for backward compatibility, this is equivalent to (HASHARRAY MINKEYS 'ERROR). (HARRAYP(HARRAYP (Function) NIL NIL NIL 1) X) [Function] Returns X if it is a hash array object; otherwise NIL. HARRAYP returns NIL if X is a list whose CAR is an HARRAYP, even though this is accepted by the hash array functions. (HARRAYPROP(HARRAYPROP (Function) NIL NIL NIL 1) HARRAY PROP NEWVALUE) [NoSpread Function] Returns the property PROP of HARRAY; PROP can have the system-defined values SIZE (returns the maximum occupancy of HARRAY), NUMKEYS (number of occupied slots), OVERFLOW (overflow method), HASHBITSFN (hashing function) and EQUIVFN (comparison function). Except for SIZE and NUMKEYS, a new value may be specified as NEWVALUE. By using other values for PROP, the user may also set and get arbitrary property values, to associate additional information with a hash array. Note: The HASHBITSFN or EQUIVFN properties can only be changed if the hash array is empty. (HARRAYSIZE(HARRAYSIZE (Function) NIL NIL NIL 2) HARRAY) [Function] Equivalent to (HARRAYPROP HARRAY 'SIZE); returns the number of slots in HARRAY. (CLRHASH(CLRHASH (Function) NIL NIL NIL 2) HARRAY) [Function] Clears all hash keys/values from HARRAY. Returns HARRAY. (PUTHASH(PUTHASH (Function) NIL NIL NIL 2) KEY VAL HARRAY) [Function] Associates the hash value VAL with the hash key KEY in HARRAY. Replaces the previous hash value, if any. If VAL is NIL, any old association is removed (hence a hash value of NIL is not allowed). If HARRAY is full when PUTHASH is called with a key not already in the hash array, the function HASHOVERFLOW is called, and the PUTHASH is applied to the value returned (see below). Returns VAL. ((GETHASH (Function) NIL NIL NIL 2)GETHASH KEY HARRAY) [Function] Returns the hash value associated with the hash key KEY in HARRAY. Returns NIL, if KEY is not found. (REHASH(REHASH (Function) NIL NIL NIL 2) OLDHARRAY NEWHARRAY) [Function] Hashes all hash keys and values in OLDHARRAY into NEWHARRAY. The two hash arrays do not have to be (and usually aren't) the same size. Returns NEWHARRAY. (MAPHASH(MAPHASH (Function) NIL NIL NIL 2) HARRAY MAPHFN) [Function] MAPHFN is a function of two arguments. For each hash key in HARRAY, MAPHFN will be applied to the hash value, and the hash key. For example: [MAPHASH A (FUNCTION (LAMBDA (VAL KEY) (if (LISTP KEY) then (PRINT VAL)] will print the hash value for all hash keys that are lists. MAPHASH returns HARRAY. (DMPHASH HARRAY1 HARRAY2 ... HARRAYN) [NLambda NoSpread Function] Prints on the primary output file LOADable forms which will restore the hash-arrays contained as the values of the atoms HARRAY1, HARRAY2, ... HARRAYN. Example: (DMPHASH SYSHASHARRAY) will dump the system hash-array. Note: All EQ identities except atoms and small integers are lost by dumping and loading because READ will create new structure for each item. Thus if two lists contain an EQ substructure, when they are dumped and loaded back in, the corresponding substructures while EQUAL are no longer EQ. The HORRIBLEVARS file package command (Chapter 17) provides a way of dumping hash tables such that these identities are preserved. Hash Overflow 1 When a hash array becomes full, attempting to add another hash key will cause the function HASHOVERFLOW to be called. This will either automatically enlarge the hash array, or cause the error HASH TABLE FULL. How hash overflow is handled is determined by the value of the OVERFLOW property of the hash array (which can be accessed by HARRAYPROP). The possibilities for the overflow method are: the litatom ERROR The error HASH ARRAY FULL is generated when the hash array overflows. This is the default overflow behavior for hash arrays returned by HARRAY. NIL The array is automatically enlarged by 1.5. This is the default overflow behavior for hash arrays returned by HASHARRAY. a positive integer N The array is enlarged to include N more slots than it currently has. a floating point number F The array is changed to include F times the number of current slots. a function or lambda expression FN Upon hash overflow, FN is called with the hash array as its argument. If FN returns a number, that will become the size of the array. Otherwise, the new size defaults to 1.5 times its previous size. FN could be used to print a message, or perform some monitor function. Note: For backwards compatibility, the hash array functions accept a list whose CAR is the hash array, and whose CDR is the overflow method. In this case, the overflow method specified in the list overrides the overflow method set in the hash array. Note that hash array functions are guaranteed to perform with maximum efficiency only if a direct value of HASHARRAY is given. User-Specified Hashing Functions 1 In general terms, when a key is looked up in a hash array, it is converted to an integer, which is used to index into a linear array. If the key is not the same as the one found at that index, other indices are tried until it the desired key is found. The value stored with that key is then returned (from GETHASH) or replaced (from PUTHASH). The important features of this algorithm, for purposes of customizing hash arrays, are the "hashing function" used to convert a key to an integer; and the comparison function used to compare the key found in the array with the key being looked up. In order for hash arrays to work correctly, any two objects which are equal according to the comparison function must "hash" to equal integers. By default, the Interlisp hash array functions use a hashing function that computes an integer from the internal address of a key, and use EQ for comparing keys. This means that if non-atoms are used as hash keys, the exact same object (not a copy) must be used to retrieve the hash value. There are some applications for which the EQ constraint is too restrictive. For example, it may be useful to use strings as hash keys, without the restriction that EQUAL but not EQ strings are considered to be different hash keys. The user can override this default behavior for any hash array by specifying the functions used to compare keys and to "hash" a key to a number. This can be done by giving the HASHBITSFN and EQUIVFN arguments to HASHARRAY (see above). The EQUIVFN argument is a function of two arguments that returns non-NIL when its arguments are considered equal. The HASHBITSFN argument is a function of one argument that produces a positive small integer (in the range [0..2^16-1]) with the property that objects that are considered equal by the EQUIVFN produce the same hash bits. For an existing hash array, the function HARRAYPROP (see above) can be used to examine the hashing and equivalence functions as the HASHBITSFN and EQUIVFN hash array properties. These properties are read-only for non-empty hash arrays, as it makes no sense to change the equivalence relationship once some keys have been hashed. The following function is useful for creating hash arrays that take strings as hash keys: (STRINGHASHBITS(STRINGHASHBITS (Function) NIL NIL NIL 4) STRING) [Function] Hashes the string STRING into an integer that can be used as a HASHBITSFN for a hash array. Strings which are STREQUAL hash to the same integer. Example: (HASHARRAY MINKEYS OVERFLOW 'STRINGHASHBITS 'STREQUAL) creates a hash array where you can use strings as hash keys. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))))),ll2l52Hll-T3(T2l,HH +,ll3HH +T,HH,HH,2F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR CLASSIC + HELVETICATITAN +TITAN +CLASSIC +CLASSIC + HELVETICA +CLASSIC +MODERN +MODERN +MODERNMODERN + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC +    HRULE.GETFNMODERN +   )n e 8# k V   + +$IM.INDEX.GETFNMODERN +#  )!-  +  + +!IM.INDEX.GETFNMODERN +  ; +"IM.INDEX.GETFNMODERN +  )  < + %IM.INDEX.GETFNMODERN +  $# +$" r + +< + %IM.INDEX.GETFN  ! +"IM.INDEX.GETFN  !  +"IM.INDEX.GETFN  18B 8 +"IM.INDEX.GETFN +  4 +!IM.INDEX.GETFN  #  V  +"IM.INDEX.GETFN   7D ) =  +   "S " +TH^ s HRULE.GETFNMODERN + [ ZB6 +3  p  n "#!$!4~ER  ! HRULE.GETFNMODERN + 4   *y 3  +  :/ + ) +Q + Z +)IM.INDEX.GETFNMODERN + +   ' +&   = &z \ No newline at end of file diff --git a/docs/porter-irm/07-NUMBERS.TEDIT b/docs/porter-irm/07-NUMBERS.TEDIT new file mode 100644 index 00000000..2665bffa --- /dev/null +++ b/docs/porter-irm/07-NUMBERS.TEDIT @@ -0,0 +1,66 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 7. NUMBERS AND ARITHMETIC FUNCTIONS 1 7. NUMBERS AND ARITHMETIC FUNCTIONS 1 7. NUMBERS AND ARITHMETIC FUNCTIONS 6 There are four different types of numbers in Interlisp: small integers, large integers, bignums (arbitrary-size integers), and floating-point numbers. Small integers are those integers that can be directly stored within a pointer value (implementation-dependent). Large integers and floating-point numbers are full-word quantities that are stored by "boxing" the number (see below). Bignums are "boxed" as a series of words. Large integers and floating-point numbers can be any full word quantity. In order to distinguish between those full word quantities that represent large integers or floating-point numbers, and other Interlisp pointers, these numbers are "boxed": When a large integer or floating-point number is created (via an arithmetic operation or by READ), Interlisp gets a new word from "number storage" and puts the large integer or floating-point number into that word. Interlisp then passes around the pointer to that word, i.e., the "boxed number", rather than the actual quantity itself. Then when a numeric function needs the actual numeric quantity, it performs the extra level of addressing to obtain the "value" of the number. This latter process is called "unboxing". Note that unboxing does not use any storage, but that each boxing operation uses one new word of number storage. Thus, if a computation creates many large integers or floating-point numbers, i.e., does lots of boxes, it may cause a garbage collection of large integer space, or of floating-point number space. The following functions can be used to distinguish the different types of numbers: (SMALLP(SMALLP (Function) NIL NIL NIL 1) X) [Function] Returns X, if X is a small integer; NIL otherwise. Does not generate an error if X is not a number. (FIXP(FIXP (Function) NIL NIL NIL 1) X) [Function] Returns X, if X is an integer; NIL otherwise. Note that FIXP is true for small integers, large integers, and bignums. Does not generate an error if X is not a number. (FLOATP(FLOATP (Function) NIL NIL NIL 1) X) [Function] Returns X if X is a floating-point number; NIL otherwise. Does not give an error if X is not a number. (NUMBERP(NUMBERP (Function) NIL NIL NIL 1) X) [Function] Returns X, if X is a number of any type (FIXP or FLOATP); NIL otherwise. Does not generate an error if X is not a number. Note that if (NUMBERP X) is true, then either (FIXP X) or (FLOATP X) is true. Each small integer has a unique representation, so EQ may be used to check equality. EQ should not be used for large integers, bignums, or floating-point numbers, EQP, IEQP, or EQUAL must be used instead. (EQP(EQP (Function) NIL NIL NIL 2) X Y) [Function] Returns T, if X and Y are EQ, or equal numbers; NIL otherwise. EQ may be used if X and Y are known to be small integers. EQP does not convert X and Y to integers, e.g., (EQP 2000 2000.3) => NIL, but it can be used to compare an integer and a floating-point number, e.g., (EQP 2000 2000.0) => T. EQP does not generate an error if X or Y are not numbers. EQP can also be used to compare stack pointers (see Chapter 11) and compiled code objects (see Chapter 10). The action taken on division by zero and floating-point overflow is determined with the following function: (OVERFLOW(OVERFLOW (Function) NIL NIL NIL 2) FLG) [Function] Sets a flag that determines the system response to arithmetic overflow (for floating-point arithmetic) and division by zero; returns the previous setting. For integer arithmetic: If FLG=T, an error occurs on division by zero. If FLG=NIL or 0, integer division by zero returns zero. Integer overflow cannot occur, because small integers are converted to bignums (see the beginning of this chapter). For floating-point arithmetic: If FLG=T, an error occurs on floating overflow or floating division by zero. If FLG=NIL or 0, the largest (or smallest) floating-point number is returned as the result of the overflowed computation or floating division by zero. The default value for OVERFLOW is T, meaning to cause an error on division by zero or floating overflow. Generic Arithmetic(GENERIC% ARITHMETIC NIL Generic% arithmetic NIL NIL 2)(ARITHMETIC NIL Arithmetic NIL NIL 2 SUBNAME GENERIC SUBTEXT generic) 1 The functions in this section are "generic" arithmetic functions. If any of the arguments are floating-point numbers (see the Floating-Point Arithmetic section below), they act exactly like floating-point functions, and float all arguments, and return a floating-point number as their value. Otherwise, they act like the integer functions (see the Integer Arithmetic section below). If given a non-numeric argument, they generate an error, NON-NUMERIC ARG. (PLUS(PLUS (Function) NIL NIL NIL 2) X1 X2 ... XN) [NoSpread Function] X1 + X2 + ... + XN. (MINUS(MINUS (Function) NIL NIL NIL 2) X) [Function] - X (DIFFERENCE(DIFFERENCE (Function) NIL NIL NIL 2) X Y) [Function] X - Y ((TIMES (Function) NIL NIL NIL 2)TIMES X1 X2 ... XN) [NoSpread Function] X1 * X2 * ... * XN (QUOTIENT(QUOTIENT (Function) NIL NIL NIL 3) X Y) [Function] If X and Y are both integers, returns the integer division of X and Y. Otherwise, converts both X and Y to floating-point numbers, and does a floating-point division. The results of division by zero and floating-point overflow is determined by the function OVERFLOW (see the section above). (REMAINDER(REMAINDER (Function) NIL NIL NIL 3) X Y) [Function] If X and Y are both integers, returns (IREMAINDER X Y), otherwise (FREMAINDER X Y). (GREATERP (GREATERP% (Function) NIL NIL NIL 3)X Y) [Function] T, if X > Y, NIL otherwise. (LESSP(LESSP (Function) NIL NIL NIL 3) X Y) [Function] T if X < Y, NIL otherwise. (GEQ(GEQ (Function) NIL NIL NIL 3) X Y) [Function] T, if X >= Y, NIL otherwise. (LEQ(LEQ (Function) NIL NIL NIL 3) X Y) [Function] T, if X <= Y, NIL otherwise. (ZEROP(ZEROP (Function) NIL NIL NIL 3) X) [Function] (EQP X 0). (MINUSP(MINUSP (Function) NIL NIL NIL 3) X) [Function] T, if X is negative; NIL otherwise. Works for both integers and floating-point numbers. (MIN(MIN (Function) NIL NIL NIL 3) X1 X2 ... XN) [NoSpread Function] Returns the minimum of X1, X2, ..., XN. (MIN) returns the value of MAX.INTEGER (see the Integer Arithmetic section below). (MAX(MAX (Function) NIL NIL NIL 3) X1 X2 ... XN) [NoSpread Function] Returns the maximum of X1, X2, ..., XN. (MAX) returns the value of MIN.INTEGER (see the Integer Arithmetic section below). (ABS(ABS (Function) NIL NIL NIL 3) X) [Function] X if X > 0, otherwise -X. ABS uses GREATERP and MINUS (not IGREATERP and IMINUS). Integer Arithmetic(ARITHMETIC NIL Arithmetic NIL NIL 3 SUBNAME INTEGER SUBTEXT integer)(INTEGER% ARITHMETIC NIL Integer% arithmetic NIL NIL 3) 1 The input syntax for an integer is an optional sign (+ or -) followed by a sequence of decimal digits, and terminated by a delimiting character. Integers entered with this syntax are interpreted as decimal integers. Integers in other radices can be entered as follows: 123Q |o123 If an integer is followed by the letter Q, or proceeded by a vertical bar and the letter "o", the digits are interpreted an octal (base 8) integer. |b10101 If an integer is proceeded by a vertical bar and the letter "b", the digits are interpreted as a binary (base 2) integer. |x1A90 If an integer is proceeded by a vertical bar and the letter "x", the digits are interpreted as a hexadecimal (base 16) integer. The uppercase letters A though F are used as the digits after 9. |5r1243 If an integer is proceeded by a vertical bar, a positive decimal integer BASE, and the letter "r", the digits are interpreted as an integer in the base BASE. For example, |8r123 = 123Q, and |16r12A3 = |x12A3. When inputting a number in a radix above ten, the uppercase letters A through Z can be used as the digits after 9 (but there is no digit above Z, so it is not possible to type all base-99 digits). Note that 77Q and 63 both correspond to the same integers, and in fact are indistinguishable internally since no record is kept of the syntax used to create an integer. The function RADIX (see Chapter 25), sets the radix used to print integers. Integers are created by PACK and MKATOM when given a sequence of characters observing the above syntax, e.g. (PACK '(1 2 Q)) => 10. Integers are also created as a result of arithmetic operations. The range of integers of various types is implementation-dependent. This information is accessible to the user through the following variables: MIN.SMALLP(MIN.SMALLP (Variable) NIL NIL NIL 4) [Variable] MAX.SMALLP(MAX.SMALLP (Variable) NIL NIL NIL 4) [Variable] The smallest/largest possible small integer. MIN.FIXP(MIN.FIXP (Variable) NIL NIL NIL 4) [Variable] MAX.FIXP(MAX.FIXP (Variable) NIL NIL NIL 4) [Variable] The smallest/largest possible large integer. MIN.INTEGER(MIN.INTEGER (Variable) NIL NIL NIL 4) [Variable] MAX.INTEGER(MAX.INTEGER (Variable) NIL NIL NIL 4) [Variable] The smallest/largest possible integers. For some algorithms, it is useful to have an integer that is larger than any other integer. Therefore, the values of MAX.INTEGER and MIN.INTEGER are two special bignums; the value of MAX.INTEGER is GREATERP than any other integer, and the value of MIN.INTEGER is LESSP than any other integer. Trying to do arithmetic using these special bignums, other than comparison, will cause an error. All of the functions described below work on integers. Unless specified otherwise, if given a floating-point number, they first convert the number to an integer by truncating the fractional bits, e.g., (IPLUS 2.3 3.8)=5; if given a non-numeric argument, they generate an error, NON-NUMERIC ARG. (IPLUS X1 X2 ... XN) [NoSpread Function] Returns the sum X1 + X2 + ... + XN. (IPLUS)=0. (IMINUS X) [Function] -X (IDIFFERENCE X Y) [Function] X - Y (ADD1(ADD1 (Function) NIL NIL NIL 5) X) [Function] X + 1 (SUB1(SUB1 (Function) NIL NIL NIL 5) X) [Function] X - 1 (ITIMES(ITIMES (Function) NIL NIL NIL 5) X1 X2 ... XN) [NoSpread Function] Returns the product X1 * X2 * ... * XN. (ITIMES)=1. (IQUOTIENT(IQUOTIENT (Function) NIL NIL NIL 5) X Y) [Function] X / Y truncated. Examples: (IQUOTIENT 3 2) => 1 (IQUOTIENT -3 2) => -1 If Y is zero, the result is determined by the function OVERFLOW . (IREMAINDER(IREMAINDER (Function) NIL NIL NIL 5) X Y) [Function] Returns the remainder when X is divided by Y. Example: (IREMAINDER 3 2) => 1 (IMOD(IMOD (Function) NIL NIL NIL 5) X N) [Function] Computes the integer modulus; this differs from IREMAINDER in that the result is always a non-negative integer in the range [0,N). (IGREATERP(IGREATERP (Function) NIL NIL NIL 5) X Y) [Function] T, if X > Y; NIL otherwise. (ILESSP(ILESSP (Function) NIL NIL NIL 5) X Y) [Function] T, if X < Y; NIL otherwise. (IGEQ(IGEQ (Function) NIL NIL NIL 5) X Y) [Function] T, if X >= Y; NIL otherwise. (ILEQ(ILEQ (Function) NIL NIL NIL 5) X Y) [Function] T, if X <= Y; NIL otherwise. (IMIN(IMIN (Function) NIL NIL NIL 5) X1 X2 ... XN) [NoSpread Function] Returns the minimum of X1, X2, ..., XN. (IMIN) returns the largest possible large integer, the value of MAX.INTEGER. (IMAX(IMAX (Function) NIL NIL NIL 5) X1 X2 ... XN) [NoSpread Function] Returns the maximum of X1, X2, ..., XN. (IMAX) returns the smallest possible large integer, the value of MIN.INTEGER. (IEQP(EQP (Function) NIL NIL NIL 6) X Y) [Function] Returns T if X and Y are EQ or equal integers; NIL otherwise. Note that EQ may be used if X and Y are known to be small integers. IEQP converts X and Y to integers, e.g., (IEQP 2000 2000.3) => T. Causes NON-NUMERIC ARG error if either X or Y are not numbers. (FIX(FIX (Function) NIL NIL NIL 6) N) [Function] If N is an integer, returns N. Otherwise, converts N to an integer by truncating fractional bits For example, (FIX 2.3) => 2, (FIX -1.7) => -1. Since FIX is also a programmer's assistant command (see Chapter 13), typing FIX directly to Interlisp will not cause the function FIX to be called. (FIXR(FIXR (Function) NIL NIL NIL 6) N) [Function] If N is an integer, returns N. Otherwise, converts N to an integer by rounding. For example, (FIXR 2.3) => 2, (FIXR -1.7) => -2, (FIXR 3.5) => 4). (GCD(GCD (Function) NIL NIL NIL 6) N1 N2) [Function] Returns the greatest common divisor of N1 and N2, e.g., (GCD 72 64)=8. Logical Arithmetic(ARITHMETIC NIL Arithmetic NIL NIL 6 SUBNAME LOGICAL% FUNCTIONS SUBTEXT logical% functions) Functions(LOGICAL% ARITHMETIC% FUNCTIONS NIL Logical% arithmetic% functions NIL NIL 6) 1 (LOGAND(LOGAND (Function) NIL NIL NIL 6) X1 X2 ... XN) [NoSpread Function] Returns the logical AND of all its arguments, as an integer. Example: (LOGAND 7 5 6) => 4 (LOGOR(LOGOR (Function) NIL NIL NIL 6) X1 X2 ... XN) [NoSpread Function] Returns the logical OR of all its arguments, as an integer. Example: (LOGOR 1 3 9) => 11 (LOGXOR(LOGXOR (Function) NIL NIL NIL 6) X1 X2 ... XN) [NoSpread Function] Returns the logical exclusive OR of its arguments, as an integer. Example: (LOGXOR 11 5) => 14 (LOGXOR 11 5 9) = (LOGXOR 14 9) => 7 (LSH(LSH (Function) NIL NIL NIL 6) X N) [Function] (arithmetic) "Left Shift." Returns X shifted left N places, with the sign bit unaffected. X can be positive or negative. If N is negative, X is shifted right -N places. (RSH(RSH (Function) NIL NIL NIL 6) X N) [Function] (arithmetic) "Right Shift." Returns X shifted right N places, with the sign bit unaffected, and copies of the sign bit shifted into the leftmost bit. X can be positive or negative. If N is negative, X is shifted left -N places. Warning: Be careful if using RSH to simulate division; RSHing a negative number is not generally equivalent to dividing by a power of two. (LLSH(LLSH (Function) NIL NIL NIL 7) X N) [Function] (LRSH(LLSH (Function) NIL NIL NIL 7) X N) [Function] "Logical Left Shift" and "Logical Right Shift". The difference between a logical and arithmetic right shift lies in the treatment of the sign bit. Logical shifting treats it just like any other bit; arithmetic shifting will not change it, and will "propagate" rightward when actually shifting rightwards. Note that shifting (arithmetic) a negative number "all the way" to the right yields -1, not 0. Note: LLSH and LRSH are currently implemented using mod-2^32 arithmetic. Passing a bignum to either of these will cause an error. LRSH of negative numbers will shift in 0s in the high bits. (INTEGERLENGTH(LLSH (Function) NIL NIL NIL 7) X) [Function] Returns the number of bits needed to represent X (coerced to an integer). This is equivalent to: 1+floor[log2[abs[X]]]. (INTEGERLENGTH 0) = 0. (POWEROFTWOP(POWEROFTWOP (Function) NIL NIL NIL 7) X) [Function] Returns non-NIL if X (coerced to an integer) is a power of two. (EVENP(EVENP (Function) NIL NIL NIL 7) X Y) [NoSpread Function] If Y is not given, equivalent to (ZEROP (IMOD X 2)); otherwise equivalent to (ZEROP (IMOD X Y)). (ODDP(ODDP (Function) NIL NIL NIL 7) N MODULUS) [NoSpread Function] Equivalent to (NOT (EVENP N MODULUS)). MODULUS defaults to 2. (LOGNOT (LOGNOT% (Macro) NIL NIL NIL 7)N) [Macro] Logical negation of the bits in N. Equivalent to (LOGXOR N -1) (BITTEST(BITTEST (Macro) NIL NIL NIL 7) N MASK) [Macro] Returns T if any of the bits in MASK are on in the number N. Equivalent to (NOT (ZEROP (LOGAND N MASK))) (BITCLEAR(BITCLEAR (Macro) NIL NIL NIL 7) N MASK) [Macro] Turns off bits from MASK in N. Equivalent to (LOGAND N (LOGNOT MASK)) (BITSET(BITSET (Macro) NIL NIL NIL 7) N MASK) [Macro] Turns on the bits from MASK in N. Equivalent to (LOGOR N MASK) (MASK.1'S(MASK.1'S (Macro) NIL NIL NIL 7) POSITION SIZE) [Macro] Returns a bit-mask with SIZE one-bits starting with the bit at POSITION. Equivalent to (LLSH (SUB1 (EXPT 2 SIZE)) POSITION) (MASK.0'S(MASK.0'S (Macro) NIL NIL NIL 7) POSITION SIZE) [Macro] Returns a bit-mask with all one bits, except for SIZE bits starting at POSITION. Equivalent to (LOGNOT (MASK.1'S POSITION SIZE)) (LOADBYTE(LOADBYTE (Function) NIL NIL NIL 7) N POS SIZE) [Function] Extracts SIZE bits from N, starting at position POS. Equivalent to (LOGAND (RSH N POS) (MASK.1'S 0 SIZE)) (DEPOSITBYTE(DEPOSITBYTE (Function) NIL NIL NIL 8) N POS SIZE VAL) [Function] Insert SIZE bits of VAL at position POS into N, returning the result. Equivalent to (LOGOR (BITCLEAR N (MASK.1'S POS SIZE)) (LSH (LOGAND VAL (MASK.1'S 0 SIZE)) POS)) (ROT(ROT (Function) NIL NIL NIL 8) X N FIELDSIZE) [Function] "Rotate bits in field". It performs a bitwise left-rotation of the integer X, by N places, within a field of FIELDSIZE bits wide. Bits being shifted out of the position selected by (EXPT 2 (SUB1 FIELDSIZE)) will flow into the "units" position. The notions of position and size can be combined to make up a "byte specifier", which is constructed by the macro BYTE [note reversal of arguments as compare with above functions]: (BYTE(BYTE (Macro) NIL NIL NIL 8) SIZE POSITION) [Macro] Constructs and returns a "byte specifier" containing SIZE and POSITION. (BYTESIZE(BYTESIZE (Macro) NIL NIL NIL 8) BYTESPEC) [Macro] Returns the SIZE componant of the "byte specifier" BYTESPEC. (BYTEPOSITION(BYTEPOSITION (Macro) NIL NIL NIL 8) BYTESPEC) [Macro] Returns the POSITION componant of the "byte specifier" BYTESPEC. (LDB(LDB (Macro) NIL NIL NIL 8) BYTESPEC VAL) [Macro] Equivalent to (LOADBYTE VAL (BYTEPOSITION BYTESPEC) (BYTESIZE BYTESPEC)) (DPB(DPB (Macro) NIL NIL NIL 8) N BYTESPEC VAL) [Macro] Equivalent to (DEPOSITBYTE VAL BYTEPOSITION BYTESPEC) (BYTESIZE BYTESPEC) N) Floating-Point Arithmetic(ARITHMETIC NIL Arithmetic NIL NIL 8 SUBNAME FLOATING% POINT SUBTEXT floating% point)(FLOATING% POINT% ARITHMETIC NIL Floating% point% arithmetic NIL NIL 8) 1 A floating-point number is input as a signed integer, followed by a decimal point, followed by another sequence of digits called the fraction, followed by an exponent (represented by E followed by a signed integer) and terminated by a delimiter. Both signs are optional, and either the fraction following the decimal point, or the integer preceding the decimal point may be omitted. One or the other of the decimal point or exponent may also be omitted, but at least one of them must be present to distinguish a floating-point number from an integer. For example, the following will be recognized as floating-point numbers: 5. 5.00 5.01 .3 5E2 5.1E2 5E-3 -5.2E+6 Floating-point numbers are printed using the format control specified by the function FLTFMT (see Chapter 25). FLTFMT is initialized to T, or free format. For example, the above floating-point numbers would be printed free format as: 5.0 5.0 5.01 .3 500.0 510.0 .005 -5.2E6 Floating-point numbers are created by the read program when a "." or an E appears in a number, e.g., 1000 is an integer, 1000. a floating-point number, as are 1E3 and 1.E3. Note that 1000D, 1000F, and 1E3D are perfectly legal literal atoms. Floating-point numbers are also created by PACK and MKATOM, and as a result of arithmetic operations. PRINTNUM (page X.XXsee Chapter 25) permits greater controls on the printed appearance of floating-point numbers, allowing such things as left-justification, suppression of trailing decimals, etc. The floating-point number range is stored in the following variables: MIN.FLOAT(MIN.FLOAT (Variable) NIL NIL NIL 9) [Variable] The smallest possible floating-point number. MAX.FLOAT(MAX.FLOAT (Variable) NIL NIL NIL 9) [Variable] The largest possible floating-point number. All of the functions described below work on floating-point numbers. Unless specified otherwise, if given an integer, they first convert the number to a floating-point number, e.g., (FPLUS 1 2.3) <=> (FPLUS 1.0 2.3) => 3.3; if given a non-numeric argument, they generate an error, NON-NUMERIC ARG. (FPLUS(FPLUS (Function) NIL NIL NIL 9) X1 X2 ... XN) [NoSpread Function] X1 + X2 + ... + XN (FMINUS(FMINUS (Function) NIL NIL NIL 9) X) [Function] - X (FDIFFERENCE(FDIFFERENCE (Function) NIL NIL NIL 9) X Y) [Function] X - Y (FTIMES(FTIMES (Function) NIL NIL NIL 9) X1 X2 ... XN) [NoSpread Function] X1 * X2 * ... * XN (FQUOTIENT(FQUOTIENT (Function) NIL NIL NIL 9) X Y) [Function] X / Y. The results of division by zero and floating-point overflow is determined by the function OVERFLOW. (FREMAINDER(FREMAINDER (Function) NIL NIL NIL 9) X Y) [Function] Returns the remainder when X is divided by Y. Equivalent to: (FDIFFERENCE X (FTIMES Y (FIX (FQUOTIENT X Y)))) Example: (FREMAINDER 7.5 2.3) => 0.6 (FGREATERP(FGREATERP (Function) NIL NIL NIL 10) X Y) [Function] T, if X > Y, NIL otherwise. (FLESSP(FLESSP (Function) NIL NIL NIL 10) X Y) [Function] T, if X < Y, NIL otherwise. (FEQP(FEQP (Function) NIL NIL NIL 10) X Y) [Function] Returns T if N and M are equal floating-point numbers; NIL otherwise. FEQP converts N and M to floating-point numbers.Causes NON-NUMERIC ARG error if either N or M are not numbers. (FMIN(FMIN (Function) NIL NIL NIL 10) X1 X2 ... XN) [NoSpread Function] Returns the minimum of X1, X2, ..., XN. (FMIN) returns the largest possible floating-point number, the value of MAX.FLOAT. (FMAX(FMAX (Function) NIL NIL NIL 10) X1 X2 ... XN) [NoSpread Function] Returns the maximum of X1, X2, ..., XN. (FMAX) returns the smallest possible floating-point number, the value of MIN.FLOAT. (FLOAT(FLOAT (Function) NIL NIL NIL 10) X) [Function] Converts X to a floating-point number. Example: (FLOAT 0) => 0.0 Other Arithmetic Functions 1 (EXPT(EXPT (Function) NIL NIL NIL 10) A N) [Function] Returns AN. If A is an integer and N is a positive integer, returns an integer, e.g, (EXPT 3 4) => 81, otherwise returns a floating-point number. If A is negative and N fractional, an error is generated: ILLEGAL EXPONENTIATION. If N is floating and either too large or too small, an error is generated: VALUE OUT OF RANGE EXPT. (SQRT(SQRT (Function) NIL NIL NIL 10) N) [Function] Returns the square root of N as a floating-point number. N may be fixed or floating-point. Generates an error if N is negative. (LOG(LOG (Function) NIL NIL NIL 10) X) [Function] Returns the natural logarithm of X as a floating-point number. X can be integer or floating-point. (ANTILOG(ANTILOG (Function) NIL NIL NIL 10) X) [Function] Returns the floating-point number whose logarithm is X. X can be integer or floating-point. Example: (ANTILOG 1) = e => 2.71828... (SIN(SIN (Function) NIL NIL NIL 10) X RADIANSFLG) [Function] Returns the sine of X as a floating-point number. X is in degrees unless RADIANSFLG=T. (COS(COS (Function) NIL NIL NIL 11) X RADIANSFLG) [Function] Similar to SIN. ((TAN (Function) NIL NIL NIL 11)TAN X RADIANSFLG) [Function] Similar to SIN. (ARCSIN (ARCSIN% (Function) NIL NIL NIL 11)X RADIANSFLG) [Function] X is a number between -1 and 1 (or an error is generated). The value of ARCSIN is a floating-point number, and is in degrees unless RADIANSFLG=T. In other words, if (ARCSIN X RADIANSFLG)=Z then (SIN Z RADIANSFLG)=X. The range of the value of ARCSIN is -90 to +90 for degrees, -~PI~/2 to ~PI~/2 for radians. (ARCCOS(ARCCOS (Function) NIL NIL NIL 11) X RADIANSFLG) [Function] Similar to ARCSIN. Range is 0 to 180, 0 to ~PI~. (ARCTAN(ARCTAN (Function) NIL NIL NIL 11) X RADIANSFLG) [Function] Similar to ARCSIN. Range is 0 to 180, 0 to ~PI~. (ARCTAN2(ARCTAN2 (Function) NIL NIL NIL 11) Y X RADIANSFLG) [Function] Computes (ARCTAN (FQUOTIENT Y X) RADIANSFLG), and returns a corresponding value in the range -180 to 180 (or -~PI~ to ~PI~), i.e. the result is in the proper quadrant as determined by the signs of X and Y. (RAND(RAND (Function) NIL NIL NIL 11) LOWER UPPER) [Function] Returns a pseudo-random number between LOWER and UPPER inclusive, i.e., RAND can be used to generate a sequence of random numbers. If both limits are integers, the value of RAND is an integer, otherwise it is a floating-point number. The algorithm is completely deterministic, i.e., given the same initial state, RAND produces the same sequence of values. The internal state of RAND is initialized using the function RANDSET described below. (RANDSET(RANDSET (Function) NIL NIL NIL 11) X) [Function] Returns the internal state of RAND. If X=NIL, just returns the current state. If X=T, RAND is initialized using the clocks, and RANDSET returns the new state. Otherwise, X is interpreted as a previous internal state, i.e., a value of RANDSET, and is used to reset RAND. For example, (SETQ OLDSTATE (RANDSET)) ... (for X from 1 to 10 do (PRIN1 (RAND 1 10))) 2847592748NIL (RANDSET OLDSTATE) ... (for X from 1 to 10 do (PRIN1 (RAND 1 10))) 2847592748NIL (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "7-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "7-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "7-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "7-" "")) (558 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "7-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "7-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))2l6T3HZ +T55,3HZT,HH +-T3(T3HZ +T,,,ll,/,HH,HHF PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGRTITAN +TITAN +TITAN +TITAN +CLASSIC +CLASSIC + HELVETICA HELVETICACLASSIC + HELVETICA +CLASSIC +MODERN +MODERNMODERN + HRULE.GETFNCLASSIC +  +  HRULE.GETFNCLASSIC +  +$ +  HRULE.GETFNCLASSIC + $ +  HRULE.GETFNCLASSIC +  & HRULE.GETFNMODERN + T  S  !IM.INDEX.GETFN    +    IM.INDEX.GETFN    n    !IM.INDEX.GETFN    3    "IM.INDEX.GETFN                 3 ! K    IM.INDEX.GETFN               N       i l  #IM.INDEX.GETFN     +    #  I      E  +7IM.INDEX.GETFNEIM.INDEX.GETFN  HRULE.GETFNMODERN +   IM.INDEX.GETFN      IM.INDEX.GETFN    %IM.INDEX.GETFN     IM.INDEX.GETFN     #IM.INDEX.GETFN   4    @ Z   +$IM.INDEX.GETFN              %IM.INDEX.GETFN       IM.INDEX.GETFNMODERN +       IM.INDEX.GETFNMODERN +       IM.INDEX.GETFNCLASSIC +        IM.INDEX.GETFNMODERN +      !IM.INDEX.GETFNMODERN +    A  IM.INDEX.GETFNMODERN +       ,  IM.INDEX.GETFNMODERN +       ,  IM.INDEX.GETFNCLASSIC +          +EIM.INDEX.GETFN7IM.INDEX.GETFN  HRULE.GETFNMODERN +5      ) k  {       J K    G  !  5  +   :   F C   +%IM.INDEX.GETFN  +%IM.INDEX.GETFN -  #IM.INDEX.GETFN #IM.INDEX.GETFN -  &IM.INDEX.GETFN  &IM.INDEX.GETFN   '  *  y   ;                   IM.INDEX.GETFN   IM.INDEX.GETFN   !IM.INDEX.GETFN       + +$IM.INDEX.GETFN     3   %IM.INDEX.GETFN      IM.INDEX.GETFN  0 +B   +$IM.INDEX.GETFN       !IM.INDEX.GETFN       IM.INDEX.GETFNCLASSIC +       IM.INDEX.GETFN       IM.INDEX.GETFN      :   IM.INDEX.GETFN       ;  IM.INDEX.GETFN             +    +     IM.INDEX.GETFN     ;    C 3   IM.INDEX.GETFN     *  IM.INDEX.GETFN  '    +[IM.INDEX.GETFN +MIM.INDEX.GETFN  HRULE.GETFNMODERN + !IM.INDEX.GETFN  G   IM.INDEX.GETFN  F  !IM.INDEX.GETFN  L ' IM.INDEX.GETFN  $  ( "      IM.INDEX.GETFN  %  b "       Q  IM.INDEX.GETFN  IM.INDEX.GETFN     p 8  IM.INDEX.GETFN  / C     &IM.INDEX.GETFN    ,   IM.INDEX.GETFN           IM.INDEX.GETFN         IM.INDEX.GETFN       IM.INDEX.GETFN            IM.INDEX.GETFN        IM.INDEX.GETFN          IM.INDEX.GETFN   #       IM.INDEX.GETFN  1       #IM.INDEX.GETFN +            &IM.INDEX.GETFN      '   +           IM.INDEX.GETFN  K   @  & r ?  IM.INDEX.GETFN  5     IM.INDEX.GETFN   #   $IM.INDEX.GETFN   #   IM.INDEX.GETFN          IM.INDEX.GETFN           +UIM.INDEX.GETFNGIM.INDEX.GETFN  HRULE.GETFNMODERN +  > |  V   b ?    !     P  ,  F  $IM.INDEX.GETFN -  $IM.INDEX.GETFN ,  ,;     IM.INDEX.GETFN      !IM.INDEX.GETFN     &IM.INDEX.GETFN     !IM.INDEX.GETFN      +$IM.INDEX.GETFN    Z   %IM.INDEX.GETFN           +%IM.INDEX.GETFN       "IM.INDEX.GETFN        IM.INDEX.GETFN     #   +  "      IM.INDEX.GETFN       B    IM.INDEX.GETFN       C   !IM.INDEX.GETFN   '   +  HRULE.GETFNMODERN +  IM.INDEX.GETFN      1 1  $  G    IM.INDEX.GETFN    8   IM.INDEX.GETFN  !  #  #IM.INDEX.GETFN  5  - " IM.INDEX.GETFNMODERN +     +   IM.INDEX.GETFNMODERN +     IM.INDEX.GETFN      $IM.INDEX.GETFN  H 6 +    +    +  ;  "IM.INDEX.GETFN   !  "IM.INDEX.GETFN   !  #IM.INDEX.GETFN      +     IM.INDEX.GETFN  '   b  > #   #IM.INDEX.GETFN     &   & $ ?        -      -  a^z \ No newline at end of file diff --git a/docs/porter-irm/09-conditionals.TEDIT b/docs/porter-irm/09-conditionals.TEDIT new file mode 100644 index 00000000..c5f55ae6 --- /dev/null +++ b/docs/porter-irm/09-conditionals.TEDIT @@ -0,0 +1,63 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 9. CONDITIONALS AND ITERATIVE STATEMENTS 1 9. CONDITIONALS AND INTERATIVE STATEMENTS 1 9. CONDITIONALS AND ITERATIVE STATEMENTS 6 In order to do any but the simplest computations, it is necessary to test values and execute expressions conditionally, and to execute a series of expressions. Interlisp supplies a large number of predicates, conditional functions, and control functions. Also, there is a complex "iterative statement" facility which allows you to easily create complex loops and iterative constructs. Data Type Predicates 1 Interlisp provides separate functions for testing whether objects are of certain commonly-used types: (LITATOM(LITATOM (Function) NIL NIL NIL 1) X) [Function] Returns T if X is a litatom; NIL otherwise. Note that a number is not a litatom. (SMALLP(SMALLP (Function) NIL NIL NIL 1) X) [Function] Returns X if X is a small integer; NIL otherwise. (The range of small integers is implementation-dependent. See Chapter 7.) (FIXP(FIXP (Function) NIL NIL NIL 1) X) [Function] Returns X if X is a small or large integer; NIL otherwise. (FLOATP(FLOATP (Function) NIL NIL NIL 1) X) [Function] Returns X if X is a floating point number; NIL otherwise. (NUMBERP(NUMBERP (Function) NIL NIL NIL 1) X) [Function] Returns X if X is a number of any type (FIXP or FLOATP), NIL otherwise. (ATOM(ATOM (Function) NIL NIL NIL 1) X) [Function] Returns T if X is an atom (i.e. a litatom or a number); NIL otherwise. Warning: (ATOM X) is NIL if X is an array, string, etc. In many dialects of Lisp, the function ATOM is defined equivalent to the Interlisp function NLISTP. (LISTP(LISTP (Function) NIL NIL NIL 1) X) [Function] Returns X if X is a list cell, e.g., something created by CONS; NIL otherwise. (NLISTP(NLISTP (Function) NIL NIL NIL 1) X) [Function] (NOT (LISTP X)). Returns T if X is not a list cell, NIL otherwise. (STRINGP(STRINGP (Function) NIL NIL NIL 1) X) [Function] Returns X if X is a string, NIL otherwise. (ARRAYP X) [Function] Returns X if X is an array, NIL otherwise. Note: In some implementations of Interlisp (but not Interlisp-D), ARRAYP(ARRAYP (Function) NIL NIL NIL 1) may also return X if it is of type CCODEP or HARRAYP. (HARRAYP X) [Function] Returns X if it is a hash array object; otherwise NIL. HARRAYP(HARRAYP (Function) NIL NIL NIL 2) returns NIL if X is a list whose CAR is an HARRAYP, even though this is accepted by the hash array functions. Note: The empty list, () or NIL, is considered to be a litatom, rather than a list. Therefore, (LITATOM NIL) = (ATOM NIL) = T and (LISTP NIL) = NIL. Care should be taken when using these functions if the object may be the empty list NIL. Equality Predicates 1 A common operation when dealing with data objects is to test whether two objects are equal. In some cases, such as when comparing two small integers, equality can be easily determined. However, sometimes there is more than one type of equality. For instance, given two lists, one can ask whether they are exactly the same object, or whether they are two distinct lists which contain the same elements. Confusion between these two types of equality is often the source of program errors. Interlisp supplies an extensive set of functions for testing equality: (EQ(EQ (Function) NIL NIL NIL 2) X Y) [Function] Returns T if X and Y are identical pointers; NIL otherwise. EQ should not be used to compare two numbers, unless they are small integers; use EQP instead. (NEQ(NEQ (Function) NIL NIL NIL 2) X Y) [Function] (NOT (EQ X Y)) (NULL(NULL (Function) NIL NIL NIL 2) X) [Function] (NOT(NOT (Function) NIL NIL NIL 2) X) [Function] (EQ X NIL) (EQP(EQP (Function) NIL NIL NIL 2) X Y) [Function] Returns T if X and Y are EQ, or if X and Y are numbers and are equal in value; NIL otherwise. For more discussion of EQP and other number functions, see Chapter 7. EQP also can be used to compare stack pointers (Section 11) and compiled code (Chapter 10). (EQUAL(EQUAL (Function) NIL NIL NIL 2) X Y) [Function] EQUAL returns T if X and Y are one of the following: 1. EQ 2. EQP, i.e., numbers with equal value; or 3. STREQUAL, i.e., strings containing the same sequence of characters; or 4. Lists and CAR of X is EQUAL to CAR of Y, and CDR of X is EQUAL to CDR of Y. EQUAL returns NIL otherwise. Note that EQUAL can be significantly slower than EQ. A loose description of EQUAL might be to say that X and Y are EQUAL if they print out the same way. (EQUALALL(EQUALALL (Function) NIL NIL NIL 3) X Y) [Function] Like EQUAL, except it descends into the contents of arrays, hash arrays, user data types, etc. Two non-EQ arrays may be EQUALALL if their respective componants are EQUALALL. Logical Predicates 1 (AND X1 X2 ... XN)(AND% X1% X2% ...% XN%) (Function) %(AND% X1% X2% ...% XN%) NIL NIL 3) [NLambda NoSpread Function] Takes an indefinite number of arguments (including zero), that are evaluated in order. If any argument evaluates to NIL, AND immediately returns NIL (without evaluating the remaining arguments). If all of the arguments evaluate to non-NIL, the value of the last argument is returned. (AND)=>T. (OR X1 X2 ... XN)(OR% X1% X2% ...% XN%) (Function) %(OR% X1% X2% ...% XN%) NIL NIL 3) [NLambda NoSpread Function] Takes an indefinite number of arguments (including zero), that are evaluated in order. If any argument is non-NIL, the value of that argument is returned by OR (without evaluating the remaining arguments). If all of the arguments evaluate to NIL, NIL is returned. (OR) => NIL. AND and OR can be used as simple logical connectives, but note that they may not evaluate all of their arguments. This makes a difference if the evaluation of some of the arguments causes side-effects. Another result of this implementation of AND and OR is that they can be used as simple conditional statements. For example: (AND (LISTP X) (CDR X)) returns the value of (CDR X) if X is a list cell, otherwise it returns NIL without evaluating (CDR X). In general, this use of AND and OR should be avoided in favor of more explicit conditional statements in order to make programs more readable. The COND Conditional Function 1 (COND CLAUSE1 CLAUSE2 ... CLAUSEK)(COND% CLAUSE1% CLAUSE2% ...% CLAUSEK%) (Function) %(COND% CLAUSE1% CLAUSE2% ...% CLAUSEK%) NIL NIL 3) [NLambda NoSpread Function] The conditional function of Interlisp, COND, takes an indefinite number of arguments, called clauses. Each CLAUSEi is a list of the form (Pi Ci1 ... CiN), where Pi is the predicate, and Ci1 ... CiN are the consequents. The operation of COND can be paraphrased as: IF P1 THEN C11 ... C1N ELSEIF P2 THEN C21 ... C2N ELSEIF P3 ... The clauses are considered in sequence as follows: the predicate P1 of the clause CLAUSEi is evaluated. If the value of P1 is "true" (non-NIL), the consequents Ci1 ... CiN are evaluated in order, and the value of the COND is the value of CiN, the last expression in the clause. If P1 is "false" (EQ to NIL), then the remainder of CLAUSEi is ignored, and the next clause, CLAUSEi+1, is considered. If no Pi is true for any clause, the value of the COND is NIL. Note: If a clause has no consequents, and has the form (Pi), then if Pi evaluates to non-NIL, it is returned as the value of the COND. It is only evaluated once. Example: (DEFINEQ (DOUBLE (X) (COND ((NUMBERP X) (PLUS X X)) ((STRINGP X) (CONCAT X X)) ((ATOM X) (PACK* X X)) (T (PRINT "unknown") X) ((HORRIBLE-ERROR))] (DOUBLE) (DOUBLE 5) 10 (DOUBLE "FOO") "FOOFOO" (DOUBLE 'BAR) BARBAR (DOUBLE '(A B C)) "unknown" (A B C) A few points about this example: Notice that 5 is both a number and an atom, but it is "caught" by the NUMBERP clause before the ATOM clause. Also notice the predicate T, which is always true. This is the normal way to indicate a COND clause which will always be executed (if none of the preceeding clauses are true). (HORRIBLE-ERROR) will never be executed. The IF Statement 1 The IF statement(IF% STATEMENT NIL IF% statement NIL NIL 4) provides a way of way of specifying conditional expressions that is much easier and readable than using the COND function directly. CLISP translates expressions employing IF, THEN, ELSEIF, or ELSE (or their lowercase versions) into equivalent COND expressions. In general, statements of the form: (if AAA then BBB elseif CCC then DDD else EEE) are translated to: (COND (AAA BBB) (CCC DDD) (T EEE) ) The segment between IF or ELSEIF and the next THEN corresponds to the predicate of a COND clause, and the segment between THEN and the next ELSE or ELSEIF as the consequent(s). ELSE is the same as ELSEIF T THEN. These words are spelling corrected using the spelling list CLISPIFWORDSPLST. Lower case versions (if, then, elseif, else) may also be used. If there is nothing following a THEN, or THEN is omitted entirely, then the resulting COND clause has a predicate but no consequent. For example, (if X then elseif ...) and (if X elseif ...) both translate to (COND (X) ...), which means that if X is not NIL, it is returned as the value of the COND. Note that only one expression is allowed as the predicate, but multiple expressions are allowed as the consequents after THEN or ELSE. Multiple consequent expressions are implicitely wrapped in a PROGN, and the value of the last one is returned as the value of the consequent. For example: (if X then (PRINT "FOO") (PRINT "BAR") elseif Y then (PRINT "BAZ")) CLISP considers IF, THEN, ELSE, and ELSEIF to have lower precedence than all infix and prefix operators, as well as Interlisp forms, so it is sometimes possible to omit parentheses around predicate or consequent forms. For example, (if FOO X Y then ...) is equivalent to (if (FOO X Y) then ...), and (if X then FOO X Y else ...) as equivalent to (if X then (FOO X Y) else ...). Essentially, CLISP determines whether the segment between THEN and the next ELSE or ELSEIF corresponds to one form or several and acts accordingly, occasionally interacting with the user to resolve ambiguous cases. Note that if FOO is bound as a variable, (if FOO then ...) is translated as (COND (FOO ...)), so if a call to the function FOO is desired, use (if (FOO) then ...). Selection Functions 1 (SELECTQ X CLAUSE1 CLAUSE2 ... CLAUSEK DEFAULT) [NLambda NoSpread Function] Selects a form or sequence of forms based on the value of X. Each clause CLAUSEi is a list of the form (Si Ci1 ... CiN) where Si is the selection key. The operation of SELECTQ can be paraphrased as: IF X = S1 THEN C11 ... C1N ELSEIF X = S2 THEN ... ELSE DEFAULT If Si is an atom, the value of X is tested to see if it is EQ to Si (which is not evaluated). If so, the expressions Ci1 ... CiN are evaluated in sequence, and the value of the SELECTQ is the value of the last expression evaluated, i.e., CiN. If Si is a list, the value of X is compared with each element (not evaluated) of Si, and if X is EQ to any one of them, then Ci1 ... CiN are evaluated as above. If CLAUSEi is not selected in one of the two ways described, CLAUSEi+1 is tested, etc., until all the clauses have been tested. If none is selected, DEFAULT is evaluated, and its value is returned as the value of the SELECTQ. DEFAULT must be present. An example of the form of a SELECTQ is: [SELECTQ MONTH (FEBRUARY (if (LEAPYEARP) then 29 else 28)) ((APRIL JUNE SEPTEMBER NOVEMBER) 30) 31] If the value of MONTH is the litatom FEBRUARY, the SELECTQ returns 28 or 29 (depending on (LEAPYEARP)); otherwise if MONTH is APRIL, JUNE, SEPTEMBER, or NOVEMBER, the SELECTQ returns 30; otherwise it returns 31. SELECTQ compiles open, and is therefore very fast; however, it will not work if the value of X is a list, a large integer, or floating point number, since SELECTQ uses EQ for all comparisons. Note: SELCHARQ (Chapter 2) is a version of SELECTQ that recognizes CHARCODE litatoms. (SELECTC X CLAUSE1 CLAUSE2 ... CLAUSEK DEFAULT) [NLambda NoSpread Function] "SELECTQ-on-Constant." Similar to SELECTQ except that the selection keys are evaluated, and the result used as a SELECTQ-style selection key. SELECTC is compiled as a SELECTQ, with the selection keys evaluated at compile-time. Therefore, the selection keys act like compile-time constants (see Chapter 18). For example: [SELECTC NUM ( (for X from 1 to 9 collect (TIMES X X)) "SQUARE" ) "HIP"] compiles as: [SELECTQ NUM ( (1 4 9 16 25 36 49 64 81) "SQUARE" ) "HIP"] PROG and Associated Control Functions 1 (PROG1 X1 X2 ... XN) [NLambda NoSpread Function] Evaluates its arguments in order, and returns the value of its first argument X1. For example, (PROG1 X (SETQ X Y)) sets X to Y, and returns X's original value. (PROG2 X1 X2 ... XN) [NoSpread Function] Similar to PROG1. Evaluates its arguments in order, and returns the value of its second argument X2. (PROGN X1 X2 ... XN) [NLambda NoSpread Function] PROGN evaluates each of its arguments in order, and returns the value of its last argument. PROGN is used to specify more than one computation where the syntax allows only one, e.g., (SELECTQ ... (PROGN ...)) allows evaluation of several expressions as the default condition for a SELECTQ. (PROG VARLST E1 E2 ... EN) [NLambda NoSpread Function] This function allows the user to write an ALGOL-like program containing Interlisp expressions (forms) to be executed. The first argument, VARLST, is a list of local variables (must be NIL if no variables are used). Each atom in VARLST is treated as the name of a local variable and bound to NIL. VARLST can also contain lists of the form (LITATOM FORM). In this case, LITATOM is the name of the variable and is bound to the value of FORM. The evaluation takes place before any of the bindings are performed, e.g., (PROG ((X Y) (Y X)) ...) will bind local variable X to the value of Y (evaluated outside the PROG) and local variable Y to the value of X (outside the PROG). An attempt to use anything other than a litatom as a PROG variable will cause an error, ARG NOT LITATOM. An attempt to use NIL or T as a PROG variable will cause an error, ATTEMPT TO BIND NIL OR T. The rest of the PROG is a sequence of non-atomic statements (forms) and litatoms (labels). The forms are evaluated sequentially; the labels serve only as markers. The two special functions GO and RETURN alter this flow of control as described below. The value of the PROG is usually specified by the function RETURN. If no RETURN is executed before the PROG "falls off the end," the value of the PROG is NIL. (GO U) [NLambda NoSpread Function] GO is used to cause a transfer in a PROG. (GO L) will cause the PROG to evaluate forms starting at the label L (GO does not evaluate its argument). A GO can be used at any level in a PROG. If the label is not found, GO will search higher progs within the same function, e.g., (PROG ... A ... (PROG ... (GO A))). If the label is not found in the function in which the PROG appears, an error is generated, UNDEFINED OR ILLEGAL GO. (RETURN X) [Function] A RETURN is the normal exit for a PROG. Its argument is evaluated and is immediately returned the value of the PROG in which it appears. Note: If a GO or RETURN is executed in an interpreted function which is not a PROG, the GO or RETURN will be executed in the last interpreted PROG entered if any, otherwise cause an error. GO or RETURN inside of a compiled function that is not a PROG is not allowed, and will cause an error at compile time. As a corollary, GO or RETURN in a functional argument, e.g., to SORT, will not work compiled. Also, since NLSETQ's and ERSETQ's compile as separate functions, a GO or RETURN cannot be used inside of a compiled NLSETQ or ERSETQ if the corresponding PROG is outside, i.e., above, the NLSETQ or ERSETQ. (LET VARLST E1 E2 ... EN) [Macro] LET is essentially a PROG that can't contain GO's or RETURN's, and whose last form is the returned value. (LET* VARLST E1 E2 ... EN) [Macro] (PROG* VARLST E1 E2 ... EN) [Macro] LET* and PROG* differ from LET and PROG only in that the binding of the bound variables is done "sequentially." Thus (LET* ((A (LIST 5)) (B (LIST A A))) (EQ A (CADR B))) would evaluate to T; whereas the same form with LET might even find A an unbound variable when evaluating (LIST A A). The Iterative Statement 1 The iterative statement(ITERATIVE% STATEMENT NIL Iterative% statement NIL NIL 7) (i.s.) in its various forms permits the user to specify complicated iterative statements in a straightforward and visible manner. Rather than the user having to perform the mental transformations to an equivalent Interlisp form using PROG, MAPC, MAPCAR, etc., the system does it for him. The goal was to provide a robust and tolerant facility which could "make sense" out of a wide class of iterative statements. Accordingly, the user should not feel obliged to read and understand in detail the description of each operator given below in order to use iterative statements. An iterative statement is a form consisting of a number of special words (known as i.s. operators or i.s.oprs), followed by operands. Many i.s.oprs (FOR, DO, WHILE, etc.) are similar to iterative statements in other programming languages; other i.s.oprs (COLLECT, JOIN, IN, etc.) specify useful operations in a Lisp environment. Lower case versions of i.s.oprs (do, collect, etc.) can also be used. Here are some examples of iterative statements: (for X from 1 to 5 do (PRINT 'FOO)) FOO FOO FOO FOO FOO NIL (for X from 2 to 10 by 2 collect (TIMES X X)) (4 16 36 64 100) (for X in '(A B 1 C 6.5 NIL (45)) count (NUMBERP X)) 2 Iterative statements are implemented through CLISP, which translates the form into the appropriate PROG, MAPCAR, etc. Iterative statement forms are translated using all CLISP declarations in effect (standard/fast/undoable/ etc.); see Chapter 21. Misspelled i.s.oprs are recognized and corrected using the spelling list CLISPFORWORDSPLST. The order of appearance of operators is never important; CLISP scans the entire statement before it begins to construct the equivalent Interlisp form. New i.s.oprs can be defined as described in the Defining New Iterative Statement Operators section in this chapter. If the user defines a function by the same name as an i.s.opr (WHILE, TO, etc.), the i.s.opr will no longer have the CLISP interpretation when it appears as CAR of a form, although it will continue to be treated as an i.s.opr if it appears in the interior of an iterative statement. To alert the user, a warning message is printed, e.g., (WHILE DEFINED, THEREFORE DISABLED IN CLISP). I.S. Types(I.S.% TYPES NIL I.S.% Types NIL NIL 8) The following i.s.oprs are examples of a certain kind of iterative statement operator called an i.s.type. The i.s.type specifies what is to be done at each iteration. Its operand is called the "body" of the iterative statement. Each iterative statement must have one and only one i.s.type. DO FORM(DO% FORM (I.S. Operator) NIL NIL NIL 8) [I.S. Operator] Specifies what is to be done at each iteration. DO with no other operator specifies an infinite loop. If some explicit or implicit terminating condition is specified, the value of the i.s. is NIL. Translates to MAPC or MAP whenever possible. COLLECT FORM(COLLECT% FORM (I.S. Operator) NIL NIL NIL 8) [I.S. Operator] Specifies that the value of FORM at each iteration is to be collected in a list, which is returned as the value of the i.s. when it terminates. Translates to MAPCAR, MAPLIST or SUBSET whenever possible. When COLLECT translates to a PROG (e.g., if UNTIL, WHILE, etc. appear in the i.s.), the translation employs an open TCONC using two pointers similar to that used by the compiler for compiling MAPCAR. To disable this translation, perform (CLDISABLE 'FCOLLECT). JOIN FORM (JOIN% FORM% (I.S. Operator) NIL NIL NIL 8) [I.S. Operator] Similar to COLLECT, except that the values of FORM at each iteration are NCONCed. Translates to MAPCONC or MAPCON whenever possible. /NCONC, /MAPCONC, and /MAPCON are used when the CLISP declaration UNDOABLE is in effect. SUM FORM(SUM% FORM (I.S. Operator) NIL NIL NIL 8) [I.S. Operator] Specifies that the values of FORM at each iteration be added together and returned as the value of the i.s., e.g., (for I from 1 to 5 sum (TIMES I I)) returns 1+4+9+16+25 = 55. IPLUS, FPLUS, or PLUS will be used in the translation depending on the CLISP declarations in effect. COUNT FORM(COUNT% FORM (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] Counts the number of times that FORM is true, and returns that count as its value. ALWAYS FORM(ALWAYS% FORM (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] Returns T if the value of FORM is non-NIL for all iterations. (Note: returns NIL as soon as the value of FORM is NIL). NEVER FORM(NEVER% FORM (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] Similar to ALWAYS, except returns T if the value of FORM is never true. (Note: returns NIL as soon as the value of FORM is non-NIL). The following i.s.types explicitly refer to the iteration variable (i.v.) of the iterative statement, which is a variable set at each iteration. This is explained below under FOR. THEREIS FORM(THEREIS% FORM (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] Returns the first value of the i.v. for which FORM is non-NIL, e.g., (for X in Y thereis (NUMBERP X)) returns the first number in Y. (Note: returns the value of the i.v. as soon as the value of FORM is non-NIL). LARGEST FORM(LARGEST% FORM (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] SMALLEST FORM(SMALLEST% FORM (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] Returns the value of the i.v. that provides the largest/smallest value of FORM. $$EXTREME is always bound to the current greatest/smallest value, $$VAL to the value of the i.v. from which it came. Iteration Variable I.s.oprs FOR VAR(FOR% VAR (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] Specifies the iteration variable (i.v.) which is used in conjunction with IN, ON, FROM, TO, and BY. The variable is rebound within the i.s., so the value of the variable outside the i.s. is not effected. Example: (SETQ X 55) 55 (for X from 1 to 5 collect (TIMES X X)) (1 4 9 16 25) (X 55 FOR VARS(FOR% VARS (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] VARS a list of variables, e.g., (for (X Y Z) in ...). The first variable is the i.v., the rest are dummy variables. See BIND below. FOR OLD VAR(FOR% OLD% VAR (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] Similar to FOR, except that VAR is not rebound within the i.s., so the value of the i.v. outside of the i.s. is changed. Example: (SETQ X 55) 55 (for old X from 1 to 5 collect (TIMES X X)) (1 4 9 16 25) X 6 BIND VAR(BIND% VAR (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] BIND VARS(BIND% VARS (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Used to specify dummy variables, which are bound locally within the i.s. Note: FOR, FOR OLD, and BIND variables can be initialized by using the form VAR_FORM: (for old (XFORM) bind (YFORM) ...) IN FORM(IN% FORM (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Specifies that the i.s. is to iterate down a list with the i.v. being reset to the corresponding element at each iteration. For example, (for X in Y do ...) corresponds to (MAPC Y (FUNCTION (LAMBDA (X) ...))). If no i.v. has been specified, a dummy is supplied, e.g., (in Y collect CADR) is equivalent to (MAPCAR Y (FUNCTION CADR)). ON FORM(ON% FORM (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Same as IN except that the i.v. is reset to the corresponding tail at each iteration. Thus IN corresponds to MAPC, MAPCAR, and MAPCONC, while ON corresponds to MAP, MAPLIST, and MAPCON. Note: For both IN and ON, FORM is evaluated before the main part of the i.s. is entered, i.e. outside of the scope of any of the bound variables of the i.s. For example, (for X bind (Y_'(1 2 3)) in Y ...) will map down the list which is the value of Y evaluated outside of the i.s., not (1 2 3). IN OLD VAR(IN% OLD% VAR (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Specifies that the i.s. is to iterate down VAR, with VAR itself being reset to the corresponding tail at each iteration, e.g., after (for X in old L do ... until ...) finishes, L will be some tail of its original value. IN OLD (VAR_FORM)(IN% OLD% %(VAR_FORM%) (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Same as IN OLD VAR, except VAR is first set to value of FORM. ON OLD VAR(ON% OLD% VAR (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Same as IN OLD VAR except the i.v. is reset to the current value of VAR at each iteration, instead of to (CAR VAR). ON OLD (VAR_FORM)(ON% OLD% %(VAR_FORM%) (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Same as ON OLD VAR, except VAR is first set to value of FORM. INSIDE FORM(INSIDE% FORM (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Similar to IN, except treats first non-list, non-NIL tail as the last element of the iteration, e.g., INSIDE '(A B C D . E) iterates five times with the i.v. set to E on the last iteration. INSIDE 'A is equivalent to INSIDE '(A), which will iterate once. FROM FORM(FROM% FORM (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Used to specify an initial value for a numerical i.v. The i.v. is automatically incremented by 1 after each iteration (unless BY is specified). If no i.v. has been specified, a dummy i.v. is supplied and initialized, e.g., (from 2 to 5 collect SQRT) returns (1.414 1.732 2.0 2.236). TO FORM(TO% FORM (I.S. Operator) NIL NIL NIL 11) [I.S. Operator] Used to specify the final value for a numerical i.v. If FROM is not specified, the i.v. is initialized to 1. If no i.v. has been specified, a dummy i.v. is supplied and initialized. If BY is not specified, the i.v. is automatically incremented by 1 after each iteration. When the i.v. is definitely being incremented, i.e., either BY is not specified, or its operand is a positive number, the i.s. terminates when the i.v. exceeds the value of FORM. Similarly, when the i.v. is definitely being decremented the i.s. terminates when the i.v. becomes less than the value of FORM (see description of BY). Note: FORM is evaluated only once, when the i.s. is first entered, and its value bound to a temporary variable against which the i.v. is checked each interation. If the user wishes to specify an i.s. in which the value of the boundary condition is recomputed each iteration, he should use WHILE or UNTIL instead of TO. Note: When both the operands to TO and FROM are numbers, and TO's operand is less than FROM's operand, the i.v. is decremented by 1 after each iteration. In this case, the i.s. terminates when the i.v. becomes less than the value of FORM. For example, (from 10 to 1 do PRINT) prints the numbers from 10 down to 1. BY FORM (with IN/ON) (BY% FORM% %(WITH% IN/ON%)% (I.S. Operator) BY% FORM% %(with% IN/ON%)% NIL NIL 11) [I.S. Operator] If IN or ON have been specified, the value of FORM determines the tail for the next iteration, which in turn determines the value for the i.v. as described earlier, i.e., the new i.v. is CAR of the tail for IN, the tail itself for ON. In conjunction with IN, the user can refer to the current tail within FORM by using the i.v. or the operand for IN/ON, e.g., (for Z in L by (CDDR Z) ...) or (for Z in L by (CDDR L) ...). At translation time, the name of the internal variable which holds the value of the current tail is substituted for the i.v. throughout FORM. For example, (for X in Y by (CDR (MEMB 'FOO (CDR X))) collect X) specifies that after each iteration, CDR of the current tail is to be searched for the atom FOO, and (CDR of) this latter tail to be used for the next iteration. BY FORM (without IN/ON) (BY% FORM% %(WITHOUT% IN/ON%)% (I.S. Operator) BY% FORM% %(without% IN/ON%)% NIL NIL 11) [I.S. Operator] If IN or ON have not been used, BY specifies how the i.v. itself is reset at each iteration. If FROM or TO have been specified, the i.v. is known to be numerical, so the new i.v. is computed by adding the value of FORM (which is reevaluated each iteration) to the current value of the i.v., e.g., (for N from 1 to 10 by 2 collect N) makes a list of the first five odd numbers. If FORM is a positive number (FORM itself, not its value, which in general CLISP would have no way of knowing in advance), the i.s. terminates when the value of the i.v. exceeds the value of TO's operand. If FORM is a negative number, the i.s. terminates when the value of the i.v. becomes less than TO's operand, e.g., (for I from N to M by -2 until (LESSP I M) ...). Otherwise, the terminating condition for each iteration depends on the value of FORM for that iteration: if FORM<0, the test is whether the i.v. is less than TO's operand, if FORM>0 the test is whether the i.v. exceeds TO's operand, otherwise if FORM=0, the i.s. terminates unconditionally. If FROM or TO have not been specified and FORM is not a number, the i.v. is simply reset to the value of FORM after each iteration, e.g., (for I from N by M ...) is equivalent to (for I_N by (PLUS I M) ...). AS VAR(AS% VAR (I.S. Operator) NIL NIL NIL 12) [I.S. Operator] Used to specify an iterative statement involving more than one iterative variable, e.g., (for X in Y as U in V do ...) corresponds to MAP2C. The i.s. terminates when any of the terminating conditions are met, e.g., (for X in Y as I from 1 to 10 collect X) makes a list of the first ten elements of Y, or however many elements there are on Y if less than 10. The operand to AS, VAR, specifies the new i.v. For the remainder of the i.s., or until another AS is encountered, all operators refer to the new i.v. For example, (for I from 1 to N1 as J from 1 to N2 by 2 as K from N3 to 1 by -1 ...) terminates when I exceeds N1, or J exceeds N2, or K becomes less than 1. After each iteration, I is incremented by 1, J by 2, and K by -1. OUTOF FORM(OUTOF% FORM (I.S. Operator) NIL NIL NIL 12) [I.S. Operator] For use with generators. On each iteration, the i.v. is set to successive values returned by the generator. The i.s. terminates when the generator runs out. Condition I.S. Oprs WHEN FORM(WHEN% FORM (I.S. Operator) NIL NIL NIL 12) [I.S. Operator] Provides a way of excepting certain iterations. For example, (for X in Y collect X when (NUMBERP X)) collects only the elements of Y that are numbers. UNLESS FORM(UNLESS% FORM (I.S. Operator) NIL NIL NIL 12) [I.S. Operator] Same as WHEN except for the difference in sign, i.e., WHEN Z is the same as UNLESS (NOT Z). WHILE FORM(WHILE% FORM (I.S. Operator) NIL NIL NIL 12) [I.S. Operator] Provides a way of terminating the i.s. WHILE FORM evaluates FORM before each iteration, and if the value is NIL, exits. UNTIL FORM(UNTIL% FORM (I.S. Operator) NIL NIL NIL 12) [I.S. Operator] Same as WHILE except for difference in sign, i.e., WHILE X is equivalent to UNTIL (NOT X). UNTIL N (N a number)(UNTIL% N% %(N% A% NUMBER%) (I.S. Operator) UNTIL% N% %(N% a% number%) NIL NIL 12) [I.S. Operator] Equivalent to UNTIL I.V. > N. REPEATWHILE FORM(REPEATWHILE% FORM (I.S. Operator) NIL NIL NIL 12) [I.S. Operator] Same as WHILE except the test is performed after the evalution of the body, but before the i.v. is reset for the next iteration. REPEATUNTIL FORM(REPEATUNTIL% FORM (I.S. Operator) NIL NIL NIL 12) [I.S. Operator] Same as UNTIL, except the test is performed after the evaluation of the body. REPEATUNTIL N (N a number)(REPEATUNTIL% N% %(N% A% NUMBER%) (I.S. Operator) REPEATUNTIL% N% %(N% a% number%) NIL NIL 12) [I.S. Operator] Equivalent to REPEATUNTIL I.V. > N. Other I.S. Oprs FIRST FORM(FIRST% FORM (I.S. Operator) NIL NIL NIL 13) [I.S. Operator] FORM is evaluated once before the first iteration, e.g., (for X Y Z in L first (FOO Y Z) ...), and FOO could be used to initialize Y and Z. FINALLY FORM(FINALLY% FORM (I.S. Operator) NIL NIL NIL 13) [I.S. Operator] FORM is evaluated after the i.s. terminates. For example, (for X in L bind Y_0 do (if (ATOM X) then (SETQ Y (PLUS Y 1))) finally (RETURN Y)) will return the number of atoms in L. EACHTIME FORM(EACHTIME% FORM (I.S. Operator) NIL NIL NIL 13) [I.S. Operator] FORM is evaluated at the beginning of each iteration before, and regardless of, any testing. For example, consider, (for I from 1 to N do (... (FOO I) ...) unless (... (FOO I) ...) until (... (FOO I) ...)) The user might want to set a temporary variable to the value of (FOO I) in order to avoid computing it three times each iteration. However, without knowing the translation, he would not know whether to put the assignment in the operand to DO, UNLESS, or UNTIL, i.e., which one would be executed first. He can avoid this problem by simply writing EACHTIME (SETQ J (FOO I)). DECLARE: DECL(DECLARE:% DECL (I.S. Operator) NIL NIL NIL 13) [I.S. Operator] Inserts the form (DECLARE DECL) immediately following the PROG variable list in the translation, or, in the case that the translation is a mapping function rather than a PROG, immediately following the argument list of the lambda expression in the translation. This can be used to declare variables bound in the iterative statement to be compiled as local or special variables. For example (for X in Y declare: (LOCALVARS X) ...). Several DECLARE:s can apppear in the same i.s.; the declarations are inserted in the order they appear. DECLARE DECL(DECLARE% DECL (I.S. Operator) NIL NIL NIL 13) [I.S. Operator] Same as DECLARE:. Since DECLARE is also the name of a function, DECLARE cannot be used as an i.s. operator when it appears as CAR of a form, i.e. as the first i.s. operator in an iterative statement. However, declare (lowercase version) can be the first i.s. operator. ORIGINAL I.S.OPR OPERAND(ORIGINAL% I.S.OPR% OPERAND (I.S. Operator) NIL NIL NIL 13) [I.S. Operator] I.S.OPR will be translated using its original, built-in interpretation, independent of any user defined i.s. operators. There are also a number of i.s.oprs that make it easier to create iterative statements that use the clock, looping for a given period of time. See timers, Chapter 12. Miscellaneous Hints on I.S.Oprs f Lowercase versions of all i.s. operators are equivalent to the uppercase, e.g., (for X in Y ...) is equivalent to (FOR X IN Y ...). f Each i.s. operator is of lower precedence than all Interlisp forms, so parentheses around the operands can be omitted, and will be supplied where necessary, e.g., BIND (X Y Z) can be written BIND X Y Z, OLD (X_FORM) as OLD X_FORM, WHEN (NUMBERP X) as WHEN NUMBERP X, etc. f RETURN or GO may be used in any operand. (In this case, the translation of the iterative statement will always be in the form of a PROG, never a mapping function.) RETURN means return from the i.s. (with the indicated value), not from the function in which the i.s appears. GO refers to a label elsewhere in the function in which the i.s. appears, except for the labels $$LP, $$ITERATE, and $$OUT which are reserved, as described below. f In the case of FIRST, FINALLY, EACHTIME, DECLARE: or one of the i.s.types, e.g., DO, COLLECT, SUM, etc., the operand can consist of more than one form, e.g., COLLECT (PRINT (CAR X)) (CDR X), in which case a PROGN is supplied. f Each operand can be the name of a function, in which case it is applied to the (last) i.v., e.g., (for X in Y do PRINT when NUMBERP) is the same as (for X in Y do (PRINT X) when (NUMBERP X)). Note that the i.v. need not be explicitly specified, e.g., (in Y do PRINT when NUMBERP) will work. For i.s.types, e.g., DO, COLLECT, JOIN, the function is always applied to the first i.v. in the i.s., whether explicity named or not. For example, (in Y as I from 1 to 10 do PRINT) prints elements on Y, not integers between 1 and 10. Note that this feature does not make much sense for FOR, OLD, BIND, IN, or ON, since they "operate" before the loop starts, when the i.v. may not even be bound. In the case of BY in conjunction with IN, the function is applied to the current tail e.g., (for X in Y by CDDR ...) is the same as (for X in Y by (CDDR X) ...). f While the exact form of the translation of an iterative statement depends on which operators are present, a PROG will always be used whenever the i.s. specifies dummy variables, i.e., if a BIND operator appears, or there is more than one variable specified by a FOR operator, or a GO, RETURN, or a reference to the variable $$VAL appears in any of the operands. When a PROG is used, the form of the translation is: (PROG VARIABLES {initialize} $$LP {eachtime} {test} {body} $$ITERATE {aftertest} {update} (GO $$LP) $$OUT {finalize} (RETURN $$VAL)) where {test} corresponds to that portion of the loop that tests for termination and also for those iterations for which {body} is not going to be executed, (as indicated by a WHEN or UNLESS); {body} corresponds to the operand of the i.s.type, e.g., DO, COLLECT, etc.; {aftertest} corresponds to those tests for termination specified by REPEATWHILE or REPEATUNTIL; and {update} corresponds to that part that resets the tail, increments the counter, etc. in preparation for the next iteration. {initialize}, {finalize}, and {eachtime} correspond to the operands of FIRST, FINALLY, and EACHTIME, if any. Note that since {body} always appears at the top level of the PROG, the user can insert labels in {body}, and GO to them from within {body} or from other i.s. operands, e.g., (for X in Y first (GO A) do (FOO) A (FIE)). However, since {body} is dwimified as a list of forms, the label(s) should be added to the dummy variables for the iterative statement in order to prevent their being dwimified and possibly "corrected", e.g., (for X in Y bind A first (GO A) do (FOO) A (FIE)). The user can also GO to $$LP, $$ITERATE, or $$OUT, or explicitly set $$VAL. Errors in Iterative Statements An error will be generated and an appropriate diagnostic printed if any of the following conditions hold: 1. Operator with null operand, i.e., two adjacent operators, as in (for X in Y until do ...) 2. Operand consisting of more than one form (except as operand to FIRST, FINALLY, or one of the i.s.types), e.g., (for X in Y (PRINT X) collect ...). 3. IN, ON, FROM, TO, or BY appear twice in same i.s. 4. Both IN and ON used on same i.v. 5. FROM or TO used with IN or ON on same i.v. 6. More than one i.s.type, e.g., a DO and a SUM. In 3, 4, or 5, an error is not generated if an intervening AS occurs. If an error occurs, the i.s. is left unchanged. If no DO, COLLECT, JOIN or any of the other i.s.types are specified, CLISP will first attempt to find an operand consisting of more than one form, e.g., (for X in Y (PRINT X) when ATOM X ...), and in this case will insert a DO after the first form. (In this case, condition 2 is not considered to be met, and an error is not generated.) If CLISP cannot find such an operand, and no WHILE or UNTIL appears in the i.s., a warning message is printed: NO DO, COLLECT, OR JOIN: followed by the i.s. Similarly, if no terminating condition is detected, i.e., no IN, ON, WHILE, UNTIL, TO, or a RETURN or GO, a warning message is printed: POSSIBLE NON-TERMINATING ITERATIVE STATEMENT: followed by the iterative statement. However, since the user may be planning to terminate the i.s. via an error, Control-E, or a RETFROM from a lower function, the i.s. is still translated. Note: The error message is not printed if the value of CLISPI.S.GAG is T (initially NIL). Defining New Iterative Statement Operators The following function is available for defining new or redefining existing iterative statement operators: (I.S.OPR NAME FORM OTHERS EVALFLG)(I.S.OPR% NAME% FORM% OTHERS% EVALFLG%) (Function) %(I.S.OPR% NAME% FORM% OTHERS% EVALFLG%) NIL NIL 15) [Function] NAME is the name of the new i.s.opr. If FORM is a list, NAME will be a new i.s.type, and FORM its body. OTHERS is an (optional) list of additional i.s. operators and operands which will be added to the i.s. at the place where NAME appears. If FORM is NIL, NAME is a new i.s.opr defined entirely by OTHERS. In both FORM and OTHERS, the atom $$VAL can be used to reference the value to be returned by the i.s., I.V. to reference the current i.v., and BODY to reference NAME's operand. In other words, the current i.v. will be substituted for all instances of I.V. and NAME's operand will be substituted for all instances of BODY throughout FORM and OTHERS. If EVALFLG is T, FORM and OTHERS are evaluated at translation time, and their values used as described above. A dummy variable for use in translation that does not clash with a dummy variable already used by some other i.s. operators can be obtained by calling (GETDUMMYVAR). (GETDUMMYVAR T) will return a dummy variable and also insure that it is bound as a PROG variable in the translation. If NAME was previously an i.s.opr and is being redefined, the message (NAME REDEFINED) will be printed (unless DFNFLG=T), and all expressions using the i.s.opr NAME that have been translated will have their translations discarded. The following are some examples of how I.S.OPR could be called to define some existing i.s.oprs, and create some new ones: COLLECT (I.S.OPR 'COLLECT '(SETQ $$VAL (NCONC1 $$VAL BODY))) SUM (I.S.OPR 'SUM '(SETQ $$VAL_ (PLUS $$VAL BODY) '(FIRST (SETQ $$VAL0)) NEVER (I.S.OPR 'NEVER '(if BODY then (SETQ $$VAL NIL) (GO $$OUT)) Note: (if BODY then (RETURN NIL)) would exit from the i.s. immediately and therefore not execute the operations specified via a FINALLY (if any). THEREIS (I.S.OPR 'THEREIS '(if BODY then (SETQ $$VAL I.V.) (GO $$OUT))) RCOLLECT To define RCOLLECT, a version of COLLECT which uses CONS instead of NCONC1 and then reverses the list of values: (I.S.OPR 'RCOLLECT '(SETQ $$VAL (CONS BODY $$VAL)) '(FINALLY (RETURN (DREVERSE $$VAL)))] TCOLLECT To define TCOLLECT, a version of COLLECT which uses TCONC: (I.S.OPR 'TCOLLECT '(TCONC $$VAL BODY) '(FIRST (SETQ $$VAL (CONS)) FINALLY (RETURN (CAR $$VAL)))] PRODUCT (I.S.OPR 'PRODUCT '(SETQ $$VAL $$VAL*BODY) '(FIRST ($$VAL 1))] UPTO To define UPTO, a version of TO whose operand is evaluated only once: (I.S.OPR 'UPTO NIL '(BIND $$FOOBODY TO $$FOO)] TO To redefine TO so that instead of recomputing FORM each iteration, a variable is bound to the value of FORM, and then that variable is used: (I.S.OPR 'TO NIL '(BIND $$END FIRST (SETQ $$END BODY) ORIGINAL TO $$END)] Note the use of ORIGINAL to redefine TO in terms of its original definition. ORIGINAL is intended for use in redefining built-in operators, since their definitions are not accessible, and hence not directly modifiable. Thus if the operator had been defined by the user via I.S.OPR, ORIGINAL would not obtain its original definition. In this case, one presumably would simply modify the i.s.opr definition. I.S.OPR can also be used to define synonyms for already defined i.s. operators by calling I.S.OPR with FORM an atom, e.g., (I.S.OPR 'WHERE 'WHEN) makes WHERE be the same as WHEN. Similarly, following (I.S.OPR 'ISTHERE 'THEREIS), one can write (ISTHERE ATOM IN Y), and following (I.S.OPR 'FIND 'FOR) and (I.S.OPR 'SUCHTHAT 'THEREIS), one can write (find X in Y suchthat X member Z). In the current system, WHERE is synonymous with WHEN, SUCHTHAT and ISTHERE with THEREIS, FIND with FOR, and THRU with TO. If FORM is the atom MODIFIER, then NAME is defined as an i.s.opr which can immediately follow another i.s. operator (i.e., an error will not be generated, as described previously). NAME will not terminate the scope of the previous operator, and will be stripped off when DWIMIFY is called on its operand. OLD is an example of a MODIFIER type of operator. The MODIFIER feature allows the user to define i.s. operators similar to OLD, for use in conjunction with some other user defined i.s.opr which will produce the appropriate translation. The file package command I.S.OPRS (Chapter 17) will dump the definition of i.s.oprs. (I.S.OPRS PRODUCT UPTO) as a file package command will print suitable expressions so that these iterative statement operators will be (re)defined when the file is loaded. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))03HT +T-llT,,,,,2l2ll2l~~3Hl +lT55,2HZZ,HH,ll2Hll2l +,ll,ll0$$T3HHT-T,22HT +T2HT +T,ll +,HH/HH/ll,ll3HH +T,HH +3(T,HHF PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR5,,3KT-T,2TITAN +TITAN +CLASSIC + HELVETICA TITAN +CLASSIC +CLASSIC + HELVETICA HELVETICA HELVETICA +CLASSIC +MODERN +MODERN +MODERNMODERN +) HRULE.GETFNCLASSIC + ) + ( HRULE.GETFNCLASSIC + ( +') + ' HRULE.GETFNCLASSIC + &* + & HRULE.GETFNCLASSIC + 0+ HRULE.GETFNMODERN +% $  HRULE.GETFNMODERN +#B  ""IM.INDEX.GETFN !   3 "!IM.INDEX.GETFN !   X "IM.INDEX.GETFN !    "!IM.INDEX.GETFN !    ""IM.INDEX.GETFN !      "IM.INDEX.GETFN !  *  ! +   C 1  " IM.INDEX.GETFN !  ,   "!IM.INDEX.GETFN !    ""IM.INDEX.GETFN !    " !    C !IM.INDEX.GETFN    "  ! )  !"IM.INDEX.GETFN    <   A  +   W  $ . HRULE.GETFNMODERN +#3 "IM.INDEX.GETFN !     P  "IM.INDEX.GETFN / "IM.INDEX.GETFN + IM.INDEX.GETFN /IM.INDEX.GETFN !      % $ , !Y " IM.INDEX.GETFN !     +   + &  + ?  +           !  "  !    ! " #IM.INDEX.GETFN ! ^  $  $  HRULE.GETFNMODERN +"FIM.INDEX.GETFN !u   X .  "DIM.INDEX.GETFN !o , T    #   J   &    m $  HRULE.GETFNMODERN +" fIM.INDEX.GETFN !' A     (           !A     .  )    "      8  +  %    ##        +!. 9  $ > U  $  HRULE.GETFNMODERN +#  + +IM.INDEX.GETFNm <    / 3        # !     >        ) 9     %  y  @ Z D        =          $  HRULE.GETFNMODERN +    !:    )          !    3  1 6  !  2      ! 3 P =   !  /(!          ' !V =           !  H  !    8 +!  * +$&  HRULE.GETFNMODERN +" !N      " ! R  "  !X V I  " ! ( * 9  &  : N        9       !   B &   '   "  !"   )  %     "9 !  "  !  I    7   * + ! - : !  $ '            "    !   / "        !   O !   % + $  HRULE.GETFNMODERN + 9IM.INDEX.GETFN   E    ]   [  K  $ . 5c    ?  U  ,  +'IM.INDEX.GETFN +#% - (IM.INDEX.GETFN !1     " -IM.INDEX.GETFN !     !    < G (  "  ,IM.INDEX.GETFN !        %  " )IM.INDEX.GETFN ! R #   P " +IM.INDEX.GETFN  ! / ",IM.INDEX.GETFN !   %    "+IM.INDEX.GETFN !        #  "-IM.INDEX.GETFN !.    @   " -IM.INDEX.GETFN  .IM.INDEX.GETFN  !J  9 .  +" (IM.INDEX.GETFN !J     u   ( " )IM.INDEX.GETFN  ! F  " -IM.INDEX.GETFN  !   ^   , " *IM.INDEX.GETFN  +IM.INDEX.GETFN I    0      "  )IM.INDEX.GETFN   $=   " )IM.INDEX.GETFN  4              @ E #.     " -IM.INDEX.GETFN +  L " * " 6IM.INDEX.GETFN     " -IM.INDEX.GETFN  2 "  " 6IM.INDEX.GETFN     " -IM.INDEX.GETFN  $ 2 *    " +IM.INDEX.GETFN  `   " )IM.INDEX.GETFN 9  w  o f             x   ' "   TIM.INDEX.GETFN   #  u    0 &     3& 4  9 "   ZIM.INDEX.GETFN    ?  l O #-      N   (R  .  (  )    ;    " (IM.INDEX.GETFN X  L (+ (    I B G     -    " ,IM.INDEX.GETFN   +" +IM.INDEX.GETFN > '  " -IM.INDEX.GETFN  *   " ,IM.INDEX.GETFN (   %  " ,IM.INDEX.GETFN  &   "   RIM.INDEX.GETFN   "  2IM.INDEX.GETFN  t "  2IM.INDEX.GETFN  A "    ^IM.INDEX.GETFN   " ,IM.INDEX.GETFN 5 $    " .IM.INDEX.GETFN 6 R$  " /IM.INDEX.GETFN q @    X  " /IM.INDEX.GETFN   l  ' X " .IM.INDEX.GETFN    ! 7 Q   " ;IM.INDEX.GETFN r #  + S     +        x  8 . ^   )        =    e " *>     m " ! 5     T   )     o M E   ! ) *     +   9  L      +  (   ? *  1       +#j ] C  " "               #   ;  0     &!   4   \   - o    * +#k - gIM.INDEX.GETFN  %      t    &      @ $  W  4           D    ?  +  ) C ' M * ,+'*,+$+*,+0 _  *,+2     ' ,+$+*  +   ,++J*,++  +  ' ,++    5 " , ++F  '   u S          !              V    = n  5   z \ No newline at end of file diff --git a/docs/porter-irm/13-EXECUTIVE.TEDIT b/docs/porter-irm/13-EXECUTIVE.TEDIT new file mode 100644 index 00000000..ef855cce --- /dev/null +++ b/docs/porter-irm/13-EXECUTIVE.TEDIT @@ -0,0 +1,337 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 13. INTERLISP EXECUTIVE 1 13. INTERLISP EXECUTIVE 1 13. INTERLISP EXECUTIVE 6 With any interactive computer language, you interact with the system through an "executive", which interprets and executes typed-in commands. In most implementations of Lisp, the executive is a simple "read-eval-print" loop, which repeatedly reads a Lisp expression, evaluates it, and prints out the value of the expression. Interlisp has an executive which allows a much greater range of inputs, other than just regular Interlisp expressions. In particular, the Interlisp executive implements a facility known as the "programmer's assistant" (or "p.a."). The central idea of the programmer's assistant is that you are addressing an active intermediary, namely your assistant. Normally, the assistant is invisible, and simply carries out your requests. However, the assistant remembers what you have done, so you can give commands to repeat a particular operation or sequence of operations, with possible modifications, or to undo the effect of specified operations. Like DWIM, the programmer's assistant embodies an approach to system design whose ultimate goal is to construct an environment that "cooperates" with you in the development of your programs, and frees you to concentrate more fully on the conceptual difficulties and creative aspects of the problem at hand. The programmer's assistant facility features the use of memory structures called "history lists." A history list is a list of the information associated with each of the individual "events" that have occurred in the system, where each event corresponds to one user input. Associated with each event on the history list is the input and its value, plus other optional information such as side-effects, formatting information, etc. The following dialogue, taken from an actual session at the terminal, contains illustrative examples and gives the flavor of the programmer's assistant facility in Interlisp. The number before each prompt is the "event number" (see page X.XX). 12(SETQ FOO 5) 5 13(SETQ FOO 10) (FOO reset) 10 The p.a. notices that the user has reset the value of FOO and informs the user. 14UNDO SETQ undone. 15FOOcr 5 This is the first example of direct communication with the p.a. The user has said to UNDO the previous input to the executive. . . . 25SET(LST1 (A B C)) (A B C) 26(SETQ LST2 '(D E F)) (D E F) 27(FOR X IN LST1 DO (REMPROP X 'MYPROP] NIL The user asked to remove the property MYPROP from the atoms A, B, and C. Now lets assume that is not what he wanted to do, but rather use the elements of LST2. 28UNDO FOR FOR undone. First he undoes the REMPROP, by undoing the iterative statement. Notice the UNDO accepted an "argument," although in this case UNDO by itself would be sufficient. 29USE LST2 FOR LST1 IN 27 NIL The user just instructed to go back to event number 27 and substitute LST2 for LST1 and then reexecute the expression. The user could have also specified -2 instead of 27 to specify a relative address. . . . 47(PUTHASH 'FOO (MKSTRING 'FOO) MYHASHARRAY) "FOO" If MKSTRING was a computationally expensive function (which it is not), then the user might be cacheing its value for later use. 48USE FIE FUM FOE FOR FOO IN MKSTRING "FIE" "FUM" "FOE" The user now decides he would like to redo the PUTHASH several times with different values. He specifies the event by "IN MKSTRING" rather than PUTHASH. 49?? USE 48. USE FIE FUM FOE FOR FOO IN MKSTRING (PUTHASH (QUOTE FIE) (MKSTRING (QUOTE FIE)) MYHASHARRAY) "FIE" (PUTHASH (QUOTE FUM) (MKSTRING (QUOTE FUM)) MYHASHARRAY) "FUM" (PUTHASH (QUOTE FOE) (MKSTRING (QUOTE FOE)) MYHASHARRAY) "FOE" Here we see the user ask the p.a. (using the ?? command) what it has on its history list for the last input to the executive. Since the event corresponds to a programmer's assistant command that evaluates several forms, these forms are saved as the input, although the user's actual input, the p.a. command, is also saved in order to clarify the printout of that event. As stated earlier, the most common interaction with the programmer's assistant occurs at the top level read-eval-print loop, or in a break, where the user types in expressions for evaluation, and sees the values printed out. In this mode, the assistant acts much like a standard Lisp executive, except that before attempting to evaluate an input, the assistant first stores it in a new entry on the history list. Thus if the operation is aborted or causes an error, the input is still saved and available for modification and/or reexecution. The assistant also notes new functions and variables to be added to its spelling lists to enable future corrections. Then the assistant executes the computation (i.e., evaluates the form or applies the function to its arguments), saves the value in the entry on the history list corresponding to the input, and prints the result, followed by a prompt character to indicate it is again ready for input. If the input typed by the user is recognized as a p.a. command, the assistant takes special action. Commands such as UNDO and ?? are immediately performed. Commands that involved reexecution of previous inputs, such as REDO and USE, are achieved by computing the corresponding input expression(s) and then unreading them. The effect of this unreading operation is to cause the assistant's input routine, LISPXREAD, to act exactly as though these expressions were typed in by the user. These expressions are processed exactly as though they had been typed, except that they are not saved on new and separate entries on the history list, but associated with the history command that generated them. Input Formats 1 The Interlisp-D executive accepts inputs in the following formats: EVALV-format input If the user types a single litatom, followed by a carriage-return, the value of the litatom is returned. For example, if the value of the variable FOO is the list (A B C): FOOcr (A B C) EVAL-format input If the user types a regular Interlisp expression, beginning with a left parenthesis or square bracket and terminated by a matching right parenthesis or square bracket, the form is simply passed to EVAL for evaluation. A right bracket matches any number of left parentheses, back to the last left bracket or the entire expression. Notice that it is not necessary to type a carriage return at the end of such a form; Interlisp will supply one automatically. If a carriage-return is typed before the final matching right parenthesis or bracket, it is treated as a space, and input continues. The following examples are all interpreted the same: (PLUS 1 (TIMES 2 3)) 7 (PLUS 1 (TIMES 2 3] 7 (PLUS 1 (TIMEScr 2 3] 7 APPLY-format input Often, the user, typing at the keyboard, calls functions with constant argument values, which would have to be quoted if the user typed it in "EVAL-format." For convience, if the user types a litatom immediately followed by a list form, the litatom is APPLYed to the elements within the list, unevaluated. The input is terminated by the matching right parenthesis or bracket. For example, LOAD(FOO) is equivalent to typing (LOAD 'FOO), and GETPROP(X COLOR) is equivalent to (GETPROP 'X 'COLOR). APPLY-format input is useful in some situations, but note that it may produce unexpected results when an nlambda function is called that explicitly evaluates its arguments (see Chapter 10). For example, typing SETQ(FOO BAR) will set FOO to the value of BAR, not to the litatom BAR itself. Other input Sometimes, a user does not want to terminate the input when a closing parenthesis is typed. For example, some programmer's assistant commands take several arguments, some of which can be lists. If the user types a sequence of litatoms and lists beginning with a litatom and a space (to distinguish it from APPLY-format), terminated by a carriage return or an extra right parenthesis or bracket, the Interlisp-D executive interprets it differently depending on the number of expressions typed. If only one expression is typed (a litatom), it is interpreted as an EVALV-format input, and the value of the litatom is returned: FOOcr (A B C) If exactly two expressions are typed, it is interpreted as APPLY-format input: LIST (A B)cr (A B) If three or more expressions are typed, it is interpreted as EVAL-format input. To warn the user, the full expression is printed out before it is executed. For example: PLUS (TIMES 2 3) 1cr = (PLUS (TIMES 2 3) 1) 7 Note: If LISPXREADFN (see the Programmer's Assistant Functiions section below) is set to READ (rather than the default, TTYINREAD), then whenever one of the elements typed is a list and the list is terminated by a right parenthesis or bracket, Interlisp will type a carriage-return and "..." to indicate that further input will be accepted. The user can type further expressions or terminate the whole expression by a carriage-return. Programmer's Assistant Commands 1 The programmer's assistant recognizes a number of commands, which usually refer to past events on the history list. These commands are treated specially; for example, they may not be put on the history list. Note: If you define a function by the same name as a p.a. command, a warning message is printed to remind you that the p.a. command interpretation will take precedence for type-in. All programmer's assistant commands use the same conventions and syntax for indicating which event or events on the history list the command refers to, even though different commands may be concerned with different aspects of the corresponding event(s), e.g., side-effects, value, input, etc. Therefore, before discussing the various p.a. commands, the following section describes the types of event specifications currently implemented. Event Specification An event address identifies one event on the history list. It consists of a sequence of "commands" for moving an imaginary cursor up or down the history list, much in the manner of the arguments to the @ break command (see Chapter 14). The event identified is the one "under" the imaginary cursor when there are no more commands. (If any command fails, an error is generated and the history command is aborted.) For example, the event address 42 refers to the event with event number 42, 42 FOO refers to the first event (searching back from event 42) whose input contains the word FOO, and 42 FOO -1 refers to the event preceeding that event. Usually, an event address will contain only one or two commands. Most of the event address commands perform searches for events which satisfy some condition. Unless the command is given (see below), this search always goes backwards through the history list, from the most recent event specified to the oldest. Each search skips the current event. For example, if FOO refers to event N, FOO FIE will refer to some event before event N, even if there is a FIE in event N. The event address commands are interpreted as follows: N (an integer) If N is the first command in an event address, refers to the event with event number N. Otherwise, refers to the event N events forward (in direction of increasing event number). If N is negative, it always refers to the event -N events backwards. For example, -1 refers to the previous event, 42 refers to event number 42 (if the first command in an event address), and 42 3 refers to the event with event number 45. LITATOM Specifies the last event with an APPLY-format input whose function matches LITATOM. Note: There must not be a space between and LITATOM. Specifies that the next search is to go forward instead of backward. If given as the first event address command, the next search begins with last (oldest) event on the history list. F Specifies that the next object in the event address is to be searched for, regardless of what it is. For example, F -2 looks for an event containing -2. = Specifies that the next object (presumably a pattern) is to be matched against the values of events, instead of the inputs. \ Specifies the event last located. SUCHTHAT PRED Specifies an event for which the function PRED returns true. PRED should be a function of two arguments, the input portion of the event, and the event itself. See the Format and Use of the History List section below for a discussion of the format of events on the history list. PAT Any other event address command specifies an event whose input contains an expression that matches PAT as described in Chapter 16. The matching is performed by the function HISTORYMATCH (see the Programmer's Assistant Functions section below), which is initially defined to call EDITFINDP but can be advised or redefined for specialized applications. Symbols used below of the form EventAddressi refer to event addresses, described above. Since an event address may contain multiple words, the event address is parsed by searching for the words which delimit it. For example, in FROM EventAddress1 THRU EventAddress2, the symbol EventAddress1 corresponds to all words between FROM and THRU in the event specification, and EventAddress2 to all words from THRU to the end of the event specification. FROM EventAddress1 THRU EventAddress2 EventAddress1 THRU EventAddress2 Specifies the sequence of events from the event with address EventAddress1 through the event with address EventAddress2. For example, FROM 47 THRU 49 specifies events 47, 48, and 49. EventAddress1 can be more recent than EventAddress2. For example, FROM 49 THRU 47 specifies events 49, 48, and 47 (note reversal of order). FROM EventAddress1 TO EventAddress2 EventAddress1 TO EventAddress2 Same as THRU but does not include event EventAddress2. FROM EventAddress1 Same as FROM EventAddress1 THRU -1. For example, if the current event is number 53, then FROM 49 specifies events 49, 50, 51, and 52. THRU EventAddress2 Same as FROM -1 THRU EventAddress2. For example, if the current event is number 53, then THRU 49 specifies events 52, 51, 50, and 49 (note reversal of order). TO EventAddress2 Same as FROM -1 TO EventAddress2. ALL EventAddress1 Specifies all events satisfying EventAddress1. For example, ALL LOAD, ALL SUCHTHAT FOO. empty If nothing is specified, it is the same as specifying -1. Note: In the special case that the last event was an UNDO, it is the same as specifying -2. For example, if you type (NCONC FOO FIE), you can then type UNDO, followed by USE NCONC1. EventSpec1 AND EventSpec2 AND ... AND EventSpecN Each of the EventSpeci is an event specification. The lists of events are concatenated. For example, FROM 30 THRU 32 AND 35 THRU 37 is the same as 30 AND 31 AND 32 AND 35 AND 36 AND 37. @ LITATOM If LITATOM is the name of a command defined via the NAME command (see the Commands section below), specifies the event(s) defining LITATOM. @@ EventSpec EventSpec is an event specification interpreted as above, but with respect to the archived history list (see the ARCHIVE command, in the Commands section below). If no events can be found that satisfy the event specification, spelling correction on each word in the event specification is performed using LISPXFINDSPLST as the spelling list. For example, REDO 3 THRUU 6 will work correctly. If the event specification still fails to specify any events after spelling correction, an error is generated. Commands All programmer's assistant commands can be input as list forms, or as lines (see the Preogrammer's Assistant Functions section below). For example, typing REDO 5cr and (REDO 5) are equivalent. EventSpec is used to denote an event specification. Unless specified otherwise, omitting EventSpec is the same as specifying EventSpec=-1. For example, REDO and REDO -1 are the same. REDO EventSpec [Prog. Asst. Command] Redoes the event or events specified by EventSpec. For example, REDO FROM -3 redoes the last three events. REDO EventSpec N TIMES [Prog. Asst. Command] Redoes the event or events specified by EventSpec N times. For example, REDO 10 TIMES redoes the last event ten times. REDO EventSpec WHILE FORM [Prog. Asst. Command] Redoes the specified events as long as the value of FORM is true. FORM is evaluated before each iteration so if its initial value is NIL, nothing will happen. REDO EventSpec UNTIL FORM [Prog. Asst. Command] Same as REDO EventSpec WHILE (NOT FORM). REPEAT EventSpec [Prog. Asst. Command] Same as REDO EventSpec WHILE T. The event(s) are repeated until an error occurs, or the user types Control-E or Control-D. REPEAT EventSpec WHILE FORM [Prog. Asst. Command] REPEAT EventSpec UNTIL FORM [Prog. Asst. Command] Same as REDO. For all history commands that perform multiple repetitions, the variable REDOCNT is initialized to 0 and incremented each iteration. If the event terminates gracefully, i.e., is not aborted by an error or Control-D, the number of iterations is printed. RETRY EventSpec [Prog. Asst. Command] Similar to REDO except sets HELPCLOCK (see Chapter 14) so that any errors that occur while executing EventSpec will cause breaks. USE EXPRS FOR ARGS IN EventSpec [Prog. Asst. Command] Substitutes EXPRS for ARGS in EventSpec, and redoes the result. Substitution is done by ESUBST (see Chapter 16), and is carried out as described below. EXPRS and ARGS can include non-atomic members. For example, USE LOG (MINUS X) FOR ANTILOG X IN -2 AND -1 will substitute LOG for every occurrence of ANTILOG in the previous two events, and substitute (MINUS X) for every occurrence of X, and reexecute them. Note that these substitutions do not change the information saved about these events on the history list. Any expression to be substituted can be preceded by a !, meaning that the expression is to be substituted as a segment, e.g., LIST(A B C) followed by USE ! (X Y Z) FOR B will produce LIST(A X Y Z C), and USE ! NIL FOR B will produce LIST(A C). If IN EventSpec is omitted, the first member of ARGS is used for EventSpec. For example, USE PUTD FOR @UTD is equivalent to USE PUTD FOR @UTD IN F @UTD. The F is inserted to handle correctly the case where the first member of ARGS could be interpreted as an event address command. USE EXPRS IN EventSpec [Prog. Asst. Command] If ARGS are omitted, and the event referred to was itself a USE command, the arguments and expression substituted into are the same as for the indicated USE command. In effect, this USE command is thus a continuation of the previous USE command. For example, following USE X FOR Y IN 50, typing USE Z IN -1 is equivalent to USE Z FOR Y IN 50. If ARGS are omitted and the event referred to was not a USE command, substitution is for the "operator" in that command. For example ARGLIST(FF) followed by USE CALLS IN -1 is equivalent to USE CALLS FOR ARGLIST IN -1. If IN EventSpec is omitted, it is the same as specifying IN -1. USE EXPRS1 FOR ARGS1 AND ... AND EXPRSN FOR ARGSN IN EventSpec [Prog. Asst. Command] More general form of USE command. See description of the substitution algorithm below. Note: The USE command is parsed by a small finite state parser to distinguish the expressions and arguments. For example, USE FOR FOR AND AND AND FOR FOR will be parsed correctly. Every USE command involves three pieces of information: the expressions to be substituted, the arguments to be substituted for, and an event specification, which defines the input expression in which the substitution takes place. If the USE command has the same number of expressions as arguments, the substitution procedure is straightforward. For example, USE X Y FOR U V means substitute X for U and Y for V, and is equivalent to USE X FOR U AND Y FOR V. However, the USE command also permits distributive substitutions, for substituting several expressions for the same argument. For example, USE A B C FOR X means first substitute A for X then substitute B for X (in a new copy of the expression), then substitute C for X. The effect is the same as three separate USE commands. Similarly, USE A B C FOR D AND X Y Z FOR W is equivalent to USE A FOR D AND X FOR W, followed by USE B FOR D AND Y FOR W, followed by USE C FOR D AND Z FOR W. USE A B C FOR D AND X FOR Y also corresponds to three substitutions, the first with A for D and X for Y, the second with B for D, and X for Y, and the third with C for D, and again X for Y. However, USE A B C FOR D AND X Y FOR Z is ambiguous and will cause an error. Essentially, the USE command operates by proceeding from left to right handling each "AND" separately. Whenever the number of expressions exceeds the number of expressions available, multiple USE expressions are generated. Thus USE A B C D FOR E F means substitute A for E at the same time as substituting B for F, then in another copy of the indicated expression, substitute C for E and D for F. This is also equivalent to USE A C FOR E AND B D FOR F. Note: Parsing the USE command gets more complicated when one of the arguments and one of the expressions are the same, e.g., USE X Y FOR Y X, or USE X FOR Y AND Y FOR X. This situation is noticed when parsing the command, and handled correctly. ... VARS [Prog. Asst. Command] Similar to USE except substitutes for the (first) operand. For example, EXPRP(FOO) followed by ... FIE FUM is equivalent to USE FIE FUM FOR FOO. In the following discussion, $ is used to represent the character "escape," since this is how this character is echoed. $ X FOR Y IN EventSpec [Prog. Asst. Command] $ (escape) is a special form of the USE command for conveniently specifying character substitutions in litatoms or strings. In addition, it has a number of useful properties in connection with events that involve errors (see below). Equivalent to USE $X$ FOR $Y$ IN EventSpec, which will do a character substitution of the characters in X for the characters in Y. For example, if you type MOVD(FOO FOOSAVE T), you can then type $ FIE FOR FOO IN MOVD to perform MOVD(FIE FIESAVE T). USE FIE FOR FOO would perform MOVD(FIE FOOSAVE T). $ Y X IN EventSpec [Prog. Asst. Command] $ Y TO X IN EventSpec [Prog. Asst. Command] $ Y = X IN EventSpec [Prog. Asst. Command] $ Y -> X IN EventSpec [Prog. Asst. Command] Abbreviated forms of the $ (escape) command: the same as $ X FOR Y IN EventSpec, which changes Ys to Xs. $ does event location the same as the USE command, i.e., if IN EventSpec is not specified, $ searches for Y. However, unlike USE, $ can only be used to specify one substitution at a time. After $ finds the event, it looks to see if an error was involved in that event, and if the indicated character substitution can be performed in the object of the error message, called the offender. If so, $ assumes the substitution refers to the offender, performs the indicated character substitution in the offender only, and then substitutes the result for the original offender throughout the event. For example, suppose the user types (PRETTYDEF FOOFNS 'FOO FOOOVARS) causing a U.B.A. FOOOVARS error message. The user can now type $ OO O, which will change FOOOVARS to FOOVARS, but not change FOOFNS or FOO. If an error did occur in the specified event, you can also omit specifying the object of the substitution, Y, in which case the offender itself is used. Thus, you could have corrected the above example by simply typing $ FOOVARS. Since ESUBST is used for performing the substitution (see Chapter 16), $ can be used in X to refer to the characters in Y. For example, if you type LOAD(PRSTRUC PROP), causing the error FILE NOT FOUND PRSTRUC, you can request the file to be loaded from LISP's directory by simply typing $ $. This is equivalent to performing (R PRSTRUC $) on the event, and therefore replaces PRSTRUC by PRSTRUC. $ never searches for an error. Thus, if you type LOAD(PRSTRUC PROP) causing a FILE NOT FOUND error, type CLOSEALL(), and then type $ $, LISPX will complain that there is no error in CLOSEALL(). In this case, you would have to type $ $ IN LOAD, or $ PRS PRS (which would cause a search for PRS). $ operates on input, not on programs. If you type FOO(), and within the call to FOO gets a U.D.F. CONDD error, you cannot repair this by $ COND. LISPX will type CONDD NOT FOUND IN FOO(). FIX EventSpec [Prog. Asst. Command] Invokes the default program editor (Dedit or the teletype editor) on a copy of the input(s) for EventSpec. Whenever the user exits via OK, the result is unread and reexecuted exactly as with REDO. FIX is provided for those cases when the modifications to the input(s) are not simple substitutions of the type that can be specified by USE. For example, if the default editor is the teletype editor, then: (DEFINEQ FOO (LAMBDA (X) (FIXSPELL SPELLINGS2 X 70] INCORRECT DEFINING FORM FOO FIX EDIT *P (DEFINEQ FOO (LAMBDA & &)) *(LI 2) *P (DEFINEQ (FOO &)) *OK (FOO) You can also specify the edit command(s) to LISPX, by typing - followed by the command(s) after the event specification, e.g., FIX - (LI 2). In this case, the editor will not type EDIT, or wait for an OK after executing the commands. FIX calls the editor on the "input sequence" of an event, adjusting the editor so it is initially editing the expression typed. However, the entire input sequence is being edited, so it is possible to give editor commands that examine this structure further. For more information on the format of an event's input, see the Format and Use of the History List section below. ?? EventSpec [Prog. Asst. Command] Prints the specified events from the history list. If EventSpec is omitted, ?? prints the entire history list, beginning with most recent events. Otherwise ?? prints only those events specified in EventSpec (in the order specified). For example, ?? -1, ?? 10 THRU 15, etc. For each event specified, ?? prints the event number, the prompt, the input line(s), and the value(s). If the event input was a p.a. command that "unread" some other input lines, the p.a. command is printed without a preceding prompt, to show that they are not stored as the input, and the input lines are printed with prompts. Events are initially stored on the history list with their value field equal to the character "bell" (Control-G). Thefore, if an operation fails to complete for any reason, e.g., causes an error, is aborted, etc., ?? will print a bell as its "value". ?? commands are not entered on the history list, and so do not affect relative event numbers. In other words, an event specification of -1 typed following a ?? command will refer to the event immediately preceding the ?? command. ?? is implemented via the function PRINTHISTORY (see the Programmer's Assistant Functions section below) which can also be called directly by the user. Printing is performed via the function SHOWPRIN2 (see Chapter 25), so that if the value of SYSPRETTYFLG=T, events will be prettyprinted. UNDO EventSpec [Prog. Asst. Command] Undoes the side effects of the specified events. For each event undone, UNDO prints a message: RPLACA undone, REDO undone etc. If nothing is undone because nothing was saved, UNDO types nothing saved. If nothing was undone because the events were already undone, UNDO types already undone. If EventSpec is not given, UNDO searches back for the last event that contained side effects, was not undone, and itself was not an UNDO command. Note that the user can undo UNDO commands themselves by specifying the corresponding event address, e.g., UNDO -7 or UNDO UNDO. To restore all pointers correctly, you should UNDO events in the reverse order from which they were executed. For example, to undo all the side effects of the last five events, perform UNDO THRU -5, not UNDO FROM -5. Undoing out of order may have unforseen effects if the operations are dependent. For example, if you performed (NCONC1 FOO FIE), followed by (NCONC1 FOO FUM), and then undoes the (NCONC1 FOO FIE), he will also have undone the (NCONC1 FOO FUM). If he then undoes the (NCONC1 FOO FUM), he will cause the FIE to reappear, by virtue of restoring FOO to its state before the execution of (NCONC1 FOO FUM). For more details, see the Undoing section below. UNDO EventSpec : X1 ... XN [Prog. Asst. Command] Each Xi is a pattern that is matched to a message printed by DWIM in the event(s) specified by EventSpec. The side effects of the corresponding DWIM corrections, and only those side effects, are undone. For example, if DWIM printed the message PRINTT [IN FOO] -> PRINT, then UNDO : PRINTT or UNDO : PRINT would undo the correction. Some portions of the messages printed by DWIM are strings, e.g., the message FOO UNSAVED is printed by printing FOO and then " UNSAVED". Therefore, if the user types UNDO : UNSAVED, the DWIM correction will not be found. He should instead type UNDO : FOO or UNDO : $UNSAVED$ (UNSAVED, see R command in editor, page X.XX). NAME LITATOM EventSpec [Prog. Asst. Command] Saves the event(s) (including side effects) specified by EventSpec on the property list of LITATOM (under the property HISTORY). For example, NAME FOO 10 THRU 15. NAME commands are undoable. Events saved on a litatom can be retrieved with the event specification @ LITATOM. For example, ?? @ FOO, REDO @ FOO, etc. Commands defined by NAME can also be typed in directly as though they were built-in commands, e.g., FOOcr is equivalent to REDO @ FOO. However, if FOO is the name of a variable, it would be evaluated, i.e., FOOcr would return the value of FOO. Commands defined by NAME can also be defined to take arguments: NAME LITATOM (ARG1 ... ARGN) : EventSpec [Prog. Asst. Command] NAME LITATOM ARG1 ... ARGN : EventSpec [Prog. Asst. Command] The arguments ARGi are interpreted the same as the arguments for a USE command. When LITATOM is invoked, the argument values are substituted for ARG1 ... ARGN using the same substitution algorithm as for USE. NAME FOO EventSpec is equivalent to NAME FOO : EventSpec. In either case, if FOO is invoked with arguments, an error is generated. For example, following the event (PUTD 'FOO (COPY (GETPROP 'FIE 'EXPR))), the user types NAME MOVE FOO FIE : PUTD. Then typing MOVE TEST1 TEST2 would cause (PUTD 'TEST1 (COPY (GETPROP 'TEST2 'EXPR))) to be executed, i.e., would be equivalent to typing USE TEST1 TEST2 FOR FOO FIE IN MOVE. Typing MOVE A B C D would cause two PUTD's to be executed. Note that !'s and $'s can also be employed the same as with USE. For example, if following PREPINDEX(14LISP.XGP) FIXFILE(14LISP.XGPIDX) the user performed NAME FOO $14$ : -2 AND -1, then FOO $15$ would perform the indicated two operations with 14 replaced by 15. RETRIEVE LITATOM [Prog. Asst. Command] Retrieves and reenters on the history list the events named by LITATOM. Causes an error if LITATOM was not named by a NAME command. For example, if the user performs NAME FOO 10 THRU 15, and at some time later types RETRIEVE FOO, six new events will be recorded on the history list (whether or not the corresponding events have been forgotten yet). Note that RETRIEVE does not reexecute the events, it simply retrieves them. The user can then REDO, UNDO, FIX, etc. any or all of these events. Note that the user can combine the effects of a RETRIEVE and a subsequent history command in a single operation, e.g., REDO FOO is equivalent to RETRIEVE FOO, followed by an appropriate REDO. Actually, REDO FOO is better than RETRIEVE followed by REDO since in the latter case, the corresponding events would be entered on the history list twice, once for the RETRIEVE and once for the REDO. Note that UNDO FOO and ?? FOO are permitted. BEFORE LITATOM [Prog. Asst. Command] Undoes the effects of the events named by LITATOM. AFTER LITATOM [Prog. Asst. Command] Undoes a BEFORE LITATOM. BEFORE and AFTER provide a convenient way of flipping back and forth between two states, namely the state before a specified event or events were executed, and that state after execution. For example, if the user has a complex data structure which he wants to be able to interrogate before and after certain modifications, he can execute the modifications, name the corresponding events with the NAME command, and then can turn these modifications off and on via BEFORE or AFTER commands. Both BEFORE and AFTER are no-ops if the LITATOM was already in the corresponding state; both generate errors if LITATOM was not named by a NAME command. The alternative to BEFORE and AFTER for repeated switching back and forth involves typing UNDO, UNDO of the UNDO, UNDO of that etc. At each stage, the user would have to locate the correct event to undo, and furthermore would run the risk of that event being "forgotten" if he did not switch at least once per time-slice. Note: Since UNDO, NAME, RETRIEVE, BEFORE, and AFTER are recorded as inputs they can be referenced by REDO, USE, etc. in the normal way. However, the user must again remember that the context in which the command is reexecuted is different than the original context. For example, if the user types NAME FOO DEFINEQ THRU COMPILE, then types ... FIE, the input that will be reread will be NAME FIE DEFINEQ THRU COMPILE as was intended, but both DEFINEQ and COMPILE, will refer to the most recent event containing those atoms, namely the event consisting of NAME FOO DEFINEQ THRU COMPILE. ARCHIVE EventSpec [Prog. Asst. Command] Records the events specified by EventSpec on a permanent "archived" history list, ARCHIVELST (page X.XX). This history list can be referenced by preceding a standard event specification with @@ (see page X.XX). For example, ?? @@ prints the archived history list, REDO @@ -1 will recover the corresponding event from the archived history list and redo it, etc. The user can also provide for automatic archiving of selected events by appropriately defining ARCHIVEFN (page X.XX), or by putting the history list property *ARCHIVE*, with value T, on the event (page X.XX). Events that are referenced by history commands are automatically marked for archiving in this fashion. FORGET EventSpec [Prog. Asst. Command] Permanently erases the record of the side effects for the events specified by EventSpec. If EventSpec is omitted, forgets side effects for entire history list. FORGET is provided for users with space problems. For example, if the user has just performed SETs, RPLACAs, RPLACDs, PUTD, REMPROPs, etc. to release storage, the old pointers would not be garbage collected until the corresponding events age sufficiently to drop off the end of the history list and be forgotten. FORGET can be used to force immediate forgetting (of the side-effects only). FORGET is not undoable (obviously). REMEMBER EventSpec [Prog. Asst. Command] Instructs the file package to "remember" the events specified by EventSpec. These events will be marked as changed objects of file package type EXPRESSIONS, which can be written out via the file package command P. For example, after the user types: MOVD?(DELFILE /DELFILE) DELFILE REMEMBER -1 (MOVD? (QUOTE DELFILE) (QUOTE /DELFILE)) If the user calls FILES?, MAKEFILES, or CLEANUP, the command (P (MOVD? (QUOTE DELFILE) (QUOTE /DELFILE))) will be constructed by the file package and added to the filecoms indicated by the user, unless the user has already explicitly added the corresponding expression to some P command himself. Note that "remembering" an event like (PUTPROP 'FOO 'CLISPTYPE EXPRESSION) will not result in a (PROP CLISPTYPE FOO) command, because this will save the current (at the time of the MAKEFILE) value for the CLISPTYPE property, which may or may not be EXPRESSION. Thus, even if there is a PROP command which saves the CLISPTYPE property for FOO in some FILECOMS, remembering this event will still require a (P (PUTPROP 'FOO 'CLISPTYPE EXPRESSION)) command to appear. PL LITATOM [Prog. Asst. Command] "Print Property List." Prints out the property list of LITATOM in a nice format, with PRINTLEVEL reset to (2 . 3). For example, PL + CLISPTYPE: 12 ACCESSFNS: (PLUS IPLUS FPLUS) PL is implemented via the function PRINTPROPS. PB LITATOM [Prog. Asst. Command] "Print Bindings." Prints the value of LITATOM with PRINTLEVEL reset to (2 . 3). If LITATOM is not bound, does not attempt spelling correction or generate an error. PB is implemented via the function PRINTBINDINGS. PB is also a break command (see Chapter 14). As a break command, it ascends the stack and, for each frame in which LITATOM is bound, prints the frame name and value of LITATOM. If typed in to the programmer's assistant when not at the top level, e.g. in the editor, etc., PB will also ascend the stack as it does with a break. However, as a programmer's assistant command, it is primarily used to examine the top level value of a variable that may or may not be bound, or to examine a variable whose value is a large list. ; FORM [Prog. Asst. Command] Allows you to type a line of text without having the programmer's assistant process it. Useful when linked to other users, or to annotate a dribble file (see Chapter 30). SHH FORM [Prog. Asst. Command] Allows you to evaluate an expression without having the programmer's assistant process it or record it on a history list. Useful when you want to bypass a programmer's assistant command or to keep the evaluation off the history list. TYPE-AHEAD [Prog. Asst. Command] A command that allows you to type-ahead an indefinite number of inputs. The assistant responds to TYPE-AHEAD with a prompt character of >. The user can now type in an indefinite number of lines of input, under ERRORSET protection. The input lines are saved and unread when the user exits the type-ahead loop with the command $GO (escape-GO). While in the type-ahead loop, ?? can be used to print the type-ahead, FIX to edit the type-ahead, and $Q (escape-Q) to erase the last input (may be used repeatedly). The TYPE-AHEAD command may be aborted by $STOP (escape-STOP); Control-E simply aborts the current line of input. For example: TYPE-AHEAD >SYSOUT(TEM) >MAKEFILE(EDIT) >BRECOMPILE((EDIT WEDIT)) >F >$Q \\F >$Q \\BRECOMPILE >LOAD(WEDIT PROP) >BRECOMPILE((EDIT WEDIT)) >F >MAKEFILE(BREAK) >LISTFILES(EDIT BREAK) >SYSOUT(CURRENT) >LOGOUT] >?? >SYSOUT(TEM) >MAKEFILE(EDIT) >LOAD(WEDIT PROP) >BRECOMPILE((EDIT WEDIT)) >F >MAKEFILE(BREAK) >LISTFILES(EDIT BREAK) >SYSOUT(CURRENT) >LOGOUT] >FIX EDIT *(R BRECOMPILE BCOMPL) *P ((LOGOUT) (SYSOUT &) (LISTFILES &) (MAKEFILE &) (F) (BCOMPL &) (LOAD &) (MAKEFILE &) (SYSOUT &)) *(DELETE LOAD) *OK >$GO Type-ahead can be addressed to the compiler, since it uses LISPXREAD for input. Type-ahead can also be directed to the editor, but type-ahead to the editor and to LISPX cannot be intermixed. The following are some useful functions and variables: (VALUEOF LINE) [NLambda NoSpread Function] An nlambda function for obtaining the value of a particular event, e.g., (VALUEOF -1), (VALUEOF _FOO -2). The value of an event consisting of several operations is a list of the values for each of the individual operations. Note: The value field of a history entry is initialized to bell (Control-G). Thus a value of bell indicates that the corresponding operation did not complete, i.e., was aborted or caused an error (or else it returned bell). Note: Although the input for VALUEOF is entered on the history list before VALUEOF is called, (VALUEOF -1) still refers to the value of the expression immediately before the VALUEOF input, because VALUEOF effectively backs the history list up one entry when it retrieves the specified event. Similarly, (VALUEOF FOO) will find the first event before this one that contains a FOO. IT (IT% (Variable) NIL NIL NIL 15) [Variable] The value of the variable IT is always the value of the last event executed, i.e. (VALUEOF -1). For example, (SQRT 2) 1.414214 (SQRT IT) 1.189207 If the last event was a multiple event, e.g. REDO -3 THRU -1, IT is set to value of the last of these events. Following a ?? command, IT is set to value of the last event printed. In other words, in all cases, IT is set to the last value printed on the terminal. P.A. Commands Applied to P.A. Commands Programmer's assistant commands that unread expressions, such as REDO, USE, etc. do not appear in the input portion of events, although they are stored elsewhere in the event. They do not interfere with or affect the searching operations of event specifications. As a result, p.a. commands themselves cannot be recovered for execution in the normal way. For example, if you type USE A B C FOR D, followqed by USE E FOR D, you will not produce the effect of USE A B C FOR E, but instead will simply cause E to be substituted for D in the last event containing a D. To produce the desired effect, you should type USE D FOR E IN USE. The appearance of the word REDO, USE or FIX in an event address specifies a search for the corresponding programmer's assistant command. It also specifies that the text of the programmer's assistant command itself be treated as though it were the input. However, remember that the context in which a history command is reexecuted is that of the current history, not the original context. For example, if you type USE FOO FOR FIE IN -1, and then later type REDO USE, the -1 will refer to the event before the REDO, not before the USE. The one exception to the statement that programmer's assistant commands "do not interfere with or affect the searching operations of event specifications" occurs when a p.a. command fails to produce any input. For example, suppose you type USE LOG FOR ANTILOG AND ANTILOG FOR LOGG, mispelling the second LOG. This will cause an error, LOGG ?. Since the USE command did not produce any input, the user can repair it by typing USE LOG FOR LOGG, without having to specify IN USE. This latter USE command will invoke a search for LOGG, which will find the bad USE command. The programmer's assistant then performs the indicated substitution, and unreads USE LOG FOR ANTILOG AND ANTILOG FOR LOG. In turn, this USE command invokes a search for ANTILOG, which, because it was not typed in but reread, ignores the bad USE command which was found by the earlier search for LOGG, and which is still on the history list. In other words, p.a. commands that fail to produce input are visible to searches arising from event specifications typed in by the user, but not to secondary event specifications. In addition, if the most recent event is a history command which failed to produce input, a secondary event specification will effectively back up the history list one event so that relative event numbers for that event specification will not count the bad p.a. command. For example, suppose the user types USE LOG FOR ANTILOG AND ANTILOG FOR LOGG IN -2 AND -1, and after the p.a. types LOGG ?, the user types USE LOG FOR LOGG. He thus causes the command USE LOG FOR ANTILOG AND ANTILOG FOR LOG IN -2 AND -1 to be constructed and unread. In the normal case, -1 would refer to the last event, i.e., the "bad" USE command, and -2 to the event before it. However, in this case, -1 refers to the event before the bad USE command, and the -2 to the event before that. In short, the caveat above that "the user must remember that the context in which a history command is reexecuted is that of the current history, not the original context" does not apply if the correction is performed immediately. Changing The Programmer's Assistant 1 (CHANGESLICE N HISTORY %) [Function] Changes the time-slice of the history list HISTORY to N (see the Format and Use of the History List section below). If HISTORY is NIL, changes both the top level history list LISPXHISTORY and the edit history list EDITHISTORY. Note: The effect of increasing the time-slice is gradual: the history list is simply allowed to grow to the corresponding length before any events are forgotten. Decreasing the time-slice will immediately remove a sufficient number of the older events to bring the history list down to the proper size. However, CHANGESLICE is undoable, so that these events are (temporarily) recoverable. Therefore, if you want to recover the storage associated with these events without waiting N more events until the CHANGESLICE event drops off the history list, you must perform a FORGET command (see the Commands section above). PROMPT#FLG(PROMPT#FLG (Variable) NIL NIL NIL 16) [Variable] When this variable is set to T, the current event number to be printed before each prompt character. See PROMPTCHAR in the Programmer's Assistant Functions section below. PROMPT#FLG is initially T. PROMPTCHARFORMS(PROMPTCHARFORMS (Variable) NIL NIL NIL 17) [Variable] The value of PROMPTCHARFORMS is a list of expression which are evaluated each time PROMPTCHAR (see the Programmer's Assistant Functions section below) is called to print the prompt character. If PROMPTCHAR is going to print something, it first maps down PROMPTCHARFORMS evaluating each expression under an ERRORSET. These expressions can access the special variables HISTORY (the current history list), ID (the prompt character to be printed), and PROMPTSTR, which is what PROMPTCHAR will print before ID, if anything. When PROMPT#FLG is T, PROMPTSTR will be the event number. The expressions on PROMPTCHARFORMS can change the shape of a cursor, update a clock, check for mail, etc. or change what PROMPTCHAR is about to print by resetting ID and/or PROMPTSTR. After the expressions on PROMPTCHARFORMS have been evaluated, PROMPTSTR is printed if it is (still) non-NIL, and then ID is printed, if it is (still) non-NIL. HISTORYSAVEFORMS(HISTORYSAVEFORMS (Variable) NIL NIL NIL 17) [Variable] The value of HISTORYSAVEFORMS is a list of expressions that are evaluated under errorset protection each time HISTORYSAVE (see the Programmer's Assistant Functions section below) creates a new event. This happens each time there is an interaction with the user, but not when performing an operation that is being redone. The expressions on HISTORYSAVEFORMS are presumably executed for effect, and can access the special variables HISTORY (the current history list), ID (the current prompt character), and EVENT (the current event which HISTORYSAVE is going to return). PROMPTCHARFORMS and HISTORYSAVEFORMS together enable bracketing each interaction with the user. These can be used to measure how long the user takes to respond, to use a different readtable or terminal table, etc. RESETFORMS [Variable] The value of RESETFORMS is a list of forms that are evaluated at each RESET, i.e., when you type Control-D, or call function RESET. ARCHIVEFN [Variable] If the value of ARCHIVEFN is T, and an event is about to drop off the end of the history list and be forgotten, ARCHIVEFN is called as a function with two arguments: the input portion of the event, and the entire event (see page X.XX for the format of events). If ARCHIVEFN returns T, the event is archived on a permanent history list (see page X.XX). ARCHIVEFN must be both set and defined. ARCHIVEFN is initially NIL and undefined. For example, defining ARCHIVEFN as (LAMBDA (X Y) (EQ (CAR X) 'LOAD)) will keep a record of all calls to LOAD. ARCHIVEFLG [Variable] If the value of ARCHIVEFLG is non-NIL, the system automatically marks all events that are referenced by history commands so that they will be archived when they drop off the history list. ARCHIVEFLG is initially T, so once an event is redone, it is guaranteed to be saved. An event is "marked for archiving" by putting the property *ARCHIVE*, value T, on the event (see page X.XX). The user could do this by means of an appropriately defined LISPXUSERFN (see below). LISPXMACROS [Variable] LISPXMACROS provides a macro facility that allows the user to define his own programmer's assistant commands. It is a list of elements of the form (COMMAND DEF). Whenever COMMAND appears as the first expression on a line in a LISPX input, the variable LISPXLINE is bound to the rest of the line, the event is recorded on the history list, DEF is evaluated, and DEF's value is stored as the value of the event. Similarly, whenever COMMAND appears as CAR of a form in a LISPX input, the variable LISPXLINE is bound to CDR of the form, the event is recorded, and DEF is evaluated. An element of the form (COMMAND NIL DEF) is interpreted to mean bind LISPXLINE and evaluate DEF as described above, except do not save the event on the history list. LISPXHISTORYMACROS [Variable] LISPXHISTORYMACROS allows the user to define programmer's assistant commands that re-execute other events. LISPXHISTORYMACROS is interpreted the same as LISPXMACROS, except that the result of evaluating DEF is treated as a list of expressions to be unread, exactly as though the expressions had been retrieved by a REDO command, or computed by a USE command. Note that returning NIL means nothing else is done. This provides a mechanism for defining LISPX commands which are executed for effect only. Many programmer's assistant commands, such as RETRIEVE, BEFORE, AFTER, etc. are implemented through LISPXMACROS or LISPXHISTORYMACROS. Note: Definitions of commands on LISPXMACROS or LISPXHISTORYMACROS can be saved on files with the file package command LISPXMACROS (see page X.XX). LISPXUSERFN [Variable] When LISPXUSERFN is set to T, it is applied as a function to all inputs not recognized as a programmer's assistant command, or on LISPXMACROS or LISPXHISTORYMACROS. If LISPXUSERFN decides to handle this input, it simply processes it (the event was already stored on the history list before LISPXUSERFN was called), sets LISPXVALUE to the value for the event, and returns T. The programmer's assistant will then know not to call EVAL or APPLY, and will simply store LISPXVALUE into the value slot for the event, and print it. If LISPXUSERFN returns NIL, EVAL or APPLY is called in the usual way. Note that LISPXUSERFN must be both set and defined. LISPXUSERFN is given two arguments: X and LINE. X is the first expression typed, and LINE is the rest of the line, as read by READLINE (page X.XX). For example, if the user typed FOO(A B C), X=FOO, and LINE=((A B C)); if the user typed (FOO A B C), X=(FOO A B C), and LINE=NIL; and if the user typed FOO A B C, X=FOO and LINE=(A B C). By appropriately defining (and setting) LISPXUSERFN, the user can with a minimum of effort incorporate the features of the programmer's assistant into his own executive (actually it is the other way around). For example, LISPXUSERFN could be defined to parse all input (other than p.a. commands) in an alternative way. Note that since LISPXUSERFN is called for each input (except for p.a. commands), it can also be used to monitor some condition or gather statistics. (LISPXPRINT X Y Z NODOFLG) [Function] (LISPXPRIN1 X Y Z NODOFLG) [Function] (LISPXPRIN2 X Y Z NODOFLG) [Function] (LISPXSPACES X Y Z NODOFLG) [Function] (LISPXTERPRI X Y Z NODOFLG) [Function] (LISPXTAB X Y Z NODOFLG) [Function] (LISPXPRINTDEF EXPR FILE LEFT DEF TAIL NODOFLG) [Function] In addition to saving inputs and values, the programmer's assistant saves most system messages on the history list. For example, FILE CREATED ..., (FN REDEFINED), (VAR RESET), output of TIME, BREAKDOWN, STORAGE, DWIM messages, etc. When ?? prints the event, the output is also printed. This facility is implemented via these functions. These functions print exactly the same as their non-LISPX counterparts. Then, they put the output on the history list under the property *LISPXPRINT* (see page X.XX). If NODOFLG is non-NIL, these fuctions do not print, but only put their output on the history list. To perform output operations from user programs so that the output will appear on the history list, the program needs simply to call the corresponding LISPX printing function. (USERLISPXPRINT X FILE Z NODOFLG) [Function] The function USERLISPXPRINT is available to permit the user to define additional LISPX printing functions. If the user has a function FN that takes three or fewer arguments, and the second argument is the file name, he can define a LISPX printing function by simply giving LISPXFN the definition of USERLISPXPRINT, for example, with MOVD(USERLISPXPRINT LISPXFN). USERLISPXPRINT is defined to look back on the stack, find the name of the calling function, strip off the leading "LISPX", perform the appropriate saving information, and then call the function to do the actual printing. LISPXPRINTFLG [Variable] If LISPXPRINTFLG=NIL, the LISPX printing functions will not store their output on the history list. LISPXPRINTFLG is initially T. Undoing 1 Note: This discussion only applies to undoing under the executive and break; the editors handles undoing itself in a slightly different fashion. The UNDO capability of the programmer's assistant is implemented by requiring that each operation that is to be undoable be responsible itself for saving on the history list enough information to enable reversal of its side effects. In other words, the assistant does not "know" when it is about to perform a destructive operation, i.e., it is not constantly checking or anticipating. Instead, it simply executes operations, and any undoable changes that occur are automatically saved on the history list by the responsible functions. The UNDO command, which involves recovering the saved information and performing the corresponding inverses, works the same way, so that the user can UNDO an UNDO, and UNDO that etc. At each point, until the user specifically requests an operation to be undone, the assistant does not know, or care, whether information has been saved to enable the undoing. Only when the user attempts to undo an operation does the assistant check to see whether any information has been saved. If none has been saved, and the user has specifically named the event he wants undone, the assistant types nothing saved. (When the user simply types UNDO, the assistant searches for the last undoable event, ignoring events already undone as well as UNDO operations themselves.) This implementation minimizes the overhead for undoing. Only those operations which actually make changes are affected, and the overhead is small: two or three cells of storage for saving the information, and an extra function call. However, even this small price may be too expensive if the operation is sufficiently primitive and repetitive, i.e., if the extra overhead may seriously degrade the overall performance of the program. Hence not every destructive operation in a program should necessarily be undoable; the programmer must be allowed to decide each case individually. Therefore for each primitive destructive function, Interlisp has defined an undoable version which always saves information. By convention, the name of the undoable version of a function is the function name, preceeded by "/." For example, there is RPLACA and /RPLACA, REMPROP and /REMPROP, etc. The "slash" functions that are currently implemented can be found as the value of /FNS. The various system packages use the appropriate undoable functions. For example, BREAK uses /PUTD and /REMPROP so as to be undoable, and DWIM uses /RPLACA and /RPLACD, when it makes a correction. Similarly, the user can simply use the corresponding / function if he wants to make a destructive operation in his own program undoable. When the / function is called, it will save the UNDO information in the current event on the history list. The effects of the following functions are always undoable: DEFINE, DEFINEQ, DEFC (used to give a function a compiled code definition), DEFLIST, LOAD, SAVEDEF, UNSAVEDEF, BREAK, UNBREAK, REBREAK, TRACE, BREAKIN, UNBREAKIN, CHANGENAME, EDITFNS, EDITF, EDITV, EDITP, EDITE, EDITL, ESUBST, ADVISE, UNADVISE, READVISE, plus any changes caused by DWIM. The programmer's assistant cannot know whether efficiency and overhead are serious considerations for the execution of an expression in a user program, so the user must decide if he wants these operations undoable by explicitly calling /MAPCONC, etc. However, typed-in expressions rarely involve iterations or lengthy computations directly. Therefore, before evaluating the user input, the programmer's assistant substitutes the corresponding undoable function for any destructive function (using LISPX/, page X.XX). For example, if the user types (MAPCONC NASDIC ...), it is actually (/MAPCONC NASDIC ...) that is evaluated. Obviously, with a more sophisticated analysis of both user input and user programs, the decision concerning which operations to make undoable could be better advised. However, we have found the configuration described here to be a very satisfactory one. The user pays a very small price for being able to undo what he types in, and if he wishes to protect himself from malfunctioning in his own programs, he can have his program explicitly call undoable functions. Note: The user can define new "slash" functions to be translated on type-in by calling NEW/FN (page X.XX). Undoing Out of Order /RPLACA operates undoably by saving (on the history list) the list cell that is to be changed and its original CAR. Undoing a /RPLACA simply restores the saved CAR. This implementation can produce unexpected results when multiple /RPLACAs are done on the same list cell, and then undone out of order. For example, if the user types (RPLACA FOO 1), followed by (RPLACA FOO 2), then undoes both events by undoing the most recent event first, then undoing the older event, FOO will be restored to its state before either RPLACA operated. However if the user undoes the first event, then the second event, (CAR FOO) will be 1, since this is what was in CAR of FOO before (RPLACA FOO 2) was executed. Similarly, if the user types (NCONC1 FOO 1), followed by (NCONC1 FOO 2), undoing just (NCONC1 FOO 1) will remove both 1 and 2 from FOO. The problem in both cases is that the two operations are not "independent." In general, operations are always independent if they affect different lists or different sublists of the same list. Undoing in reverse order of execution, or undoing independent operations, is always guaranteed to do the "right" thing. However, undoing dependent operations out of order may not always have the predicted effect. Property list operations, (i.e., PUTPROP, ADDPROP and REMPROP) are handled specially, so that operations that affect different properties on the same property list are always independent. For example, if the user types (PUTPROP 'FOO 'BAR 1) then (PUTPROP 'FOO 'BAZ 2), then undoes the first event, the BAZ property will remain, even though it may not have been on the property list of FOO at the time the first event was executed. SAVESET Typed-in SETs are made undoable by substituting a call to SAVESET. SETQ is made undoable by substituting SAVESETQ, and SETQQ by SAVESETQQ, both of which are implemented in terms of SAVESET. In addition to saving enough information on the history list to enable undoing, SAVESET operates in a manner analogous to SAVEDEF (page X.XX) when it resets a top level value: when it changes a top level binding from a value other than NOBIND to a new value that is not EQUAL to the old one, SAVESET saves the old value of the variable being set on the variable's property list under the property VALUE, and prints the message (VARIABLE RESET). The old value can be restored via the function UNSET, which also saves the current value (but does not print a message). Thus UNSET can be used to flip back and forth between two values. Of course, UNDO can be used as long as the event containing this call to SAVESET is still active. Note however that the old value will remain on the property list, and therefore be recoverable via UNSET, even after the original event has been forgotten. RPAQ and RPAQQ are implemented via calls to SAVESET. Thus old values will be saved and messages printed for any variables that are reset as the result of loading a file. For top level variables, SAVESET also adds the variable to the appropriate spelling list, thereby noticing variables set in files via RPAQ or RPAQQ, as well as those set via type-in. (SAVESET NAME VALUE TOPFLG FLG) [Function] An undoable SET. SAVESET scans the stack looking for the last binding of NAME, sets NAME to VALUE, and returns VALUE. If the binding changed was a top level binding, NAME is added to the spelling list SPELLINGS3 (see page X.XX). Furthermore, if the old value was not NOBIND, and was also not EQUAL to the new value, SAVESET calls the file package to update the necessary file records. Then, if DFNFLG is not equal to T, SAVESET prints (NAME RESET), and saves the old value on the property list of NAME, under the property VALUE. If TOPFLG=T, SAVESET operates as above except that it always uses NAME's top-level value cell. When TOPFLG is T, and DFNFLG is ALLPROP and the old value was not NOBIND, SAVESET simply stores VALUE on the property list of NAME under the property VALUE, and returns VALUE. This option is used for loading files without disturbing the current value of variables (see page X.XX). If FLG=NOPRINT, SAVESET saves the old value, but does not print the message. This option is used by UNSET. If FLG=NOSAVE, SAVESET does not save the old value on the property list, nor does it add NAME to SPELLINGS3. However, the call to SAVESET is still undoable. This option is used by /SET. If FLG=NOSTACKUNDO, SAVESET is undoable only if the binding being changed is a top-level binding, i.e. this says when resetting a variable that has been rebound, don't bother to make it undoable. (UNSET NAME) [Function] If NAME does not contain a property VALUE, UNSET generates an error. Otherwise UNSET calls SAVESET with NAME, the property value, TOPFLG=T, and FLG=NOPRINT. UNDONLSETQ and RESETUNDO The function UNDONLSETQ provides a limited form of backtracking: if an error occurs under the UNDONLSETQ, all undoable side effects executed under the UNDONLSETQ are undone. RESETUNDO, used in conjunction with RESETLST and RESETSAVE (see Chapter 14), provides a more general undo capability where the user can specify that the side effects be undone after the specified computation finishes, is aborted by an error, or by a Control-D. (UNDONLSETQ UNDOFORM %) [NLambda Function] An nlambda function similar to NLSETQ (see Chapter 14). UNDONLSETQ evaluates UNDOFORM, and if no error occurs during the evaluation, returns (LIST (EVAL UNDOFORM)) and passes the undo information from UNDOFORM (if any) upwards. If an error does occur, the UNDONLSETQ returns NIL, and any undoable changes made during the evaluation of UNDOFORM are undone. Any undo information is stored directly on the history event (if LISPXHIST is not NIL), so that if the user Control-D's out of the UNDONLSETQ, the event is still undoable. UNDONLSETQ will operate correctly if #UNDOSAVES is or has been exceeded for this event, or is exceeded while under the scope of the UNDONLSETQ. Exercise caution when using coroutines or other non-standard means of exiting while under an UNDONLSETQ. See discussion in Chapter 14. (RESETUNDO X STOPFLG) [Function] For use in conjunction with RESETLST (see Chapter 14). (RESETUNDO) initializes the saving of undo information and returns a value which when given back to RESETUNDO undoes the intervening side effects. For example, (RESETLST (RESETSAVE (RESETUNDO)) . FORMS) will undo the side effects of FORMS on normal exit, or if an error occurs or a Control-D is typed. If STOPFLG=T, RESETUNDO stops accumulating undo information it is saving on X. This has no bearing on the saving of undo information on higher RESETUNDO's, or on being able to undo the entire event. For example, (RESETLST (SETQ FOO (RESETUNDO)) (RESETSAVE NIL (LIST 'RESETUNDO FOO)) (ADVISE ...) (RESETUNDO FOO T) . FORMS) would cause the advice to be undone, but not any of the side effects in FORMS. Format and Use of the History List 1 The system currently uses three history lists, LISPXHISTORY for the top-level Interlisp executive, EDITHISTORY for the editors, and ARCHIVELST for archiving events (see the Commands section above). All history lists have the same format, use the same functions, HISTORYSAVE, for recording events, and use the same set of functions for implementing commands that refer to the history list, e.g., HISTORYFIND, PRINTHISTORY, UNDOSAVE, etc. Each history list is a list of the form (L EVENT# SIZE MOD), where L is the list of events with the most recent event first, EVENT# is the event number for the most recent event on L, SIZE is the size of the time-slice (below), i.e., the maximum length of L, and MOD is the highest possible event number. LISPXHISTORY and EDITHISTORY are both initialized to (NIL 0 100 100). Setting LISPXHISTORY or EDITHISTORY to NIL disables all history features, so LISPXHISTORY and EDITHISTORY act like flags as well as repositories of events. Note: One of the reasons why users may disable the history list facility is to allow the garbage collector to reclaim objects stored on the history list. Simply setting LISPXHISTORY to NIL will not necessarily remove all pointers to the history list. GAINSPACE (see Chapter 22) is a useful function when trying to reclaim memory space. Each history list has a maximum length, called its "time-slice." As new events occur, existing events are aged, and the oldest events are "forgotten." For efficiency, the storage used to represent the forgotten event is reused in the representation of the new event, so the history list is actually a ring buffer. The time-slice of a history list can be changed with the function CHANGESLICE (see the Changing the Programmer's Assistant section above). Larger time-slices enable longer "memory spans," but tie up correspondingly greater amounts of storage. Since the user seldom needs really "ancient history," and a facility is provided for saving and remembering selected events (see NAME and RETRIEVE, in the Commands section above), a relatively small time-slice such as 30 events is more than adequate, although some users prefer to set the time-slice as large as 100 events. If PROMPT#FLG (see the Changing the Programmer's Assistant section above) is set to T, an "event number" will be printed before each prompt. More recent events have higher numbers. When the event number of the current event is 100, the next event will be given number 1. If the time-slice is greater than 100, the "roll-over" occurs at the next highest hundred, so that at no time will two events ever have the same event number. For example, if the time-slice is 150, event number 1 will follow event number 200. Each individual event on L is a list of the form (INPUT ID VALUE . PROPS). ID is the prompt character for this event, e.g., , :, *, etc. VALUE is the value of the event, and is initialized to bell. On EDITHISTORY, this field is used to save the side effects of each command (see the Editor and the Programmer's Assistant section below). PROPS is a property list used to associate other information with the event (described below). INPUT is the input sequence for the event. Normally, this is just the input that the user typed-in. For an APPLY-format input (see the Input Formats section above), this is a list consisting of two expressions; for an EVAL-format input, this is a list of just one expression; for an input entered as list of atoms, INPUT is simply that list. For example, User Input INPUT is: PLUS[1 1] (PLUS (1 1)) (PLUS 1 1) ((PLUS 1 1)) PLUS 1 1cr (PLUS 1 1) If the user types in a programmer's assistant command that "unreads" and reexecutes other events (REDO, USE,, etc.), INPUT contains a "sequence" of the inputs from the redone events. Specifically, the INPUT fields from the specified events are concatenated into a single list, seperated by special markers called "pseudo-carriage returns," which print out as the string "". When the result of this concatenation is "reread," the pseudo-carriage-returns are treated by LISPXREAD and READLINE exactly as real carriage returns, i.e., they serve to distinguish between APPLY-format and EVAL-format inputs to LISPX, and to delimit line commands to the editor. Note: The value of the variable HISTSTR0 is used to represent a pseudo-carriage return. This is initially the string "". Note that the functions that recognize pseudo-carriage returns compare them to HISTSTR0 using EQ, so this marker will never be confused with a string that was typed in by the user. The same convention is used for representing multiple inputs when a USE command involves sequential substitutions. For example, if the user types GETD(FOO) and then USE FIE FUM FOR FOO, the input sequence that will be constructed is (GETD (FIE) "" GETD (FUM)), which is the result of substituting FIE for FOO in (GETD (FOO)) concatenated with the result of substituting FUM for FOO in (GETD (FOO)). Once a multiple input has been entered as the input portion of a new event, that event can be treated exactly the same as one resulting from type-in. In other words, no special checks have to be made when referencing an event, to see if it is simple or multiple. This implementation permits an event specification to refer to a single simple event, or to several events, or to a single event originally constructed from several events (which may themselves have been multiple input events, etc.) without having to treat each case separately. REDO, RETRY, USE, ..., and FIX commands, i.e., those commands that reexecute previous events, are not stored as inputs, because the input portion for these events are the expressions to be "reread". The history commands UNDO, NAME, RETRIEVE, BEFORE, and AFTER are recorded as inputs, and ?? prints them exactly as they were typed. PROPS is a property list of the form (PROPERTY1 VALUE1 PROPERTY2 VALUE2 ...), that can be used to associate arbitrary information with a particular event. Currently, the following properties are used by the programmer's assistant: SIDE A list of the side effects of the event. See UNDOSAVE, in the Programmer's Assistant Functions section below. *PRINT* Used by the ?? command when special formatting is required, for example, when printing events corresponding to the break commands OK, GO, EVAL, and ?=. USE-ARGS ...ARGS The USE-ARGS and ...ARGS properties are used to save the arguments and expression for the corresponding history command. *ERROR* *CONTEXT* *ERROR* and *CONTEXT* are used to save information when errors occur for subsequent use by the $ command. Whenever an error occurs, the offender is automatically saved on that event's entry in the history list, under the *ERROR* property. *LISPXPRINT* Used to record calls to LISPXPRINT, LISPXPRIN1, etc. (see the Changing the Programmer's Assistant section above). *ARCHIVE* The property *ARCHIVE* on an event causes the event to be automatically archived when it "falls off the end" of the history list (see the Commands section above). *GROUP* *HISTORY* The *HISTORY* and *GROUP* properties are used for commands that reexecute previous events, i.e., REDO, RETRY, USE, ..., and FIX. The value of the *HISTORY* property is the history command that the user actually typed, e.g., REDO FROM F. This is used by the ?? command when printing the event. The value of the *GROUP* property is a structure containing the side effects, etc. for the individual inputs being reexecuted. This structure is described below. When LISPX is given an input, it calls HISTORYSAVE (see the Programmer's Assistant Functions section below) to record the input in a new event (except for the commands ??, FORGET, TYPE-AHEAD, $BUFS, and ARCHIVE, that are executed immediately and are not recorded on the history list). Normally, HISTORYSAVE creates and returns a new event. LISPX binds the variable LISPXHIST to the value of HISTORYSAVE, so that when the operation has completed, LISPX knows where to store the value. Note that by the time it completes, the operation may no longer correspond to the most recent event on the history list. For example, all inputs typed to a lower break will appear later on the history list. After binding LISPXHIST, LISPX executes the input, stores its value in the value field of the LISPXHIST event, prints the value, and returns. When the input is a REDO, RETRY, USE, ..., or FIX command, the procedure is similar, except that the event is also given a *GROUP* property, initially NIL, and a *HISTORY* property, and LISPX simply unreads the input and returns. When the input is "reread", it is HISTORYSAVE, not LISPX, that notices this fact, and finds the event from which the input originally came. If HISTORYSAVE cannot find the event, for example if a user program unreads the input directly, and not via a history command, HISTORYSAVE proceeds as though the input were typed. HISTORYSAVE then adds a new (INPUT ID VALUE . PROPS) entry to the *GROUP* property for this event, and returns this entry as the "new event." LISPX then proceeds exactly as when its input was typed directly, i.e., it binds LISPXHIST to the value of HISTORYSAVE, executes the input, stores the value in CADDR of LISPXHIST, prints the value, and returns. In fact, LISPX never notices whether it is working on freshly typed input, or input that was reread. Similarly, UNDOSAVE will store undo information on LISPXHIST the same as always, and does not know or care that LISPXHIST is not the entire event, but one of the elements of the *GROUP* property. Thus when the event is finished, its entry will look like: (INPUT ID VALUE *HISTORY* COMMAND *GROUP* ((INPUT1 ID1 VALUE1 SIDE SIDE1) (INPUT2 ID2 VALUE2 SIDE SIDE2) ...)) In this case, the value field of the event with the *GROUP* property is not being used; VALUEOF instead returns a list of the values from the *GROUP* property. Similarly, UNDO operates by collecting the SIDE properties from each of the elements of the *GROUP* property, and then undoing them in reverse order. This implementation removes the burden from the function calling HISTORYSAVE of distinguishing between new input and reexecution of input whose history entry has already been set up. Programmer's Assistant Functions 1 (LISPX(LISPX (Function) NIL NIL NIL 26) LISPXX LISPXID LISPXXMACROS LISPXXUSERFN LISPXFLG) [Function] LISPX is the primary function of the programmer's assistant. LISPX takes one user input, saves it on the history list, evaluates it, saves its value, and prints and returns it. LISPX also interpretes p.a. commands, LISPXMACROS, LISPXHISTORYMACROS, and LISPXUSERFN. If LISPXX is a list, it is interpreted as the input expression. Otherwise, LISPX calls READLINE, and uses LISPXX plus the value of READLINE as the input for the event. If LISPXX is a list CAR of which is LAMBDA or NLAMBDA, LISPX calls LISPXREAD to obtain the arguments. LISPXID is the prompt character to print before accepting user input. The user should be careful about using the prompt characters "," "*," or ":," because in certain cases LISPX uses the value of LISPXID to tell whether or not it was called from the break package or editor. If LISPXXMACROS is not NIL, it is used as the list of LISPX macros, otherwise the top level value of the variable LISPXMACROS is used. If LISPXXUSERFN is not NIL, it is used as the LISPXUSERFN. In this case, it is not necessary to both set and define LISPXUSERFN as described in the Changing the Programmer's Assistant section above. LISPXFLG is used by the E command in the editor (see the Editor and Programmer's Assistant section below). Note that the history is not one of the arguments to LISPX, i.e., the editor must bind (reset) LISPXHISTORY to EDITHISTORY before calling LISPX to carry out a history command. LISPX will continue to operate as an EVAL/APPLY function if LISPXHISTORY is NIL. Only those functions and commands that involve the history list will be affected. LISPX performs spelling corrections using LISPXCOMS, a list of its commands, as a spelling list whenever it is given an unbound atom or undefined function, before attempting to evaluate the input. LISPX is responsible for rebinding HELPCLOCK, used by BREAKCHECK (see Chapter 14) for computing the amount of time spent in a computation, in order to determine whether to go into a break if and when an error occurs. (USEREXEC(USEREXEC (Function) NIL NIL NIL 26) LISPXID LISPXXMACROS LISPXXUSERFN) [Function] Repeatedly calls LISPX under errorset protection specifying LISPXXMACROS and LISPXXUSERFN, and using LISPXID (or if LISPXID=NIL) as a prompt character. USEREXEC is exited via the command OK, or else with a RETFROM. (LISPXEVAL(LISPXEVAL (Function) NIL NIL NIL 26) LISPXFORM LISPXID) [Function] Evaluates LISPXFORM (using EVAL) the same as though it were typed in to LISPX, i.e., the event is recorded, and the evaluation is made undoable by substituting the slash functions for the corresponding destructive functions (see below). LISPXEVAL returns the value of the form, but does not print it. When LISPX recieves an "input," it may come from the user typing it in, or it may be an input that has been "unread." LISPX handles these two cases by getting inputs with LISPXREAD and READLINE, described below. These functions use the following variable to store the expressions that have been unread: READBUF(READBUF (Variable) NIL NIL NIL 27) [Variable] This variable is used by LISPXREAD and READLINE to store the expressions that have been unread. When READBUF is not NIL, READLINE and LISPXREAD "read" expressions from READBUF until READBUF is NIL, or until they read a pseudo-carriage return (see the Format and Use of the History List section above). Both functions return a list of the expressions that have been "read." (The pseudo-carriage return is not included in the list.) When READBUF is NIL, both LISPXREAD and READLINE actually obtain their input by performing (APPLY* LISPXREADFN FILE), where LISPXREADFN is initially set to TTYINREAD (Chapter 26). The user can make LISPX, the editor, break, etc. do their reading via a different input function by simply setting LISPXREADFN to the name of that function (or an appropriate LAMBDA expression). You should only add expressions to READBUF using the function LISPXUNREAD (see below), which knows about the format of READBUF. (READLINE(READLINE (Function) NIL NIL NIL 27) RDTBL % %) [Function] Reads a line from the terminal, returning it as a list. If (READP T) is NIL, READLINE returns NIL. Otherwise it reads expressions until it encounters one of the following: f an EOL (typed by the user) that is not preceded by any spaces, e.g., A B Ccr and READLINE returns (A B C) f a list terminating in a "]", in which case the list is included in the value of READLINE, e.g., A B (C D] and READLINE returns (A B (C D)). f an unmatched right parentheses or right square bracket, which is not included in the value of READLINE, e.g., A B C] and READLINE returns (A B C). In the case that one or more spaces precede a carriage-return, or a list is terminated with a ")", READLINE will type "..." and continue reading on the next line, e.g., A B Ccr ...(D E F) ...(X Y Z] and READLINE returns (A B C (D E F) (X Y Z)). If the user types another carriage-return after the "...", the line will terminate, e.g., A B Ccr ...cr and READLINE returns (A B C). Carriage-return, i.e., the EOL character, can be redefined with SETSYNTAX (Chapter 25). READLINE actually checks for the EOL character, whatever that may be. The same is true for right parenthesis and right bracket. When READLINE is called from LISPX, it operates differently in two respects: 1. If the line consists of a single ) or ], READLINE returns (NIL) instead of NIL, i.e., the ) or ] is included in the line. This permits the user to type FOO) or FOO], meaning call the function FOO with no arguments, as opposed to FOOcr (FOO), meaning evaluate the variable FOO. 2. If the first expression on the line is a list that is not preceded by any spaces, the list terminates the line regardless of whether or not it is terminated by ]. This permits the user to type EDITF(FOO) as a single input. If any spaces are inserted between the atom and the left parentheses or bracket, READLINE assumes that the list does not terminate the line. This enables you to type a line command such as USE (FOO) FOR FOO. Therefore, if you accidentially put an extra space between a function and its arguments, you have to complete the input with another carriage return, e.g., EDITF (FOO) ...cr EDIT * Note: READLINE reads expressions by performing (APPLY* LISPXREADFN T). LISPXREADFN (above) is initially set to TTYINREAD (Chapter 26). (LISPXREAD(LISPXREAD (Function) NIL NIL NIL 28) FILE RDTBL) [Function] A generalized READ. If READBUF=NIL, LISPXREAD performs (APPLY* LISPXREADFN FILE), which it returns as its value. If READBUF is not NIL, LISPXREAD "reads" and returns the next expression on READBUF. LISPXREAD also sets REREADFLG (see below) to NIL when it reads via LISPXREADFN, and sets REREADFLG to the value of READBUF when rereading. (LISPXREADP(LISPXREADP (Function) NIL NIL NIL 28) FLG) [Function] A generalized READP. If FLG=T, LISPXREADP returns T if there is any input waiting to be "read", in the manner of LISPXREAD. If FLG=NIL, LISPXREADP returns T only if there is any input waiting to be "read" on this line. In both cases, leading spaces are ignored, i.e., skipped over with READC, so that if only spaces have been typed, LISPXREADP will return NIL. (LISPXUNREAD LST %) [Function] Unreads LST, a list of expressions. (PROMPTCHAR ID FLG HISTORY) [Function] Called by LISPX to print the prompt character ID before each input. PROMPTCHAR will not print anything when the next input will be "reread", i.e., when READBUF is not NIL. PROMPTCHAR will not print when (READP)=T, unless FLG is T. The editor calls PROMPTCHAR with FLG=NIL so that extra *'s are not printed when the user types several commands on one line. However, EVALQT calls PROMPTCHAR with FLG=T, since it always wants the _ printed (except when "rereading"). If PROMPT#FLG (see the Changing the Programmer's Assistant section above) is T and HISTORY is not NIL, PROMPTCHAR prints the current event number (of HISTORY) before printing ID. The value of PROMPTCHARFORMS (see the Changing the Programmer's Assistant section above) is a list of expressions that are evaluated by PROMPTCHAR before, and if, it does any printing. (HISTORYSAVE(HISTORYSAVE (Function) NIL NIL NIL 29) HISTORY ID INPUT1 INPUT2 INPUT3 PROPS) [Function] Records one event on HISTORY. If INPUT1 is not NIL, the input is of the form (INPUT1 INPUT2 . INPUT3). If INPUT1 is NIL, and INPUT2 is not NIL, the input is of the form (INPUT2 . INPUT3). Otherwise, the input is just INPUT3. HISTORYSAVE creates a new event with the corresponding input, ID, value field initialized to bell, and PROPS. If the HISTORY has reached its full size, the last event is removed and cannibalized. The value of HISTORYSAVE is the new event. However, if REREADFLG is not NIL, and the most recent event on the history list contains the history command that produced this input, HISTORYSAVE does not create a new event, but simply adds an (INPUT ID bell . PROPS) entry to the *GROUP* property for that event and returns that entry. See the discussion in the prior section. HISTORYSAVEFORMS (see the Changing the Programmer's Assistant section above) is a list of expressions that are evaluated under errorset protection each time HISTORYSAVE creates a new event. (LISPXSTOREVALUE(LISPXSTOREVALUE (Function) NIL NIL NIL 29) EVENT VALUE) [Function] Used by LISPX for storing the value of an event. Can be advised by user to watch for particular values or perform other monitoring functions. (LISPXFIND(LISPXFIND (Function) NIL NIL NIL 29) HISTORY LINE TYPE BACKUP %) [Function] LINE is an event specification, TYPE specifies the format of the value to be returned by LISPXFIND, and can be either ENTRY, ENTRIES, COPY, COPIES, INPUT, or REDO. LISPXFIND parses LINE, and uses HISTORYFIND (see below) to find the corresponding events. LISPXFIND then assembles and returns the appropriate structure. LISPXFIND incorporates the following special features: 1. if BACKUP=T, LISPXFIND interprets LINE in the context of the history list before the current event was added. This feature is used, for example, by VALUEOF, so that (VALUEOF -1) will not refer to the VALUEOF event itself. 2. if LINE=NIL and the last event is an UNDO, the next to the last event is taken. This permits the user to type UNDO followed by REDO or USE. 3. LISPXFIND recognizes @@, and searches the archived history list instead of HISTORY (see the ARCHIVE command in the Commands section above). 4. LISPXFIND recognizes @, and retrieves the corresponding event(s) from the property list of the atom following @ (see the Commands section above). (HISTORYFIND(HISTORYFIND (Function) NIL NIL NIL 29) LST INDEX MOD EVENTADDRESS %) [Function] Searches LST and returns the tails of LST beginning with the event corresponding to EVENTADDRESS. LST, INDEX, and MOD are the first three elements of a "history list" structure (see the Format and Use of the History List section above). EVENTADDRESS is an event address (see the Event Specification section abo e) e.g., (43), (-1), (FOO FIE), (LOAD FOO), etc. If HISTORYFIND cannot find EVENTADDRESS, it generates an error. (HISTORYMATCH(HISTORYMATCH (Function) NIL NIL NIL 30) INPUT PAT EVENT) [Function] Used by HISTORYFIND for "matching" when EVENTADDRESS specifies a pattern. Matches PAT against INPUT, the input portion of the history event EVENT, as matching is defined in Chapter 16. Initially defined as (EDITFINDP INPUT PAT T), but can be advised or redefined by the user. (ENTRY#(ENTRY# (Function) NIL NIL NIL 30) HIST X) [Function] HIST is a history list (see the Format and Use of the History List section above). X is EQ to one of the events on HIST. ENTRY# returns the event number for X. (UNDOSAVE(UNDOSAVE (Function) NIL NIL NIL 30) UNDOFORM HISTENTRY) [Function] UNDOSAVE adds the "undo information" UNDOFORM to the SIDE property of the history event HISTENTRY. If there is no SIDE property, one is created. If the value of the SIDE property is NOSAVE, the information is not saved. HISTENTRY specifies an event. If HISTENTRY=NIL, the value of LISPXHIST is used. If both HISTENTRY and LISPXHIST are NIL, UNDOSAVE is a no-op. Note that HISTENTRY (or LISPXHIST) can either be a "real" event, or an event within the *GROUP* property of another event (see the Format and Use of the History List section above). The form of UNDOFORM is (FN . ARGS). Undoing is done by performing (APPLY (CAR UNDOFORM) (CDR UNDOFORM)). For example, if the definition of FOO is DEF, (/PUTD FOO NEWDEF) will cause a call to UNDOSAVE with UNDOFORM=(/PUTD FOO DEF). Note: In the special case of /RPLNODE and /RPLNODE2, the format of UNDOFORM is (X OLDCAR . OLDCDR). When UNDOFORM is undone, this form is recognized and handled specially. This implementation saves space. CAR of the SIDE property of an event is a count of the number of UNDOFORMs saved for this event. Each call to UNDOSAVE increments this count. If this count is set to -1, then it is never incremented, and any number of UNDOFORMs can be saved. If this count is a positive number, UNDOSAVE restricts the number of UNDOFORMs saved to the value of #UNDOSAVES, described below. LOAD initializes the count to -1, so that regardless of the value of #UNDOSAVES, no message will be printed, and the LOAD will be undoable. #UNDOSAVES(#UNDOSAVES (Variable) NIL NIL NIL 30) [Variable] The value of #UNDOSAVES is the maximum number of UNDOFORMs to be saved for a single event. When the count of UNDOFORMs reaches this number, UNDOSAVE prints the message CONTINUE SAVING?, asking the user if he wants to continue saving. If the user answers NO or defaults, UNDOSAVE discards the previously saved information for this event, and makes NOSAVE be the value of the property SIDE, which disables any further saving for this event. If the user answers YES, UNDOSAVE changes the count to -1, which is then never incremented, and continues saving. The purpose of this feature is to avoid tying up large quantities of storage for operations that will never need to be undone. If #UNDOSAVES is negative, then when the count reaches -#UNDOSAVES, UNDOSAVE simply stops saving without printing any messages or interacting with the user. #UNDOSAVES=NIL is equivalent to #UNDOSAVES=infinity. #UNDOSAVES is initially NIL. (NEW/FN FN) [Function] NEW/FN performs the necessary housekeeping operations to make FN be translated to the undoable version /FN when typed-in. For example, RADIX can be made undoable when typed-in by performing: (DEFINEQ (/RADIX (X) (UNDOSAVE (LIST '/RADIX (RADIX X)) (/RADIX) (NEW/FN 'RADIX) (LISPX/(LISPX/ (Function) NIL NIL NIL 31) X FN VARS) [Function] LISPX/ performs the substitution of / functions for destructive functions that are typed-in. If FN is not NIL, it is the name of a function, and X is its argument list. If FN is NIL, X is a form. In both cases, LISPX/ returns X with the appropriate substitutions. VARS is a list of bound variables (optional). LISPX/ incorporates information about the syntax and semantics of Interlisp expressions. For example, it does not bother to make undoable operations involving variables bound in X. It does not perform substitution inside of expressions CAR of which is an nlambda function (unless CAR of the form has the property INFO value EVAL, see Chapter 21). For example, (BREAK PUTD) typed to LISPX, will break on PUTD, not /PUTD. Similarly, substitution should be performed in the arguments for functions like MAPC, RPTQ, etc., since these contain expressions that will be evaluated or applied. For example, if the user types (MAPC '(FOO1 FOO2 FOO3) 'PUTD) the PUTD must be replaced by /PUTD. (UNDOLISPX(UNDOLISPX (Function) NIL NIL NIL 31) LINE) [Function] LINE is an event specification. UNDOLISPX is the function that executes UNDO commands by calling UNDOLISPX1 on the appropriate entry(s). (UNDOLISPX1(UNDOLISPX (Function) NIL NIL NIL 31) EVENT FLG %) [Function] Undoes one event. UNDOLISPX1 returns NIL if there is nothing to be undone. If the event is already undone, UNDOLISPX1 prints already undone and returns T. Otherwise, UNDOLISPX1 undoes the event, prints a message, e.g., SETQ undone, and returns T. If FLG=T and the event is already undone, or is an undo command, UNDOLISPX1 takes no action and returns NIL. UNDOLISPX uses this option to search for the last event to undo. Thus when LINE=NIL, UNDOLISPX simply searches history until it finds an event for which UNDOLISPX1 returns T. Undoing an event consists of mapping down (CDR of) the property value for SIDE, and for each element, applying CAR to CDR, and then marking the event undone by attaching (with /ATTACH) a NIL to the front of its SIDE property. Note that the undoing of each element on the SIDE property will usually cause undosaves to be added to the current LISPXHIST, thereby enabling the effects of UNDOLISPX1 to be undone. (PRINTHISTORY(PRINTHISTORY (Function) NIL NIL NIL 31) HISTORY LINE SKIPFN NOVALUES FILE) [Function] LINE is an event specification. PRINTHISTORY prints the events on HISTORY specified by LINE, e.g., (-1 THRU -10). Printing is performed via the function SHOWPRIN2, so that if the value of SYSPRETTYFLG=T, events will be prettyprinted. SKIPFN is an (optional) functional argument that is applied to each event before printing. If it returns non-NIL, the event is skipped, i.e., not printed. If NOVALUES=T, or NOVALUES applied to the corresponding event is true, the value is not printed. For example, NOVALUES is T when printing events on EDITHISTORY. For example, the following LISPXMACRO will define ??' as a command for printing the history list while skipping all "large events" and not printing any values. (??' (PRINTHISTORY LISPXHISTORY LISPXLINE (FUNCTION (LAMBDA (X) (IGREATERP (COUNT (CAR X)) 5))) T T)) The Editor and the Programmer's Assistant 1 As mentioned earlier, all of the remarks concerning "the programmer's assistant" apply equally well to user interactions with EVALQT, BREAK or the editor. The differences between the editor's implementation of these features and that of LISPX are mostly obvious or inconsequential. However, for completeness, this section discusses the editor's implementation of the programmer's assistant. The editor uses PROMPTCHAR to print its prompt character, and LISPXREAD, LISPXREADP, and READLINE for obtaining inputs. When the editor is given an input, it calls HISTORYSAVE to record the input in a new event on its history list, EDITHISTORY, except that the atomic commands OK, STOP, SAVE, P, ?, PP and E are not recorded. In addition, number commands are grouped together in a single event. For example, 3 3 -1 is considered as one command for changing position. EDITHISTORY follows the same conventions and format as LISPXHISTORY (see the Format and Use of the History List section above). However, since edit commands have no value, the editor uses the value field for saving side effects, rather than storing them under the property SIDE. The editor recognizes and processes the four commands DO, !E, !F, and !N which refer to previous events on EDITHISTORY. The editor also processes UNDO itself, as described below. All other history commands are simply given to LISPX for execution, after first binding (resetting) LISPXHISTORY to EDITHISTORY. The editor also calls LISPX when given an E command (see Chapter 16). In this case, the editor uses the fifth argument to LISPX, LISPXFLG, to specify that any history commands are to be executed by a recursive call to LISPX, rather than by unreading. For example, if the user types E REDO in the editor, he wants the last event on LISPXHISTORY processed as LISPX input, and not to be unread and processed by the editor. Note: The editor determines which history commands to pass to LISPX by looking at HISTORYCOMS, a list of the history commands. EDITDEFAULT (see Chapter 16) interrogates HISTORYCOMS before attempting spelling correction. (All of the commands on HISTORYCOMS are also on EDITCOMSA and EDITCOMSL so that they can be corrected if misspelled in the editor.) Thus if the user defines a LISPXMACRO and wishes it to operate in the editor as well, he need simply add it to HISTORYCOMS. For example, RETRIEVE is implemented as a LISPXMACRO and works equally well in LISPX and the editor. The major implementation difference between the editor and LISPX occurs in undoing. EDITHISTORY is a list of only the last N commands, where N is the value of the time-slice. However the editor provides for undoing all changes made in a single editing session, even if that session consisted of more than N edit commands. Therefore, the editor saves undo information independently of the EDITHISTORY on a list called UNDOLST, (although it also stores each entry on UNDOLST in the field of the corresponding event on EDITHISTORY.) Thus, the commands UNDO, !UNDO, and UNBLOCK, are not dependent on EDITHISTORY, and in fact will work if EDITHISTORY=NIL, or even in a system which does not contain LISPX at all. For example, UNDO specifies undoing the last command on UNDOLST, even if that event no longer appears on EDITHISTORY. The only interaction between UNDO and the history list occurs when the user types UNDO followed by an event specification. In this case, the editor calls LISPXFIND to find the event, and then undoes the corresponding entry on UNDOLST. Thus the user can only undo a specified command within the scope of the EDITHISTORY. (Note that this is also the only way UNDO commands themselves can be undone, that is, by using the history feature, to specify the corresponding event, e.g., UNDO UNDO.) The implementation of the actual undoing is similar to the way it is done in LISPX: each command that makes a change in the structure being edited does so via a function that records the change on a variable. After the command has completed, this variable contains a list of all the pointers that have been changed and their original contents. Undoing that command simply involves mapping down that list and restoring the pointers. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "13-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "13-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "13-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "13-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "13-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "13-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))/2l,ll/HH52HH +l2l~~2l~~3HZ +T-llT6T-llT5lxx2lxx2lxx,,/ll6HT6HT6HT52Hll5,,3HZT3HZ +T3$$(T,ll,ll,ll,23(T-T3(T,HH +,HH,HHF PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR5,,2TITAN +TITAN +TITAN +TITAN +CLASSIC + HELVETICA  HELVETICACLASSIC +CLASSIC +CLASSIC +TITAN + HELVETICA HELVETICA +CLASSIC +MODERNMODERN +MODERN +MODERNMODERN ++ HRULE.GETFNCLASSIC ++ * HRULE.GETFNCLASSIC +* ) ) HRULE.GETFNCLASSIC +( ( HRULE.GETFNCLASSIC +/!  HRULE.GETFNMODERN +'&B&&       O                   &                +    $       (  9   9   9  . D &&v  \  K Z $ # HRULE.GETFNMODERN +%C  . -   . - - - -  - -         d c +    . +. +  - Q. +  - .  - -   E   +" # HRULE.GETFNMODERN +%&%  + X  n&0   '  + &7  Q " ? -   K + "   0  t   T #%  + +   d , ^ ?%   +  +  +   "  !  (       >    #   ;       +      +  + 8 &    + 8 ?   +  +      < 8  +       +   Q %   +  * K    h * % %& Q      (    (   " +   4  ?       +  + +  +     +  + ^       I      @           2 ;  ! ,   ,  6 8        + !     D 2   +  5 Z  0 !    +  K     + *   +             @  n %  v      %  |     4  ,      9             )% B h "   "  ?      h  N   $   +   % Z       # %   + >          +      +      +      +    +  +  +  +  +    %   +     @    '      k p  ; +   - + $ &    "   +    ) + '  !  +            `  6   D4             ,  A *   t  7  O ' )  +   - #   ; + !  +    I +  7  A    e ' J  .    I !      % & 4  + +  +   X d)  L +  + + + +  A +   + 9   + + +H +  + + + +L + + + +9 + % (            + +  1 + 5  . + +  +  +  #!'    ++ 4 +#    + +)     1    ?   +"   {  D   S ?      Y       + *      + %  Z ;  ?     A  +&  7    &      2    (   ]     ) + d  #  + W_ 6    N  ; Y      H   A G 8 %   )    ,  &  + +    A  # +      .  + +    8  + +     ! +  '  + +  K !  q . b     + H +  J l  " &   9 +   6                               ? "    ; ` %7   I y '  D  d ;   IM.INDEX.GETFN  6 . - - + - -  ; + K 3& %A + +4 %    2    ~   $  &(    E   "   \  +   A 3 &45 +4 4 +0  +1 +$  +$$ # HRULE.GETFNMODERN +   +  A  *   +  +    6 * + &IM.INDEX.GETFNMODERN +  L + 9 +   +IM.INDEX.GETFN   7 + g + 1 % 3  +  +   +   / W +     !  "  ,IM.INDEX.GETFN   Q  J  %  %   +    + / 2      R   F     ! $  +   +   +  >; + ]     +  +  +  0  N  B     )  +  +  +   $   Z  ' + <   E ..     "  5     f   o  + ) 9   + 6    (     $ % . +               (  h y        +    +    +        +        + +    +     b4 Q   N       6 1 ` $       e d     F  $# HRULE.GETFNMODERN +%&     &  ` &J&     Z &R   %  T ] & 7&<   7           +           #& V  ?  .  &X % h   C `  ` - 7      -     + +&! + + + + +# +P ++%  +.  "   , &P +# l + + b    + 2 K 8&  : +v 4&   x& f  $       1    0  + 9   H      + 2     .           l   N     :  +  ,                  %    ^  6 + c   &  +  8  +  & 0 +  9 A  . +  +  + U + ] +      Y 4  +  +   @   5 C /  +  )      )  $# # HRULE.GETFNMODERN +%/ (  + y z   &( +  +  +  +  + 9 2  D  (      #  3&  @ L& )  & + G &    +  +  +  +   2   < ~ Z& 8 $,  , , ,  &b  + P  a  r .&! N R  U&D L + 1  + &   .   & G&            )&  +   +  +  +  +  . 7   t     +    a  +   J ~   +  + C      H      E  4   v   +   V #   ,   @ '     J    J  X q +    +  +  +   F L  *  + c  4 9 G   +  +  + +  + +  + +  + +   +  +  +  +   + +   +  +  +  +   + &4  /   - 3&A j$! # HRULE.GETFNMODERN + !IM.INDEX.GETFNMODERN +2    9 p !    C    !           H   7 +   < H  R  %   "  +   T %   + +  $IM.INDEX.GETFN!    &   +      + %IM.INDEX.GETFNMODERN +    +  )  7 m 0  n #IM.INDEX.GETFNMODERN +   7           +  +    " ] 1 #  .  $IM.INDEX.GETFNMODERN +  <    L ?       6     a     _   +/      5 +"       "   ]  +$        6   "  2   + Q e       !$   + %IM.INDEX.GETFNMODERN + +       +  +  %   ,        &IM.INDEX.GETFN       +  >    +  1 E * +           +   + J   +      +    O  +    +$ + @    + %    l + & 'IM.INDEX.GETFN&         +  +         +    3 ' + H    g 1   +  +  +   [   +IM.INDEX.GETFN     %IM.INDEX.GETFNTITAN +4    5          0 7 .    $ E +     F     4 + )  X # 'IM.INDEX.GETFNTITAN +8     +    y G        (IM.INDEX.GETFN       ) > +  +  +  + / "IM.INDEX.GETFN    P      $IM.INDEX.GETFN        0            7 W   +  +  !    +  %   +  +     +  +        +  +   ]  2 & e 5   +  @ + &  + &IM.INDEX.GETFNMODERN +   +  5  G  E  I   + + +  R +   +  +       8 '   3 (    "IM.INDEX.GETFN     <  $      & *  : )   ! +    2  l    + %IM.INDEX.GETFNMODERN +       +  %IM.INDEX.GETFNMODERN +   +  D +    + +    9 +   C   ; +  +  !  6   9 :  " +  (IM.INDEX.GETFN"        )    h +   U    +  k    ,   $* # HRULE.GETFNMODERN +%~  c & + $  +  C 9 "       g 6 ,  &6    #  M 0    P  Q <  +  :?  #  A   Y + J   +  &;    J W S  ) ,       -  ' *  1 E ? ! ! ( u &M `&}#z \ No newline at end of file diff --git a/docs/porter-irm/14-ERRORS.TEDIT b/docs/porter-irm/14-ERRORS.TEDIT new file mode 100644 index 00000000..39473ea5 Binary files /dev/null and b/docs/porter-irm/14-ERRORS.TEDIT differ diff --git a/docs/porter-irm/15-BREAKING.TEDIT b/docs/porter-irm/15-BREAKING.TEDIT new file mode 100644 index 00000000..9f2f4075 Binary files /dev/null and b/docs/porter-irm/15-BREAKING.TEDIT differ diff --git a/docs/porter-irm/16-LIST-STRUCTURE.TEDIT b/docs/porter-irm/16-LIST-STRUCTURE.TEDIT new file mode 100644 index 00000000..76fd2792 Binary files /dev/null and b/docs/porter-irm/16-LIST-STRUCTURE.TEDIT differ diff --git a/docs/porter-irm/17-FILEPACKAGE.TEDIT b/docs/porter-irm/17-FILEPACKAGE.TEDIT new file mode 100644 index 00000000..00bc2150 --- /dev/null +++ b/docs/porter-irm/17-FILEPACKAGE.TEDIT @@ -0,0 +1,3477 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 17. FILE PACKAGE 1 17. FILE PACKAGE 1 17. FILE PACKAGE 6 Warning: The subsystem within the Interlisp-D environment used for managing collections of definitions (of functions, variables, etc.) is known as the "File Package." This terminology is confusing, because the word "file" is also used in the more conventional sense as meaning a collection of data stored some physical media. Unfortunately, it is not possible to change this terminology at this time, because many functions and variables (MAKEFILE, FILEPKGTYPES, etc.) incorporate the word "file" in their names. Eventually, the system and the documentation will be revamped to consistantly use the term "module" or "definition group" or "defgroup." Most implementations of Lisp treat symbolic files as unstructured text, much as they are treated in most conventional programming environments. Function definitions are edited with a character-oriented text editor, and then the changed definitions (or sometimes the entire file) is read or compiled to install those changes in the running memory image. Interlisp incorporates a different philosophy. A symbolic file is considered as a database of information about a group of data objects---function definitions, variable values, record declarations, etc. The text in a symbolic file is never edited directly. Definitions are edited only after their textual representations on files have been converted to data-structures that reside inside the Lisp address space. The programs for editing definitions inside Interlisp can therefore make use of the full set of data-manipulation capabilities that the environment already provides, and editing operations can be easily intermixed with the processes of evaluation and compilation. Interlisp is thus a "resident" programming environment, and as such it provides facilities for moving definitions back and forth between memory and the external databases on symbolic files, and for doing the bookkeeping involved when definitions on many symbolic files with compiled counterparts are being manipulated. The file package provides those capabilities. It removes from the user the burden of keeping track of where things are and what things have changed. The file package also keeps track of which files have been modified and need to be updated and recompiled. The file package is integrated into many other system packages. For example, if only the compiled version of a file is loaded and the user attempts to edit a function, the file package will attempt to load the source of that function from the appropriate symbolic file. In many cases, if a datum is needed by some program, the file package will automatically retrieve it from a file if it is not already in the user's working environment. Some of the operations of the file package are rather complex. For example, the same function may appear in several different files, or the symbolic or compiled files may be in different directories, etc. Therefore, this chapter does not document how the file package works in each and every situation, but instead makes the deliberately vague statement that it does the "right" thing with respect to keeping track of what has been changed, and what file operations need to be performed in accordance with those changes. For a simple illustration of what the file package does, suppose that the symbolic file FOO contains the functions FOO1 and FOO2, and that the file BAR contains the functions BAR1 and BAR2. These two files could be loaded into the environment with the function LOAD: _ (LOAD 'FOO) FILE CREATED 4-MAR-83 09:26:55 FOOCOMS {DSK}FOO.;1 _ (LOAD 'BAR) FILE CREATED 4-MAR-83 09:27:24 BARCOMS {DSK}BAR.;1 Now, suppose that we change the definition of FOO2 with the editor, and we define two new functions, NEW1 and NEW2. At that point, the file package knows that the in-memory definition of FOO2 is no longer consistent with the definition in the file FOO, and that the new functions have been defined but have not yet been associated with a symbolic file and saved on permanent storage. The function FILES? summarizes this state of affairs and enters into an interactive dialog in which we can specify what files the new functions are to belong to. _ (FILES?) FOO...to be dumped. plus the functions: NEW1,NEW2 want to say where the above go ? Yes (functions) NEW1 File name: BAR NEW2 File name: ZAP new file ? Yes NIL The file package knows that the file FOO has been changed, and needs to be dumped back to permanent storage. This can be done with MAKEFILE. _(MAKEFILE 'FOO) {DSK}FOO.;2 Since we added NEW1 to the old file BAR and established a new file ZAP to contain NEW2, both BAR and ZAP now also need to be dumped. This is confirmed by a second call to FILES?: _ (FILES?) BAR, ZAP...to be dumped. FOO...to be listed. FOO...to be compiled NIL We are also informed that the new version we made of FOO needs to be listed (sent to a printer) and that the functions on the file must be compiled. Rather than doing several MAKEFILEs to dump the files BAR and ZAP, we can simply call CLEANUP. Without any further user interaction, this will dump any files whose definitions have been modified. CLEANUP will also send any unlisted files to the printer and recompile any files which need to be recompiled. CLEANUP is a useful function to use at the end of a debugging session. It will call FILES? if any new objects have been defined, so the user does not lose the opportunity to say explicitly where those belong. In effect, the function CLEANUP executes all the operations necessary to make the user's permanent files consistent with the definitions in his current core-image. _ (CLEANUP) FOO...compiling {DSK}FOO.;2 . . . BAR...compiling {DSK}BAR.;2 . . . ZAP...compiling {DSK}ZAP.;1 . . . In addition to the definitions of functions, symbolic files in Interlisp can contain definitions of a variety of other types, e.g. variable values, property lists, record declarations, macro definitions, hash arrays, etc. In order to treat such a diverse assortment of data uniformly from the standpoint of file operations, the file package uses the concept of a typed definition, of which a function definition is just one example. A typed definition associates with a name (usually a litatom), a definition of a given type (called the file package type). Note that the same name may have several definitions of different types. For example, a litatom may have both a function definition and a variable definition. The file package also keeps track of the files that a particular typed definition is stored on, so one can think of a typed definition as a relation between four elements: a name, a definition, a type, and a file. Symbolic files on permanent storage devices are referred to by names that obey the naming conventions of those devices, usually including host, directory, and version fields. When such definition groups are noticed by the file package, they are assigned simple root names and these are used by all file package operations to refer to those groups of definitions. The root name for a group is computed from its full permanent storage name by applying the function ROOTFILENAME; this strips off the host, directory, version, etc., and returns just the simple name field of the file. For each file, the file package also has a data structure that describes what definitions it contains. This is known as the commands of the file, or its "filecoms". By convention, the filecoms of a file whose root name is X is stored as the value of the litatom XCOMS. For example, the value of FOOCOMS is the filecoms for the file FOO. This variable can be directly manipulated, but the file package contains facilities such as FILES? which make constructing and updating filecoms easier, and in some cases automatic. See page X.XX. The file package is able to maintain its databases of information because it is notified by various other routines in the system when events take place that may change that database. A file is "noticed" when it is loaded, or when a new file is stored (though there are ways to explicitly notice files without completely loading all their definitions). Once a file is noticed, the file package takes it into account when modifying filecoms, dumping files, etc. The file package also needs to know what typed definitions have been changed or what new definitions have been introduced, so it can determine which files need to be updated. This is done by "marking changes". All the system functions that perform file package operations (LOAD, TCOMPL, PRETTYDEF, etc.), as well as those functions that define or change data, (EDITF, EDITV, EDITP, DWIM corrections to user functions) interact with the file package. Also, typed-in assignment of variables or property values is noticed by the file package. (Note that modifications to variable or property values during the execution of a function body are not noticed.) In some cases the marking procedure can be subtle, e.g. if the user edits a property list using EDITP, only those properties whose values are actually changed (or added) are marked. All file package operations can be disabled with FILEPKGFLG. FILEPKGFLG [Variable] 1 The file package can be disabled by setting FILEPKGFLG to NIL. This will turn off noticing files and marking changes. FILEPKGFLG is initially T. 1 The rest of this chapter goes into further detail about the file package. Functions for loading and storing symbolic files are presented first, followed by functions for adding and removing typed definitions from files, moving typed definitions from one file to another, determining which file a particular definition is stored in, and so on. LOADING FILES 1 LOADING FILES 1 2 0.1 Loading Files 1 The functions below load information from symbolic files into the Interlisp environment. A symbolic file contains a sequence of Interlisp expressions that can be evaluated to establish specified typed definitions. The expressions on symbolic files are read using FILERDTBL as the read table. The loading functions all have an argument LDFLG. LDFLG affects the operation of DEFINE, DEFINEQ, RPAQ, RPAQ?, and RPAQQ. While a source file is being loaded, DFNFLG (page X.XX) is rebound to LDFLG. Thus, if LDFLG=NIL, and a function is redefined, a message is printed and the old definition saved. If LDFLG=T, the old definition is simply overwritten. If LDFLG=PROP, the functions are stored as "saved" definitions on the property lists under the property EXPR instead of being installed as the active definitions. If LDFLG=ALLPROP, not only function definitions but also variables set by RPAQQ, RPAQ, RPAQ? are stored on property lists (except when the variable has the value NOBIND, in which case they are set to the indicated value regardless of DFNFLG). Another option is available for users who are loading systems for others to use and who wish to suppress the saving of information used to aid in development and debugging. If LDFLG=SYSLOAD, LOAD will: (1) Rebind DFNFLG to T, so old definitions are simply overwritten; (2) Rebind LISPXHIST to NIL, thereby making the LOAD not be undoable and eliminating the cost of saving undo information (See page X.XX); (3) Rebind ADDSPELLFLG to NIL, to suppress adding to spelling lists; (4) Rebind FILEPKGFLG to NIL, to prevent the file from being "noticed" by the file package; (5) Rebind BUILDMAPFLG to NIL, to prevent a file map from being constructed; (6) After the load has completed, set the filecoms variable and any filevars variables to NOBIND; and (7) Add the file name to SYSFILES rather than FILELST. Note: A filevars variable is any variable appearing in a file package command of the form (FILECOM * VARIABLE) (see page X.XX). Therefore, if the filecoms includes (FNS * FOOFNS), FOOFNS is set to NOBIND. If the user wants the value of such a variable to be retained, even when the file is loaded with LDFLG=SYSLOAD, then he should replace the variable with an equivalent, non-atomic expression, such as (FNS * (PROGN FOOFNS)). All functions that have LDFLG as an argument perform spelling correction using LOADOPTIONS as a spelling list when LDFLG is not a member of LOADOPTIONS. LOADOPTIONS is initially (NIL T PROP ALLPROP SYSLOAD). (LOAD FILE LDFLG PRINTFLG) [Function] 1 Reads successive expressions from FILE (with FILERDTBL as read table) and evaluates each as it is read, until it reads either NIL, or the single atom STOP. Note that LOAD can be used to load both symbolic and compiled files. Returns FILE (full name). If PRINTFLG=T, LOAD prints the value of each expression; otherwise it does not. 1 (LOAD? FILE LDFLG PRINTFLG) [Function] 1 Similar to LOAD except that it does not load FILE if it has already been loaded, in which case it returns NIL. Note: LOAD? loads FILE except when the same version of the file has been loaded (either from the same place, or from a copy of it from a different place). Specifically, LOAD? considers that FILE has already been loaded if the full name of FILE is on LOADEDFILELST (page X.XX) or the date stored on the FILEDATES property of the root file name of FILE is the same as the FILECREATED expression on FILE. 1 (LOADFNS FNS FILE LDFLG VARS) [Function] 1 Permits selective loading of definitions. FNS is a list of function names, a single function name, or T, meaning to load all of the functions on the file. FILE can be either a compiled or symbolic file. If a compiled definition is loaded, so are all compiler-generated subfunctions. The interpretation of LDFLG is the same as for LOAD. If FILE=NIL, LOADFNS will use WHEREIS (page X.XX) to determine where the first function in FNS resides, and load from that file. Note that the file must previously have been "noticed" (see page X.XX). If WHEREIS returns NIL, and the WHEREIS library package has been loaded, LOADFNS will use the WHEREIS data base to find the file containing FN. VARS specifies which non-DEFINEQ expressions are to be loaded (i.e., evaluated). It is interpreted as follows: T Means to load all non-DEFINEQ expressions. NIL Means to load none of the non-DEFINEQ expressions. VARS Means to evaluate all variable assignment expressions (beginning with RPAQ, RPAQQ, or RPAQ?, see page X.XX). Any other litatom Means the same as specifying a list containing that atom. A list If VARS is a list that is not a valid function definition, each element in VARS is "matched" against each non-DEFINEQ expression, and if any elements in VARS "match" successfully, the expression is evaluated. "Matching" is defined as follows: If an element of VARS is an atom, it matches an expression if it is EQ to either the CAR or the CADR of the expression. If an element of VARS is a list, it is treated as an edit pattern (page X.XX), and matched with the entire expression (using EDIT4E, page X.XX). For example, if VARS was (FOOCOMS DECLARE: (DEFLIST & (QUOTE MACRO))), this would cause (RPAQQ FOOCOMS ...), all DECLARE:s, and all DEFLISTs which set up MACROs to be read and evaluated. A function definition If VARS is a list and a valid function definition ((FNTYP VARS) is true), then LOADFNS will invoke that function on every non-DEFINEQ expression being considered, applying it to two arguments, the first and second elements in the expression. If the function returns NIL, the expression will be skipped; if it returns a non-NIL litatom (e.g. T), the expression will be evaluated; and if it returns a list, this list is evaluated instead of the expression. Note: The file pointer is set to the very beginning of the expression before calling the VARS function definition, so it may read the entire expression if necessary. If the function returns a litatom, the file pointer is reset and the expression is READ or SKREAD. However, the file pointer is not reset when the function returns a list, so the function must leave it set immediately after the expression that it has presumably read. LOADFNS returns a list of: (1) The names of the functions that were found; (2) A list of those functions not found (if any) headed by the litatom NOT-FOUND:; (3) All of the expressions that were evaluated; (4) A list of those members of VARS for which no corresponding expressions were found (if any), again headed by the litatom NOT-FOUND:. For example, _ (LOADFNS '(FOO FIE FUM) FILE NIL '(BAZ (DEFLIST &))) (FOO FIE (NOT-FOUND: FUM) (RPAQ BAZ ...) (NOT-FOUND: (DEFLIST &))) 1 (LOADVARS VARS FILE LDFLG) [Function] 1 Same as (LOADFNS NIL FILE LDFLG VARS). 1 (LOADFROM FILE FNS LDFLG) [Function] 1 Same as (LOADFNS FNS FILE LDFLG T). 1 Once the file package has noticed a file, the user can edit functions contained in the file without explicitly loading them. Similarly, those functions which have not been modified do not have to be loaded in order to write out an updated version of the file. Files are normally noticed (i.e., their contents become known to the file package; see page X.XX) when either the symbolic or compiled versions of the file are loaded. If the file is not going to be loaded completely, the preferred way to notice it is with LOADFROM. Note that the user can also load some functions at the same time by giving LOADFROM a second argument, but it is normally used simply to inform the file package about the existence and contents of a particular file. (LOADBLOCK FN FILE LDFLG) [Function] 1 Calls LOADFNS on those functions contained in the block declaration containing FN (See page X.XX). LOADBLOCK is designed primarily for use with symbolic files, to load the EXPRs for a given block. It will not load a function which already has an in-core EXPR definition, and it will not load the block name, unless it is also one of the block functions. 1 (LOADCOMP FILE LDFLG) [Function] 1 Performs all operations on FILE associated with compilation, i.e. evaluates all expressions under a DECLARE: EVAL@COMPILE (see page X.XX), and "notices" the function and variable names by adding them to the lists NOFIXFNSLST and NOFIXVARSLST (see page X.XX). Thus, if building a system composed of many files with compilation information scattered among them, all that is required to compile one file is to LOADCOMP the others. 1 (LOADCOMP? FILE LDFLG) [Function] 1 Similar to LOADCOMP, except it does not load if file has already been loaded (with LOADCOMP), in which case its value is NIL. Note: LOADCOMP? will load the file even if it has been loaded with LOAD, LOADFNS, etc. The only time it will not load the file is if the file has already been loaded with LOADCOMP. 1 FILESLOAD provides an easy way for the user to load a series of files, setting various options: (FILESLOAD FILE1 ... FILEN) [NLambda NoSpread Function] 1 Loads the files FILE1 ... FILEN (all arguments unevaluated). If any of these arguments are lists, they specify certain loading options for all following files (unless changed by another list). Within these lists, the following commands are recognized: FROM DIR Search the specified directories for the file. DIR can either be a single directory, or a list of directories to search in order. For example, (FILESLOAD (FROM {ERIS}SOURCES>) ...) will search the directory {ERIS}SOURCES> for the files. If this is not specified, the default is to search the contents of DIRECTORIES (page X.XX). If FROM is followed by the key word VALUEOF, the following word is evaluated, and the value is used as the list of directories to search. For example, (FILESLOAD (FROM VALUEOF FOO) ...) will search the directory list that is the value of the variable FOO. As a special case, if DIR is a litatom, and the litatom DIRDIRECTORIES is bound, the value of this variable is used as the directory search list. For example, since the variable LISPUSERSDIRECTORIES (page X.XX) is commonly used to contain a list of directories containing "library" packages, (FILESLOAD (FROM LISPUSERS) ...) can be used instead of (FILESLOAD (FROM VALUEOF LISPUSERSDIRECTORIES) ...) Note: If a FILESLOAD is read and evaluated while loading a file, and it doesn't contain a FROM expression, the default is to search the directory containing the FILESLOAD expression before the value of DIRECTORIES. FILESLOAD expressions can be dumped on files using the FILES file package command (page X.XX). SOURCE Load the source version of the file rather than the compiled version. COMPILED Load the compiled version of the file. Note: If COMPILED is specified, the compiled version will be loaded, if it is found. The source will not be loaded. If neither SOURCE or COMPILED is specified, the compiled version of the file will be loaded if it is found, otherwise the source will be loaded if it is found. LOAD Load the file by calling LOAD, if it has not already been loaded. This is the default unless LOADCOMP or LOADFROM is specified. Note: If LOAD is specified, FILESLOAD considers that the file has already been loaded if the root name of the file has a non-NIL FILEDATES property. This is a somewhat different algorithm than LOAD? uses. In particular, FILESLOAD will not load a newer version of a file that has already been loaded. LOADCOMP Load the file with LOADCOMP? rather than LOAD. Automatically implies SOURCE. LOADFROM Load the file with LOADFROM rather than LOAD. NIL T PROP ALLPROP SYSLOAD The loading function is called with its LDFLG argument set to the specified token (see page X.XX). LDFLG affects the operation of the loading functions by resetting DFNFLG (page X.XX) to LDFLG during the loading. If none of these tokens are specified, the value of the variable LDFLG is used if it is bound, otherwise NIL is used. NOERROR If NOERROR is specified, no error occurs when a file is not found. Each list determines how all further files in the lists are loaded, unless changed by another list. The tokens above can be joined together in a single list. For example, (FILESLOAD (LOADCOMP) NET (SYSLOAD FROM VALUEOF NEWDIRECTORIES) CJSYS) will call LOADCOMP? to load the file NET searching the value of DIRECTORIES, and then call LOADCOMP? to load the file CJSYS with LDFLG set to SYSLOAD, searching the directory list that is the value of the variable NEWDIRECTORIES. FILESLOAD expressions can be dumped on files using the FILES file package command (page X.XX). 1 STORING FILES 1 STORING FILES 1 2 0.2 Storing Files 1 (MAKEFILE FILE OPTIONS REPRINTFNS SOURCEFILE) [Function] 1 Makes a new version of the file FILE, storing the information specified by FILE's filecoms. Notices FILE if not previously noticed (see page X.XX). Then, it adds FILE to NOTLISTEDFILES and NOTCOMPILEDFILES. OPTIONS is a litatom or list of litatoms which specify options. By specifying certain options, MAKEFILE can automatically compile or list FILE. Note that if FILE does not contain any function definitions, it is not compiled even when OPTIONS specifies C or RC. The options are spelling corrected using the list MAKEFILEOPTIONS. If spelling correction fails, MAKEFILE generates an error. The options are interpreted as follows: C RC After making FILE, MAKEFILE will compile FILE by calling TCOMPL (if C is specified) or RECOMPILE (if RC is specified). If there are any block declarations specified in the filecoms for FILE, BCOMPL or BRECOMPILE will be called instead. If F, ST, STF, or S is the next item on OPTIONS following C or RC, it is given to the compiler as the answer to the compiler's question LISTING? (see page X.XX). For example, (MAKEFILE 'FOO '(C F LIST)) will dump FOO, then TCOMPL or BCOMPL it specifying that functions are not to be redefined, and finally list the file. LIST After making FILE, MAKEFILE calls LISTFILES to print a hardcopy listing of FILE. CLISPIFY MAKEFILE calls PRETTYDEF with CLISPIFYPRETTYFLG=T (see page X.XX). This causes CLISPIFY to be called on each function defined as an EXPR before it is prettyprinted. Alternatively, if FILE has the property FILETYPE with value CLISP or a list containing CLISP, PRETTYDEF is called with CLISPIFYPRETTYFLG reset to CHANGES, which will cause CLISPIFY to be called on all functions marked as having been changed. If FILE has property FILETYPE with value CLISP, the compiler will DWIMIFY its functions before compiling them (see page X.XX). FAST MAKEFILE calls PRETTYDEF with PRETTYFLG=NIL (see page X.XX). This causes data objects to be printed rather than prettyprinted, which is much faster. REMAKE MAKEFILE "remakes" FILE: The prettyprinted definitions of functions that have not changed are copied from an earlier version of the symbolic file. Only those functions that have changed are prettyprinted. See page X.XX. NEW MAKEFILE does not remake FILE. If MAKEFILEREMAKEFLG=T (the initial setting), the default for all calls to MAKEFILE is to remake. The NEW option can be used to override this default. REPRINTFNS and SOURCEFILE are used when remaking a file, as described on page X.XX. Note: FILE is not added to NOTLISTEDFILES if FILE has on its property list the property FILETYPE with value DON'TLIST, or a list containing DON'TLIST. FILE is not added to NOTCOMPILEDFILES if FILE has on its property list the property FILETYPE with value DON'TCOMPILE, or a list containing DON'TCOMPILE. Also, if FILE does not contain any function definitions, it is not added to NOTCOMPILEDFILES, and it is not compiled even when OPTIONS specifies C or RC. 1 If a remake is not being performed, MAKEFILE checks the state of FILE to make sure that the entire source file was actually LOADed. If FILE was loaded as a compiled file, MAKEFILE prints the message CAN'T DUMP: ONLY THE COMPILED FILE HAS BEEN LOADED. Similarly, if only some of the symbolic definitions were loaded via LOADFNS or LOADFROM, MAKEFILE prints CAN'T DUMP: ONLY SOME OF ITS SYMBOLICS HAVE BEEN LOADED. In both cases, MAKEFILE will then ask the user if it should dump anyway; if the user declines, MAKEFILE does not call PRETTYDEF, but simply returns (FILE NOT DUMPED) as its value. The user can indicate that FILE must be block compiled together with other files as a unit by putting a list of those files on the property list of each file under the property FILEGROUP. If FILE has a FILEGROUP property, the compiler will not be called until all files on this property have been dumped that need to be. MAKEFILE operates by rebinding PRETTYFLG, PRETTYTRANFLG, and CLISPIFYPRETTYFLG, evaluating each expression on MAKEFILEFORMS (under errorset protection), and then calling PRETTYDEF (page X.XX). Note: PRETTYDEF calls PRETTYPRINT with its second argument PRETTYDEFLG=T, so whenever PRETTYPRINT (and hence MAKEFILE) start printing a new function, the name of that function is printed if more than 30 seconds (real time) have elapsed since the last time it printed the name of a function. (MAKEFILES OPTIONS FILES) [Function] 1 Performs (MAKEFILE FILE OPTIONS) for each file on FILES that needs to be dumped. If FILES=NIL, FILELST is used. For example, (MAKEFILES 'LIST) will make and list all files that have been changed. In this case, if any typed definitions for any items have been defined or changed and they are not contained in one of the files on FILELST, MAKEFILES calls ADDTOFILES? to allow the user to specify where these go. MAKEFILES returns a list of all files that are made. 1 (CLEANUP FILE1 FILE2 ... FILEN) [NLambda NoSpread Function] 1 Dumps, lists, and recompiles (with RECOMPILE or BRECOMPILE) any of the specified files (unevaluated) requiring the corresponding operation. If no files are specified, FILELST is used. CLEANUP returns NIL. CLEANUP uses the value of the variable CLEANUPOPTIONS as the OPTIONS argument to MAKEFILE. CLEANUPOPTIONS is initially (RC), to indicate that the files should be recompiled. If CLEANUPOPTIONS is set to (RC F), no listing will be performed, and no functions will be redefined as the result of compiling. Alternatively, if FILE1 is a list, it will be interpreted as the list of options regardless of the value of CLEANUPOPTIONS. 1 (FILES?) [Function] 1 Prints on the terminal the names of those files that have been modified but not dumped, dumped but not listed, dumped but not compiled, plus the names of any functions and other typed definitions (if any) that are not contained in any file. If there are any, FILES? then calls ADDTOFILES? to allow the user to specify where these go. 1 (ADDTOFILES? %) [Function] 1 Called from MAKEFILES, CLEANUP, and FILES? when there are typed definitions that have been marked as changed which do not belong to any file. ADDTOFILES? lists the names of the changed items, and asks the user if he wants to specify where these items should be put. If user answers N(o), ADDTOFILES? returns NIL without taking any action. If the user answers ], this is taken to be an answer to each question that would be asked, and all the changed items are marked as dummy items to be ignored. Otherwise, ADDTOFILES? prints the name of each changed item, and accepts one of the following responses: A file name A filevar If the user gives a file name or a variable whose value is a list (a filevar), the item is added to the corresponding file or list, using ADDTOFILE. If the user response is not the name of a file on FILELST or a variable whose value is a list, the user will be asked whether it is a new file. If he says no, then ADDTOFILES? will check whether the item is the name of a list, i.e. whether its value is a list. If not, the user will be asked whether it is a new list. line-feed Same as the user's previous response. space carriage return Take no action. ] The item is marked as a dummy item by adding it to NILCOMS. This tells the file package simply to ignore this item. [ The "definition" of the item in question is prettyprinted to the terminal, and then the user is asked again about its disposition. ( ADDTOFILES? prompts with "LISTNAME: (", the user types in the name of a list, i.e. a variable whose value is a list, terminated by a ). The item will then only be added to (under) a command in which the named list appears as a filevar. If none are found, a message is printed, and the user is asked again. For example, the user defines a new function FOO3, and when asked where it goes, types (FOOFNS). If the command (FNS * FOOFNS) is found, FOO3 will be added to the value of FOOFNS. If instead the user types (FOOCOMS), and the command (COMS * FOOCOMS) is found, then FOO3 will be added to a command for dumping functions that is contained in FOOCOMS. Note: If the named list is not also the name of a file, the user can simply type it in without parenthesis as described above. @ ADDTOFILES? prompts with "Near: (", the user types in the name of an object, and the item is then inserted in a command for dumping objects (of its type) that contains the indicated name. The item is inserted immediately after the indicated name. 1 (LISTFILES FILE1 FILE2 ... FILEN) [NLambda NoSpread Function] 1 Lists each of the specified files (unevaluated). If no files are given, NOTLISTEDFILES is used. Each file listed is removed from NOTLISTEDFILES if the listing is completed. For each file not found, LISTFILES prints the message "FILENAME NOT FOUND" and proceeds to the next file. LISTFILES calls the function LISTFILES1 on each file to be listed. Normally, LISTFILES1 is defined to simply call SEND.FILE.TO.PRINTER (page X.XX), but the user can advise or redefine LISTFILES1 for more specialized applications. Any lists inside the argument list to LISTFILES are interpreted as property lists that set the various printing options, such as the printer, number of copies, banner page name, etc (see page X.XX). Later properties override earlier ones. For example, (LISTFILES FOO (HOST JEDI) FUM (#COPIES 3) FIE) will cause one copy of FOO to be printed on the default printer, and 1 copy of FUM and 3 copies of FIE to be printed on the printer JEDI. 1 (COMPILEFILES FILE1 FILE2 ... FILEN) [NLambda NoSpread Function] 1 Executes the RC and C options of MAKEFILE for each of the specified files (unevaluated). If no files are given, NOTCOMPILEDFILES is used. Each file compiled is removed from NOTCOMPILEDFILES. If FILE1 is a list, it is interpreted as the OPTIONS argument to MAKEFILES. This feature can be used to supply an answer to the compiler's LISTING? question, e.g., (COMPILEFILES (STF)) will compile each file on NOTCOMPILEDFILES so that the functions are redefined without the EXPRs definitions being saved. 1 (WHEREIS NAME TYPE FILES FN) [Function] 1 TYPE is a file package type. WHEREIS sweeps through all the files on the list FILES and returns a list of all files containing NAME as a TYPE. WHEREIS knows about and expands all file package commands and file package macros. TYPE=NIL defaults to FNS (to retrieve function definitions). If FILES is not a list, the value of FILELST is used. If FN is given, it should be a function (with arguments NAME, FILE, and TYPE) which is applied for every file in FILES that contains NAME as a TYPE. In this case, WHEREIS returns NIL. If the WHEREIS library package has been loaded, WHEREIS is redefined so that FILES=T means to use the whereis package data base, so WHEREIS will find NAME even if the file has not been loaded or noticed. FILES=NIL always means use FILELST. 1 REMAKING A SYMBOLIC FILE 1 REMAKING A SYMBOLIC FILE 1 2 0.3 Remaking a Symbolic File 1 Most of the time that a symbolic file is written using MAKEFILE, only a few of the functions that it contains have been changed since the last time the file was written. Rather than prettprinting all of the functions, it is often considerably faster to "remake" the file, copying the prettprinted definitions of unchanged functions from an earlier version of the symbolic file, and only prettyprinting those functions that have been changed. MAKEFILE will remake the symbolic file if the REMAKE option is specified. If the NEW option is given, the file is not remade, and all of the functions are prettprinted. The default action is specified by the value of MAKEFILEREMAKEFLG: if T (its initial value), MAKEFILE will remake files unless the NEW option is given; if NIL, MAKEFILE will not remake unless the REMAKE option is given. Note: If the file has never been loaded or dumped, for example if the filecoms were simply set up in memory, then MAKEFILE will never attempt to remake the file, regardless of the setting of MAKEFILEREMAKEFLG, or whether the REMAKE option was specified. When MAKEFILE is remaking a symbolic file, the user can explicitly indicate the functions which are to be prettyprinted and the file to be used for copying the rest of the function definitions from via the REPRINTFNS and SOURCEFILE arguments to MAKEFILE. Normally, both of these arguments are defaulted to NIL. In this case, REPRINTFNS will be set to those functions that have been changed since the last version of the file was written. For SOURCEFILE, MAKEFILE obtains the full name of the most recent version of the file (that it knows about) from the FILEDATES property of the file, and checks to make sure that the file still exists and has the same file date as that stored on the FILEDATES property. If it does, MAKEFILE uses that file as SOURCEFILE. This procedure permits the user to LOAD or LOADFROM a file in a different directory, and still be able to remake the file with MAKEFILE. In the case where the most recent version of the file cannot be found, MAKEFILE will attempt to remake using the original version of the file (i.e., the one first loaded), specifying as REPRINTFNS the union of all changes that have been made since the file was first loaded, which is obtained from the FILECHANGES property of the file. If both of these fail, MAKEFILE prints the message "CAN'T FIND EITHER THE PREVIOUS VERSION OR THE ORIGINAL VERSION OF FILE, SO IT WILL HAVE TO BE WRITTEN ANEW", and does not remake the file, i.e. will prettyprint all of the functions. When a remake is specified, MAKEFILE also checks to see how the file was originally loaded (see page X.XX). If the file was originally loaded as a compiled file, MAKEFILE will call LOADVARS to obtain those DECLARE: expressions that are contained on the symbolic file, but not the compiled file, and hence have not been loaded. If the file was loaded by LOADFNS (but not LOADFROM), then LOADVARS is called to obtain any non-DEFINEQ expressions. Before calling LOADVARS to re-load definitions, MAKEFILE asks the user, e.g. "Only the compiled version of FOO was loaded, do you want to LOADVARS the (DECLARE: .. DONTCOPY ..) expressions from {DSK}FOO.;3?". The user can respond Yes to execute the LOADVARS and continue the MAKEFILE, No to proceed with the MAKEFILE without performing the LOADVARS, or Abort to abort the MAKEFILE. The use may wish to skip the LOADVARS if the user had circumvented the file package in some way, and loading the old definitions would overwrite new ones. Note: Remaking a symbolic file is considerably faster if the earlier version has a file map indicating where the function definitions are located (page X.XX), but it does not depend on this information. LOADING FILES IN A DISTRIBUTED ENVIRONMENT 1 LOADING FILES IN A DISTRIBUTED ENVIRONMENT 1 2 0.4 Loading Files in a Distributed Environment 1 Each Interlisp source and compiled code file contains the full filename of the file, including the host and directory names, in a FILECREATED expression at the beginning of the file. The compiled code file also contains the full file name of the source file it was created from. In earlier versions of Interlisp, the file package used this information to locate the appropriate source file when "remaking" or recompiling a file. This turned out to be a bad feature in distributed environments, where users frequently move files from one place to another, or where files are stored on removable media. For example, suppose you MAKEFILE to a floppy, and then copy the file to a file server. If you loaded and edited the file from a file server, and tried to do MAKEFILE, it would try to locate the source file on the floppy, which is probably no longer loaded. Currently, the file package searches for sources file on the connected directory, and on the directory search path (on the variable DIRECTORIES). If it is not found, the host/directory information from the FILECREATED expression be used. Warning: One situation where the new algorithm does the wrong thing is if you explicitly LOADFROM a file that is not on your directory search path. Future MAKEFILEs and CLEANUPs will search the connected directory and DIRECTORIES to find the source file, rather than using the file that the LOADFROM was done from. Even if the correct file is on the directory search path, you could still create a bad file if there is another version of the file in an earlier directory on the search path. In general, you should either explicitly specify the SOURCEFILE argument to MAKEFILE to tell it where to get the old source, or connect to the directory where the correct source file is. MARKING CHANGES 1 MARKING CHANGES 1 2 0.5 Marking Changes 1 The file package needs to know what typed definitions have been changed, so it can determine which files need to be updated. This is done by "marking changes". All the system functions that perform file package operations (LOAD, TCOMPL, PRETTYDEF, etc.), as well as those functions that define or change data, (EDITF, EDITV, EDITP, DWIM corrections to user functions) interact with the file package by marking changes. Also, typed-in assignment of variables or property values is noticed by the file package. (Note that if a program modifies a variable or property value, this is not noticed.) In some cases the marking procedure can be subtle, e.g. if the user edits a property list using EDITP, only those properties whose values are actually changed (or added) are marked. The various system functions which create or modify objects call MARKASCHANGED to mark the object as changed. For example, when a function is defined via DEFINE or DEFINEQ, or modified via EDITF, or a DWIM correction, the function is marked as being a changed object of type FNS. Similarly, whenever a new record is declared, or an existing record redeclared or edited, it is marked as being a changed object of type RECORDS, and so on for all of the other file package types. The user can also call MARKASCHANGED directly to mark objects of a particular file package type as changed: (MARKASCHANGED NAME TYPE REASON) [Function] 1 Marks NAME of type TYPE as being changed. MARKASCHANGED returns NAME. MARKASCHANGED is undoable. REASON is a litatom that indicated how NAME was changed. MARKASCHANGED recognizes the following values for REASON: DEFINED Used to indicate the creation of NAME, e.g. from DEFINEQ (page X.XX). CHANGED Used to indicate a change to NAME, e.g. from the editor. DELETED Used to indicate the deletion of NAME, e.g. by DELDEF (page X.XX). CLISP Used to indicate the modification of NAME by CLISP translation. For backwards compatibility, MARKASCHANGED also accepts a REASON of T (=DEFINED) and NIL (=CHANGED). New programs should avoid using these values. Note: The variable MARKASCHANGEDFNS is a list of functions that MARKASCHANGED calls (with arguments NAME, TYPE, and REASON). Functions can be added to this list to "advise" MARKASCHANGED to do additional work for all types of objects. The WHENCHANGED file package type property (page X.XX) can be used to specify additional actions when MARKASCHANGED gets called on specific types of objects. 1 (UNMARKASCHANGED NAME TYPE) [Function] 1 Unmarks NAME of type TYPE as being changed. Returns NAME if NAME was marked as changed and is now unmarked, NIL otherwise. UNMARKASCHANGED is undoable. 1 (FILEPKGCHANGES TYPE LST) [NoSpread Function] 1 If LST is not specified (as opposed to being NIL), returns a list of those objects of type TYPE that have been marked as changed but not yet associated with their corresponding files (See page X.XX). If LST is specified, FILEPKGCHANGES sets the corresponding list. (FILEPKGCHANGES) returns a list of all objects marked as changed as a list of elements of the form (TYPENAME . CHANGEDOBJECTS). 1 Some properties (e.g. EXPR, ADVICE, MACRO, I.S.OPR, etc..) are used to implement other file package types. For example, if the user changes the value of the property I.S.OPR, he is really changing an object of type I.S.OPR, and the effect is the same as though he had redefined the i.s.opr via a direct call to the function I.S.OPR. If a property whose value has been changed or added does not correspond to a specific file package type, then it is marked as a changed object of type PROPS whose name is (VARIABLENAME PROPNAME) (except if the property name has a property PROPTYPE with value IGNORE). Similarly, if the user changes a variable which implements the file package type ALISTS (as indicated by the appearance of the property VARTYPE with value ALIST on the variable's property list), only those entries that are actually changed are marked as being changed objects of type ALISTS, and the "name" of the object will be (VARIABLENAME KEY) where KEY is CAR of the entry on the alist that is being marked. If the variable corresponds to a specific file package type other than ALISTS, e.g. USERMACROS, LISPXMACROS, etc., then an object of that type is marked. In this case, the name of the changed object will be CAR of the corresponding entry on the alist. For example, if the user edits LISPXMACROS and changes a definition for PL, then the object PL of type LISPXMACROS is marked as being changed. NOTICING FILES 1 NOTICING FILES 1 2 0.6 Noticing Files 1 Already existing files are "noticed" by LOAD or LOADFROM (or by LOADFNS or LOADVARS when the VARS argument is T. New files are noticed when they are constructed by MAKEFILE, or when definitions are first associated with them via FILES? or ADDTOFILES?. Noticing a file updates certain lists and properties so that the file package functions know to include the file in their operations. For example, CLEANUP will only dump files that have been noticed. The user can explicitly tell the file package to notice a newly-created file by defining the filecoms for the file (see page X.XX), and calling ADDFILE: (ADDFILE FILE % % %) [Function] 1 Tells the file package that FILE should be recognized as a file; it adds FILE to FILELST, and also sets up the FILE property of FILE to reflect the current set of changes which are "registered against" FILE. 1 The file package uses information stored on the property list of the root name of noticed files. The following property names are used: FILE [Property Name] 1 When a file is noticed, the property FILE, value ((FILECOMS . LOADTYPE)) is added to the property list of its root name. FILECOMS is the variable containing the filecoms of the file (see page X.XX). LOADTYPE indicates how the file was loaded, e.g., completely loaded, only partially loaded as with LOADFNS, loaded as a compiled file, etc. The property FILE is used to determine whether or not the corresponding file has been modified since the last time it was loaded or dumped. CDR of the FILE property records by type those items that have been changed since the last MAKEFILE. Whenever a file is dumped, these items are moved to the property FILECHANGES, and CDR of the FILE property is reset to NIL. 1 FILECHANGES [Property Name] 1 The property FILECHANGES contains a list of all changed items since the file was loaded (there may have been several sequences of editing and rewriting the file). When a file is dumped, the changes in CDR of the FILE property are added to the FILECHANGES property. 1 FILEDATES [Property Name] 1 The property FILEDATES contains a list of version numbers and corresponding file dates for this file. These version numbers and dates are used for various integrity checks in connection with remaking a file (see page X.XX). 1 FILEMAP [Property Name] 1 The property FILEMAP is used to store the filemap for the file (see page X.XX). This is used to directly load individual functions from the middle of a file. 1 To compute the root name, ROOTFILENAME is applied to the name of the file as indicated in the FILECREATED expression appearing at the front of the file, since this name corresponds to the name the file was originally made under. The file package detects that the file being noticed is a compiled file (regardless of its name), by the appearance of more than one FILECREATED expressions. In this case, each of the files mentioned in the following FILECREATED expressions are noticed. For example, if the user performs (BCOMPL '(FOO FIE)), and subsequently loads FOO.DCOM, both FOO and FIE will be noticed. When a file is noticed, its root name is added to the list FILELST: FILELST [Variable] 1 Contains a list of the root names of the files that have been noticed. 1 LOADEDFILELST [Variable] 1 Contains a list of the actual names of the files as loaded by LOAD, LOADFNS, etc. For example, if the user performs (LOAD 'EDITA.COM;3), EDITA will be added to FILELST, but EDITA.COM;3 is added to LOADEDFILELST. LOADEDFILELST is not used by the file package; it is maintained solely for the user's benefit. 1 DISTRIBUTING CHANGE INFORMATION 1 DISTRIBUTING CHANGE INFORMATION 1 2 0.7 Distributing Change Information 1 Periodically, the function UPDATEFILES is called to find which file(s) contain the elements that have been changed. UPDATEFILES is called by FILES?, CLEANUP, and MAKEFILES, i.e., any procedure that requires the FILE property to be up to date. This procedure is followed rather than updating the FILE property after each change because scanning FILELST and examining each file package command can be a time-consuming process; this is not so noticeable when performed in conjunction with a large operation like loading or writing a file. UPDATEFILES operates by scanning FILELST and interrogating the file package commands for each file. When (if) any files are found that contain the corresponding typed definition, the name of the element is added to the value of the property FILE for the corresponding file. Thus, after UPDATEFILES has completed operating, the files that need to be dumped are simply those files on FILELST for which CDR of their FILE property is non-NIL. For example, if the user loads the file FOO containing definitions for FOO1, FOO2, and FOO3, edits FOO2, and then calls UPDATEFILES, (GETPROP 'FOO 'FILE) will be ((FOOCOMS . T) (FNS FOO2)). If any objects marked as changed have not been transferred to the FILE property for some file, e.g., the user defines a new function but forgets (or declines) to add it to the file package commands for the corresponding file, then both FILES? and CLEANUP will print warning messages, and then call ADDTOFILES? to permit the user to specify on which files these items belong. The user can also invoke UPDATEFILES directly: (UPDATEFILES % %) [Function] 1 (UPDATEFILES) will update the FILE properties of the noticed files. 1 FILE PACKAGE TYPES 1 FILE PACKAGE TYPES 1 2 0.8 File Package Types 1 In addition to the definitions of functions and values of variables, source files in Interlisp can contain a variety of other information, e.g. property lists, record declarations, macro definitions, hash arrays, etc. In order to treat such a diverse assortment of data uniformly from the standpoint of file operations, the file package uses the concept of a typed definition, of which a function definition is just one example. A typed definition associates with a name (usually a litatom), a definition of a given type (called the file package type). Note that the same name may have several definitions of different types. For example, a litatom may have both a function definition and a variable definition. The file package also keeps track of the file that a particular typed definition is stored on, so one can think of a typed definition as a relation between four elements: a name, a definition, a type, and a file. A file package type is an abstract notion of a class of objects which share the property that every object of the same file package type is stored, retrieved, edited, copied etc., by the file package in the same way. Each file package type is identified by a litatom, which can be given as an argument to the functions that manipulate typed definitions. The user may define new file package types, as described in page X.XX. FILEPKGTYPES [Variable] 1 The value of FILEPKGTYPES is a list of all file package types, including any that may have been defined by the user. 1 The file package is initialized with the following built-in file package types: ADVICE [File Package Type] 1 Used to access "advice" modifying a function (see page X.XX). 1 ALISTS [File Package Type] 1 Used to access objects stored on an association list that is the value of a litatom (see page X.XX). A variable is declared to have an association list as its value by putting on its property list the property VARTYPE with value ALIST. In this case, each dotted pair on the list is an object of type ALISTS. When the value of such a variable is changed, only those entries in the association list that are actually changed or added are marked as changed objects of type ALISTS (with "name" (LITATOM KEY)). Objects of type ALISTS are dumped via the ALISTS or ADDVARS file package commands. Note that some association lists are used to "implement" other file package types. For example, the value of the global variable USERMACROS implements the file package type USERMACROS and the values of LISPXMACROS and LISPXHISTORYMACROS implement the file package type LISPXMACROS. This is indicated by putting on the property list of the variable the property VARTYPE with value a list of the form (ALIST FILEPKGTYPE). For example, (GETPROP 'LISPXHISTORYMACROS 'VARTYPE) => (ALIST LISPXMACROS). 1 COURIERPROGRAMS [File Package Type] 1 Used to access Courier programs (see page X.XX). 1 EXPRESSIONS [File Package Type] 1 Used to access lisp expressions that are put on a file by using the REMEMBER programmers assistant command (page X.XX), or by explicitly putting the P file package command (page X.XX) on the filecoms. 1 FIELDS [File Package Type] 1 Used to access fields of records. The "definition" of an object of type FIELDS is a list of all the record declarations which contain the name. See page X.XX. 1 FILEPKGCOMS [File Package Type] 1 Used to access file package commands and types. A single name can be defined both as a file package type and a file package command. The "definition" of an object of type FILEPKGCOMS is a list structure of the form ((COM . COMPROPS) (TYPE . TYPEPROPS)), where COMPROPS is a property list specifying how the name is defined as a file package command by FILEPKGCOM (page X.XX), and TYPEPROPS is a property list specifying how the name is defined as a file package type by FILEPKGTYPE (page X.XX). 1 FILES [File Package Type] 1 Used to access files. This file package type is most useful for renaming files. The "definition" of a file is not a useful structure. 1 FILEVARS [File Package Type] 1 Used to access Filevars (see page X.XX). 1 FNS [File Package Type] 1 Used to access function definitions. 1 I.S.OPRS [File Package Type] 1 Used to access the definitions of iterative statement operators (see page X.XX). 1 LISPXMACROS [File Package Type] 1 Used to access programmer's assistant commands defined on the variables LISPXMACROS and LISPXHISTORYMACROS (see page X.XX). 1 MACROS [File Package Type] 1 Used to access macro definitions (see page X.XX). 1 PROPS [File Package Type] 1 Used to access objects stored on the property list of a litatom (see page X.XX). When a property is changed or added, an object of type PROPS, with "name" (LITATOM PROPNAME) is marked as being changed. Note that some litatom properties are used to implement other file package types. For example, the property MACRO implements the file package type MACROS, the property ADVICE implements ADVICE, etc. This is indicated by putting the property PROPTYPE, with value of the file package type on the property list of the property name. For example, (GETPROP 'MACRO 'PROPTYPE) => MACROS. When such a property is changed or added, an object of the corresponding file package type is marked. If (GETPROP PROPNAME 'PROPTYPE) => IGNORE, the change is ignored. The FILE, FILEMAP, FILEDATES, etc. properties are all handled this way. (Note that IGNORE cannot be the name of a file package type implemented as a property). 1 RECORDS [File Package Type] 1 Used to access record declarations (see page X.XX). 1 RESOURCES [File Package Type] 1 Used to access resources (see page X.XX). 1 TEMPLATES [File Package Type] 1 Used to access Masterscope templates (see page X.XX). 1 USERMACROS [File Package Type] 1 Used to access user edit macros (see page X.XX). 1 VARS [File Package Type] 1 Used to access top-level variable values. 1 0.8.1 Functions for Manipulating Typed Definitions 1 The functions described below can be used to manipulate typed definitions, without needing to know how the manipulations are done. For example, (GETDEF 'FOO 'FNS) will return the function definition of FOO, (GETDEF 'FOO 'VARS) will return the variable value of FOO, etc. All of the functions use the following conventions: (1) All functions which make destructive changes are undoable. (2) Any argument that expects a list of litatoms will also accept a single litatom, operating as though it were enclosed in a list. For example, if the argument FILES should be a list of files, it may also be a single file. (3) TYPE is a file package type. TYPE=NIL is equivalent to TYPE=FNS. The singular form of a file package type is also recognized, e.g. TYPE=VAR is equivalent to TYPE=VARS. (4) FILES=NIL is equivalent to FILES=FILELST. (5) SOURCE is used to indicate the source of a definition, that is, where the definition should be found. SOURCE can be one of: CURRENT Get the definition currently in effect. SAVED Get the "saved" definition, as stored by SAVEDEF (page X.XX). FILE Get the definition contained on the (first) file determined by WHEREIS (page X.XX). Note: WHEREIS is called with FILES=T, so that if the WHEREIS library package is loaded, the WHEREIS data base will be used to find the file containing the definition. ? Get the definition currently in effect if there is one, else the saved definition if there is one, otherwise the definition from a file determined by WHEREIS. Like specifying CURRENT, SAVED, and FILE in order, and taking the first definition that is found. a file name a list of file names Get the definition from the first of the indicated files that contains one. NIL In most cases, giving SOURCE=NIL (or not specifying it at all) is the same as giving ?, to get either the current, saved, or filed definition. However, with HASDEF, SOURCE=NIL is interpreted as equal to SOURCE=CURRENT, which only tests if there is a current definition. The operation of most of the functions described below can be changed or extended by modifying the appropriate properties for the corresponding file package type using the function FILEPKGTYPE, described on page X.XX. (GETDEF NAME TYPE SOURCE OPTIONS) [Function] 1 Returns the definition of NAME, of type TYPE, from SOURCE. For most types, GETDEF returns the expression which would be pretty printed when dumping NAME as TYPE. For example, for TYPE=FNS, an EXPR definition is returned, for TYPE=VARS, the value of NAME is returned, etc. OPTIONS is a list which specifies certain options: NOERROR GETDEF causes an error if an appropriate definition cannot be found, unless OPTIONS is or contains NOERROR. In this case, GETDEF returns the value of the NULLDEF file package type property (page X.XX), usually NIL. a string If OPTIONS is or contains a string, that string will be returned if no definition is found (and NOERROR is not among the options). The caller can thus determine whether a definition was found, even for types for which NIL or NOBIND are acceptable definitions. NOCOPY GETDEF returns a copy of the definition unless OPTIONS is or contains NOCOPY. EDIT If OPTIONS is or contains EDIT, GETDEF returns a copy of the definition unless it is possible to edit the definition "in place." With some file package types, such as functions, it is meaningful (and efficient) to edit the definition by destructively modifying the list structure, without calling PUTDEF. However, some file package types (like records) need to be "installed" with PUTDEF after they are edited. The default EDITDEF (see page X.XX) calls GETDEF with OPTIONS of (EDIT NOCOPY), so it doesn't use a copy unless it has to, and only calls PUTDEF if the result of editing is not EQUAL to the old definition. NODWIM A FNS definition will be dwimified if it is likely to contain CLISP unless OPTIONS is or contains NODWIM. 1 (PUTDEF NAME TYPE DEFINITION REASON) [Function] 1 Defines NAME of type TYPE with DEFINITION. For TYPE=FNS, does a DEFINE; for TYPE=VARS, does a SAVESET, etc. For TYPE=FILES, PUTDEF establishes the command list, notices NAME, and then calls MAKEFILE to actually dump the file NAME, copying functions if necessary from the "old" file (supplied as part of DEFINITION). PUTDEF calls MARKASCHANGED (page X.XX) to mark NAME as changed, giving a reason of REASON. If REASON is NIL, the default is DEFINED. Note: If TYPE=FNS, PUTDEF prints a warning if the user tries to redefine a function on the list UNSAFE.TO.MODIFY.FNS (page X.XX). 1 (HASDEF NAME TYPE SOURCE SPELLFLG) [Function] 1 Returns (OR NAME T) if NAME is the name of something of type TYPE. If not, attempts spelling correction if SPELLFLG=T, and returns the spelling-corrected NAME. Otherwise returns NIL. (HASDEF NIL TYPE) returns T if NIL has a valid definition. Note: if SOURCE=NIL, HASDEF interprets this as equal to SOURCE=CURRENT, which only tests if there is a current definition. 1 (TYPESOF NAME POSSIBLETYPES IMPOSSIBLETYPES SOURCE) [Function] 1 Returns a list of the types in POSSIBLETYPES but not in IMPOSSIBLETYPES for which NAME has a definition. FILEPKGTYPES is used if POSSIBLETYPES is NIL. 1 (COPYDEF OLD NEW TYPE SOURCE OPTIONS) [Function] 1 Defines NEW to have a copy of the definition of OLD by doing PUTDEF on a copy of the definition retrieved by (GETDEF OLD TYPE SOURCE OPTIONS). NEW is substituted for OLD in the copied definition, in a manner that may depend on the TYPE. For example, (COPYDEF 'PDQ 'RST 'FILES) sets up RSTCOMS to be a copy of PDQCOMS, changes things like (VARS * PDQVARS) to be (VARS * RSTVARS) in RSTCOMS, and performs a MAKEFILE on RST such that the appropriate definitions get copied from PDQ. COPYDEF disables the NOCOPY option of GETDEF, so NEW will always have a copy of the definition of OLD. Note: COPYDEF substitutes NEW for OLD throughout the definition of OLD. This is usually the right thing to do, but in some cases, e.g., where the old name appears within a quoted expression but was not used in the same context, the user must re-edit the definition. 1 (DELDEF NAME TYPE) [Function] 1 Removes the definition of NAME as a TYPE that is currently in effect. 1 (SHOWDEF NAME TYPE FILE) [Function] 1 Prettyprints the definition of NAME as a TYPE to FILE. This shows the user how NAME would be written to a file. Used by ADDTOFILES? (page X.XX). 1 (EDITDEF NAME TYPE SOURCE EDITCOMS) [Function] 1 Edits the definition of NAME as a TYPE. Essentially performs (PUTDEF NAME TYPE (EDITE (GETDEF NAME TYPE SOURCE) EDITCOMS)) 1 (SAVEDEF NAME TYPE DEFINITION) [Function] 1 Sets the "saved" definition of NAME as a TYPE to DEFINITION. If DEFINITION=NIL, the current definition of NAME is saved. If TYPE=FNS (or NIL), the function definition is saved on NAME's property list under the property EXPR, or CODE (depending on the FNTYP of the function definition). If (GETD NAME) is non-NIL, but (FNTYP FN)=NIL, SAVEDEF saves the definition on the property name LIST. This can happen if a function was somehow defined with an illegal expr definition, such as (LAMMMMDA (X) ...). If TYPE=VARS, the definition is stored as the value of the VALUE property of NAME. For other types, the definition is stored in an internal data structure, from where it can be retrieved by GETDEF or UNSAVEDEF. 1 (UNSAVEDEF NAME TYPE %) [Function] 1 Restores the "saved" definition of NAME as a TYPE, making it be the current definition. Returns PROP. If TYPE=FNS (or NIL), UNSAVEDEF unsaves the function definition from the EXPR property if any, else CODE, and returns the property name used. UNSAVEDEF also recognizes TYPE=EXPR, CODE, or LIST, meaning to unsave the definition only from the corresponding property only. If DFNFLG is not T (see page X.XX), the current definition of NAME, if any, is saved using SAVEDEF. Thus one can use UNSAVEDEF to switch back and forth between two definitions. 1 (LOADDEF NAME TYPE SOURCE) [Function] 1 Equivalent to (PUTDEF NAME TYPE (GETDEF NAME TYPE SOURCE)). LOADDEF is essentially a generalization of LOADFNS, e.g. it enables loading a single record declaration from a file. Note that (LOADDEF FN) will give FN an EXPR definition, either obtained from its property list or a file, unless it already has one. 1 (CHANGECALLERS OLD NEW TYPES FILES METHOD) [Function] 1 Finds all of the places where OLD is used as any of the types in TYPES and changes those places to use NEW. For example, (CHANGECALLERS 'NLSETQ 'ERSETQ) will change all calls to NLSETQ to be calls to ERSETQ. Also changes occurrences of OLD to NEW inside the filecoms of any file, inside record declarations, properties, etc. CHANGECALLERS attempts to determine if OLD might be used as more than one type; for example, if it is both a function and a record field. If so, rather than performing the transformation OLD -> NEW automatically, the user is allowed to edit all of the places where OLD occurs. For each occurrence of OLD, the user is asked whether he wants to make the replacement. If he responds with anything except Yes or No, the editor is invoked on the expression containing that occurrence. There are two different methods for determining which functions are to be examined. If METHOD=EDITCALLERS, EDITCALLERS is used to search FILES (see page X.XX). If METHOD=MASTERSCOPE, then the Masterscope database is used instead. METHOD=NIL defaults to MASTERSCOPE if the value of the variable DEFAULTRENAMEMETHOD is MASTERSCOPE and a Masterscope database exists, otherwise it defaults to EDITCALLERS. 1 (RENAME OLD NEW TYPES FILES METHOD) [Function] 1 First performs (COPYDEF OLD NEW TYPE) for all TYPE inside TYPES. It then calls CHANGECALLERS to change all occurrences of OLD to NEW, and then "deletes" OLD with DELDEF. For example, if the user has a function FOO which he now wishes to call FIE, he simply performs (RENAME 'FOO 'FIE), and FIE will be given FOO's definition, and all places that FOO are called will be changed to call FIE instead. METHOD is interpreted the same as the METHOD argument to CHANGECALLERS, above. 1 (COMPARE NAME1 NAME2 TYPE SOURCE1 SOURCE2) [Function] 1 Compares the definition of NAME1 with that of NAME2, by calling COMPARELISTS (page X.XX) on (GETDEF NAME1 TYPE SOURCE1) and (GETDEF NAME2 TYPE SOURCE2), which prints their differences on the terminal. For example, if the current value of the variable A is (A B C (D E F) G), and the value of the variable B on the file FOO is (A B C (D F E) G), then: _(COMPARE 'A 'B 'VARS 'CURRENT 'FOO) A from CURRENT and B from TEST differ: (E -> F) (F -> E) T 1 (COMPAREDEFS NAME TYPE SOURCES) [Function] 1 Calls COMPARELISTS (page X.XX) on all pairs of definitions of NAME as a TYPE obtained from the various SOURCES (interpreted as a list of source specifications). 1 0.8.2 Defining New File Package Types 1 All manipulation of typed definitions in the file package is done using the type-independent functions GETDEF, PUTDEF, etc. Therefore, to define a new file package type, it is only necessary to specify (via the function FILEPKGTYPE) what these functions should do when dealing with a typed definition of the new type. Each file package type has the following properties, whose values are functions or lists of functions: Note: These functions are defined to take a TYPE argument so that the user may have the same function for more than one type. GETDEF [File Package Type Property] 1 Value is a function of three arguments, NAME, TYPE, and OPTIONS, which should return the current definition of NAME as a type TYPE. Used by GETDEF (page X.XX), which passes its OPTIONS argument. If there is no GETDEF property, a file package command for dumping NAME is created (by MAKENEWCOM). This command is then used to write the definition of NAME as a type TYPE onto the file FILEPKG.SCRATCH (in Interlisp-D, this file is created on the {CORE} device). This expression is then read back in and returned as the current definition. Note: In some situations, the function HASDEF (page X.XX) needs to call GETDEF to determine whether a definition exists. In this case, OPTIONS will include the litatom HASDEF, and it is permissable for a GETDEF function to return T or NIL, rather than creating a complex structure which will not be used. 1 NULLDEF [File Package Type Property] 1 The value of the NULLDEF property is returned by GETDEF (page X.XX) when there is no definition and the NOERROR option is supplied. For example, the NULLDEF of VARS is NOBIND. 1 FILEGETDEF [File Package Type Property] 1 This enables the user to provide a way of obtaining definitions from a file that is more efficient than the default procedure used by GETDEF (page X.XX). Value is a function of four arguments, NAME, TYPE, FILE, and OPTIONS. The function is applied by GETDEF when it is determined that a typed definition is needed from a particular file. The function must open and search the given file and return any TYPE definition for NAME that it finds. 1 CANFILEDEF [File Package Type Property] 1 If the value of this property is non-NIL, this indicates that definitions of this file package type are not loaded when a file is loaded with LOADFROM (page X.XX). The default is NIL. Initially, only FNS has this property set to non-NIL. 1 PUTDEF [File Package Type Property] 1 Value is a function of three arguments, NAME, TYPE, and DEFINITION, which should store DEFINITION as the definition of NAME as a type TYPE. Used by PUTDEF (page X.XX). 1 HASDEF [File Package Type Property] 1 Value is a function of three arguments, NAME, TYPE, and SOURCE, which should return (OR NAME T) if NAME is the name of something of type TYPE. SOURCE is as interpreted by HASDEF (page X.XX), which uses this property. 1 EDITDEF [File Package Type Property] 1 Value is a function of four arguments, NAME, TYPE, SOURCE, and EDITCOMS, which should edit the definition of NAME as a type TYPE from the source SOURCE, interpreting the edit commands EDITCOMS. If sucessful, should return NAME (or a spelling-corrected NAME). If it returns NIL, the "default" editor is called. Used by EDITDEF (page X.XX). 1 DELDEF [File Package Type Property] 1 Value is a function of two arguments, NAME, and TYPE, which removes the definition of NAME as a TYPE that is currently in effect. Used by DELDEF (page X.XX). 1 NEWCOM [File Package Type Property] 1 Value is a function of four arguments, NAME, TYPE, LISTNAME, and FILE. Specifies how to make a new (instance of a) file package command to dump NAME, an object of type TYPE. The function should return the new file package command. Used by ADDTOFILE and SHOWDEF. If LISTNAME is non-NIL, this means that the user specified LISTNAME as the filevar in his interaction with ADDTOFILES? (see page X.XX). If no NEWCOM is specified, the default is to call DEFAULTMAKENEWCOM, which will construct and return a command of the form (TYPE NAME). DEFAULTMAKENEWCOM can be advised or redefined by the user. 1 WHENCHANGED [File Package Type Property] 1 Value is a list of functions to be applied to NAME, TYPE, and REASON when NAME, an instance of type TYPE, is changed or defined (see MARKASCHANGED, page X.XX). Used for various applications, e.g. when an object of type I.S.OPRS changes, it is necessary to clear the corresponding translatons from CLISPARRAY. The WHENCHANGED functions are called before the object is marked as changed, so that it can, in fact, decide that the object is not to be marked as changed, and execute (RETFROM 'MARKASCHANGED). Note: For backwards compatibility, the REASON argument passed to WHENCHANGED functions is either T (for DEFINED) and NIL (for CHANGED). 1 WHENFILED [File Package Type Property] 1 Value is a list of functions to be applied to NAME, TYPE, and FILE when NAME, an instance of type TYPE, is added to FILE. 1 WHENUNFILED [File Package Type Property] 1 Value is a list of functions to be applied to NAME, TYPE, and FILE when NAME, an instance of type TYPE, is removed from FILE. 1 DESCRIPTION [File Package Type Property] 1 Value is a string which describes instances of this type. For example, for type RECORDS, the value of DESCRIPTION is the string "record declarations". 1 The function FILEPKGTYPE is used to define new file package types, or to change the properties of existing types. Note that it is possible to redefine the attributes of system file package types, such as FNS or PROPS. (FILEPKGTYPE TYPE PROP1 VAL1 ... PROPN VALN) [NoSpread Function] 1 Nospread function for defining new file package types, or changing properties of existing file package types. PROPi is one of the property names given above; VALi is the value to be given to that property. Returns TYPE. (FILEPKGTYPE TYPE PROP) returns the value of the property PROP, without changing it. (FILEPKGTYPE TYPE) returns an alist of all of the defined properties of TYPE, using the property names as keys. Note: Specifying TYPE as the litatom TYPE can be used to define one file package type as a synonym of another. For example, (FILEPKGTYPE 'R 'TYPE 'RECORDS) defines R as a synonym for the file package type RECORDS. 1 FILE PACKAGE COMMANDS 1 FILE PACKAGE COMMANDS 1 2 0.9 File Package Commands 1 The basic mechanism for creating symbolic files is the function MAKEFILE (page X.XX). For each file, the file package has a data structure known as the "filecoms", which specifies what typed descriptions are contained in the file. A filecoms is a list of file package commands, each of which specifies objects of a certain file package type which should be dumped. For example, the filecoms ( (FNS FOO) (VARS FOO BAR BAZ) (RECORDS XYZZY) ) has a FNS, a VARS, and a RECORDS file package command. This filecoms specifies that the function definition for FOO, the variable values of FOO, BAR, and BAZ, and the record declaration for XYZZY should be dumped. By convention, the filecoms of a file X is stored as the value of the litatom XCOMS. For example, (MAKEFILE 'FOO.;27) will use the value of FOOCOMS as the filecoms. This variable can be directly manipulated, but the file package contains facilities which make constructing and updating filecoms easier, and in some cases automatic (See page X.XX). A file package command is an instruction to MAKEFILE to perform an explicit, well-defined operation, usually printing an expression. Usually there is a one-to-one correspondence between file package types and file package commands; for each file package type, there is a file package command which is used for writing objects of that type to a file, and each file package command is used to write objects of a particular type. However, in some cases, the same file package type can be dumped by several different file package commands. For example, the file package commands PROP, IFPROP, and PROPS all dump out objects with the file package type PROPS. This means if the user changes an object of file package type PROPS via EDITP, a typed-in call to PUTPROP, or via an explicit call to MARKASCHANGED, this object can be written out with any of the above three commands. Thus, when the file package attempts to determine whether this typed object is contained on a particular file, it must look at instances of all three file package commands PROP, IFPROP, and PROPS, to see if the corresponding atom and property are specified. It is also permissible for a single file package command to dump several different file package types. For example, the user can define a file package command which dumps both a function definition and its macro. Conversely, some file package comands do not dump any file package types at all, such as the E command. For each file package command, the file package must be able to determine what typed definitions the command will cause to be printed so that the file package can determine on what file (if any) an object of a given type is contained (by searching through the filecoms). Similarly, for each file package type, the file package must be able to construct a command that will print out an object of that type. In other words, the file package must be able to map file package commands into file package types, and vice versa. Information can be provided to the file package about a particular file package command via the function FILEPKGCOM (page X.XX), and information about a particular file package type via the function FILEPKGTYPE (page X.XX). In the absence of other information, the default is simply that a file package command of the form (X NAME) prints out the definition of NAME as a type X, and, conversely, if NAME is an object of type X, then NAME can be written out by a command of the form (X NAME). If a file package function is given a command or type that is not defined, it attempts spelling correction using FILEPKGCOMSPLST as a spelling list (unless DWIMFLG or NOSPELLFLG=NIL; see page X.XX). If successful, the corrected version of the list of file package commands is written (again) on the output file, since at this point, the uncorrected list of file package commands would already have been printed on the output file. When the file is loaded, this will result in FILECOMS being reset, and may cause a message to be printed, e.g., (FOOCOMS RESET). The value of FOOCOMS would then be the corrected version. If the spelling correction is unsuccessful, the file package functions generate an error, BAD FILE PACKAGE COMMAND. File package commands can be used to save on the output file definitions of functions, values of variables, property lists of atoms, advised functions, edit macros, record declarations, etc. The interpretation of each file package command is documented in the following sections. (USERMACROS LITATOM1 ... LITATOMN) [File Package Command] 1 Each litatom LITATOMi is the name of a user edit macro. Writes expressions to add the edit macro definitions of LITATOMi to USERMACROS, and adds the names of the commands to the appropriate spelling lists. If LITATOMi is not a user macro, a warning message "no EDIT MACRO for LITATOMi" is printed. 1 0.9.1 Functions and Macros 1 (FNS FN1 ... FNN) [File Package Command] 1 Writes a DEFINEQ expression with the function definitions of FN1 ... FNN. The user should never print a DEFINEQ expression directly onto a file himself (by using the P file package command, for example), because MAKEFILE generates the filemap of function definitions from the FNS file package commands (see page X.XX). 1 (ADVISE FN1 ... FNN) [File Package Command] 1 For each function FNi, writes expressions to reinstate the function to its advised state when the file is loaded. See page X.XX. Note: When advice is applied to a function programmatically or by hand, it is additive. That is, if a function already has some advice, further advice is added to the already-existing advice. However, when advice is applied to a function as a result of loading a file with an ADVISE file package command, the new advice replaces any earlier advice. ADVISE works this way to prevent problems with loading different versions of the same advice. If the user really wants to apply additive advice, a file package command such as (P (ADVISE ...)) should be used (see page X.XX). 1 (ADVICE FN1 ... FNN) [File Package Command] 1 For each function FNi, writes a PUTPROPS expression which will put the advice back on the property list of the function. The user can then use READVISE (page X.XX) to reactivate the advice. 1 (MACROS LITATOM1 ... LITATOMN) [File Package Command] 1 Each LITATOMi is a litatom with a MACRO definition (and/or a DMACRO, 10MACRO, etc.). Writes out an expression to restore all of the macro properties for each LITATOMi, embedded in a DECLARE: EVAL@COMPILE so the macros will be defined when the file is compiled. See page X.XX. 1 0.9.2 Variables 1 (VARS VAR1 ... VARN) [File Package Command] 1 For each VARi, writes an expression to set its top level value when the file is loaded. If VARi is atomic, VARS writes out an expression to set VARi to the top-level value it had at the time the file was written. If VARi is non-atomic, it is interpreted as (VAR FORM), and VARS write out an expression to set VAR to the value of FORM (evaluated when the file is loaded). VARS prints out expressions using RPAQQ and RPAQ, which are like SETQQ and SETQ except that they also perform some special operations with respect to the file package (see page X.XX). Note: VARS cannot be used for putting arbitrary variable values on files. For example, if the value of a variable is an array (or many other data types), a litatom which represents the array is dumped in the file instead of the array itself. The HORRIBLEVARS file package command (page X.XX) provides a way of saving and reloading variables whose values contain re-entrant or circular list structure, user data types, arrays, or hash arrays. 1 (INITVARS VAR1 ... VARN) [File Package Command] 1 INITVARS is used for initializing variables, setting their values only when they are currently NOBIND. A variable value defined in an INITVARS command will not change an already established value. This means that re-loading files to get some other information will not automatically revert to the initialization values. The format of an INITVARS command is just like VARS. The only difference is that if VARi is atomic, the current value is not dumped; instead NIL is defined as the initialization value. Therefore, (INITVARS FOO (FUM 2)) is the same as (VARS (FOO NIL)(FUM 2)), if FOO and FUM are both NOBIND. INITVARS writes out an RPAQ? expression on the file instead of RPAQ or RPAQQ. 1 (ADDVARS (VAR1 . LST1) ... (VARN . LSTN)) [File Package Command] 1 For each (VARi . LSTi), writes an ADDTOVAR (page X.XX) to add each element of LSTi to the list that is the value of VARi at the time the file is loaded. The new value of VARi will be the union of its old value and LSTi. If the value of VARi is NOBIND, it is first set to NIL. For example, (ADDVARS (DIRECTORIES LISP LISPUSERS)) will add LISP and LISPUSERS to the value of DIRECTORIES. If LSTi is not specified, VARi is initialized to NIL if its current value is NOBIND. In other words, (ADDVARS (VAR)) will initialize VAR to NIL if VAR has not previously been set. 1 (APPENDVARS (VAR1 . LST1) ... (VARN . LSTN)) [File Package Command] 1 The same as ADDVARS, except that the values are added to the end of the lists (using APPENDTOVAR, page X.XX), rather than at the beginning. 1 (UGLYVARS VAR1 ... VARN) [File Package Command] 1 Like VARS, except that the value of each VARi may contain structures for which READ is not an inverse of PRINT, e.g. arrays, readtables, user data types, etc. Uses HPRINT (page X.XX). 1 (HORRIBLEVARS VAR1 ... VARN) [File Package Command] 1 Like UGLYVARS, except structures may also contain circular pointers. Uses HPRINT (page X.XX). The values of VAR1 ... VARN are printed in the same operation, so that they may contain pointers to common substructures. UGLYVARS does not do any checking for circularities, which results in a large speed and internal-storage advantage over HORRIBLEVARS. Thus, if it is known that the data structures do not contain circular pointers, UGLYVARS should be used instead of HORRIBLEVARS. 1 (ALISTS (VAR1 KEY1 KEY2 ...) ... (VARN KEY3 KEY4 ...) ) [File Package Command] 1 VARi is a variable whose value is an association list, such as EDITMACROS, BAKTRACELST, etc. For each VARi, ALISTS writes out expressions which will restore the values associated with the specified keys. For example, (ALISTS (BREAKMACROS BT BTV)) will dump the definition for the BT and BTV commands on BREAKMACROS. Some association lists (USERMACROS, LISPXMACROS, etc.) are used to implement other file package types, and they have their own file package commands. 1 (SPECVARS VAR1 ... VARN) [File Package Command] 1 (LOCALVARS VAR1 ... VARN) [File Package Command] 1 (GLOBALVARS VAR1 ... VARN) [File Package Command] 1 Outputs the corresponding compiler declaration embedded in a DECLARE: DOEVAL@COMPILE DONTCOPY. See page X.XX. 1 (CONSTANTS VAR1 ... VARN) [File Package Command] 1 Like VARS, for each VARi writes an expression to set its top level value when the file is loaded. Also writes a CONSTANTS expression to declare these variables as constants (see page X.XX). Both of these expressions are wrapped in a (DECLARE: EVAL@COMPILE ...) expression, so they can be used by the compiler. Like VARS, VARi can be non-atomic, in which case it is interpreted as (VAR FORM), and passed to CONSTANTS (along with the variable being initialized to FORM). 1 0.9.3 Litatom Properties 1 (PROP PROPNAME LITATOM1 ... LITATOMN) [File Package Command] 1 Writes a PUTPROPS expression to restore the value of the PROPNAME property of each litatom LITATOMi when the file is loaded. If PROPNAME is a list, expressions will be written for each property on that list. If PROPNAME is the litatom ALL, the values of all user properties (on the property list of each LITATOMi) are saved. SYSPROPS is a list of properties used by system functions. Only properties not on that list are dumped when the ALL option is used. If LITATOMi does not have the property PROPNAME (as opposed to having the property with value NIL), a warning message "NO PROPNAME PROPERTY FOR LITATOMi" is printed. The command IFPROP can be used if it is not known whether or not an atom will have the corresponding property. 1 (IFPROP PROPNAME LITATOM1 ... LITATOMN) [File Package Command] 1 Same as the PROP file package command, except that it only saves the properties that actually appear on the property list of the corresponding atom. For example, if FOO1 has property PROP1 and PROP2, FOO2 has PROP3, and FOO3 has property PROP1 and PROP3, then (IFPROP (PROP1 PROP2 PROP3) FOO1 FOO2 FOO3) will save only those five property values. 1 (PROPS (LITATOM1 PROPNAME1) ... (LITATOMN PROPNAMEN) ) [File Package Command] 1 Similar to PROP command. Writes a PUTPROPS expression to restore the value of PROPNAMEi for each LITATOMi when the file is loaded. As with the PROP command, if LITATOMi does not have the property PROPNAME (as opposed to having the property with NIL value), a warning message "NO PROPNAMEi PROPERTY FOR LITATOMi" is printed. 1 0.9.4 Miscellaneous File Package Commands 1 (RECORDS REC1 ... RECN) [File Package Command] 1 Each RECi is the name of a record (see page X.XX). Writes expressions which will redeclare the records when the file is loaded. 1 (INITRECORDS REC1 ... RECN) [File Package Command] 1 Similar to RECORDS, INITRECORDS writes expressions on a file that will, when loaded, perform whatever initialization/allocation is necessary for the indicated records. However, the record declarations themselves are not written out. This facility is useful for building systems on top of Interlisp, in which the implementor may want to eliminate the record declarations from a production version of the system, but the allocation for these records must still be done. 1 (LISPXMACROS LITATOM1 ... LITATOMN) [File Package Command] 1 Each LITATOMi is defined on LISPXMACROS or LISPXHISTORYMACROS (see page X.XX). Writes expressions which will save and restore the definition for each macro, as well as making the necessary additions to LISPXCOMS 1 (I.S.OPRS OPR1 ... OPRN) [File Package Command] 1 Each OPRi is the name of a user-defined i.s.opr (see page X.XX). Writes expressions which will redefine the i.s.oprs when the file is loaded. 1 (RESOURCES RESOURCE1 ... RESOURCEN) [File Package Command] 1 Each RESOURCESi is the name of a resource (see page X.XX). Writes expressions which will redeclare the resource when the file is loaded. 1 (INITRESOURCES RESOURCE1 ... RESOURCEN) [File Package Command] 1 Parallel to INITRECORDS (page X.XX), INITRESOURCES writes expressions on a file to perform whatever initialization/allocation is necessary for the indicated resources, without writing the resource declaration itself. 1 (COURIERPROGRAMS NAME1 ... NAMEN) [File Package Command] 1 Each NAMEi is the name of a Courier program (see page X.XX). Writes expressions which will redeclare the Courier program when the file is loaded. 1 (TEMPLATES LITATOM1 ... LITATOMN) [File Package Command] 1 Each LITATOMi is a litatom which has a Masterscope template (see page X.XX). Writes expressions which will restore the templates when the file is loaded. 1 (FILES FILE1 ... FILEN) [File Package Command] 1 Used to specify auxiliary files to be loaded in when the file is loaded. Dumps an expression calling FILESLOAD (page X.XX), with FILE1 ... FILEN as the arguments. FILESLOAD interprets FILE1 ... FILEN as files to load, possibly interspersed with lists used to specify certain loading options. 1 (FILEPKGCOMS LITATOM1 ... LITATOMN) [File Package Command] 1 Each litatom LITATOMi is either the name of a user-defined file package command or a user-defined file package type (or both). Writes expressions which will restore each command/type. If LITATOMi is not a file package command or type, a warning message "no FILE PACKAGE COMMAND for LITATOMi" is printed. 1 (* . TEXT) [File Package Command] 1 Used for inserting comments in a file. The file package command is simply written on the output file; it will be ignored when the file is loaded. If the first element of TEXT is another *, a form-feed is printed on the file before the comment. 1 (P EXP1 ... EXPN) [File Package Command] 1 Writes each of the expressions EXP1 ... EXPN on the output file, where they will be evaluated when the file is loaded. 1 (E FORM1 ... FORMN) [File Package Command] 1 Each of the forms FORM1 ... FORMN is evaluated at output time, when MAKEFILE interpretes this file package command. 1 (COMS COM1 ... COMN) [File Package Command] 1 Each of the commands COM1 ... COMN is interpreted as a file package command. 1 (ORIGINAL COM1 ... COMN) [File Package Command] 1 Each of the commands COMi will be interpreted as a file package command without regard to any file package macros (as defined by the MACRO property of the FILEPKGCOM function, page X.XX). Useful for redefining a built-in file package command in terms of itself. Note that some of the "built-in" file package commands are defined by file package macros, so interpreting them (or new user-defined file package commands) with ORIGINAL will fail. ORIGINAL was never intended to be used outside of a file package command macro. 1 0.9.5 DECLARE: 1 (DECLARE: . FILEPKGCOMS/FLAGS) [File Package Command] 1 Normally expressions written onto a symbolic file are (1) evaluated when loaded; (2) copied to the compiled file when the symbolic file is compiled (see page X.XX); and (3) not evaluated at compile time. DECLARE: allows the user to override these defaults. FILEPKGCOMS/FLAGS is a list of file package commands, possibly interspersed with "tags". The output of those file package commands within FILEPKGCOMS/FLAGS is embedded in a DECLARE: expression, along with any tags that are specified. For example, (DECLARE: EVAL@COMPILE DONTCOPY (FNS ...) (PROP ...)) would produce (DECLARE: EVAL@COMPILE DONTCOPY (DEFINEQ ...) (PUTPROPS ...)). DECLARE: is defined as an nlambda nospread function, which processes its arguments by evaluating or not evaluating each expression depending on the setting of internal state variables. The initial setting is to evaluate, but this can be overridden by specifying the DONTEVAL@LOAD tag. DECLARE: expressions are specially processed by the compiler. For the purposes of compilation, DECLARE: has two principal applications: (1) to specify forms that are to be evaluated at compile time, presumably to affect the compilation, e.g., to set up macros; and/or (2) to indicate which expressions appearing in the symbolic file are not to be copied to the output file. (Normally, expressions are not evaluated and are copied.) Each expression in CDR of a DECLARE: form is either evaluated/not-evaluated and copied/not-copied depending on the settings of two internal state variables, initially set for copy and not-evaluate. These state variables can be reset for the remainder of the expressions in the DECLARE: by means of the tags DONTCOPY, EVAL@COMPILE, etc. The tags are: EVAL@LOAD DOEVAL@LOAD Evaluate the following forms when the file is loaded (unless overridden by DONTEVAL@LOAD). DONTEVAL@LOAD Do not evaluate the following forms when the file is loaded. EVAL@LOADWHEN This tag can be used to provide conditional evaluation. The value of the expression immediately following the tag determines whether or not to evaluate subsequent expressions when loading. ... EVAL@LOADWHEN T ... is equivalent to ... EVAL@LOAD ... COPY DOCOPY When compiling, copy the following forms into the compiled file. DONTCOPY When compiling, do not copy the following forms into the compiled file. Note: If the file package commands following DONTCOPY include record declarations for datatypes, or records with initialization forms, it is necessary to include a INITRECORDS file package command (page X.XX) outside of the DONTCOPY form so that the initialization information is copied. For example, if FOO was defined as a datatype, (DECLARE: DONTCOPY (RECORDS FOO)) (INITRECORDS FOO) would copy the data type declaration for FOO, but would not copy the whole record declaration. COPYWHEN When compiling, if the next form evaluates to non-NIL, copy the following forms into the compiled file. EVAL@COMPILE DOEVAL@COMPILE When compiling, evaluate the following forms. DONTEVAL@COMPILE When compiling, do not evaluate the following forms. EVAL@COMPILEWHEN When compiling, if the next form evaluates to non-NIL, evaluate the following forms. FIRST For expressions that are to be copied to the compiled file, the tag FIRST can be used to specify that the following expressions in the DECLARE: are to appear at the front of the compiled file, before anything else except the FILECREATED expressions (see page X.XX). For example, (DECLARE: COPY FIRST (P (PRINT MESS1 T)) NOTFIRST (P (PRINT MESS2 T))) will cause (PRINT MESS1 T) to appear first in the compiled file, followed by any functions, then (PRINT MESS2 T). NOTFIRST Reverses the effect of FIRST. The value of DECLARETAGSLST is a list of all the tags used in DECLARE: expressions. If a tag not on this list appears in a DECLARE: file package command, spelling correction is performed using DECLARETAGSLST as a spelling list. Note that the function LOADCOMP (page X.XX) provides a convenient way of obtaining information from the DECLARE: expressions in a file, without reading in the entire file. This information may be used for compiling other files. 1 (BLOCKS BLOCK1 ... BLOCKN) [File Package Command] 1 For each BLOCKi, writes a DECLARE: expression which the block compile functions interpret as a block declaration. See page X.XX. 1 0.9.6 Exporting Definitions 1 When building a large system in Interlisp, it is often the case that there are record definitions, macros and the like that are needed by several different system files when running, analyzing and compiling the source code of the system, but which are not needed for running the compiled code. By using the DECLARE: file package command with tag DONTCOPY (page X.XX), these definitions can be kept out of the compiled files, and hence out of the system constructed by loading the compiled files files into Interlisp. This saves loading time, space in the resulting system, and whatever other overhead might be incurred by keeping those definitions around, e.g., burden on the record package to consider more possibilities in translating record accesses, or conflicts between system record fields and user record fields. However, if the implementor wants to debug or compile code in the resulting system, the definitions are needed. And even if the definitions had been copied to the compiled files, a similar problem arises if one wants to work on system code in a regular Interlisp environment where none of the system files had been loaded. One could mandate that any definition needed by more than one file in the system should reside on a distinguished file of definitions, to be loaded into any environment where the system files are worked on. Unfortunately, this would keep the definitions away from where they logically belong. The EXPORT mechanism is designed to solve this problem. To use the mechanism, the implementor identifies any definitions needed by files other than the one in which the definitions reside, and wraps the corresponding file package commands in the EXPORT file package command. Thereafter, GATHEREXPORTS can be used to make a single file containing all the exports. (EXPORT COM1 ... COMN) [File Package Command] 1 This command is used for "exporting" definitions. Like COM, each of the commands COM1 ... COMN is interpreted as a file package command. The commands are also flagged in the file as being "exported" commands, for use with GATHEREXPORTS (see page X.XX). 1 (GATHEREXPORTS FROMFILES TOFILE FLG) [Function] 1 FROMFILES is a list of files containing EXPORT commands. GATHEREXPORTS extracts all the exported commands from those files and produces a loadable file TOFILE containing them. If FLG = EVAL, the expressions are evaluated as they are gathered; i.e., the exports are effectively loaded into the current environment as well as being written to TOFILE. 1 (IMPORTFILE FILE RETURNFLG) [Function] 1 If RETURNFLG is NIL, this loads any exported definitions from FILE into the current environment. If RETURNFLG is T, this returns a list of the exported definitions (evaluable expressions) without actually evaluating them. 1 (CHECKIMPORTS FILES NOASKFLG) [Function] 1 Checks each of the files in FILES to see if any exists in a version newer than the one from which the exports in memory were taken (GATHEREXPORTS and IMPORTFILE note the creation dates of the files involved), or if any file in the list has not had its exports loaded at all. If there are any such files, the user is asked for permission to IMPORTFILE each such file. If NOASKFLG is non-NIL, IMPORTFILE is performed without asking. 1 For example, suppose file FOO contains records R1, R2, and R3, macros BAR and BAZ, and constants CON1 and CON2. If the definitions of R1, R2, BAR, and BAZ are needed by files other than FOO, then the file commands for FOO might contain the command (DECLARE: EVAL@COMPILE DONTCOPY (EXPORT (RECORDS R1 R2) (MACROS BAR BAZ)) (RECORDS R3) (CONSTANTS BAZ)) None of the commands inside this DECLARE: would appear on FOO's compiled file, but (GATHEREXPORTS '(FOO) 'MYEXPORTS) would copy the record definitions for R1 and R2 and the macro definitions for BAR and BAZ to the file MYEXPORTS. 0.9.7 FileVars 1 In each of the file package commands described above, if the litatom * follows the command type, the form following the *, i.e., CADDR of the command, is evaluated and its value used in executing the command, e.g., (FNS * (APPEND FNS1 FNS2)). When this form is a litatom, e.g. (FNS * FOOFNS), we say that the variable is a "filevar". Note that (COMS * FORM) provides a way of computing what should be done by MAKEFILE. Example: _ (SETQ FOOFNS '(FOO1 FOO2 FOO3)) (FOO1 FOO2 FOO3) _ (SETQ FOOCOMS '( (FNS * FOOFNS) (VARS FIE) (PROP MACRO FOO1 FOO2) (P (MOVD 'FOO1 'FIE1))] _ (MAKEFILE 'FOO) would create a file FOO containing: (FILECREATED "time and date the file was made" . "other information") (PRETTYCOMPRINT FOOCOMS) (RPAQQ FOOCOMS ((FNS * FOOFNS) ...) (RPAQQ FOOFNS (FOO1 FOO3 FOO3)) (DEFINEQ "definitions of FOO1, FOO2, and FOO3") (RPAQQ FIE "value of FIE") (PUTPROPS FOO1 MACRO PROPVALUE) (PUTPROPS FOO2 MACRO PROPVALUE) (MOVD (QUOTE FOO1) (QUOTE FIE1)) STOP Note: For the PROP and IFPROP commands (page X.XX), the * follows the property name instead of the command, e.g., (PROP MACRO * FOOMACROS). Also, in the form (* * comment ...), the word comment is not treated as a filevar. 0.9.8 Defining New File Package Commands 1 A file package command is defined by specifying the values of certain properties. The user can specify the various attributes of a file package command for a new command, or respecify them for an existing command. The following properties are used: MACRO [File Package Command Property] 1 Defines how to dump the file package command. Used by MAKEFILE. Value is a pair (ARGS . COMS). The "arguments" to the file package command are substituted for ARGS throughout COMS, and the result treated as a list of file package commands. For example, following (FILEPKGCOM 'FOO 'MACRO '((X Y) . COMS)), the file package command (FOO A B) will cause A to be substituted for X and B for Y throughout COMS, and then COMS treated as a list of commands. The substitution is carried out by SUBPAIR (page X.XX), so that the "argument list" for the macro can also be atomic. For example, if (X . COMS) was used instead of ((X Y) . COMS), then the command (FOO A B) would cause (A B) to be substituted for X throughout COMS. Note: Filevars are evaluated before substitution. For example, if the litatom * follows NAME in the command, CADDR of the command is evaluated substituting in COMS. 1 ADD [File Package Command Property] 1 Specifies how (if possible) to add an instance of an object of a particular type to a given file package command. Used by ADDTOFILE. Value is FN, a function of three arguments, COM, a file package command CAR of which is EQ to COMMANDNAME, NAME, a typed object, and TYPE, its type. FN should return T if it (undoably) adds NAME to COM, NIL if not. If no ADD property is specified, then the default is (1) if (CAR COM)=TYPE and (CADR COM)=*, and (CADDR COM) is a filevar (i.e. a literal atom), add NAME to the value of the filevar, or (2) if (CAR COM)=TYPE and (CADR COM) is not *, add NAME to (CDR COM). Actually, the function is given a fourth argument, NEAR, which if non-NIL, means the function should try to add the item after NEAR. See discussion of ADDTOFILES?, page X.XX. 1 DELETE [File Package Command Property] 1 Specifies how (if possible) to delete an instance of an object of a particular type from a given file package command. Used by DELFROMFILES. Value is FN, a function of three arguments, COM, NAME, and TYPE, same as for ADD. FN should return T if it (undoably) deletes NAME from COM, NIL if not. If no DELETE property is specified, then the default is (1) (CAR COM)=TYPE and (CADR COM)=*, and (CADDR COM) is a filevar (i.e. a literal atom), and NAME is contained in the value of the filevar, then remove NAME from the filevar, or (2) if (CAR COM)=TYPE and (CADR COM) is not *, and NAME is contained in (CDR COM), then remove NAME from (CDR COM). If FN returns the value of ALL, it means that the command is now "empty", and can be deleted entirely from the command list. 1 CONTENTS [File Package Command Property] 1 CONTAIN [File Package Command Property] 1 Determines whether an instance of an object of a given type is contained in a given file package command. Used by WHEREIS and INFILECOMS?. Value is FN, a function of three arguments, COM, a file package command CAR of which is EQ to COMMANDNAME, NAME, and TYPE. The interpretation of NAME is as follows: if NAME is NIL, FN should return a list of elements of type TYPE contained in COM. If NAME is T, FN should return T if there are any elements of type TYPE in COM. If NAME is an atom other than T or NIL, return T if NAME of type TYPE is contained in COM. Finally, if NAME is a list, return a list of those elements of type TYPE contained in COM that are also contained in NAME. Note that it is sufficient for the CONTENTS function to simply return the list of items of type TYPE in command COM, i.e. it can in fact ignore the NAME argument. The NAME argument is supplied mainly for those situations where producing the entire list of items involves significantly more computation or creates more storage than simply determining whether a particular item (or any item) of type TYPE is contained in the command. If a CONTENTS property is specified and the corresponding function application returns NIL and (CAR COM)=TYPE, then the operation indicated by NAME is performed (1) on the value of (CADDR COM), if (CADR COM)=*, otherwise (2) on (CDR COM). In other words, by specifying a CONTENTS property that returns NIL, e.g. the function NILL, the user specifies that a file package command of name FOO produces objects of file package type FOO and only objects of type FOO. If the CONTENTS property is not provided, the command is simply expanded according to its MACRO definition, and each command on the resulting command list is then interrogated. Note that if COMMANDNAME is a file package command that is used frequently, its expansion by the various parts of the system that need to interrogate files can result in a large number of CONSes and garbage collections. By informing the file package as to what this command actually does and does not produce via the CONTENTS property, this expansion is avoided. For example, suppose the user has a file package command called GRAMMARS which dumps various property lists but no functions. The file package could ignore this command when seeking information about FNS. 1 The function FILEPKGCOM is used to define new file package commands, or to change the properties of existing commands. Note that it is possible to redefine the attributes of system file package commands, such as FNS or PROPS, and to cause unpredictable results. (FILEPKGCOM COMMANDNAME PROP1 VAL1 ... PROPN VALN) [NoSpread Function] 1 Nospread function for defining new file package commands, or changing properties of existing file package commands. PROPi is one of of the property names described above; VALi is the value to be given that property of the file package command COMMANDNAME. Returns COMMANDNAME. (FILEPKGCOM COMMANDNAME PROP) returns the value of the property PROP, without changing it. (FILEPKGCOM COMMANDNAME) returns an alist of all of the defined properties of COMMANDNAME, using the property names as keys. Note: Specifying TYPE as the litatom COM can be used to define one file package command as a synonym of another. For example, (FILEPKGCOM 'INITVARIABLES 'COM 'INITVARS) defines INITVARIABLES as a synonym for the file package command INITVARS. 1 FUNCTIONS FOR MANIPULATING FILE COMMAND LISTS 1 FUNCTIONS FOR MANIPULATING FILE COMMAND LISTS 1 2 0.10 Functions for Manipulating File Command Lists 1 The following functions may be used to manipulate filecoms. The argument COMS does not have to correspond to the filecoms for some file. For example, COMS can be the list of commands generated as a result of expanding a user defined file package command. Note: The following functions will accept a file package command as a valid value for their TYPE argument, even if it does not have a corresponding file package type. User-defined file package commands are expanded as necessary. (INFILECOMS? NAME TYPE COMS %) [Function] 1 COMS is a list of file package commands, or a variable whose value is a list of file package commands. TYPE is a file package type. INFILECOMS? returns T if NAME of type TYPE is "contained" in COMS. If NAME=NIL, INFILECOMS? returns a list of all elements of type TYPE. If NAME=T, INFILECOMS? returns T if there are any elements of type TYPE in COMS. 1 (ADDTOFILE NAME TYPE FILE NEAR LISTNAME) [Function] 1 Adds NAME of type TYPE to the file package commands for FILE. If NEAR is given and it is the name of an item of type TYPE already on FILE, then NAME is added to the command that dumps NEAR. If LISTNAME is given and is the name of a list of items of TYPE items on FILE, then NAME is added to that list. Uses ADDTOCOMS and MAKENEWCOM. Returns FILE. ADDTOFILE is undoable. 1 (DELFROMFILES NAME TYPE FILES) [Function] 1 Deletes all instances of NAME of type TYPE from the filecoms for each of the files on FILES. If FILES is a non-NIL litatom, (LIST FILES) is used. FILES=NIL defaults to FILELST. Returns a list of files from which NAME was actually removed. Uses DELFROMCOMS. DELFROMFILES is undoable. Note: Deleting a function will also remove the function from any BLOCKS declarations in the filecoms. 1 (ADDTOCOMS COMS NAME TYPE NEAR LISTNAME) [Function] 1 Adds NAME as a TYPE to COMS, a list of file package commands or a variable whose value is a list of file package commands. Returns NIL if ADDTOCOMS was unable to find a command appropriate for adding NAME to COMS. NEAR and LISTNAME are described in the discussion of ADDTOFILE. ADDTOCOMS is undoable. Note that the exact algorithm for adding commands depends the particular command itself. See discussion of the ADD property, in the description of FILEPKGCOM, page X.XX. Note: ADDTOCOMS will not attempt to add an item to any command which is inside of a DECLARE: unless the user specified a specific name via the LISTNAME or NEAR option of ADDTOFILES?. 1 (DELFROMCOMS COMS NAME TYPE) [Function] 1 Deletes NAME as a TYPE from COMS. Returns NIL if DELFROMCOMS was unable to modify COMS to delete NAME. DELFROMCOMS is undoable. 1 (MAKENEWCOM NAME TYPE % %) [Function] 1 Returns a file package command for dumping NAME of type TYPE. Uses the procedure described in the discussion of NEWCOM, page X.XX. 1 (MOVETOFILE TOFILE NAME TYPE FROMFILE) [Function] 1 Moves the definition of NAME as a TYPE from FROMFILE to TOFILE by modifying the file commands in the appropriate way (with DELFROMFILES and ADDTOFILE). Note that if FROMFILE is specified, the definition will be retrieved from that file, even if there is another definition currently in the user's environment. 1 (FILECOMSLST FILE TYPE %) [Function] 1 Returns a list of all objects of type TYPE in FILE. 1 (FILEFNSLST FILE) [Function] 1 Same as (FILECOMSLST FILE 'FNS). 1 (FILECOMS FILE TYPE) [Function] 1 Returns (PACK* FILE (OR TYPE 'COMS)). Note that (FILECOMS 'FOO) returns the litatom FOOCOMS, not the value of FOOCOMS. 1 (SMASHFILECOMS FILE) [Function] 1 Maps down (FILECOMSLST FILE 'FILEVARS) and sets to NOBIND all filevars (see page X.XX), i.e. any variable used in a command of the form (COMMAND * VARIABLE). Also sets (FILECOMS FILE) to NOBIND. Returns FILE. 1 SYMBOLIC FILE FORMAT 1 SYMBOLIC FILE FORMAT 1 2 0.11 Symbolic File Format 1 The file package manipulates symbolic files in a particular format. This format is defined so that the information in the file is easily readable when the file is listed, as well as being easily manipulated by the file package functions. In general, there is no reason for the user to manually change the contents of a symbolic file. However, in order to allow users to extend the file package, this section describes some of the functions used to write symbolic files, and other matters related to their format. (PRETTYDEF PRTTYFNS PRTTYFILE PRTTYCOMS REPRINTFNS SOURCEFILE CHANGES) [Function] 1 Writes a symbolic file in PRETTYPRINT format for loading, using FILERDTBL as its read table. PRETTYDEF returns the name of the symbolic file that was created. PRETTYDEF operates under a RESETLST (see page X.XX), so if an error occurs, or a control-D is typed, all files that PRETTYDEF has opened will be closed, the (partially complete) file being written will be deleted, and any undoable operations executed will be undone. The RESETLST also means that any RESETSAVEs executed in the file package commands will also be protected. PRTTYFNS is an optional list of function names. It is equivalent to including (FNS * PRTTYFNS) in the file package commands in PRTTYCOMS. PRTTYFNS is an anachronism from when PRETTYDEF did not use a list of file package commands, and should be specified as NIL. PRTTYFILE is the name of the file on which the output is to be written. If PRTTYFILE=NIL, the primary output file is used. If PRTTYFILE is atomic the file is opened if not already open, and it becomes the primary output file. PRTTYFILE is closed at end of PRETTYDEF, and the primary output file is restored. Finally, if PRTTYFILE is a list, CAR of PRTTYFILE is assumed to be the file name, and is opened if not already open. In this case, the file is left open at end of PRETTYDEF. PRTTYCOMS is a list of file package commands interpreted as described on page X.XX. If PRTTYCOMS is atomic, its top level value is used and an RPAQQ is written which will set that atom to the list of commands when the file is subsequently loaded. A PRETTYCOMPRINT expression (see below) will also be written which informs the user of the named atom or list of commands when the file is subsequently loaded. In addition, if any of the functions in the file are nlambda functions, PRETTYDEF will automatically print a DECLARE: expression suitable for informing the compiler about these functions, in case the user recompiles the file without having first loaded the nlambda functions (see page X.XX). REPRINTFNS and SOURCEFILE are for use in conjunction with remaking a file (see page X.XX). REPRINTFNS can be a list of functions to be prettyprinted, or EXPRS, meaning prettyprint all functions with EXPR definitions, or ALL meaning prettyprint all functions either defined as EXPRs, or with EXPR properties. Note that doing a remake with REPRINTFNS=NIL makes sense if there have been changes in the file, but not to any of the functions, e.g., changes to variables or property lists. SOURCEFILE is the name of the file from which to copy the definitions for those functions that are not going to be prettyprinted, i.e., those not specified by REPRINTFNS. SOURCEFILE=T means to use most recent version (i.e., highest number) of PRTTYFILE, the second argument to PRETTYDEF. If SOURCEFILE cannot be found, PRETTYDEF prints the message "FILE NOT FOUND, SO IT WILL BE WRITTEN ANEW", and proceeds as it does when REPRINTFNS and SOURCEFILE are both NIL. PRETTYDEF calls PRETTYPRINT with its second argument PRETTYDEFLG=T, so whenever PRETTYPRINT starts a new function, it prints (on the terminal) the name of that function if more than 30 seconds (real time) have elapsed since the last time it printed the name of a function. Note that normally if PRETTYPRINT is given a litatom which is not defined as a function but is known to be on one of the files noticed by the file package, PRETTYPRINT will load in the definition (using LOADFNS) and print it. This is not done when PRETTYPRINT is called from PRETTYDEF. 1 (PRINTFNS X %) [Function] 1 X is a list of functions. PRINTFNS prettyprints a DEFINEQ epression that defines the functions to the primary output stream using the primary read table. Used by PRETTYDEF to implement the FNS file package command. 1 (PRINTDATE FILE CHANGES) [Function] 1 Prints the FILECREATED expression at beginning of PRETTYDEF files. CHANGES used by the file package. 1 (FILECREATED X) [NLambda NoSpread Function] 1 Prints a message (using LISPXPRINT) followed by the time and date the file was made, which is (CAR X). The message is the value of PRETTYHEADER, initially "FILE CREATED". If PRETTYHEADER=NIL, nothing is printed. (CDR X) contains information about the file, e.g., full name, address of file map, list of changed items, etc. FILECREATED also stores the time and date the file was made on the property list of the file under the property FILEDATES and performs other initialization for the file package. 1 (PRETTYCOMPRINT X) [NLambda Function] 1 Prints X (unevaluated) using LISPXPRINT, unless PRETTYHEADER=NIL. 1 PRETTYHEADER [Variable] 1 Value is the message printed by FILECREATED. PRETTYHEADER is initially "FILE CREATED". If PRETTYHEADER=NIL, neither FILECREATED nor PRETTYCOMPRINT will print anything. Thus, setting PRETTYHEADER to NIL will result in "silent loads". PRETTYHEADER is reset to NIL during greeting (page X.XX). 1 (FILECHANGES FILE TYPE) [Function] 1 Returns a list of the changed objects of file package type TYPE from the FILECREATED expression of FILE. If TYPE=NIL, returns an alist of all of the changes, with the file package types as the CARs of the elements.. 1 (FILEDATE FILE %) [Function] 1 Returns the file date contained in the FILECREATED expression of FILE. 1 (LISPSOURCEFILEP FILE) [Function] 1 Returns a non-NIL value if FILE is an Interlisp source file, NIL otherwise. 1 0.11.1 Copyright Notices 1 The system has a facility for automatically printing a copyright notice near the front of files, right after the FILECREATED expression, specifying the years it was edited and the copyright owner. The format of the copyright notice is: (* Copyright (c) 1981 by Foo Bars Corporation) Once a file has a copyright notice then every version will have a new copyright notice inserted into the file without user intervention. (The copyright information necessary to keep the copyright up to date is stored at the end of the file.). Any year the file has been edited is considered a "copyright year" and therefore kept with the copyright information. For example, if a file has been edited in 1981, 1982, and 1984, then the copyright notice would look like: (* Copyright (c) 1981,1982,1984 by Foo Bars Corporation) When a file is made, if it has no copyright information, the system will ask the user to specify the copyright owner (if COPYRIGHTFLG=T). The user may specify one of the names from COPYRIGHTOWNERS, or give one of the following responses: (1) Type a left-square-bracket. The system will then prompt for an arbitrary string which will be used as the owner-string (2) Type a right-square-bracket, which specifies that the user really does not want a copyright notice. (3) Type "NONE" which specifies that this file should never have a copyright notice. For example, if COPYRIGHTOWNERS has the value ((BBN "Bolt Beranek and Newman Inc.") (XEROX "Xerox Corporation")) then for a new file FOO the following interaction will take place: Do you want to Copyright FOO? Yes Copyright owner: (user typed ?) one of: BBN - Bolt Beranek and Newman Inc. XEROX - Xerox Corporation NONE - no copyright ever for this file [ - new copyright owner -- type one line of text ] - no copyright notice for this file now Copyright owner: BBN Then "Foo Bars Corporation" in the above copyright notice example would have been "Bolt Beranek and Newman Inc." The following variables control the operation of the copyright facility: COPYRIGHTFLG [Variable] 1 The value of COPYRIGHTFLG determines whether copyright information is maintained in files. Its value is interpreted as follows: NIL The system will preserve old copyright information, but will not ask the user about copyrighting new files. This is the default value of COPYRIGHTFLG. T When a file is made, if it has no copyright information, the system will ask the user to specify the copyright owner. NEVER The system will neither prompt for new copyright information nor preserve old copyright information. DEFAULT The value of DEFAULTCOPYRIGHTOWNER (below) is used for putting copyright information in files that don't have any other copyright. The prompt "Copyright owner for file xx:" will still be printed, but the default will be filled in immediately. 1 COPYRIGHTOWNERS [Variable] 1 COPYRIGHTOWNERS is a list of entries of the form (KEY OWNERSTRING), where KEY is used as a response to ASKUSER and OWNERSTRING is a string which is the full identification of the owner. 1 DEFAULTCOPYRIGHTOWNER [Variable] 1 If the user does not respond in DWIMWAIT seconds to the copyright query, the value of DEFAULTCOPYRIGHTOWNER is used. 1 0.11.2 Functions Used Within Source Files 1 The following functions are normally only used within symbolic files, to set variable values, property values, etc. Most of these have special behavior depending on file package variables. (RPAQ VAR VALUE) [NLambda Function] 1 An nlambda function like SETQ that sets the top level binding of VAR (unevaluated) to VALUE. 1 (RPAQQ VAR VALUE) [NLambda Function] 1 An nlambda function like SETQQ that sets the top level binding of VAR (unevaluated) to VALUE (unevaluated). 1 (RPAQ? VAR VALUE) [NLambda Function] 1 Similar to RPAQ, except that it does nothing if VAR already has a top level value other than NOBIND. Returns VALUE if VAR is reset, otherwise NIL. 1 RPAQ, RPAQQ, and RPAQ? generate errors if X is not a litatom. All are affected by the value of DFNFLG (page X.XX). If DFNFLG=ALLPROP (and the value of VAR is other than NOBIND), instead of setting X, the corresponding value is stored on the property list of VAR under the property VALUE. All are undoable. (ADDTOVAR VAR X1 X2 ... XN) [NLambda NoSpread Function] 1 Each Xi that is not a member of the value of VAR is added to it, i.e. after ADDTOVAR completes, the value of VAR will be (UNION (LIST X1 X2 ... XN) VAR). ADDTOVAR is used by PRETTYDEF for implementing the ADDVARS command. It performs some file package related operations, i.e. "notices" that VAR has been changed. Returns the atom VAR (not the value of VAR). 1 (APPENDTOVAR VAR X1 X2 ... XN) [NLambda NoSpread Function] 1 Similar to ADDTOVAR, except that the values are added to the end tof the list, rather than at the beginning. 1 (PUTPROPS ATM PROP1 VAL1 ... PROPN VALN) [NLambda NoSpread Function] 1 Nlambda nospread version of PUTPROP (none of the arguments are evaluated). For i=1...N, puts property PROPi, value VALi, on the property list of ATM. Performs some file package related operations, i.e., "notices" that the corresponding properties have been changed. 1 (SAVEPUT ATM PROP VAL) [Function] 1 Same as PUTPROP, but marks the corresponding property value as having been changed (used by the file package). 1 0.11.3 File Maps 1 A file map is a data structure which contains a symbolic 'map' of the contents of a file. Currently, this consists of the begin and end byte address (see GETFILEPTR, page X.XX) for each DEFINEQ expression in the file, the begin and end address for each function definition within the DEFINEQ, and the begin and end address for each compiled function. MAKEFILE, PRETTYDEF, LOADFNS, RECOMPILE, and numerous other system functions depend heavily on the file map for efficient operation. For example, the file map enables LOADFNS to load selected function definitions simply by setting the file pointer to the corresponding address using SETFILEPTR, and then performing a single READ. Similarly, the file map is heavily used by the "remake" option of MAKEFILE (page X.XX): those function definitions that have been changed since the previous version are prettyprinted; the rest are simply copied from the old file to the new one, resulting in a considerable speedup. Whenever a file is written by MAKEFILE, a file map for the new file is built. Building the map in this case essentially comes for free, since it requires only reading the current file pointer before and after each definition is written or copied. However, building the map does require that PRETTYPRINT know that it is printing a DEFINEQ expression. For this reason, the user should never print a DEFINEQ expression onto a file himself, but should instead always use the FNS file package command (page X.XX). The file map is stored on the property list of the root name of the file, under the property FILEMAP. In addition, MAKEFILE writes the file map on the file itself. For cosmetic reasons, the file map is written as the last expression in the file. However, the address of the file map in the file is (over)written into the FILECREATED expression that appears at the beginning of the file so that the file map can be rapidly accessed without having to scan the entire file. In most cases, LOAD and LOADFNS do not have to build the file map at all, since a file map will usually appear in the corresponding file, unless the file was written with BUILDMAPFLG=NIL, or was written outside of Interlisp. Currently, file maps for compiled files are not written onto the files themselves. However, LOAD and LOADFNS will build maps for a compiled file when it is loaded, and store it on the property FILEMAP. Similary, LOADFNS will obtain and use the file map for a compiled file, when available. The use and creation of file maps is controlled by the following variables: BUILDMAPFLG [Variable] 1 Whenever a file is read by LOAD or LOADFNS, or written by MAKEFILE, a file map is automatically built unless BUILDMAPFLG=NIL. (BUILDMAPFLG is initially T.) While building the map will not help the first reference to a file, it will help in future references. For example, if the user performs (LOADFROM 'FOO) where FOO does not contain a file map, the LOADFROM will be (slightly) slower than if FOO did contain a file map, but subsequent calls to LOADFNS for this version of FOO will be able to use the map that was built as the result of the LOADFROM, since it will be stored on FOO's FILEMAP property. 1 USEMAPFLG [Variable] 1 If USEMAPFLG=T (the initial setting), the functions that use file maps will first check the FILEMAP property to see if a file map for this file was previously obtained or built. If not, the first expression on the file is checked to see if it is a FILECREATED expression that also contains the address of a file map. If the file map is not on the FILEMAP property or in the file, a file map will be built (unless BUILDMAPFLG=NIL). If USEMAPFLG=NIL, the FILEMAP property and the file will not be checked for the file map. This allows the user to recover in those cases where the file and its map for some reason do not agree. For example, if the user uses a text editor to change a symbolic file that contains a map (not recommended), inserting or deleting just one character will throw that map off. The functions which use file maps contain various integrity checks to enable them to detect that something is wrong, and to generate the error FILEMAP DOES NOT AGREE WITH CONTENTS OF FILE. In such cases, the user can set USEMAPFLG to NIL, causing the map contained in the file to be ignored, and then reexecute the operation. 1 (SEQUENCE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE NIL) (0 0 612 792) ((HEADING NIL (HEADINGTYPE RECTOFOOT) (58 22 554 36) NIL) (HEADING NIL (HEADINGTYPE RECTOFOOTRULE) (58 30 554 36) NIL) (HEADING NIL (HEADINGTYPE DRAFTMESSAGE) (200 5 412 36) NIL) (TEXT NIL NIL (58 54 500 726) NIL))) (ALTERNATE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE NIL) (0 0 612 792) ((HEADING NIL (HEADINGTYPE DRAFTMESSAGE) (200 775 412 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (58 761 554 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEADRULE) (58 757 554 36) NIL) (HEADING NIL (HEADINGTYPE VERSOFOOT) (58 22 554 36) NIL) (HEADING NIL (HEADINGTYPE VERSOFOOTRULE) (58 30 554 36) NIL) (HEADING NIL (HEADINGTYPE DRAFTMESSAGE) (200 5 412 36) NIL) (TEXT NIL NIL (58 54 500 684) NIL))) (PAGE NIL (PAPERSIZE NIL) (0 0 612 792) ((HEADING NIL (HEADINGTYPE DRAFTMESSAGE) (200 775 412 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (58 761 554 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEADRULE) (58 757 554 36) NIL) (HEADING NIL (HEADINGTYPE RECTOFOOT) (58 22 554 36) NIL) (HEADING NIL (HEADINGTYPE RECTOFOOTRULE) (58 30 554 36) NIL) (HEADING NIL (HEADINGTYPE DRAFTMESSAGE) (200 5 412 36) NIL) (TEXT NIL NIL (58 54 500 684) NIL)))))))F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR,23#(T@ PAGEHEADING VERSOHEAD6T56T53(T-7T-T,-KKT3KT,,,],D PAGEHEADING RECTOHEADRULE@ PAGEHEADING RECTOHEADD PAGEHEADING VERSOHEADRULE@ PAGEHEADING VERSOHEAD, + HELVETICA +CLASSIC +MODERN MODERN +MODERNMODERN +MODERN +MODERNMODERNMODERN + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + HRULE.GETFNMODERN + + +  + +B + + +X + + + + + +J + +    . +3 + +J +9 + + + "% % +\ + +  + + + + + +D + + 5 +] + + + + +i +h +N + + + l ++ + + + + K +' + + +_ +d + + + + A + + +M + +Q +1 + + + + + HRULE.GETFNMODERN + +, + + +; + + + + HRULE.GETFNMODERN + +[ +  HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + + HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + + +  ++ + + + + + + +( + + + +V + +0 + +[ +; + +: + + +F +B + + + + + + +9 +  + +b +  +4 + + +L +  + + + + +[ +7 + + +d + +: + + + + +2 +  + +  +  + + + HRULE.GETFNMODERN + +" + + H + + +@ + + + + +> + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + +9 + + + + + + +- + + ' + # + +  + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + ++ +9 +5 + + + + + + + + +6 +p + +3 +< + + +P +  + + +  + + +  +G + + + + M + +D + +$ +i +/ + + +& +h + + +, + + + + +  +, + +( + +6 + + + + + + + +S +Y + + +C HRULE.GETFNMODERN + + + + HRULE.GETFNMODERN + + +  + HRULE.GETFNMODERN + + + + HRULE.GETFNMODERN + + +  + HRULE.GETFNMODERN + + +G +N + +   + HRULE.GETFNMODERN + + +B + + @ +O +` + HRULE.GETFNMODERN + + + + + HRULE.GETFNMODERN + + +E +\ +  +  + + + HRULE.GETFNMODERN + +  + + HRULE.GETFNMODERN + + +@ + + + + 4 + +\ + + HRULE.GETFNMODERN + + Y +   + HRULE.GETFNMODERN + + + + +  +1 +^ +/ +T +  + + +m +"B + + + + m +^ +  +3 + + F +C +  +  + . +# +  +G +  +( + + +p + + +  + +A + + + + + + X + + 8 + + G +  + +  + + +  + + + +  + +  + +  + +  + + + +) +7 += + +W +# + + +  + +; + +G + +  + +  +  + + +A + + . +# + HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + + HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + + +" + HRULE.GETFNMODERN + + +' + +; + + + +Y +# + +I + + +5 +! +> +  + + + + + + + + + +  +S + + + + + + + + + + + + +G +! + + + +R +  + + + +  + +  + + +  + + +- + + + + + + +  + + + +B + + + +6 +  + + +  +  +k +  + + + +  + + + + + +5 + +. + + + +; + + + +' + +  +  + + +' + +  +  +? +# + + + + HRULE.GETFNMODERN + + + + +7 + + + +2G + + + +7 +H + +  +  + + +  + + n + +  +  + + / +  + +  +  +  + +  + +   + HRULE.GETFNMODERN + + + + + + + + + +" + +  + / + , + HRULE.GETFNMODERN + +   + HRULE.GETFNMODERN + +# +  + +n + + + + + + + + +7 + +r +U + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + + . + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + +  + +e +  + +  +1 + + S +  + + +  +2 +l +  + 1 +  + +! +  +4 +; +  + +  + +  + ` + +& + + + + +  + +G + + +  + +  +  + HRULE.GETFNMODERN + +   + HRULE.GETFNMODERN + +I +, +8 +  +  +  + +' + + +2 + +$ + +0 +5 + + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + + +H +. + +% + + B + + +1 + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + +* +, + + +M + + +) + + + + +3 + + +% + + + + + +0 + + +0 + +3 + + + + HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + + HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + +7 +| +& + + + + + + + + + +s +E + + + + + + + + +6 + + +l + + +] + { +  + + +& + +L +J +" +A + +j + / + +B$M + + + + + + + + + + + + + + + + + + + + + +v +T +p ++  HRULE.GETFNMODERN + ++  HRULE.GETFNMODERN + + HRULE.GETFNMODERN + + 1 HRULE.GETFNMODERN + + + " + +~ +\ + + @ +  +Z +; + +* + > + + + +g +  HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + + HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + + + + + A + + +` + +Q +A + M + + +Q + +7 + + H + + HRULE.GETFNMODERN + + + + +  + +  +! + + % + +  +" + + +  + + +  +" + + + +  +& + + +  + + + + +2 + + +  + + +4 + 6 + W + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + + + +, + + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + +' ++ +m + + + += + + HRULE.GETFNMODERN + + + + + +u +* +g + + + + . + + +Q +1 + +| +' +  + +y + + + + e +J +  + + +  +  HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + + HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + +( + + + + + + +6 +9 + +. +\ +  + + + HRULE.GETFNMODERN + + +) + + + +F + + HRULE.GETFNMODERN + + + + HRULE.GETFNMODERN + +% + +2 +G + +M +" + +| + +L +D +  + + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + +  + + +  + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + +  + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + + HRULE.GETFNMODERN + + + 8 +  + J + = + + + + +; + + + HRULE.GETFNMODERN + +G + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + +> + +* + + + + +  + R + HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + + HRULE.GETFNMODERN + + & HRULE.GETFNMODERN + + + O +  + + + ( +Q +- + +  + +* + U + + + + ++ + + + + + +  + +  +L +  + +X + +2 + + + HRULE.GETFNMODERN + +  +" + HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + + HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + +h +* + +  + HRULE.GETFNMODERN + + + \ + HRULE.GETFNMODERN + +P + + HRULE.GETFNMODERN + +> + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +e +m + +C + + + + + + + + +" + + +  +! + R + +  +? + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +1 + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + +D +I +4 + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +I +S + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + ! + +  +T + + + Q +  + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +) + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +% + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +Q + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + +H +  + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +2 + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + + +m +" + + +2 +_ +&m +  + + + 8 +I + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +4 + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + +* + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + +6 + HRULE.GETFNMODERN + + + + HRULE.GETFNMODERN + +1 + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +* + HRULE.GETFNMODERN + +5 HRULE.GETFNMODERN + + +( + +# +< +@ + +: + + + + + +E + + + + + + + + + + +a + +  +) +  +* + +  +@ + + + + + +  + + + + +: +  + +b +  + + +5 +H + + + + +5 + +  + + HRULE.GETFNMODERN + + + + + + +C + + + + + + + + +, +  + +F + + + +1 + + +V +t + + +  + +) + + +  + + + + +O +% + + + + < +! + +  + +F + + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + + + + + + + + + + + + + +' + + +J + + + +  + + + + + + + + + +G + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + +" ++ + +% + + +  + + + + + + + + +5 + HRULE.GETFNMODERN + + ) + HRULE.GETFNMODERN + + +  + + +  +  + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + +% + + +* + + +> + + + + + + + + + +7 + + + + + + + + + + + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + + + +& +  + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + + +  HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + + + + + + + + + + + +' +$ + + +" + + + + ++ +^ + + + +/ + +n + +  + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +# + +0 + + + + + + * + +' +  + + + +N + + +, + + + 3 + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + +  +$ +M +  + +Z + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + +! + + + + + +Q +  + +D +! +c + +F +X + +  +  + + + 2 + + +  + + = +  + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + +  + + +  + + + ++ + + + + +# +$ + + + + +  + HRULE.GETFNMODERN + +   + HRULE.GETFNMODERN + + + + +  + +2 +2 + + + +  + ++- HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + , + + +4 + HRULE.GETFNMODERN + +( HRULE.GETFNMODERN + +g + +h +  +- +N + + HRULE.GETFNMODERN + +( + + +0 + + + + + +. + + +9 + + +. +X +( + +: + + + + +C + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + +1 +' + + + + HRULE.GETFNMODERN + + + + HRULE.GETFNMODERN + + +6 + + + + + + + + HRULE.GETFNMODERN + + + + HRULE.GETFNMODERN + +% +f + + + + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +( + + + + + + + + + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +( + + + + +" + + +( + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +' + + + +& + + +! + + + ++ + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +& + +" + +' + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +' + + + +L + +E +  + + + +% +( +  + +& +8 + ++ + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + +. + + + + + + J +F + + + + q +& + +( + +  + + + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + +. + + + + + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + +. + + + + + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + +Q + +  + + HRULE.GETFNMODERN + + +  + + +    + HRULE.GETFNMODERN + +o +, +5 + + # + + 6 +$ + + +T + +( + + HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + + HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + +@ +B +  + + +Q + + + +! + +& +' + + + +, + + + +1 +A + + + +  + + +t + +w + +T + r + + + + + +- + +q + + + + +* +; + + + + +   + HRULE.GETFNMODERN + + +\ + + +H + +( + + HRULE.GETFNMODERN + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + +- + + + +7 +- +8 +( + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + +m + +D + +! + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + +h +' + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + + + +S + +J + HRULE.GETFNMODERN + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + +O + +! +E +% + + + +& + + + + +j + + +  + HRULE.GETFNMODERN + + + + HRULE.GETFNMODERN + +W +" + + + +" +5 +5 + + + + + + + +# + + + HRULE.GETFNMODERN + + + + HRULE.GETFNMODERN + + + +$ +" +3 +( + + + + + +& + + +  +  + + + + + + + + + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + +B + , + HRULE.GETFNMODERN + + + + HRULE.GETFNMODERN + + + +" + +6 + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + +> + + +` +p + 3 + + +  + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + +; + + +  + +h +" + + +  + + + + g + HRULE.GETFNMODERN + + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + += +! + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + +Y + q +2 + + +7 + + / + + HRULE.GETFNMODERN + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + +( + + + +L + +B + +D +" + + + +/ + + +] + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + + + + + + + + + ++, + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + +$ + + + + + + +) + + + HRULE.GETFNMODERN + +, HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + +x + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + +  + HRULE.GETFNMODERN + +   + HRULE.GETFNMODERN + + + +  + +  + HRULE.GETFNMODERN + + + + HRULE.GETFNMODERN + + + + HRULE.GETFNMODERN + +   + HRULE.GETFNMODERN + + + { + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + +  +  + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + + HRULE.GETFNMODERN + +   + HRULE.GETFNMODERN + + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + +f +  + + +  + +] + HRULE.GETFNMODERN + +   + HRULE.GETFNMODERN + + + + +: + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + + +9 + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + +K + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + + + +( + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + ++ + HRULE.GETFNMODERN + + + + HRULE.GETFNMODERN + + +l + + +b + + +H + HRULE.GETFNMODERN + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + +- +z + +C +5 += + + +  +X + += + + + + + + +  + +  +  + + + L +  +  + > +  +  + + +  + + + +B +  +I +. +o + 1 +I + +") +3 +  +3 +3 +  +  + + +/ +  +6 +  +3 + +  +E +> +R + , + +G + +  + + + +# +6 +> + + +I +u + HRULE.GETFNMODERN + + + + HRULE.GETFNMODERN + + + +` + HRULE.GETFNMODERN + + HRULE.GETFNMODERN + +4 + + + + +. + +$ + ? + + HRULE.GETFNMODERN + +8 + + + +  + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +  + + R + + + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + +  ++ +# +  +l + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + +c +  + + + + + + + + + HRULE.GETFNMODERN + + + + + + + + + + + + + + + + + ! + + +!' + + + + +  + HRULE.GETFNMODERN + +E +2 + +Q +% +6 + +  + + +"  + +$  +    ! + + +9 + + + ++ HRULE.GETFNMODERN + + +# + HRULE.GETFNMODERN + +7 + +C + +U +" +  + + + + + + +# +] + +  +  + + + + +, + + +- + + HRULE.GETFNMODERN + +# + HRULE.GETFNMODERN + +{ +  +! + + + +  + + + + + + + +3 + + + + +) +( + + + + + + +3 + +6 + +  + HRULE.GETFNMODERN + +# + HRULE.GETFNMODERN + + +  +! + + + + + + + + + +0 + + + + +) +7 + + + + + + + + + + + +_ + HRULE.GETFNMODERN + +# + HRULE.GETFNMODERN + +# + HRULE.GETFNMODERN + +s + +  +! + + + +  + + + + + +* + + + + + + + + + + + + + + + + +4 + + + +# +5 + +! + + + + +J + + +# +# + + + +# + + +9 +' + + + +K +R + +  +~ +g + + + HRULE.GETFNMODERN + + + + + +& +   + HRULE.GETFNMODERN + +u +2 +D +  +  +  # + +  6 + $ + + +W +* + + + + HRULE.GETFNMODERN + +.  HRULE.GETFNMODERN + +.  HRULE.GETFNMODERN + + HRULE.GETFNMODERN + + 5 HRULE.GETFNMODERN + +J + +A +e +] + + +  + HRULE.GETFNMODERN + +e + +  + + + + + + + + ( + + + + +  + + + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + +" + +0 + + +$ + +0 + + + + +  + + + +  + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + +, + + + + + + + + +& + +  +  +B + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + + +i + + 5 + + + +$ +  +  +p +! + + + + E +3 + + +  + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + + + + +  + + +  + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + ++ + +4 + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + + + += +  +  + + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +& + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + +  + HRULE.GETFNMODERN + + +  + HRULE.GETFNMODERN + + + + + + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + +   +O + + + + + + HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + + HRULE.GETFNMODERN + +  HRULE.GETFNMODERN + + + : + HRULE.GETFNMODERN + + +  +  + 9 +  +Q +  + + @ +G +! +  + + I + + C +  +' + \ +  + 8 +  + + s +  + O + / +f + +  + + + + +C + +4 +) + +5 + +, + + + + +Y +9 + + + + +< +  +  + + +  +' + + + + + + +  +  +  + +  + + { + $ +' +  +  + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + +j +  + + HRULE.GETFNMODERN + +   + HRULE.GETFNMODERN + + +  +  + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + + +< + +  + +  + +i + e + 9 + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + + + +  + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + +  +  + +  + + +  +% +  +! +  + + HRULE.GETFNMODERN + +   + HRULE.GETFNMODERN + +; + + +  + + +M + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + +' +  + + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + + + + + + HRULE.GETFNMODERN + + HRULE.GETFNMODERN + +q + q +/ + +9y +  +/ +* +| +h + + +G +. +& +, +"#'1*q +I +  + HRULE.GETFNMODERN + + + h +  + +  +  +w +  +f +  + +n +H + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + +" +  + + + > + HRULE.GETFNMODERN + + + HRULE.GETFNMODERN + + +. + + + HRULE.GETFNMODERN + +, HRULE.GETFNMODERN + + +  + HRULE.GETFNMODERN + + +$ + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + +$ + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + +! +* + + + + + HRULE.GETFNMODERN + + + + +5 + + + + + +< + + + + + HRULE.GETFNMODERN + + +& + + + +  + +  +Q +% + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + +Z + HRULE.GETFNMODERN + + +  + HRULE.GETFNMODERN + + +- + + + + +w + HRULE.GETFNMODERN + +   + HRULE.GETFNMODERN + + +` + HRULE.GETFNMODERN + + HRULE.GETFNMODERN + + + + +[ +< + +  + +  +m + + +D + + + +  + += +C +# +] + + +7 +  + + +  +' + +< + +U + +G +L +  + HRULE.GETFNMODERN + + + + ++ +  + +  + + + +" +# +1 + +A + + + + HRULE.GETFNMODERN + +  + HRULE.GETFNMODERN + + +  +N + + Y +; +  + + +  + + +(# +  +Y + HRULE.GETFNMODERN + +Qz \ No newline at end of file diff --git a/docs/porter-irm/18-COMPILER.TEDIT b/docs/porter-irm/18-COMPILER.TEDIT new file mode 100644 index 00000000..a71ac21a --- /dev/null +++ b/docs/porter-irm/18-COMPILER.TEDIT @@ -0,0 +1,90 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 18. COMPILER 1 18. COMPILER 1 18. COMPILER 6 The compiler is contained in the standard Interlisp system. It may be used to compile functions defined in the user's Interlisp system, or to compile definitions stored in a file. The resulting compiled code may be stored as it is compiled, so as to be available for immediate use, or it may be written onto a file for subsequent loading. The most common way to use the compiler is to use one of the file package functions, such as MAKEFILE (Chapter 17), which automatically updates source files, and produces compiled versions. However, it is also possible to compile individual functions defined in the user's Interlisp system, by directly calling the compiler using functions such as COMPILE. No matter how the compiler is called, the function COMPSET is called which asks the user certain questions concerning the compilation. (COMPSET sets the free variables LAPFLG, STRF, SVFLG, LCFIL and LSTFIL which determine various modes of operation.) Those that can be answered "yes" or "no" can be answered with YES, Y, or T for "yes"; and NO, N, or NIL for "no". The questions are: LISTING? This asks whether to generate a listing of the compiled code. The LAP and machine code are usually not of interest but can be helpful in debugging macros. Possible answers are: 1 Prints output of pass 1, the LAP macro code 2 Prints output of pass 2, the machine code YES Prints output of both passes NO Prints no listings The variable LAPFLG is set to the answer. FILE: This question (which only appears if the answer to LISTING? is affirmative) ask where the compiled code listing(s) should be written. Answering T will print the listings at the terminal. The variable LSTFIL is set to the answer. REDEFINE? This question asks whether the functions compiled should be redefined to their compiled definitions. If this is answered YES, the compiled code is stored and the function definition changed, otherwise the function definition remains unchanged. The compiler does not respect the value of DFNFLG (Chapter 10) when it redefines functions to their compiled definitions. Therefore, if you set DFNFLG to PROP to completely avoid inadvertantly redefining something in your running system, you must not answer YES to this question. The variable STRF is set to T (if this is answered YES) or NIL. SAVE EXPRS? This question asks whether the original defining EXPRs of functions should be saved. If answered YES, then before redefining a function to its compiled definition, the EXPR definition is saved on the property list of the function name. Otherwise they are discarded. It is very useful to save the EXPR definitions, just in case the compiled function needs to be changed. The editing functions will retrieve this saved definition if it exists, rather than reading from a source file. The variable SVFLG is set to T (if this is answered YES) or NIL. OUTPUT FILE? This question asks whether (and where) the compiled definitions should be written into a file for later loading. If you answer with the name of a file, that file will be used. If you answer Y or YES, you will be asked the name of the file. If the file named is already open, it will continue to be used. If you answer T or TTY:, the output will be typed on the teletype (not particularly useful). If you answer N, NO, or NIL, output will not be done. The variable LCFIL is set to the name of the file. In order to make answering these questions easier, there are four other possible answers to the LISTING? question, which specify common compiling modes: S Same as last setting. Uses the same answers to compiler questions as given for the last compilation. F Compile to File, without redefining functions. ST STore new definitions, saving EXPR definitions. STF STore new definitions; Forget EXPR definitions. Implicit in these answers are the answers to the questions on disposition of compiled code and EXPR definitions, so the questions REDEFINE? and SAVE EXPRS? would not be asked if these answers were given. OUTPUT FILE? would still be asked, however. For example: COMPILE((FACT FACT1 FACT2)) LISTING? ST OUTPUT FILE? FACT.DCOM (FACT COMPILING) . . (FACT REDEFINED) . . (FACT2 REDEFINED) (FACT FACT1 FACT2) This process caused the functions FACT, FACT1, and FACT2 to be compiled, redefined, and the compiled definitions also written on the file FACT.DCOM for subsequent loading. Compiler Printout 1 In Interlisp-D, for each function FN compiled, whether by TCOMPL, RECOMPILE, or COMPILE, the compiler prints: (FN (ARG1 ... ARGN) (uses: VAR1 ... VARN) (calls: FN1 ... FNN)) The message is printed at the beginning of the second pass of the compilation of FN. (ARG1 ... ARGN) is the list of arguments to FN; following uses: are the free variables referenced or set in FN (not including global variables); following calls: are the undefined functions called within FN. If the compilation of FN causes the generation of one or more auxilary functions, a compiler message will be printed for these functions before the message for FN, e.g., (FOOA0027 (X) (uses: XX)) (FOO (A B)) When compiling a block, the compiler first prints (BLKNAME BLKFN1 BLKFN2 ...). Then the normal message is printed for the entire block. The names of the arguments to the block are generated by suffixing # and a number to the block name, e.g., (FOOBLOCK (FOOBLOCK#0 FOOBLOCK#1) FREE-VARIABLES). Then a message is printed for each entry to the block. In addition to the above output, both RECOMPILE and BRECOMPILE print the name of each function that is being copied from the old compiled file to the new compiled file. The normal compiler message is printed for each function that is actually compiled. The compiler prints out error messages when it encounters problems compiling a function. For example: ----- In BAZ: ***** (BAZ - illegal RETURN) ----- The above error message indicates that an illegal RETURN compiler error occurred while trying to compile the function BAZ. Some compiler errors cause the compilation to terminate, producing nothing; however, there are other compiler errors which do not stop compilation. The compiler error messages are described in the last section of this chapter. Compiler printout and error messages go to the file COUTFILE, initially T. COUTFILE can also be set to the name of a file opened for output, in which case all compiler printout will go to COUTFILE, i.e. the compiler will compile "silently." However, any error messages will be printed to both COUTFILE as well as T. Global Variables 1 Variables that appear on the list GLOBALVARS, or have the property GLOBALVAR with value T, or are declared with the GLOBALVARS file package command, are called global variables. Such variables are always accessed through their top level value when they are used freely in a compiled function. In other words, a reference to the value of a global variable is equivalent to calling GETTOPVAL on the variable, regardless of whether or not it is bound in the current access chain. Similarly, (SETQ VARIABLE VALUE) will compile as (SETTOPVAL (QUOTE VARIABLE) VALUE). All system parameters, unless otherwise specified, are declared as global variables. Thus, rebinding these variables in a deep bound system (like Interlisp-D) will not affect the behavior of the system: instead, the variables must be reset to their new values, and if they are to be restored to their original values, reset again. For example, the user might write (SETQ GLOBALVARIABLE NEWVALUE) FORM (SETQ GLOBALVARIABLE OLDVALUE) Note that in this case, if an error occurred during the evaluation of FORM, or a control-D was typed, the global variable would not be restored to its original value. The function RESETVAR provides a convenient way of resetting global variables in such a way that their values are restored even if an error occurred or Control-D is typed. Note: The variables that a given function accesses as global variables can be determined by using the function CALLS. Local Variables and Special Variables 1 In normal compiled and interpreted code, all variable bindings are accessible by lower level functions because the variable's name is associated with its value. We call such variables special variables, or specvars. As mentioned earlier, the block compiler normally does not associate names with variable values. Such unnamed variables are not accessible from outside the function which binds them and are therefore local to that function. We call such unnamed variables local variables, or localvars. The time economies of local variables can be achieved without block compiling by use of declarations. Using local variables will increase the speed of compiled code; the price is the work of writing the necessary specvar declarations for those variables which need to be accessed from outside the block. LOCALVARS and SPECVARS are variables that affect compilation. During regular compilation, SPECVARS is normally T, and LOCALVARS is NIL or a list. This configuration causes all variables bound in the functions being compiled to be treated as special except those that appear on LOCALVARS. During block compilation, LOCALVARS is normally T and SPECVARS is NIL or a list. All variables are then treated as local except those that appear on SPECVARS. Declarations to set LOCALVARS and SPECVARS to other values, and therefore affect how variables are treated, may be used at several levels in the compilation process with varying scope. 1. The declarations may be included in the filecoms of a file, by using the LOCALVARS and SPECVARS file package commands. The scope of the declaration is then the entire file: ... (LOCALVARS . T) (SPECVARS X Y) ... 2. The declarations may be included in block declarations; the scope is then the block, e.g., (BLOCKS ((FOOBLOCK FOO FIE (SPECVARS . T) (LOCALVARS X))) 3. The declarations may also appear in individual functions, or in PROG's or LAMBDA's within a function, using the DECLARE function. In this case, the scope of the declaration is the function or the PROG or LAMBDA in which it appears. LOCALVARS and SPECVARS declarations must appear immediately after the variable list in the function, PROG, or LAMBDA, but intervening comments are permitted. For example: (DEFINEQ ((FOO (LAMBDA (X Y) (DECLARE (LOCALVARS Y)) (PROG (X Y Z) (DECLARE (LOCALVARS X)) ... ] If the above function is compiled (non-block), the outer X will be special, the X bound in the PROG will be local, and both bindings of Y will be local. Declarations for LOCALVARS and SPECVARS can be used in two ways: either to cause variables to be treated the same whether the function(s) are block compiled or compiled normally, or to affect one compilation mode while not affecting the default in the other mode. For example: (LAMBDA (X Y) (DECLARE (SPECVARS . T)) (PROG (Z) ... ] will cause X, Y, and Z to be specvars for both block and normal compilation while (LAMBDA (X Y) (DECLARE (SPECVARS X)) ... ] will make X a specvar when block compiling, but when regular compiling the declaration will have no effect, because the default value of specvars would be T, and therefore both X and Y will be specvars by default. Although LOCALVARS and SPECVARS declarations have the same form as other components of block declarations such as (LINKFNS . T), their operation is somewhat different because the two variables are not independent. (SPECVARS . T) will cause SPECVARS to be set to T, and LOCALVARS to be set to NIL. (SPECVARS V1 V2 ...) will have no effect if the value of SPECVARS is T, but if it is a list (or NIL), SPECVARS will be set to the union of its prior value and (V1 V2 ...). The operation of LOCALVARS is analogous. Thus, to affect both modes of compilation one of the two (LOCALVARS or SPECVARS) must be declared T before specifying a list for the other. Note: The variables that a given function binds as local variables or accesses as special variables can be determined by using the function CALLS. Note: LOCALVARS and SPECVARS declarations affect the compilation of local variables within a function, but the arguments to functions are always accessible as specvars. This can be changed by redefining the following function: (DASSEM.SAVELOCALVARS FN) [Function] This function is called by the compiler to determine whether argument information for FN should be written on the compiled file for FN. If it returns NIL, the argument information is not saved, and the function is stored with arguments U, V, W, etc instead of the originals. Initially, DASSEM.SAVELOCALVARS is defined to return T. (MOVD 'NILL 'DASSEM.SAVELOCALVARS) causes the compiler to retain no local variable or argument names. Alternatively, DASSEM.SAVELOCALVARS could be redefined as a more complex predicate, to allow finer discrimination. Constants 1 Interlisp allows the expression of constructions which are intended to be description of their constant values. The following functions are used to define constant values. The function SELECTC provides a mechanism for comparing a value to a number of constants. (CONSTANT X) [Function] This function enables the user to define that the expression X should be treated as a "constant" value. When CONSTANT is interpreted, X is evaluted each time it is encountered. If the CONSTANT form is compiled, however, the expression will be evaluated only once. If the value of X has a readable print name, then it will be evaluated at compile-time, and the value will be saved as a literal in the compiled function's definition, as if (QUOTE VALUE-OF-EXPRESSION) had appeared instead of (CONSTANT EXPRESSION). If the value of X does not have a readable print name, then the expression X itself will be saved with the function, and it will be evaluated when the function is first loaded. The value will then be stored in the function's literals, and will be retrieved on future references. If a user program needed a list of 30 NILs, the user could specify (CONSTANT (to 30 collect NIL)) instead of (QUOTE (NIL NIL ...)). The former is more concise and displays the important parameter much more directly than the latter. CONSTANT can also be used to denote values that cannot be quoted directly, such as (CONSTANT (PACK NIL)), (CONSTANT (ARRAY 10)). It is also useful to parameterize quantities that are constant at run time but may differ at compile time, e.g., (CONSTANT BITSPERWORD) in a program is exactly equivalent to 36, if the variable BITSPERWORD is bound to 36 when the CONSTANT expression is evaluated at compile time. Whereas the function CONSTANT attempts to evaluate the expression as soon as possible (compile-time, load-time, or first-run-time), other options are available, using the folowing two function: (LOADTIMECONSTANT X) [Function] Similar to CONSTANT, except that the evaluation of X is deferred until the compiled code for the containing function is loaded in. For example, (LOADTIMECONSTANT (DATE)) will return the date the code was loaded. If LOADTIMECONSTANT is interpreted, it merely returns the value of X. (DEFERREDCONSTANT X) [Function] Similar to CONSTANT, except that the evaluation of X is always deferred until the compiled function is first run. This is useful when the storage for the constant is excessive so that it shouldn't be allocated until (unless) the function is actually invoked. If DEFERREDCONSTANT is interpreted, it merely returns the value of X. (CONSTANTS VAR1 VAR2 ... VARN) [NLambda NoSpread Function] Defines VAR1, ... VARN (unevaluated) to be compile-time constants. Whenever the compiler encounters a (free) reference to one of these constants, it will compile the form (CONSTANT VARi) instead. If VARi is a list of the form (VAR FORM), a free reference to the variable will compile as (CONSTANT FORM). The compiler prints a warning if user code attempts to bind a variable previously declared as a constant. Constants can be saved using the CONSTANTS file package command. Compiling Function Calls 1 When compiling the call to a function, the compiler must know the type of the function, to determine how the arguments should be prepared (evaluated/unevaluated, spread/nospread). There are three seperate cases: lambda, nlambda spread, and nlambda nospread functions. To determine which of these three cases is appropriate, the compiler will first look for a definition among the functions in the file that is being compiled. The function can be defined anywhere in any of the files given as arguments to BCOMPL, TCOMPL, BRECOMPILE or RECOMPILE. If the function is not contained in the file, the compiler will look for other information in the variables NLAMA, NLAML, and LAMS, which can be set by you: NLAMA [Variable] (for NLAMbda Atoms) A list of functions to be treated as nlambda nospread functions by the compiler. NLAML [Variable] (for NLAMbda List) A list of functions to be treated as nlambda spread functions by the compiler. LAMS [Variable] A list of functions to be treated as lambda functions by the compiler. Note that including functions on LAMS is only necessary to override in-core nlambda definitions, since in the absence of other information, the compiler assumes the function is a lambda. If the function is not contained in a file, or on the lists NLAMA, NLAML, or LAMS, the compiler will look for a current definition in the Interlisp system, and use its type. If there is no current definition, next COMPILEUSERFN is called: COMPILEUSERFN [Variable] When compiling a function call, if the function type cannot be found by looking in files, the variables NLAMA, NLAML, or LAMS, or at a current definition, then if the value of COMPILEUSERFN is not NIL, the compiler calls (the value of) COMPILEUSERFN giving it as arguments CDR of the form and the form itself, i.e., the compiler does (APPLY* COMPILEUSERFN (CDR FORM) FORM). If a non-NIL value is returned, it is compiled instead of FORM. If NIL is returned, the compiler compiles the original expression as a call to a lambda spread that is not yet defined. Note that COMPILEUSERFN is only called when the compiler encounters a list CAR of which is not the name of a defined function. The user can instruct the compiler about how to compile other data types via COMPILETYPELST. CLISP uses COMPILEUSERFN to tell the compiler how to compile iterative statements, IF-THEN-ELSE statements, and pattern match constructs. If the compiler cannot determine the function type by any of the means above, it assumes that the function is a lambda function, and its arguments are to be evaluated. If there are nlambda functions called from the functions being compiled, and they are only defined in a separate file, they must be included on NLAMA or NLAML, or the compiler will incorrectly assume that their arguments are to be evaluated, and compile the calling function correspondingly. Note that this is only necessary if the compiler does not "know" about the function. If the function is defined at compile time, or is handled via a macro, or is contained in the same group of files as the functions that call it, the compiler will automatically handle calls to that function correctly. FUNCTION and Functional Arguments 1 Compiling the function FUNCTION may involve creating and compiling a seperate "auxiliary function", which will be called at run time. An auxiliary function is named by attaching a GENSYM to the end of the name of the function in which they appear, e.g., FOOA0003. For example, suppose FOO is defined as (LAMBDA (X) ... (FOO1 X (FUNCTION ...)) ...) and compiled. When FOO is run, FOO1 will be called with two arguments, X, and FOOA000N and FOO1 will call FOOA000N each time it uses its functional argument. Compiling FUNCTION will not create an auxiliary function if it is a functional argument to a function that compiles open, such as most of the mapping functions (MAPCAR, MAPLIST, etc.). Note that a considerable savings in time could be achieved by making FOO1 compile open via a computed macro, e.g. (PUTPROP 'FOO1 'MACRO '(Z (LIST (SUBST (CADADR Z) (QUOTE FN) DEF) (CAR Z))) DEF is the definition of FOO1 as a function of just its first argument, and FN is the name used for its functional argument in its definition. In this case, (FOO1 X (FUNCTION ...)) would compile as an expression, containing the argument to FUNCTION as an open LAMBDA expression. Thus you save not only the function call to FOO1, but also each of the function calls to its functional argument. For example, if FOO1 operates on a list of length ten, eleven function calls will be saved. Of course, this savings in time costs space, and the user must decide which is more important. Open Functions 1 When a function is called from a compiled function, a system routine is invoked that sets up the parameter and control push lists as necessary for variable bindings and return information. If the amount of time spent inside the function is small, this function calling time will be a significant percentage of the total time required to use the function. Therefore, many "small" functions, e.g., CAR, CDR, EQ, NOT, CONS are always compiled "open", i.e., they do not result in a function call. Other larger functions such as PROG, SELECTQ, MAPC, etc. are compiled open because they are frequently used. The user can make other functions compile open via MACRO definitions. The user can also affect the compiled code via COMPILEUSERFN and COMPILETYPELST. COMPILETYPELST 1 Most of the compiler's mechanism deals with how to handle forms (lists) and variables (literal atoms). The user can affect the compiler's behaviour with respect to lists and literal atoms in a number of ways, e.g. macros, declarations, COMPILEUSERFN, etc. COMPILETYPELST allows you to tell the compiler what to do when it encounters a data type other than a list or an atom. It is the facility in the compiler that corresponds to DEFEVAL for the interpreter. COMPILETYPELST [Variable] A list of elements of the form (TYPENAME . FUNCTION). Whenever the compiler encounters a datum that is not a list and not an atom (or a number) in a context where the datum is being evaluated, the type name of the datum is looked up on COMPILETYPELST. If an entry appears CAR of which is equal to the type name, CDR of that entry is applied to the datum. If the value returned by this application is not EQ to the datum, then that value is compiled instead. If the value is EQ to the datum, or if there is no entry on COMPILETYPELST for this type name, the compiler simply compiles the datum as (QUOTE DATUM). Compiling CLISP 1 Since the compiler does not know about CLISP, in order to compile functions containing CLISP constructs, the definitions must first be DWIMIFYed. The user can automate this process in several ways: 1. If the variable DWIMIFYCOMPFLG is T, the compiler will always DWIMIFY expressions before compiling them. DWIMIFYCOMPFLG is initially NIL. 2. If a file has the property FILETYPE with value CLISP on its property list, TCOMPL, BCOMPL, RECOMPILE, and BRECOMPILE will operate as though DWIMIFYCOMPFLG is T and DWIMIFY all expressions before compiling. 3. If the function definition has a local CLISP declaration, including a null declaration, i.e., just (CLISP:), the definition will be automatically DWIMIFYed before compiling. Note: COMPILEUSERFN is defined to call DWIMIFY on iterative statements, IF-THEN statements, and fetch, replace, and match expressions, i.e., any CLISP construct which can be recognized by its CAR of form. Thus, if the only CLISP constructs in a function appear inside of iterative statements, IF statements, etc., the function does not have to be dwimified before compiling. If DWIMIFY is ever unsuccessful in processing a CLISP expression, it will print the error message UNABLE TO DWIMIFY followed by the expression, and go into a break unless DWIMESSGAG=T. In this case, the expression is just compiled as is, i.e. as though CLISP had not been enabled. The user can exit the break in one of these ways: 1. Type OK to the break, which will cause the compiler to try again, e.g. the user could define some missing records while in the break, and then continue; or 2. Type ^, which will cause the compiler to simply compile the expression as is, i.e. as though CLISP had not been enabled in the first place; or 3. Return an expression to be compiled in its place by using the RETURN break command. Note: TCOMPL, BCOMPL, RECOMPILE, and BRECOMPILE all scan the entire file before doing any compiling, and take note of the names of all functions that are defined in the file as well as the names of all variables that are set by adding them to NOFIXFNSLST and NOFIXVARSLST, respectively. Thus, if a function is not currently defined, but is defined in the file being compiled, when DWIMIFY is called before compiling, it will not attempt to interpret the function name as CLISP when it appears as CAR of a form. DWIMIFY also takes into account variables that have been declared to be LOCALVARS, or SPECVARS, either via block declarations or DECLARE expressions in the function being compiled, and does not attempt spelling correction on these variables. The declaration USEDFREE may also be used to declare variables simply used freely in a function. These variables will also be left alone by DWIMIFY. Finally, NOSPELLFLG is reset to T when compiling functions from a file (as opposed to from their in-core definition) so as to suppress spelling correction. Compiler Functions 1 Normally, the compiler is envoked through file package commands that keep track of the state of functions, and manage a set of files, such as MAKEFILE. However, it is also possible to explicitly call the compiler using one of a number of functions. Functions may be compiled from in-core definitions (via COMPILE), or from definitions in files (TCOMPL), or from a combination of in-core and file definitions (RECOMPILE). TCOMPL and RECOMPILE produce "compiled" files. Compiled files usually have the same name as the symbolic file they were made from, suffixed with DCOM (the compiled file extension is stored as the value of the variable COMPILE.EXT). The file name is constructed from the name field only, e.g., (TCOMPL 'FOO.TEM;3) produces FOO.DCOM on the connected directory. The version number will be the standard default. A "compiled file" contains the same expressions as the original symbolic file, except for the following: 1. A special FILECREATED expression appears at the front of the file which contains information used by the file package, and which causes the message COMPILED ON DATE to be printed when the file is loaded (the actual string printed is the value of COMPILEHEADER). 2. Every DEFINEQ in the symbolic file is replaced by the corresponding compiled definitions in the compiled file. 3. Expressions following a DONTCOPY tag inside of a DECLARE: that appears in the symbolic file are not copied to the compiled file. The compiled definitions appear at the front of the compiled file, i.e., before the other expressions in the symbolic file, regardless of where they appear in the symbolic file. The only exceptions are expressions that follow a FIRST tag inside of a DECLARE:. This "compiled" file can be loaded into any Interlisp system with LOAD. Note: When a function is compiled from its in-core definition (as opposed to being compiled from a definition in a file), and the function has been modified by BREAK, TRACE, BREAKIN, or ADVISE, it is first restored to its original state, and a message is printed out, e.g., FOO UNBROKEN. If the function is not defined by an expr definition, the value of the function's EXPR property is used for the compilation, if there is one. If there is no EXPR property, and the compilation is being performed by RECOMPILE, the definition of the function is obtained from the file (using LOADFNS). Otherwise, the compiler prints (FN NOT COMPILEABLE), and goes on to the next function. (COMPILE X FLG) [Function] X is a list of functions (if atomic, (LIST X) is used). COMPILE first asks the standard compiler questions, and then compiles each function on X, using its in-core definition. Returns X. If compiled definitions are being written to a file, the file is closed unless FLG=T. (COMPILE1 FN DEF %) [Function] Compiles DEF, redefining FN if STRF=T (STRF is one of the variables set by COMPSET). COMPILE1 is used by COMPILE, TCOMPL, and RECOMPILE. If DWIMIFYCOMPFLG is T, or DEF contains a CLISP declaration, DEF is dwimified before compiling. (TCOMPL FILES) [Function] TCOMPL is used to "compile files"; given a symbolic LOAD file (e.g., one created by MAKEFILE), it produces a "compiled file". FILES is a list of symbolic files to be compiled (if atomic, (LIST FILES) is used). TCOMPL asks the standard compiler questions, except for "OUTPUT FILE:". The output from the compilation of each symbolic file is written on a file of the same name suffixed with DCOM, e.g., (TCOMPL '(SYM1 SYM2)) produces two files, SYM1.DCOM and SYM2.DCOM. TCOMPL processes the files one at a time, reading in the entire file. For each FILECREATED expression, the list of functions that were marked as changed by the file package is noted, and the FILECREATED expression is written onto the output file. For each DEFINEQ expression, TCOMPL adds any nlambda functions defined in the DEFINEQ to NLAMA or NLAML, and adds lambda functions to LAMS, so that calls to these functions will be compiled correctly. NLAMA, NLAML, and LAMS are rebound to their top level values (using RESETVAR) by all of the compiling functions, so that any additions to these lists while inside of these functions will not propagate outside. Expressions beginning with DECLARE: are processed specially. All other expressions are collected to be subsequently written onto the output file. After processing the file in this fashion, TCOMPL compiles each function, except for those functions which appear on the list DONTCOMPILEFNS (initially NIL), and writes the compiled definition onto the output file. TCOMPL then writes onto the output file the other expressions found in the symbolic file. DONTCOMPILEFNS might be used for functions that compile open, since their definitions would be superfluous when operating with the compiled file. Note that DONTCOMPILEFNS can be set via block declarations. Note: If the rootname of a file has the property FILETYPE with value CLISP, or value a list containing CLISP, TCOMPL rebinds DWIMIFYCOMPFLG to T while compiling the functions on FILE, so the compiler will DWIMIFY all expressions before compiling them. TCOMPL returns a list of the names of the output files. All files are properly terminated and closed. If the compilation of any file is aborted via an error or control-D, all files are properly closed, and the (partially complete) compiled file is deleted. (RECOMPILE PFILE CFILE FNS) [Function] The purpose of RECOMPILE is to allow the user to update a compiled file without recompiling every function in the file. RECOMPILE does this by using the results of a previous compilation. It produces a compiled file similar to one that would have been produced by TCOMPL, but at a considerable savings in time by only compiling selected functions, and copying the compiled definitions for the remainder of the functions in the file from an earlier TCOMPL or RECOMPILE file. PFILE is the name of the Pretty file (source file) to be compiled; CFILE is the name of the Compiled file containing compiled definitions that may be copied. FNS indicates which functions in PFILE are to be recompiled, e.g., have been changed or defined for the first time since CFILE was made. Note that PFILE, not FNS, drives RECOMPILE. RECOMPILE asks the standard compiler questions, except for "OUTPUT FILE:". As with TCOMPL, the output automatically goes to PFILE.DCOM. RECOMPILE processes PFILE the same as does TCOMPL except that DEFINEQ expressions are not actually read into core. Instead, RECOMPILE uses the filemap to obtain a list of the functions contained in PFILE. The filemap enables RECOMPILE to skip over the DEFINEQs in the file by simply resetting the file pointer, so that in most cases the scan of the symbolic file is very fast (the only processing required is the reading of the non-DEFINEQs and the processing of the DECLARE: expressions as with TCOMPL). A map is built if the symbolic file does not already contain one, for example if it was written in an earlier system, or with BUILDMAPFLG=NIL. After this initial scan of PFILE, RECOMPILE then processes the functions defined in the file. For each function in PFILE, RECOMPILE determines whether or not the function is to be (re)compiled. Functions that are members of DONTCOMPILEFNS are simply ignored. Otherwise, a function is recompiled if : 1. FNS is a list and the function is a member of that list 2. FNS=T or EXPRS and the function is defined by an expr definition 3. FNS=CHANGES and the function is marked as having been changed in the FILECREATED expression in PFILE 4. FNS=ALL If a function is not to be recompiled, RECOMPILE obtains its compiled definition from CFILE, and copies it (and all generated subfunctions) to the output file, PFILE.DCOM. If the function does not appear on CFILE, RECOMPILE simply recompiles it. Finally, after processing all functions, RECOMPILE writes out all other expressions that were collected in the prescan of PFILE. Note: If FNS=ALL, CFILE is superfluous, and does not have to be specified. This option may be used to compile a symbolic file that has never been compiled before, but which has already been loaded (since using TCOMPL would require reading the file in a second time). If CFILE=NIL, PFILE.DCOM (the old version of the output file) is used for copying from. If both FNS and CFILE are NIL, FNS is set to the value of RECOMPILEDEFAULT, which is initially CHANGES. Thus the user can perform his edits, dump the file, and then simply (RECOMPILE 'FILE) to update the compiled file. The value of RECOMPILE is the file name of the new compiled file, PFILE.DCOM. If RECOMPILE is aborted due to an error or Control-D, the new (partially complete) compiled file will be closed and deleted. RECOMPILE is designed to allow the user to conveniently and efficiently update a compiled file, even when the corresponding symbolic file has not been (completely) loaded. For example, the user can perform a LOADFROM to "notice" a symbolic file, edit the functions he wants to change (the editor will automatically load those functions not already loaded), call MAKEFILE to update the symbolic file (MAKEFILE will copy the unchanged functions from the old symbolic file), and then perform (RECOMPILE PFILE). Note: Since PRETTYDEF automatically outputs a suitable DECLARE: expression to indicate which functions in the file (if any) are defined as NLAMBDAs, calls to these functions will be handled correctly, even though the NLAMBDA functions themselves may never be loaded, or even looked at, by RECOMPILE. Block Compiling 1 In Interlisp-10, block compiling provides a way of compiling several functions into a single block. Function calls between the component functions of the block are very fast. Thus, compiling a block consisting of just a single recursive function may be yield great savings if the function calls itself many times. The output of a block compilation is a single, usually large, function. Calls from within the block to functions outside of the block look like regular function calls. A block can be entered via several different functions, called entries. These must be specified when the block is compiled. In Interlisp-D, block compiling is handled somewhat differently; block compiling provides a mechanism for hiding function names internal to a block, but it does not provide a performance improvement. Block compiling in Interlisp-D works by automatically renaming the block functions with special names, and calling these functions with the normal function-calling mechanisms. Specifically, a function FN is renamed to \BLOCK-NAME/FN. For example, function FOO in block BAR is renamed to \BAR/FOO. Note that it is possible with this scheme to break functions internal to a block. Block Declarations Block compiling a file frequently involves giving the compiler a lot of information about the nature and structure of the compilation, e.g., block functions, entries, specvars, etc. To help with this, there is the BLOCKS file package command, which has the form: (BLOCKS BLOCK1 BLOCK2 ... BLOCKN) where each BLOCKi is a block declaration. The BLOCKS command outputs a DECLARE: expression, which is noticed by BCOMPL and BRECOMPILE. BCOMPL and BRECOMPILE are sensitive to these declarations and take the appropriate action. Note: Masterscope includes a facility for checking the block declarations of a file or files for various anomalous conditions, e.g. functions in block declarations which aren't on the file(s), functions in ENTRIES not in the block, variables that may not need to be SPECVARS because they are not used freely below the places they are bound, etc. A block declaration is a list of the form: (BLKNAME BLKFN1 ... BLKFNM (VAR1 . VALUE1) ... (VARN . VALUEN)) BLKNAME is the name of a block. BLKFN1 ... BLKFNM are the functions in the block and correspond to BLKFNS in the call to BLOCKCOMPILE. The (VARi . VALUEi) expressions indicate the settings for variables affecting the compilation of that block. If VALUEi is atomic, then VARi is set to VALUEi, otherwise VARi is set to the UNION of VALUEi and the current value of the variable VARi. Also, expressions of the form (VAR * FORM) will cause FORM to be evaluated and the resulting list used as described above (e.g. (GLOBALVARS * MYGLOBALVARS)). For example, consider the block declaration below. The block name is EDITBLOCK, it includes a number of functions (EDITL0, EDITL1, ... EDITH), and it sets the variables ENTRIES, SPECVARS, RETFNS, and GLOBALVARS. (EDITBLOCK EDITL0 EDITL1 UNDOEDITL EDITCOM EDITCOMA EDITMAC EDITCOMS EDIT]UNDO UNDOEDITCOM EDITH (ENTRIES EDITL0 ## UNDOEDITL) (SPECVARS L COM LCFLG #1 #2 #3 LISPXBUFS) (RETFNS EDITL0) (GLOBALVARS EDITCOMSA EDITCOMSL EDITOPS)) Whenever BCOMPL or BRECOMPILE encounter a block declaration, they rebind RETFNS, SPECVARS, GLOBALVARS, BLKLIBRARY, and DONTCOMPILEFNS to their top level values, bind BLKAPPLYFNS and ENTRIES to NIL, and bind BLKNAME to the first element of the declaration. They then scan the rest of the declaration, setting these variables as described above. When the declaration is exhausted, the block compiler is called and given BLKNAME, the list of block functions, and ENTRIES. If a function appears in a block declaration, but is not defined in one of the files, then if it has an in-core definition, this definition is used and a message printed NOT ON FILE, COMPILING IN CORE DEFINITION. Otherwise, the message NOT COMPILEABLE, is printed and the block declaration processed as though the function were not on it, i.e. calls to the function will be compiled as external function calls. Note that since all compiler variables are rebound for each block declaration, the declaration only has to set those variables it wants changed. Furthermore, setting a variable in one declaration has no effect on the variable's value for another declaration. After finishing all blocks, BCOMPL and BRECOMPILE treat any functions in the file that did not appear in a block declaration in the same way as do TCOMPL and RECOMPILE. If the user wishes a function compiled separately as well as in a block, or if he wishes to compile some functions (not blockcompile), with some compiler variables changed, he can use a special pseudo-block declaration of the form (NIL BLKFN1 ... BLKFNM (VAR1 . VALUE1) ... (VARN . VALUEN)) which means that BLKFN1 ... BLKFNM should be compiled after first setting VAR1 ... VARN as described above. The following variables control other aspects of compiling a block: RETFNS [Variable] Value is a list of internal block functions whose names must appear on the stack, e.g., if the function is to be returned from RETFROM, RETTO, RETEVAL, etc. Usually, internal calls between functions in a block are not put on the stack. BLKAPPLYFNS [Variable] Value is a list of internal block functions called by other functions in the same block using BLKAPPLY or BLKAPPLY* for efficiency reasons. Normally, a call to APPLY from inside a block would be the same as a call to any other function outside of the block. If the first argument to APPLY turned out to be one of the entries to the block, the block would have to be reentered. BLKAPPLYFNS enables a program to compute the name of a function in the block to be called next, without the overhead of leaving the block and reentering it. This is done by including on the list BLKAPPLYFNS those functions which will be called in this fashion, and by using BLKAPPLY in place of APPLY, and BLKAPPLY* in place of APPLY*. If BLKAPPLY or BLKAPPLY* is given a function not on BLKAPPLYFNS, the effect is the same as a call to APPLY or APPLY* and no error is generated. Note however, that BLKAPPLYFNS must be set at compile time, not run time, and furthermore, that all functions on BLKAPPLYFNS must be in the block, or an error is generated (at compile time), NOT ON BLKFNS. BLKAPPLYFNS [Variable] Value is a list of functions that are considered to be in the "block library" of functions that should automatically be included in the block if they are called within the block. Compiling a function open via a macro provides a way of eliminating a function call. For block compiling, the same effect can be achieved by including the function in the block. A further advantage is that the code for this function will appear only once in the block, whereas when a function is compiled open, its code appears at each place where it is called. The block library feature provides a convenient way of including functions in a block. It is just a convenience since the user can always achieve the same effect by specifying the function(s) in question as one of the block functions, provided it has an expr definition at compile time. The block library feature simply eliminates the burden of supplying this definition. To use the block library feature, place the names of the functions of interest on the list BLKLIBRARY, and their expr definitions on the property list of the functions under the property BLKLIBRARYDEF. When the block compiler compiles a form, it first checks to see if the function being called is one of the block functions. If not, and the function is on BLKLIBRARY, its definition is obtained from the property value of BLKLIBRARYDEF, and it is automatically included as part of the block. Block Compiling Functions There are three user level functions for block compiling, BLOCKCOMPILE, BCOMPL, and BRECOMPILE, corresponding to COMPILE, TCOMPL, and RECOMPILE. Note that all of the remarks on macros, globalvars, compiler messages, etc., all apply equally for block compiling. Using block declarations, the user can intermix in a single file functions compiled normally and block compiled functions. (BLOCKCOMPILE BLKNAME BLKFNS ENTRIES FLG) [Function] BLKNAME is the name of a block, BLKFNS is a list of the functions comprising the block, and ENTRIES a list of entries to the block. Each of the entries must also be on BLKFNS or an error is generated, NOT ON BLKFNS. If only one entry is specified, the block name can also be one of the BLKFNS, e.g., (BLOCKCOMPILE 'FOO '(FOO FIE FUM) '(FOO)). However, if more than one entry is specified, an error will be generated, CAN'T BE BOTH AN ENTRY AND THE BLOCK NAME. If ENTRIES is NIL, (LIST BLKNAME) is used, e.g., (BLOCKCOMPILE 'COUNT '(COUNT COUNT1)) If BLKFNS is NIL, (LIST BLKNAME) is used, e.g., (BLOCKCOMPILE 'EQUAL) BLOCKCOMPILE asks the standard compiler questions, and then begins compiling. As with COMPILE, if the compiled code is being written to a file, the file is closed unless FLG=T. The value of BLOCKCOMPILE is a list of the entries, or if ENTRIES=NIL, the value is BLKNAME. The output of a call to BLOCKCOMPILE is one function definition for BLKNAME, plus definitions for each of the functions on ENTRIES if any. These entry functions are very short functions which immediately call BLKNAME. (BCOMPL FILES CFILE % %) [Function] FILES is a list of symbolic files (if atomic, (LIST FILES) is used). BCOMPL differs from TCOMPL in that it compiles all of the files at once, instead of one at a time, in order to permit one block to contain functions in several files. (If you have several files to be BCOMPLed separately, you must make several calls to BCOMPL.) Output is to CFILE if given, otherwise to a file whose name is (CAR FILES) suffixed with DCOM. For example, (BCOMPL '(EDIT WEDIT)) produces one file, EDIT.DCOM. BCOMPL asks the standard compiler questions, except for "OUTPUT FILE:", then processes each file exactly the same as TCOMPL. BCOMPL next processes the block declarations as described above. Finally, it compiles those functions not mentioned in one of the block declarations, and then writes out all other expressions. If any of the files have property FILETYPE with value CLISP, or a list containing CLISP, then DWIMIFYCOMPFLG is rebound to T for all of the files. The value of BCOMPL is the output file (the new compiled file). If the compilation is aborted due to an error or control-D, all files are closed and the (partially complete) output file is deleted. Note that it is permissible to TCOMPL files set up for BCOMPL; the block declarations will simply have no effect. Similarly, you can BCOMPL a file that does not contain any block declarations and the result will be the same as having TCOMPLed it. (BRECOMPILE FILES CFILE FNS %) [Function] BRECOMPILE plays the same role for BCOMPL that RECOMPILE plays for TCOMPL. Its purpose is to allow the user to update a compiled file without requiring an entire BCOMPL. FILES is a list of symbolic files (if atomic, (LIST FILES) is used). CFILE is the compiled file produced by BCOMPL or a previous BRECOMPILE that contains compiled definitions that may be copied. The interpretation of FNS is the same as with RECOMPILE. BRECOMPILE asks the standard compiler questions, except for "OUTPUT FILE:". As with BCOMPL, output automatically goes to FILE.DCOM, where FILE is the first file in FILES. BRECOMPILE processes each file the same as RECOMPILE, then processes each block declaration. If any of the functions in the block are to be recompiled, the entire block must be (is) recompiled. Otherwise, the block is copied from CFILE as with RECOMPILE. For pseudo-block declarations of the form (NIL FN1 ...), all variable assignments are made, but only those functions indicated by FNS are recompiled. After completing the block declarations, BRECOMPILE processes all functions that do not appear in a block declaration, recompiling those dictated by FNS, and copying the compiled definitions of the remaining from CFILE. Finally, BRECOMPILE writes onto the output file the "other expressions" collected in the initial scan of FILES. The value of BRECOMPILE is the output file (the new compiled file). If the compilation is aborted due to an error or Control-D, all files are closed and the (partially complete) output file is deleted. If CFILE=NIL, the old version of FILE.DCOM is used, as with RECOMPILE. In addition, if FNS and CFILE are both NIL, FNS is set to the value of RECOMPILEDEFAULT, initially CHANGES. Compiler Error Messages 1 Messages describing errors in the function being compiled are also printed on the terminal. These messages are always preceded by *****. Unless otherwise indicated below, the compilation will continue. (FN NOT ON FILE, COMPILING IN CORE DEFINITION) From calls to BCOMPL and BRECOMPILE. (FN NOT COMPILEABLE) An EXPR definition for FN could not be found. In this case, no code is produced for FN, and the compiler proceeds to the next function to be compiled, if any. (FN NOT FOUND) Occurs when RECOMPILE or BRECOMPILE try to copy the compiled definition of FN from CFILE, and cannot find it. In this case, no code is copied and the compiler proceeds to the next function to be compiled, if any. (FN NOT ON BLKFNS) FN was specified as an entry to a block, or else was on BLKAPPLYFNS, but did not appear on the BLKFNS. In this case, no code is produced for the entire block and the compiler proceeds to the next function to be compiled, if any. (FN CAN'T BE BOTH AN ENTRY AND THE BLOCK NAME) In this case, no code is produced for the entire block and the compiler proceeds to the next function to be compiled, if any. (BLKNAME - USED BLKAPPLY WHEN NOT APPLICABLE) BLKAPPLY is used in the block BLKNAME, but there are no BLKAPPLYFNS or ENTRIES declared for the block. (VAR SHOULD BE A SPECVAR - USED FREELY BY FN) While compiling a block, the compiler has already generated code to bind VAR as a LOCALVAR, but now discovers that FN uses VAR freely. VAR should be declared a SPECVAR and the block recompiled. ((* --) COMMENT USED FOR VALUE) A comment appears in a context where its value is being used, e.g. (LIST X (* --) Y). The compiled function will run, but the value at the point where the comment was used is undefined. ((FORM) - NON-ATOMIC CAR OF FORM) If user intended to treat the value of FORM as a function, he should use APPLY* (Chapter 10). FORM is compiled as if APPLY* had been used. ((SETQ VAR EXPR --) BAD SETQ) SETQ of more than two arguments. (FN - USED AS ARG TO NUMBER FN?) The value of a predicate, such as GREATERP or EQ, is used as an argument to a function that expects numbers, such as IPLUS. (FN - NO LONGER INTERPRETED AS FUNCTIONAL ARGUMENT) The compiler has assumed FN is the name of a function. If the user intended to treat the value of FN as a function, APPLY* (Chater 10) should be used. This message is printed when FN is not defined, and is also a local variable of the function being compiled. (FN - ILLEGAL RETURN) RETURN encountered when not in PROG. (TG - ILLEGAL GO) GO encountered when not in a PROG. (TG - MULTIPLY DEFINED TAG) TG is a PROG label that is defined more than once in a single PROG. The second definition is ignored. (TG - UNDEFINED TAG) TG is a PROG label that is referenced but not defined in a PROG. (VAR - NOT A BINDABLE VARIABLE) VAR is NIL, T, or else not a literal atom. (VAR VAL -- BAD PROG BINDING) Occurs when there is a prog binding of the form (VAR VAL1 ... VALN). (TG - MULTIPLY DEFINED TAG, LAP) TG is a label that was encountered twice during the second pass of the compilation. If this error occurs with no indication of a multiply defined tag during pass one, the tag is in a LAP macro. (TG - UNDEFINED TAG, LAP) TG is a label that is referenced during the second pass of compilation and is not defined. LAP treats TG as though it were a COREVAL, and continues the compilation. (TG - MULTIPLY DEFINED TAG, ASSEMBLE) TG is a label that is defined more than once in an assemble form. (TG - UNDEFINED TAG, ASSEMBLE) TG is a label that is referenced but not defined in an assemble form. (OP - OPCODE? - ASSEMBLE) OP appears as CAR of an assemble statement, and is illegal. (NO BINARY CODE GENERATED OR LOADED FOR FN) A previous error condition was sufficiently serious that binary code for FN cannot be loaded without causing an error. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))-0HH +T,ll3H +T,ll,ll,ll,ll,ll2l2l~~3HH +T5;ll8ll8ll2HZZ2Hll2Hll2Hll2Hll/HH3HH +T/ll +2Hll2HH +2HH5,HH,2F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR3$$(T,HH/ll/HH +,HH +-T3(T3KT-T,,TITAN +TITAN +TITAN + HELVETICA + HELVETICA  HELVETICACLASSIC +TITAN +CLASSIC + HELVETICACLASSIC +CLASSIC +MODERN +MODERN +MODERNMODERN +" HRULE.GETFNCLASSIC + " ! HRULE.GETFNCLASSIC + !    HRULE.GETFNCLASSIC +    HRULE.GETFNCLASSIC + + HRULE.GETFNMODERN +U $]  6 O      m                    3 V 8   z x   `  T         1 - D `           z  U     +  ! ` 1  d   "         _   2 . - , ,,,,,,,,,, "   R  )( HRULE.GETFNMODERN +"                 +    Q          -  - +        1         ( !   &   &  + g * >  4   i b   )( HRULE.GETFNMODERN +" +    + c           \                 F  k  p  )&( HRULE.GETFNMODERN +  Q   R 1   E    t       5       L  O (^ ;D   N    O  8  9   %        <  +          S X             1  J   )         + %V  ,     2    %   #T P ) +( HRULE.GETFNMODERN +& G    %=  0   2 H %      +  %  :  %&   g %K  t ;  * %     %   ] / 0      %   0    +    %        + %      3    %j %!  )( HRULE.GETFNMODERN +&    o        W     T   %i  %<       %h   3  $  :      .   r % + /   ~  % ; + % %   )"+ HRULE.GETFNMODERN +&  D   ,  $      ,  +    O )         / P <  : S  )+ HRULE.GETFNMODERN +&      j  o >   )+ HRULE.GETFNMODERN +&  K  Q  * %       $ V   B   ) ?    )+ HRULE.GETFNMODERN +& 9    %         +   # * 7 '           G  c  P  & - 8      B      +  B  * l  A  # { u  + { )+ HRULE.GETFNMODERN +&  ! :   ~ E A  + O i     R   b    K | 5 4   E      R U G 5 B #   $     % $    P  )   %O          %           %         %.  #  8    3 n     %J e 7  +    @   .  p %+ M  = U  $  2      "   ( %    % a     %   )    B    S       % 3  #       8 A          %   I   ^ ?  +  6  +    4  +   T    +    %' &  E  &   A H    +     3 %     :          G   % ,   q % 3   Q +    " L G A  )+ HRULE.GETFNMODERN +'f $   +    +  U #' +        $    !  +  +F  5 H $+                $     2       _           '  !        F  +  $F %     +  ,0!--$  +,   + +  #  $    $  u $  +b                $    (     $D *    V *  ^    w Z  D       &  0   < B  *   l v [ +V  +8 8 #':   +    *   +    6  ! $    I   )M                      K M    !         0  P        (       + !   -        3 0                 I _      +   Z   )     "  +O     +3           +! -   -    K   ) +b  =    +V    +           +      )( HRULE.GETFNMODERN +' D & *%  + & %   <  I &  +%  +(    ~ &  % 6   & /%Q * &  %%     & % %I       +    &"%C g & %'       &   % & %"  E  & 3%  ?     ;  N & %  & %  & %  2 %  %  /  & %    &   %0       & % & % e   ! & $% B & % D & % + &( %I  , %[ +Fz \ No newline at end of file diff --git a/docs/porter-irm/19-MASTERSCOPE.TEDIT b/docs/porter-irm/19-MASTERSCOPE.TEDIT new file mode 100644 index 00000000..61a627b6 --- /dev/null +++ b/docs/porter-irm/19-MASTERSCOPE.TEDIT @@ -0,0 +1,177 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 19. MASTERSCOPE 1 19. MASTERSCOPE 1 "19"19. MASTERSCOPE 6 Masterscope is an interactive program for analyzing and cross-referencing user programs. It contains facilities for analyzing user functions to determine what other functions are called, how and where variables are bound, set, or referenced, as well as which functions use particular record declarations. Masterscope is able to analyze definitions directly from a file as well as in-core definitions. Masterscope maintains a database of the results of the analyses it performs. Using a simple command language, you may interrogate the database, call the editor on those expressions in functions that were analyzed which use variables or functions in a particular way, or display the tree structure of function calls among any set of functions. Masterscope is interfaced with the editor and file package so that when a function is edited or a new definition loaded in, Masterscope knows that it must re-analyze that function. The following sample session illustrates some of these facilities. 50. ANALYZE FUNCTIONS ON RECORD ............................... NIL 51. WHO CALLS RECFIELDLOOK (RECFIELDLOOK ACCESSDEF ACCESSDEF2 EDITREC) 52_. EDIT WHERE ANY CALL RECFIELDLOOK RECFIELDLOOK : (RECFIELDLOOK (CDR Y) FIELD) tty: 5*OK ACCESSDEF : (RECFIELDLOOK DECLST FIELD VAR1) 6*OK (RECFIELDLOOK USERRECLST FIELD) 7*N VAR1 8*OK ACCESSDEF2 : (RECFIELDLOOK (RECORD.SUBDECS TRAN) FIELD) tty: (RECFIELDLOOK (RECORD.SUBDECS TRAN) FIELD) 9*N (CAR TAIL] 10*OK EDITREC : (RECFIELDLOOK USERRECLST (CAR EDITRECX)) 11*OK NIL 53. WHO CALLS ERROR .. (EDITREC) 54. SHOW PATHS TO RECFIELDLOOK FROM ACCESSDEF (inverted tree) 1. RECFIELDLOOK RECFIELDLOOK 2. ACCESSDEF 3. ACCESSDEF2 ACCESSDEF2 4. ACCESSDEF 5. RECORDCHAIN ACCESSDEF NIL 55. WHO CALLS WHO IN /FNS RECORDSTATEMENT -- /RPLNODE RECORDECL1 -- /NCONC, /RPLACD, /RPLNODE RECREDECLARE1 -- /PUTHASH UNCLISPTRAN -- /PUTHASH, /RPLNODE2 RECORDWORD -- /RPLACA RECORD1 -- /RPLACA, /SETTOPVAL EDITREC -- /SETTOPVAL Statement 50 The user directs that the functions on file RECORD be analyzed. The leading period and space specify that this line is a Masterscope command. The user may also call Masterscope directly by typing (MASTERSCOPE). Masterscope prints a greeting and prompts with ". ". Within the top-level executive of Masterscope, the user may issue Masterscope commands, programmer's assistant commands, (e.g., REDO, FIX), or run programs. The user can exit from the Masterscope executive by typing OK. The function . is defined as a nlambda nospread function which interprets its argument as a Masterscope command, executes the command and returns. Masterscope prints a . whenever it (re)analyzes a function, to let the user know what it is happening. The feedback when Masterscope analyzes a function is controlled by the flag MSPRINTFLG: if MSPRINTFLG is the atom ".", Masterscope will print out a period. (If an error in the function is detected, "?" is printed instead.) If MSPRINTFLG is a number N, Masterscope will print the name of the function it is analyzing every Nth function. If MSPRINTFLG is NIL, Masterscope won't print anything. Initial setting is ".". Note that the function name is printed when Masterscope starts analyzing, and the comma is printed when it finishes. Statement 51 The user asks which functions call RECFIELDLOOK. Masterscope responds with the list. Statement 52 The user asks to edit the expressions where the function RECFIELDLOOK is called. Masterscope calls EDITF on the functions it had analyzed that call RECFIELDLOOK, directing the editor to the appropriate expressions. The user then edits some of those expressions. In this example, the teletype editor is used. If Dedit is enabled as the primary editor, it would be called to edit the appropriate functions (see Chapter 16). Statement 53 Next the user asks which functions call ERROR. Since some of the functions in the database have been changed, Masterscope re-analyzes the changed definitions (and prints out .'s for each function it analyzes). Masterscope responds that EDITREC is the only analyzed function that calls ERROR. Statement 54 The user asks to see a map of the ways in which RECFIELDLOOK is called from ACCESSDEF. A tree structure of the calls is displayed. Statement 55 The user then asks to see which functions call which functions in the list /FNS. Masterscope responds with a structured printout of these relations. Command Language 1 You communicate with Masterscope using an English-like command language, e.g., WHO CALLS PRINT. With these commands, you can direct that functions be analyzed, interrogate Masterscope's database, and perform other operations. The commands deal with sets of functions, variables, etc., and relations between them (e.g., call, bind). Sets correspond to English nouns, relations to verbs. A set of atoms can be specified in a variety of ways, either explicitly, e.g., FUNCTIONS ON FIE specifies the atoms in (FILEFNSLST 'FIE), or implicitly, e.g., NOT CALLING Y, where the meaning must be determined in the context of the rest of the command. Such sets of atoms are the basic building blocks which the command language deals with. Masterscope also deals with relations between sets. For example, the relation CALL relates functions and other functions; the relations BIND and USE FREELY relate functions and variables. These relations are what get stored in the Masterscope database when functions are analyzed. In addition, Masterscope "knows" about file package conventions; CONTAIN relates files and various types of objects (functions, variables). Sets and relations are used (along with a few additional words) to form sentence-like commands. For example, the command WHO ON 'FOO USE 'X FREELY will print out the list of functions contained in the file FOO which use the variable X freely. The command EDIT WHERE ANY CALLS 'ERROR will call EDITF on those functions which have previously been analyzed that directly call ERROR, pointing at each successive expression where the call to ERROR actually occurs. Commands The normal mode of communication with Masterscope is via "commands". These are sentences in the Masterscope command language which direct Masterscope to answer questions or perform various operations. Any command may be followed by OUTPUT FILENAME to send output to the given file rather than the terminal, e.g. WHO CALLS WHO OUTPUT CROSSREF. ANALYZE SET [Masterscope Command] Analyze the functions in SET (and any functions called by them) and include the information gathered in the database. Masterscope will not re-analyzing a function if it thinks it already has valid information about that function in its database. The user may use the command REANALYZE (below) to force re-analysis. Whenever a function is referred to in a command as a "subject" of one of the relations, it is automatically analyzed; the user need not give an explicit ANALYZE command. Thus, WHO IN MYFNS CALLS FIE will automatically analyze the functions in MYFNS if they have not already been analyzed. Only expr definitions will be analyzed; that is, Masterscope will not analyze compiled code. If necessary, the definition will be DWIMIFYed before analysis. If there is no in-core definition for a function (either in the function definition cell or an EXPR property), Masterscope will attempt to read in the definition from a file. Files which have been explicitly mentioned previously in some command are searched first. If the definition cannot be found on any of those files, Masterscope looks among the files on FILELST for a definition. If a function is found in this manner, Masterscope will print a message "(reading from FILENAME)". If no definition can be found at all, Masterscope will print a message "FN can't be analyzed". If the function previously was known, the message "FN disappeared!" is printed. REANALYZE SET [Masterscope Command] Causes Masterscope to reanalyze the functions in SET (and any functions called by them) even if it thinks it already has valid information in its database. For example, this would be necessary if the user had disabled or subverted the file package, e.g. performed PUTD's to change the definition of functions. ERASE SET [Masterscope Command] Erase all information about the functions in SET from the database. ERASE by itself clears the entire database. SHOW PATHS PATHOPTIONS [Masterscope Command] Displays a tree of function calls. This is described in the SHOW PATHS section below. SET RELATION SET [Masterscope Command] SET IS SET [Masterscope Command] SET ARE SET [Masterscope Command] This command has the same format as an English sentence with a subject (the first SET), a verb (the RELATION or IS or ARE), and an object (the second SET). Any of the SETs within the command may be preceded by the question determiners WHICH or WHO (or just WHO alone). For example, WHICH FUNCTIONS CALL X prints the list of functions that call the function X. RELATION may be one of the relation words in present tense (CALL, BIND, TEST, SMASH, etc.) or used as a passive (e.g., WHO IS CALLED BY WHO). Other variants are allowed, e.g. WHO DOES X CALL, IS FOO CALLED BY FIE, etc. The interpretation of the command depends on the number of question elements present: 1. If there is no question element, the command is treated as an assertion and Masterscope returns either T or NIL, depending on whether that assertion is true. Thus, ANY IN MYFNS CALL HELP will print T if any function in MYFNS call the function HELP, and NIL otherwise. 2. If there is one question element, Masterscope returns the list of items for which the assertion would be true. For example, MYFN BINDS WHO USED FREELY BY YOURFN prints the list of variables bound by MYFN which are also used freely by YOURFN. 3. If there are two question elements, Masterscope will print a doubly indexed list: . WHO CALLS WHO IN /FNScr RECORDSTATEMENT -- /RPLNODE RECORDECL1 -- /NCONC, /RPLACD, /RPLNODE RECREDECLARE1 -- /PUTHASH UNCLISPTRAN -- /PUTHASH, /RPLNODE2 RECORDWORD -- /RPLACA RECORD1 -- /RPLACA, /SETTOPVAL EDITREC -- /SETTOPVAL EDIT WHERE SET RELATION SET [- EDITCOMS] [Masterscope Command] (WHERE may be omitted.) The first SET refers to a set of functions. The EDIT command calls the editor on each expression where the RELATION actually occurs. For example, EDIT WHERE ANY CALL ERROR will call EDITF on each (analyzed) function which calls ERROR stopping within a TTY: at each call to ERROR. Currently one cannot EDIT WHERE a file which CONTAINS a datum, nor where one function CALLS another SOMEHOW. EDITCOMS, if given, are a list of commands passed to EDITF to be performed at each expression. For example, EDIT WHERE ANY CALLS MYFN DIRECTLY - (SW 2 3) P will switch the first and second arguments to MYFN in every call to MYFN and print the result. EDIT WHERE ANY ON MYFILE CALL ANY NOT @ GETD will call the editor on any expression involving a call to an undefined function. Note that EDIT WHERE X SETS Y will point only at those expressions where Y is actually set, and will skip over places where Y is otherwise mentioned. SHOW WHERE SET RELATION SET [Masterscope Command] Like the EDIT command except merely prints out the expressions without calling the editor. EDIT SET [- EDITCOMS] [Masterscope Command] Calls EDITF on each function in SET. EDITCOMS, if given, will be passed as a list of editor commands to be executed. For example EDIT ANY CALLING FN1 - (R FN1 FN2) will replace FN1 by FN2 in those functions that call FN1. DESCRIBE SET [Masterscope Command] Prints out the BIND, USE FREELY and CALL information about the functions in SET. For example, the command DESCRIBE PRINTARGS might print out: PRINTARGS[N,FLG] binds: TEM,LST,X calls: MSRECORDFILE,SPACES,PRIN1 called by: PRINTSENTENCE,MSHELP,CHECKER This shows that PRINTARGS has two arguments, N and FLG, binds internally the variables TEM, LST and X, calls MSRECORDFILE, SPACES and PRIN1 and is called by PRINTSENTENCE, MSHELP, and CHECKER. The user can specify additional information to be included in the description. DESCRIBELST is a list each of whose elements is a list containing a descriptive string and a form. The form is evaluated (it can refer to the name of the funtion being described by the free variable FN); if it returns a non-NIL value, the description string is printed followed by the value. If the value is a list, its elements are printed with commas between them. For example, the entry ("types: " (GETRELATION FN '(USE TYPE) T) would include a listing of the types used by each function. CHECK SET [Masterscope Command] Checks for various anomolous conditions (mainly in the compiler declarations) for the files in SET (if SET is not given, FILELST is used). For example, this command will warn about variables which are bound but never referenced, functions in BLOCKS delarations which aren't on the file containing the declaration, functions declared as ENTRIES but not in the block, variables which may not need to be declared SPECVARS because they are not used freely below the places where they are bound, etc. FOR VARIABLE SET I.S.TAIL [Masterscope Command] This command provides a way of combining CLISP iterative statements with Masterscope. An iterative statement will be constructed in which VARIABLE is iteratively assigned to each element of SET, and then the iterative statement tail I.S.TAIL is executed. For example, FOR X CALLED BY FOO WHEN CCODEP DO (PRINTOUT T X ,,, (ARGLIST X) T) will print out the name and argument list of all of the compiled functions which are called by FOO. Relations A relation is specified by one of the keywords below. Some of these "verbs" accept modifiers. For example, USE, SET, SMASH and REFERENCE all may be modified by FREELY. The modifier may occur anywhere within the command. If there is more than one verb, any modifier between two verbs is assumed to modify the first one. For example, in USING ANY FREELY OR SETTING X, the FREELY modifies USING but not SETTING; the entire phrase is interpreted as the set of all functions which either use any variable freely or set the variable X, whether or not X is set freely. Verbs can occur in the present tense (e.g., USE, CALLS, BINDS, USES) or as present or past participles (e.g., CALLING, BOUND, TESTED). The relations (with their modifiers) recognized by Masterscope are: CALL [Masterscope Relation] Function F1 calls F2 if the definition of F1 contains a form (F2 --). The CALL relation also includes any instance where a function uses a name as a function, as in (APPLY (QUOTE F2) --), (FUNCTION F2), etc. CALL SOMEHOW [Masterscope Relation] One function calls another SOMEHOW if there is some path from the first to the other. That is, if F1 calls F2, and F2 calls F3, then F1 CALLS F3 SOMEHOW. This information is not stored directly in the database; instead, Masterscope stores only information about direct function calls, and (re)computes the CALL SOMEHOW relation as necessary. USE [Masterscope Relation] If unmodified, the relation USE denotes variable usage in any way; it is the union of the relations SET, SMASH, TEST, and REFERENCE. SET [Masterscope Relation] A function SETs a variable if the function contains a form (SETQ var --), (SETQQ var --), etc. SMASH [Masterscope Relation] A function SMASHes a variable if the function calls a destructive list operation (RPLACA, RPLACD, DREMOVE, SORT, etc.) on the value of that variable. Masterscope will also find instances where the operation is performed on a "part" of the value of the variable; for example, if a function contains a form (RPLACA (NTH X 3) T) it will be noted as SMASHING X. Note that if the function contains a sequence (SETQ Y X), (RPLACA Y T) then Y is noted as being smashed, but not X. TEST [Masterscope Relation] A variable is TESTed by a function if its value is only distinguished between NIL and non-NIL. For example, the form (COND ((AND X --) --)) tests the value of X. REFERENCE [Masterscope Relation] This relation includes all variable usage except for SET. The verbs USE, SET, SMASH, TEST and REFERENCE may be modified by the words FREELY or LOCALLY. A variable is used FREELY if it is not bound in the function at the place of its use; alternatively, it is used LOCALLY if the use occurs within a PROG or LAMBDA that binds the variable. Masterscope also distinguishes between CALL DIRECTLY and CALL INDIRECTLY. A function is called DIRECTLY if it occurs as CAR-of-form in a normal evaluation context. A function is called INDIRECTLY if its name appears in a context which does not imply its immediate evaluation, for example (SETQ Y (LIST (FUNCTION FOO) 3)). The distinction is whether or not the compiled code of the caller would contain a direct call to the callee. Note that an occurrence of (FUNCTION FOO) as the functional argument to one of the built-in mapping functions which compile open is considered to be a direct call. In addition, CALL FOR EFFECT (where the value of the function is not used) is distinguished from CALL FOR VALUE. BIND [Masterscope Relation] The BIND relation between functions and variables includes both variables bound as function arguments and those bound in an internal PROG or LAMBDA expression. USE AS A FIELD [Masterscope Relation] Masterscope notes all uses of record field names within FETCH, REPLACE or CREATE expressions. FETCH [Masterscope Relation] Use of a field within a FETCH expression. REPLACE [Masterscope Relation] Use of a record field name within a REPLACE or CREATE expression. USE AS A RECORD [Masterscope Relation] Masterscope notes all uses of record names within CREATE or TYPE? expressions. Additionally, in (fetch (FOO FIE) of X), FOO is used as a record name. CREATE [Masterscope Relation] Use of a record name within a CREATE expression. USE AS A PROPERTY NAME [Masterscope Relation] Masterscope notes the property names used in GETPROP, PUTPROP, GETLIS, etc. expressions if the name is quoted. For example, if a function contains a form (GETPROP X (QUOTE INTERP)), then that function USEs INTERP as a property name. USE AS A CLISP WORD [Masterscope Relation] Masterscope notes all iterative statement operators and user defined CLISP words as being used as a CLISP word. CONTAIN [Masterscope Relation] Files contain functions, records, and variables. This relation is not stored in the database but is computed using the file package. DECLARE AS LOCALVAR [Masterscope Relation] DECLARE AS SPECVAR [Masterscope Relation] Masterscope notes internal "calls" to DECLARE from within functions. The following abbreviations are recognized: FREE=FREELY, LOCAL=LOCALLY, PROP=PROPERTY, REF=REFERENCE. Also, the words A, AN and NAME (after AS) are "noise" words and may be omitted. Note: Masterscope uses "templates" (see the Affecting Masterscope Analysis section below) to decide which relations hold between functions and their arguments. For example, the information that SORT SMASHes its first argument is contained in the template for SORT. Masterscope initially contains templates for most system functions which set variables, test their arguments, or perform destructive operations. The user may change existing templates or insert new ones in Masterscope's tables via the SETTEMPLATE function (see below). Set Specifications A "set" is a collection of things (functions, variables, etc.). A set is specified by a set phrase, consisting of a determiner (e.g., ANY, WHICH, WHO) followed by a type (e.g., FUNCTIONS, VARIABLES) followed by a specification (e.g., IN MYFNS, @ SUBRP). The determiner, type and specification may be used alone or in combination. For example, ANY FUNCTIONS IN MYFNS, ANY @ SUBRP, VARIABLES IN GLOBALVARS, and WHO are all acceptable set phrases. Set specifications are explained below: 'ATOM [Masterscope Set Specification] The simplest way to specify a set consisting of a single thing is by the name of that thing. For example, in the command WHO CALLS 'ERROR, the function ERROR is referred to by its name. Although the ' can be left out, to resolve possible ambiguities names should usually be quoted; e.g., WHO CALLS 'CALLS will return the list of functions which call the function CALLS. 'LIST [Masterscope Set Specification] Sets consisting of several atoms may be specified by naming the atoms. For example, the command WHO USES '(A B) returns the list of functions that use the variables A or B. IN EXPRESSION [Masterscope Set Specification] The form EXPRESSION is evaluated, and its value is treated as a list of the elements of a set. For example, IN GLOBALVARS specifies the list of variables in the value of the variable GLOBALVARS. @ PREDICATE [Masterscope Set Specification] A set may also be specified by giving a predicate which the elements of that set must satisfy. PREDICATE is either a function name, a LAMBDA expression, or an expression in terms of the variable X. The specification @ PREDICATE represents all atom for which the value of PREDICATE is non-NIL. For example, @ EXPRP specifies all those atoms which have expr definitions; @ (STRPOSL X CLISPCHARRAY) specifies those atoms which contain CLISP characters. The universe to be searched is either determined by the context within the command (e.g., in WHO IN FOOFNS CALLS ANY NOT @ GETD, the predicate is only applied to functions which are called by any functions in the list FOOFNS), or in the extreme case, the universe defaults to the entire set of things which have been noticed by Masterscope, as in the command WHO IS @ EXPRP. LIKE ATOM [Masterscope Set Specification] ATOM may contain ESCs; it is used as a pattern to be matched (as in the editor). For example, WHO LIKE /R$ IS CALLED BY ANY would find both /RPLACA and /RPLNODE. A set may also be specified by giving a relation its members must have with the members of another set: RELATIONING SET [Masterscope Set Specification] RELATIONING is used here generically to mean any of the relation words in the present participle form (possibly with a modifier), e.g., USING, SETTING, CALLING, BINDING. RELATIONING SET specifies the set of all objects which have that relation with some element of SET. For example, CALLING X specifies the set of functions which call the function X; USING ANY IN FOOVARS FREELY specifies the set of functions which uses freely any variable in the value of FOOVARS. RELATIONED BY SET [Masterscope Set Specification] RELATIONED IN SET [Masterscope Set Specification] This is similar to the RELATIONING construction. For example, CALLED BY ANY IN FOOFNS represents the set of functions which are called by any element of FOOFNS; USED FREELY BY ANY CALLING ERROR is the set of variables which are used freely by any function which also calls the function ERROR. BLOCKTYPE OF FUNCTIONS [Masterscope Set Specification] BLOCKTYPE ON FILES [Masterscope Set Specification] These phrases allow the user to ask about BLOCKS declarations on files (see Chapter 18). BLOCKTYPE is one of LOCALVARS, SPECVARS, GLOBALVARS, ENTRIES, BLKFNS, BLKAPPLYFNS, or RETFNS. BLOCKTYPE OF FUNCTIONS specifies the names which are declared to be BLOCKTYPE in any blocks declaration which contain any of FUNCTIONS (a "set" of functions). The "functions" in FUNCTIONS can either be block names or just functions in a block. For example, WHICH ENTRIES OF ANY CALLING 'Y BIND ANY GLOBALVARS ON 'FOO. BLOCKTYPE ON FILES specifies all names which are declared to be BLOCKTYPE on any of the given FILES (a "set" of files). FIELDS OF SET [Masterscope Set Specification] SET is a set of records. This denotes the field names of those records. For example, the command WHO USES ANY FIELDS OF BRECORD returns the list of all functions which do a fetch or replace with any of the field names declared in the record declaration of BRECORD. KNOWN [Masterscope Set Specification] The set of all functions which have been analyzed. For example, the command WHO IS KNOWN will print out the list of functions which have been analyzed. THOSE [Masterscope Set Specification] The set of things printed out by the last Masterscope question. For example, following the command WHO IS USED FREELY BY PARSE, the user could ask WHO BINDS THOSE to find out where those variables are bound. ON PATH PATHOPTIONS [Masterscope Set Specification] Refers to the set of functions which would be printed by the command SHOW PATHS PATHOPTIONS. For example, IS FOO BOUND BY ANY ON PATH TO 'PARSE tests if FOO might be bound "above" the function PARSE. SHOW PATHS is explained in detail in the SHOW PATHS section below. Sets may also be specified with "relative clauses" introduced by the word THAT, e.g. THE FUNCTIONS THAT BIND 'X. Set Determiners(MASTERSCOPE NIL Masterscope NIL NIL 10 SUBNAME SET% DETERMINERS SUBTEXT set% determiners) Set phrases may be preceded by a determiner. A determiner is one of the words THE, ANY, WHO or WHICH. The "question" determiners (WHO and WHICH) are only meaningful in some of the commands, namely those that take the form of questions. ANY and WHO (or WHOM) can be used alone; they are "wild-card" elements, e.g., the command WHO USES ANY FREELY, will print out the names of all (known) functions which use any variable freely. If the determiner is omitted, ANY is assumed; e.g. the command WHO CALLS '(PRINT PRIN1 PRIN2) will print the list of functions which call any of PRINT, PRIN1, PRIN2. THE is also allowed, e.g. WHO USES THE RECORD FIELD FIELDX. Set Types(MASTERSCOPE NIL Masterscope NIL NIL 10 SUBNAME SET% TYPES SUBTEXT set% types) Any set phrase has a type; that is, a set may specify either functions, variables, files, record names, record field names or property names. The type may be determined by the context within the command (e.g., in CALLED BY ANY ON FOO, the set ANY ON FOO is interpreted as meaning the functions on FOO since only functions can be CALLED), or the type may be given explicitly by the user (e.g., FUNCTIONS ON FIE). The following types are recognized: FUNCTIONS, VARIABLES, FILES, PROPERTY NAMES, RECORDS, FIELDS, I.S.OPRS. Also, the abbreviations FNS, VARS, PROPNAMES or the singular forms FUNCTION, FN, VARIABLE, VAR, FILE, PROPNAME, RECORD, FIELD are recognized. Note that most of these types correspond to built-in "file package types" (see Chapter 17). The type is used by Masterscope in a variety of ways when interpreting the set phrase: 1. Set types are used to disambiguate possible parsings. For example, both commands WHO SETS ANY BOUND IN X OR USED BY Y and WHO SETS ANY BOUND IN X OR CALLED BY Y have the same general form. However, the first case is parsed as WHO SETS ANY (BOUND BY X OR USED BY Y) since both BOUND BY X and USED BY Y refer to variables; while the second case as WHO SETS ANY BOUND IN (X OR CALLED BY Y), since CALLED BY Y and X must refer to functions. Note that parentheses may be used to group phrases. 2. The type is used to determine the modifier for USE: FOO USES WHICH RECORDS is equivalent to FOO USES WHO AS A RECORD FIELD. 3. The interpretation of CONTAIN depends on the type of its object: the command WHAT FUNCTIONS ARE CONTAINED IN MYFILE prints the list of functions in MYFILE; WHAT RECORDS ARE ON MYFILE prints the list of records. 4. The implicit "universe" in which a set expression is interpreted depends on the type: ANY VARIABLES @ GETD is interpreted as the set of all variables which have been noticed by Masterscope (i.e., bound or used in any function which has been analyzed) that also have a definition. ANY FUNCTIONS @ (NEQ (GETTOPVAL X) 'NOBIND) is interpreted as the set of all functions which have been noticed (either analyzed or called by a function which has been analyzed) that also have a top-level value. Conjunctions of Sets Sets may be joined by the conjunctions AND and OR or preceded by NOT to form new sets. AND is always interpreted as meaning "intersection"; OR as "union", while NOT means "complement". For example, the set CALLING X AND NOT CALLED BY Y specifies the set of all functions which call the function X but are not called by Y. Masterscope's interpretation of AND and OR follow LISP conventions rather than the conventional English interpretation. For example "calling X and Y" would, in English, be interpreted as the intersection of (CALLING X) and (CALLING Y); but Masterscope interprets CALLING X AND Y as CALLING ('X AND 'Y); which is the null set. Only sets may be joined with conjunctions: joining modifiers, as in USING X AS A RECORD FIELD OR PROPERTY NAME, is not allowed; in this case, the user must say USING X AS A RECORD FIELD OR USING X AS A PROPERTY NAME. As described above, the type of sets is used to disambiguate parsings. The algorithm used is to first try to match the type of the phrases being joined and then try to join with the longest preceding phrase. In any case, the user may group phrases with parentheses to specify the manner in which conjunctions should be parsed. SHOW PATHS 1 In trying to work with large programs, you can lose track of the hierarchy of functions. The Masterscope SHOW PATHS command aids you by providing a map showing the calling structure of a set of functions. SHOW PATHS prints out a tree structure showing which functions call which other functions. For example, the command SHOW PATHS FROM MSPARSE will print out the structure of Masterscope's parser: 1.MSPARSE MSINIT MSMARKINVALID 2. | MSINITH MSINITH 3. MSINTERPRET MSRECORDFILE 4. | MSPRINTWORDS 5. | PARSECOMMAND GETNEXTWORD CHECKADV 6. | | PARSERELATION {a} 7. | | PARSESET {b} 8. | | PARSEOPTIONS {c} 9. | | MERGECONJ GETNEXTWORD {5} 10. | GETNEXTWORD {5} 11. | FIXUPTYPES SUBJTYPE 12. | | OBJTYPE 13. | FIXUPCONJUNCTIONS MERGECONJ {9} 14. | MATCHSCORE 15. MSPRINTSENTENCE ----------------------------------------------------- overflow - a 16.PARSERELATION GETNEXTWORD {5} 17. CHECKADV ----------------------------------------------------- overflow - b 19.PARSESET PARSESET 20. GETNEXTWORD {5} 21. PARSERELATION {6} 22. SUBPARSE GETNEXTWORD {5} ----------------------------------------------------- overflow - c 23.PARSEOPTIONS GETNEXTWORD {5} 24. PARSESET {19} The above printout displays that the function MSPARSE calls MSINIT, MSINTERPRET, and MSPRINTSENTENCE. MSINTERPRET in turn calls MSRECORDFILE, MSPRINTWORDS, PARSECOMMAND, GETNEXTWORD, FIXUPTYPES, and FIXUPCONJUNCTIONS. The numbers in braces {} after a function name are backward references: they indicate that the tree for that function was expanded on a previous line. The lowercase letters in braces are forward references: they indicate that the tree for that function will be expanded below, since there is no more room on the line. The vertical bar is used to keep the output aligned. Note: Loading the Browser library package modifies the SHOW PATHS command so the command's output is displayed as an undirected graph. The SHOW PATHS command takes the form: SHOW PATHS followed by some combination of the following path options: FROM SET [Masterscope Path Option] Display the function calls from the elements of SET. TO SET [Masterscope Path Option] Display the function calls leading to elements of SET. If TO is given before FROM (or no FROM is given), the tree is "inverted" and a message, (inverted tree) is printed to warn the user that if FN1 appears after FN2 it is because FN1 is called by FN2. When both FROM and TO are given, the first one indicates a set of functions which are to be displayed while the second restricts the paths that will be traced; i.e., the command SHOW PATHS FROM X TO Y will trace the elements of the set CALLED SOMEHOW BY X AND CALLING Y SOMEHOW. If TO is not given, TO KNOWN OR NOT @ GETD is assumed; that is, only functions which have been analyzed or which are undefined will be included. Note that Masterscope will analyze a function while printing out the tree if that function has not previously been seen and it currently has an expr definition; thus, any function which can be analyzed will be displayed. AVOIDING SET [Masterscope Path Option] Do not display any function in SET. AMONG is recognized as a synonym for AVOIDING NOT. For example, SHOW PATHS TO ERROR AVOIDING ON FILE2 will not display (or trace) any function on FILE2. NOTRACE SET [Masterscope Path Option] Do not trace from any element of SET. NOTRACE differs from AVOIDING in that a function which is marked NOTRACE will be printed, but the tree beyond it will not be expanded; the functions in an AVOIDING set will not be printed at all. For example, SHOW PATHS FROM ANY ON FILE1 NOTRACE ON FILE2 will display the tree of calls eminating from FILE1, but will not expand any function on FILE2. SEPARATE SET [Masterscope Path Option] Give each element of SET a separate tree. Note that FROM and TO only insure that the designated functions will be displayed. SEPARATE can be used to guarantee that certain functions will begin new tree structures. SEPARATE functions are displayed in the same manner as overflow lines; i.e., when one of the functions indicated by SEPARATE is found, it is printed followed by a forward reference (a lower-case letter in braces) and the tree for that function is then expanded below. LINELENGTH N [Masterscope Path Option] Resets LINELENGTH to N before displaying the tree. The linelength is used to determine when a part of the tree should "overflow" and be expanded lower. Error Messages 1 When the user gives Masterscope a command, the command is first parsed, i.e. translated to an internal representation, and then the internal representation is interpreted. If a command cannot be parsed, e.g. if you typed SHOW WHERE CALLED BY X, the message "Sorry, I can't parse that!" is printed and an error is generated. If the command is of the correct form but cannot be interpreted (e.g., the command EDIT WHERE ANY CONTAINS ANY) Masterscope will print the message "Sorry, that isn't implemented!" and generate an error. If the command requires that some functions having been analyzed (e.g., the command WHO CALLS X) and the database is empty, Masterscope will print the message "Sorry, no functions have been analyzed!" and generate an error. Macro Expansion 1 As part of analysis, Masterscope will expand the macro definition of called functions, if they are not otherwise defined (see Chapter 10). Masterscope macro expansion is controlled by the variable MSMACROPROPS: MSMACROPROPS [Variable] Value is an ordered list of macro-property names that Masterscope will search to find a macro definition. Only the kinds of macros that appear on MSMACROPROPS will be expanded. All others will be treated as function calls and left unexpanded. Initially (MACRO). MSMACROPROPS initially contains only MACRO (and not 10MACRO, DMACRO, etc.) in the theory that the machine-dependent macro definitions are more likely "optimizers". If you edit a macro, Masterscope will know to reanalyze the functions which call that macro. However, if your macro is of the "computed-macro" style, and it calls functions which you edit, Masterscope will not notice. You must be careful to tell masterscope to REANALYZE the appropriate functions (e.g., if you edit FOOEXPANDER which is used to expand FOO macros, you have to . REANALYZE ANY CALLING FOO. Affecting Masterscope Analysis 1 Masterscope analyzes the expr definitions of functions and notes in its database the relations that function has with other functions and with variables. To perform this analysis, Masterscope uses templates which describe the behavior of functions. For example, the information that SORT destructively modifies its first argument is contained in the template for SORT. Masterscope initially contains templates for most system functions which set variables, test their arguments, or perform destructive operations. A template is a list structure containing any of the following atoms: PPE [in Masterscope template] If an expression appears in this location, there is most likely a parenthesis error. Masterscope notes this as a "call" to the function "ppe" (lowercase). Therefore, SHOW WHERE ANY CALLS ppe will print out all possible parenthesis errors. When Masterscope finds a possible parenthesis error in the course of analyzing a function definition, rather than printing the usual ".", it prints out a "?" instead. NIL [in Masterscope template] The expression occuring at this location is not evaluated. SET [in Masterscope template] A variable appearing at this place is set. SMASH [in Masterscope template] The value of this expression is smashed. TEST [in Masterscope template] This expression is used as a predicate (that is, the only use of the value of the expression is whether it is NIL or non-NIL). PROP [in Masterscope template] The value of this expression is used as a property name. If the expression is of the form (QUOTE ATOM), Masterscope will note that ATOM is USED AS A PROPERTY NAME. For example, the template for GETPROP is (EVAL PROP . PPE). FUNCTION [in Masterscope template] The expression at this point is used as a functional argument. For example, the template for MAPC is (SMASH FUNCTION FUNCTION . PPE). FUNCTIONAL [in Masterscope template] The expression at this point is used as a functional argument. This is like FUNCTION, except that Masterscope distinguishes between functional arguments to functions which "compile open" from those that do not. For the latter (e.g. SORT and APPLY), FUNCTIONAL should be used rather than FUNCTION. EVAL [in Masterscope template] The expression at this location is evaluated (but not set, smashed, tested, used as a functional argument, etc.). RETURN [in Masterscope template] The value of the function (of which this is the template) is the value of this expression. TESTRETURN [in Masterscope template] A combination of TEST and RETURN: If the value of the function is non-NIL, then it is returned. For instance, a one-element COND clause is this way. EFFECT [in Masterscope template] The expression at this location is evaluated, but the value is not used. FETCH [in Masterscope template] An atom at this location is a field which is fetched. REPLACE [in Masterscope template] An atom at this location is a field which is replaced. RECORD [in Masterscope template] An atom at this location is used as a record name. CREATE [in Masterscope template] An atom at this location is a record which is created. BIND [in Masterscope template] An atom at this location is a variable which is bound. CALL [in Masterscope template] An atom at this location is a function which is called. CLISP [in Masterscope template] An atom at this location is used as a CLISP word. ! [in Masterscope template] This atom, which can only occur as the first element of a template, allows one to specify a template for the CAR of the function form. If ! doesn't appear, the CAR of the form is treated as if it had a CALL specified for it. In other words, the templates (.. EVAL) and (! CALL .. EVAL) are equivalent. If the next atom after a ! is NIL, this specifies that the function name should not be remembered. For example, the template for AND is (! NIL .. TEST RETURN), which means that if you see an "AND", don't remember it as being called. This keeps the Masterscope database from being cluttered by too many uninteresting relations; Masterscope also throws away relations for COND, CAR, CDR, and a couple of others. In addition to the above atoms which occur in templates, there are some "special forms" which are lists keyed by their CAR. .. TEMPLATE [in Masterscope template] Any part of a template may be preceded by the atom .. (two periods) which specifies that the template should be repeated an indefinite number (N>=0) of times to fill out the expression. For example, the template for COND might be (.. (TEST .. EFFECT RETURN)) while the template for SELECTQ is (EVAL .. (NIL .. EFFECT RETURN) RETURN). (BOTH TEMPLATE1 TEMPLATE2) [in Masterscope template] Analyze the current expression twice, using the each of the templates in turn. (IF EXPRESSION TEMPLATE1 TEMPLATE2) [in Masterscope template] Evaluate EXPRESSION at analysis time (the variable EXPR will be bound to the expression which corresponds to the IF), and if the result is non-NIL, use TEMPLATE1, otherwise TEMPLATE2. If EXPRESSION is a literal atom, it is APPLY'd to EXPR. For example, (IF LISTP (RECORD FETCH) FETCH) specifies that if the current expression is a list, then the first element is a record name and the second element a field name, otherwise it is a field name. (@ EXPRFORM TEMPLATEFORM) [in Masterscope template] Evaluate EXPRFORM giving EXPR, evaluate TEMPLATEFORM giving TEMPLATE. Then analyze EXPR with TEMPLATE. @ lets you compute on the fly both a template and an expression to analyze with it. The forms can use the variable EXPR, which is bound to the current expression. (MACRO . MACRO) [in Masterscope template] MACRO is interpreted in the same way as a macro (see Chapter 10) and the resulting form is analyzed. If the template is the atom MACRO alone, Masterscope will use the MACRO property of the function itself. This is useful when analyzing code which contains calls to user-defined macros. If you change a macro property (e.g., by editing it) of an atom which has template of MACRO, Masterscope will mark any function which used that macro as needing to be reanalyzed. Some examples of templates: function: template: DREVERSE (SMASH . PPE) AND (! NIL TEST .. RETURN) MAPCAR (EVAL FUNCTION FUNCTION) COND (! NIL .. (IF CDR (TEST .. EFFECT RETURN) (TESTRETURN . PPE))) Templates may be changed and new templates defined using the functions: (GETTEMPLATE FN) [Function] Returns the current template of FN. (SETTEMPLATE FN TEMPLATE) [Function] Changes the template for the function FN and returns the old value. If any functions in the database are marked as calling FN, they will be marked as needing re-analysis. Database Updating 1 Masterscope is interfaced to the editor and file package so that it notes whenever a function has been changed, either through editing or loading in a new definition. Whenever a command is given which requires knowing the information about a specific function, if that function has been noted as being changed, the function is automatically re-analyzed before the command is interpreted. If the command requires that all the information in the database be consistent (e.g., the user asks WHO CALLS X) then all functions which have been marked as changed are re-analyzed. Masterscope Entries 1 (CALLS FN USEDATABASE %) [Function] FN can be a function name, a definition, or a form. CALLS will also work on compiled code. CALLS returns a list of four elements: a list of all the functions called by FN, a list of all the variables bound in FN, a list of all the variables used freely in FN, and a list of the variables used globally in FN. For the purpose of CALLS, variables used freely which are on GLOBALVARS or have a property GLOBALVAR value T are considered to be used globally. If USEDATABASE is NIL (or FN is not a litatom), CALLS will perform a one-time analysis of FN. Otherwise (i.e. if USEDATABASE is non-NIL and FN a function name), CALLS will use the information in Masterscope's database (FN will be analyzed first if necessary). (CALLSCCODE FN % %) [Function] The sub-function of CALLS which analyzes compiled code. CALLSCCODE returns a list of five elements: a list of all the functions called via "linked" function calls (not implemented in Interlisp-D), a list of all functions called regularly, a list of variables bound in FN, a list of variables used freely, and a list of variables used globally. (FREEVARS FN USEDATABASE) [Function] Equivalent to (CADDR (CALLS FN USEDATABASE)). Returns the list of variables used freely within FN. (MASTERSCOPE COMMAND %) [Function] Top level entry to Masterscope. If COMMAND is NIL, will enter into an executive in which the user may enter commands. If COMMAND is not NIL, the command is interpreted and MASTERSCOPE will return the value that would be printed by the command. Note that only the question commands return meaningful values. (SETSYNONYM PHRASE MEANING %) [Function] Defines a new synonym for Masterscope's parser. Both OLDPHRASE and NEWPHRASE are words or lists of words; anywhere OLDPHRASE is seen in a command, NEWPHRASE will be substituted. For example, (SETSYNONYM 'GLOBALS '(VARS IN GLOBALVARS OR @(GETPROP X 'GLOBALVAR))) would allow the user to refer with the single word GLOBALS to the set of variables which are either in GLOBALVARS or have a GLOBALVAR property. The following functions allow you to write your own routines using Masterscope's database: (PARSERELATION RELATION) [Function] RELATION is a relation phrase; e.g., (PARSERELATION '(USE FREELY)). PARSERELATION returns an internal representation for RELATION. For use in conjunction with GETRELATION. (GETRELATION ITEM RELATION INVERTED) [Function] RELATION is an internal representation as returned by PARSERELATION (if not, GETRELATION will first perform (PARSERELATION RELATION)); ITEM is an atom. GETRELATION returns the list of all atoms which have the given relation to ITEM. For example, (GETRELATION 'X '(USE FREELY)) returns the list of variables that X uses freely. If INVERTED is T, the inverse relation is used; e.g. (GETRELATION 'X '(USE FREELY) T) returns the list of functions which use X freely. If ITEM is NIL, GETRELATION will return the list of atoms which have RELATION with any other item; i.e., answers the question WHO RELATIONS ANY. GETRELATION does not check to see if ITEM has been analyzed, or that other functions that have been changed have been re-analyzed. (TESTRELATION ITEM RELATION ITEM2 INVERTED) [Function] Equivalent to (MEMB ITEM2 (GETRELATION ITEM RELATION INVERTED)), that is, tests if ITEM and ITEM2 are related via RELATION. If ITEM2 is NIL, the call is equivalent to (NOT (NULL (GETRELATION ITEM RELATION INVERTED))), i.e., TESTRELATION tests if ITEM has the given RELATION with any other item. (MAPRELATION RELATION MAPFN) [Function] Calls the function MAPFN on every pair of items related via RELATION. If (NARGS MAPFN) is 1, then MAPFN is called on every item which has the given RELATION to any other item. (MSNEEDUNSAVE FNS MSG MARKCHANGEFLG) [Function] Used to mark functions which depend on a changed record declaration (or macro, etc.), and which must be LOADed or UNSAVEd (see below). FNS is a list of functions to be marked, and MSG is a string describing the records, macros, etc. on which they depend. If MARKCHANGEFLG is non-NIL, each function in the list is marked as needing re-analysis. (UPDATEFN FN EVENIFVALID %) [Function] Equivalent to the command ANALYZE 'FN; that is, UPDATEFN will analyze FN if FN has not been analyzed before or if it has been changed since the time it was analyzed. If EVENIFVALID is non-NIL, UPDATEFN will re-analyze FN even if Masterscope thinks it has a valid analysis in the database. (UPDATECHANGED) [Function] Performs (UPDATEFN FN) on every function which has been marked as changed. (MSMARKCHANGED NAME TYPE REASON) [Function] Mark that NAME has been changed and needs to be reanalyzed. See MARKASCHANGED in Chapter 17. (DUMPDATABASE FNLST) [Function] Dumps the current Masterscope database on the current output file in a LOADable form. If FNLST is not NIL, DUMPDATABASE will only dump the information for the list of functions in FNLST. The variable DATABASECOMS is initialized to ((E (DUMPDATABASE))); thus, the user may merely perform (MAKEFILE 'DATABASE.EXTENSION) to save the current Masterscope database. If a Masterscope database already exists when a DATABASE file is loaded, the database on the file will be merged with the one in core. Note that functions whose definitions are different from their definition when the database was made must be REANALYZEd if their new definitions are to be noticed. The Databasefns library package provides a more convenient way of saving databases along with the source files which they correspond to. Noticing Changes that Require Recompiling 1 When a record declaration, iterative statement operator or macro is changed, and Masterscope has "noticed" a use of that declaration or macro (i.e. it is used by some function known about in the database), Masterscope alerts you about those functions which might need to be re-compiled (e.g. they do not currently have expr definitions). Extra functions may be noticed. For example, if FOO contains (fetch (REC X) --), and some declaration other than REC which contains X is changed, Masterscope will still think that FOO needs to be loaded/unsaved. The functions which need recompiling are added to the list MSNEEDUNSAVE and a message is printed out: The functions FN1, FN2,... use macros which have changed. Call UNSAVEFNS() to load and/or unsave them. In this situation, the following function is useful: (UNSAVEFNS %) [Function] Uses LOADFNS or UNSAVEDEF to make sure that all functions in the list MSNEEDUNSAVE have expr definitions, and then sets MSNEEDUNSAVE to NIL. Note: If RECOMPILEDEFAULT (see Chapter 18) is set to CHANGES, UNSAVEFNS prints out "WARNING: you must set RECOMPILEDEFAULT to EXPRS in order to have these functions recompiled automatically". Implementation Notes 1 Masterscope keeps a database of the relations noticed when functions are analyzed. The relations are intersected to form "primitive relationships" such that there is little or no overlap of any of the primitives. For example, the relation SET is stored as the union of SET LOCAL and SET FREE. The BIND relation is divided into BIND AS ARG, BIND AND NOT USE, and SET LOCAL, SMASH LOCAL, etc. Splitting the relations in this manner reduces the size of the database considerably, to the point where it is reasonable to maintain a Masterscope database for a large system of functions during a normal debugging session. Each primitive relationship is stored in a pair of hash-tables, one for the "forward" direction and one for the "reverse". For example, there are two hash tables, USE AS PROPERTY and USED AS PROPERTY. To retrieve the information from the database, Masterscope performs unions of the hash-values. For example, to answer FOO BINDS WHO Masterscope will look in all of the tables which make up the BIND relation. The "internal representation" returned by PARSERELATION is just a list of dotted pairs of hash-tables. To perform GETRELATION requires only mapping down that list, doing GETHASH's on the appropriate hash-tables and UNIONing the result. Hash tables are used for a variety of reasons: storage space is smaller; it is not necessary to maintain separate lists of which functions have been analyzed (a special table, DOESN'T DO ANYTHING is maintained for functions which neither call other functions nor bind or use any variables); and accessing is relatively fast. Within any of the tables, if the hash-value would be a list of one atom, then the atom itself, rather than the list, is stored as the hash-value. This also reduces the size of the database significantly. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))))) ,,2Hll-llT4(TT2l2l~~F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR,ll5,ll,ll2HZZ3HZT3$$(T0$$T3HZ +T,ll0$$T-T3(T,2,HH +,HH,HH5,,TITAN +TITAN +CLASSIC + HELVETICA + HELVETICA  HELVETICA HELVETICATITAN +CLASSIC +CLASSIC +CLASSIC +MODERNMODERN +MODERN +MODERN +  HRULE.GETFNCLASSIC +    + HRULE.GETFNCLASSIC +  +    HRULE.GETFNCLASSIC +   HRULE.GETFNCLASSIC +  IM.CHAP.GETFN HELVETICA HRULE.GETFNMODERN + X  C    ,& !   ++ +)  + ,#!- .(( :  +4  +  P  +  +    + + +  +T  +  H   + 9  +y  1 '  G  ,   6   +> *   >  .  Y G  HRULE.GETFNMODERN +O ' = +    +   &  " 6  + D V   <    K ;      + A         + - )  t  ]  +  +L   +7  +     1  *    -    +'  +  = +                 R          A   +  5   4 +  $ & % V   Y  6        n $'   U   .(( +   +      # 6   )    + !    - 2 //    +,] , 2   +    M    +      T "         + $     + +* +- +   !    +       O    * +<    _     s X C M    ) ]  ,  (   F_   l     e  @   +  x  =    +   H       W      A        +   E       -    B      + .  $    <         *     +        W    '    ? +;   }  E    }    8        $    2         -   V     o     w   &  +  +            +(  +7   u +           ]  +   J  + ! z  +  +X ;   + ! a 6    + + !  + Z > +  + ! `  7   + ,   8  "[    !  K    h   !  }       + P   8   +O       !    +  + D  ]            * *   +       + + . 0 - G $  + + .         _ .  +  +C   L ? ! c  -   %    + +  %2  +9 J   ZIM.INDEX.GETFN! + $      ^   F r  -        +  NIM.INDEX.GETFN   + +   : (                  n W U $ &B & + . (  O 2     0  +!!   Z  + '    2  + <     c  ;    ^  +2 7 I   HRULE.GETFNMODERN +j +[ +k + + 7 !   $    -            &         +  (  C !  C    % C  .          +    8 +F  + +/     0       #      2  +%       +   $ )   "           %-     !    $ S / -/ &         ? R l   +    +   HRULE.GETFNMODERN +  +  +|  +&  +n A ' +   HRULE.GETFNMODERN +     a     +  a  .     HRULE.GETFNMODERN + N L  F  U 4    +  +  :  *  (  n    [  +     + !   +  ]    + M    +   q  Z  +   ' 3   H  5  6  2  6  6  8  1  m   ' 2     a  "     w     3  +Z  I  +  +   +     N      + :       +                   s +    } !  W   !EH           &  T  -  HRULE.GETFNMODERN +   >  HRULE.GETFNMODERN +      3 # H  '  -  /   % +  )     %       5  &       +   I       +  + 4       $   I   ! |     6  '  $  +;3 - +  [       (         .  +   +    @   $    % )      *    (  +  +     Z       +  +  +  +  +            +  +  +   +            $    +   -          *  L H           \     D    + 4     +  3      G     =    $  + \  .  * HRULE.GETFNMODERN +  + "  / Y   +  + $ +- +5      - &    +    j +  HRULE.GETFNMODERN +       +     + z  +> 6 < - &    + N Q z \ No newline at end of file diff --git a/docs/porter-irm/20-DWIM.TEDIT b/docs/porter-irm/20-DWIM.TEDIT new file mode 100644 index 00000000..5e2ac615 --- /dev/null +++ b/docs/porter-irm/20-DWIM.TEDIT @@ -0,0 +1,130 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 20. DWIM 1 20. DWIM 1 20. DWIM 6 A surprisingly large percentage of the errors made by Interlisp users are of the type that could be corrected by another Lisp programmer without any information about the purpose of the program or expression in question, e.g., misspellings, certain kinds of parentheses errors, etc. To correct these types of errors we have implemented in Interlisp a DWIM facility, short for Do-What-I-Mean. DWIM is called automatically whenever an error occurs in the evaluation of an Interlisp expression. (Currently, DWIM only operates on unbound atoms and undefined function errors.) DWIM then proceeds to try to correct the mistake using the current context of computation plus information about what the user had previously been doing, (and what mistakes he had been making) as guides to the remedy of the error. If DWIM is able to make the correction, the computation continues as though no error had occurred. Otherwise, the procedure is the same as though DWIM had not intervened: a break occurs, or an unwind to the last ERRORSET (see Chapter 14). The following protocol illustrates the operation of DWIM. For example, suppose the user defines the factorial function (FACT N) as follows: DEFINEQ((FACT (LAMBDA (N) (COND ((ZEROP N0 1) ((T (ITIMS N (FACCT 9SUB1 N] (FACT) Note that the definition of FACT contains several mistakes: ITIMES and FACT have been misspelled; the 0 in N0 was intended to be a right parenthesis, but the shift key was not depressed; similarly, the 9 in 9SUB1 was intended to be a left parenthesis; and finally, there is an extra left parenthesis in front of the T that begins the final clause in the conditional. PRETTYPRNT((FACCT] =PRETTYPRINT =FACT (FACT [LAMBDA (N) (COND ((ZEROP N0 1) ((T (ITIMS N (FACCT 9SUB1 N]) (FACT) After defining FACT, the user wishes to look at its definition using PRETTYPRINT, which he unfortunately misspells. Since there is no function PRETTYPRNT in the system, an undefined function error occurs, and DWIM is called. DWIM invokes its spelling corrector, which searches a list of functions frequently used (by this user) for the best possible match. Finding one that is extremely close, DWIM proceeds on the assumption that PRETTYPRNT meant PRETTYPRINT, notifies the user of this, and calls PRETTYPRINT. At this point, PRETTYPRINT would normally print (FACCT NOT PRINTABLE) and exit, since FACCT has no definition. Note that this is not an Interlisp error condition, so that DWIM would not be called as described above. However, it is obviously not what the user meant. This sort of mistake is corrected by having PRETTYPRINT itself explicitly invoke the spelling corrector portion of DWIM whenever given a function with no EXPR definition. Thus, with the aid of DWIM PRETTYPRINT is able to determine that the user wants to see the definition of the function FACT, and proceeds accordingly. FACT(3] N0 [IN FACT] -> N ) ? YES [IN FACT] (COND -- ((T --))) -> (COND -- (T --)) ITIMS [IN FACT] -> ITIMES FACCT [IN FACT] -> FACT 9SUB1 [IN FACT] -> ( SUB1 ? YES 6 PP FACT (FACT [LAMBDA (N) (COND ((ZEROP N) 1) (T (ITIMES N (FACT (SUB1 N]) FACT The user now calls FACT. During its execution, five errors occur, and DWIM is called five times. At each point, the error is corrected, a message is printed describing the action taken, and the computation is allowed to continue as if no error had occurred. Following the last correction, 6 is printed, the value of (FACT 3). Finally, the user prettyprints the new, now correct, definition of FACT. In this particular example, the user was shown operating in TRUSTING mode, which gives DWIM carte blanche for most corrections. The user can also operate in CAUTIOUS mode, in which case DWIM will inform him of intended corrections before they are made, and allow the user to approve or disapprove of them. If DWIM was operating in CAUTIOUS mode in the example above, it would proceed as follows: FACT(3) N0 [IN FACT] -> N ) ? YES U.D.F. T [IN FACT] FIX? YES [IN FACT] (COND -- ((T --))) -> (COND -- (T --)) ITIMS [IN FACT] -> ITIMES ? ...YES FACCT [IN FACT] -> FACT ? ...YES 9SUB1 [IN FACT] -> ( SUB1 ? NO U.B.A. (9SUB1 BROKEN) : For most corrections, if the user does not respond in a specified interval of time, DWIM automatically proceeds with the correction, so that the user need intervene only when he does not approve. Note that the user responded to the first, second, and fifth questions; DWIM responded for him on the third and fourth. DWIM uses ASKUSER for its interactions with the user (see Chapter 26). Whenever an interaction is about to take place and the user has typed ahead, ASKUSER types several bells to warn the user to stop typing, then clears and saves the input buffers, restoring them after the interaction is complete. Thus if the user has typed ahead before a DWIM interaction, DWIM will not confuse his type ahead with the answer to its question, nor will his typeahead be lost. The bells are printed by the function PRINTBELLS, which can be advised or redefined for specialized applications, e.g. to flash the screen for a display terminal. A great deal of effort has gone into making DWIM "smart", and experience with a large number of users indicates that DWIM works very well; DWIM seldom fails to correct an error the user feels it should have, and almost never mistakenly corrects an error. However, it is important to note that even when DWIM is wrong, no harm is done: since an error had occurred, the user would have had to intervene anyway if DWIM took no action. Thus, if DWIM mistakenly corrects an error, the user simply interrupts or aborts the computation, UNDOes the DWIM change using UNDO (see Chapter 13), and makes the correction he would have had to make without DWIM. An exception is if DWIM's correction mistakenly caused a destructive computation to be initiated, and information was lost before the user could interrupt. We have not yet had such an incident occur. (DWIM X) [Function] Used to enable/disable DWIM. If X is the litatom C, DWIM is enabled in CAUTIOUS mode, so that DWIM will ask you before making corrections. If X is T, DWIM is enabled in TRUSTING mode, so DWIM will make most corrections automatically. If X is NIL, DWIM is disabled. Interlisp initially has DWIM enabled in CAUTIOUS mode. DWIM returns CAUTIOUS, TRUSTING or NIL, depending to what mode it has just been put into. For corrections to expressions typed in by the user for immediate execution (typed into LISPX, Chapter 13), DWIM always acts as though it were in TRUSTING mode, i.e., no approval necessary. For certain types of corrections, e.g., run-on spelling corrections, 9-0 errors, etc., DWIM always acts like it was in CAUTIOUS mode, and asks for approval. In either case, DWIM always informs the user of its action as described below. Spelling Correction Protocol 1 One type of error that DWIM can correct is the misspelling of a function or a variable name. When an unbound litatom or undefined function error occurs, DWIM tries to correct the spelling of the bad litatom. If a litatom is found whose spelling is "close" to the offender, DWIM proceeds as follows: If the correction occurs in the typed-in expression, DWIM prints =CORRECT-SPELLINGcr and continues evaluating the expression. For example: (SETQ FOO (IPLUSS 1 2)) =IPLUS 3 If the correction does not occur in type-in, DWIM prints BAD-SPELLING [IN FUNCTION-NAME] -> CORRECT-SPELLING The appearance of -> is to call attention to the fact that the user's function will be or has been changed. Then, if DWIM is in TRUSTING mode, it prints a carriage return, makes the correction, and continues the computation. If DWIM is in CAUTIOUS mode, it prints a few spaces and ? and then wait for approval. The user then has six options: 1. Type Y. DWIM types es, and proceeds with the correction. 2. Type N. DWIM types o, and does not make the correction. 3. Type . DWIM does not make the correction, and furthermore guarantees that the error will not cause a break. 4. Type Control-E. For error correction, this has the same effect as typing N. 5. Do nothing. In this case DWIM waits for DWIMWAIT seconds, and if the user has not responded, DWIM will type ... followed by the default answer. The default on spelling corrections is determined by the value of the variable FIXSPELLDEFAULT, whose top level value is initially Y. 6. Type space or carriage-return. In this case DWIM will wait indefinitely. This option is intended for those cases where the user wants to think about his answer, and wants to insure that DWIM does not get "impatient" and answer for him. The procedure for spelling correction on other than Interlisp errors is analogous. If the correction is being handled as type-in, DWIM prints = followed by the correct spelling, and returns it to the function that called DWIM. Otherwise, DWIM prints the incorrect spelling, followed by the correct spelling. Then, if DWIM if in TRUSTING mode, DWIM prints a carriage-return and returns the correct spelling. Otherwise, DWIM prints a few spaces and a ? and waits for approval. The user can then respond with Y, N, Control-E, space, carriage return, or do nothing as described above. The spelling corrector itself is not ERRORSET protected like the DWIM error correction routines. Therefore, typing N and typing Control-E may have different effects when the spelling corrector is called directly. The former simply instructs the spelling corrector to return NIL, and lets the calling function decide what to do next; the latter causes an error which unwinds to the last ERRORSET, however far back that may be. Parentheses Errors Protocol 1 When an unbound litatom or undefined error occurs, and the offending litatom contains 9 or 0, DWIM tries to correct errors caused by typing 9 for left parenthesis and 0 for right parenthesis. In these cases, the interaction with the user is similar to that for spelling correction. If the error occurs in type-in, DWIM types =CORRECTIONcr, and continues evaluating the expression. For example: (SETQ FOO 9IPLUS 1 2] = ( IPLUS 3 If the correction does not occur in type-in, DWIM prints BAD-ATOM [IN FUNCTION-NAME] -> CORRECTION ? and then waits for approval. The user then has the same six options as for spelling correction, except the waiting time is 3*DWIMWAIT seconds. If the user types Y, DWIM then operates as if it were in TRUSTING mode, i.e., it makes the correction and prints its message. Actually, DWIM uses the value of the variables LPARKEY and RPARKEY to determine the corresponding lower case character for left and right parentheses. LPARKEY and RPARKEY are initially 9 and 0 respectively, but they can be reset for other keyboard layouts, e.g., on some terminals left parenthesis is over 8, and right parenthesis is over 9. Undefined Function T Errors 1 When an undefined function error occurs, and the offending function is T, DWIM tries to correct certain types of parentheses errors involving a T clause in a conditional. DWIM recognizes errors of the following forms: (COND --) (T --) The T clause appears outside and immediately following the COND. (COND -- (-- & (T --))) The T clause appears inside a previous clause. (COND -- ((T --))) The T clause has an extra pair of parentheses around it. For undefined function errors that are not one of these three types, DWIM takes no corrective action at all, and the error will occur. If the error occurs in type-in, DWIM simply types T FIXED and makes the correction. Otherwise if DWIM is in TRUSTING mode, DWIM makes the correction and prints the message: [IN FUNCTION-NAME] {BAD-COND} -> {CORRECTED-COND} If DWIM is in CAUTIOUS mode, DWIM prints UNDEFINED FUNCTION T [IN FUNCTION-NAME] FIX? and waits for approval. The user then has the same options as for spelling corrections and parenthesis errors. If you type Y or default, DWIM makes the correction and prints its message. Having made the correction, DWIM must then decide how to proceed with the computation. In the first case, (COND --) (T --), DWIM cannot know whether the T clause would have been executed if it had been inside of the COND. Therefore DWIM asks you CONTINUE WITH T CLAUSE (with a default of YES). If you type N, DWIM continues with the form after the COND, i.e., the form that originally followed the T clause. In the second case, (COND -- (-- & (T --))), DWIM has a different problem. After moving the T clause to its proper place, DWIM must return as the value of & as the value of the COND. Since this value is no longer around, DWIM asks you OK TO REEVALUATE and then prints the expression corresponding to &. If you type Y, or default, DWIM continues by reevaluating &, otherwise DWIM aborts, and a U.D.F. T error will then occur (even though the COND has in fact been fixed). If DWIM can determine for itself that the form can safely be reevaluated, it does not consult you before reevaluating. DWIM can do this if the form is atomic, or CAR of the form is a member of the list OKREEVALST, and each of the arguments can safely be reevaluated. For example, (SETQ X (CONS (IPLUS Y Z) W)) is safe to reevaluate because SETQ, CONS, and IPLUS are all on OKREEVALST. In the third case, (COND -- ((T --))), there is no problem with continuation, so no further interaction is necessary. DWIM Operation 1 Whenever the interpreter encounters an atomic form with no binding, or a non-atomic form CAR of which is not a function or function object, it calls the function FAULTEVAL. Similarly, when APPLY is given an undefined function, FAULTAPPLY is called. When DWIM is enabled, FAULTEVAL and FAULTAPPLY are redefined to first call the DWIM package, which tries to correct the error. If DWIM cannot decide how to fix the error, or the user disapproves of DWIM's correction (by typing N), or the user types control-E, then FAULTEVAL and FAULTAPPLY cause an error or break. If the user types ^ to DWIM, DWIM exits by performing (RETEVAL 'FAULTEVAL '(ERROR!)), so that an error will be generated at the position of the call to FAULTEVAL. If DWIM can (and is allowed to) correct the error, it exits by performing RETEVAL of the corrected form, as of the position of the call to FAULTEVAL or FAULTAPPLY. Thus in the example at the beginning of the chapter, when DWIM determined that ITIMS was ITIMES misspelled, DWIM called RETEVAL with (ITIMES N (FACCT 9SUB1 N)). Since the interpreter uses the value returned by FAULTEVAL exactly as though it were the value of the erroneous form, the computation will thus proceed exactly as though no error had occurred. In addition to continuing the computation, DWIM also repairs the cause of the error whenever possible; in the above example, DWIM also changed (with RPLACA) the expression (ITIMS N (FACCT 9SUB1 N)) that caused the error. Note that if the user's program had computed the form and called EVAL, it would not be possible to repair the cause of the error, although DWIM could correct the misspelling each time it occurred. Error correction in DWIM is divided into three categories: unbound atoms, undefined CAR of form, and undefined function in APPLY. Assuming that the user approves DWIM's corrections, the action taken by DWIM for the various types of errors in each of these categories is summarized below. DWIM Correction: Unbound Atoms If DWIM is called as the result of an unbound atom error, it proceeds as follows: 1. If the first character of the unbound atom is ', DWIM assumes that the user (intentionally) typed 'ATOM for (QUOTE ATOM) and makes the appropriate change. No message is typed, and no approval is requested. If the unbound atom is just ' itself, DWIM assumes the user wants the next expression quoted, e.g., (CONS X '(A B C)) will be changed to (CONS X (QUOTE (A B C))). Again no message will be printed or approval asked. If no expression follows the ', DWIM gives up. Note: ' is normally defined as a read-macro character which converts 'FOO to (QUOTE FOO) on input, so DWIM will not see the ' in the case of expressions that are typed-in. 2. If CLISP (see Chapter 21) is enabled, and the atom is part of a CLISP construct, the CLISP transformation is performed and the result returned. For example, N-1 is transformed to (SUB1 N), and (... FOO_3 ...) is transformed into (... (SETQ FOO 3) ...). 3. If the atom contains an 9 (actually LPARKEY, see the DWIM Functions and Variables section below), DWIM assumes the 9 was intended to be a left parenthesis, and calls the editor to make appropriate repairs on the expression containing the atom. DWIM assumes that the user did not notice the mistake, i.e., that the entire expression was affected by the missing left parenthesis. For example, if you type (SETQ X (LIST (CONS 9CAR Y) (CDR Z)) Y), the expression will be changed to (SETQ X (LIST (CONS (CAR Y) (CDR Z)) Y)). The 9 does not have to be the first character of the atom: DWIM will handle (CONS X9CAR Y) correctly. 4. If the atom contains a 0 (actually RPARKEY, see the DWIM Functions and Variables section below), DWIM assumes the 0 was intended to be a right parenthesis and operates as in the case above. 5. If the atom begins with a 7, the 7 is treated as a '. For example, 7FOO becomes 'FOO, and then (QUOTE FOO). 6. The expressions on DWIMUSERFORMS (see the DWIMUSERFORMS section below) are evaluated in the order that they appear. If any of these expressions returns a non-NIL value, this value is treated as the form to be used to continue the computation, it is evaluated and its value is returned by DWIM. 7. If the unbound atom occurs in a function, DWIM attempts spelling correction using the LAMBDA and PROG variables of the function as the spelling list. 8. If the unbound atom occurred in a type-in to a break, DWIM attempts spelling correction using the LAMBDA and PROG variables of the broken function as the spelling list. 9. Otherwise, DWIM attempts spelling correction using SPELLINGS3 (see the Spelling Lists section below). 10. If all of the above fail, DWIM gives up. Undefined CAR of Form If DWIM is called as the result of an undefined CAR of form error, it proceeds as follows: 1. If CAR of the form is T, DWIM assumes a misplaced T clause and operates as described in the Undefined Function T Errors section above. 2. If CAR of the form is F/L, DWIM changes the "F/L" to "FUNCTION(LAMBDA". For example, (F/L (Y) (PRINT (CAR Y))) is changed to (FUNCTION (LAMBDA (Y) (PRINT (CAR Y))). No message is printed and no approval requested. If the user omits the variable list, DWIM supplies (X), e.g., (F/L (PRINT (CAR X))) is changed to (FUNCTION (LAMBDA (X) (PRINT (CAR X)))). DWIM determines that the user has supplied the variable list when more than one expression follows F/L, CAR of the first expression is not the name of a function, and every element in the first expression is atomic. For example, DWIM will supply (X) when correcting (F/L (PRINT (CDR X)) (PRINT (CAR X))). 3. If CAR of the form is a CLISP word (IF, FOR, DO, FETCH, etc.), the indicated CLISP transformation is performed, and the result is returned as the corrected form. See Chapter 21. 4. If CAR of the form has a function definition, DWIM attempts spelling correction on CAR of the definition using as spelling list the value of LAMBDASPLST, initially (LAMBDA NLAMBDA). 5. If CAR of the form has an EXPR or CODE property, DWIM prints CAR-OF-FORM UNSAVED, performs an UNSAVEDEF, and continues. No approval is requested. 6. If CAR of the form has a FILEDEF property, the definition is loaded from a file (except when DWIMIFYing). If the value of the property is atomic, the entire file is to be loaded. If the value is a list, CAR is the name of the file and CDR the relevant functions, and LOADFNS will be used. For both cases, LDFLG will be SYSLOAD (see Chapter 17). DWIM uses FINDFILE (Chapter 24), so that the file can be on any of the directories on DIRECTORIES, initially (NIL NEWLISP LISP LISPUSERS). If the file is found, DWIM types SHALL I LOAD followed by the file name or list of functions. If the user approves, DWIM loads the function(s) or file, and continues the computation. 7. If CLISP is enabled, and CAR of the form is part of a CLISP construct, the indicated transformation is performed, e.g., (N_N-1) becomes (SETQ N (SUB1 N)). 8. If CAR of the form contains an 9, DWIM assumes a left parenthesis was intended e.g., (CONS9CAR X). 9. If CAR of the form contains a 0, DWIM assumes a right parenthesis was intended. 10. If CAR of the form is a list, DWIM attempts spelling correction on CAAR of the form using LAMBDASPLST as spelling list. If successful, DWIM returns the corrected expression itself. 11. The expressions on DWIMUSERFORMS are evaluated in the order they appear. If any returns a non-NIL value, this value is treated as the corrected form, it is evaluated, and DWIM returns its value. 12. Otherwise, DWIM attempts spelling correction using SPELLINGS2 as the spelling list (see the Spelling Lists section below). When DWIMIFYing, DWIM also attemps spelling correction on function names not defined but previously encountered, using NOFIXFNSLST as a spelling list (see Chapter 21). 13. If all of the above fail, DWIM gives up. Undefined Function in APPLY If DWIM is called as the result of an undefined function in APPLY error, it proceeds as follows: 1. If the function has a definition, DWIM attempts spelling correction on CAR of the definition using LAMBDASPLST as spelling list. 2. If the function has an EXPR or CODE property, DWIM prints FN UNSAVED, performs an UNSAVEDEF and continues. No approval is requested. 3. If the function has a property FILEDEF, DWIM proceeds as in case 6 of undefined CAR of form. 4. If the error resulted from type-in, and CLISP is enabled, and the function name contains a CLISP operator, DWIM performs the indicated transformation, e.g., type FOO_(APPEND FIE FUM). 5. If the function name contains an 9, DWIM assumes a left parenthesis was intended, e.g., EDIT9FOO]. 6. If the "function" is a list, DWIM attempts spelling correction on CAR of the list using LAMBDASPLST as spelling list. 7. The expressions on DWIMUSERFORMS are evaluated in the order they appear, and if any returns a non-NIL value, this value is treated as the function used to continue the computation, i.e., it will be applied to its arguments. 8. DWIM attempts spelling correction using SPELLINGS1 as the spelling list. 9. DWIM attempts spelling correction using SPELLINGS2 as the spelling list. 10. If all fail, DWIM gives up. DWIMUSERFORMS 1 The variable DWIMUSERFORMS provides a convenient way of adding to the transformations that DWIM performs. For example, the user might want to change atoms of the form $X to (QA4LOOKUP X). Before attempting spelling correction, but after performing other transformations (F/L, 9, 0, CLISP, etc.), DWIM evaluates the expressions on DWIMUSERFORMS in the order they appear. If any expression returns a non-NIL value, this value is treated as the transformed form to be used. If DWIM was called from FAULTEVAL, this form is evaluated and the resulting value is returned as the value of FAULTEVAL. If DWIM is called from FAULTAPPLY, this form is treated as a function to be applied to FAULTARGS, and the resulting value is returned as the value of FAULTAPPLY. If all of the expressions on DWIMUSERFORMS return NIL, DWIM proceeds as though DWIMUSERFORMS=NIL, and attempts spelling correction. Note that DWIM simply takes the value and returns it; the expressions on DWIMUSERFORMS are responsible for making any modifications to the original expression. The expressions on DWIMUSERFORMS should make the transformation permanent, either by associating it with FAULTX via CLISPTRAN, or by destructively changing FAULTX. In order for an expression on DWIMUSERFORMS to be able to be effective, it needs to know various things about the context of the error. Therefore, several of DWIM's internal variables have been made SPECVARS (see Chapter 18) and are therefore "visible" to DWIMUSERFORMS. Below are a list of those variables that may be useful. FAULTX [Variable] For unbound atom and undefined car of form errors, FAULTX is the atom or form. For undefined function in APPLY errors, FAULTX is the name of the function. FAULTARGS [Variable] For undefined function in APPLY errors, FAULTARGS is the list of arguments. FAULTARGS may be modified or reset by expressions on DWIMUSERFORMS. FAULTAPPLYFLG [Variable] Value is T for undefined function in APPLY errors; NIL otherwise. The value of FAULTAPPLYFLG after an expression on DWIMUSERFORMS returns a non-NIL value determines how the latter value is to be treated. Following an undefined function in APPLY error, if an expression on DWIMUSERFORMS sets FAULTAPPLYFLG to NIL, the value returned is treated as a form to be evaluated, rather than a function to be applied. FAULTAPPLYFLG is necessary to distinguish between unbound atom and undefined function in APPLY errors, since FAULTARGS may be NIL and FAULTX atomic in both cases. TAIL [Variable] For unbound atom errors, TAIL is the tail of the expression CAR of which is the unbound atom. DWIMUSERFORMS expression can replace the atom by another expression by performing (/RPLACA TAIL EXPR) PARENT [Variable] For unbound atom errors, PARENT is the form in which the unbound atom appears. TAIL is a tail of PARENT. TYPE-IN? [Variable] True if the error occurred in type-in. FAULTFN [Variable] Name of the function in which error occurred. FAULTFN is TYPE-IN when the error occurred in type-in, and EVAL or APPLY when the error occurred under an explicit call to EVAL or APPLY. DWIMIFYFLG [Variable] True if the error was encountered while DWIMIFYing (as opposed to happening while running a program). EXPR [Variable] Definition of FAULTFN, or argument to EVAL, i.e., the superform in which the error occurs. The initial value of DWIMUSERFORMS is ((DWIMLOADFNS?)). DWIMLOADFNS? is a function for automatically loading functions from files. If DWIMLOADFNSFLG is T (its initial value), and CAR of the form is the name of a function, and the function is contained on a file that has been noticed by the file package, the function is loaded, and the computation continues. DWIM Functions and Variables 1 DWIMWAIT [Variable] Value is the number of seconds that DWIM will wait before it assumes that the user is not going to respond to a question and uses the default response FIXSPELLDEFAULT. DWIM operates by dismissing for 250 milliseconds, then checking to see if anything has been typed. If not, it dismisses again, etc. until DWIMWAIT seconds have elapsed. Thus, there will be a delay of at most 1/4 second before DWIM responds to the user's answer. FIXSPELLDEFAULT [Variable] If approval is requested for a spelling correction, and user does not respond, defaults to value of FIXSPELLDEFAULT, initially Y. FIXSPELLDEFAULT is rebound to N when DWIMIFYing. ADDSPELLFLG [Variable] If NIL, suppresses calls to ADDSPELL. Initially T. NOSPELLFLG [Variable] If T, suppresses all spelling correction. If some other non-NIL value, suppresses spelling correction in programs but not type-in. NOSPELLFLG is initially NIL. It is rebound to T when compiling from a file. RUNONFLG [Variable] If NIL, suppresses run-on spelling corrections. Initially NIL. DWIMLOADFNSFLG [Variable] If T, tells DWIM that when it encounters a call to an undefined function contained on a file that has been noticed by the file package, to simply load the function. DWIMLOADFNSFLG is initially T (see above). LPARKEY [Variable] RPARKEY [Variable] DWIM uses the value of the variables LPARKEY and RPARKEY (initially 9 and 0 respectively) to determine the corresponding lower case character for left and right parentheses. LPARKEY and RPARKEY can be reset for other keyboard layouts. For example, on some terminals left parenthesis is over 8, and right parenthesis is over 9. OKREEVALST [Variable] The value of OKREEVALST is a list of functions that DWIM can safely reevaluate. If a form is atomic, or CAR of the form is a member of OKREEVALST, and each of the arguments can safely be reevaluated, then the form can be safely reevaluated. For example, (SETQ X (CONS (IPLUS Y Z) W)) is safe to reevaluate because SETQ, CONS, and IPLUS are all on OKREEVALST. DWIMFLG [Variable] DWIMFLG=NIL, all DWIM operations are disabled. (DWIM 'C) and (DWIM T) set DWIMFLG to T; (DWIM NIL) sets DWIMFLG to NIL. APPROVEFLG [Variable] APPROVEFLG=T if DWIM should ask the user for approval before making a correction that will modify the definition of one of his functions; NIL otherwise. When DWIM is put into CAUTIOUS mode with (DWIM 'C), APPROVEFLG is set to T; for TRUSTING mode, APPROVEFLG is set to NIL. LAMBDASPLST [Variable] DWIM uses the value of LAMBDASPLST as the spelling list when correcting "bad" function definitions. Initially (LAMBDA NLAMBDA). The user may wish to add to LAMBDASPLST if he elects to define new "function types" via an appropriate DWIMUSERFORMS entry. For example, the QLAMBDAs of SRI's QLISP are handled in this way. Spelling Correction 1 The spelling corrector is given as arguments a misspelled word (word means literal atom), a spelling list (a list of words), and a number: XWORD, SPLST, and REL respectively. Its task is to find that word on SPLST which is closest to XWORD, in the sense described below. This word is called a respelling of XWORD. REL specifies the minimum "closeness" between XWORD and a respelling. If the spelling corrector cannot find a word on SPLST closer to XWORD than REL, or if it finds two or more words equally close, its value is NIL, otherwise its value is the respelling. The spelling corrector can also be given an optional functional argument, FN, to be used for selecting out a subset of SPLST, i.e., only those members of SPLST that satisfy FN will be considered as possible respellings. The exact algorithm for computing the spelling metric is described later, but briefly "closeness" is inversely proportional to the number of disagreements between the two words, and directly proportional to the length of the longer word. For example, PRTTYPRNT is "closer" to PRETTYPRINT than CS is to CONS even though both pairs of words have the same number of disagreements. The spelling corrector operates by proceeding down SPLST, and computing the closeness between each word and XWORD, and keeping a list of those that are closest. Certain differences between words are not counted as disagreements, for example a single transposition, e.g., CONS to CNOS, or a doubled letter, e.g., CONS to CONSS, etc. In the event that the spelling corrector finds a word on SPLST with no disagreements, it will stop searching and return this word as the respelling. Otherwise, the spelling corrector continues through the entire spelling list. Then if it has found one and only one "closest" word, it returns this word as the respelling. For example, if XWORD is VONS, the spelling corrector will probably return CONS as the respelling. However, if XWORD is CONZ, the spelling corrector will not be able to return a respelling, since CONZ is equally close to both CONS and COND. If the spelling corrector finds an acceptable respelling, it interacts with the user as described earlier. In the special case that the misspelled word contains one or more $s (escape), the spelling corrector searches for those words on SPLST that match XWORD, where a $ can match any number of characters (including 0), e.g., FOO$ matches FOO1 and FOO, but not NEWFOO. $FOO$ matches all three. Both completion and correction may be involved, e.g. RPETTY$ will match PRETTYPRINT, with one mistake. The entire spelling list is always searched, and if more than one respelling is found, the spelling corrector prints AMBIGUOUS, and returns NIL. For example, CON$ would be ambiguous if both CONS and COND were on the spelling list. If the spelling corrector finds one and only one respelling, it interacts with the user as described earlier. For both spelling correction and spelling completion, regardless of whether or not the user approves of the spelling corrector's choice, the respelling is moved to the front of SPLST. Since many respellings are of the type with no disagreements, this procedure has the effect of considerably reducing the time required to correct the spelling of frequently misspelled words. Synonyms Spelling lists also provide a way of defining synonyms for a particular context. If a dotted pair appears on a spelling list (instead of just an atom), CAR is interpreted as the correct spelling of the misspelled word, and CDR as the antecedent for that word. If CAR is identical with the misspelled word, the antecedent is returned without any interaction or approval being necessary. If the misspelled word corrects to CAR of the dotted pair, the usual interaction and approval will take place, and then the antecedent, i.e., CDR of the dotted pair, is returned. For example, the user could make IFLG synonymous with CLISPIFTRANFLG by adding (IFLG . CLISPIFTRANFLG) to SPELLINGS3, the spelling list for unbound atoms. Similarly, the user could make OTHERWISE mean the same as ELSEIF by adding (OTHERWISE . ELSEIF) to CLISPIFWORDSPLST, or make L be synonymous with LAMBDA by adding (L . LAMBDA) to LAMBDASPLST. You can also use L as a variable without confusion, since the association of L with LAMBDA occurs only in the appropriate context. Spelling Lists Any list of atoms can be used as a spelling list, e.g., BROKENFNS, FILELST, etc. Various system packages have their own spellings lists, e.g., LISPXCOMS, CLISPFORWORDSPLST, EDITCOMSA, etc. These are documented under their corresponding sections, and are also indexed under "spelling lists." In addition to these spelling lists, the system maintains, i.e., automatically adds to, and occasionally prunes, four lists used solely for spelling correction: SPELLINGS1, SPELLINGS2, SPELLINGS3, and USERWORDS. These spelling lists are maintained only when ADDSPELLFLG is non-NIL. ADDSPELLFLG is initially T. SPELLINGS1 [Variable] SPELLINGS1 is a list of functions used for spelling correction when an input is typed in apply format, and the function is undefined, e.g., EDTIF(FOO). SPELLINGS1 is initialized to contain DEFINEQ, BREAK, MAKEFILE, EDITF, TCOMPL, LOAD, etc. Whenever LISPX is given an input in apply format, i.e., a function and arguments, the name of the function is added to SPELLINGS1 if the function has a definition. For example, typing CALLS(EDITF) will cause CALLS to be added to SPELLINGS1. Thus if the user typed CALLS(EDITF) and later typed CALLLS(EDITV), since SPELLINGS1 would then contain CALLS, DWIM would be successful in correcting CALLLS to CALLS. SPELLINGS2 [Variable] SPELLINGS2 is a list of functions used for spelling correction for all other undefined functions. It is initialized to contain functions such as ADD1, APPEND, COND, CONS, GO, LIST, NCONC, PRINT, PROG, RETURN, SETQ, etc. Whenever LISPX is given a non-atomic form, the name of the function is added to SPELLINGS2. For example, typing (RETFROM (STKPOS (QUOTE FOO) 2)) to a break would add RETFROM to SPELLINGS2. Function names are also added to SPELLINGS2 by DEFINE, DEFINEQ, LOAD (when loading compiled code), UNSAVEDEF, EDITF, and PRETTYPRINT. SPELLINGS3 [Variable] SPELLINGS3 is a list of words used for spelling correction on all unbound atoms. SPELLINGS3 is initialized to EDITMACROS, BREAKMACROS, BROKENFNS, and ADVISEDFNS. Whenever LISPX is given an atom to evaluate, the name of the atom is added to SPELLINGS3 if the atom has a value. Atoms are also added to SPELLINGS3 whenever they are edited by EDITV, and whenever they are set via RPAQ or RPAQQ. For example, when a file is loaded, all of the variables set in the file are added to SPELLINGS3. Atoms are also added to SPELLINGS3 when they are set by a LISPX input, e.g., typing (SETQ FOO (REVERSE (SETQ FIE ...))) will add both FOO and FIE to SPELLINGS3. USERWORDS [Variable] USERWORDS is a list containing both functions and variables that the user has referred to, e.g., by breaking or editing. USERWORDS is used for spelling correction by ARGLIST, UNSAVEDEF, PRETTYPRINT, BREAK, EDITF, ADVISE, etc. USERWORDS is initially NIL. Function names are added to it by DEFINE, DEFINEQ, LOAD, (when loading compiled code, or loading exprs to property lists) UNSAVEDEF, EDITF, EDITV, EDITP, PRETTYPRINT, etc. Variable names are added to USERWORDS at the same time as they are added to SPELLINGS3. In addition, the variable LASTWORD is always set to the last word added to USERWORDS, i.e., the last function or variable referred to by the user, and the respelling of NIL is defined to be the value of LASTWORD. Thus, if the user has just defined a function, he can then prettyprint it by typing PP(). Each of the above four spelling lists are divided into two sections separated by a special marker (the value of the variable SPELLSTR1). The first section contains the "permanent" words; the second section contains the temporary words. New words are added to the corresponding spelling list at the front of its temporary section (except that functions added to SPELLINGS1 or SPELLINGS2 by LISPX are always added to the end of the permanent section. If the word is already in the temporary section, it is moved to the front of that section; if the word is in the permanent section, no action is taken. If the length of the temporary section then exceeds a specified number, the last (oldest) word in the temporary section is forgotten, i.e., deleted. This procedure prevents the spelling lists from becoming cluttered with unimportant words that are no longer being used, and thereby slowing down spelling correction time. Since the spelling corrector usually moves each word selected as a respelling to the front of its spelling list, the word is thereby moved into the permanent section. Thus once a word is misspelled and corrected, it is considered important and will never be forgotten. Note: The spelling correction algorithm will not alter a spelling list unless it contains the special marker (the value of SPELLSTR1). This provides a way to ensure that a spelling list will not be altered. #SPELLINGS1 [Variable] #SPELLINGS2 [Variable] #SPELLINGS3 [Variable] #USERWORDS [Variable] The maximum length of the temporary section for SPELLINGS1, SPELLINGS2, SPELLINGS3 and USERWORDS is given by the value of #SPELLINGS1, #SPELLINGS2, #SPELLINGS3, and #USERWORDS, initialized to 30, 30, 30, and 60 respectively. You can alter these values to modify the performance behavior of spelling correction. Generators for Spelling Correction For some applications, it is more convenient to generate candidates for a respelling one by one, rather than construct a complete list of all possible candidates, e.g., spelling correction involving a large directory of files, or a natural language data base. For these purposes, SPLST can be an array (of any size). The first element of this array is the generator function, which is called with the array itself as its argument. Thus the function can use the remainder of the array to store "state" information, e.g., the last position on a file, a pointer into a data structure, etc. The value returned by the function is the next candidate for respelling. If NIL is returned, the spelling "list" is considered to be exhausted, and the closest match is returned. If a candidate is found with no disagreements, it is returned immediately without waiting for the "list" to exhaust. SPLST can also be a generator, i.e. the value of the function GENERATOR (Chapter 11). The generator SPLST will be started up whenever the spelling corrector needs the next candidate, and it should return candidates via the function PRODUCE. For example, the following could be used as a "spelling list" which effectively contains all functions in the system: [GENERATOR (MAPATOMS (FUNCTION (LAMBDA (X) (if (GETD X) then (PRODUCE X] Spelling Corrector Algorithm The basic philosophy of DWIM spelling correction is to count the number of disagreements between two words, and use this number divided by the length of the longer of the two words as a measure of their relative disagreement. One minus this number is then the relative agreement or closeness. For example, CONS and CONX differ only in their last character. Such substitution errors count as one disagreement, so that the two words are in 75% agreement. Most calls to the spelling corrector specify a relative agreement of 70, so that a single substitution error is permitted in words of four characters or longer. However, spelling correction on shorter words is possible since certain types of differences such as single transpositions are not counted as disagreements. For example, AND and NAD have a relative agreement of 100. Calls to the spelling corrector from DWIM use the value of FIXSPELLREL, which is initially 70. Note that by setting FIXSPELLREL to 100, only spelling corrections with "zero" mistakes, will be considered, e.g., transpositions, double characters, etc. The central function of the spelling corrector is CHOOZ. CHOOZ takes as arguments: a word, a minimum relative agreement, a spelling list, and an optional functional argument, XWORD, REL, SPLST, and FN respectively. CHOOZ proceeds down SPLST examining each word. Words not satisfying FN (if FN is non-NIL), or those obviously too long or too short to be sufficiently close to XWORD are immediately rejected. For example, if REL=70, and XWORD is 5 characters long, words longer than 7 characters will be rejected. Special treatment is necessary for words shorter than XWORD, since doubled letters are not counted as disagreements. For example, CONNSSS and CONS have a relative agreement of 100. CHOOZ handles this by counting the number of doubled characters in XWORD before it begins scanning SPLST, and taking this into account when deciding whether to reject shorter words. If TWORD, the current word on SPLST, is not rejected, CHOOZ computes the number of disagreements between it and XWORD by calling a subfunction, SKOR. SKOR operates by scanning both words from left to right one character at a time. SKOR operates on the list of character codes for each word. This list is computed by CHOOZ before calling SKOR. Characters are considered to agree if they are the same characters or appear on the same key (i.e., a shift mistake). The variable SPELLCASEARRAY is a CASEARRAY which is used to determine equivalence classes for this purpose. It is initialized to equivalence lowercase and upper case letters, as well as the standard key transitions: for example, 1 with !, 3 with #, etc. If the first character in XWORD and TWORD do not agree, SKOR checks to see if either character is the same as one previously encountered, and not accounted-for at that time. (In other words, transpositions are not handled by lookahead, but by lookback.) A displacement of two or fewer positions is counted as a tranposition; a displacement by more than two positions is counted as a disagreement.In either case, both characters are now considered as accounted for and are discarded, and SKORing continues. If the first character in XWORD and TWORD do not agree, and neither agree with previously unaccounted-for characters, and TWORD has more characters remaining than XWORD, SKOR removes and saves the first character of TWORD, and continues by comparing the rest of TWORD with XWORD as described above. If TWORD has the same or fewer characters remaining than XWORD, the procedure is the same except that the character is removed from XWORD. In this case, a special check is first made to see if that character is equal to the previous character in XWORD, or to the next character in XWORD, i.e., a double character typo, and if so, the character is considered accounted-for, and not counted as a disagreement. In this case, the "length" of XWORD is also decremented. Otherwise making XWORD sufficiently long by adding double characters would make it be arbitrarily close to TWORD, e.g., XXXXXX would correct to PP. When SKOR has finished processing both XWORD and TWORD in this fashion, the value of SKOR is the number of unaccounted-for characters, plus the number of disagreements, plus the number of tranpositions, with two qualifications: (1) if both XWORD and TWORD have a character unaccounted-for in the same position, the two characters are counted only once, i.e., substitution errors count as only one disagreement, not two; and (2) if there are no unaccounted-for characters and no disagreements, transpositions are not counted. This permits spelling correction on very short words, such as edit commands, e.g., XRT->XTR. Transpositions are also not counted when FASTYPEFLG=T, for example, IPULX and IPLUS will be in 80% agreement with FASTYPEFLG=T, only 60% with FASTYPEFLG=NIL. The rationale behind this is that transpositions are much more common for fast typists, and should not be counted as disagreements, whereas more deliberate typists are not as likely to combine tranpositions and other mistakes in a single word, and therefore can use more conservative metric. FASTYPEFLG is initially NIL. Spelling Corrector Functions and Variables (ADDSPELL X SPLST N) [Function] Adds X to one of the spelling lists as determined by the value of SPLST: NIL Adds X to USERWORDS and to SPELLINGS2. Used by DEFINEQ. 0 Adds X to USERWORDS. Used by LOAD when loading EXPRs to property lists. 1 Adds X to SPELLINGS1 (at end of permanent section). Used by LISPX. 2 Adds X to SPELLINGS2 (at end of permanent section). Used by LISPX. 3 Adds X to USERWORDS and SPELLINGS3. a spelling list If SPLST is a spelling list, X is added to it. In this case, N is the (optional) length of the temporary section. If X is already on the spelling list, and in its temporary section, ADDSPELL moves X to the front of that section. ADDSPELL sets LASTWORD to X when SPLST=NIL, 0 or 3. If X is not a literal atom, ADDSPELL takes no action. Note that the various systems calls to ADDSPELL, e.g. from DEFINE, EDITF, LOAD, etc., can all be suppressed by setting or binding ADDSPELLFLG to NIL (see the DWIM Functions and Variables section above). (MISSPELLED? XWORD REL SPLST FLG TAIL FN) [Function] If XWORD=NIL or $ (), MISSPELLED? prints = followed by the value of LASTWORD, and returns this as the respelling, without asking for approval. Otherwise, MISSPELLED? checks to see if XWORD is really misspelled, i.e., if FN applied to XWORD is true, or XWORD is already contained on SPLST. In this case, MISSPELLED? simply returns XWORD. Otherwise MISSPELLED? computes and returns (FIXSPELL XWORD REL SPLST FLG TAIL FN). (FIXSPELL XWORD REL SPLST FLG TAIL FN TIEFLG DONTMOVETOPFLG % %) [Function] The value of FIXSPELL is either the respelling of XWORD or NIL. If for some reason XWORD itself is on SPLST, then FIXSPELL aborts and calls ERROR!. If there is a possibility that XWORD is spelled correctly, MISSPELLED? should be used instead of FIXSPELL. FIXSPELL performs all of the interactions described earlier, including requesting user approval if necessary. If XWORD=NIL or $ (escape), the respelling is the value of LASTWORD, and no approval is requested. If XWORD contains lowercase characters, and the corresponding uppercase word is correct, i.e. on SPLST or satisfies FN, the uppercase word is returned and no interaction is performed. If FIXSPELL.UPPERCASE.QUIET is NIL (the default), a warning "=XX" is printed when coercing from "xx" to "XX". If FIXSPELL.UPPERCASE.QUIET is non-NIL, no warning is given. If REL=NIL, defaults to the value of FIXSPELLREL (initially 70). If FLG=NIL, the correction is handled in type-in mode, i.e., approval is never requested, and XWORD is not typed. If FLG=T, XWORD is typed (before the =) and approval is requested if APPROVEFLG=T. If FLG=NO-MESSAGE, the correction is returned with no further processing. In this case, a run-on correction will be returned as a dotted pair of the two parts of the word, and a synonym correction as a list of the form (WORD1 WORD2), where WORD1 is (the corrected version of) XWORD, and WORD2 is the synonym. The effect of the function CHOOZ can be obtained by calling FIXSPELL with FLG=NO-MESSAGE. If TAIL is not NIL, and the correction is successful, CAR of TAIL is replaced by the respelling (using /RPLACA). FIXSPELL will attempt to correct misspellings caused by running two words together, if the global variable RUNONFLG is non-NIL (default is NIL). In this case, approval is always requested. When a run-on error is corrected, CAR of TAIL is replaced by the two words, and the value of FIXSPELL is the first one. For example, if FIXSPELL is called to correct the edit command (MOVE TO AFTERCOND 3 2) with TAIL=(AFTERCOND 3 2), TAIL would be changed to (AFTER COND 2 3), and FIXSPELL would return AFTER (subject to user approval where necessary). If TAIL=T, FIXSPELL will also perform run-on corrections, returning a dotted pair of the two words in the event the correction is of this type. If TIEFLG=NIL and a tie occurs, i.e., more than one word on SPLST is found with the same degree of "closeness", FIXSPELL returns NIL, i.e., no correction. If TIEFLG=PICKONE and a tie occurs, the first word is taken as the correct spelling. If TIEFLG=LIST, the value of FIXSPELL is a list of the respellings (even if there is only one), and FIXSPELL will not perform any interaction with the user, nor modify TAIL, the idea being that the calling program will handle those tasks. Similarly, if TIEFLG=EVERYTHING, a list of all candidates whose degree of closeness is above REL will be returned, regardless of whether some are better than others. No interaction will be performed. If DONTMOVETOPFLG=T and a correction occurs, it will not be moved to the front of the spelling list. Also, the spelling list will not be altered unless it contains the special marker used to separate the temporary and perminant parts of the system spelling lists (the value of SPELLSTR1). (FNCHECK FN NOERRORFLG SPELLFLG PROPFLG TAIL) [Function] The task of FNCHECK is to check whether FN is the name of a function and if not, to correct its spelling. If FN is the name of a function or spelling correction is successful, FNCHECK adds the (corrected) name of the function to USERWORDS using ADDSPELL, and returns it as its value. Since FNCHECK is called by many low level functions such as ARGLIST, UNSAVEDEF, etc., spelling correction only takes place when DWIMFLG=T, so that these functions can operate in a small Interlisp system which does not contain DWIM. NOERRORFLG informs FNCHECK whether or not the calling function wants to handle the unsuccessful case: if NOERRORFLG is T, FNCHECK simply returns NIL, otherwise it prints fn NOT A FUNCTION and generates a non-breaking error. If FN does not have a definition, but does have an EXPR property, then spelling correction is not attempted. Instead, if PROPFLG=T, FN is considered to be the name of a function, and is returned. If PROPFLG=NIL, FN is not considered to be the name of a function, and NIL is returned or an error generated, depending on the value of NOERRORFLG. FNCHECK calls MISSPELLED? to perform spelling correction, so that if FN=NIL, the value of LASTWORD will be returned. SPELLFLG corresponds to MISSPELLED?'s fourth argument, FLG. If SPELLFLG=T, approval will be asked if DWIM was enabled in CAUTIOUS mode, i.e., if APPROVEFLG=T. TAIL corresponds to the fifth argument to MISSPELLED?. FNCHECK is currently used by ARGLIST, UNSAVEDEF, PRETTYPRINT, BREAK0, BREAKIN, ADVISE, and CALLS. For example, BREAK0 calls FNCHECK with NOERRORFLG=T since if FNCHECK cannot produce a function, BREAK0 wants to define a dummy one. CALLS however calls FNCHECK with NOERRORFLG=NIL, since it cannot operate without a function. Many other system functions call MISSPELLED? or FIXSPELL directly. For example, BREAK1 calls FIXSPELL on unrecognized atomic inputs before attempting to evaluate them, using as a spelling list a list of all break commands. Similarly, LISPX calls FIXSPELL on atomic inputs using a list of all LISPX commands. When UNBREAK is given the name of a function that is not broken, it calls FIXSPELL with two different spelling lists, first with BROKENFNS, and if that fails, with USERWORDS. MAKEFILE calls MISSPELLED? using FILELST as a spelling list. Finally, LOAD, BCOMPL, BRECOMPILE, TCOMPL, and RECOMPILE all call MISSPELLED? if their input file(s) won't open. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))"0HH +T6T3HZ +ZT2HZ +Z5.llTT,ll,ll,ll5/HH +/ll3llT3llT2HHZ2HHZ2HZZ,ll,ll3HZT3HZ +T3$$(T,HH +3(T,ll,HH,HH,2F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR-T  HELVETICA CLASSIC + HELVETICATITAN +CLASSIC +CLASSIC + HELVETICA HELVETICA +CLASSIC +MODERN +MODERNMODERN +! HRULE.GETFNCLASSIC + !   HRULE.GETFNCLASSIC +     HRULE.GETFNCLASSIC +    HRULE.GETFNCLASSIC +    HRULE.GETFNMODERN +  N =    +      ]  h 2    +&  2 @ + o + '     '   , c ) P    "  + #  ( F  < Z  9    %#!=  +  [ +s 5      !   @   =  =     4 X 5  m  " HRULE.GETFNMODERN + - A 8  9    X  h " =   $   $ q M  , < ! P %     r 9  G % G  m  " HRULE.GETFNMODERN + V  0    +9   +9   +~  & = /  V    r   " HRULE.GETFNMODERN + G H J   6   *   4  2 4 9   %     } ? k  >    ) .   2 >  7 1  -  (  % +E     +  Q " HRULE.GETFNMODERN + Y F  ! +#  + %  +- # D  J :  +R    4    =   T $   R 1 3  W  )   U   >  $ /        H ! '$ ( H    H K            Y  1 e  8 6 +) -  0 (    T       &g   'f    %      }  M 7        ,   = i      D  $   \     5    1  =  Q  ? J  7 +D  b & .  <  J       + " *  +   $ 6  E    B { + + + +   " HRULE.GETFNMODERN +    V   2 < [ M  +6 6 +    n ^ I      1 ;   3 1          ,            ]    a  L           E     1      &    /  )  3     +  ( 6      0     C    " HRULE.GETFNMODERN +     t    d                +    ) E +       5           +    %    d  c     +  +R  +n     +     %     +     +  + ~      +   +       M  @  )  " HRULE.GETFNMODERN +    1  7 +  + D   ? s +   -     | 4     A    . !  H   n B ?   + 9    +  J            D &    h D    +F     +     ;  )  8  F    + + + &       +  + + +       i +#    +   + )     +  +            B +   +# +         +  +G + +   + @ +2 +   X + +  #   +      E # $        $  C     # ' + ) U  V  }  + +  | M        + 0 + + +     +2 V  " 0  ~  9   x  A3    H  . { 2  q      +   H +  H 6 G  # >  N    5   M Q                 Q $  * )   1 F X     ' U          c + +    +  + ( +  *   +    <   +    +   +       +   +)   +   +)   +    +    4  + @                '    4  7         +   N             F'          "    f     *    Y  F  P           T      +   +    -    +    #  '   c   S  0 $ '       0   }    / /    I   ? < R  +> i    "    $   D A .     /  2  _  + O +    %   . C   B    . > +   ,        0  +  &             +  +     + .  !      &   4 /        +   + $ v +&z \ No newline at end of file diff --git a/docs/porter-irm/20-SYSFUNS.TEDIT b/docs/porter-irm/20-SYSFUNS.TEDIT new file mode 100644 index 00000000..3db359a6 Binary files /dev/null and b/docs/porter-irm/20-SYSFUNS.TEDIT differ diff --git a/docs/porter-irm/21-CLISP.TEDIT b/docs/porter-irm/21-CLISP.TEDIT new file mode 100644 index 00000000..b85498c3 --- /dev/null +++ b/docs/porter-irm/21-CLISP.TEDIT @@ -0,0 +1,119 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 21. CLISP 1 21. CLISP 1 21. CLISP 6 The syntax of Lisp is very simple, in the sense that it can be described concisely, but not in the sense that Lisp programs are easy to read or write! This simplicity of syntax is achieved by, and at the expense of, extensive use of explicit structuring, namely grouping through parenthesization. Unlike many languages, there are no reserved words in Lisp such as IF, THEN, FOR, DO, etc., nor reserved characters like +, -, =, , etc. The only special characters are left and right parentheses and period, which are used for indicating structure, and space and end-of-line, which are used for delimiting identifiers. This eliminates entirely the need for parsers and precedence rules in the Lisp interpreter and compiler, and thereby makes program manipulation of Lisp programs straightforward. In other words, a program that "looks at" other Lisp programs does not need to incorporate a lot of syntactic information. For example, a Lisp interpreter can be written in one or two pages of Lisp code. It is for this reason that Lisp is by far the most suitable, and frequently used, programming language for writing programs that deal with other programs as data, e.g., programs that analyze, modify, or construct other programs. However, it is precisely this same simplicity of syntax that makes Lisp programs difficult to read and write (especially for beginners). 'Pushing down' is something programs do very well, and people do poorly. As an example, consider the following two "equivalent" sentences: "The rat that the cat that the dog that I owned chased caught ate the cheese." versus "I own the dog that chased the cat that caught the rat that ate the cheese." Natural language contains many linguistic devices such as that illustrated in the second sentence above for minimizing embedding, because embedded sentences are more difficult to grasp and understand than equivalent non-embedded ones (even if the latter sentences are somewhat longer). Similarly, most high level programming languages offer syntactic devices for reducing apparent depth and complexity of a program: the reserved words and infix operators used in ALGOL-like languages simultaneously delimit operands and operations, and also convey meaning to the programmer. They are far more intuitive than parentheses. In fact, since Lisp uses parentheses (i.e., lists) for almost all syntactic forms, there is very little information contained in the parentheses for the person reading a Lisp program, and so the parentheses tend mostly to be ignored: the meaning of a particular Lisp expression for people is found almost entirely in the words, not in the structure. For example, the expression (COND (EQ N 0) 1) (T TIMES N FACTORIAL ((SUB1 N))) is recognizable as factorial even though there are five misplaced or missing parentheses. Grouping words together in parentheses is done more for Lisp's benefit, than for the programmer's. CLISP is designed to make Interlisp programs easier to read and write by permitting the user to employ various infix operators, IF statements (Chapter 9), and iterative statements (Chapter 9), which are automatically converted to equivalent Interlisp expressions when they are first interpreted. For example, factorial could be written in CLISP: (IF N=0 THEN 1 ELSE N*(FACTORIAL N-1)) Note that this expression would become an equivalent COND after it had been interpreted once, so that programs that might have to analyze or otherwise process this expression could take advantage of the simple syntax. There have been similar efforts in other Lisp systems. CLISP differs from these in that it does not attempt to replace the Lisp syntax so much as to augment it. In fact, one of the principal criteria in the design of CLISP was that users be able to freely intermix Lisp and CLISP without having to identify which is which. You can write programs, or type in expressions for evaluation, in Lisp, CLISP, or a mixture of both. In this way, you do not have to learn a whole new language and syntax to be able to use selected facilities of CLISP when and where they find them useful. CLISP is implemented via the error correction machinery in Interlisp (see Chapter 20). Thus, any expression that is well-formed from Interlisp's standpoint will never be seen by CLISP (i.e., if you defined a function IF, he would effectively turn off that part of CLISP). This means that interpreted programs that do not use CLISP constructs do not pay for its availability by slower execution time. In fact, the Interlisp interpreter does not "know" about CLISP at all. It operates as before, and when an erroneous form is encountered, the interpreter calls an error routine which in turn invokes the Do-What-I-Mean (DWIM) analyzer which contains CLISP. If the expression in question turns out to be a CLISP construct, the equivalent Interlisp form is returned to the interpreter. In addition, the original CLISP expression, is modified so that it becomes the correctly translated Interlisp form. In this way, the analysis and translation are done only once. Integrating CLISP into the Interlisp system (instead of, for example, implementing it as a separate preprocessor) makes possible Do-What-I-Mean features for CLISP constructs as well as for pure Lisp expressions. For example, if the user has defined a function named GET-PARENT, CLISP would know not to attempt to interpret the form (GET-PARENT) as an arithmetic infix operation. (Actually, CLISP would never get to see this form, since it does not contain any errors.) If the user mistakenly writes (GET-PRAENT), CLISP would know he meant (GET-PARENT), and not (DIFFERENCE GET PRAENT), by using the information that PARENT is not the name of a variable, and that GET-PARENT is the name of a user function whose spelling is "very close" to that of GET-PRAENT. Similarly, by using information about the program's environment not readily available to a preprocessor, CLISP can successfully resolve the following sorts of ambiguities: 1. (LIST X*FACT N), where FACT is the name of a variable, means (LIST (X*FACT) N). 2. (LIST X*FACT N), where FACT is not the name of a variable but instead is the name of a function, means (LIST X*(FACT N)), i.e., N is FACT's argument. 3. (LIST X*FACT(N)), FACT the name of a function (and not the name of a variable), means (LIST X*(FACT N)). 4. Cases 1, 2 and 3 with FACT misspelled! The first expression is correct both from the standpoint of CLISP syntax and semantics and the change would be made without the user being notified. In the other cases, the user would be informed or consulted about what was taking place. For example, to take an extreme case, suppose the expression (LIST X*FCCT N) were encountered, where there was both a function named FACT and a variable named FCT. The user would first be asked if FCCT were a misspelling of FCT. If he said YES, the expression would be interpreted as (LIST (X*FCT) N). If he said NO, the user would be asked if FCCT were a misspelling of FACT, i.e., if he intended X*FCCT N to mean X*(FACT N). If he said YES to this question, the indicated transformation would be performed. If he said NO, the system would then ask if X*FCCT should be treated as CLISP, since FCCT is not the name of a (bound) variable. If he said YES, the expression would be transformed, if NO, it would be left alone, i.e., as (LIST X*FCCT N). Note that we have not even considered the case where X*FCCT is itself a misspelling of a variable name, e.g., a variable named XFCT (as with GET-PRAENT). This sort of transformation would be considered after the user said NO to X*FCCT N -> X*(FACT N). The question of whether X*FCCT should be treated as CLISP is important because Interlisp users may have programs that employ identifiers containing CLISP operators. Thus, if CLISP encounters the expression A/B in a context where either A or B are not the names of variables, it will ask the user if A/B is intended to be CLISP, in case the user really does have a free variable named A/B. Note: Through the discussion above, we speak of CLISP or DWIM asking the user. Actually, if the expression in question was typed in by the user for immediate execution, the user is simply informed of the transformation, on the grounds that the user would prefer an occasional misinterpretation rather than being continuously bothered, especially since he can always retype what he intended if a mistake occurs, and ask the programmer's assistant to UNDO the effects of the mistaken operations if necessary. For transformations on expressions in user programs, the user can inform CLISP whether he wishes to operate in CAUTIOUS or TRUSTING mode. In the former case (most typical) the user will be asked to approve transformations, in the latter, CLISP will operate as it does on type-in, i.e., perform the transformation after informing the user. CLISP can also handle parentheses errors caused by typing 8 or 9 for ( or ). (On most terminals, 8 and 9 are the lowercase characters for ( and ), i.e., ( and 8 appear on the same key, as do ) and 9.) For example, if you write N*8FACTORIAL N-1, the parentheses error can be detected and fixed before the infix operator * is converted to the Interlisp function TIMES. CLISP is able to distinguish this situation from cases like N*8*X meaning (TIMES N 8 X), or N*8X, where 8X is the name of a variable, again by using information about the programming environment. In fact, by integrating CLISP with DWIM, CLISP has been made sufficiently tolerant of errors that almost everything can be misspelled! For example, CLISP can successfully translate the definition of FACTORIAL: (IFF N=0 THENN1 ESLE N*8FACTTORIALNN-1) to the corresponding COND, while making five spelling corrections and fixing the parenthesis error. CLISP also contains a facility for converting from Interlisp back to CLISP, so that after running the above incorrect definition of FACTORIAL, the user could "clispify" the now correct version to obtain (IF N=0 THEN 1 ELSE N*(FACTORIAL N-1)). This sort of robustness prevails throughout CLISP. For example, the iterative statement permits the user to say things like: (FOR OLD X FROM M TO N DO (PRINT X) WHILE (PRIMEP X)) However, you can also write OLD (X_M), (OLD X_M), (OLD (X_M)), permute the order of the operators, e.g., (DO PRINT X TO N FOR OLD X_M WHILE PRIMEP X), omit either or both sets of parentheses, misspell any or all of the operators FOR, OLD, FROM, TO, DO, or WHILE, or leave out the word DO entirely! And, of course, you can also misspell PRINT, PRIMEP, M or N! In this example, the only thing you could not misspell is the first X, since it specifies the name of the variable of iteration. The other two instances of X could be misspelled. CLISP is well integrated into the Interlisp system. For example, the above iterative statement translates into an following equivalent Interlisp form using PROG, COND, GO, etc. When the interpreter subsequently encounters this CLISP expression, it automatically obtains and evaluates the translation. Similarly, the compiler "knows" to compile the translated form. However, if the user PRETTYPRINTs his program, PRETTYPRINT "knows" to print the original CLISP at the corresponding point in his function. Similarly, when the user edits his program, the editor keeps the translation invisible to the user. If the user modifies the CLISP, the translation is automatically discarded and recomputed the next time the expression is evaluated. In short, CLISP is not a language at all, but rather a system. It plays a role analagous to that of the programmer's assistant (Chapter 13). Whereas the programmer's assistant is an invisible intermediary agent between the user's console requests and the Interlisp executive, CLISP sits between the user's programs and the Interlisp interpreter. Only a small effort has been devoted to defining the core syntax of CLISP. Instead, most of the effort has been concentrated on providing a facility which "makes sense" out of the input expressions using context information as well as built-in and acquired information about user and system programs. It has been said that communication is based on the intention of the speaker to produce an effect in the recipient. CLISP operates under the assumption that what the user said was intended to represent a meaningful operation, and therefore tries very hard to make sense out of it. The motivation behind CLISP is not to provide the user with many different ways of saying the same thing, but to enable him to worry less about the syntactic aspects of his communication with the system. In other words, it gives the user a new degree of freedom by permitting him to concentrate more on the problem at hand, rather than on translation into a formal and unambiguous language. DWIM and CLISP are invoked on iterative statements because CAR of the iterative statement is not the name of a function, and hence generates an error. If the user defines a function by the same name as an i.s. operator, e.g., WHILE, TO, etc., the operator will no longer have the CLISP interpretation when it appears as CAR of a form, although it will continue to be treated as an i.s. operator if it appears in the interior of an i.s. To alert the user, a warning message is printed, e.g., (WHILE DEFINED, THEREFORE DISABLED IN CLISP). CLISP Interaction with User 1 Syntactically and semantically well formed CLISP transformations are always performed without informing the user. Other CLISP transformations described in the previous section, e.g., misspellings of operands, infix operators, parentheses errors, unary minus - binary minus errors, all follow the same protocol as other DWIM transformations (Chapter 20). That is, if DWIM has been enabled in TRUSTING mode, or the transformation is in an expression you typed in for immediate execution, your approval is not requested, but you are informed. However, if the transformation involves a user program, and DWIM was enabled in CAUTIOUS mode, you will be asked to approve. If you say NO, the transformation is not performed. Thus, in the previous section, phrases such as "one of these (transformations) succeeds" and "the transformation LAST-ELL -> LAST-EL would be found" etc., all mean if you are in CAUTIOUS mode and the error is in a program, the corresponding transformation will be performed only if you approve (or defaults by not responding). If you say NO, the procedure followed is the same as though the transformation had not been found. For example, if A*B appears in the function FOO, and B is not bound (and no other transformations are found) the user would be asked A*B [IN FOO] TREAT AS CLISP ? (The waiting time on such interactions is three times as long as for simple corrections, i.e., 3*DWIMWAIT). In certain situations, DWIM asks for approval even if DWIM is enabled in TRUSTING mode. For example, you are always asked to approve a spelling correction that might also be interpreted as a CLISP transformation, as in LAST-ELL -> LAST-EL. If you approved, A*B would be transformed to (ITIMES A B), which would then cause a U.B.A.B. error in the event that the program was being run (remember the entire discussion also applies to DWIMifying). If you said NO, A*B would be left alone. If the value of CLISPHELPFLG=NIL (initally T), the user will not be asked to approve any CLISP transformation. Instead, in those situations where approval would be required, the effect is the same as though the user had been asked and said NO. CLISP Character Operators 1 CLISP recognizes a number of special characters operators, both prefix and infix, which are translated into common expressions. For example, the character + is recognized to represent addition, so CLISP translates the litatom A+B to the form (IPLUS A B). Note that CLISP is envoked, and this translation is made, only if an error occurs, such as an unbound atom error or an undefined function error for the perfectly legitamate litatom A+B. Therefore the user may choose not to use these facilities with no penalty, similar to other CLISP facilities. The user has a lot of flexability in using CLISP character operators. A list, can always be substituted for a litatom, and vice versa, without changing the interpretation of a phrase. For example, if the value of (FOO X) is A, and the value of (FIE Y) is B, then (LIST (FOO X)+(FIE Y)) has the same value as (LIST A+B). Note that the first expression is a list of four elements: the atom "LIST", the list "(FOO X)", the atom "+", and the list "(FIE X)", whereas the second expression, (LIST A+B), is a list of only two elements: the litatom "LIST" and the litatom "A+B". Since (LIST (FOO X)+(FIE Y)) is indistinguishable from (LIST (FOO X) + (FIE Y)) because spaces before or after parentheses have no effect on the Interlisp READ program, to be consistent, extra spaces have no effect on atomic operands either. In other words, CLISP will treat (LIST A+ B), (LIST A +B), and (LIST A + B) the same as (LIST A+B). Note: CLISP does not use its own special READ program because this would require the user to explicitly identify CLISP expressions, instead of being able to intermix Interlisp and CLISP. +(+ (CLISP Operator) NIL NIL NIL NIL) [CLISP Operator] -(- (CLISP Operator) NIL NIL NIL NIL) [CLISP Operator] *(* (CLISP Operator) NIL NIL NIL NIL) [CLISP Operator] /(/ (CLISP Operator) NIL NIL NIL NIL) [CLISP Operator] ( (CLISP Operator) NIL NIL NIL NIL) [CLISP Operator] CLISP recognizes +, -, *, /, and as the normal arithmetic infix operators. The - is also recognized as the prefix operator, unary minus. These are converted to PLUS, DIFFERENCE (or in the case of unary minus, MINUS), TIMES, QUOTIENT, and EXPT. Normally, CLISP uses the "generic" arithmetic functions PLUS, TIMES, etc. CLISP contains a facility for declaring which type of arithmetic is to be used, either by making a global declaration, or by separate declarations about individual functions or variables. The usual precedence rules apply (although these can be easily changed by the user), i.e., * has higher precedence than + so that A+B*C is the same as A+(B*C), and both * and / are lower than so that 2*X2 is the same as 2*(X2). Operators of the same precedence group from left to right, e.g., A/B/C is equivalent to (A/B)/C. Minus is binary whenever possible, i.e., except when it is the first operator in a list, as in (-A) or (-A), or when it immediately follows another operator, as in A*-B. Note that grouping with parentheses can always be used to override the normal precedence grouping, or when the user is not sure how a particular expression will parse. The complete order of precedence for CLISP operators is given below. Note that + in front of a number will disappear when the number is read, e.g., (FOO X +2) is indistinguishable from (FOO X 2). This means that (FOO X +2) will not be interpreted as CLISP, or be converted to (FOO (IPLUS X 2)). Similarly, (FOO X -2) will not be interpreted the same as (FOO X-2). To circumvent this, always type a space between the + or - and a number if an infix operator is intended, e.g., write (FOO X + 2). =(= (CLISP Operator) NIL NIL NIL NIL) [CLISP Operator] GT(GT (CLISP Operator) NIL NIL NIL NIL) [CLISP Operator] LT(LT (CLISP Operator) NIL NIL NIL NIL) [CLISP Operator] GE(GE (CLISP Operator) NIL NIL NIL NIL) [CLISP Operator] LE(LE (CLISP Operator) NIL NIL NIL NIL) [CLISP Operator] These are infix operators for "Equal", "Greater Than", "Less Than", "Greater Than or Equal", and "Less Than or Equal". GT, LT, GE, and LE are all affected by the same declarations as + and *, with the initial default to use GREATERP and LESSP. Note that only single character operators, e.g., +, _, =, etc., can appear in the interior of an atom. All other operators must be set off from identifiers with spaces. For example, XLTY will not be recognized as CLISP. In some cases, DWIM will be able to diagnose this situation as a run-on spelling error, in which case after the atom is split apart, CLISP will be able to perform the indicated transformation. A number of Lisp functions, such as EQUAL, MEMBER, AND, OR, etc., can also be treated as CLISP infix operators. New infix operators can be easily added (see page X.XX). Spelling correction on misspelled infix operators is peformed using CLISPINFIXSPLST as a spelling list. AND is higher than OR, and both AND and OR are lower than the other infix operators, so (X OR Y AND Z) is the same as (X OR (Y AND Z)), and (X AND Y EQUAL Z) is the same as (X AND (Y EQUAL Z)). All of the infix predicates have lower precedence than Interlisp forms, since it is far more common to apply a predicate to two forms, than to use a Boolean as an argument to a function. Therefore, (FOO X GT FIE Y) is translated as ((FOO X) GT (FIE Y)), rather than as (FOO (X GT (FIE Y))). However, the user can easily change this. :(: (CLISP Operator) NIL NIL NIL NIL) [CLISP Operator] X:N extracts the Nth element of the list X. FOO:3 specifies the third element of FOO, or (CADDR FOO). If N is less than zero, this indicates elements counting from the end of the list; i.e. FOO:-1 is the last element of FOO. : operators can be nested, so FOO:1:2 means the second element of the first element of FOO, or (CADAR FOO). The : operator can also be used for extracting substructures of records (see page X.XX). Record operations are implemented by replacing expressions of the form X:FOO by (fetch FOO of X). Both lower- and uppercase are acceptable. : is also used to indicate operations in the pattern match facility (page X.XX). X:(& 'A -- 'B) translates to (match X with (& 'A -- 'B)) .(%. (CLISP Operator) NIL NIL NIL NIL) [CLISP Operator] In combination with :, a period can be used to specify the "data path" for record operations. For example, if FOO is a field of the BAR record, X:BAR.FOO is translated into (fetch (BAR FOO) of X). Subrecord fields can be specified with multiple periods: X:BAR.FOO.BAZ translates into (fetch (BAR FOO BAZ) of X). Note: If a record contains fields with periods in them, CLISPIFY will not translate a record operation into a form using periods to specify the data path. For example, CLISPIFY will NOT translate (fetch A.B of X) into X:A.B. ::(:: (CLISP Operator) NIL NIL NIL NIL) [CLISP Operator] X:N, returns the Nth tail of the list X. For example, FOO::3 is (CDDDR FOO), and FOO::-1 is (LAST FOO). [CLISP Operator] is used to indicate assignment. For example, XY translates to (SETQ X Y). If X does not have a value, and is not the name of one of the bound variables of the function in which it appears, spelling correction is attempted. However, since this may simply be a case of assigning an initial value to a new free variable, DWIM will always ask for approval before making the correction. In conjunction with : and ::, can also be used to perform a more general type of assignment, involving structure modification. For example, X:2Y means "make the second element of X be Y", in Interlisp terms (RPLACA (CDR X) Y). Note that the value of this operation is the value of RPLACA, which is (CDR X), rather than Y. Negative numbers can also be used, e.g., X:-2_Y, which translates to (RPLACA (NLEFT X 2) Y). The user can indicate he wants /RPLACA and /RPLACD used (undoable version of RPLACA and RPLACD, see Chapter 13), or FRPLACA and FRPLACD (fast versions of RPLACA and RPLACD, see Chapter 3), by means of CLISP declarations. The initial default is to use RPLACA and RPLACD. is also used to indicate assignment in record operations (X:FOOY translates to (replace FOO of X with Y).), and pattern match operations (Chapter 12). has different precedence on the left from on the right. On the left, is a "tight" operator, i.e., high precedence, so that A+BC is the same as A+(BC). On the right, has broader scope so that AB+C is the same as A(B+C). On typein, $FORM (where $ is the escape key) is equivalent to set the "last thing mentioned", i.e., is equivalent to (SET LASTWORD FORM) (see Chapter 20). For example, immediately after examining the value of LONGVARIABLENAME, the user could set it by typing $ followed by a form. Note that an atom of the form XY, appearing at the top level of a PROG, will not be recognized as an assignment statement because it will be interpreted as a PROG label by the Interlisp interpreter, and therefore will not cause an error, so DWIM and CLISP will never get to see it. Instead, one must write (XY). < [CLISP Operator] > [CLISP Operator] Angle brackets are used in CLISP to indicate list construction. The appearance of a "<" corresponds to a "(" and indicates that a list is to be constructed containing all the elements up to the corresponding ">". For example, > translates to (LIST A B (LIST C)). ! can be used to indicate that the next expression is to be inserted in the list as a segment, e.g., translates to (CONS A (CONS B C)) and to (APPEND A B (LIST C)). !! is used to indicate that the next expression is to be inserted as a segment, and furthermore, all list structure to its right in the angle brackets is to be physically attached to it, e.g., translates to (NCONC1 A B), and to (NCONC A (APPEND B C)). Not (NCONC (APPEND A B) C), which would have the same value, but would attach C to B, and not attach either to A. Note that <, !, !!, and > need not be separate atoms, for example, may be written equally well as < A B !C >. Also, arbitrary Interlisp or CLISP forms may be used within angle brackets. For example, one can write which translates to (CONS (SETQ FOO (FIE X)) Y). CLISPIFY converts expressions in CONS, LIST, APPEND, NCONC, NCONC1, /NCONC, and /NCONC1 into equivalent CLISP expressions using <, >, !, and !!. Note: brackets differ from other CLISP operators. For example, translates to (LIST A B (QUOTE C)) even though following ', all operators are ignored for the rest of the identifier. (This is true only if a previous unmatched < has been seen, e.g., (PRINT 'A>B) will print the atom A>B.) Note however that D> is equivalent to (LIST A B (QUOTE C>) D). ' [CLISP Operator] CLISP recognizes ' as a prefix operator. ' means QUOTE when it is the first character in an identifier, and is ignored when it is used in the interior of an identifier. Thus, X='Y means (EQ X (QUOTE Y)), but X=CAN'T means (EQ X CAN'T), not (EQ X CAN) followed by (QUOTE T). This enables users to have variable and function names with ' in them (so long as the ' is not the first character). Following ', all operators are ignored for the rest of the identifier, e.g., '*A means (QUOTE *A), and 'X=Y means (QUOTE X=Y), not (EQ (QUOTE X) Y). To write (EQ (QUOTE X) Y), one writes Y='X, or 'X =Y. This is one place where an extra space does make a difference. On typein, '$ (escape) is equivalent to (QUOTE VALUE-OF-LASTWORD) (see page X.XX). For example, after calling PRETTYPRINT on LONGFUNCTION, the user could move its definition to FOO by typing (MOVD '$ 'FOO). Note that this is not (MOVD $ 'FOO), which would be equivalent to (MOVD LONGFUNCTION 'FOO), and would (probably) cause a U.B.A. LONGFUNCTION error, nor MOVD($ FOO), which would actually move the definition of $ to FOO, since DWIM and the spelling corrector would never be invoked. ~ [CLISP Operator] CLISP recognizes ~ as a prefix operator meaning NOT. ~ can negate a form, as in ~(ASSOC X Y), or ~X, or negate an infix operator, e.g., (A ~GT B) is the same as (A LEQ B). Note that ~A=B means (EQ (NOT A) B). When ~ negates an operator, e.g., ~=, ~LT, the two operators are treated as a single operator whose precedence is that of the second operator. When ~ negates a function, e.g., (~FOO X Y), it negates the whole form, i.e., (~(FOO X Y)). Order of Prededence of CLISP Operators: ' : _ (left precedence) - (unary), ~ ^ *, / +, - (binary) _ (right precedence) = Interlisp forms LT, GT, EQUAL, MEMBER, etc. AND OR IF, THEN, ELSEIF, ELSE iterative statement operators Declarations 1 CLISP declarations are used to affect the choice of Interlisp function used as the translation of a particular operator. For example, A+B can be translated as either (PLUS A B), (FPLUS A B), or (IPLUS A B), depending on the declaration in effect. Similarly X:1_Y can mean (RPLACA X Y), (FRPLACA X Y), or (/RPLACA X Y), and either (NCONC1 A B) or (/NCONC1 A B). Note that the choice of function on all CLISP transformations are affected by the CLISP declaration in effect, i.e., iterative statements, pattern matches, record operations, as well as infix and prefix operators. (CLISPDEC DECLST) [Function] 1 Puts into effect the declarations in DECLST. CLISPDEC performs spelling corrections on words not recognized as declarations. CLISPDEC is undoable. The user can makes (changes) a global declaration by calling CLISPDEC with DECLST a list of declarations, e.g., (CLISPDEC '(FLOATING UNDOABLE)). Changing a global declaration does not affect the speed of subsequent CLISP transformations, since all CLISP transformation are table driven (i.e., property list), and global declarations are accomplished by making the appropriate internal changes to CLISP at the time of the declaration. If a function employs local declarations (described below), there will be a slight loss in efficiency owing to the fact that for each CLISP transformation, the declaration list must be searched for possibly relevant declarations. Declarations are implemented in the order that they are given, so that later declarations override earlier ones. For example, the declaration FAST specifies that FRPLACA, FRPLACD, FMEMB, and FLAST be used in place of RPLACA, RPLACD, MEMB, and LAST; the declaration RPLACA specifies that RPLACA be used. Therefore, the declarations (FAST RPLACA RPLACD) will cause FMEMB, FLAST, RPLACA, and RPLACD to be used. The initial global declaration is MIXED and STANDARD. The table below gives the declarations available in CLISP, and the Interlisp functions they indicate: Declaration: Interlisp Functions to be used: MIXED PLUS, MINUS, DIFFERENCE, TIMES, QUOTIENT, LESSP, GREATERP INTEGER or FIXED IPLUS, IMINUS, IDIFFERENCE, ITIMES, IQUOTIENT, ILESSP, IGREATERP FLOATING FPLUS, FMINUS, FDIFFERENCE, FTIMES, FQUOTIENT, LESSP, FGREATERP FAST FRPLACA, FRPLACD, FMEMB, FLAST, FASSOC UNDOABLE /RPLACA, /RPLACD, /NCONC, /NCONC1, /MAPCONC, /MAPCON STANDARD RPLACA, RPLACD, MEMB, LAST, ASSOC, NCONC, NCONC1, MAPCONC, MAPCON RPLACA, RPLACD, /RPLACA, etc. corresponding function The user can also make local declarations affecting a selected function or functions by inserting an expression of the form (CLISP: . DECLARATIONS) immediately following the argument list, i.e., as CADDR of the definition. Such local declarations take precedence over global declarations. Declarations affecting selected variables can be indicated by lists, where the first element is the name of a variable, and the rest of the list the declarations for that variable. For example, (CLISP: FLOATING (X INTEGER)) specifies that in this function integer arithmetic be used for computations involving X, and floating arithmetic for all other computations, where "involving" means where the variable itself is an operand. For example, with the declaration (FLOATING (X INTEGER)) in effect, (FOO X)+(FIE X) would translate to FPLUS, i.e., use floating arithmetic, even though X appears somewhere inside of the operands, whereas X+(FIE X) would translate to IPLUS. If there are declarations involving both operands, e.g., X+Y, with (X FLOATING) (Y INTEGER), whichever appears first in the declaration list will be used. The user can also make local record declarations by inserting a record declaration, e.g., (RECORD --), (ARRAYRECORD --), etc., in the local declaration list. In addition, a local declaration of the form (RECORDS A B C) is equivalent to having copies of the global declarations A, B, and C in the local declaration. Local record declarations override global record declarations for the function in which they appear. Local declarations can also be used to override the global setting of certain DWIM/CLISP parameters effective only for transformations within that function, by including in the local declaration an expression of the form (VARIABLE = VALUE), e.g., (PATVARDEFAULT = QUOTE). The CLISP: expression is converted to a comment of a special form recognized by CLISP. Whenever a CLISP transformation that is affected by declarations is about to be performed in a function, this comment will be searched for a relevant declaration, and if one is found, the corresponding function will be used. Otherwise, if none are found, the global declaration(s) currently in effect will be used. Local declarations are effective in the order that they are given, so that later declarations can be used to override earlier ones, e.g., (CLISP: FAST RPLACA RPLACD) specifies that FMEMB, FLAST, RPLACA, and RPLACD be used. An exception to this is that declarations for specific variables take precedence of general, function-wide declarations, regardless of the order of appearance, as in (CLISP: (X INTEGER) FLOATING). CLISPIFY also checks the declarations in effect before selecting an infix operator to ensure that the corresponding CLISP construct would in fact translate back to this form. For example, if a FLOATING declaration is in effect, CLISPIFY will convert (FPLUS X Y) to X+Y, but leave (IPLUS X Y) as is. Note that if (FPLUS X Y) is CLISPIFYed while a FLOATING declaration is under effect, and then the declaration is changed to INTEGER, when X+Y is translated back to Interlisp, it will become (IPLUS X Y). CLISP Operation 1 CLISP is a part of the basic Interlisp system. Without any special preparations, the user can include CLISP constructs in programs, or type them in directly for evaluation (in EVAL or APPLY format), then, when the "error" occurrs, and DWIM is called, it will destructively transform the CLISP to the equivalent Interlisp expression and evaluate the Interlisp expression. CLISP transformations, like all DWIM corrections, are undoable. User approval is not requested, and no message is printed. Note that this entire discussion also applies to CLISP transformation initiated by calls to DWIM from DWIMIFY. However, if a CLISP construct contains an error, an appropriate diagnostic is generated, and the form is left unchanged. For example, if the user writes (LIST X+Y*), the error diagnostic MISSING OPERAND AT X+Y* IN (LIST X+Y*) would be generated. Similarly, if the user writes (LAST+EL X), CLISP knows that ((IPLUS LAST EL) X) is not a valid Interlisp expression, so the error diagnostic MISSING OPERATOR IN (LAST+EL X) is generated. (For example, the user might have meant to say (LAST+EL*X).) Note that if LAST+EL were the name of a defined function, CLISP would never see this form. Since the bad CLISP transformation might not be CLISP at all, for example, it might be a misspelling of a user function or variable, DWIM holds all CLISP error messages until after trying other corrections. If one of these succeeds, the CLISP message is discarded. Otherwise, if all fail, the message is printed (but no change is made). For example, suppose the user types (R/PLACA X Y). CLISP generates a diagnostic, since ((IQUOTIENT R PLACA) X Y) is obviously not right. However, since R/PLACA spelling corrects to /RPLACA, this diagnostic is never printed. Note: CLISP error messages are not printed on type-in. For example, typing X+*Y will just produce a U.B.A. X+*Y message. If a CLISP infix construct is well formed from a syntactic standpoint, but one or both of its operands are atomic and not bound, it is possible that either the operand is misspelled, e.g., the user wrote X+YY for X+Y, or that a CLISP transformation operation was not intended at all, but that the entire expression is a misspelling. For the purpose of DWIMIFYing, "not bound" means no top level value, not on list of bound variables built up by DWIMIFY during its analysis of the expression, and not on NOFIXVARSLST, i.e., not previously seen. For example, if the user has a variable named LAST-EL, and writes (LIST LAST-ELL). Therefore, CLISP computes, but does not actually perform, the indicated infix transformation. DWIM then continues, and if it is able to make another correction, does so, and ignores the CLISP interpretation. For example, with LAST-ELL, the transformation LAST-ELL -> LAST-EL would be found. If no other transformation is found, and DWIM is about to interpret a construct as CLISP for which one of the operands is not bound, DWIM will ask the user whether CLISP was intended, in this case by printing LAST-ELL TREAT AS CLISP ?. Note: If more than one infix operator was involved in the CLISP construct, e.g., X+Y+Z, or the operation was an assignment to a variable already noticed, or TREATASCLISPFLG is T (initially NIL), the user will simply be informed of the correction, e.g., X+Y+Z TREATED AS CLISP. Otherwise, even if DWIM was enabled in TRUSTING mode, the user will be asked to approve the correction. The same sort of procedure is followed with 8 and 9 errors. For example, suppose the user writes FOO8*X where FOO8 is not bound. The CLISP transformation is noted, and DWIM proceeds. It next asks the user to approve FOO8*X -> FOO ( *X. For example, this would make sense if the user has (or plans to define) a function named *X. If he refuses, the user is asked whether FOO8*X is to be treated as CLISP. Similarly, if FOO8 were the name of a variable, and the user writes FOOO8*X, he will first be asked to approve FOOO8*X -> FOOO ( XX, and if he refuses, then be offered the FOOO8 -> FOO8 correction. The 8-9 transformation is tried before spelling correction since it is empirically more likely that an unbound atom or undefined function containing an 8 or a 9 is a parenthesis error, rather than a spelling error. CLISP also contains provision for correcting misspellings of infix operators (other than single characters), IF words, and i.s. operators. This is implemented in such a way that the user who does not misspell them is not penalized. For example, if the user writes IF N=0 THEN 1 ELSSE N*(FACT N-1) CLISP does not operate by checking each word to see if it is a misspelling of IF, THEN, ELSE, or ELSEIF, since this would seriously degrade CLISP's performance on all IF statements. Instead, CLISP assumes that all of the IF words are spelled correctly, and transforms the expression to (COND ((ZEROP N) 1 ELSSE N*(FACT N-1))). Later, after DWIM cannot find any other interpretation for ELSSE, and using the fact that this atom originally appeared in an IF statement, DWIM attempts spelling correction, using (IF THEN ELSE ELSEIF) for a spelling list. When this is successful, DWIM "fails" all the way back to the original IF statement, changes ELSSE to ELSE, and starts over. Misspellings of AND, OR, LT, GT, etc. are handled similarly. CLISP also contains many Do-What-I-Mean features besides spelling corrections. For example, the form (LIST +X Y) would generate a MISSING OPERATOR error. However, (LIST -X Y) makes sense, if the minus is unary, so DWIM offers this interpretation to the user. Another common error, especially for new users, is to write (LIST X*FOO(Y)) or (LIST X*FOO Y), where FOO is the name of a function, instead of (LIST X*(FOO Y)). Therefore, whenever an operand that is not bound is also the name of a function (or corrects to one), the above interpretations are offered. CLISP Translations 1 The translation of CLISP character operators and the CLISP word IF are handled by replacing the CLISP expression with the corresponding Interlisp expression, and discarding the original CLISP. This is done because (1) the CLISP expression is easily recomputable (by CLISPIFY) and (2) the Interlisp expressions are simple and straightforward. Another reason for discarding the original CLISP is that it may contain errors that were corrected in the course of translation (e.g., FOO_FOOO:1, N*8FOO X), etc.). If the original CLISP were retained, either the user would have to go back and fix these errors by hand, thereby negating the advantage of having DWIM perform these corrections, or else DWIM would have to keep correcting these errors over and over. Note that CLISPIFY is sufficiently fast that it is practical for the user to configure his Interlisp system so that all expressions are automatically CLISPIFYed immediately before they are presented to him. For example, he can define an edit macro to use in place of P which calls CLISPIFY on the current expression before printing it. Similarly, he can inform PRETTYPRINT to call CLISPIFY on each expression before printing it, etc. Where (1) or (2) are not the case, e.g., with iterative statements, pattern matches, record expressions, etc. the original CLISP is retained (or a slightly modified version thereof), and the translation is stored elsewhere (by the function CLISPTRAN, page X.XX), usually in the hash array CLISPARRAY. The interpreter automatically checks this array when given a form CAR of which is not a function. Similarly, the compiler performs a GETHASH when given a form it does not recognize to see if it has a translation, which is then compiled instead of the form. Whenever the user changes a CLISP expresson by editing it, the editor automatically deletes its translation (if one exists), so that the next time it is evaluated or dwimified, the expression will be retranslated (if the value of CLISPRETRANFLG is T, DWIMIFY will also (re)translate any expressions which have translations stored remotely, see page X.XX). The function PPT and the edit commands PPT and CLISP: are available for examining translations (page X.XX). The user can also indicate that he wants the original CLISP retained by embedding it in an expression of the form (CLISP . CLISP-EXPRESSION), e.g., (CLISP X:5:3) or (CLISP ). In such cases, the translation will be stored remotely as described above. Furthermore, such expressions will be treated as CLISP even if infix and prefix transformations have been disabled by setting CLISPFLG to NIL (page X.XX). In other words, the user can instruct the system to interpret as CLISP infix or prefix constructs only those expressions that are specifically flagged as such. The user can also include CLISP declarations by writing (CLISP DECLARATIONS . FORM), e.g., (CLISP (CLISP: FLOATING) ... ). These declarations will be used in place of any CLISP declarations in the function definition. This feature provides a way of including CLISP declarations in macro definitions. Note: CLISP translations can also be used to supply an interpretation for function objects, as well as forms, either for function objects that are used openly, i.e., appearing as CAR of form, function objects that are explicitly APPLYed, as with arguments to mapping functions, or function objects contained in function definition cells. In all cases, if CAR of the object is not LAMBDA or NLAMBDA, the interpreter and compiler will check CLISPARRAY. DWIMIFY 1 DWIMIFY is effectively a preprocessor for CLISP. DWIMIFY operates by scanning an expression as though it were being interpreted, and for each form that would generate an error, calling DWIM to "fix" it. DWIMIFY performs all DWIM transformations, not just CLISP transformations, so it does spelling correction, fixes 8-9 errors, handles F/L, etc. Thus the user will see the same messages, and be asked for approval in the same situations, as he would if the expression were actually run. If DWIM is unable to make a correction, no message is printed, the form is left as it was, and the analysis proceeds. DWIMIFY knows exactly how the interpreter works. It knows the syntax of PROGs, SELECTQs, LAMBDA expressions, SETQs, et al. It knows how variables are bound, and that the argument of NLAMBDAs are not evaluated (the user can inform DWIMIFY of a function or macro's nonstandard binding or evaluation by giving it a suitable INFO property, see page X.XX). In the course of its analysis of a particular expression, DWIMIFY builds a list of the bound variables from the LAMBDA expressions and PROGs that it encounters. It uses this list for spelling corrections. DWIMIFY also knows not to try to "correct" variables that are on this list since they would be bound if the expression were actually being run. However, note that DWIMIFY cannot, a priori, know about variables that are used freely but would be bound in a higher function if the expression were evaluated in its normal context. Therefore, DWIMIFY will try to "correct" these variables. Similarly, DWIMIFY will attempt to correct forms for which CAR is undefined, even when the form is not in error from the user's standpoint, but the corresponding function has simply not yet been defined. Note: DWIMIFY rebinds FIXSPELLDEFAULT to N, so that if the user is not at the terminal when dwimifying (or compiling), spelling corrections will not be performed. DWIMIFY will also inform the user when it encounters an expression with too many arguments (unless DWIMCHECK#ARGSFLG=NIL), because such an occurrence, although does not cause an error in the Interlisp interpreter, nevertheless is frequently symptomatic of a parenthesis error. For example, if the user wrote (CONS (QUOTE FOO X)) instead of (CONS (QUOTE FOO) X), DWIMIFY will print: POSSIBLE PARENTHESIS ERROR IN (QUOTE FOO X) TOO MANY ARGUMENTS (MORE THAN 1) DWIMIFY will also check to see if a PROG label contains a clisp character (unless DWIMCHECKPROGLABELSFLG=NIL, or the label is a member of NOFIXVARSLST), and if so, will alert the user by printing the message SUSPICIOUS PROG LABEL, followed by the label. The PROG label will not be treated as CLISP. Note that in most cases, an attempt to transform a form that is already as the user intended will have no effect (because there will be nothing to which that form could reasonably be transformed). However, in order to avoid needless calls to DWIM or to avoid possible confusion, the user can inform DWIMIFY not to attempt corrections or transformations on certain functions or variables by adding them to the list NOFIXFNSLST or NOFIXVARSLST respectively. Note that the user could achieve the same effect by simply setting the corresponding variables, and giving the functions dummy definitions. DWIMIFY will never attempt corrections on global variables, i.e., variables that are a member of the list GLOBALVARS, or have the property GLOBALVAR with value T, on their property list. Similarly, DWIMIFY will not attempt to correct variables declared to be SPECVARS in block declarations or via DECLARE expressions in the function body. The user can also declare variables that are simply used freely in a function by using the USEDFREE declaration. DWIMIFY and DWIMIFYFNS (used to DWIMIFY several functions) maintain two internal lists of those functions and variables for which corrections were unsuccessfully attempted. These lists are initialized to the values of NOFIXFNSLST and NOFIXVARSLST. Once an attempt is made to fix a particular function or variable, and the attempt fails, the function or variable is added to the corresponding list, so that on subsequent occurrences (within this call to DWIMIFY or DWIMIFYFNS), no attempt at correction is made. For example, if FOO calls FIE several times, and FIE is undefined at the time FOO is dwimified, DWIMIFY will not bother with FIE after the first occurrence. In other words, once DWIMIFY "notices" a function or variable, it no longer attempts to correct it. DWIMIFY and DWIMIFYFNS also "notice" free variables that are set in the expression being processed. Moreover, once DWIMIFY "notices" such functions or variables, it subsequently treats them the same as though they were actually defined or set. Note that these internal lists are local to each call to DWIMIFY and DWIMIFYFNS, so that if a function containing FOOO, a misspelled call to FOO, is DWIMIFYed before FOO is defined or mentioned, if the function is DWIMIFYed again after FOO has been defined, the correction will be made. The user can undo selected transformations performed by DWIMIFY, as described on page X.XX. (DWIMIFY(DWIMIFY (Function) NIL NIL NIL NIL) X QUIETFLG L) [Function] Performs all DWIM and CLISP corrections and transformations on X that would be performed if X were run, and prints the result unless QUIETFLG=T. If X is an atom and L is NIL, X is treated as the name of a function, and its entire definition is dwimified. If X is a list or L is not NIL, X is the expression to be dwimified. If L is not NIL, it is the edit push-down list leading to X, and is used for determining context, i.e., what bound variables would be in effect when X was evaluated, whether X is a form or sequence of forms, e.g., a COND clause, etc. If X is an iterative statement and L is NIL, DWIMIFY will also print the translation, i.e., what is stored in the hash array. (DWIMIFYFNS(DWIMIFYFNS (Function) NIL NIL NIL NIL) FN1 ... FNN) [NLambda NoSpread Function] Dwimifies each of the functions given. If only one argument is given, it is evalued. If its value is a list, the functions on this list are dwimified. If only one argument is given, it is atomic, its value is not a list, and it is the name of a known file, DWIMIFYFNS will operate on (FILEFNSLST FN1), e.g. (DWIMIFYFNS FOO.LSP) will dwimify every function in the file FOO.LSP. Every 30 seconds, DWIMIFYFNS prints the name of the function it is processing, a la PRETTYPRINT. Value is a list of the functions dwimified. DWIMINMACROSFLG(DWIMINMACROSFLG (Variable) NIL NIL NIL NIL) [Variable] Controls how DWIMIFY treats the arguments in a "call" to a macro, i.e., where the CAR of the form is undefined, but has a macro definition. If DWIMINMACROSFLG is T, then macros are treated as LAMBDA functions, i.e., the arguments are assumed to be evaluated, which means that DWIMIFY will descend into the argument list. If DWIMINMACROSFLG is NIL, macros are treated as NLAMBDA functions. DWIMINMACROSFLG is initially T. INFO(INFO (Property) NIL NIL NIL NIL) [Property Name] Used to inform DWIMIFY of nonstandard behavior of particular forms with respect to evaluation, binding of arguments, etc. The INFO property of a litatom is a single atom or list of atoms chosen from among the following: EVAL Informs DWIMIFY (and CLISP and Masterscope) that an nlambda function does evaluate its arguments. Can also be placed on a macro name to override the behavior of DWIMINMACROSFLG = NIL. NOEVAL Informs DWIMIFY that a macro does not evaluate all of its arguments, even when DWIMINMACROSFLG = T. BINDS Placed on the INFO property of a function or the CAR of a special form to inform DWIMIFY that the function or form binds variables. In this case, DWIMIFY assumes that CADR of the form is the variable list, i.e., a list of litatoms, or lists of the form (VAL VALUE). LAMBDA, NLAMBDA, PROG, and RESETVARS are handled in this fashion. LABELS Informs CLISPIFY that the form interprets top-level litatoms as labels, so that CLISPIFY will never introduce an atom (by packing) at the top level of the expression. PROG is handled in this fashion. NOFIXFNSLST (NOFIXFNSLST% (Variable) NIL NIL NIL NIL) [Variable] List of functions that DWIMIFY will not try to correct. NOFIXVARSLST(NOFIXVARSLST (Variable) NIL NIL NIL NIL) [Variable] List of variables that DWIMIFY will not try to correct. NOSPELLFLG(NOSPELLFLG (Variable) NIL NIL NIL NIL) [Variable] If T, DWIMIFY will not perform any spelling corrections. Initially NIL. NOSPELLFLG is reset to T when compiling functions whose definitions are obtained from a file, as opposed to being in core. CLISPHELPFLG (CLISPHELPFLG% (Variable) NIL NIL NIL NIL) [Variable] If NIL, DWIMIFY will not ask the user for approval of any CLISP transformations. Instead, in those situations where approval would be required, the effect is the same as though the user had been asked and said NO. Initially T. DWIMIFYCOMPFLG (DWIMIFYCOMPFLG% (Variable) NIL NIL NIL NIL) [Variable] If T, DWIMIFY is called before compiling an expression. Initially NIL. DWIMCHECK#ARGSFLG(DWIMCHECK#ARGSFLG (Variable) NIL NIL NIL NIL) [Variable] If T, causes DWIMIFY to check for too many arguments in a form. Initially T. DWIMCHECKPROGLABELSFLG(DWIMCHECKPROGLABELSFLG (Variable) NIL NIL NIL NIL) [Variable] If T, causes DWIMIFY to check whether a PROG label contains a CLISP character. Initially T. DWIMESSGAG(DWIMESSGAG (Variable) NIL NIL NIL NIL) [Variable] If T, suppresses all DWIMIFY error messages. Initially NIL. CLISPRETRANFLG(CLISPRETRANFLG (Variable) NIL NIL NIL NIL) [Variable] If T, informs DWIMIFY to (re)translate all expressions which have remote translations in the CLISP hash array. Initially NIL. CLISPIFY 1 CLISPIFY converts Interlisp expressions to CLISP. Note that the expression given to CLISPIFY need not have originally been input as CLISP, i.e., CLISPIFY can be used on functions that were written before CLISP was even implemented. CLISPIFY is cognizant of declaration rules as well as all of the precedence rules. For example, CLISPIFY will convert (IPLUS A (ITIMES B C)) into A+B*C, but (ITIMES A (IPLUS B C)) into A*(B+C). CLISPIFY handles such cases by first DWIMIFYing the expression. CLISPIFY also knows how to handle expressions consisting of a mixture of Interlisp and CLISP, e.g., (IPLUS A B*C) is converted to A+B*C, but (ITIMES A B+C) to (A*(B+C)). CLISPIFY converts calls to the six basic mapping functions, MAP, MAPC, MAPCAR, MAPLIST, MAPCONC, and MAPCON, into equivalent iterative statements. It also converts certain easily recognizable internal PROG loops to the corresponding iterative statements. CLISPIFY can convert all iterative statements input in CLISP back to CLISP, regardless of how complicated the translation was, because the original CLISP is saved. CLISPIFY is not destructive to the original Interlisp expression, i.e., CLISPIFY produces a new expression without changing the original. The new expression may however contain some "pieces" of the original, since CLISPIFY attempts to minimize the number of CONSes by not copying structure whenever possible. CLISPIFY will not convert expressions appearing as arguments to NLAMBDA functions, except for those functions whose INFO property is or contains the atom EVAL. CLISPIFY also contains built in information enabling it to process special forms such as PROG, SELECTQ, etc. If the INFO property is or contains the atom LABELS, CLISPIFY will never create an atom (by packing) at the top level of the expression. PROG is handled in this fashion. Note: Disabling a CLISP operator with CLDISABLE (page X.XX) will also disable the corresponding CLISPIFY transformation. Thus, if _ is "turned off", A_B will not transform to (SETQ A B), nor vice versa. (CLISPIFY X EDITCHAIN) [Function] Clispifies X. If X is an atom and EDITCHAIN is NIL, X is treated as the name of a function, and its definition (or EXPR property) is clispified. After CLISPIFY has finished, X is redefined (using /PUTD) with its new CLISP definition. The value of CLISPIFY is X. If X is atomic and not the name of a function, spelling correction is attempted. If this fails, an error is generated. If X is a list, or EDITCHAIN is not NIL, X itself is the expression to be clispified. If EDITCHAIN is not NIL, it is the edit push-down list leading to X and is used to determine context as with DWIMIFY, as well as to obtain the local declarations, if any. The value of CLISPIFY is the clispified version of X. (CLISPIFYFNS FN1 ... FNN) [NLambda NoSpread Function] Like DWIMIFYFNS except calls CLISPIFY instead of DWIMIFY. CL:FLG(CL:FLG (Variable) NIL NIL NIL NIL) [Variable] Affects CLISPIFY's handling of forms beginning with CAR, CDR, ... CDDDDR, as well as pattern match and record expressions. If CL:FLG is NIL, these are not transformed into the equivalent : expressions. This will prevent CLISPIFY from constructing any expression employing a : infix operator, e.g., (CADR X) will not be transformed to X:2. If CL:FLG is T, CLISPIFY will convert to : notation only when the argument is atomic or a simple list (a function name and one atomic argument). If CL:FLG is ALL, CLISPIFY will convert to : expressions whenever possible. CL:FLG is initially T. CLREMPARSFLG (CLREMPARSFLG% (Variable) NIL NIL NIL NIL) [Variable] If T, CLISPIFY will remove parentheses in certain cases from simple forms, where "simple" means a function name and one or two atomic arguments. For example, (COND ((ATOM X) --)) will CLISPIFY to (IF ATOM X THEN --). However, if CLREMPARSFLG is set to NIL, CLISPIFY will produce (IF (ATOM X) THEN --). Regardless of the flag setting, the expression can be input in either form. CLREMPARSFLG is initially NIL. CLISPIFYPACKFLG (CLISPIFYPACKFLG% (Variable) NIL NIL NIL NIL) [Variable] CLISPIFYPACKFLG affects the treatment of infix operators with atomic operands. If CLISPIFYPACKFLG is T, CLISPIFY will pack these into single atoms, e.g., (IPLUS A (ITIMES B C)) becomes A+B*C. If CLISPIFYPACKFLG is NIL, no packing is done, e.g., the above becomes A + B * C. CLISPIFYPACKFLG is initially T. CLISPIFYUSERFN(CLISPIFYUSERFN (Variable) NIL NIL NIL NIL) [Variable] If T, causes the function CLISPIFYUSERFN, which should be a function of one argument, to be called on each form (list) not otherwise recognized by CLISPIFY. If a non-NIL value is returned, it is treated as the clispified form. Initially NIL Note that CLISPIFYUSERFN must be both set and defined to use this feature. FUNNYATOMLST(FUNNYATOMLST (Variable) NIL NIL NIL NIL) [Variable] Suppose the user has variables named A, B, and A*B. If CLISPIFY were to convert (ITIMES A B) to A*B, A*B would not translate back correctly to (ITIMES A B), since it would be the name of a variable, and therefore would not cause an error. The user can prevent this from happening by adding A*B to the list FUNNYATOMLST. Then, (ITIMES A B) would CLISPIFY to A * B. Note that A*B's appearance on FUNNYATOMLST would not enable DWIM and CLISP to decode A*B+C as (IPLUS A*B C); FUNNYATOMLST is used only by CLISPIFY. Thus, if an identifier contains a CLISP character, it should always be separated (with spaces) from other operators. For example, if X* is a variable, the user should write (SETQ X* FORM) in CLISP as X* _FORM, not X*_FORM. In general, it is best to avoid use of identifiers containing CLISP character operators as much as possible. Miscellaneous Functions and Variables 1 CLISPFLG(CLISPFLG (Variable) NIL NIL NIL NIL) [Variable] If CLISPFLG=NIL, disables all CLISP infix or prefix transformations (but does not affect IF/THEN/ELSE statements, or iterative statements). If CLISPFLG=TYPE-IN, CLISP transformations are performed only on expressions that are typed in for evaluation, i.e., not on user programs. If CLISPFLG=T, CLISP transformations are performed on all expressions. The initial value for CLISPFLG is T. CLISPIFYing anything will cause CLISPFLG to be set to T. CLISPCHARS (CLISPCHARS% (Variable) NIL NIL NIL NIL) [Variable] A list of the operators that can appear in the interior of an atom. Currently (+ - * / ^ ~ ' = _ : < > +- ~= @ !). CLISPCHARRAY(CLISPCHARRAY (Variable) NIL NIL NIL NIL) [Variable] A bit table of the characters on CLISPCHARS used for calls to STRPOSL (Chapter 4). CLISPCHARRAY is initialized by performing (SETQ CLISPCHARRAY (MAKEBITTABLE CLISPCHARS)). (CLISPINFIXSPLST% (Variable) NIL NIL NIL NIL)CLISPINFIXSPLST [Variable] A list of infix operators used for spelling correction. CLISPARRAY (CLISPARRAY (Variable) NIL NIL NIL NIL) [Variable] Hash array used for storing CLISP translations. CLISPARRAY is checked by FAULTEVAL and FAULTAPPLY on erroneous forms before calling DWIM, and by the compiler. (CLISPTRAN(CLISPTRAN (Function) NIL NIL NIL NIL) X TRAN) [Function] Gives X the translation TRAN by storing (key X, value TRAN) in the hash array CLISPARRAY. CLISPTRAN is called for all CLISP translations, via a non-linked, external function call, so it can be advised. (CLISPDEC(CLISPDEC (Function) NIL NIL NIL NIL) DECLST) [Function] Puts into effect the declarations in DECLST. CLISPDEC performs spelling corrections on words not recognized as declarations. CLISPDEC is undoable. (CLDISABLE(CLDISABLE (Function) NIL NIL NIL NIL) OP) [Function] Disables the CLISP operator OP. For example, (CLDISABLE '-) makes - be just another character. CLDISABLE can be used on all CLISP operators, e.g., infix operators, prefix operators, iterative statement operators, etc. CLDISABLE is undoable. Note: Simply removing a character operator from CLISPCHARS will prevent it from being treated as a CLISP operator when it appears as part of an atom, but it will continue to be an operator when it appears as a separate atom, e.g. (FOO + X) vs FOO+X. CLISPIFTRANFLG(CLISPIFTRANFLG (Variable) NIL NIL NIL NIL) [Variable] Affects handling of translations of IF-THEN-ELSE statements (see Chapter 9). If T, the translations are stored elsewhere, and the (modified) CLISP retained. If NIL, the corresponding COND expression replaces the CLISP. Initially T. CLISPIFYPRETTYFLG(CLISPIFYPRETTYFLG (Variable) NIL NIL NIL NIL) [Variable] If non-NIL, causes PRETTYPRINT (and therefore PP and MAKEFILE) to CLISPIFY selected function definitions before printing them according to the following interpretations of CLISPIFYPRETTYFLG: ALL Clispify all functions. T or EXPRS Clispify all functions currently defined as EXPRs. CHANGES Clispify all functions marked as having been changed. a list Clispify all functions in that list. CLISPIFYPRETTYFLG is (temporarily) reset to T when MAKEFILE is called with the option CLISPIFY, and reset to CHANGES when the file being dumped has the property FILETYPE value CLISP. CLISPIFYPRETTYFLG is initially NIL. Note: If CLISPIFYPRETTYFLG is non-NIL, and the only transformation performed by DWIM are well formed CLISP transformations, i.e., no spelling corrections, the function will not be marked as changed, since it would only have to be re-clispified and re-prettyprinted when the file was written out. (PPT X) [NLambda NoSpread Function] Both a function and an edit macro for prettyprinting translations. It performs a PP after first resetting PRETTYTRANFLG to T, thereby causing any translations to be printed instead of the corresponding CLISP. CLISP:(CL (Editor Command) NIL NIL NIL NIL) [Editor Command] Edit macro that obtains the translation of the correct expression, if any, from CLISPARRAY, and calls EDITE on it. CL(CL (Editor Command) NIL NIL NIL NIL) [Editor Command] Edit macro. Replaces current expression with CLISPIFYed current expression. Current expression can be an element or tail. DW(DW (Editor Command) NIL NIL NIL NIL) [Editor Command] Edit macro. DWIMIFYs current expression, which can be an element (atom or list) or tail. Both CL and DW can be called when the current expression is either an element or a tail and will work properly. Both consult the declarations in the function being edited, if any, and both are undoable. (LOWERCASE(LOWERCASE (Function) NIL NIL NIL NIL) FLG) [Function] If FLG=T, LOWERCASE makes the necessary internal modifications so that CLISPIFY will use lower case versions of AND, OR, IF, THEN, ELSE, ELSEIF, and all i.s. operators. This produces more readable output. Note that the user can always type in either upper or lower case (or a combination), regardless of the action of LOWERCASE. If FLG=NIL, CLISPIFY will use uppercase versions of AND, OR, et al. The value of LOWERCASE is its previous "setting". LOWERCASE is undoable. The initial setting for LOWERCASE is T. CLISP Internal Conventions 1 CLISP is almost entirely table driven by the property lists of the corresponding infix or prefix operators. For example, much of the information used for translating the + infix operator is stored on the property list of the litatom "+". Thus it is relatively easy to add new infix or prefix operators or change old ones, simply by adding or changing selected property values. (There is some built in information for handling minus, :, ', and ~, i.e., the user could not himself add such "special" operators, although he can disable or redefine them.) Global declarations operate by changing the LISPFN and CLISPINFIX properties of the appropriate operators. CLISPTYPE(CLISPTYPE (Property) NIL NIL NIL NIL) [Property Name] The property value of the property CLISPTYPE is the precedence number of the operator: higher values have higher precedence, i.e., are tighter. Note that the actual value is unimportant, only the value relative to other operators. For example, CLISPTYPE for :, , and * are 14, 6, and 4 respectively. Operators with the same precedence group left to right, e.g., / also has precedence 4, so A/B*C is (A/B)*C. An operator can have a different left and right precedence by making the value of CLISPTYPE be a dotted pair of two numbers, e.g., CLISPTYPE of is (8 . -12). In this case, CAR is the left precedence, and CDR the right, i.e., CAR is used when comparing with operators on the left, and CDR with operators on the right. For example, A*BC+D is parsed as A*(B(C+D)) because the left precedence of is 8, which is higher than that of *, which is 4. The right precedence of is -12, which is lower than that of +, which is 2. If the CLISPTYPE property for any operator is removed, the corresponding CLISP transformation is disabled, as well as the inverse CLISPIFY transformation. UNARYOP(UNARYOP (Property) NIL NIL NIL NIL) [Property Name] The value of property UNARYOP must be T for unary operators or brackets. The operand is always on the right, i.e., unary operators or brackets are always prefix operators. BROADSCOPE(BROADSCOPE (Property) NIL NIL NIL NIL) [Property Name] The value of property BROADSCOPE is T if the operator has lower precedence than Interlisp forms, e.g., LT, EQUAL, AND, etc. For example, (FOO X AND Y) parses as ((FOO X) AND Y). If the BROADSCOPE property were removed from the property list of AND, (FOO X AND Y) would parse as (FOO (X AND Y)). LISPFN(LISPFN (Property) NIL NIL NIL NIL) [Property Name] The value of the property LISPFN is the name of the function to which the infix operator translates. For example, the value of LISPFN for is EXPT, for ' QUOTE, etc. If the value of the property LISPFN is NIL, the infix operator itself is also the function, e.g., AND, OR, EQUAL. SETFN(SETFN (Property) NIL NIL NIL NIL) [Property Name] If FOO has a SETFN property FIE, then (FOO --)X translates to (FIE -- X). For example, if the user makes ELT be an infix operator, e.g. #, by putting appropriate CLISPTYPE and LISPFN properties on the property list of # then he can also make # followed by translate to SETA, e.g., X#NY to (SETA X N Y), by putting SETA on the property list of ELT under the property SETFN. Putting the list (ELT) on the property list of SETA under property SETFN will enable SETA forms to CLISPIFY back to ELT's. CLISPINFIX(CLISPINFIX (Property) NIL NIL NIL NIL) [Property Name] The value of this property is the CLISP infix to be used in CLISPIFYing. This property is stored on the property list of the corresponding Interlisp function, e.g., the value of property CLISPINFIX for EXPT is , for QUOTE is ' etc. CLISPWORD(CLISPWORD (Property) NIL NIL NIL NIL) [Property Name] Appears on the property list of clisp operators which can appear as CAR of a form, such as FETCH, REPLACE, IF, iterative statement operators, etc. Value of property is of the form (KEYWORD . NAME), where NAME is the lowercase version of the operator, and KEYWORD is its type, e.g. FORWORD, IFWORD, RECORDWORD, etc. KEYWORD can also be the name of a function. When the atom appears as CAR of a form, the function is applied to the form and the result taken as the correct form. In this case, the function should either physically change the form, or call CLISPTRAN to store the translation. As an example, to make & be an infix character operator meaning OR, you could do the following: (PUTPROP '& 'CLISPTYPE (GETPROP 'OR 'CLISPTYPE)) (PUTPROP '& 'LISPFN 'OR) (PUTPROP '& 'BROADSCOPE T) (PUTPROP 'OR 'CLISPINFIX '&) (SETQ CLISPCHARS (CONS '& CLISPCHARS)) (SETQ CLISPCHARRAY (MAKEBITTABLE CLISPCHARS)) (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))",,,ll53HHT3(T2l3HH +T2HH2HH +-T,lH,HH +3(T3(T,/2Hll/HH,ll2HZZ,HH,HHF PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR,,-KKT3KT,5,CLASSIC +TITAN + HELVETICACLASSIC +CLASSIC +TITAN + HELVETICA HELVETICA +CLASSIC +MODERN +MODERNMODERN +MODERN +MODERNMODERN + HRULE.GETFNCLASSIC +   HRULE.GETFNCLASSIC +  +  HRULE.GETFNCLASSIC +  +  HRULE.GETFNCLASSIC +  HRULE.GETFNMODERN +n    T  O  M  3 [ '5  p   V i  +8    +  ) +J +   !     E      @    , 9  $  : -     "  8 D  + M      9 R      :      "       L ( >    !  (  > & ~  2  , ,P       1    G  ;       < \    ;   U      HRULE.GETFNMODERN +     . w p I      }         HRULE.GETFNMODERN + F   q       +/    ! +         +   +$IM.INDEX.GETFN   $IM.INDEX.GETFN  $IM.INDEX.GETFN  $IM.INDEX.GETFN   $IM.INDEX.GETFN     4 Q  +!     8   [          D  b  9   + D +  +6  +% 7  <  $IM.INDEX.GETFN  %IM.INDEX.GETFN  %IM.INDEX.GETFN  %IM.INDEX.GETFN  %IM.INDEX.GETFN w    .  "   1   F  $         5       - $IM.INDEX.GETFN        T   V      . Q    %IM.INDEX.GETFN o     >   : i      %IM.INDEX.GETFN        +  /   + 1    o  #     #   +           Q   <   / ~    -        \ J "       " X N =    V  f    '.           4       *  +k          )     A    Y           z      + ?    + B  +      B   .  (        .  @        %        k  +#  (                         HRULE.GETFNMODERN +  +  5  +        +    HRULE.GETFNMODERN +%  H  =   :             '      "   f !. !    +     !         !        !      ! "      !          !   | +  3  W    - 3  '   @Z  U ;   _                  +  E  1    HRULE.GETFNMODERN +      &4  > ?  G z ' )  $ M   +    V 3  .       R G   = * 9 b  h \ , + 2 $ )  m   @    <  5 ? '> u ]   $     f      '    HRULE.GETFNMODERN + @    +   +  n  I  -  m ( +E A     p   7 r    +      / z   ) +   HRULE.GETFNMODERN + +   + q  B    F ) T V /  D   4 )     y E       ! *   :    ,  h   c +  & 6     + +     +6      3 I  +^ z 9  +#    + -  0 8   $IM.INDEX.GETFN  ?  (       S    (  + Z  )      I  'IM.INDEX.GETFN   +  +  +    +  )  + 8 , ,IM.INDEX.GETFN  > ;   N *      !IM.INDEX.GETFN i Z  6 Y     *      ;  R           @ P  *IM.INDEX.GETFN   )IM.INDEX.GETFN  + 'IM.INDEX.GETFN  7  +  c +IM.INDEX.GETFN      -IM.INDEX.GETFN   6  .IM.INDEX.GETFN  7  3IM.INDEX.GETFN   .  + 'IM.INDEX.GETFN    +IM.INDEX.GETFN + e    HRULE.GETFNMODERN +  M  , P Y        \     4      _ 3  @  $ / 8 - "  Q   "  M ' 1    +  +        > !   /   t    0  + * E     +   #IM.INDEX.GETFN  $   7  0 ! .       k        +IM.INDEX.GETFN          O    .IM.INDEX.GETFN D   *    .    +IM.INDEX.GETFN   k  E   + 3  )IM.INDEX.GETFN %       '        +   !     %   o &  HRULE.GETFNMODERN +%IM.INDEX.GETFN  J '   x   :        +)IM.INDEX.GETFN O #  )IM.INDEX.GETFNMODERN +! +   - .IM.INDEX.GETFNTITAN + 7  +'IM.INDEX.GETFNTITAN + 1 +  +=  +&IM.INDEX.GETFN      + f  %IM.INDEX.GETFN  + %  I   +&IM.INDEX.GETFN     s  1 +   +IM.INDEX.GETFN$ ! P  +  .IM.INDEX.GETFNMODERN +     b  !  !  -  ! 7 !-     ,       +  C w   R   T %IM.INDEX.GETFNMODERN + P +  %IM.INDEX.GETFNMODERN + . E %IM.INDEX.GETFNMODERN +  F     +&IM.INDEX.GETFN     4 !      f E        '     HRULE.GETFNMODERN + ?  /   l  ,  +*  &IM.INDEX.GETFN #    _    R (  +    .       E M   r  $IM.INDEX.GETFN     +'IM.INDEX.GETFN  + B      + +1    #IM.INDEX.GETFN  `  +  %  8    "IM.INDEX.GETFN    +    +"    $              +    +'IM.INDEX.GETFN < x +     &IM.INDEX.GETFN D    H  /    + ?    (   1    ' . I z \ No newline at end of file diff --git a/docs/porter-irm/22-PERFORMANCE.TEDIT b/docs/porter-irm/22-PERFORMANCE.TEDIT new file mode 100644 index 00000000..5ee695e7 --- /dev/null +++ b/docs/porter-irm/22-PERFORMANCE.TEDIT @@ -0,0 +1,64 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 22. PERFORMANCE ISSUES 1 22. PERFORMANCE ISSUES 1 22. PERFORMANCE ISSUES 6 This chapter describes a number of areas that often contribute to performance problems in Interlisp-D programs. Many performance problems can be improved by optimizing the use of storage, since allocating and reclaiming large amounts of storage is expensive. Another tactic that can sometimes yield performance improvements is to change the use of variable bindings on the stack to reduce variable lookup time. There are a number of tools that can be used to determine which parts of a computation cause performance bottlenecks. Storage Allocation and Garbage Collection 1 As an Interlisp-D applications program runs, it creates data structures (allocated out of free storage space), manipulates them, and then discards them. If there were no way of reclaiming this space, over time the Interlisp-D memory (both the physical memory in the machine and the virtual memory stored on the disk) would fill up, and the computation would come to a halt. Actually, long before this could happen the system would probably become intolerably slow, due to "data fragmentation," which occurs when the data currently in use are spread over many virtual memory pages, so that most of the computer time must be spent swapping disk pages into physical memory. The problem of fragmentation will occur in any situation where the virtual memory is significantly larger than the real physical memory. To reduce swapping, it is desirable to keep the "working set" (the set of pages containing actively referenced data) as small as possible. It is possible to write programs that don't generate much "garbage" data, or which recycle data, but such programs tend to be overly complicated and difficult to debug. Spending effort writing such programs defeats the whole point of using a system with automatic storage allocation. An important part of any Lisp implementation is the "garbage collector" which identifies discarded data and reclaims its space. There are several well-known approaches to garbage collection. One method is the traditional mark-and-sweep garbage collection algorithm, which identifies "garbage" data by marking all accessible data structures, and then sweeping through the data spaces to find all unmarked objects (i.e., not referenced by any other object). Although this method is guaranteed to reclaim all garbage, it takes time proportional to the number of allocated objects, which may be very large. (Some allocated objects will have been marked during the "mark" phase, and the remainder will be collected during the "sweep" phase; so all will have to be touched in some way.) Also, the time that a mark-and-sweep garbage collection takes is independent of the amount of garbage collected; it is possible to sweep through the whole virtual memory, and only recover a small amount of garbage. For interactive applications, it is not acceptable to have long interruptions in a computation for the purpose of garbage collection. Interlisp-D solves this problem by using a reference-counting garbage collector. With this scheme, there is a table containing counts of how many times each object is referenced. This table is incrementally updated as pointers are created and discarded, incurring a small overhead distributed over the computation as a whole. (Note: References from the stack are not counted, but are handled separately at "sweep" time; thus the vast majority of data manipulations do not cause updates to this table.) At opportune moments, the garbage collector scans this table, and reclaims all objects that are no longer accessible (have a reference count of zero). The pause while objects are reclaimed is only the time for scanning the reference count tables (small) plus time proportional to the amount of garbage that has to be collected (typically less than a second). "Opportune" times occur when a certain number of cells have been allocated or when the system has been waiting for the user to type something for long enough. The frequency of garbage collection is controlled by the functions and variables described below. For the best system performance, it is desirable to adjust these parameters for frequent, short garbage collections, which will not interrupt interactive applications for very long, and which will have the added benefit of reducing data fragmentation, keeping the working set small. One problem with the Interlisp-D garbage collector is that not all garbage is guaranteed to be collected. Circular data structures, which point to themselves directly or indirectly, are never reclaimed, since their reference counts are always at least one. With time, this unreclaimable garbage may increase the working set to unacceptable levels. Some users have worked with the same Interlisp-D virtual memory for a very long time, but it is a good idea to occasionally save all of your functions in files, reinitialize Interlisp-D, and rebuild your system. Many users end their working day by issuing a command to rebuild their system and then leaving the machine to perform this task in their absence. If the system seems to be spending too much time swapping (an indication of fragmented working set), this procedure is definitely recommended. Garbage collection in Interlisp-D is controlled by the following functions and variables: (RECLAIM(RECLAIM (Function) NIL NIL NIL 2)) [Function] Initiates a garbage collection. Returns 0. (RECLAIMMIN(RECLAIMMIN (Function) NIL NIL NIL 2) N) [Function] Sets the frequency of garbage collection. Interlisp keeps track of the number of cells of any type that have been allocated; when it reaches a given number, a garbage collection occurs. If N is non-NIL, this number is set to N. Returns the current setting of the number. RECLAIMWAIT(RECLAIMWAIT (Variable) NIL NIL NIL 2) [Variable] Interlisp-D will invoke a RECLAIM if the system is idle and waiting for user input for RECLAIMWAIT seconds (currently set for 4 seconds). (GCGAG(GCGAG (Function) NIL NIL NIL 2) MESSAGE) [Function] Sets the behavior that occurs while a garbage collection is taking place. If MESSAGE is non-NIL, the cursor is complemented during a RECLAIM; if MESSAGE=NIL, nothing happens. The value of GCGAG is its previous setting. (GCTRP(GCGAG (Function) NIL NIL NIL 2)) [Function] Returns the number of cells until the next garbage collection, according to the RECLAIMMIN number. The amount of storage allocated to different data types, how much of that storage is in use, and the amount of data fragmentation can be determined using the following function: (STORAGE(STORAGE (Function) NIL NIL NIL 2) TYPES PAGETHRESHOLD) [Function] STORAGE prints out a summary, for each data type, of the amount of space allocated to the data type, and how much of that space is currently in use. If TYPES is non-NIL, STORAGE only lists statistics for the specified types. TYPES can be a litatom or a list of types. If PAGETHRESHOLD is non-NIL, then STORAGE only lists statistics for types that have at least PAGETHRESHOLD pages allocated to them. STORAGE prints out a table with the column headings Type, Assigned, Free Items, In use, and Total alloc. Type is the name of the data type. Assigned is how much of your virtual memory is set aside for items of this type. Currently, memory is allocated in quanta of two pages (1024 bytes). The numbers under Assigned show the number of pages and the total number of items that fit on those pages. Free Items shows how many items are available to be allocated (using the create construct, Chapter 8); these constitute the "free list" for that data type. In use shows how many items of this type are currently in use, i.e., have pointers to them and hence have not been garbage collected. If this number is higher than your program seems to warrant, you may want to look for storage leaks. The sum of Free Items and In use is always the same as the total Assigned items. Total alloc is the total number of items of this type that have ever been allocated (see BOXCOUNT, in the Performance Measuring section below). Note: The information about the number of items of type LISTP is only approximate, because list cells are allocated in a special way that precludes easy computation of the number of items per page. Note: When a data type is redeclared, the data type name is reassigned. Pages which were assigned to instances of the old data type are labeled **DEALLOC**. At the end of the table printout, STORAGE prints a "Data Spaces Summary" listing the number of pages allocated to the major data areas in the virtual address space: the space for fixed-length items (including datatypes), the space for variable-length items, and the space for litatoms. Variable-length data types such as arrays have fixed-length "headers," which is why they also appear in the printout of fixed-length data types. Thus, the line printed for the BITMAP data type says how many bitmaps have been allocated, but the "assigned pages" column counts only the headers, not the space used by the variable-length part of the bitmap. This summary also lists "Remaining Pages" in relation to the largest possible virtual memory, not the size of the virtual memory backing file in use. This file may fill up, causing a STORAGE FULL error, long before the "Remaining Pages" numbers reach zero. STORAGE also prints out information about the sizes of the entries on the variable-length data free list. The block sizes are broken down by the value of the variable STORAGE.ARRAYSIZES, initially (4 16 64 256 1024 4096 16384 NIL), which yields a printout of the form: variable-datum free list: le 4 26 items; 104 cells. le 16 72 items; 783 cells. le 64 36 items; 964 cells. le 256 28 items; 3155 cells. le 1024 3 items; 1175 cells. le 4096 5 items; 8303 cells. le 16384 3 items; 17067 cells. others 1 items; 17559 cells. This information can be useful in determining if the variable-length data space is fragmented. If most of the free space is composed of small items, then the allocator may not be able to find room for large items, and will extend the variable datum space. If this is extended too much, this could cause an ARRAYS FULL error, even if there is a lot of space left in little chunks. (STORAGE.LEFT(STORAGE.LEFT (Function) NIL NIL NIL 4)) [Function] Provides a programmatic way of determining how much storage is left in the major data areas in the virtual address space. Returns a list of the form (MDSFREE MDSFRAC 8MBFRAC ATOMFREE ATOMFRAC), where the elements are interpreted as follows: MDSFREE The number of free pages left in the main data space (which includes both fixed-length and variable-length data types). MDSFRAC The fraction of the total possible main data space that is free. 8MBFRAC The fraction of the total main data space that is free, relative to eight megabytes. This number is useful when using Interlisp-D on some early computers where the hardware limits the address space to eight megabytes. The function 32MBADDRESSABLE returns non-NIL if the currently running Interlisp-D system can use the full 32 megabyte address space. ATOMFREE The number of free pages left in the litatom space. ATOMFRAC The fraction of the total litatom space that is free. Note: Another important space resource is the amount of the virtual memory backing file in use (see VMEMSIZE, Chapter 12). The system will crash if the virtual memory file is full, even if the address space is not exhausted. Variable Bindings 1 Different implementations of lisp use different methods of accessing free variables. The binding of variables occurs when a function or a PROG is entered. For example, if the function FOO has the definition (LAMBDA (A B) BODY), the variables A and B are bound so that any reference to A or B from BODY or any function called from BODY will refer to the arguments to the function FOO and not to the value of A or B from a higher level function. All variable names (litatoms) have a top level value cell which is used if the variable has not been bound in any function. In discussions of variable access, it is useful to distinquish between three types of variable access: local, special and global. Local variable access is the use of a variable that is bound within the function from which it is used. Special variable access is the use of a variable that is bound by another function. Global variable access is the use of a variable that has not been bound in any function. We will often refer to a variable all of whose accesses are local as a "local variable." Similarly, a variable all of whose accesses are global we call a "global variable." In a "deep" bound system, a variable is bound by saving on the stack the variable's name together with a value cell which contains that variable's new value. When a variable is accessed, its value is found by searching the stack for the most recent binding (occurrence) and retrieving the value stored there. If the variable is not found on the stack, the variable's top level value cell is used. In a "shallow" bound system, a variable is bound by saving on the stack the variable name and the variable's old value and putting the new value in the variable's top level value cell. When a variable is accessed, its value is always found in its top level value cell. The deep binding scheme has one disadvantage: the amount of cpu time required to fetch the value of a variable depends on the stack distance between its use and its binding. The compiler can determine local variable accesses and compiles them as fetches directly from the stack. Thus this computation cost only arises in the use of variable not bound in the local frame ("free" variables). The process of finding the value of a free variable is called free variable lookup. In a shallow bound system, the amount of cpu time required to fetch the value of a variable is constant regardless of whether the variable is local, special or global. The disadvantages of this scheme are that the actual binding of a variable takes longer (thus slowing down function call), the cells that contain the current in use values are spread throughout the space of all litatom value cells (thus increasing the working set size of functions) and context switching between processes requires unwinding and rewinding the stack (thus effectively prohibiting the use of context switching for many applications). Interlisp-D uses deep binding, because of the working set considerations and the speed of context switching. The free variable lookup routine is microcoded, thus greatly reducing the search time. In benchmarks, the largest percentage of free variable lookup time was 20 percent of the total ellapsed time; the normal time was between 5 and 10 percent. One consequence of Interlisp-D's deep binding scheme is that users may significantly improve performance by declaring global variables in certain situations. If a variable is declared global, the compiler will compile an access to that variable as a retrieval of its top level value, completely bypassing a stack search. This should be done only for variables that are never bound in functions, such as global databases and flags. Global variable declarations should be done using the GLOBALVARS file package command (Chapter 17). Its form is (GLOBALVARS VAR1 ... VARN). Another way of improving performance is to declare variables as local within a function. Normally, all variables bound within a function have their names put on the stack, and these names are scanned during free variable lookup. If a variable is declared to be local within a function, its name is not put on the stack, so it is not scanned during free variable lookup, which may increase the speed of lookups. The compiler can also make some other optimizations if a variable is known to be local to a function. A variable may be declared as local within a function by including the form (DECLARE (LOCALVARS VAR1 ... VARN)) following the argument list in the definition of the function. Local variable declarations only effect the compilation of a function. Interpreted functions put all of their variable names on the stack, regardless of any declarations. Performance Measuring 1 This section describes functions that gather and display statistics about a computation, such as as the elapsed time, and the number of data objects of different types allocated. TIMEALL and TIME gather statistics on the evaluation of a specified form. BREAKDOWN gathers statistics on individual functions called during a computation. These functions can be used to determine which parts of a computation are consuming the most resources (time, storage, etc.), and could most profitably be improved. (TIMEALL(TIMEALL (Function) NIL NIL NIL 5) TIMEFORM NUMBEROFTIMES TIMEWHAT INTERPFLG %) [NLambda Function] Evaluates the form TIMEFORM and prints statistics on time spent in various categories (elapsed, keyboard wait, swapping time, gc) and data type allocation. For more accurate measurement on small computations, NUMBEROFTIMES may be specified (its default is 1) to cause TIMEFORM to be executed NUMBEROFTIMES times. To improve the accuracy of timing open-coded operations in this case, TIMEALL compiles a form to execute TIMEFORM NUMBEROFTIMES times (unless INTERPFLG is non-NIL), and then times the execution of the compiled form. Note: If TIMEALL is called with NUMBEROFTIMES>1, the dummy form is compiled with compiler optimizations on. This means that it is not meaningful to use TIMEALL with very simple forms that are optimized out by the compiler. For example, (TIMEALL '(IPLUS 2 3) 1000) will time a compiled function which simply returns the number 5, since (IPLUS 2 3) is optimized to the integer 5. TIMEWHAT restricts the statistics to specific categories. It can be an atom or list of datatypes to monitor, and/or the atom TIME to monitor time spent. Note that ordinarily, TIMEALL monitors all time and datatype usage, so this argument is rarely needed. TIMEALL returns the value of the last evaluation of TIMEFORM. (TIME(TIME (Function) NIL NIL NIL 6) TIMEX TIMEN TIMETYP) [NLambda Function] TIME evaluates the form TIMEX, and prints out the number of CONS cells allocated and computation time. Garbage collection time is subtracted out. This function has been largely replaced by TIMEALL. If TIMEN is greater than 1, TIMEX is executed TIMEN times, and TIME prints out (number of conses)/TIMEN, and (computation time)/TIMEN. If TIMEN=NIL, it defaults to 1. This is useful for more accurate measurement on small computations. If TIMETYP is 0, TIME measures and prints total real time as well as computation time. If TIMETYP = 3, TIME measures and prints garbage collection time as well as computation time. If TIMETYP=T, TIME measures and prints the number of pagefaults. TIME returns the value of the last evaluation of TIMEX. (BOXCOUNT(BOXCOUNT (Function) NIL NIL NIL 6) TYPE N) [Function] Returns the number of data objects of type TYPE allocated since this Interlisp system was created. TYPE can be any data type name (see TYPENAME, Chapter 8). If TYPE is NIL, it defaults to FIXP. If N is non-NIL, the corresponding counter is reset to N. (CONSCOUNT(CONSCOUNT (Function) NIL NIL NIL 6) N) [Function] Returns the number of CONS cells allocated since this Interlisp system was created. If N is non-NIL, resets the counter to N. Equivalent to (BOXCOUNT 'LISTP N). (PAGEFAULTS(PAGEFAULTS (Function) NIL NIL NIL 6)) [Function] Returns the number of page faults since this Interlisp system was created. BREAKDOWN 1 TIMEALL collects statistics for whole computations. BREAKDOWN is available to analyze the breakdown of computation time (or any other measureable quantity) function by function. (BREAKDOWN FN1 ... FNN) [NLambda NoSpread Function] The user calls BREAKDOWN giving it a list of function names (unevaluated). These functions are modified so that they keep track of various statistics. To remove functions from those being monitored, simply UNBREAK (Chapter 15) the functions, thereby restoring them to their original state. To add functions, call BREAKDOWN on the new functions. This will not reset the counters for any functions not on the new list. However (BREAKDOWN) will zero the counters of all functions being monitored. The procedure used for measuring is such that if one function calls other and both are "broken down", then the time (or whatever quantity is being measured) spent in the inner function is not charged to the outer function as well. Note: BREAKDOWN will not give accurate results if a function being measured is not returned from normally, e.g., a lower RETFROM (or ERROR) bypasses it. In this case, all of the time (or whatever quantity is being measured) between the time that function is entered and the time the next function being measured is entered will be charged to the first function. (BRKDWNRESULTS(BRKDWNRESULTS (Function) NIL NIL NIL 7) RETURNVALUESFLG) [Function] BRKDWNRESULTS prints the analysis of the statistics requested as well as the number of calls to each function. If RETURNVALUESFLG is non-NIL, BRKDWNRESULTS will not to print the results, but instead return them in the form of a list of elements of the form (FNNAME #CALLS VALUE). Example: (BREAKDOWN SUPERPRINT SUBPRINT COMMENT1) (SUPERPRINT SUBPRINT COMMENT1) (PRETTYDEF '(SUPERPRINT) 'FOO) FOO.;3 (BRKDWNRESULTS) FUNCTIONS TIME #CALLS PER CALL % SUPERPRINT 8.261 365 0.023 20 SUBPRINT 31.910 141 0.226 76 COMMENT1 1.612 8 0.201 4 TOTAL 41.783 514 0.081 NIL (BRKDWNRESULTS T) ((SUPERPRINT 365 8261) (SUBPRINT 141 31910) (COMMENT1 8 1612)) BREAKDOWN can be used to measure other statistics, by setting the following variables: BRKDWNTYPE(BRKDWNTYPE (Variable) NIL NIL NIL 7) [Variable] To use BREAKDOWN to measure other statistics, before calling BREAKDOWN, set the variable BRKDWNTYPE to the quantity of interest, e.g., TIME, CONSES, etc, or a list of such quantities. Whenever BREAKDOWN is called with BRKDWNTYPE not NIL, BREAKDOWN performs the necessary changes to its internal state to conform to the new analysis. In particular, if this is the first time an analysis is being run with a particular statistic, a measuring function will be defined, and the compiler will be called to compile it. The functions being broken down will be redefined to call this measuring function. When BREAKDOWN is through initializing, it sets BRKDWNTYPE back to NIL. Subsequent calls to BREAKDOWN will measure the new statistic until BRKDWNTYPE is again set and a new BREAKDOWN performed. BRKDWNTYPES(BRKDWNTYPES (Variable) NIL NIL NIL 8) [Variable] The list BRKDWNTYPES contains the information used to analyze new statistics. Each entry on BRKDWNTYPES should be of the form (TYPE FORM FUNCTION), where TYPE is a statistic name (as would appear in BRKDWNTYPE), FORM computes the statistic, and FUNCTION (optional) converts the value of form to some more interesting quantity. For example, (TIME (CLOCK 2) (LAMBDA (X) (FQUOTIENT X 1000))) measures computation time and reports the result in seconds instead of milliseconds. BRKDWNTYPES currently contains entries for TIME, CONSES, PAGEFAULTS, BOXES, and FBOXES. Example: (SETQ BRKDWNTYPE '(TIME CONSES)) (TIME CONSES) (BREAKDOWN MATCH CONSTRUCT) (MATCH CONSTRUCT) (FLIP '(A B C D E F G H C Z) '(.. $1 .. #2 ..) '(.. #3 ..)) (A B D E F G H Z) (BRKDWNRESULTS) FUNCTIONS TIME #CALLS PER CALL % MATCH 0.036 1 0.036 54 CONSTRUCT 0.031 1 0.031 46 TOTAL 0.067 2 0.033 FUNCTIONS CONSES #CALLS PER CALL % MATCH 32 1 32.000 40 CONSTRUCT 49 1 49.000 60 TOTAL 81 2 40.500 NIL Occasionally, a function being analyzed is sufficiently fast that the overhead involved in measuring it obscures the actual time spent in the function. If the user were using TIME, he would specify a value for TIMEN greater than 1 to give greater accuracy. A similar option is available for BREAKDOWN. The user can specify that a function(s) be executed a multiple number of times for each measurement, and the average value reported, by including a number in the list of functions given to BREAKDOWN, e.g., BREAKDOWN(EDITCOM EDIT4F 10 EDIT4E EQP) means normal breakdown for EDITCOM and EDIT4F but executes (the body of) EDIT4E and EQP 10 times each time they are called. Of course, the functions so measured must not cause any harmful side effects, since they are executed more than once for each call. The printout from BRKDWNRESULTS will look the same as though each function were run only once, except that the measurement will be more accurate. Another way of obtaining more accurate measurement is to expand the call to the measuring function in-line. If the value of BRKDWNCOMPFLG is non-NIL (initially NIL), then whenever a function is broken-down, it will be redefined to call the measuring function, and then recompiled. The measuring function is expanded in-line via an appropriate macro. In addition, whenever BRKDWNTYPE is reset, the compiler is called for all functions for which BRKDWNCOMPFLG was set at the time they were originally broken-down, i.e. the setting of the flag at the time a function is broken-down determines whether the call to the measuring code is compiled in-line. GAINSPACE 1 If you have large programs and databases, you may sometimes find yourself in a situation where you need to obtain more space, and are willing to pay the price of eliminating some or all of the context information that the various user-assistance facilities such as the programmer's assistant, file package, CLISP, etc., have accumulated during the course of his session. The function GAINSPACE provides an easy way to selectively throw away accumulated data: (GAINSPACE(GAINSPACE (Function) NIL NIL NIL 9)) [Function] Prints a list of deletable objects, allowing the user to specify at each point what should be discarded and what should be retained. For example: (GAINSPACE) purge history lists ? Yes purge everything, or just the properties, e.g., SIDE, LISPXPRINT, etc. ? just the properties discard definitions on property lists ? Yes discard old values of variables ? Yes erase properties ? No erase CLISP translations? Yes GAINSPACE is driven by the list GAINSPACEFORMS. Each element on GAINSPACEFORMS is of the form (PRECHECK MESSAGE FORM KEYLST). If PRECHECK, when evaluated, returns NIL, GAINSPACE skips to the next entry. For example, you will not be asked whether or not to purge the history list if it is not enabled. Otherwise, ASKUSER (Chapter 26) is called with the indicated MESSAGE and the (optional) KEYLST. If the user responds No, i.e., ASKUSER returns N, GAINSPACE skips to the next entry. Otherwise, FORM is evaluated with the variable RESPONSE bound to the value of ASKUSER. In the above example, the FORM for the "purge history lists" question calls ASKUSER to ask "purge everything, ..." only if you had responded Yes. If you had responded with Everything, the second question would not have been asked. The "erase properties" question is driven by a list SMASHPROPSMENU. Each element on this list is of the form (MESSAGE . PROPS). You are prompted with MESSAGE (by ASKUSER), and if your response is Yes, PROPS is added to the list SMASHPROPS. The "discard definitions on property lists" and "discard old values of variables" questions also add to SMASHPROPS. You will not be prompted for any entry on SMASHPROPSMENU for which all of the corresponding properties are already on SMASHPROPS. SMASHPROPS is initially set to the value of SMASHPROPSLST. This permits you to specify in advance those properties which you always want discarded, and not be asked about them subsequently. After finishing all the entries on GAINSPACEFORMS, GAINSPACE checks to see if the value of SMASHPROPS is non-NIL, and if so, does a MAPATOMS, i.e., looks at every atom in the system, and erases the indicated properties. You can change or add new entries to GAINSPACEFORMS or SMASHPROPSMENU, so that GAINSPACE can also be used to purge structures that your programs have accumulated. Using Data Types Instead of Records 1 If a program uses large numbers of large data structures, there are several advantages to representing them as user data types rather than as list structures. The primary advantage is increased speed: accessing and setting the fields of a data type can be significantly faster than walking through a list with repeated CARs and CDRs. Also, compiled code for referencing data types is usually smaller. Finally, by reducing the number of objects created (one object against many list cells), this can reduce the expense of garbage collection. User data types are declared by using the DATATYPE record type (Chapter 8). If a list structure has been defined using the RECORD record type (Chapter 8), and all accessing operations are written using the record package's fetch, replace, and create operations, changing from RECORDs to DATATYPEs only requires editing the record declaration (using EDITREC, Chapter 8) to replace declaration type RECORD by DATATYPE, and recompiling. Note: There are some minor disadvantages with allocating new data types: First, there is an upper limit on the number of data types which can exist. Also, space for data types is allocated a page at a time, so each data type has at least one page assigned to it, which may be wasteful of space if there are only a few examples of a given data type. These problems should not effect most applications programs. Using Incomplete File Names 1 Currently, Interlisp allows you to specify an open file by giving the file name. If the file name is incomplete (it doesn't have the device/host, directory, name, extension, and version number all supplied), the system converts it to a complete file name, by supplying defaults and searching through directories (which may be on remote file servers), and then searches the open streams for one corresponding to that file name. This file name-completion process happens whenever any I/O function is given an incomplete file name, which can cause a serious performance problem if I/O operations are done repeatedly. In general, it is much faster to convert an incomplete file name to a stream once, and use the stream from then on. For example, suppose a file is opened with (SETQ STRM (OPENSTREAM 'MYNAME 'INPUT)). After doing this, (READC 'MYNAME) and (READC STRM) would both work, but (READC 'MYNAME) would take longer (sometimes orders of magnitude longer). This could seriously effect the performance if a program which is doing many I/O operations. Note: At some point in the future, when multiple streams are supported to a single file, the feature of mapping file names to streams will be removed. This is yet another reason why programs should use streams as handles to open files, instead of file names. For more information on efficiency considerations when using files, see Chapter 24. Using "Fast" and "Destructive" Functions 1 Among the functions used for manipulating objects of various data types, there are a number of functions which have "fast" and "destructive" versions. The user should be aware of what these functions do, and when they should be used. "Fast" functions: By convention, a function named by prefixing an existing function name with F indicates that the new function is a "fast" version of the old. These usually have the same definitions as the slower versions, but they compile open and run without any "safety" error checks. For example, FNTH runs faster than NTH, however, it does not make as many checks (for lists ending with anything but NIL, etc). If these functions are given arguments that are not in the form that they expect, their behavior is unpredictable; they may run forever, or cause a system error. In general, the user should only use "fast" functions in code that has already been completely debugged, to speed it up. "Destructive" functions: By convention, a function named by prefixing an existing function with D indicates the new function is a "destructive" version of the old one, which does not make any new structure but cannibalizes its argument(s). For example, REMOVE returns a copy of a list with a particular element removed, but DREMOVE actually changes the list structure of the list. (Unfortunately, not all destructive functions follow this naming convention: the destructive version of APPEND is NCONC.) The user should be careful when using destructive functions that they do not inadvertantly change data structures. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "22-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "22-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "22-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "22-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "22-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "22-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))-HH +T-HHT5,,3HZ +T-llT2l,,3HZ +T,ll3(T,HH +,HH,HH-T3(T,/F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR2 TITAN +CLASSIC +CLASSIC +CLASSIC + HELVETICA HELVETICA HELVETICA +CLASSIC +MODERN +MODERNMODERN + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + + + HRULE.GETFNMODERN + * HRULE.GETFNMODERN + VZ "IM.INDEX.GETFN  +  %IM.INDEX.GETFNMODERN +  -  &IM.INDEX.GETFNMODERN +  6 '   IM.INDEX.GETFN  N&!   IM.INDEX.GETFN  P +   "IM.INDEX.GETFN  1* 4 - +  R +?N +!  N/9  "f >  !'######## 4 >  'IM.INDEX.GETFNMODERN +  1yBV Y57ev  HRULE.GETFN HELVETICA+ $-jb6 +1 L +  HRULE.GETFNMODERN + F +v "IM.INDEX.GETFNTITAN +V &  5 . O  6  lNH   v/L -  IM.INDEX.GETFN      Y 'N/ -  #IM.INDEX.GETFNMODERN +  +5 (  +$IM.INDEX.GETFN  >  %IM.INDEX.GETFN  J  + HRULE.GETFNMODERN + . u +    7e i : *  a  (IM.INDEX.GETFN  f f  +)     ) ) ) ) "   ?  M +%IM.INDEX.GETFN   -  +$/  + e " +  & +    &IM.INDEX.GETFNMODERN +   I ) +X0W   +  +!    <   ( ( ( # ( ( ( #  M  ' s }   +&  + HRULE.GETFNMODERN +  B +$IM.INDEX.GETFN   +   I  , &     +  &  +  +1, +% +-> + +"   +P% + K $ HRULE.GETFNMODERN + @ *J^6)  HRULE.GETFNMODERN + ( T ) HRULE.GETFNMODERN + _O&aAwx z \ No newline at end of file diff --git a/docs/porter-irm/23-PROCESSES.TEDIT b/docs/porter-irm/23-PROCESSES.TEDIT new file mode 100644 index 00000000..2ecba137 Binary files /dev/null and b/docs/porter-irm/23-PROCESSES.TEDIT differ diff --git a/docs/porter-irm/24-STREAMS.TEDIT b/docs/porter-irm/24-STREAMS.TEDIT new file mode 100644 index 00000000..f1566124 Binary files /dev/null and b/docs/porter-irm/24-STREAMS.TEDIT differ diff --git a/docs/porter-irm/25-IO.TEDIT b/docs/porter-irm/25-IO.TEDIT new file mode 100644 index 00000000..3b0d20c4 --- /dev/null +++ b/docs/porter-irm/25-IO.TEDIT @@ -0,0 +1,2188 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 25. INPUT/OUTPUT FUNCTIONS 1 25. INPUT/OUTPUT FUNCTIONS 1 25. INPUT/OUTPUT FUNCTIONS 6 This chapter describes the standard I/O functions used for reading and printing characters and Interlisp expressions on files and other streams. First, the primitive input functions are presented, then the output functions, then functions for random-access operations (such as searching a file for a given stream, or changing the "next-character" pointer to a position in a file). Next, the PRINTOUT statement is documented (see below), which provides an easy way to write complex output operations. Finally, read tables, used to parse characters as Interlisp expressions, are documented. Specifying Streams for Input/Output Functions(INPUT/OUTPUT% FUNCTIONS NIL Input/Output% Functions NIL NIL 1 SUBNAME SPECIFYING% STREAMS% FOR SUBTEXT specifying% streams% for) 1 Most of the input/output functions in Interlisp-D have an argument named STREAM or FILE, specifying on which open stream the function's action should occur (the name FILE is used in older functions that predate the concept of stream; the two should, however, be treated synonymously). The value of this argument should be one of the following: a stream An object of type STREAM, as returned by OPENSTREAM (Chapter 24) or other stream-producing functions, is always the most precise and efficient way to designate a stream argument. T The litatom T designates the terminal input or output stream of the currently running process, controlling input from the keyboard and output to the display screen. For functions where the direction (input or output) is ambiguous, T is taken to designate the terminal output stream. The T streams are always open; they cannot be closed. The terminal output stream can be set to a given window or display stream by using TTYDISPLAYSTREAM (Chapter 28). The terminal input stream cannot be changed. For more information on terminal I/O, see Chapter 30. NIL The litatom NIL designates the "primary" input or output stream. These streams are initially the same as the terminal input/output streams, but they can be changed by using the functions INPUT and OUTPUT. For functions where the direction (input or output) is ambiguous, e.g., GETFILEPTR, the argument NIL is taken to mean the primary input stream, if that stream is not identical to the terminal input stream, else the primary output stream. a window Uses the display stream of the window . Valid for output only. a file name As of this writing, the name of an open file (as a litatom) can be used as a stream argument. However, there are inefficiencies and possible future incompatibilities associated with doing so. See Chapter 24 for details. (GETSTREAM(GETSTREAM (Function) NIL NIL NIL 1) FILE ACCESS) [Function] Coerces the argument FILE to a stream by the above rules. If ACCESS is INPUT, OUTPUT, or BOTH, produces the stream designated by FILE that is open for ACCESS. If ACCESS=NIL, returns a stream for FILE open for any kind of input/output (see the list above for the ambiguous cases). If FILE does not designate a stream open in the specified mode, causes an error, FILE NOT OPEN. (STREAMP(STREAMP (Function) NIL NIL NIL 2) X) [Function] Returns X if X is a STREAM, otherwise NIL. Input Functions 1 While the functions described below can take input from any stream, some special actions occur when the input is from the terminal (the T input stream, see above). When reading from the terminal, the input is buffered a line at a time, unless buffering has been inhibited by CONTROL (Chapter 30) or the input is being read by READC or PEEKC. Using specified editing characters, the user can erase a character at a time, a word at a time, or the whole line. The keys that perform these editing functions are assignable via SETSYNTAX, with the initial settings chosen to be those most natural for the given operating system. In Interlisp-D, the initial settings are as follows: characters are deleted one at a time by Backspace; words are erased by control-W; the whole line is erased by Control-Q. On the Interlisp-D display, deleting a character or a line causes the characters to be physically erased from the screen. In Interlisp-10, the deleting action can be modified for various types of display terminals by using DELETECONTROL (Chapter 30). Unless otherwise indicated, when the end of file is encountered while reading from a file, all input functions generate an error, END OF FILE. Note that this does not close the input file. The ENDOFSTREAMOP stream attribute (Chapter 24) is useful for changing the behavior at end of file. Most input functions have a RDTBL argument, which specifies the read table to be used for input. Unless otherwise specified, if RDTBL is NIL, the primary read table is used. If the FILE or STREAM argument to an input function is NIL, the primary input stream is used. (INPUT(INPUT (Function) NIL NIL NIL 2) FILE) [Function] Sets FILE as the primary input stream; returns the old primary input stream. FILE must be open for input. (INPUT) returns the current primary input stream, which is not changed. Note: If the primary input stream is set to a file, the file's full name, rather than the stream itself, is returned. See discussion in Chapter 24. (READ(READ (Function) NIL NIL NIL 2) FILE RDTBL FLG) [Function] Reads one expression from FILE. Atoms are delimited by the break and separator characters as defined in RDTBL. To include a break or separator character in an atom, the character must be preceded by the character %, e.g., AB%(C is the atom AB(C, %% is the atom %, %control-K is the atom Control-K. For input from the terminal, an atom containing an interrupt character can be input by typing instead the corresponding alphabetic character preceded by Control-V, e.g., ^VD for Control-D. Strings are delimited by double quotes. To input a string containing a double quote or a %, precede it by %, e.g., "AB%"C" is the string AB"C. Note that % can always be typed even if next character is not "special", e.g., %A%B%C is read as ABC. If an atom is interpretable as a number, READ creates a number, e.g., 1E3 reads as a floating point number, 1D3 as a literal atom, 1.0 as a number, 1,0 as a literal atom, etc. An integer can be input in a non-decimal radix by using syntax such as 123Q, |b10101, |5r1234 (see Chapter 7). The function RADIX, sets the radix used to print integers. When reading from the terminal, all input is line-buffered to enable the action of the backspacing control characters, unless inhibited by CONTROL (Chapter 30). Thus no characters are actually seen by the program until a carriage-return (actually the character with terminal syntax class EOL, see Chapter 30), is typed. However, for reading by READ, when a matching right parenthesis is encountered, the effect is the same as though a carriage-return were typed, i.e., the characters are transmitted. To indicate this, Interlisp also prints a carriage-return line-feed on the terminal. The line buffer is also transmitted to READ whenever an IMMEDIATE read macro character is typed (see below). FLG=T suppresses the carriage-return normally typed by READ following a matching right parenthesis. (However, the characters are still given to READ; i.e., the user does not have to type the carriage-return.) (RATOM FILE RDTBL) [Function] Reads in one atom from FILE. Separation of atoms is defined by RDTBL. % is also defined for RATOM, and the remarks concerning line-buffering and editing control characters also apply. If the characters comprising the atom would normally be interpreted as a number by READ, that number is returned by RATOM. Note however that RATOM takes no special action for " whether or not it is a break character, i.e., RATOM never makes a string. (RSTRING(RSTRING (Function) NIL NIL NIL 3) FILE RDTBL) [Function] Reads characters from FILE up to, but not including, the next break or separator character, and returns them as a string. Backspace, Control-W, Control-Q, Control-V, and % have the same effect as with READ. Note that the break or separator character that terminates a call to RATOM or RSTRING is not read by that call, but remains in the buffer to become the first character seen by the next reading function that is called. If that function is RSTRING, it will return the null string. This is a common source of program bugs. (RATOMS(RATOMS (Function) NIL NIL NIL 3) A FILE RDTBL) [Function] Calls RATOM repeatedly until the atom A is read. Returns a list of the atoms read, not including A. (RATEST(RATEST (Function) NIL NIL NIL 3) FLG) [Function] If FLG = T, RATEST returns T if a separator was encountered immediately prior to the atom returned by the last RATOM or READ, NIL otherwise. If FLG = NIL, RATEST returns T if last atom read by RATOM or READ was a break character, NIL otherwise. If FLG = 1, RATEST returns T if last atom read (by READ or RATOM) contained a % used to quote the next character (as in %[ or %A%B%C), NIL otherwise. (READC (READC% (Function) NIL NIL NIL 3)FILE RDTBL) [Function] Reads and returns the next character, including %, ", etc, i.e., is not affected by break or separator characters. The action of READC is subject to line-buffering, i.e., READC does not return a value until the line has been terminated even if a character has been typed. Thus, the editing control characters have their usual effect. RDTBL does not directly affect the value returned, but is used as usual in line-buffering, e.g., determining when input has been terminated. If (CONTROL T) has been executed (Chapter 30), defeating line-buffering, the RDTBL argument is irrelevant, and READC returns a value as soon as a character is typed (even if the character typed is one of the editing characters, which ordinarily would never be seen in the input buffer). (PEEKC (PEEKC% (Function) NIL NIL NIL 4)FILE %) [Function] Returns the next character, but does not actually read it and remove it from the buffer. If reading from the terminal, the character is echoed as soon as PEEKC reads it, even though it is then "put back" into the system buffer, where Backspace, Control-W, etc. could change it. Thus it is possible for the value returned by PEEKC to "disagree" in the first character with a subsequent READ. (LASTC(LASTC (Function) NIL NIL NIL 4) FILE) [Function] Returns the last character read from FILE. (READCCODE(READCCODE (Function) NIL NIL NIL 4) FILE RDTBL) [Function] Returns the next character code from STREAM; thus, this operation is equivalent to, but more efficient than, (CHCON1 (READC FILE RDTBL)). (PEEKCCODE(PEEKCCODE (Function) NIL NIL NIL 4) FILE %) [Function] Returns, without consuming, the next character code from STREAM; thus, this operation is equivalent to, but more efficient than, (CHCON1 (PEEKC FILE)). (BIN(BIN (Function) NIL NIL NIL 4) STREAM) [Function] Returns the next byte from STREAM. This operation is useful for reading streams of binary, rather than character, data. Note: BIN is similar to READCCODE, except that BIN always reads a single byte, whereas READCCODE reads a "character" that can consist of more than one byte, depending on the character and its encoding. READ, RATOM, RATOMS, PEEKC, READC all wait for input if there is none. The only way to test whether or not there is input is to use READP: (READP(READP (Function) NIL NIL NIL 4) FILE FLG) [Function] Returns T if there is anything in the input buffer of FILE, NIL otherwise. This operation is only interesting for streams whose source of data is dynamic, e.g., the terminal or a byte stream over a network; for other streams, such as to files, (READP FILE) is equivalent to (NOT (EOFP FILE)). Note that because of line-buffering, READP may return T, indicating there is input in the buffer, but READ may still have to wait. Frequently, the terminal's input buffer contains a single EOL character left over from a previous input. For most applications, this situation wants to be treated as though the buffer were empty, and so READP returns NIL in this case. However, if FLG=T, READP returns T if there is any character in the input buffer, including a single EOL. FLG is ignored for streams other than the terminal. (EOFP(EOFP (Function) NIL NIL NIL 5) FILE) [Function] Returns true if FILE is at "end of file", i.e., the next call to an input function would cause an END OF FILE error; NIL otherwise. For randomly accessible files, this can also be thought of as the file pointer pointing beyond the last byte of the file. FILE must be open for (at least) input, or an error is generated, FILE NOT OPEN. Note that EOFP can return NIL and yet the next call to READ might still cause an END OF FILE error, because the only characters remaining in the input were separators or otherwise constituted an incomplete expression. The function SKIPSEPRS is sometimes more useful as a way of detecting end of file when it is known that all the expressions in the file are well formed. (WAITFORINPUT (WAITFORINPUT% (Function) NIL NIL NIL 5)FILE) [Function] Waits until input is available from FILE or from the terminal, i.e. from T. WAITFORINPUT is functionally equivalent to (until (OR (READP T) (READP FILE)) do NIL), except that it does not use up machine cycles while waiting. Returns the device for which input is now available, i.e. FILE or T. FILE can also be an integer, in which case WAITFORINPUT waits until there is input available from the terminal, or until FILE milliseconds have elapsed. Value is T if input is now available, NIL in the case that WAITFORINPUT timed out. (SKREAD(SKREAD (Function) NIL NIL NIL 5) FILE REREADSTRING RDTBL) [Function] "Skip Read". SKREAD consumes characters from FILE as if one call to READ had been performed, without paying the storage and compute cost to really read in the structure. REREADSTRING is for the case where the caller has already performed some READC's and RATOM's before deciding to skip this expression. In this case, REREADSTRING should be the material already read (as a string), and SKREAD operates as though it had seen that material first, thus setting up its parenthesis count, double-quote count, etc. The read table RDTBL is used for reading from FILE. If RDTBL is NIL, it defaults to the value of FILERDTBL. SKREAD may have difficulties if unusual read macros are defined in RDTBL. SKREAD does not recognize read macro characters in REREADSTRING, nor SPLICE or INFIX read macros. This is only a problem if the read macros are defined to parse subsequent input in the stream that does not follow the normal parenthesis and string-quote conventions. SKREAD returns %) if the read terminated on an unbalanced closing parenthesis; %] if the read terminated on an unbalanced %], i.e., one which also would have closed any extant open left parentheses; otherwise NIL. (SKIPSEPRS(SKIPSEPRS (Function) NIL NIL NIL 5) FILE RDTBL) [Function] Consumes characters from FILE until it encounters a non-separator character (as defined by RDTBL). SKIPSEPRS returns, but does not consume, the terminating character, so that the next call to READC would return the same character. If no non-separator character is found before the end of file is reached, SKIPSEPRS returns NIL and leaves the stream at end of file. This function is useful for skipping over "white space" when scanning a stream character by character, or for detecting end of file when reading expressions from a stream with no pre-arranged terminating expression. Output Functions(OUTPUT% FUNCTIONS NIL Output% Functions NIL NIL 6) 1 Unless otherwise specified by DEFPRINT, pointers other than lists, strings, atoms, or numbers, are printed in the form {DATATYPE} followed by the octal representation of the address of the pointer (regardless of radix). For example, an array pointer might print as {ARRAYP}#43,2760. This printed representation is for compactness of display on the user's terminal, and will not read back in correctly; if the form above is read, it will produce the litatom {ARRAYP}#43,2760. Note: The term "end-of-line" appearing in the description of an output function means the character or characters used to terminate a line in the file system being used by the given implementation of Interlisp. For example, in Interlisp-D end-of-line is indicated by the character carriage-return. Some of the functions described below have a RDTBL argument, which specifies the read table to be used for output. If RDTBL is NIL, the primary read table is used. Most of the functions described below have an argument FILE, which specifies the stream on which the operation is to take place. If FILE is NIL, the primary output stream is used . (OUTPUT(OUTPUT (Function) NIL NIL NIL 6) FILE) [Function] Sets FILE as the primary output stream; returns the old primary output stream. FILE must be open for output. (OUTPUT) returns the current primary output stream, which is not changed. Note: If the primary output stream is set to a file, the file's full name, rather than the stream itself, is returned. See the discussion in Chapter 24. (PRIN1(PRIN1 (Function) NIL NIL NIL 6) X FILE) [Function] Prints X on FILE. (PRIN2(PRIN2 (Function) NIL NIL NIL 6) X FILE RDTBL) [Function] Prints X on FILE with %'s and "'s inserted where required for it to read back in properly by READ, using RDTBL. Both PRIN1 and PRIN2 print any kind of Lisp expression, including lists, atoms, numbers, and strings. PRIN1 is generally used for printing expressions where human readability, rather than machine readability, is important, e.g., when printing text rather than program fragments. PRIN1 does not print double quotes around strings, or % in front of special characters. PRIN2 is used for printing Interlisp expressions which can then be read back into Interlisp with READ; i.e., break and separator characters in atoms will be preceded by %'s. For example, the atom "()" is printed as %(%) by PRIN2. If the integer output radix (as set by RADIX) is not 10, PRIN2 prints the integer using the input syntax for non-decimal integers (see Chapter 7) but PRIN1 does not (but both print the integer in the output radix). (PRIN3(PRIN3 (Function) NIL NIL NIL 6) X FILE) [Function] (PRIN4(PRIN4 (Function) NIL NIL NIL 6) X FILE RDTBL) [Function] PRIN3 and PRIN4 are the same as PRIN1 and PRIN2 respectively, except that they do not increment the horizontal position counter nor perform any linelength checks. They are useful primarily for printing control characters. (PRINT(PRINT (Function) NIL NIL NIL 6) X FILE RDTBL) [Function] Prints the expression X using PRIN2 followed by an end-of-line. Returns X. (PRINTCCODE(PRINT (Function) NIL NIL NIL 7) CHARCODE FILE) [Function] Outputs a single character whose code is CHARCODE to FILE. This is similar to (PRIN1 (CHARACTER CHARCODE)), except that numeric characters are guaranteed to print "correctly"; e.g., (PRINTCCODE (CHARCODE 9)) always prints "9", independent of the setting of RADIX. PRINTCCODE may actually print more than one byte on FILE, due to character encoding and end of line conventions; thus, no assumptions should be made about the relative motion of the file pointer (see GETFILEPTR) during this operation. (BOUT(BOUT (Function) NIL NIL NIL 7) STREAM BYTE) [Function] Outputs a single 8-bit byte to STREAM. This is similar to PRINTCCODE, but for binary streams the character position in STREAM is not updated (as with PRIN3), and end of line conventions are ignored. Note: BOUT is similar to PRINTCCODE, except that BOUT always writes a single byte, whereas PRINTCCODE writes a "character" that can consist of more than one byte, depending on the character and its encoding. (SPACES(SPACES (Function) NIL NIL NIL 7) N FILE) [Function] Prints N spaces. Returns NIL. (TERPRI (TERPRI% (Function) NIL NIL NIL 7)FILE) [Function] Prints an end-of-line character. Returns NIL. (FRESHLINE(FRESHLINE (Function) NIL NIL NIL 7) STREAM) [Function] Equivalent to TERPRI, except it does nothing if it is already at the beginning of the line. Returns T if it prints an end-of-line, NIL otherwise. (TAB(TAB (Function) NIL NIL NIL 7) POS MINSPACES FILE) [Function] Prints the appropriate number of spaces to move to position POS. MINSPACES indicates how many spaces must be printed (if NIL, 1 is used). If the current position plus MINSPACES is greater than POS, TAB does a TERPRI and then (SPACES POS). If MINSPACES is T, and the current position is greater than POS, then TAB does nothing. Note: A sequence of PRINT, PRIN2, SPACES, and TERPRI expressions can often be more conveniently coded with a single PRINTOUT statement. (SHOWPRIN2(SHOWPRIN2 (Function) NIL NIL NIL 7) X FILE RDTBL) [Function] Like PRIN2 except if SYSPRETTYFLG=T, prettyprints X instead. Returns X. (SHOWPRINT(SHOWPRINT (Function) NIL NIL NIL 7) X FILE RDTBL) [Function] Like PRINT except if SYSPRETTYFLG=T, prettyprints X instead, followed by an end-of-line. Returns X. SHOWPRINT and SHOWPRIN2 are used by the programmer's assistant (Chapter 13) for printing the values of expressions and for printing the history list, by various commands of the break package (Chapter 14), e.g. ?= and BT commands, and various other system packages. The idea is that by simply settting or binding SYSPRETTYFLG to T (initially NIL), the user instructs the system when interacting with the user to PRETTYPRINT expressions (Chapter 26) instead of printing them. (PRINTBELLS(PRINTBELLS (Function) NIL NIL NIL 8) %) [Function] Used by DWIM (Chapter 20) to print a sequence of bells to alert the user to stop typing. Can be advised or redefined for special applications, e.g., to flash the screen on a display terminal. (FORCEOUTPUT(FORCEOUTPUT (Function) NIL NIL NIL 8) STREAM WAITFORFINISH) [Function] Forces any buffered output data in STREAM to be transmitted. If WAITFORFINISH is non-NIL, this doesn't return until the data has been forced out. (POSITION(POSITION (Function) NIL NIL NIL 8) FILE N) [Function] Returns the column number at which the next character will be read or printed. After a end of line, the column number is 0. If N is non-NIL, resets the column number to be N. Note that resetting POSITION only changes Lisp's belief about the current column number; it does not cause any horizontal motion. Also note that (POSITION FILE) is not the same as (GETFILEPTR FILE) which gives the position in the file, not on the line. (LINELENGTH(LINELENGTH (Function) NIL NIL NIL 8) N FILE) [Function] Sets the length of the print line for the output file FILE to N; returns the former setting of the line length. FILE defaults to the primary output stream. (LINELENGTH NIL FILE) returns the current setting for FILE. When a file is first opened, its line length is set to the value of the variable FILELINELENGTH. Whenever printing an atom or string would increase a file's position beyond the line length of the file, an end of line is automatically inserted first. This action can be defeated by using PRIN3 and PRIN4. (SETLINELENGTH(SETLINELENGTH (Function) NIL NIL NIL 8) N) [Function] Sets the line length for the terminal by doing (LINELENGTH N T). If N is NIL, it determines N by consulting the operating system's belief about the terminal's characteristics. In Interlisp-D, this is a no-op. PRINTLEVEL When using Interlisp one often has to handle large, complicated lists, which are difficult to understand when printed out. PRINTLEVEL allows the user to specify in how much detail lists should be printed. The print functions PRINT, PRIN1, and PRIN2 are all affected by level parameters set by: (PRINTLEVEL(PRINTLEVEL (Function) NIL NIL NIL 8) CARVAL CDRVAL) [Function] Sets the CAR print level to CARVAL, and the CDR print level to CDRVAL. Returns a list cell whose CAR and CDR are the old settings. PRINTLEVEL is initialized with the value (1000 . -1). In order that PRINTLEVEL can be used with RESETFORM or RESETSAVE, if CARVAL is a list cell it is equivalent to (PRINTLEVEL (CAR CARVAL) (CDR CARVAL)). (PRINTLEVEL N NIL) changes the CAR printlevel without affecting the CDR printlevel. (PRINTLEVEL NIL N) changes the CDR printlevel with affecting the CAR printlevel. (PRINTLEVEL) gives the current setting without changing either. Note: Control-P (Chapter 30) can be used to change the PRINTLEVEL setting dynamically, even while Interlisp is printing. The CAR printlevel specifies how "deep" to print a list. Specifically, it is the number of unpaired left parentheses which will be printed. Below that level, all lists will be printed as &. If the CAR printlevel is negative, the action is similar except that an end-of-line is inserted after each right parentheses that would be immediately followed by a left parenthesis. The CDR printlevel specifies how "long" to print a list. It is the number of top level list elements that will be printed before the printing is terminated with --. For example, if CDRVAL=2, (A B C D E) will print as (A B --). For sublists, the number of list elements printed is also affected by the depth of printing in the CAR direction: Whenever the sum of the depth of the sublist (i.e. the number of unmatched left parentheses) and the number of elements is greater than the CDR printlevel, -- is printed. This gives a "triangular" effect in that less is printed the farther one goes in either CAR or CDR direction. If the CDR printlevel is negative, then it is the same as if the CDR printlevel were infinite. Examples: After: (A (B C (D (E F) G) H) K L) prints as: (PRINTLEVEL 3 -1) (A (B C (D & G) H) K L) (PRINTLEVEL 2 -1) (A (B C & H) K L) (PRINTLEVEL 1 -1) (A & K L) (PRINTLEVEL 0 -1) & (PRINTLEVEL 1000 2) (A (B --) --) (PRINTLEVEL 1000 3) (A (B C --) K --) (PRINTLEVEL 1 3) (A & K --) PLVLFILEFLG (PLVLFILEFLG% (Variable) NIL NIL NIL 9) [Variable] Normally, PRINTLEVEL only affects terminal output. Output to all other files acts as though the print level is infinite. However, if PLVLFILEFLG is T (initially NIL), then PRINTLEVEL affects output to files as well. The following three functions are useful for printing isolated expressions at a specified print level without going to the overhead of resetting the global print level. (LVLPRINT (LVLPRINT% (Function) NIL NIL NIL 9)X FILE CARLVL CDRLVL TAIL) [Function] Performs PRINT of X to FILE, using as CAR and CDR print levels the values CARLVL and CDRLVL, respectively. Uses the T read table. If TAIL is specified, and X is a tail of it, then begins its printing with "...", rather than on open parenthesis. (LVLPRIN2(LVLPRIN2 (Function) NIL NIL NIL 9) X FILE CARLVL CDRLVL TAIL) [Function] Similar to LVLPRIN2, but performs a PRIN2. (LVLPRIN1(LVLPRIN1 (Function) NIL NIL NIL 10) X FILE CARLVL CDRLVL TAIL) [Function] Similar to LVLPRIN1, but performs a PRIN1. Printing Numbers(PRINTING% NUMBERS NIL Printing% Numbers NIL NIL 10) How the ordinary printing functions (PRIN1, PRIN2, etc.) print numbers can be affected in several ways. RADIX influences the printing of integers, and FLTFMT influences the printing of floating point numbers. The setting of the variable PRXFLG determines how the symbol-manipulation functions handle numbers. The PRINTNUM package permits greater controls on the printed appearance of numbers, allowing such things as left-justification, suppression of trailing decimals, etc. (RADIX(RADIX (Function) NIL NIL NIL 10) N) [Function] Resets the output radix for integers to the absolute value of N. The value of RADIX is its previous setting. (RADIX) gives the current setting without changing it. The initial setting is 10. Note that RADIX affects output only. There is no input radix; on input, numbers are interpreted as decimal unless they are entered in a non-decimal radix with syntax such as 123Q, |b10101, |5r1234 (see Chapter 7). RADIX does not affect the behavior of UNPACK, etc., unless the value of PRXFLG (below) is T. For example, if PRXFLG is NIL and the radix is set to 8 with (RADIX 8), the value of (UNPACK 9) is (9), not (1 1). Using PRINTNUM (below) or the PRINTOUT command .I (below) is often a more convenient and appropriate way to print a single number in a specified radix than to globally change RADIX. (FLTFMT(FLTFMT (Function) NIL NIL NIL 10) FORMAT) [Function] Resets the output format for floating point numbers to the FLOAT format FORMAT (see PRINTNUM below for a description of FLOAT formats). FORMAT=T specifies the default "free" formatting: some number of significant digits (a function of the implementation) are printed, with trailing zeros suppressed; numbers with sufficiently large or small exponents are instead printed in exponent notation. FLTFMT returns its current setting. (FLTFMT) returns the current setting without changing it. The initial setting is T. Note: In Interlisp-D, FLTFMT ignores the WIDTH and PAD fields of the format (they are implemented only by PRINTNUM). Whether print name manipulation functions (UNPACK, NCHARS, etc.) use the values of RADIX and FLTFMT is determined by the variable PRXFLG: PRXFLG(PRXFLG (Variable) NIL NIL NIL 10) [Variable] If PRXFLG=NIL (the initial setting), then the "PRIN1" name used by PACK, UNPACK, MKSTRING, etc., is computed using base 10 for integers and the system default floating format for floating point numbers, independent of the current setting of RADIX or FLTFMT. If PRXFLG=T, then RADIX and FLTFMT do dictate the "PRIN1" name of numbers. Note that in this case, PACK and UNPACK are not inverses. Examples with (RADIX 8), (FLTFMT '(FLOAT 4 2)): With PRXFLG=NIL, (UNPACK 13) => (1 3) (PACK '(A 9)) => A9 (UNPACK 1.2345) => (1 %. 2 3 4 5) With PRXFLG=T, (UNPACK 13) => (1 5) (PACK '(A 9)) => A11 (UNPACK 1.2345) => (1 %. 2 3) Note that PRXFLG does not effect the radix of "PRIN2" names, so with (RADIX 8), (NCHARS 9 T), which uses PRIN2 names, would return 3, (since 9 would print as 11Q) for either setting of PRXFLG. Warning: Some system functions will not work correctly if PRXFLG is not NIL. Therefore, resetting the global value of PRXFLG is not recommended. It is much better to rebind PRXFLG as a SPECVAR for that part of a program where it needs to be non-NIL. The basic function for printing numbers under format control is PRINTNUM. Its utility is considerably enhanced when used in conjunction with the PRINTOUT package, which implements a compact language for specifying complicated sequences of elementary printing operations, and makes fancy output formats easy to design and simple to program. (PRINTNUM FORMAT NUMBER FILE) [Function] Prints NUMBER on FILE according to the format FORMAT. FORMAT is a list structure with one of the forms described below. If FORMAT is a list of the form (FIX WIDTH RADIX PAD0 LEFTFLUSH), this specifies a FIX format. NUMBER is rounded to the nearest integer, and then printed in a field WIDTH characters long with radix set to RADIX (or 10 if RADIX=NIL; note that the setting from the function RADIX is not used as the default). If PAD0 and LEFTFLUSH are both NIL, the number is right-justified in the field, and the padding characters to the left of the leading digit are spaces. If PAD0 is T, the character "0" is used for padding. If LEFTFLUSH is T, then the number is left-justified in the field, with trailing spaces to fill out WIDTH characters. The following examples illustrate the effects of the FIX format options on the number 9 (the vertical bars indicate the field width): FORMAT: (PRINTNUM FORMAT 9) prints: (FIX 2) | 9| (FIX 2 NIL T) |09| (FIX 12 8 T) |000000000011| (FIX 5 NIL NIL T) |9 | If FORMAT is a list of the form (FLOAT WIDTH DECPART EXPPART PAD0 ROUND), this specifies a FLOAT format. NUMBER is printed as a decimal number in a field WIDTH characters wide, with DECPART digits to the right of the decimal point. If EXPPART is not 0 (or NIL), the number is printed in exponent notation, with the exponent occupying EXPPART characters in the field. EXPPART should allow for the character E and an optional sign to be printed before the exponent digits. As with FIX format, padding on the left is with spaces, unless PAD0 is T. If ROUND is given, it indicates the digit position at which rounding is to take place, counting from the leading digit of the number. Interlisp-D interprets WIDTH=NIL to mean no padding, i.e., to use however much space the number needs, and interprets DECPART=NIL to mean as many decimal places as needed. The following examples illustrate the effects of the FLOAT format options on the number 27.689 (the vertical bars indicate the field width): FORMAT: (PRINTNUM FORMAT 27.689) prints: (FLOAT 7 2) | 27.69| (FLOAT 7 2 NIL T) |0027.69| (FLOAT 7 2 2) | 2.77E1| (FLOAT 11 2 4) | 2.77E+01| (FLOAT 7 2 NIL NIL 1) | 30.00| (FLOAT 7 2 NIL NIL 2) | 28.00| NILNUMPRINTFLG(NILNUMPRINTFLG (Variable) NIL NIL NIL 12) [Variable] If PRINTNUM's NUMBER argument is not a number and not NIL, a NON-NUMERIC ARG error is generated. If NUMBER is NIL, the effect depends on the setting of the variable NILNUMPRINTFLG. If NILNUMPRINTFLG is NIL, then the error occurs as usual. If it is non-NIL, then no error occurs, and the value of NILNUMPRINTFLG is printed right-justified in the field described by FORMAT. This option facilitates the printing of numbers in aggregates with missing values coded as NIL. User Defined Printing Initially, Interlisp only knows how to print in an interesting way objects of type litatom, number, string, list and stackp. All other types of objects are printed in the form {datatype} followed by the octal representation of the address of the pointer, a format that cannot be read back in to produce an equivalent object. When defining user data types (using the DATATYPE record type, Chapter 8), it is often desirable to specify as well how objects of that type should be printed, so as to make their contents readable, or at least more informative to the viewer. The function DEFPRINT is used to specify the printing format of a data type. (DEFPRINT(DEFPRINT (Function) NIL NIL NIL 12) TYPE FN) [Function] TYPE is a type name. Whenever a printing function (PRINT, PRIN1, PRIN2, etc.) or a function requiring a print name (CHCON, NCHARS, etc.) encounters an object of the indicated type, FN is called with two arguments: the item to be printed and the name of the stream, if any, to which the object is to be printed. The second argument is NIL on calls that request the print name of an object without actually printing it. If FN returns a list of the form (ITEM1 . ITEM2), ITEM1 is printed using PRIN1 (unless it is NIL), and then ITEM2 is printed using PRIN2 (unless it is NIL). No spaces are printed between the two items. Typically, ITEM1 is a read macro character. If FN returns NIL, the datum is printed in the system default manner. If FN returns T, nothing further is printed; FN is assumed to have printed the object to the stream itself. Note that this case if permitted only when the second argument passed to FN is non-NIL; otherwise, there is no destination for FN to do its printing, so it must return as in one of the other two cases. Printing Unusual Data Structures HPRINT (for "Horrible Print") and HREAD provide a mechanism for printing and reading back in general data structures that cannot normally be dumped and loaded easily, such as (possibly re-entrant or circular) structures containing user datatypes, arrays, hash tables, as well as list structures. HPRINT will correctly print and read back in any structure containing any or all of the above, chasing all pointers down to the level of literal atoms, numbers or strings. HPRINT currently cannot handle compiled code arrays, stack positions, or arbitrary unboxed numbers. HPRINT operates by simulating the Interlisp PRINT routine for normal list structures. When it encounters a user datatype (see Chapter 8), or an array or hash array, it prints the data contained therein, surrounded by special characters defined as read macro characters. While chasing the pointers of a structure, it also keeps a hash table of those items it encounters, and if any item is encountered a second time, another read macro character is inserted before the first occurrence (by resetting the file pointer with SETFILEPTR) and all subsequent occurrences are printed as a back reference using an appropriate macro character. Thus the inverse function, HREAD merely calls the Interlisp READ routine with the appropriate read table. (HPRINT(HPRINT (Function) NIL NIL NIL 13) EXPR FILE UNCIRCULAR DATATYPESEEN) [Function] Prints EXPR on FILE. If UNCIRCULAR is non-NIL, HPRINT does no checking for any circularities in EXPR (but is still useful for dumping arbitrary structures of arrays, hash arrays, lists, user data types, etc., that do not contain circularities). Specifying UNCIRCULAR as non-NIL results in a large speed and internal-storage advantage. Normally, when HPRINT encounters a user data type for the first time, it outputs a summary of the data type's declaration. When this is read in, the data type is redeclared. If DATATYPESEEN is non-NIL, HPRINT assumes that the same data type declarations will be in force at read time as were at HPRINT time, and not output declarations. HPRINT is intended primarily for output to random access files, since the algorithm depends on being able to reset the file pointer. If FILE is not a random access file (and UNCIRCULAR = NIL), a temporary file, HPRINT.SCRATCH, is opened, EXPR is HPRINTed on it, and then that file is copied to the final output file and the temporary file is deleted. (HREAD(HREAD (Function) NIL NIL NIL 13) FILE) [Function] Reads and returns an HPRINT-ed expression from FILE. (HCOPYALL(HCOPYALL (Function) NIL NIL NIL 13) X) [Function] Copies data structure X. X may contain circular pointers as well as arbitrary structures. Note: HORRIBLEVARS and UGLYVARS (Chapter 17) are two file package commands for dumping and reloading circular and re-entrant data structures. They provide a convenient interface to HPRINT and HREAD. When HPRINT is dumping a data structure that contains an instance of an Interlisp datatype, the datatype declaration is also printed onto the file. Reading such a data structure with HREAD can cause problems if it redefines a system datatype. Redefining a system datatype will almost definitely cause serious errors. The Interlisp system datatypes do not change very often, but there is always a possibility when loading in old files created under an old Interlisp release. To prevent accidental system crashes, HREAD will not redefine datatypes. Instead, it will cause an error "attempt to read DATATYPE with different field specification than currently defined". Continuing from this error will redefine the datatype. Random Access File Operations 1 For most applications, files are read starting at their beginning and proceeding sequentially, i.e., the next character read is the one immediately following the last character read. Similarly, files are written sequentially. However, for files on some devices, it is also possible to read/write characters at arbitrary positions in a file, essentially treating the file as a large block of auxiliary storage. For example, one application might involve writing an expression at the beginning of the file, and then reading an expression from a specified point in its middle. This particular example requires the file be open for both input and output. However, random file input or output can also be performed on files that have been opened for only input or only output. Associated with each file is a "file pointer" that points to the location where the next character is to be read from or written to. The file position of a byte is the number of bytes that precede it in the file, i.e., 0 is the position of the beginning of the file. The file pointer to a file is automatically advanced after each input or output operation. This section describes functions which can be used to reposition the file pointer on those files that can be randomly accessed. A file used in this fashion is much like an array in that it has a certain number of addressable locations that characters can be put into or taken from. However, unlike arrays, files can be enlarged. For example, if the file pointer is positioned at the end of a file and anything is written, the file "grows." It is also possible to position the file pointer beyond the end of file and then to write. (If the program attempts to read beyond the end of file, an END OF FILE error occurs.) In this case, the file is enlarged, and a "hole" is created, which can later be written into. Note that this enlargement only takes place at the end of a file; it is not possible to make more room in the middle of a file. In other words, if expression A begins at position 1000, and expression B at 1100, and the program attempts to overwrite A with expression C, whose printed representation is 200 bytes long, part of B will be altered. Warning: File positions are always in terms of bytes, not characters. The user should thus be very careful about computing the space needed for an expression. In particular, NS characters may take multiple bytes (see below). Also, the end-of-line character (see Chapter 24) may be represented by a different number of characters in different implementations. Output functions may also introduce end-of-line's as a result of LINELENGTH considerations. Therefore NCHARS (see Chapter 2) does not specify how many bytes an expression takes to print, even ignoring line length considerations. (GETFILEPTR(GETFILEPTR (Function) NIL NIL NIL 14) FILE) [Function] Returns the current position of the file pointer for FILE, i.e., the byte address at which the next input/output operation will commence. (SETFILEPTR(SETFILEPTR (Function) NIL NIL NIL 14) FILE ADR) [Function] Sets the file pointer for FILE to the position ADR; returns ADR. The special value ADR=-1 is interpreted to mean the address of the end of file. Note: If a file is opened for output only, the end of file is initially zero, even if an old file by the same name had existed (see OPENSTREAM, Chapter 24). If a file is opened for both input and output, the initial file pointer is the beginning of the file, but (SETFILEPTR FILE -1) sets it to the end of the file. If the file had been opened in append mode by (OPENSTREAM FILE 'APPEND), the file pointer right after opening would be set to the end of the existing file, in which case a SETFILEPTR to position the file at the end would be unnecessary. (GETEOFPTR(GETEOFPTR (Function) NIL NIL NIL 15) FILE) [Function] Returns the byte address of the end of file, i.e., the number of bytes in the file. Equivalent to performing (SETFILEPTR FILE -1) and returning (GETFILEPTR FILE) except that it does not change the current file pointer. (RANDACCESSP(RANDACCESSP (Function) NIL NIL NIL 15) FILE) [Function] Returns FILE if FILE is randomly accessible, NIL otherwise. The file T is not randomly accessible, nor are certain network file connections in Interlisp-D. FILE must be open or an error is generated, FILE NOT OPEN. (COPYBYTES(COPYBYTES (Function) NIL NIL NIL 15) SRCFIL DSTFIL START END) [Function] Copies bytes from SRCFIL to DSTFIL, starting from position START and up to but not including position END. Both SRCFIL and DSTFIL must be open. Returns T. If END=NIL, START is interpreted as the number of bytes to copy (starting at the current position). If START is also NIL, bytes are copied until the end of the file is reached. Warning: COPYBYTES does not take any account of multi-byte NS characters (see Chapter 2). COPYCHARS (below) should be used whenever copying information that might include NS characters. (COPYCHARS(COPYCHARS (Function) NIL NIL NIL 15) SRCFIL DSTFIL START END) [Function] Like COPYBYTES except that it copies NS characters (see Chapter 2), and performs the proper conversion if the end-of-line conventions of SRCFIL and DSTFIL are not the same (see Chapter 24). START and END are interpreted the same as with COPYBYTES, i.e., as byte (not character) specifications in SRCFIL. The number of bytes actually output to DSTFIL might be more or less than the number of bytes specified by START and END, depending on what the end-of-line conventions are. In the case where the end-of-line conventions happen to be the same, COPYCHARS simply calls COPYBYTES. (FILEPOS(FILEPOS (Function) NIL NIL NIL 15) PATTERN FILE START END SKIP TAIL CASEARRAY) [Function] Analogous to STRPOS (see Chapter 4), but searches a file rather than a string. FILEPOS searches FILE for the string PATTERN. Search begins at START (or the current position of the file pointer, if START=NIL), and goes to END (or the end of FILE, if END=NIL). Returns the address of the start of the match, or NIL if not found. SKIP can be used to specify a character which matches any character in the file. If TAIL is T, and the search is successful, the value is the address of the first character after the sequence of characters corresponding to PATTERN, instead of the starting address of the sequence. In either case, the file is left so that the next i/o operation begins at the address returned as the value of FILEPOS. CASEARRAY should be a "case array" that specifies that certain characters should be transformed to other characters before matching. Case arrays are returned by CASEARRAY or SEPRCASE below. CASEARRAY=NIL means no transformation will be performed. A case array is an implementation-dependent object that is logically an array of character codes with one entry for each possible character. FILEPOS maps each character in the file "through" CASEARRAY in the sense that each character code is transformed into the corresponding character code from CASEARRAY before matching. Thus if two characters map into the same value, they are treated as equivalent by FILEPOS. CASEARRAY and SETCASEARRAY provide an implementation-independent interface to case arrays. For example, to search without regard to upper and lower case differences, CASEARRAY would be a case array where all characters map to themselves, except for lower case characters, whose corresponding elements would be the upper case characters. To search for a delimited atom, one could use " ATOM " as the pattern, and specify a case array in which all of the break and separator characters mapped into the same code as space. For applications calling for extensive file searches, the function FFILEPOS is often faster than FILEPOS. (FFILEPOS(FFILEPOS (Function) NIL NIL NIL 16) PATTERN FILE START END SKIP TAIL CASEARRAY) [Function] Like FILEPOS, except much faster in most applications. FFILEPOS is an implementation of the Boyer-Moore fast string searching algorithm. This algorithm preprocesses the string being searched for and then scans through the file in steps usually equal to the length of the string. Thus, FFILEPOS speeds up roughly in proportion to the length of the string, e.g., a string of length 10 will be found twice as fast as a string of length 5 in the same position. Because of certain fixed overheads, it is generally better to use FILEPOS for short searches or short strings. (CASEARRAY(CASEARRAY (Function) NIL NIL NIL 16) OLDARRAY) [Function] Creates and returns a new case array, with all elements set to themselves, to indicate the identity mapping. If OLDARRAY is given, it is reused. (SETCASEARRAY(SETCASEARRAY (Function) NIL NIL NIL 16) CASEARRAY FROMCODE TOCODE) [Function] Modifies the case array CASEARRAY so that character code FROMCODE is mapped to character code TOCODE. (GETCASEARRAY(GETCASEARRAY (Function) NIL NIL NIL 16) CASEARRAY FROMCODE) [Function] Returns the character code that FROMCODE is mapped to in CASEARRAY. (SEPRCASE(SEPRCASE (Function) NIL NIL NIL 16) CLFLG) [Function] Returns a new case array suitable for use by FILEPOS or FFILEPOS in which all of the break/separators of FILERDTBL are mapped into character code zero. If CLFLG is non-NIL, then all CLISP characters are mapped into this character as well. This is useful for finding a delimited atom in a file. For example, if PATTERN is " FOO ", and (SEPRCASE T) is used for CASEARRAY, then FILEPOS will find "(FOO_". UPPERCASEARRAY(UPPERCASEARRAY (Variable) NIL NIL NIL 17) [Variable] Value is a case array in which every lowercase character is mapped into the corresponding uppercase character. Useful for searching text files. Input/Output Operations with Characters and Bytes 1 Interlisp-D supports the 16-bit NS character set (see Chapter 2). All of the standard string and print name functions accept litatoms and strings containing NS characters. In almost all cases, a program does not have to distinguish between NS characters or 8-bit characters. The exception to this rule is the handling of input/output operations. Interlisp-D uses two ways of writing 16-bit NS characters on files. One way is to write the full 16-bits (two bytes) every time a character is output. The other way is to use "run-encoding." Each 16 NS character can be decoded into a character set (an integer from 0 to 254 inclusive) and a character number (also an integer from 0 to 254 inclusive). In run-encoding, the byte 255 (illegal as either a character set number or a character number) is used to signal a change to a given character set, and the following bytes are all assumed to come from the same character set (until the next change-character set sequence). Run-encoding can reduce the number of bytes required to encode a string of NS characters, as long as there are long sequences of characters from the same character set (usually the case). Note that characters are not the same as bytes. A single character can take anywhere from one to four bytes bytes, depending on whether it is in the same character set as the preceeding character, and whether run-encoding is enabled. Programs which assume that characters are equal to bytes must be changed to work with NS characters. The functions BIN and BOUT (see above) should only be used to read and write single eight-bit bytes. The functions READCCODE and PRINTCCODE (see above) should be used to read and write single character codes, interpreting run-encoded NS characters. COPYBYTES should only be used to copy blocks of 8-bit data; COPYCHARS should be used to copy characters. Most I/O functions (READC, PRIN1, etc.) read or write 16-bit NS characters. The use of NS characters has serious consequences for any program that uses file pointers to access a file in a random access manner. At any point when a file is being read or written, it has a "current character set." If the file pointer is changed with SETFILEPTR to a part of the file with a different character set, any characters read or written may have the wrong character set. The current character set can be accessed with the following function: (CHARSET(CHARSET (Function) NIL NIL NIL 17) STREAM CHARACTERSET) [Function] Returns the current character set of the stream STREAM. If CHARACTERSET is non-NIL, the current character set for STREAM is set. Note that for output streams this may cause bytes to be written to the stream. If CHARACTERSET is T, run encoding for STREAM is disabled: both the character set and the character number (two bytes total) will be written to the stream for each character printed. PRINTOUT 1 Interlisp provides many facilities for controlling the format of printed output. By executing various sequences of PRIN1, PRIN2, TAB, TERPRI, SPACES, PRINTNUM, and PRINTDEF, almost any effect can be achieved. PRINTOUT implements a compact language for specifying complicated sequences of these elementary printing functions. It makes fancy output formats easy to design and simple to program. PRINTOUT is a CLISP word (like FOR and IF) for interpreting a special printing language in which the user can describe the kinds of printing desired. The description is translated by DWIMIFY to the appropriate sequence of PRIN1, TAB, etc., before it is evaluated or compiled. PRINTOUT printing descriptions have the following general form: (PRINTOUT STREAM PRINTCOM1 ... PRINTCOMN) STREAM is evaluated to obtain the stream to which the output from this specification is directed. The PRINTOUT commands are strung together, one after the other without punctuation, after STREAM. Some commands occupy a single position in this list, but many commands expect to find arguments following the command name in the list. The commands fall into several logical groups: one set deals with horizontal and vertical spacing, another group provides controls for certain formatting capabilities (font changes and subscripting), while a third set is concerned with various ways of actually printing items. Finally, there is a command that permits escaping to a simple Lisp evaluation in the middle of a PRINTOUT form. The various commands are described below. The following examples give a general flavor of how PRINTOUT is used: Example 1: Suppose the user wanted to print out on the terminal the values of three variables, X, Y, and Z, separated by spaces and followed by a carriage return. This could be done by: (PRIN1 X T) (SPACES 1 T) (PRIN1 Y T) (SPACES 1 T) (PRIN1 Z T) (TERPRI T) or by the more concise PRINTOUT form: (PRINTOUT T X , Y , Z T) Here the first T specifies output to the terminal, the commas cause single spaces to be printed, and the final T specifies a TERPRI. The variable names are not recognized as special PRINTOUT commands, so they are printed using PRIN1 by default. Example 2: Suppose the values of X and Y are to be pretty-printed lined up at position 10, preceded by identifying strings. If the output is to go to the primary output stream, the user could write either: (PRIN1 "X =") (PRINTDEF X 10 T) (TERPRI ) (PRIN1 "Y =") (PRINTDEF Y 10 T) (TERPRI) or the equivalent: (PRINTOUT NIL "X =" 10 .PPV X T "Y =" 10 .PPV Y T) Since strings are not recognized as special commands, "X =" is also printed with PRIN1 by default. The positive integer means TAB to position 10, where the .PPV command causes the value of X to be prettyprinted as a variable. By convention, special atoms used as PRINTOUT commands are prefixed with a period. The T causes a carriage return, so the Y information is printed on the next line. Example 3. As a final example, suppose that the value of X is an integer and the value of Y is a floating-point number. X is to be printed right-flushed in a field of width 5 beginning at position 15, and Y is to be printed in a field of width 10 also starting at position 15 with 2 places to the right of the decimal point. Furthermore, suppose that the variable names are to appear in the font class named BOLDFONT and the values in font class SMALLFONT. The program in ordinary Interlisp that would accomplish these effects is too complicated to include here. With PRINTOUT, one could write: (PRINTOUT NIL .FONT BOLDFONT "X =" 15 .FONT SMALLFONT .I5 X T .FONT BOLDFONT "Y =" 15 .FONT SMALLFONT .F10.2 Y T .FONT BOLDFONT) The .FONT commands do whatever is necessary to change the font on a multi-font output device. The .I5 command sets up a FIX format for a call to the function PRINTNUM (see above) to print X in the desired format. The .F10.2 specifies a FLOAT format for PRINTNUM. Horizontal Spacing Commands The horizontal spacing commands provide convenient ways of calling TAB and SPACES. In the following descriptions, N stands for a literal positive integer (not for a variable or expression whose value is an integer). N (N a number) [PRINTOUT Command] Used for absolute spacing. It results in a TAB to position N (literally, a (TAB N)). If the line is currently at position N or beyond, the file will be positioned at position N on the next line. .TAB(TAB (Command) .TAB NIL NIL 19)(.TAB (Command) NIL NIL NIL 19) POS [PRINTOUT Command] Specifies TAB to position (the value of) POS. This is one of several commands whose effect could be achieved by simply escaping to Lisp, and executing the corresponding form. It is provided as a separate command so that the PRINTOUT form is more concise and is prettyprinted more compactly. Note that .TAB N and N, where N is an integer, are equivalent. .TAB0(TAB0 (Command) .TAB0 NIL NIL 19)(.TAB0 (Command) NIL NIL NIL 19) POS [PRINTOUT Command] Like .TAB except that it can result in zero spaces (i.e. the call to TAB specifies MINSPACES=0). -N (N a number) [PRINTOUT Command] Negative integers indicate relative (as opposed to absolute) spacing. Translates as (SPACES |N|). ,(, (Command) NIL NIL NIL 19) [PRINTOUT Command] ,,(,, (Command) NIL NIL NIL 19) [PRINTOUT Command] ,,,(,,, (Command) NIL NIL NIL 19) [PRINTOUT Command] (1, 2 or 3 commas) Provides a short-hand way of specifying 1, 2 or 3 spaces, i.e., these commands are equivalent to -1, -2, and -3, respectively. .SP(SP (Command) .SP NIL NIL 19)(.SP (Command) NIL NIL NIL 19) DISTANCE [PRINTOUT Command] Translates as (SPACES DISTANCE). Note that .SP N and -N, where N is an integer, are equivalent. Vertical Spacing Commands Vertical spacing is obtained by calling TERPRI or printing form-feeds. The relevant commands are: T(T (Command) NIL NIL NIL 20) [PRINTOUT Command] Translates as (TERPRI), i.e., move to position 0 (the first column) of the next line. To print the letter T, use the string "T". .SKIP(SKIP (Command) .SKIP NIL NIL 20)(.SKIP (Command) NIL NIL NIL 20) LINES [PRINTOUT Command] Equivalent to a sequence of LINES (TERPRI)'s. The .SKIP command allows for skipping large constant distances and for computing the distance to be skipped. .PAGE(PAGE (Command) .PAGE NIL NIL 20)(.PAGE (Command) NIL NIL NIL 20) [PRINTOUT Command] Puts a form-feed (Control-L) out on the file. Care is taken to make sure that Interlisp's view of the current line position is correctly updated. Special Formatting Controls There are a small number of commands for invoking some of the formatting capabilities of multi-font output devices. The available commands are: .FONT(FONT (Command) .FONT NIL NIL 20)(.FONT (Command) NIL NIL NIL 20) FONTSPEC [PRINTOUT Command] Changes printing to the font FONTSPEC, which can be a font descriptor, a "font list" such as '(MODERN 10), an image stream (coerced to its current font), or a windows (coerced to the current font of its display stream). See fonts (Chapter 27) for more information. FONTSPEC may also be a positive integer N, which is taken as an abbreviated reference to the font class named FONTN (e.g. 1 => FONT1). .SUP(SUP (Command) .SUP NIL NIL 20)(.SUP (Command) NIL NIL NIL 20) [PRINTOUT Command] Specifies superscripting. All subsequent characters are printed above the base of the current line. Note that this is absolute, not relative: a .SUP following a .SUP is a no-op. .SUB(SUB (Command) .SUB NIL NIL 20)(.SUB (Command) NIL NIL NIL 20) [PRINTOUT Command] Specifies subscripting. Subsequent printing is below the base of the current line. As with superscripting, the effect is absolute. .BASE(BASE (Command) .BASE NIL NIL 20)(.BASE (Command) NIL NIL NIL 20) [PRINTOUT Command] Moves printing back to the base of the current line. Un-does a previous .SUP or .SUB; a no-op, if printing is currently at the base. Printing Specifications The value of any expression in a PRINTOUT form that is not recognized as a command itself or as a command argument is printed using PRIN1 by default. For example, title strings can be printed by simply including the string as a separate PRINTOUT command, and the values of variables and forms can be printed in much the same way. Note that a literal integer, say 51, cannot be printed by including it as a command, since it would be interpreted as a TAB; the desired effect can be obtained by using instead the string specification "51", or the form (QUOTE 51). For those instances when PRIN1 is not appropriate, e.g., PRIN2 is required, or a list structures must be prettyprinted, the following commands are available: .P2(P2 (Command) .P2 NIL NIL 21)(.P2 (Command) NIL NIL NIL 21) THING [PRINTOUT Command] Causes THING to be printed using PRIN2; translates as (PRIN2 THING). .PPF(PPF (Command) .PPF NIL NIL 21)(.PPF (Command) NIL NIL NIL 21) THING [PRINTOUT Command] Causes THING to be prettyprinted at the current line position via PRINTDEF (see Chapter 26). The call to PRINTDEF specifies that THING is to be printed as if it were part of a function definition. That is, SELECTQ, PROG, etc., receive special treatment. .PPV(PPV (Command) .PPV NIL NIL 21)(.PPV (Command) NIL NIL NIL 21) THING [PRINTOUT Command] Prettyprints THING as a variable; no special interpretation is given to SELECTQ, PROG, etc. .PPFTL(PPFTL (Command) .PPFTL NIL NIL 21)(.PPFTL (Command) NIL NIL NIL 21) THING [PRINTOUT Command] Like .PPF, but prettyprints THING as a tail, that is, without the initial and final parentheses if it is a list. Useful for prettyprinting sub-lists of a list whose other elements are formatted with other commands. .PPVTL(PPVTL (Command) .PPVTL NIL NIL 21)(.PPVTL (Command) NIL NIL NIL 21) THING [PRINTOUT Command] Like .PPV, but prettyprints THING as a tail. Paragraph Format Interlisp's prettyprint routines are designed to display the structure of expressions, but they are not really suitable for formatting unstructured text. If a list is to be printed as a textual paragraph, its internal structure is less important than controlling its left and right margins, and the indentation of its first line. The .PARA and .PARA2 commands allow these parameters to be conveniently specified. .PARA(PARA (Command) .PARA NIL NIL 21)(.PARA (Command) NIL NIL NIL 21) LMARG RMARG LIST [PRINTOUT Command] Prints LIST in paragraph format, using PRIN1. Translates as (PRINTPARA LMARG RMARG LIST) (see below). Example: (PRINTOUT T 10 .PARA 5 -5 LST) will print the elements of LST as a paragraph with left margin at 5, right margin at (LINELENGTH)-5, and the first line indented to 10. .PARA2(PARA2 (Command) .PARA2 NIL NIL 21)(.PARA2 (Command) NIL NIL NIL 21) LMARG RMARG LIST [PRINTOUT Command] Print as paragraph using PRIN2 instead of PRIN1. Translates as (PRINTPARA LMARG RMARG LIST T). Right-Flushing Two commands are provided for printing simple expressions flushed-right against a specified line position, using the function FLUSHRIGHT (see below). They take into account the current position, the number of characters in the print-name of the expression, and the position the expression is to be flush against, and then print the appropriate number of spaces to achieve the desired effect. Note that this might entail going to a new line before printing. Note also that right-flushing of expressions longer than a line (e.g. a large list) makes little sense, and the appearance of the output is not guaranteed. .FR(FR (Command) .FR NIL NIL 22)(.FR (Command) NIL NIL NIL 22) POS EXPR [PRINTOUT Command] Flush-right using PRIN1. The value of POS determines the position that the right end of EXPR will line up at. As with the horizontal spacing commands, a negative position number means |POS| columns from the current position, a positive number specifies the position absolutely. POS=0 specifies the right-margin, i.e. is interpreted as (LINELENGTH). .FR2(FR2 (Command) .FR2 NIL NIL 22)(.FR2 (Command) NIL NIL NIL 22) POS EXPR [PRINTOUT Command] Flush-right using PRIN2 instead of PRIN1. Centering Commands for centering simple expressions between the current line position and another specified position are also available. As with right flushing, centering of large expressions is not guaranteed. .CENTER(CENTER (Command) .CENTER NIL NIL 22)(.CENTER (Command) NIL NIL NIL 22) POS EXPR [PRINTOUT Command] Centers EXPR between the current line position and the position specified by the value of POS. A positive POS is an absolute position number, a negative POS specifies a position relative to the current position, and 0 indicates the right-margin. Uses PRIN1 for printing. .CENTER2(CENTER2 (Command) .CENTER2 NIL NIL 22)(.CENTER2 (Command) NIL NIL NIL 22) POS EXPR [PRINTOUT Command] Centers using PRIN2 instead of PRIN1. Numbering The following commands provide FORTRAN-like formatting capabilities for integer and floating-point numbers. Each command specifies a printing format and a number to be printed. The format specification translates into a format-list for the function PRINTNUM. .I(I (Command) .I NIL NIL 22)(.I (Command) NIL NIL NIL 22)FORMAT NUMBER [PRINTOUT Command] Specifies integer printing. Translates as a call to the function PRINTNUM with a FIX format-list constructed from FORMAT. The atomic format is broken apart at internal periods to form the format-list. For example, .I5.8.T yields the format-list (FIX 5 8 T), and the command sequence (PRINTOUT T .I5.8.T FOO) translates as (PRINTNUM '(FIX 5 8 T) FOO). This expression causes the value of FOO to be printed in radix 8 right-flushed in a field of width 5, with 0's used for padding on the left. Internal NIL's in the format specification may be omitted, e.g., the commands .I5..T and .I5.NIL.T are equivalent. The format specification .I1 is often useful for forcing a number to be printed in radix 10 (but not otherwise specially formatted), independent of the current setting of RADIX. .F(F (Command) .F NIL NIL 22)(.F (Command) NIL NIL NIL 22) FORMAT NUMBER [PRINTOUT Command] Specifies floating-number printing. Like the .I format command, except translates with a FLOAT format-list. .N(N (Command) .N NIL NIL 23)(.N (Command) NIL NIL NIL 23) FORMAT NUMBER [PRINTOUT Command] The .I and .F commands specify calls to PRINTNUM with quoted format specifications. The .N command translates as (PRINTNUM FORMAT NUMBER), i.e., it permits the format to be the value of some expression. Note that, unlike the .I and .F commands, FORMAT is a separate element in the command list, not part of an atom beginning with .N. Escaping to Lisp There are many reasons for taking control away from PRINTOUT in the middle of a long printing expression. Common situations involve temporary changes to system printing parameters (e.g. LINELENGTH), conditional printing (e.g. print FOO only if FIE is T), or lower-level iterative printing within a higher-level print specification. #(# (Command) NIL NIL NIL 23) FORM [PRINTOUT Command] The escape command. FORM is an arbitrary Lisp expression that is evaluated within the context established by the PRINTOUT form, i.e., FORM can assume that the primary output stream has been set to be the FILE argument to PRINTOUT. Note that nothing is done with the value of FORM; any printing desired is accomplished by FORM itself, and the value is discarded. Note: Although PRINTOUT logically encloses its translation in a RESETFORM (Chapter 14) to change the primary output file to the FILE argument (if non-NIL), in most cases it can actually pass FILE (or a locally bound variable if FILE is a non-trivial expression) to each printing function. Thus, the RESETFORM is only generated when the # command is used, or user-defined commands (below) are used. If many such occur in repeated PRINTOUT forms, it may be more efficient to embed them all in a single RESETFORM which changes the primary output file, and then specify FILE=NIL in the PRINTOUT expressions themselves. User-Defined Commands The collection of commands and options outlined above is aimed at fulfilling all common printing needs. However, certain applications might have other, more specialized printing idioms, so a facility is provided whereby the user can define new commands. This is done by adding entries to the global list PRINTOUTMACROS to define how the new commands are to be translated. PRINTOUTMACROS(PRINTOUTMACROS (Variable) NIL NIL NIL 23) [Variable] PRINTOUTMACROS is an association-list whose elements are of the form (COMM FN). Whenever COMM appears in command position in the sequence of PRINTOUT commands (as opposed to an argument position of another command), FN is applied to the tail of the command-list (including the command). After inspecting as much of the tail as necessary, the function must return a list whose CAR is the translation of the user-defined command and its arguments, and whose CDR is the list of commands still remaining to be translated in the normal way. For example, suppose the user wanted to define a command "?", which will cause its single argument to be printed with PRIN1 only if it is not NIL. This can be done by entering (? ?TRAN) on PRINTOUTMACROS, and defining the function ?TRAN as follows: (DEFINEQ (?TRAN (COMS) (CONS (SUBST (CADR COMS) 'ARG '(PROG ((TEMP ARG)) (COND (TEMP (PRIN1 TEMP))))) (CDDR COMS))] Note that ?TRAN does not do any printing itself; it returns a form which, when evaluated in the proper context, will perform the desired action. This form should direct all printing to the primary output file. Special Printing Functions The paragraph printing commands are translated into calls on the function PRINTPARA, which may also be called directly: (PRINTPARA(PRINTPARA (Function) NIL NIL NIL 24) LMARG RMARG LIST P2FLAG PARENFLAG FILE) [Function] Prints LIST on FILE in line-filled paragraph format with its first element beginning at the current line position and ending at or before RMARG, and with subsequent lines appearing between LMARG and RMARG. If P2FLAG is non-NIL, prints elements using PRIN2, otherwise PRIN1. If PARENFLAG is non-NIL, then parentheses will be printed around the elements of LIST. If LMARG is zero or positive, it is interpreted as an absolute column position. If it is negative, then the left margin will be at |LMARG|+(POSITION). If LMARG=NIL, the left margin will be at (POSITION), and the paragraph will appear in block format. If RMARG is positive, it also is an absolute column position (which may be greater than the current (LINELENGTH)). Otherwise, it is interpreted as relative to (LINELENGTH), i.e., the right margin will be at (LINELENGTH)+|RMARG|. Example: (TAB 10) (PRINTPARA 5 -5 LST T) will PRIN2 the elements of LST in a paragraph with the first line beginning at column 10, subsequent lines beginning at column 5, and all lines ending at or before (LINELENGTH)-5. The current (LINELENGTH) is unaffected by PRINTPARA, and upon completion, FILE will be positioned immediately after the last character of the last item of LIST. PRINTPARA is a no-op if LIST is not a list. The right-flushing and centering commands translate as calls to the function FLUSHRIGHT: (FLUSHRIGHT(FLUSHRIGHT (Function) NIL NIL NIL 24) POS X MIN P2FLAG CENTERFLAG FILE) [Function] If CENTERFLAG=NIL, prints X right-flushed against position POS on FILE; otherwise, centers X between the current line position and POS. Makes sure that it spaces over at least MIN spaces before printing by doing a TERPRI if necessary; MIN=NIL is equivalent to MIN=1. A positive POS indicates an absolute position, while a negative POS signifies the position which is |POS| to the right of the current line position. POS=0 is interpreted as (LINELENGTH), the right margin. READFILE and WRITEFILE 1 For those applications where the user simply wants to simply read all of the expressions on a file, and not evaluate them, the function READFILE is available: (READFILE(READFILE (Function) NIL NIL NIL 25) FILE RDTBL ENDTOKEN) [NoSpread Function] Reads successive expressions from file using READ (with read table RDTBL) until the single litatom ENDTOKEN is read, or an end of file encountered. Returns a list of these expressions. If RDTBL is not specified, it defaults to FILERDTBL. If ENDTOKEN is not specified, it defaults to the litatom STOP. (WRITEFILE(WRITEFILE (Function) NIL NIL NIL 25) X FILE) [Function] Writes a date expression onto FILE, followed by successive expressions from X, using FILERDTBL as a read table. If X is atomic, its value is used. If FILE is not open, it is opened. If FILE is a list, (CAR FILE) is used and the file is left opened. Otherwise, when X is finished, the litatom STOP is printed on FILE and it is closed. Returns FILE. (ENDFILE(ENDFILE (Function) NIL NIL NIL 25) FILE) [Function] Prints STOP on FILE and closes it. Read Tables 1 Many Interlisp input functions treat certain characters in special ways. For example, READ recognizes that the right and left parenthesis characters are used to specify list structures, and that the quote character is used to delimit text strings. The Interlisp input and (to a certain extent) output routines are table driven by read tables. Read tables are objects that specify the syntactic properties of characters for input routines. Since the input routines parse character sequences into objects, the read table in use determines which sequences are recognized as literal atoms, strings, list structures, etc. Most Interlisp input functions take an optional read table argument, which specifies the read table to use when reading an expression. If NIL is given as the read table, the "primary read table" is used. If T is specified, the system terminal read table is used. Some functions will also accept the atom ORIG (not the value of ORIG) as indicating the "original" system read table. Some output functions also take a read table argument. For example, PRIN2 prints an expression so that it would be read in correctly using a given read table. The Interlisp-D system uses the following read tables: T for input/output from terminals, the value of FILERDTBL for input/output from files, the value of EDITRDTBL for input from terminals while in the tty-based editor, the value of DEDITRDTBL for input from terminals while in the display-based editor, and the value of CODERDTBL for input/output from compiled files. These five read tables are initially copies of the ORIG read table, with changes made to some of them to provide read macros that are specific to terminal input or file input. Using the functions described below, the user may further change, reset, or copy these tables. However, in the case of FILERDTBL and CODERDTBL, the user is cautioned that changing these tables may prevent the system from being able to read files made with the original tables, or prevent users possessing only the standard tables from reading files made using the modified tables. The user can also create new read tables, and either explicitly pass them to input/output functions as arguments, or install them as the primary read table, via SETREADTABLE, and then not specify a RDTBL argument, i.e., use NIL. Read Table Functions (READTABLEP(READTABLEP (Function) NIL NIL NIL 26) RDTBL) [Function] Returns RDTBL if RDTBL is a real read table (not T or ORIG), otherwise NIL. (GETREADTABLE(GETREADTABLE (Function) NIL NIL NIL 26) RDTBL) [Function] If RDTBL=NIL, returns the primary read table. If RDTBL=T, returns the system terminal read table. If RDTBL is a real read table, returns RDTBL. Otherwise, generates an ILLEGAL READTABLE error. (SETREADTABLE(SETREADTABLE (Function) NIL NIL NIL 26) RDTBL FLG) [Function] Sets the primary read table to RDTBL. If FLG=T, SETREADTABLE sets the system terminal read table, T. Note that the user can reset the other system read tables with SETQ, e.g., (SETQ FILERDTBL (GETREADTABLE)). Generates an ILLEGAL READTABLE error if RDTBL is not NIL, T, or a real read table. Returns the previous setting of the primary read table, so SETREADTABLE is suitable for use with RESETFORM (Chapter 14). (COPYREADTABLE(COPYREADTABLE (Function) NIL NIL NIL 26) RDTBL) [Function] Returns a copy of RDTBL. RDTBL can be a real read table, NIL, T, or ORIG (in which case COPYREADTABLE returns a copy of the original system read table), otherwise COPYREADTABLE generates an ILLEGAL READTABLE error. Note that COPYREADTABLE is the only function that creates a read table. (RESETREADTABLE(RESETREADTABLE (Function) NIL NIL NIL 26) RDTBL FROM) [Function] Copies (smashes) FROM into RDTBL. FROM and RDTBL can be NIL, T, or a real read table. In addition, FROM can be ORIG, meaning use the system's original read table. Syntax Classes A read table is an object that contains information about the "syntax class" of each character. There are nine basic syntax classes: LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, STRINGDELIM, ESCAPE, BREAKCHAR, SEPRCHAR, and OTHER, each associated with a primitive syntactic property. In addition, there is an unlimited assortment of user-defined syntax classes, known as "read macros". The basic syntax classes are interpreted as follows: LEFTPAREN (normally left parenthesis) Begins list structure. RIGHTPAREN (normally right parenthesis) Ends list structure. LEFTBRACKET (normally left bracket) Begins list structure. Also matches RIGHTBRACKET characters. RIGHTBRACKET (normally left bracket) Ends list structure. Can close an arbitrary numbers of LEFTPAREN lists, back to the last LEFTBRACKET. STRINGDELIM (normally double quote) Begins and ends text strings. Within the string, all characters except for the one(s) with class ESCAPE are treated as ordinary, i.e., interpreted as if they were of syntax class OTHER. To include the string delimiter inside a string, prefix it with the ESCAPE character. ESCAPE (normally percent sign) Inhibits any special interpretation of the next character, i.e., the next character is interpreted to be of class OTHER, independent of its normal syntax class. BREAKCHAR (None initially) Is a break character, i.e., delimits atoms, but is otherwise an ordinary character. SEPRCHAR (space, carriage return, etc.) Delimits atoms, and is otherwise ignored. OTHER Characters that are not otherwise special belong to the class OTHER. Characters of syntax class LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, and STRINGDELIM are all break characters. That is, in addition to their interpretation as delimiting list or string structures, they also terminate the reading of an atom. Characters of class BREAKCHAR serve only to terminate atoms, with no other special meaning. In addition, if a break character is the first non-separator encountered by RATOM, it is read as a one-character atom. In order for a break character to be included in an atom, it must be preceded by the ESCAPE character. Characters of class SEPRCHAR also terminate atoms, but are otherwise completely ignored; they can be thought of as logically spaces. As with break characters, they must be preceded by the ESCAPE character in order to appear in an atom. For example, if $ were a break character and * a separator character, the input stream ABC**DEF$GH*$$ would be read by six calls to RATOM returning respectively ABC, DEF, $, GH, $, $. Although normally there is only one character in a read table having each of the list- and string-delimiting syntax classes (such as LEFTPAREN), it is perfectly acceptable for any character to have any syntax class, and for more than one to have the same class. Note that a "syntax class" is an abstraction: there is no object referencing a collection of characters called a syntax class. Instead, a read table provides the association between a character and its syntax class, and the input/output routines enforce the abstraction by using read tables to drive the parsing. The functions below are used to obtain and set the syntax class of a character in a read table. CH can either be a character code (a integer), or a character (a single-character atom). Single-digit integers are interpreted as character codes, rather than as characters. For example, 1 indicates Control-A, and 49 indicates the character 1. Note that CH can be a full sixteen-bit NS character (see Chapter 2). Note: Terminal tables, described in Chapter 30, also associate characters with syntax classes, and they can also be manipulated with the functions below. The set of read table and terminal table syntax classes are disjoint, so there is never any ambiguity about which type of table is being referred to. (GETSYNTAX CH TABLE) [Function] Returns the syntax class of CH, a character or a character code, with respect to TABLE. TABLE can be NIL, T, ORIG, or a real read table or terminal table. CH can also be a syntax class, in which case GETSYNTAX returns a list of the character codes in TABLE that have that syntax class. (SETSYNTAX CHAR CLASS TABLE) [Function] Sets the syntax class of CHAR, a character or character code, in TABLE. TABLE can be either NIL, T, or a real read table or terminal table. SETSYNTAX returns the previous syntax class of CHAR. CLASS can be any one of the following: f The name of one of the basic syntax classes. f A list, which is interpreted as a read macro (see below). f NIL, T, ORIG, or a real read table or terminal table, which means to give CHAR the syntax class it has in the table indicated by CLASS. For example, (SETSYNTAX '%( 'ORIG TABLE) gives the left parenthesis character in TABLE the same syntax class that it has in the original system read table. f A character code or character, which means to give CHAR the same syntax class as the character CHAR in TABLE. For example, (SETSYNTAX '{ '%[ TABLE) gives the left brace character the same syntax class as the left bracket. (SYNTAXP(SYNTAXP (Function) NIL NIL NIL 28) CODE CLASS TABLE) [Function] CODE is a character code; TABLE is NIL, T, or a real read table or terminal table. Returns T if CODE has the syntax class CLASS in TABLE; NIL otherwise. CLASS can also be a read macro type (MACRO, SPLICE, INFIX), or a read macro option (FIRST, IMMEDIATE, etc.), in which case SYNTAXP returns T if the syntax class is a read macro with the specified property. SYNTAXP will not accept a character as an argument, only a character code. For convenience in use with SYNTAXP, the atom BREAK may be used to refer to all break characters, i.e., it is the union of LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, STRINGDELIM, and BREAKCHAR. For purely symmetrical reasons, the atom SEPR corresponds to all separator characters. However, since the only separator characters are those that also appear in SEPRCHAR, SEPR and SEPRCHAR are equivalent. Note that GETSYNTAX never returns BREAK or SEPR as a value although SETSYNTAX and SYNTAXP accept them as arguments. Instead, GETSYNTAX returns one of the disjoint basic syntax classes that comprise BREAK. BREAK as an argument to SETSYNTAX is interpreted to mean BREAKCHAR if the character is not already of one of the BREAK classes. Thus, if %( is of class LEFTPAREN, then (SETSYNTAX '%( 'BREAK) doesn't do anything, since %( is already a break character, but (SETSYNTAX '%( 'BREAKCHAR) means make %( be just a break character, and therefore disables the LEFTPAREN function of %(. Similarly, if one of the format characters is disabled completely, e.g., by (SETSYNTAX '%( 'OTHER), then (SETSYNTAX '%( 'BREAK) would make %( be only a break character; it would not restore %( as LEFTPAREN. The following functions provide a way of collectively accessing and setting the separator and break characters in a read table: (GETSEPR(GETSEPR (Function) NIL NIL NIL 28) RDTBL) [Function] Returns a list of separator character codes in RDTBL. Equivalent to (GETSYNTAX 'SEPR RDTBL). (GETBRK(GETBRK (Function) NIL NIL NIL 28) RDTBL) [Function] Returns a list of break character codes in RDTBL. Equivalent to (GETSYNTAX 'BREAK RDTBL). (SETSEPR(SETSEPR (Function) NIL NIL NIL 29) LST FLG RDTBL) [Function] Sets or removes the separator characters for RDTBL. LST is a list of charactors or character codes. FLG determines the action of SETSEPR as follows: If FLG=NIL, makes RDTBL have exactly the elements of LST as separators, discarding from RDTBL any old separator characters not in LST. If FLG=0, removes from RDTBL as separator characters all elements of LST. This provides an "UNSETSEPR". If FLG=1, makes each of the characters in LST be a separator in RDTBL. If LST=T, the separator characters are reset to be those in the system's read table for terminals, regardless of the value of FLG, i.e., (SETSEPR T) is equivalent to (SETSEPR (GETSEPR T)). If RDTBL is T, then the characters are reset to those in the original system table. Returns NIL. (SETBRK(SETBRK (Function) NIL NIL NIL 29) LST FLG RDTBL) [Function] Sets the break characters for RDTBL. Similar to SETSEPR. As with SETSYNTAX to the BREAK class, if any of the list- or string-delimiting break characters are disabled by an appropriate SETBRK (or by making it be a separator character), its special action for READ will not be restored by simply making it be a break character again with SETBRK. However, making these characters be break characters when they already are will have no effect. The action of the ESCAPE character (normally %) is not affected by SETSEPR or SETBRK. It can be disabled by setting its syntax to the class OTHER, and other characters can be used for escape on input by assigning them the class ESCAPE. As of this writing, however, there is no way to change the output escape character; it is "hardwired" as %. That is, on output, characters of special syntax that need to be preceded by the ESCAPE character will always be preceded by %, independent of the syntax of % or which, if any characters, have syntax ESCAPE. The following function can be used for defeating the action of the ESCAPE character or characters: (ESCAPE(ESCAPE (Function) NIL NIL NIL 29) FLG RDTBL) [Function] If FLG=NIL, makes characters of class ESCAPE behave like characters of class OTHER on input. Normal setting is (ESCAPE T). ESCAPE returns the previous setting. Read Macros Read macros are user-defined syntax classes that can cause complex operations when certain characters are read. Read macro characters are defined by specifying as a syntax class an expression of the form: (TYPE OPTION1 ... OPTIONN FN) where TYPE is one of MACRO, SPLICE, or INFIX, and FN is the name of a function or a lambda expression. Whenever READ encounters a read macro character, it calls the associated function, giving it as arguments the input stream and read table being used for that call to READ. The interpretation of the value returned depends on the type of read macro: MACRO This is the simplest type of read macro. The result returned from the macro is treated as the expression to be read, instead of the read macro character. Often the macro reads more input itself. For example, in order to cause ~EXPR to be read as (NOT EXPR), one could define ~ as the read macro: [MACRO (LAMBDA (FL RDTBL) (LIST 'NOT (READ FL RDTBL] SPLICE The result (which should be a list or NIL) is spliced into the input using NCONC. For example, if $ is defined by the read macro: (SPLICE (LAMBDA NIL (APPEND FOO))) and the value of FOO is (A B C), then when the user inputs (X $ Y), the result will be (X A B C Y). INFIX The associated function is called with a third argument, which is a list, in TCONC format (Chapter 3), of what has been read at the current level of list nesting. The function's value is taken as a new TCONC list which replaces the old one. For example, the infix operator + could be defined by the read macro: (INFIX (LAMBDA (FL RDTBL Z) (RPLACA (CDR Z) (LIST (QUOTE IPLUS) (CADR Z) (READ FL RDTBL))) Z)) If an INFIX read macro character is encountered not in a list, the third argument to its associated function is NIL. If the function returns NIL, the read macro character is essentially ignored and reading continues. Otherwise, if the function returns a TCONC list of one element, that element is the value of the READ. If it returns a TCONC list of more than one element, the list is the value of the READ. The specification for a read macro character can be augmented to specify various options OPTION1 ... OPTIONN, e.g., (MACRO FIRST IMMEDIATE FN). The following three disjoint options specify when the read macro character is to be effective: ALWAYS The default. The read macro character is always effective (except when preceded by the % character), and is a break character, i.e., a member of (GETSYNTAX 'BREAK RDTBL). FIRST The character is interpreted as a read macro character only when it is the first character seen after a break or separator character; in all other situations, the character is treated as having class OTHER. The read macro character is not a break character. For example, the quote character is a FIRST read macro character, so that DON'T is read as the single atom DON'T, rather than as DON followed by (QUOTE T). ALONE The read macro character is not a break character, and is interpreted as a read macro character only when the character would have been read as a separate atom if it were not a read macro character, i.e., when its immediate neighbors are both break or separator characters. Making a FIRST or ALONE read macro character be a break character (with SETBRK) disables the read macro interpretation, i.e., converts it to syntax class BREAKCHAR. Making an ALWAYS read macro character be a break character is a no-op. The following two disjoint options control whether the read macro character is to be protected by the ESCAPE character on output when a litatom containing the character is printed: ESCQUOTE or ESC The default. When printed with PRIN2, the read macro character will be preceded by the output escape character (%) as needed to permit the atom containing it to be read correctly. Note that for FIRST macros, this means that the character need be quoted only when it is the first character of the atom. NOESCQUOTE or NOESC The read macro character will always be printed without an escape. For example, the ? read macro in the T read table is a NOESCQUOTE character. Unless you are very careful what you are doing, read macro characters in FILERDTBL should never be NOESCQUOTE, since symbols that happen to contain the read macro character will not read back in correctly. The following two disjoint options control when the macro's function is actually executed: IMMEDIATE or IMMED The read macro character is immediately activated, i.e., the current line is terminated, as if an EOL had been typed, a carriage-return line-feed is printed, and the entire line (including the macro character) is passed to the input function. IMMEDIATE read macro characters enable the user to specify a character that will take effect immediately, as soon as it is encountered in the input, rather than waiting for the line to be terminated. Note that this is not necessarily as soon as the character is typed. Characters that cause action as soon as they are typed are interrupt characters (see Chapter 30). Note that since an IMMEDIATE macro causes any input before it to be sent to the reader, characters typed before an IMMEDIATE read macro character cannot be erased by Control-A or Control-Q once the IMMEDIATE character has been typed, since they have already passed through the line buffer. However, an INFIX read macro can still alter some of what has been typed earlier, via its third argument. NONIMMEDIATE or NONIMMED The default. The read macro character is a normal character with respect to the line buffering, and so will not be activated until a carriage-return or matching right parenthesis or bracket is seen. Making a read macro character be both ALONE and IMMEDIATE is a contradiction, since ALONE requires that the next character be input in order to see if it is a break or separator character. Thus, ALONE read macros are always NONIMMEDIATE, regardless of whether or not IMMEDIATE is specified. Read macro characters can be "nested". For example, if = is defined by (MACRO (LAMBDA (FL RDTBL) (EVAL (READ FL RDTBL)))) and ! is defined by (SPLICE (LAMBDA (FL RDTBL) (READ FL RDTBL))) then if the value of FOO is (A B C), and (X =FOO Y) is input, (X (A B C) Y) will be returned. If (X !=FOO Y) is input, (X A B C Y) will be returned. Note: If a read macro's function calls READ, and the READ returns NIL, the function cannot distinguish the case where a RIGHTPAREN or RIGHTBRACKET followed the read macro character, (e.g. "(A B ')"), from the case where the atom NIL (or "()") actually appeared. In Interlisp-D, a READ inside of a read macro when the next input character is a RIGHTPAREN or RIGHTBRACKET reads the character and returns NIL, just as if the READ had not occurred inside a read macro. If a call to READ from within a read macro encounters an unmatched RIGHTBRACKET within a list, the bracket is simply put back into the buffer to be read (again) at the higher level. Thus, inputting an expression such as (A B '(C D] works correctly. (INREADMACROP(INREADMACROP (Function) NIL NIL NIL 32)) [Function] Returns NIL if currently not under a read macro function, otherwise the number of unmatched left parentheses or brackets. (READMACROS(READMACROS (Function) NIL NIL NIL 32) FLG RDTBL) [Function] If FLG=NIL, turns off action of read macros in read table RDTBL. If FLG=T, turns them on. Returns previous setting. The following read macros are standardly defined in Interlisp in the T and EDITRDTBL read tables: ' (single-quote) Returns the next expression, wrapped in a call to QUOTE; e.g., 'FOO reads as (QUOTE FOO). The macro is defined as a FIRST read macro, so that the quote character has no effect in the middle of a symbol. The macro is also ignored if the quote character is immediately followed by a separator character. Control-Y Defined in T and EDITRDTBL. Returns the result of evaluating the next expression. For example, if the value of FOO is (A B), then (LIST 1 control-YFOO 2) is read as (LIST 1 (A B) 2). Note that no structure is copied; the third element of that input expression is still EQ to the value of FOO. Control-Y can thus be used to read structures that ordinarily have no read syntax. For example, the value returned from reading (KEY1 Control-Y(ARRAY 10)) has an array as its second element. Control-Y can be thought of as an "un-quote" character. The choice of character to perform this function is changeable with SETTERMCHARS (see Chapter 16). ` (backquote) Backquote makes it easier to write programs to construct complex data structures. Backquote is like quote, except that within the backquoted expression, forms can be evaluated. The general idea is that the backquoted expression is a "template" containing some constant parts (as with a quoted form) and some parts to be filled in by evaluating something. Unlike with control-Y, however, the evaluation occurs not at the time the form is read, but at the time the backquoted expression is evaluated. That is, the backquote macro returns an expression which, when evaluated, produces the desired structure. Within the backquoted expression, the character "," (comma) introduces a form to be evaluated. The value of a form preceded by ",@" is to be spliced in, using APPEND. If it is permissible to destroy the list being spliced in (i.e., NCONC may be used in the translation), then ",." can be used instead of ",@". For example, if the value of FOO is (1 2 3 4), then the form `(A ,(CAR FOO) ,@(CDDR FOO) D E) evaluates to (A 1 3 4 D E); it is logically equivalent to writing (CONS 'A (CONS (CAR FOO) (APPEND (CDDR FOO) '(D E)))) . Backquote is particularly useful for writing macros. For example, the body of a macro that refers to X as the macro's argument list might be `(COND ((FIXP ,(CAR X)) ,(CADR X)) (T .,(CDDR X))) which is equivalent to writing (LIST 'COND (LIST (LIST 'FIXP (CAR X)) (CADR X)) (CONS 'T (CDDR X))) Note that comma does not have any special meaning outside of a backquote context. For users without a backquote character on their keyboards, backquote can also be written as |' (vertical-bar, quote). ? Implements the ?= command for on-line help regarding the function currently being "called" in the typein (see Chapter 26). | (vertical bar) When followed by an end of line, tab or space, | is ignored, i.e., treated as a separator character, enabling the editor's CHANGECHAR feature (see Chapter 26). Otherwise it is a "dispatching" read macro whose meaning depends on the character(s) following it. The following are currently defined: ' (quote) -- A synonym for backquote. . (period) -- Returns the evaluation of the next expression, i.e., this is a synonym for Control-Y. , (comma) -- Returns the evaluation of the next expression at load time, i.e., the following expression is quoted in such a manner that the compiler treats it as a literal whose value is not determined until the compiled expression is loaded. O or o (the letter O) -- Treats the next number as octal, i.e., reads it in radix 8. For example, |o12 = 10 (decimal). B or b -- Treats the next number as binary, i.e., reads it in radix 2. For example, |b101 = 5 (decimal). X or x -- Treats the next number as hexadecimal, i.e., reads it in radix 16. The uppercase letters A though F are used as the digits after 9. For example, |x1A = 26 (decimal). R or r -- Reads the next number in the radix specified by the (decimal) number that appears between the | and the R. When inputting a number in a radix above ten, the upper-case letters A through Z can be used as the digits after 9 (but there is no digit above Z, so it is not possible to type all base-99 digits). For example, |3r120 reads 120 in radix 3, returning 15. (, {, ^ -- Used internally by HPRINT and HREAD (see above) to print and read unusual expressions. The dispatching characters that are letters can appear in either upper- or lowercase. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))9/HH +52HHl2HHl5/HH3(T-T3HZ +T-llT3lT,-ll +T4HZ +TT2lxx2Hll/HH +5,2lll2lll2HHl2HH +2l0HHT/ll3$$lT2HH +l3HH +T5,,53$$(T2l +2Hll3(T2Hl +l,ll +3HZT,HH3HZ +T,ll5,HH +-T3(T,HH,2F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR,,,CLASSIC + HELVETICA TITAN +TITAN +CLASSIC +CLASSIC + HELVETICA HELVETICA HELVETICA +CLASSIC +MODERN +MODERN +MODERNMODERN +6 HRULE.GETFNCLASSIC + +6  +5 HRULE.GETFNCLASSIC + +5 4  +4 HRULE.GETFNCLASSIC + +3  +3 HRULE.GETFNCLASSIC + + 221 HRULE.GETFNMODERN +0 + +/-IM.INDEX.GETFN. HRULE.GETFNMODERN +-I + +O + +, + + + +, + + +8 +1 +,U +t +, + + + + +,J + + + +,J +, +* +$IM.INDEX.GETFN +  ++ +% + + + +$ + + + + +U +J +  +*"IM.INDEX.GETFN ++ + + + + +/. HRULE.GETFNMODERN +- + +, + + +  +) +  +) + 6 + S +) +a + +" +) + +" +$ +* IM.INDEX.GETFN + ++ +E + ++A ++ +*IM.INDEX.GETFN + ++ +K +i + + + + + +  + ++t + +R + + ++) + +# + + +a + + + +) ++ + +6 + + + , ++ +2 +V += +* + + ++ +% + + +W ++S + + + +/ + +*"IM.INDEX.GETFN + + ++ + + + +-E + + + +L +*!IM.INDEX.GETFN + + + +; + +*!IM.INDEX.GETFN + ++ + + + +S + + + ++ + + + + + + + ++ + + + + + + +) + + + +*"IM.INDEX.GETFN + ++0 + +N +% + + + ? + + + +*"IM.INDEX.GETFNTITAN + + ++ + +8 + +* IM.INDEX.GETFN + ++% + +* +$IM.INDEX.GETFN + + ++ + +B + + +*$IM.INDEX.GETFNTITAN + +  + ++/ + +B + +*IM.INDEX.GETFNMODERN + + ++ +X +& + +  +% + j +- + + + +c + +* IM.INDEX.GETFN + ++ +- + + + + + ++% + +/ + ++: + + + + + + + +3 + +1 +IM.INDEX.GETFN + ++ +N +  + +> +  ++ + + + + +  +  +*)IM.INDEX.GETFNMODERN + ++$ +! + +  +z + + ++' + B +% + + +  +*!IM.INDEX.GETFNMODERN + + ++ + + +c + = + +; + 8 +u ++ + + + + +  += + +- +  + + ++ +> +) +U + +* +$IM.INDEX.GETFNMODERN + + + ++ +> + + T +m +  + +%3IM.INDEX.GETFN. HRULE.GETFNMODERN +- +R + +^ +P + +$, +)- +E + +" +)7 +J + +& +*!IM.INDEX.GETFN + ++ +G + ++B + +* IM.INDEX.GETFN + ++ + + +* IM.INDEX.GETFN +  ++ + + + +> + + +- + +S + +1 +" +\ +D + + + +* + +X +< +* IM.INDEX.GETFN + + +( IM.INDEX.GETFN +  +' + + + +* IM.INDEX.GETFN +  +' + +& + +*  IM.INDEX.GETFN +  +') + + +L + +! + +' +* + + + +*IM.INDEX.GETFN +  +' + + +3 + +. +# + + + +& + +j +*!IM.INDEX.GETFN + +' + + +*#IM.INDEX.GETFN +'* + +* +$IM.INDEX.GETFN + +' +Q + + +*IM.INDEX.GETFN + +'< + + / +, +  + + + + + +  ++ + + +& + + + +@ + +* +$IM.INDEX.GETFN +  +' + +  + + + +* +$IM.INDEX.GETFN +  +' + +  + +/ + +- + +  +I + + + + + + + + + + +; +*%IM.INDEX.GETFNMODERN +  + +' +* &IM.INDEX.GETFNMODERN + + +'# + +' +  +: +* #IM.INDEX.GETFNMODERN +  +' + + + + +' +v +  + + ! + + +* %IM.INDEX.GETFNCLASSIC + + +'6 + +2 +) +! +T + +'E +t + + +*(IM.INDEX.GETFNCLASSIC + + +'/ +  + + +u +" -| + +] + + +. +* %IM.INDEX.GETFNMODERN + +  +' + + + + + + + + + +  +' + + +  +  +$ + +  +" + + + + + 4 +#8 + +7 +' + + + + + +' + + + +  +f + +| + +f + + +7 + +' + +! + +!+!%!!!#!'!* (IM.INDEX.GETFN +' + + +s +  + + + +" +- +*  %IM.INDEX.GETFN +' + + + + + + + + + +0 +" +* #IM.INDEX.GETFNMODERN + + +' + + +* $IM.INDEX.GETFNMODERN +  +' + + +"4IM.INDEX.GETFN-% + +8 +* +Q +G + +*!IM.INDEX.GETFNMODERN + + +'> + + +L +' + + + + + + +! + + + + + +  + + + + +' + + +~ + +*"IM.INDEX.GETFN  +'; + + + + + + +' +J + +' + + +4 + +-+ + + + + + +*"IM.INDEX.GETFNMODERN + +' + +" + + + + + + + + + + +, + + + + +! +  +   $  + +  ' + + + +  +  +K + +'; + +, +2 + +5 + +-@ +J + +*   + ++ + + + +; ++ + +  + + +@ +# + + +* + + + +  + +z + + + +  +S + ++5 +N + +   + + + + + + +  + + ++ + + + + ++ + +/ + + +K + + +K +4 + + +~ ++ + +V + ++ ++5 +S +  +  + + +  + + + + + + +  + + + + +**IM.INDEX.GETFNMODERN + ++ + +" + + + +4 + + +0 +) +6 +^ + +"- + + + +8 +* $IM.INDEX.GETFNMODERN + ++0 + + +. + +4 + +Q ++ + + + + + + + += + ++ + +5 ++ + + + + +) +H +" - + + +^ +)& + + + + +* +*"IM.INDEX.GETFNMODERN + +! ++ + + + + + ++ + + + +: ++ + +  + +W +$ ++ +" + + + + + +b +*!IM.INDEX.GETFNMODERN + + ++ + + +* $IM.INDEX.GETFNMODERN + + ++ + +? ++ +  + + + ++ + + ++& + +7 +R; +/. HRULE.GETFNMODERN +) + K +9 + +) + + +A + +  +i +) +0 + +: + +) + + + +` +* &IM.INDEX.GETFN + ++5 +Q +* &IM.INDEX.GETFN + ++ + + + + + +8 + + +z + P + e + +7 +* +%IM.INDEX.GETFN + ++n +  + : +* 'IM.INDEX.GETFNCLASSIC + ++ + + + +W +( +  +* +%IM.INDEX.GETFNMODERN + + ++ + + +& + + + + ++ + + +W + +9 ++ + + I + W +* +%IM.INDEX.GETFNMODERN +  ++ + { + +% + +" + 2 +* += + +{ +  +  +*#IM.INDEX.GETFNMODERN +* ++ += + + + + +2 + + + + + +6 + ++Q + +P +- + + + +  +  + +  +, ++ ++ + a + e + +  + C ++K +  + +C + + +* $IM.INDEX.GETFNMODERN + +* ++ +, + + ++B +& +* +%IM.INDEX.GETFNMODERN + + ++q + +* (IM.INDEX.GETFNMODERN + + ++ +  + + +* (IM.INDEX.GETFNMODERN + + ++ + +  +* $IM.INDEX.GETFNMODERN + + ++- + +) + * + + + + +  +  + + + *IM.INDEX.GETFN ++ +/2. HRULE.GETFNMODERN +-] +)0 +)/" +) + +Z +  + +o + 3 + 9 + +, +) + + +*#IM.INDEX.GETFNMODERN + + ++0 + +  + +Y ++ +  + + +% . HRULE.GETFNMODERN +-t + + + + + + +& + +) + + + + +- +8 + +)a +N + +g + + +) W + + +Q +      ) + +) +_ + +4 +% + +)  + + + + ) + )6 + +) + + +J ++ +" +* +) 1 + + +T + + + s + +) +Z + +# + + + + + +"-C + +" +( +: +*  + ++, + + +) +4 + +*IM.INDEX.GETFNMODERN +IM.INDEX.GETFN ++ + + + +F + + + +*!IM.INDEX.GETFNMODERN + IM.INDEX.GETFN ++ +< + +  +*    ++U + + + +* IM.INDEX.GETFN +( IM.INDEX.GETFN +( IM.INDEX.GETFN ++t + + + +*IM.INDEX.GETFNIM.INDEX.GETFNCLASSIC + ++ + + + + +"-( +5 +*IM.INDEX.GETFNCLASSIC + ++ +U + + +*!IM.INDEX.GETFN IM.INDEX.GETFNCLASSIC + ++ + + +d +*!IM.INDEX.GETFN IM.INDEX.GETFNCLASSIC + ++ +"- +*!IM.INDEX.GETFN IM.INDEX.GETFN ++ +8 +  ++ +E + + +*IM.INDEX.GETFNIM.INDEX.GETFNCLASSIC + ++ + + +*IM.INDEX.GETFNIM.INDEX.GETFNCLASSIC + ++ +*!IM.INDEX.GETFNCLASSIC + IM.INDEX.GETFN ++I + +1 +"-! +[ +e + +a + + +) + +a +*IM.INDEX.GETFNIM.INDEX.GETFNCLASSIC + ++ + + + +*IM.INDEX.GETFNIM.INDEX.GETFNCLASSIC + ++ +6 + + +I + +# +*IM.INDEX.GETFNIM.INDEX.GETFNCLASSIC + ++ +6 + + +*#IM.INDEX.GETFN!IM.INDEX.GETFNCLASSIC + ++ + + + +*#IM.INDEX.GETFNCLASSIC +!IM.INDEX.GETFN ++ + + +-P + +A +*!IM.INDEX.GETFN IM.INDEX.GETFNCLASSIC + ++ + + + + ++ + + +7 + ( +*#IM.INDEX.GETFN!IM.INDEX.GETFNCLASSIC + ++ + + + + +-~ + + +*IM.INDEX.GETFNIM.INDEX.GETFNCLASSIC + ++ + +/ +^ +[ +7 +  +*IM.INDEX.GETFNCLASSIC +IM.INDEX.GETFN ++ + + + +- +*%IM.INDEX.GETFN"IM.INDEX.GETFNCLASSIC + ++ +N + +, +` + +*'IM.INDEX.GETFNCLASSIC +#IM.INDEX.GETFN ++ + + +  - + +*IM.INDEX.GETFNIM.INDEX.GETFN ++B + + +` + +  + +' +p +B + +  ++ + + +*IM.INDEX.GETFNIM.INDEX.GETFN ++. +* + + IM.INDEX.GETFNIM.INDEX.GETFN ++ + + +) + + Y + + +O + +"-4 + + +$ + + +P +* IM.INDEX.GETFN   ++ +Y + +B + +& + +* +% +& +) + 7 + +& +! +D +  +] +? + 9 + + + +"-2 +8 +**IM.INDEX.GETFN ++6 + +0 +C +E ++Y +M +M ++: +; + + +  + + + ++ + + +"-J + % +* +%IM.INDEX.GETFN& ++ + +w +. + + + + + + +  +: + ++ +} + + + + + + +1 ++ +\ + 0 + % +  + + + + + ++ +  +  +M + +  + +-M + + +* &IM.INDEX.GETFN  ++ + + + + + + +' ++ +# + + + + +2 +" +. + +  +. HRULE.GETFNMODERN +- +* $IM.INDEX.GETFNMODERN + + ++- + + +O ++ +" +  +. + +* +%IM.INDEX.GETFNMODERN + + ++ +* + +  +# + + +7 + + + + +*#IM.INDEX.GETFNMODERN +  ++ + + +% . HRULE.GETFNMODERN +-W + +) +C +a + + + +x +V +)8 +/ + + + F + +N + [ + +  +  +) +  + + +"* &IM.INDEX.GETFN + + + + + + + +* (IM.INDEX.GETFN + + +& + +. + + + +* (IM.INDEX.GETFN  + + + + + & +B + + + + + + + +T +  +  +*)IM.INDEX.GETFN + + + + + + +  + +  + + + +  + +**IM.INDEX.GETFN + + + + + + + +& + +/ +" - +  + + +  +  +  + +  + + + + 5 + + +4 + + ? +  + + R +  +  + + | +L +G + + +* + +g + +K +? + + +  + + +  +  +  + +  + +| + + + +* + + +) + + + + + + + + + + z +r + & +  +b + +  +9 +2 +* + ++ +3 + + + + +* +++ + * + + + + +$ + + + ++ + & + +" + +0 + += + + + + +> +3 + +) +F + +7 +( + + +K +*#IM.INDEX.GETFNCLASSIC + + + + +3 + + + + + + + + + + +  + +B + +5 + + + + +, +  + + +  +  +  + , +v + + + + + +  + + +  +% + @ + + +  + / + + +  + +# + + +/ +  +O + + + + + + +  + +*#IM.INDEX.GETFNCLASSIC + +/ + + +*"IM.INDEX.GETFNCLASSIC + ++ + + + +*#IM.INDEX.GETFNCLASSIC +  +- + +. + + + + + + +% + + +) + +  +$ + + + + +v + +  + + +G + + +*"IM.INDEX.GETFNCLASSIC +  + + + + +  +a +D + +A +J + + + + + +9 +S +l +T +& + +* + +C + +*"IM.INDEX.GETFNCLASSIC +  + + + +! + + + + +   + + + + + += + +O +, + + + + +87$, +' +" + + +%, + + + +  +, +N +y +C +% +877 77$7 , +% += + +o +7 + += + +Y + + +b +, +Y +9 + +, +8 + + +; + + + + +  +, + + + + +1 +L +  +7 +h +I + + +! +L +R +g + + + +V + + + +V +  + +a +] + +  +c + +  +e + + W + J + ` +Y + +  + +( + +  +k + +  +  +8 + + + + + + +  +  +  +  +( + + + +3 + + + + +! + +) +; + + + ! + +' + +2 +  + +  + (IM.INDEX.GETFN + + +] + &IM.INDEX.GETFNMODERN +  + + +0 + + +, +E + +  + +A + + +  + + + + W + + +  +Y + + +   +  + +n +3 +O + +D +( + + + + +  +# + ( + %h +' +87779 +8 7 77 +: +` + + +j + + + +% +c +: +  + +^ + + + +P + + + +^ + + + + + + +l +H + +! + +D +% + + + +5 +X +z \ No newline at end of file diff --git a/docs/porter-irm/29-HARDCOPY.TEDIT b/docs/porter-irm/29-HARDCOPY.TEDIT new file mode 100644 index 00000000..a9c65a95 --- /dev/null +++ b/docs/porter-irm/29-HARDCOPY.TEDIT @@ -0,0 +1,59 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 29. HARDCOPY FACILITIES 1 29. HARDCOPY FACILITIES 1 29. HARDCOPY FACILITIES 6 Interlisp-D includes facilities for generating hardcopy in "Interpress" format and "Press" format. Interpress is a file format used for communicating documents to Xerox Network System printers such as the Xerox 8044 and Xerox 5700. Press is a file format used for communicating documents to Xerox laser Xerographic printers known by the names "Dover", "Spruce", "Penguin", and "Raven". There are also library packages available for supporting other types of printer formats (4045, FX-80, C150, etc.). The hardcopy facilities are designed to allow the user to support new types of printers with minimal changes to the user interface. Files can be in a number of formats, including Interpress files, plain text files, and formatted Tedit files. In order to print a file on a given printer, it is necessary to identify the format of the file, convert the file to a format that the printer can accept, and transmit it. Rather than require that the user explicitly determine file types and do the conversion, the Interlisp-D hardcopy functions generate Interpress or other format output depending on the appropriate choice for the designated printer. The hardcopy functions use the variables PRINTERTYPES and PRINTFILETYPES (described below) to determine the type of a file, how to convert it for a given printer, and how to send it. By changing these variables, the user can define other kinds of printers and print to them using the normal hardcopy functions. (SEND.FILE.TO.PRINTER(SEND.FILE.TO.PRINTER (Function) NIL NIL NIL 1) FILE HOST PRINTOPTIONS) [Function] The function SEND.FILE.TO.PRINTER causes the file FILE to be sent to the printer HOST. If HOST is NIL, the first host in the list DEFAULTPRINTINGHOST which can print FILE is used. PRINTOPTIONS is a property list of the form (PROP1 VALUE1 PROP2 VALUE2 ...). The properties accepted depends on the type of printer. For Interpress printers, the following properties are accepted: DOCUMENT.NAME The document name to appear on the header page (a string). Default is the full name of the file. DOCUMENT.CREATION.DATE The creation date to appear on the header page (a Lisp integer date, such as returned by IDATE). The default value is the creation date of the file. SENDER.NAME The name of the sender to appear on the header page (a string). The default value is the name of the user. RECIPIENT.NAME The name of the recipient to appear on the header page (a string). The default is none. MESSAGE An additional message to appear on the header page (a string). The default is none. #COPIES The number of copies to be printed. The default value is 1. PAGES.TO.PRINT The pages of the document that should be printed, represented as a list (FIRSTPAGE# LASTPAGE#). For example, if this option is (3 5), this specifies that pages 3 through 5, inclusive, should be printed. Note that the page numbering used for this purpose has no connection to any page numbers that may be printed on the document. The default is to print all of the pages in the document. MEDIUM The medium on which the master is to be printed. If omitted, this defaults to the value of NSPRINT.DEFAULT.MEDIUM, as follows: NIL means to use the printer's default; T means to use the first medium reported available by the printer; any other value must be a Courier value of type MEDIUM. The format of this type is a list (PAPER (KNOWN.SIZE TYPE)) or (PAPER (OTHER.SIZE (WIDTH LENGTH))). The paper TYPE is one of US.LETTER, US.LEGAL, A0 through A10, ISO.B0 through ISO.B10, and JIS.B0 through JIS.B10. For users who use A4 paper exclusively, it should be sufficient to set NSPRINT.DEFAULT.MEDIUM to (PAPER (KNOWN.SIZE "A4")). When using different paper sizes, it may be necessary to reset the variable DEFAULTPAGEREGION, the region on the page used for printing (measured in micas from the lower-left corner). STAPLE? True if the document should be stapled. #SIDES 1 or 2 to indicate that the document should be printed on one or two sides, respectively. The default is the value of EMPRESS#SIDES. PRIORITY The priority of this print request, one of LOW, NORMAL, or HIGH. The default is the printer's default. Note: Press printers only recognize the options #COPIES, #SIDES, DOCUMENT.CREATION.DATE, and DOCUMENT.NAME. For example, (SEND.FILE.TO.PRINTER 'FOO NIL '(#COPIES 3 #SIDES 2 DOCUMENT.NAME "For John")) SEND.FILE.TO.PRINTER calls PRINTERTYPE and PRINTFILETYPE to determine the printer type of HOST and the file format of FILE. If FILE is a formatted file already in a form that the printer can print, it is transmitted directly. Otherwise, CONVERT.FILE.TO.TYPE.FOR.PRINTER is called to do the conversion. [Note: If the file is converted, PRINTOPTIONS is passed to the formatting function, so it can include properties such as HEADING, REGION, and FONTS.] All of these functions use the lists PRINTERTYPES and PRINTFILETYPES to actually determine how to do the conversion. LISTFILES (Chapter 17) calls the function LISTFILES1 to send a single file to a hardcopy printing device. Interlisp-D is initialized with LISTFILES1 defined to call SEND.FILE.TO.PRINTER. (HARDCOPYW(HARDCOPYW (Function) NIL NIL NIL 3) WINDOW/BITMAP/REGION FILE HOST SCALEFACTOR ROTATION PRINTERTYPE) [Function] Creates a hardcopy file from a bitmap and optionally sends it to a printer. Note that some printers may have limitations concerning how big or how "complicated" the bitmap may be printed. WINDOW/BITMAP/REGION can either be a WINDOW (open or closed), a BITMAP, or a REGION (interpreted as a region of the screen). If WINDOW/BITMAP/REGION is NIL, the user is prompted for a screen region using GETREGION. If FILE is non-NIL, it is used as the name of the file for output. If HOST=NIL, this file is not printed. If FILE is NIL, a temporary file is created, and sent to HOST. To save an image on a file without printing it, perform (HARDCOPYW IMAGE FILE). To print an image to the printer PRINTER without saving the file, perform (HARDCOPYW IMAGE NIL PRINTER). If both FILE and HOST are NIL, the default action is to print the image, without saving the file. The printer used is determined by the argument PRINTERTYPE and the value of the variable DEFAULTPRINTINGHOST. If PRINTERTYPE is non-NIL, the first host on DEFAULTPRINTINGHOST of the type PRINTERTYPE is used. If PRINTERTYPE is NIL, the first printer on DEFAULTPRINTINGHOST that implements the BITMAPSCALE (as determined by PRINTERTYPES) operation is used, if any. Otherwise, the first printer on DEFAULTPRINTINGHOST is used. The type of hardcopy file produced is determined by HOST if non-NIL, else by PRINTERTYPE if non-NIL, else by the value of DEFAULTPRINTINGHOST, as described above. SCALEFACTOR is a reduction factor. If not given, it is computed automatically based on the size of the bitmap and the capabilities of the printer type. This may not be supported for some printers. ROTATION specifies how the bitmap image should be rotated on the printed page. Most printers (including Interpress printers) only support a ROTATION of multiples of 90. PRINTERTYPE specifies what type of printer to use when HOST is NIL. HARDCOPYW uses this information to select which printer to use or what print file format to convert the output into, as described above. The background menu contains a "Hardcopy" command (Chapter 28) that prompts the user for a region on the screen, and sends the image to the default printer. Hardcopy output may also be obtained by writing a file on the printer device LPT, e.g. (COPYFILE 'FOO '{LPT}). When a file on this device is closed, it is converted to Interpress or some other format (if necessary) and sent to the default printer (the first host on DEFAULTPRINTINGHOST). One can include the printer name directly in the file name, e.g. (COPYFILE 'FOO {LPT}TREMOR:) will send the file to the printer TREMOR:. (PRINTERSTATUS(PRINTERSTATUS (Function) NIL NIL NIL 3) PRINTER) [Function] Returns a list describing the current status of the printer named PRINTER. The exact form of the value returned depends on the type of printer. For InterPress printers, the status describes whether the printer is available or busy or needs attention, and what type of paper is loaded in the printer. Returns NIL if the printer does not respond in a reasonable time, which can occur if the printer is very busy, or does not implement the printer status service. DEFAULTPRINTINGHOST (DEFAULTPRINTINGHOST% (Variable) NIL NIL NIL 4) [Variable] The variable DEFAULTPRINTINGHOST is used to designate the default printer to be used as the output of printing operations. It should be a list of the known printer host names, for example, (QUAKE LISPPRINT:). If an element of DEFAULTPRINTINGHOST is a list, is interpreted as (PRINTERTYPE HOST), specifying both the host type and the host name. The type of the printer, which determines the protocol used to send to it and the file format it requires, is determined by the function PRINTERTYPE. If DEFAULTPRINTINGHOST is a single printer name, it is treated as if it were a list of one element. (PRINTFILETYPE(PRINTFILETYPE (Function) NIL NIL NIL 4) FILE %) [Function] Returns the format of the file FILE. Possible values include INTERPRESS, TEDIT, etc. If it cannot determine the file type, it returns NIL. Uses the global variable PRINTFILETYPES. (PRINTERTYPE(PRINTERTYPE (Function) NIL NIL NIL 4) HOST) [Function] Returns the type of the printer HOST. Currently uses the following heuristic: 1. If HOST is a list, the CAR is assumed to be the printer type and CADR the name of the printer 2. If HOST is a litatom with a non-NIL PRINTERTYPE property, the property value is returned as the printer type 3. If HOST contains a colon (e.g., PRINTER:PARC:XEROX) it is assumed to be an INTERPRESS printer 4. If HOST is the CADR of a list on DEFAULTPRINTINGHOST, the CAR is returned as the printer type 5. Otherwise, the value of DEFAULTPRINTERTYPE is returned as the printer type. Low-level Hardcopy Variables 1 The following variables are used to define how Interlisp should generate hardcopy of different types. The user should only need to change these variables when it is necessary to access a new type of printer, or define a new hardcopy document type (not often). PRINTERTYPES(PRINTERTYPES (Variable) NIL NIL NIL 4) [Variable] The characteristics of a given printer are determined by the value of the list PRINTERTYPES. Each element is a list of the form (TYPES (PROPERTY1 VALUE1) (PROPERTY2 VALUE2) ...) TYPES is a list of the printer types that this entry addresses. The (PROPERTYn VALUEn) pairs define properties associated with each printer type. The printer properties include the following: CANPRINT Value is a list of the file types that the printer can print directly. STATUS Value is a function that knows how to find out the status of the printer, used by PRINTERSTATUS. PROPERTIES Value is a function which returns a list of known printer properties. SEND Value is a function which invokes the appropriate protocol to send a file to the printer. BITMAPSCALE Value is a function of arguments WIDTH and HEIGHT in bits which returns a scale factor for scaling a bitmap. BITMAPFILE Value is a form which, when evaluated, converts a bitmap to a file format that the printer will accept. Note: The name 8044 is defined on PRINTERTYPES as a synonym for the INTERPRESS printer type. The names SPRUCE, PENGUIN, and DOVER are defined on PRINTERTYPES as synonyms for the PRESS printer type. The printer types FULLPRESS and RAVEN are also defined the same as PRESS, except that these printer types indicate that the printer is a "Full Press" printer that is able to scale bitmap images, in addition to the normal Press printer facilities. PRINTFILETYPES(PRINTFILETYPES (Variable) NIL NIL NIL 5) [Variable] The variable PRINTFILETYPES contains information about various file formats, such as Tedit files and Interpress files. The format is similar to PRINTERTYPES. The properties that can be specified include: TEST Value is a function which tests a file if it is of the given type. Note that this function is passed an open stream. CONVERSION Value is a property list of other file types and funcitons that convert from the specified type to the file format. EXTENSION Value is a list of possible file extensions for files of this type. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))2l2l~~,-T3(T552ll,HH +/ll0llT3H` +T2l5,ll3HH +T,HH,HH,2F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR TITAN + HELVETICACLASSIC +CLASSIC +TITAN + HELVETICA +CLASSIC +MODERN +MODERN +MODERNMODERN + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + + + HRULE.GETFNMODERN + }- /IM.INDEX.GETFNMODERN +    +  {   b Z8  l  Y  U  =  H + "  \%r%        JN[  '   w   +) 1  4  "k D L) 1 ! +W + +$IM.INDEX.GETFNMODERN +?  + +.1  +5$2 +B%. +u       > + +4 +  +  + + ,  + +ME# (IM.INDEX.GETFNMODERN + +  B +  0IM.INDEX.GETFN  +    +M (IM.INDEX.GETFNMODERN +  + +9 &IM.INDEX.GETFN  + +' > + !" HRULE.GETFNMODERN +   'IM.INDEX.GETFNMODERN +  +O &   +@ < +.HS  +G[ "< +i  + "  )IM.INDEX.GETFNMODERN +  v 1 w u E1az \ No newline at end of file diff --git a/docs/porter-irm/41-RESOURCE-MANAGEMENT.TEDIT b/docs/porter-irm/41-RESOURCE-MANAGEMENT.TEDIT new file mode 100644 index 00000000..737bebf5 Binary files /dev/null and b/docs/porter-irm/41-RESOURCE-MANAGEMENT.TEDIT differ diff --git a/docs/porter-irm/INDEX.INTERPRESS b/docs/porter-irm/INDEX.INTERPRESS new file mode 100644 index 00000000..652d519a Binary files /dev/null and b/docs/porter-irm/INDEX.INTERPRESS differ diff --git a/docs/porter-irm/INDEX.TEDIT b/docs/porter-irm/INDEX.TEDIT new file mode 100644 index 00000000..ef9e6934 --- /dev/null +++ b/docs/porter-irm/INDEX.TEDIT @@ -0,0 +1,1998 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual INDEX 1 INDEX 1 INDEX 6 A ABS (Function) I: 7-3 ACCESSFNS (Record Type) I: 8-9 ADD (Change Word) I: 8-14 ADD (Property) II: 17-35 ADD.PROCESS (Function) II: 23-1 ADD1 (Function) I: 7-5 ADDFILE (Function) II: 17-15 ADDPROP (Function) I: 2-4 ADDSPELL (Function) II: 20-15 ADDSPELLFLG (Variable) II: 20-10 ADDTOCOMS (Function) II: 17-37 ADDTOFILES? (Function) II: 17-10 ADDTOSCRATCHLIST (Function) I: 3-6 ADIEU (Function) I: 11-15 ADVISE II: 15-8 functions II: 15-8 ADVISE (Command) II: 17-26 ADVISEDUMP (Function) II: 15-10 Advising II: 15-7 After (Command) II: 13-12; 16-5 ALLOCATE.PUP (Function) III: 31-22 ALLOCATE.XIP (Function) III: 31-28 ALLOW.BUTTON.EVENTS (Function) II: 23-12 ALPHORDER (Function) I: 3-14 ALWAYS FORM (I.S. Operator) I: 9-9 (AND X1 X2 ... XN) (Function) I: 9-3 ANTILOG (Function) I: 7-10 APPEND (Function) I: 3-4 APPROVEFLG (Variable) II: 20-11 APROPOS (Function) I: 2-8 ARCCOS (Function) I: 7-11 ARCHIVE (Command) II: 13-13 ARCHIVEFLG (Variable) II: 13-17 ARCHIVEFN (Variable) II: 13-17 ARCSIN (Function) I: 7-11 ARCTAN (Function) I: 7-11 ARCTAN2 (Function) I: 7-11 ARGS (Break Command) II: 14-7 Arithmetic floating point I: 7-8 generic I: 7-2 integer I: 7-3 logical functions I: 7-6 ARRAY (Function) I: 5-1 ARRAYORIG (Function) I: 5-2 ARRAYP (Function) I: 5-1; 9-1 ARRAYRECORD (Record Type) I: 8-6 Arrays I: 5-1 ARRAYSIZE (Function) I: 5-2 ARRAYTYP (Function) I: 5-1 AS VAR (I.S. Operator) I: 9-12 ASSOC (Function) I: 3-12 ASSOCRECORD (Record Type) I: 8-6 ATOM (Function) I: 9-1 ATOM (Function) I: 2-1 ATOMRECORD (Record Type) I: 8-7 ATTACH (Function) I: 3-4 AU-REVOIR (Function) I: 11-15 AUTOBACKTRACEFLG (Variable) II: 14-11 AWAIT.EVENT (Function) II: 23-6 B BACKGROUNDPAGEFREQ (Variable) I: 12-6 BACKTRACE (Function) I: 11-8 BACKTRACEFONT (Variable) II: 14-11 BAKTRACE (Function) I: 11-9 BAKTRACELST (Variable) I: 11-9 .BASE (Command) III: 25-20 BCOMPL (Function) II: 18-15 Before (Command) II: 13-12; 16-5 BeginDST (Variable) I: 12-11 BELOW (Command) II: 16-19 BELOW COM X (Command) II: 16-19 BF (Command) II: 16-17 BF PATTERN NIL (Command) II: 16-18 BI (Command) II: 16-30 BI N (Command) II: 16-30 BIN (Function) III: 25-4 BIND (Command) II: 16-48 BIND VAR (I.S. Operator) I: 9-10 BIND VARS (I.S. Operator) I: 9-10 BITCLEAR (Macro) I: 7-7 BITSET (Macro) I: 7-7 BITTEST (Macro) I: 7-7 BK (Command) II: 16-12 BLIPSCAN (Function) I: 11-12 BLIPVAL (Function) I: 11-12 BLKAPPLYFNS (Variable) II: 18-14 BLKNAME - USED BLKAPPLY WHEN NOT APPLICABLE (Error Message) II: 18-17 Block compiling II: 18-12 compiling functions II: 18-15 declarations II: 18-13 BLOCK (Function) II: 23-4 BLOCKCOMPILE (Function) II: 18-15 BLOCKRECORD (Record Type) I: 8-8 BLOCKS (Command) II: 17-33 BO N (Command) II: 16-31 BOUNDP (Function) I: 2-2 BOUT (Function) III: 25-7 BOXCOUNT (Function) II: 22-6 Boxed numbers I: 7-1 Break (Command) II: 16-7 BREAK (Function) II: 15-4 BREAK0 (Function) II: 15-3 BREAK1 (Function) II: 14-12 BREAKCHECK (Function) II: 14-10 BREAKCHK (Variable) II: 14-17 BREAKIN (Function) II: 15-4 Breaking Functions II: 15-1 BREAKMACROS (Variable) II: 14-13 BREAKREAD (Function) II: 14-13 BREAKREGIONSPEC (Variable) II: 14-12 BREAKRESETFORMS (Variable) II: 14-14 BRECOMPILE (Function) II: 18-16 BRKDWNRESULTS (Function) II: 22-7 BRKDWNTYPE (Variable) II: 22-7 BRKDWNTYPES (Variable) II: 22-8 BROADSCOPE (Property) II: 21-20 BT (Break Command) II: 14-7 BTV (Break Command) II: 14-7 BTV! (Break Command) II: 14-7 BTV* (Break Command) II: 14-7 BTV+ (Break Command) II: 14-7 BY FORM (with IN/ON) (I.S. Operator) I: 9-11 BY FORM (without IN/ON) (I.S. Operator) I: 9-11 BYTE (Macro) I: 7-8 BYTEPOSITION (Macro) I: 7-8 BYTESIZE (Macro) I: 7-8 C CANFILEDEF (File Package Type Property) II: 17-23 CAP (Command) II: 16-39 CAR (Function) I: 3-1 CAR/CDRERR (Variable) I: 3-1 CASEARRAY (Function) III: 25-16 CDR (Function) I: 3-1 Center (Command) II: 16-7; III: 25-22 .CENTER2 (Command) III: 25-22 CH.DEFAULT.DOMAIN (Variable) III: 31-6 CH.DEFAULT.ORGANIZATION (Variable) III: 31-6 CH.ISMEMBER III: 31-9 CH.LIST.ALIASES (Function) III: 31-8 CH.LIST.ALIASES.OF (Function) III: 31-8 CH.LIST.DOMAINS (Function) III: 31-8 CH.LIST.OBJECTS (Function) III: 31-8 CH.LIST.ORGANIZATIONS (Function) III: 31-8 CH.LOOKUP.OBJECT (Function) III: 31-8 CH.NET.HINT (Variable) III: 31-7 CH.RETRIEVE.ITEM (Function) III: 31-8 CH.RETRIEVE.MEMBERS (Function) III: 31-8 CHANGE (Change Word) I: 8-15 CHANGECALLERS (Function) II: 17-22 CHANGENAME (Function) II: 15-6 CHANGEPROP (Function) I: 2-5 CHANGESLICE (Function) II: 13-16 CHARACTER (Function) I: 2-10 CHARCODE (Function) I: 2-10 CHARSET (Function) III: 25-17 CHCON (Function) I: 2-10 CHCON1 (Function) I: 2-10 CHECKIMPORTS (Function) II: 17-33 CL (Command) II: 16-41 CL (Editor Command) II: 21-19 CL:FLG (Variable) II: 21-17 CLDISABLE (Function) II: 21-18 CLEANPOSLST (Function) I: 11-16 CLEANUP (Function) II: 17-10 Clear (Command) II: 16-7 CLEARPUP (Function) III: 31-22 CLEARSTK (Function) I: 11-7 CLEARSTKLST (Variable) I: 11-7 CLISP II: 21-1 character operators II: 21-5 compiling II: 18-9 interaction with user II: 21-4 CLISPARRAY (Variable) II: 21-18 CLISPCHARRAY (Variable) II: 21-18 CLISPCHARS (Variable) II: 21-18 CLISPDEC (Function) II: 21-18; 21-9 CLISPFLG (Variable) II: 21-18 CLISPHELPFLG (Variable) II: 21-16 CLISPIFTRANFLG (Variable) II: 21-19 CLISPIFYPACKFLG (Variable) II: 21-17 CLISPIFYPRETTYFLG (Variable) II: 21-19 CLISPIFYUSERFN (Variable) II: 21-17 CLISPINFIX (Property) II: 21-21 CLISPINFIXSPLST (Variable) II: 21-18 CLISPRETRANFLG (Variable) II: 21-16 CLISPTRAN (Function) II: 21-18 CLISPTYPE (Property) II: 21-20 CLISPWORD (Property) II: 21-21 CLOCK (Function) I: 12-10 CLOSEALL (Function) III: 24-4 CLOSEBREAKWINDOWFLG (Variable) II: 14-12 CLOSEF (Function) III: 24-3 CLOSEF? (Function) III: 24-3 CLOSENSOCKET (Function) III: 31-28 CLOSEPUPSOCKET (Function) III: 31-22 CLREMPARSFLG (Variable) II: 21-17 CLRHASH (Function) I: 6-2 CNDIR (Function) III: 24-8 COLLECT FORM (I.S. Operator) I: 9-8 COMPARE (Function) II: 17-22 COMPAREDEFS (Function) II: 17-23 COMPARELISTS (Function) I: 3-15 COMPILE (Function) II: 18-10 COMPILE1 (Function) II: 18-10 COMPILEFILES (Function) II: 17-11 Compiler constants II: 18-5 error messages II: 18-16 functions II: 18-9 global variables II: 18-3 local variables II: 18-4 printout II: 18-2 special variables II: 18-4 COMPILETYPELST (Variable) II: 18-8 COMPILEUSERFN (Variable) II: 18-7 Compiling block II: 18-12 CLISP II: 18-9 function calls II: 18-6 FUNCTION function II: 18-7 functional arguments II: 18-7 CONCAT (Function) I: 4-3 CONCATLIST (Function) I: 4-3 (COND CLAUSE1 CLAUSE2 ... CLAUSEK) (Function) I: 9-3 CONN (Command) III: 24-8 CONS (Function) I: 3-1 CONSCOUNT (Function) II: 22-6 CONSTANT (Function) II: 18-5 CONSTANTS (Function) II: 18-6 CONTAIN (Property) II: 17-35 Control-X (Command) II: 16-14 Control-Z (Command) II: 16-14 Copy (Command) II: 16-7 COPY (Function) I: 3-6 COPYALL (Function) I: 3-6 COPYARRAY (Function) I: 5-2 COPYBYTES (Function) III: 25-15 COPYCHARS (Function) III: 25-15 COPYDEF (Function) II: 17-21 COPYFILE (Function) III: 24-24 COPYREADTABLE (Function) III: 25-26 COREDEVICE (Function) III: 24-23 COROUTINE (Function) I: 11-14 COS (Function) I: 7-11 COUNT (Function) I: 3-8 COUNT FORM (I.S. Operator) I: 9-9 COURIER.BROADCAST.CALL (Function) III: 31-18 COURIER.CALL (Function) III: 31-16 COURIER.CREATE (Macro) III: 31-14 COURIER.EXPEDITED.CALL (Function) III: 31-17 COURIER.FETCH (Macro) III: 31-14 COURIER.OPEN (Function) III: 31-16 COURIER.READ (Function) III: 31-19 COURIER.READ.BULKDATA (Function) III: 31-19 COURIER.READ.REP (Function) III: 31-20 COURIER.READ.SEQUENCE (Function) III: 31-20 COURIER.WRITE (Function) III: 31-19 COURIER.WRITE.SEQUENCE (Function) III: 31-20 COURIERDEF (Property Name) III: 31-15 COURIERPROGRAM (Function) III: 31-12 COURIERPROGRAMS (File Package Type) II: 17-17 CREATE.EVENT (Function) II: 23-6 CREATE.MONITORLOCK (Function) II: 23-6 CREATEDSKDIRECTORY (Function) III: 24-17 D D (Command) II: 16-43 DASSEM.SAVELOCALVARS (Function) II: 18-5 DATATYPE (Record Type) I: 8-7 DATATYPES (Function) I: 8-16 DATE (Function) I: 12-9 DATEFORMAT (Function) I: 12-9 DC (Function) II: 16-2 DCHCON (Function) I: 2-10 Debugging II: 15-1 DECLARE DECL (I.S. Operator) I: 9-13 DECLARE: (Command) II: 17-31 DECLARE: DECL (I.S. Operator) I: 9-13 DECLAREDATATYPE (Function) I: 8-16 DEdit II: 16-5; 16-4; 16-1 calling II: 16-1 commands II: 16-5 idioms II: 16-8 multiple commands II: 16-8 parameters II: 16-9 selecting objects and lists II: 16-4 DEditLinger (Variable) II: 16-9 DEDITTYPEINCOMS (Variable) II: 16-9 DEFAULTEOFCLOSE (Variable) III: 24-16 DEFAULTPRINTINGHOST (Variable) III: 29-3 DEFERREDCONSTANT (Function) II: 18-6 DEFINE (Function) I: 10-2 DEFINEQ (Function) I: 10-2 DEFLIST (Function) I: 2-5 DEFMACRO (Function) I: 10-6 DEFPRINT (Function) III: 25-12 DEL.PROCESS (Function) II: 23-3 DELDEF (File Package Type Property) II: 17-24 DELDEF (Function) II: 17-21 Delete (Command) II: 16-24; 16-6 DELETE (Property) II: 17-35 DELFILE (Function) III: 24-24 DELFROMFILES (Function) II: 17-37 DEPOSITBYTE (Function) I: 7-8 DESCRIPTION (File Package Type Property) II: 17-24 DF (Function) II: 16-2 DFNFLG (Variable) I: 10-3 DIFFERENCE (Function) I: 7-2 DIR (Function) III: 24-27 DIRECTORIES (Variable) III: 24-24 DIRECTORY (Function) III: 24-25 DIRECTORYNAME (Function) III: 24-8 DIRECTORYNAMEP (Function) III: 24-8 DISCARDPUPS (Function) III: 31-23 DISCARDXIPS (Function) III: 31-29 DISKFREEPAGES (Function) III: 24-18 DISKPARTITION (Function) III: 24-18 DISMISS (Function) II: 23-4 DMPHASH (Function) I: 6-2 DO (Command) II: 16-41 DO FORM (I.S. Operator) I: 9-8 DOCOLLECT (Function) I: 3-5 DP (Function) II: 16-2 DPB (Macro) I: 7-8 DREMOVE (Function) I: 3-15 DREVERSE (Function) I: 3-15 DSKDISPLAY (Function) III: 24-18 DSUBLIS (Function) I: 3-11 DSUBST (Function) I: 3-10 DT.EDITMACROS (Variable) II: 16-10 DUMMYFRAMEP (Function) I: 11-10 DUNPACK (Function) I: 2-7 during (I.S. Operator) I: 12-12 DV (Function) II: 16-2 DW (Command) II: 16-41 DW (Editor Command) II: 21-19 DWIM functions II: 20-10 operation II: 20-5 variables II: 20-10 DWIM (Function) II: 20-3 DWIMCHECK#ARGSFLG (Variable) II: 21-16 DWIMCHECKPROGLABELSFLG (Variable) II: 21-16 DWIMESSGAG (Variable) II: 21-16 DWIMFLG (Variable) II: 20-10 DWIMIFY (Function) II: 21-14 DWIMIFYCOMPFLG (Variable) II: 21-16 DWIMIFYFLG (Variable) II: 20-9 DWIMIFYFNS (Function) II: 21-15 DWIMINMACROSFLG (Variable) II: 21-15 DWIMLOADFNSFLG (Variable) II: 20-10 DWIMUSERFORMS (Variable) II: 20-8 DWIMWAIT (Variable) II: 20-10 E E (Command) II: 16-43 E X (Command) II: 16-43 E X T (Command) II: 16-43 EACHTIME FORM (I.S. Operator) I: 9-13 EDIT (Break Command) II: 14-9 Edit (Command) II: 16-7 EDIT (Function) II: 16-51 EDIT4E (Function) II: 16-55 EDITCALLERS (Function) II: 16-56 EditCom (Command) II: 16-7 EDITDEF (File Package Type Property) II: 17-24 EDITDEF (Function) II: 17-21 EDITE (Function) II: 16-54 EDITEMBEDTOKEN (Variable) II: 16-28; 16-9 EDITF (Function) II: 16-51 EDITFINDP (Function) II: 16-55 EDITFNS (Function) II: 16-53 EDITFPAT (Function) II: 16-55 EDITL (Function) II: 16-54 EDITL0 (Function) II: 16-55 EDITLOADFNS? (Function) II: 16-55 EDITMODE (Function) II: 16-3 EDITP (Function) II: 16-53 EDITRACEFN (Variable) II: 16-56 EDITREC (Function) I: 8-12 EDITV (Function) II: 16-53 EF (Command) II: 16-39 EFTP (Function) III: 31-5 ELT (Function) I: 5-1 EMBED (Command) II: 16-28 ENCAPSULATE.ETHERPACKET (Function) III: 31-30 EndDST (Variable) I: 12-11 ENDFILE (Function) III: 25-25 ENTRY# (Function) II: 13-30 ENVAPPLY (Function) I: 11-6 ENVEVAL (Function) I: 11-6 EOFP (Function) III: 25-5 EP (Command) II: 16-39 EQ (Function) I: 9-2 EQLENGTH (Function) I: 3-8 EQMEMB (Function) I: 3-10 EQP (Function) I: 7-6; 7-2; 9-2 EQUAL (Function) I: 9-2 EQUALALL (Function) I: 9-3 EQUALN (Function) I: 3-8 ERROR (Function) II: 14-14 Error messages, compiler II: 18-16 ERROR! (Function) II: 14-15 ERRORMESS (Function) II: 14-15 ERRORMESS (Variable) II: 14-17 ERRORMESS1 (Function) II: 14-15 ERRORN (Function) II: 14-15 ERRORPOS (Variable) II: 14-17 ERRORSET (Function) II: 14-16 ERRORSTRING (Function) II: 14-16 ERRORTYPELST (Variable) II: 14-17 ERRORX (Function) II: 14-14 ERSETQ (Function) II: 14-16 ESCAPE (Function) III: 25-29 ESUBST (Function) II: 16-55 ETHERHOSTNAME (Function) III: 31-5 ETHERHOSTNUMBER (Function) III: 31-4 Ethernet NS protocol functions III: 31-5 protocols III: 31-1 higher level III: 31-3 layering III: 31-1 level one III: 31-2 level zero III: 31-2 PUP protocol functions III: 31-4 ETHERPORT (Function) III: 31-4 EV (Command) II: 16-39 EVAL (Break Command) II: 14-4 Eval (Command) II: 16-44; 16-7 EVAL.AS.PROCESS (Function) II: 23-14 EVAL.IN.TTY.PROCESS (Function) II: 23-14 EVALV (Function) I: 11-6 EVENP (Function) I: 7-7 EXAM (Command) II: 16-46 EXCHANGEPUPS (Function) III: 31-23 EXCHANGEXIPS (Function) III: 31-29 Exit (Command) II: 16-8 EXPANDMACRO (Function) I: 10-6 EXPR (Variable) II: 20-9 EXPRESSIONS (File Package Type) II: 17-17 EXPT (Function) I: 7-10 EXTRACT @1 FROM . @2 (Command) II: 16-27 F .F (Command) III: 25-22 F PATTERN (Command) II: 16-16 F PATTERN N (Command) II: 16-16 F PATTERN NIL (Command) II: 16-17 F PATTERN T (Command) II: 16-16 F= (Command) II: 16-17 FASSOC (Function) I: 3-12 FAULTAPPLYFLG (Variable) II: 20-9 FAULTARGS (Variable) II: 20-9 FAULTFN (Variable) II: 20-9 FAULTX (Variable) II: 20-9 FCHARACTER (Function) I: 2-10 FDIFFERENCE (Function) I: 7-9 FEQP (Function) I: 7-10 FETCHFIELD (Function) I: 8-17 FFILEPOS (Function) III: 25-16 FGREATERP (Function) I: 7-10 FIELDLOOK (Function) I: 8-12 FIELDS (File Package Type) II: 17-17 FILDIR (Function) III: 24-27 FILE (Property) II: 17-15 File directories, searching III: 24-24 FILECHANGES (Property) II: 17-15 FILEDATES (Property) II: 17-15 FILEGETDEF (File Package Type Property) II: 17-23 FILELST (Variable) II: 17-16 FILEMAP (Property) II: 17-15 FILENAMEFIELD (Function) III: 24-6 FILEPKGCHANGES (Function) II: 17-14 FILEPKGCOM (Function) II: 17-36 FILEPKGCOMS (File Package Type) II: 17-17 FILEPKGFLG (Variable) II: 17-3 FILEPKGTYPE (Function) II: 17-25 FILEPOS (Function) III: 25-15 Files copying III: 24-24 deleting III: 24-24 renaming III: 24-24 FILES (File Package Type) II: 17-18 FILES? (Function) II: 17-10 FILESLOAD (Function) II: 17-6 FILEVARS (File Package Type) II: 17-18 FINALLY FORM (I.S. Operator) I: 9-13 Find (Command) II: 16-6 FIND.PROCESS (Function) II: 23-4 FINDCALLERS (Function) II: 16-56 FINDFILE (Function) III: 24-25 FIRST FORM (I.S. Operator) I: 9-13 FIX (Command) II: 13-9 FIX (Function) I: 7-6 FIXP (Function) I: 7-1; 9-1 FIXR (Function) I: 7-6 FIXSPELL (Function) II: 20-16 FIXSPELLDEFAULT (Variable) II: 20-10 FLAST (Function) I: 3-7 FLENGTH (Function) I: 2-12; 3-8 FLESSP (Function) I: 7-10 FLOAT (Function) I: 7-10 Floating point arithmetic I: 7-8 FLOATP (Function) I: 7-1; 9-1 FLOPPY.ARCHIVE (Function) III: 24-22 FLOPPY.CAN.READP (Function) III: 24-21 FLOPPY.CAN.WRITEP (Function) III: 24-21 FLOPPY.FORMAT (Function) III: 24-20 FLOPPY.FREE.PAGES (Function) III: 24-21 FLOPPY.FROM.FILE (Function) III: 24-22 FLOPPY.MODE (Function) III: 24-19 FLOPPY.NAME (Function) III: 24-21 FLOPPY.SCAVENGE (Function) III: 24-21 FLOPPY.TO.FILE (Function) III: 24-21 FLOPPY.UNARCHIVE (Function) III: 24-22 FLOPPY.WAIT.FOR.FLOPPY (Function) III: 24-21 FLTFMT (Function) III: 25-10 FLUSHRIGHT (Function) III: 25-24 FMAX (Function) I: 7-10 FMEMB (Function) I: 3-10 FMIN (Function) I: 7-10 FMINUS (Function) I: 7-9 FN - ILLEGAL RETURN (Error Message) II: 18-17 FN - NO LONGER INTERPRETED AS FUNCTIONAL ARGUMENT (Error Message) II: 18-17 FN - USED AS ARG TO NUMBER FN? (Error Message) II: 18-17 FN CAN'T BE BOTH AN ENTRY AND THE BLOCK NAME (Error Message) II: 18-17 FN NOT COMPILEABLE (Error Message) II: 18-16 FN NOT FOUND (Error Message) II: 18-16 FN NOT ON BLKFNS (Error Message) II: 18-17 FN NOT ON FILE, COMPILING IN CORE DEFINITION (Error Message) II: 18-16 FNCHECK (Function) II: 20-17 FNS (Command) II: 17-26 FNS (File Package Type) II: 17-18 FNTH (Function) I: 3-7 .FONT (Command) III: 25-20 FOR OLD VAR (I.S. Operator) I: 9-9 FOR VAR (I.S. Operator) I: 9-9 FOR VARS (I.S. Operator) I: 9-9 FORCEOUTPUT (Function) III: 25-8 forDuration (I.S. Operator) I: 12-12 FORGET (Command) II: 13-13 FPLUS (Function) I: 7-9 FQUOTIENT (Function) I: 7-9 .FR (Command) III: 25-22 .FR2 (Command) III: 25-22 FRAMESCAN (Function) I: 11-5 FREERESOURCE (Macro) I: 12-16 FREMAINDER (Function) I: 7-9 FRESHLINE (Function) III: 25-7 FROM FORM (I.S. Operator) I: 9-10 FRPLACA (Function) I: 3-2 FRPLACD (Function) I: 3-2 FRPLNODE (Function) I: 3-2 FRPLNODE2 (Function) I: 3-2 FS (Command) II: 16-17 FTIMES (Function) I: 7-9 FULLNAME (Function) III: 24-9 FUNNYATOMLST (Variable) II: 21-17 G GAINSPACE (Function) II: 22-9 GATHEREXPORTS (Function) II: 17-33 GCD (Function) I: 7-6 GCGAG (Function) II: 22-2 GDATE (Function) I: 12-9 GE (CLISP Operator) II: 21-6 GENERATE (Function) I: 11-13 GENERATOR (Function) I: 11-13 Generic arithmetic I: 7-2 GENNUM (Variable) I: 2-8 GENSYM (Function) I: 2-8 GEQ (Function) I: 7-3 GET* (Command) II: 16-42 GETBRK (Function) III: 25-28 GETCASEARRAY (Function) III: 25-16 GETD (Function) I: 10-3 GETDEF (File Package Type Property) II: 17-23 GETDESCRIPTORS (Function) I: 8-17 GETEOFPTR (Function) III: 25-15 GETFIELDSPECS (Function) I: 8-17 GETFILEINFO (Function) III: 24-12 GETFILEPTR (Function) III: 25-14 GETHASH (Function) I: 6-2 GETLIS (Function) I: 2-5 GETPROP (Function) I: 2-4 GETPROPLIST (Function) I: 2-5 GETPUP (Function) III: 31-23 GETPUPBYTE (Function) III: 31-24 GETPUPSTRING (Function) III: 31-24 GETPUPWORD (Function) III: 31-24 GETREADTABLE (Function) III: 25-26 GETRESOURCE (Macro) I: 12-16 GETSEPR (Function) III: 25-28 GETSTREAM (Function) III: 25-1 GETTOPVAL (Function) I: 2-3 GETVAL (Command) II: 16-44 GETXIP (Function) III: 31-29 GIVE.TTY.PROCESS (Function) II: 23-10 GLC (Function) I: 4-3 GNC (Function) I: 4-2 GO (Break Command) II: 14-4 GO LABEL (Command) II: 16-18 GREATERP (Function) I: 7-3 GREET (Function) I: 12-1 GREETDATES (Variable) I: 12-2 GREETFILENAME (Function) I: 12-1 GT (CLISP Operator) II: 21-6 H HARDCOPYW (Function) III: 29-2 HARDRESET (Function) II: 23-1 HARRAY (Function) I: 6-1 HARRAYP (Function) I: 6-1; 9-2 HARRAYPROP (Function) I: 6-1 HARRAYSIZE (Function) I: 6-2 HASDEF (File Package Type Property) II: 17-24 HASDEF (Function) II: 17-20 Hash arrays I: 6-1 overflow I: 6-3 user-specified functions I: 6-3 HASHARRAY (Function) I: 6-1 HASHLINK (Record Type) I: 8-6 HASTTYWINDOWP (Function) II: 23-9 HCOPYALL (Function) I: 3-6; III: 25-13 HELP (Function) II: 14-15 HISTORYFIND (Function) II: 13-29 HISTORYMATCH (Function) II: 13-30 HISTORYSAVE (Function) II: 13-29 HISTORYSAVEFORMS (Variable) II: 13-17 HOSTNAMEP (Function) III: 24-9 HPRINT (Function) III: 25-13 HREAD (Function) III: 25-13 I .I (Command) III: 25-22 I.S. Types I: 9-8 (I.S.OPR NAME FORM OTHERS EVALFLG) (Function) I: 9-15 I.S.OPRS (File Package Type) II: 17-18 IDATE (Function) I: 12-9 IF (Command) II: 16-45 IF statement I: 9-4 IFY (Command) II: 16-42 IGEQ (Function) I: 7-5 IGREATERP (Function) I: 7-5 ILEQ (Function) I: 7-5 ILESSP (Function) I: 7-5 IMAX (Function) I: 7-5 IMIN (Function) I: 7-5 IMOD (Function) I: 7-5 IMPORTFILE (Function) II: 17-33 IN FORM (I.S. Operator) I: 9-10 IN OLD (VAR_FORM) (I.S. Operator) I: 9-10 IN OLD VAR (I.S. Operator) I: 9-10 IN? (Break Command) II: 14-10 INFILE (Function) III: 24-11 INFILECOMS? (Function) II: 17-37 INFILEP (Function) III: 24-10 INFO (Property) II: 21-15 INITRESOURCE (Macro) I: 12-16 INPUT (Function) III: 25-2 Input/Output Functions specifying streams for III: 25-1 INREADMACROP (Function) III: 25-32 INSIDE FORM (I.S. Operator) I: 9-10 Integer arithmetic I: 7-3 Interlisp executive II: 13-1 input formats II: 13-3 programmer's assistant commands II: 13-4 INTERSECTION (Function) I: 3-9 IOFILE (Function) III: 24-11 IQUOTIENT (Function) I: 7-5 IREMAINDER (Function) I: 7-5 IT (Variable) II: 13-15 Iterative statement I: 9-7 ITIMES (Function) I: 7-5 J JOIN FORM (I.S. Operator) I: 9-8 JOINC (Command) II: 16-40 L L-CASE (Function) I: 2-7 LAMBDASPLST (Variable) II: 20-11 LAMS (Variable) II: 18-7 LARGEST FORM (I.S. Operator) I: 9-9 LAST (Function) I: 3-7 LASTC (Function) III: 25-4 LASTN (Function) I: 3-7 LASTVMEMFILEPAGE (Variable) I: 12-7 LC . @ (Command) II: 16-18 LCONC (Function) I: 3-5 LDB (Macro) I: 7-8 LDIFFERENCE (Function) I: 3-9 LE (CLISP Operator) II: 21-6 LENGTH (Function) I: 3-8 LEQ (Function) I: 7-3 LESSP (Function) I: 7-3 LI N (Command) II: 16-31 line-feed (Command) II: 16-13 LINELENGTH (Function) III: 25-8 LISP-IMPLEMENTATION-TYPE (Function) I: 12-7 LISP-IMPLEMENTATION-VERSION (Function) I: 12-8 LISPDIRECTORYP (Function) III: 24-17 LISPFN (Property) II: 21-21 LISPUSERSDIRECTORIES (Variable) III: 24-24 LISPX (Function) II: 13-26 LISPX/ (Function) II: 13-31 LISPXEVAL (Function) II: 13-26 LISPXFIND (Function) II: 13-29 LISPXHISTORYMACROS (Variable) II: 13-18 LISPXMACROS (File Package Type) II: 17-18 LISPXMACROS (Variable) II: 13-18 LISPXPRINT (Function) II: 13-19 LISPXPRINTDEF (Function) II: 13-19 LISPXPRINTFLG (Variable) II: 13-19 LISPXREAD (Function) II: 13-28 LISPXREADP (Function) II: 13-28 LISPXSPACES (Function) II: 13-19 LISPXSTOREVALUE (Function) II: 13-29 LISPXTAB (Function) II: 13-19 LISPXTERPRI (Function) II: 13-19 LISPXUSERFN (Variable) II: 13-18 List structure editor II: 16-1 LIST* (Function) I: 3-3 LISTGET (Function) I: 3-12 LISTGET1 (Function) I: 3-13 LISTP (Function) I: 3-1; 9-1 LISTPUT1 (Function) I: 3-13 LITATOM (Function) I: 2-1; 9-1 LLSH (Function) I: 7-7 LO N (Command) II: 16-31 LOAD (Function) II: 17-4 LOAD? (Function) II: 17-4 LOADBLOCK (Function) II: 17-6 LOADBYTE (Function) I: 7-7 LOADCOMP (Function) II: 17-6 LOADCOMP? (Function) II: 17-6 LOADDEF (Function) II: 17-22 LOADEDFILELST (Variable) II: 17-16 LOADFNS (Function) II: 17-5 LOADFROM (Function) II: 17-6 LOADTIMECONSTANT (Function) II: 18-6 LOADVARS (Function) II: 17-6 Location specification in the editor II: 16-18 LOG (Function) I: 7-10 LOGAND (Function) I: 7-6 Logical arithmetic functions I: 7-6 LOGINHOST/DIR (Variable) III: 24-8 LOGNOT (Macro) I: 7-7 LOGOR (Function) I: 7-6 LOGOUT (Function) I: 12-4 LOGXOR (Function) I: 7-6 LONG-SITE-NAME (Function) I: 12-8 LOOKUP.NS.SERVER (Function) III: 31-7 LOWER (Command) II: 16-39 LOWERCASE (Function) II: 21-20 LPARKEY (Variable) II: 20-10 LPQ (Command) II: 16-46 LSH (Function) I: 7-6 LSUBST (Function) I: 3-10 LT (CLISP Operator) II: 21-6 LVLPRIN1 (Function) III: 25-10 LVLPRIN2 (Function) III: 25-9 LVLPRINT (Function) III: 25-9 M MACHINE-INSTANCE (Function) I: 12-8 MACHINE-TYPE (Function) I: 12-8 MACHINE-VERSION (Function) I: 12-8 MACHINETYPE (Function) I: 12-9 MACRO (Property) II: 17-34 MACROS (File Package Type) II: 17-18 MAKEBITTABLE (Function) I: 4-5 MAKEFILE (Function) II: 17-8 MAKEFILES (Function) II: 17-9 MAKESYS (Function) I: 12-6 MAKESYSDATE (Variable) I: 12-8 MAKESYSNAME (Variable) I: 12-8 MAP.PROCESSES (Function) II: 23-4 MAPATOMS (Function) I: 2-8 MAPHASH (Function) I: 6-2 MARK (Command) II: 16-21 MARKASCHANGED (Function) II: 17-14 MASK.0'S (Macro) I: 7-7 MASK.1'S (Macro) I: 7-7 MAX (Function) I: 7-3 MAX.FIXP (Variable) I: 7-4 MAX.FLOAT (Variable) I: 7-9 MAX.INTEGER (Variable) I: 7-4 MAX.SMALLP (Variable) I: 7-4 MaxBkMenuHeight (Variable) II: 14-11 MaxBkMenuWidth (Variable) II: 14-11 MBD (Command) II: 16-27 MEMB (Function) I: 3-10 MEMBER (Function) I: 3-10 MERGE (Function) I: 3-14 MERGEINSERT (Function) I: 3-14 MIN (Function) I: 7-3 MIN.FIXP (Variable) I: 7-4 MIN.FLOAT (Variable) I: 7-9 MIN.INTEGER (Variable) I: 7-4 MIN.SMALLP (Variable) I: 7-4 MINUS (Function) I: 7-2 MINUSP (Function) I: 7-3 MISSPELLED? (Function) II: 20-16 MKATOM (Function) I: 2-6 MKLIST (Function) I: 3-3 MONITOR.AWAIT.EVENT (Function) II: 23-7 MOVD (Function) I: 10-3 MOVD? (Function) I: 10-3 MOVE @1 TO (Command) II: 16-28 N .N (Command) III: 25-23 NAME (Command) II: 13-11 NCHARS (Function) I: 2-7 NCONC (Function) I: 3-4 NCONC1 (Function) I: 3-4 NCREATE (Function) I: 8-17 NDIR (Function) III: 24-27 NEGATE (Command) II: 16-40 NEGATE (Function) I: 3-16 NEQ (Function) I: 9-2 NEVER FORM (I.S. Operator) I: 9-9 NEWCOM (File Package Type Property) II: 17-24 NEWRESOURCE (Macro) I: 12-16 NEX (Command) II: 16-20 NIL (Command) II: 16-41 NILNUMPRINTFLG (Variable) III: 25-12 NLAMA (Variable) II: 18-7 NLAML (Variable) II: 18-7 NLEFT (Function) I: 3-7 NLISTP (Function) I: 3-1; 9-1 NLSETQ (Function) II: 14-16 NLSETQGAG (Variable) II: 14-17 NO BINARY CODE GENERATED OR LOADED FOR FN (Error Message) II: 18-19 NOCLEARSTKLST (Variable) I: 11-7 NOFIXFNSLST (Variable) II: 21-15 NOFIXVARSLST (Variable) II: 21-15 NOSPELLFLG (Variable) II: 20-10; 21-15 NOT (Function) I: 9-2 NOTE (Function) I: 11-15 NOTIFY.EVENT (Function) II: 23-6 NSNAME.TO.STRING (Function) III: 31-6 NSOCKETEVENT (Function) III: 31-28 NSOCKETNUMBER (Function) III: 31-28 NSPRINT (Function) III: 31-9 NSPRINTER.PROPERTIES (Function) III: 31-9 NSPRINTER.STATUS (Function) III: 31-9 NTH (Command) II: 16-13 NTH (Function) I: 3-7 NTHCAR (Function) I: 2-7 NTHCHARCODE (Function) I: 2-10 NULL (Function) I: 9-2 NULL Device III: 24-24 NULLDEF (File Package Type Property) II: 17-23 NUMBERP (Function) I: 7-1; 9-1 Numbers I: 7-1 bignums I: 7-1 floating-point I: 7-1 large integers I: 7-1 small integers I: 7-1 NX (Command) II: 16-12 O OBTAIN.MONITORLOCK (Function) II: 23-8 OCTALSTRING (Function) III: 31-27 ODDP (Function) I: 7-7 OK (Break Command) II: 14-4 OK (Command) II: 16-37; 16-8 OKREEVALST (Variable) II: 20-10 ON FORM (I.S. Operator) I: 9-10 ON OLD (VAR_FORM) (I.S. Operator) I: 9-10 ON OLD VAR (I.S. Operator) I: 9-10 OP - OPCODE? - ASSEMBLE (Error Message) II: 18-19 OPENFILE (Function) III: 24-11 OPENNSOCKET (Function) III: 31-28 OPENP (Function) III: 24-3 OPENPUPSOCKET (Function) III: 31-22 OPENSTREAM (Function) III: 24-1 OPENSTRINGSTREAM (Function) III: 24-22 (OR X1 X2 ... XN) (Function) I: 9-3 ORF (Command) II: 16-17 ORIGINAL (Break Command) II: 14-8 ORIGINAL (Command) II: 16-48; 17-31 ORIGINAL I.S.OPR OPERAND (I.S. Operator) I: 9-13 ORR (Command) II: 16-46 OUTFILE (Function) III: 24-11 OUTFILEP (Function) III: 24-10 OUTOF FORM (I.S. Operator) I: 9-12 OUTPUT (Function) III: 25-6 Output Functions III: 25-6 OVERFLOW (Function) I: 7-2 P P (Command) II: 16-35 .P2 (Command) III: 25-21 PACK (Function) I: 2-6 PACK* (Function) I: 2-7 PACKC (Function) I: 2-9 PACKFILENAME (Function) III: 24-7 PACKFILENAME.STRING (Function) III: 24-6 .PAGE (Command) III: 25-20 PAGEFAULTS (Function) II: 22-6 .PARA (Command) III: 25-21 .PARA2 (Command) III: 25-21 PARENT (Variable) II: 20-9 PARSE.NSNAME (Function) III: 31-6 PB (Break Command) II: 14-6 PB (Command) II: 13-14 PEEKC (Function) III: 25-4 PEEKCCODE (Function) III: 25-4 PL (Command) II: 13-13 PLUS (Function) I: 7-2 PLVLFILEFLG (Variable) III: 25-9 POP (Change Word) I: 8-14 Pop (Command) II: 16-7 PORTSTRING (Function) III: 31-27 POSITION (Function) III: 25-8 POWEROFTWOP (Function) I: 7-7 PP (Command) II: 16-35 PP* (Command) II: 16-36 .PPF (Command) III: 25-21 .PPFTL (Command) III: 25-21 PPT (Command) II: 16-36 .PPV (Command) II: 16-36; III: 25-21 .PPVTL (Command) III: 25-21 PRIN1 (Function) III: 25-6 PRIN2 (Function) III: 25-6 PRIN3 (Function) III: 25-6 PRIN4 (Function) III: 25-6 PRINT (Function) III: 25-7; 25-6 PRINT-LISP-INFORMATION (Function) I: 12-7 PRINTBELLS (Function) III: 25-8 PRINTCONSTANT (Function) III: 31-27 PRINTERSTATUS (Function) III: 29-3 PRINTERTYPE (Function) III: 29-4 PRINTERTYPES (Variable) III: 29-4 PRINTFILETYPE (Function) III: 29-4 PRINTFILETYPES (Variable) III: 29-5 PRINTHISTORY (Function) II: 13-31 Printing numbers III: 25-10 PRINTLEVEL (Function) III: 25-8 PRINTMSG (Variable) II: 14-17 PRINTOUTMACROS (Variable) III: 25-23 PRINTPACKET (Function) III: 31-31 PRINTPACKETDATA (Function) III: 31-27 PRINTPARA (Function) III: 25-24 PRINTPUP (Function) III: 31-25 PRINTPUPROUTE (Function) III: 31-27 PRINTROUTINGTABLE (Function) III: 31-23 PROCESS.APPLY (Function) II: 23-5 PROCESS.EVAL (Function) II: 23-5 PROCESS.EVALV (Function) II: 23-5 PROCESS.FINISHEDP (Function) II: 23-4 PROCESS.RETURN (Function) II: 23-3 PROCESS.STATUS.WINDOW (Function) II: 23-13 PROCESSP (Function) II: 23-4 PROCESSPROP (Function) II: 23-2 PROCESSWORLD (Function) II: 23-1 PRODUCE (Function) I: 11-13 PROMPT#FLG (Variable) II: 13-16 PROMPTCHARFORMS (Variable) II: 13-17 PROPNAMES (Function) I: 2-5 PROPRECORD (Record Type) I: 8-6 PROPS (File Package Type) II: 17-18 PRXFLG (Variable) III: 25-10 PSETQ (Macro) I: 2-3 PUP.ECHOUSER (Function) III: 31-26 PUPIGNORETYPES (Variable) III: 31-25 PUPNET.DISTANCE (Function) III: 31-23 PUPONLYTYPES (Variable) III: 31-25 PUPPRINTMACROS (Variable) III: 31-25 PUPs, sending and receiving III: 31-23 PUPSOCKETEVENT (Function) III: 31-22 PUPSOCKETNUMBER (Function) III: 31-22 PUPTRACE (Function) III: 31-25 PUPTRACEFILE (Variable) III: 31-25 PUPTRACEFLG (Variable) III: 31-24 PUPTRACETIME (Variable) III: 31-25 PURGEDSKDIRECTORY (Function) III: 24-17 PUSH (Change Word) I: 8-14 PUSHLIST (Change Word) I: 8-14 PUTASSOC (Function) I: 3-12 PUTD (Function) I: 10-3 PUTDEF (File Package Type Property) II: 17-23 PUTDEF (Function) II: 17-20 PUTHASH (Function) I: 6-2 PUTPROP (Function) I: 2-4 PUTPUPBYTE (Function) III: 31-24 PUTPUPSTRING (Function) III: 31-24 PUTPUPWORD (Function) III: 31-24 Q Q (Command) II: 16-43 QUOTIENT (Function) I: 7-3 R R1 (Command) II: 16-35 RADIX (Function) III: 25-10 RAISE (Command) II: 16-39 RAND (Function) I: 7-11 RANDACCESSP (Function) III: 25-15 RANDSET (Function) I: 7-11 RATEST (Function) III: 25-3 RATOMS (Function) III: 25-3 RC (Command) II: 16-35 RC1 (Command) II: 16-35 READ (Function) III: 25-2 READBUF (Variable) II: 13-27 READC (Function) III: 25-3 READCCODE (Function) III: 25-4 READFILE (Function) III: 25-25 READLINE (Function) II: 13-27 READMACROS (Function) III: 25-32 READP (Function) III: 25-4 READTABLEP (Function) III: 25-26 READVISE (Function) II: 15-9 REALFRAMEP (Function) I: 11-10 REALMEMORYSIZE (Function) I: 12-7 REBREAK (Function) II: 15-6 RECLAIM (Function) II: 22-2 RECLAIMMIN (Function) II: 22-2 RECLAIMWAIT (Variable) II: 22-2 RECLOOK (Function) I: 8-12 RECOMPILE (Function) II: 18-11 RECORD (Record Type) I: 8-5 RECORDACCESS (Function) I: 8-13 RECORDFIELDNAMES (Function) I: 8-13 RECORDS (File Package Type) II: 17-18 REDO (Command) II: 13-7 REHASH (Function) I: 6-2 RELEASE.MONITORLOCK (Function) II: 23-8 RELEASE.PUP (Function) III: 31-22 RELEASE.XIP (Function) III: 31-28 RELPROCESSP (Function) II: 23-4 RELSTK (Function) I: 11-7 RELSTKP (Function) I: 11-7 REMAINDER (Function) I: 7-3 REMEMBER (Command) II: 13-13 REMOVE (Function) I: 3-15 REMPROP (Function) I: 2-4 REMPROPLIST (Function) I: 2-4 RENAME (Function) II: 17-22 RENAMEFILE (Function) III: 24-24 REPACK (Command) II: 16-40 REPACK @ (Command) II: 16-40 REPEAT (Command) II: 13-7 REPEATUNTIL FORM (I.S. Operator) I: 9-12 REPEATUNTIL N (N a number) (I.S. Operator) I: 9-12 REPEATWHILE FORM (I.S. Operator) I: 9-12 Replace (Command) II: 16-6 REPLACEFIELD (Function) I: 8-17 Reprint (Command) II: 16-7 RESET (Function) II: 14-15 RESETDEDIT (Function) II: 16-3 RESETFORM (Function) II: 14-19 RESETFORMS (Variable) II: 13-17 RESETLST (Function) II: 14-18 RESETREADTABLE (Function) III: 25-26 RESETSAVE (Function) II: 14-18 RESETVAR (Function) II: 14-19 RESETVARS (Function) II: 14-19 resourceName (I.S. Operator) I: 12-13 RESOURCES (File Package Type) II: 17-18 RESTART.ETHER (Function) III: 31-29 RESTART.PROCESS (Function) II: 23-4 RESUME (Function) I: 11-14 RETAPPLY (Function) I: 11-7 RETEVAL (Function) I: 11-7 RETFNS (Variable) II: 18-14 RETFROM (Function) I: 11-6 RETRIEVE (Command) II: 13-12 RETRY (Command) II: 13-7 RETTO (Function) I: 11-6 RETURN (Break Command) II: 14-4 REVERSE (Function) I: 3-15 REVERT (Break Command) II: 14-7 RI N M (Command) II: 16-31 RO (Command) II: 16-31 ROT (Function) I: 7-8 RPARKEY (Variable) II: 20-10 RPLACD (Function) I: 3-2 RPLCHARCODE (Function) I: 4-4 RPLNODE (Function) I: 3-2 RPLNODE2 (Function) I: 3-2 RPLSTRING (Function) I: 4-3 RSH (Function) I: 7-6 RSTRING (Function) III: 25-3 RUNONFLG (Variable) II: 20-10 S SASSOC (Function) I: 3-12 SAVE (Command) II: 16-37 SAVEDEF (Function) II: 17-21 SAVESET (Function) II: 13-21 SAVEVM (Function) I: 12-4 SAVEVMMAX (Variable) I: 12-4 SAVEVMWAIT (Variable) I: 12-4 SCAVENGEDSKDIRECTORY (Function) III: 24-18 SCRATCHLIST (Function) I: 3-6 Searching in an editor II: 16-14 SEARCHPDL (Function) I: 11-10 SEND.FILE.TO.PRINTER (Function) III: 29-1 SENDPUP (Function) III: 31-23 SENDXIP (Function) III: 31-29 SEPRCASE (Function) III: 25-16 SET (Function) I: 2-2 SETA (Function) I: 5-1 SETATOMVAL (Function) I: 2-3 SETBLIPVAL (Function) I: 11-12 SETBRK (Function) III: 25-29 SETCASEARRAY (Function) III: 25-16 SETERRORN (Function) II: 14-15 SETFILEINFO (Function) III: 24-13 SETFILEPTR (Function) III: 25-14 SETFN (Property) II: 21-21 SETLINELENGTH (Function) III: 25-8 SETPROPLIST (Function) I: 2-5 SETQ (Function) I: 2-2 SETQQ (Function) I: 2-2 SETREADTABLE (Function) III: 25-26 SETSEPR (Function) III: 25-29 SETSTKARG (Function) I: 11-5 SETSTKARGNAME (Function) I: 11-5 SETSTKNAME (Function) I: 11-4 SETTERMCHARS (Function) II: 16-57 SETTIME (Function) I: 12-10 SETTOPVAL (Function) I: 2-3 SETUPPUP (Function) III: 31-24 SETUPTIMER (Function) I: 12-12 SETUPTIMER.DATE (Function) I: 12-12 SHH (Command) II: 13-14 SHORT-SITE-NAME (Function) I: 12-8 SHOULDNT (Function) II: 14-15 SHOW (Command) II: 16-46 SHOW.CLEARINGHOUSE (Function) III: 31-7 SHOWDEF (Function) II: 17-21 SHOWPRIN2 (Function) III: 25-7 SHOWPRINT (Function) III: 25-7 SIN (Function) I: 7-10 .SKIP (Command) III: 25-20 SKIPSEPRS (Function) III: 25-5 SKREAD (Function) III: 25-5 SMALLEST FORM (I.S. Operator) I: 9-9 SMALLP (Function) I: 7-1; 9-1 SOFTWARE-TYPE (Function) I: 12-8 SOFTWARE-VERSION (Function) I: 12-8 SORT (Function) I: 3-13 SORT.PUPHOSTS.BY.DISTANCE (Function) III: 31-23 .SP (Command) III: 25-19 SPACES (Function) III: 25-7 SPAWN.MOUSE (Function) II: 23-12 SPELLFILE (Function) III: 24-25 Spelling correction II: 20-11 algorithm II: 20-14 functions II: 20-15 generators for II: 20-14 spelling lists II: 20-12 synonyms II: 20-12 variables II: 20-15 SPELLINGS1 (Variable) II: 20-12 SPELLINGS2 (Variable) II: 20-12 SPELLINGS3 (Variable) II: 20-13 SPLITC (Command) II: 16-40 SPP.CLEARATTENTION (Function) III: 31-11 SPP.CLEAREOM (Function) III: 31-11 SPP.DSTYPE (Function) III: 31-10 SPP.OPEN (Function) III: 31-9 SPP.SENDATTENTION (Function) III: 31-11 SPP.SENDEOM (Function) III: 31-10 SPP.USER.TIMEOUT (Variable) III: 31-10 SPPOUTPUTSTREAM (Function) III: 31-10 SQRT (Function) I: 7-10 STACKP (Function) I: 11-7 START.CLEARINGHOUSE (Function) III: 31-7 STKAPPLY (Function) I: 11-6 STKARG (Function) I: 11-5 STKARGNAME (Function) I: 11-5 STKARGS (Function) I: 11-5 STKEVAL (Function) I: 11-6 STKNAME (Function) I: 11-4 STKNARGS (Function) I: 11-5 STKNTH (Function) I: 11-4 STKNTHNAME (Function) I: 11-4 STKPOS (Function) I: 11-4 STKSCAN (Function) I: 11-5 Stop (Command) II: 16-37; 16-8 STORAGE (Function) II: 22-2 STORAGE.LEFT (Function) II: 22-4 STREAMP (Function) III: 25-2 STRINGHASHBITS (Function) I: 6-4 STRINGP (Function) I: 9-1 STRPOS (Function) I: 4-4 STRPOSL (Function) I: 4-4 .SUB (Command) III: 25-20 SUB1 (Function) I: 7-5 SUBATOM (Function) I: 2-6 SUBLIS (Function) I: 3-11 SUBPAIR (Function) I: 3-11 SUBST (Function) I: 3-10 SUBSTRING (Function) I: 4-2 SUM FORM (I.S. Operator) I: 9-8 .SUP (Command) III: 25-20 SUSPEND.PROCESS (Function) II: 23-5 SW (Command) II: 16-35 SWAP (Change Word) I: 8-15 Swap (Command) II: 16-35; 16-7 SWAPC (Command) II: 16-40 SWAPPUPPORTS (Function) III: 31-24 Switch (Command) II: 16-6 SYNTAXP (Function) III: 25-28 SYSOUT (Function) I: 12-5 SYSOUTDATE (Variable) I: 12-8 SYSOUTGAG (Variable) I: 12-6 SYSTEMTYPE (Function) I: 12-9 T T (Command) III: 25-20 .TAB (Command) III: 25-19 TAB (Function) III: 25-7 .TAB0 (Command) III: 25-19 TAIL (Variable) II: 20-9 TAILP (Function) I: 3-6 TAN (Function) I: 7-11 TCOMPL (Function) II: 18-10 TCONC (Function) I: 3-4 TEMPLATES (File Package Type) II: 17-18 TERPRI (Function) III: 25-7 TEST (Command) II: 16-49 TG - ILLEGAL GO (Error Message) II: 18-17 TG - MULTIPLY DEFINED TAG (Error Message) II: 18-17 TG - MULTIPLY DEFINED TAG, ASSEMBLE (Error Message) II: 18-19 TG - MULTIPLY DEFINED TAG, LAP (Error Message) II: 18-19 TG - UNDEFINED TAG (Error Message) II: 18-19 TG - UNDEFINED TAG, ASSEMBLE (Error Message) II: 18-19 TG - UNDEFINED TAG, LAP (Error Message) II: 18-19 THEREIS FORM (I.S. Operator) I: 9-9 THIS.PROCESS (Function) II: 23-3 THRU (Command) II: 16-31 TIME (Function) II: 22-6 TIME.ZONES (Variable) I: 12-11 TIMEALL (Function) II: 22-5 timerUnits (I.S. Operator) I: 12-12 TIMES (Function) I: 7-2 TimeZoneComp (Variable) I: 12-11 TO (Command) II: 16-32 TO FORM (I.S. Operator) I: 9-11 TRACE (Function) II: 15-4 TRACEREGION (Variable) II: 14-12 TRACEWINDOW (Variable) II: 14-12 TRANSMIT.ETHERPACKET (Function) III: 31-30 TRYNEXT (Function) I: 11-16 TTY.PROCESS (Function) II: 23-9 TTY.PROCESSP (Function) II: 23-9 TYPE-AHEAD (Command) II: 13-14 TYPE-IN? (Variable) II: 20-9 TYPENAME (Function) I: 8-16 TYPENAMEP (Function) I: 8-16 TYPERECORD (Record Type) I: 8-5 TYPESOF (Function) II: 17-20 U U-CASE (Function) I: 2-8 U-CASEP (Function) I: 2-8 UALPHORDER (Function) I: 3-14 UB (Break Command) II: 14-5 UNADVISE (Function) II: 15-9 UNARYOP (Property) II: 21-20 UNBLOCK (Command) II: 16-49 UNBREAK (Function) II: 15-6 UNBREAK0 (Function) II: 15-6 UNBREAKIN (Function) II: 15-6 UNDO (Command) II: 13-10; 16-49; 16-48 UNDOLISPX (Function) II: 13-31 UNDONLSETQ (Function) II: 13-22 UNDOSAVE (Function) II: 13-30 UNLESS FORM (I.S. Operator) I: 9-12 UNMARKASCHANGED (Function) II: 17-14 UNPACK (Function) I: 2-7 UNPACKFILENAME (Function) III: 24-6 UNPACKFILENAME.STRING (Function) III: 24-5 UNSAFE.TO.MODIFY.FNS (Variable) I: 10-2 UNSAVEDEF (Function) II: 17-21 UNSET (Function) II: 13-22 UNTIL FORM (I.S. Operator) I: 9-12 UNTIL N (N a number) (I.S. Operator) I: 9-12 untilDate (I.S. Operator) I: 12-12 UP (Command) II: 16-10 UPDATEFILES (Function) II: 17-16 UPPERCASEARRAY (Variable) III: 25-17 USE (Command) II: 13-7 USERDATATYPES (Function) I: 8-16 USEREXEC (Function) II: 13-26 USERGREETFILES (Variable) I: 12-2 USERLISPXPRINT (Function) II: 13-19 USERMACROS (File Package Type) II: 17-18 USERWORDS (Variable) II: 20-13 usingTimer (I.S. Operator) I: 12-12 V VALUEOF (Function) II: 13-15 VAR - NOT A BINDABLE VARIABLE (Error Message) II: 18-19 VAR SHOULD BE A SPECVAR - USED FREELY BY FN (Error Message) II: 18-17 VAR VAL -- BAD PROG BINDING (Error Message) II: 18-19 Variables global II: 18-3 local II: 18-4 special II: 18-4 VARIABLES (Function) I: 11-5 VARS (File Package Type) II: 17-18 VIRGINFN (Function) II: 15-7 VMEM.PURE.STATE (Function) I: 12-6 VMEMSIZE (Function) I: 12-7 VOLUMES (Function) III: 24-17 VOLUMESIZE (Function) III: 24-17 W WAIT.FOR.TTY (Function) II: 23-10 WAITFORINPUT (Function) III: 25-5 WAKE.PROCESS (Function) II: 23-5 WBREAK (Function) II: 14-11 WHEN FORM (I.S. Operator) I: 9-12 WHENCHANGED (File Package Type Property) II: 17-24 WHENCLOSE (Function) III: 24-15 WHENFILED (File Package Type Property) II: 17-24 WHENUNFILED (File Package Type Property) II: 17-24 WHEREIS (Function) II: 17-11 WHILE FORM (I.S. Operator) I: 9-12 WITH-RESOURCES (Macro) I: 12-16 WITH.FAST.MONITOR (Macro) II: 23-7 WITH.MONITOR (Macro) II: 23-7 WRITEFILE (Function) III: 25-25 X XTR . @ (Command) II: 16-27 Z ZEROP (Function) I: 7-3 \ \ (Command) II: 16-21 \ADD.PACKET.FILTER (Function) III: 31-31 \ALLOCATE.ETHERPACKET (Function) III: 31-30 \BeginDST (Variable) I: 12-11 \CHECKSUM (Function) III: 31-31 \DEL.PACKET.FILTER (Function) III: 31-31 \EndDST (Variable) I: 12-11 \LASTVMEMFILEPAGE (Variable) I: 12-7 \LOCALNDBS (Variable) III: 31-30 \P (Command) II: 16-21 \PACKET.PRINTERS (Variable) III: 31-31 \RELEASE.ETHERPACKET (Function) III: 31-30 \TimeZoneComp (Variable) I: 12-11 (Command) II: 16-21 (Command) II: 16-21 (CLISP Operator) II: 21-5 (Command) II: 16-12 (Break Command) II: 14-4 ! !0 (Command) II: 16-12 !E (Command) II: 16-41 !EVAL (Break Command) II: 14-4 !F (Command) II: 16-41 !GO (Break Command) II: 14-4 !NX (Command) II: 16-13 !OK (Break Command) II: 14-5 !UNDO (Command) II: 16-49 # # (Command) III: 25-23 ## (Function) II: 16-44 #SPELLINGS1 (Variable) II: 20-13 #SPELLINGS2 (Variable) II: 20-13 #SPELLINGS3 (Variable) II: 20-13 #UNDOSAVES (Variable) II: 13-30 #USERWORDS (Variable) II: 20-13 & &Undo (Command) II: 16-6 ( ( in (Command) II: 16-6 () (Command) II: 16-6 () out (Command) II: 16-6 (* --) COMMENT USED FOR VALUE (Error Message) II: 18-17 (FORM) - NON-ATOMIC CAR OF FORM (Error Message) II: 18-17 (PROCESS.RESULT (Function) II: 23-3 (SETQ VAR EXPR --) BAD SETQ (Error Message) II: 18-17 ) ) in (Command) II: 16-6 * * (CLISP Operator) II: 21-5 + + (CLISP Operator) II: 21-5 , , (Command) III: 25-19 ,, (Command) III: 25-19 ,,, (Command) III: 25-19 - - (CLISP Operator) II: 21-5 -> (Break Command) II: 14-8 . . (CLISP Operator) II: 21-6 .BASE (Command) III: 25-20 .CENTER (Command) III: 25-22 .CENTER2 (Command) III: 25-22 .F (Command) III: 25-22 .FONT (Command) III: 25-20 .FR (Command) III: 25-22 .FR2 (Command) III: 25-22 .I (Command) III: 25-22 .N (Command) III: 25-23 .P2 (Command) III: 25-21 .PAGE (Command) III: 25-20 .PARA (Command) III: 25-21 .PARA2 (Command) III: 25-21 .PPF (Command) III: 25-21 .PPFTL (Command) III: 25-21 .PPV (Command) III: 25-21 .PPVTL (Command) III: 25-21 .SKIP (Command) III: 25-20 .SP (Command) III: 25-19 .SUB (Command) III: 25-20 .SUP (Command) III: 25-20 .TAB (Command) III: 25-19 .TAB0 (Command) III: 25-19 / / (CLISP Operator) II: 21-5 0 0 (Command) II: 16-12 : : (CLISP Operator) II: 21-6 :: (CLISP Operator) II: 21-7 ; ; (Command) II: 13-14 = = (Break Command) II: 14-8 = (CLISP Operator) II: 21-6 ? ? (Command) II: 16-36 ?= (Break Command) II: 14-6 ?= (Command) II: 16-36 @ @ (Break Command) II: 14-5 (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "Index-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "Index-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE TITLEHEAD) (54 762 558 36) NIL) (HEADING NIL (HEADINGTYPE TITLEHEADRULE) (54 753 558 36) NIL) (TEXT NIL NIL (54 54 241 666) NIL) (TEXT NIL NIL (314 54 241 666) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "Index-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "Index-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 241 684) NIL) (TEXT NIL NIL (316 54 241 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "Index-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "Index-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 241 684) NIL) (TEXT NIL NIL (315 54 241 684) NIL))))) +J PAGEHEADING TITLEHEADRULEF PAGEHEADING TITLEHEADF PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR,2K,K-K T,KOPTIMA + HELVETICACLASSIC + HELVETICA +MODERN +MODERN +MODERN + HRULE.GETFNMODERN + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + HRULE.GETFNOPTIMA +  + +  +  + +  + +    +  +   + +   + +  + +   + +   + +   + +  +   +    +  + +   + + +  +  +   +   +  + +   +  +  +   +  +   + +   +  +   + +   + +   + +   +  +   +  +    +  +   +   +  + +   + +  + +  +  +  +  +  +  +    +  + +     + +  +   +  + + +  +   +   +  + +  + +   +  + +   + +  + +  + +  + +  + +  + +  + + + +  + + + + + +  + +  + + +   +    +   + +- + +  + + + +   +  + +  +  + +  + +  +  + + +   + +   +   +   +  + +   + + +  + +    +  +   + +   + +  + +  + +   + +   +    +    +   + +  +  +  +  +  + + + + + + + + +  + +  + +  + +  +   +    +  +  + +   +  + +  + +  + +  + +  + +  + +  + +  + +  + +   + +  + +  + + +  + +    +   +   + +   + +  +    +  +  +  + +  + + + +  + +   + +    +   + +   + +   + +  +   +      +   + +  + +   + + +  + +  + +  + +  + +  + +  + +  + +   + +  + +  + +   + +   + +   + +   + +  + +  + +   + +   + +   +   +  + +   +  + + +   + +   + +  +   + + +  + +  + + +   +     +   +   +   +  +     +  +   +$  +  + +  +    + +   +    +   + +   + +   + +   +  +   +   +    +    +   + + +   +   +    +    +  +  +  +   +   + +  +   + +  +   +   +   +   +   +   +   +   +   + + +   +   +    + +  + +   + + +   +  +   +   +  +   + + +  + + +  +   +       +    +   +   +  + +   +  +   +   + +  + +   +    + + +  + +  +  + +    +  + +   +  + +   +  +   +   +    +    +  + +  + +    +    +   +   +    +   +  + +  +   +   + + +   + +  +    +   +  +  + +    +   +  +   +  + + + +  +    + +   +  + +  + +   + +   + +   + +  + +    +   + +  + +  + +   + +  +  + +  + +  + +  + + +  +   +  + +   + +   + +    +  + +   + +  + +  +  + +   + +   + + +  + +  + +  + +  + + +   +  + +   + +   +  + +  + +  + +  +  + +   +   +    +  + + +  +   +  + +  + +  + +  +  +  +  + +  +  +  + + + +  + +   + +   + +   + +  + + +  + + +  + +   + +  + +  + +  + +   +  + +  + +  + + + +  + + + +  +  + + +   + +  + +  +  +  + +  + +  +  +  + +   +   +   +   +   +  + +  +  +  + +   +   + +   + +  + +   + +  + +  +   +    +    +   +   +   +  +   + +   +   +   + + +   +  + +  +   + +   + +  + +   + +   + +  + +  + +   + + + +    +   + +    +   +  +  + + +  + +    + + + + +   +   +   + + +   +  +   +  +   +  + +  + +  + +  +    +  +  + +   +   +   +   +   +   +   +    +    +   +   +   +   +   +    +  +  +  +  + + +3 + +  + +. + + + + + + + +. + +   + +  + + + +  +   +  +  + + +   + +   +  + +  +   +   +   +   + +  +   +   + +  +   +   + +  +   +  + +  + +  + +  +  + +    +  + +  +   +  +  + +   +    + +  +  +  +  + +   +   +  + + +  +    +  +    +    +   +  +   +   +   +    +   +    +   +  +  +    +   + +   +  + +   +  + +  +  +  + +  + +   +  +   +  +   + +   + +    +  +    +   +   + + +  + + + +   + + +   + +  +  + +   + +  + +   + +  + +   + +   +    + +   +  +$  + + + +  +  + + +  + +  +   +  +  +  +  +  +   + +  + +  + + +   +   + +    +  + + +  +  + + + +   +  + +  !  +  +   +   +   +  + + +   + +  +  +  + +  +   + +   + +  +  + +  +  +  + +  + + +   +  +  +  +  +  + +   + +   + +  +  +   +  + +   +  + +  + +   + +   + +  + +  + +   + +   + +  + +  + +   + +   + +   + +  + + +  + +   + +   + +  +  +   + +  +   + +  +    +  +  + +   +   +    + +  + +   +    +   + +  + +    + +   +   + +   +& + +  +  + +  + +  + +  +  +  +  +  + +  + +   + +   + +  + +  +  +  + +   + +  + +   +  + +  +  +  +   +  + + + +  + +   +    +   +   +   +   + +  +   +  + +  + + + + + + + +  + +  +   +   +   +  + +  + +  + +  +  +  +   +  + +  +   +   +   +  +  +   + +  +  +   +  +  +   +  + +   +  + +  +  +  +   +   +  + +  +  +  + + +  +  +  + +  + +   +   +   +  +   +  + +   + ++ + +  +  + +  + +   +  +   +   +  + +   +   +   + +  + +  + +  + +  +  +   +  +   +  + +    +   +  +  + +   +    +  +  +  +   + +  + +  + + + +   +    +  + +   +   + +   +  +  + + +  + +  + +  + +    + +   +  +  + + + + +   + +  + +   +  +  +  +  + +  + +   +    +   +   +   +  + +  +  + +  + +   + +  + +  +  + + +   +    + +  + +   +  + +  + +   +   +  + +  +   +  + +  + +  + +  + +  +  +   + +   +  + +   + +  + +  + +  + +  + +  +   + + +  + +   +    +   +    + +   +   +   +   +   +   +   +   +  + + +   +    +   +    +   + +  + +   +  + + +   + + +   +   +   +   +   +  +   +   + +   +   +    +   +   + + + + +  +  + + +  + +   +   +    +   +     + +  + + +   + +  + +   +  + +  +    +   +  + +  + +  + +  + +  + +   + +  + +   + + +   + +  + +    +  + +    + +   +    +  +    +    +    +    +   +   + + +  +  +  + +   +  +   +    +    +    +  +   +   + +  + +  +   +   +  + +    +  + + +  + +   + + + +    +  +    +  + +    +   + +   + + +  + +   +   + + +  + +   + +  +  + +   +   +   + +  +   +  + +   + +  + +   +  +  +   +  +  + +  + +  +   + +  +   +   + +  +   +  +   + + +  +  + +  +  + +   + +   + +  +   +   +   +   + + +    +  + +    +    + +   +  +  +   +    +   +   +   + +    +    +  + +  + +   +  +  +   +    +   +  +   +  + +    +   + +   +    +   +  + +  + +  + +  + +  + +   + +   + +   + +  +   +   + +  + + +   +  +  +  +   +   +  + +   + +    + +  +  + + + + +  + +   + +   + +   + +  + +   +   +    + +  + +   +    +   +   +  +  +  + + +  +  +   +   +   +   + +  +  +   +  +   +  +    +   +   + +  +   +  +   +   +  +   +  +   +  +   + + +   +   +  + + +  +  + +   +   +    +  +   +   +    + +   +   +  + +   +   +  +  +  + +  +  + +   + +  + + + + + +% + +  + + + + + + + + +   +  + +   +    +    +   +  +   +  + +  +   +   + +   + +   +    +    +   +   + + +   + +  +   +  +   +  + +  +   +   +  + +   +   + +   + +    + +   +    +  +   + +   + + +  + +  +  + +  +  + +  + +  +   + +  + +  + +   +  + +   + +   +   +  + +  + +  +  + +  + +   + +    + +   + + + +- + + + +      +   + + + +   +  + +  +    +     + +  + +  + +   +  + +  +  + +    +  + +  + +   + +  + +  + +  + +  +     + +   +  + +   + +  + +   +   +    +    +   +    +  +    +  + +   +   +    + +  + +  +  + +  +  + +   + +  + +  + +  +  + +  +  + +  +  +  + +   +  + +   + +   + +   + +   + +   +  +    + +   +   +   + + +! + +   + +  + +    + +   + +   + +   +   +    + +  +   + +  +   +    + +   +   +   +   +   +   +   +   +   +   +   +   +   +   +   +   +   +   +   +   +    + +   + +  +  + +  +   + +  +  + +  +   + +  + +  +  +  + +  +bz \ No newline at end of file diff --git a/docs/porter-irm/IRM-VOL1-TITLEPAGE.TEDIT b/docs/porter-irm/IRM-VOL1-TITLEPAGE.TEDIT new file mode 100644 index 00000000..e7e01bdf Binary files /dev/null and b/docs/porter-irm/IRM-VOL1-TITLEPAGE.TEDIT differ diff --git a/docs/porter-irm/IRM-VOL2-TITLEPAGE.TEDIT b/docs/porter-irm/IRM-VOL2-TITLEPAGE.TEDIT new file mode 100644 index 00000000..27218945 Binary files /dev/null and b/docs/porter-irm/IRM-VOL2-TITLEPAGE.TEDIT differ diff --git a/docs/porter-irm/IRM-VOL3-TITLEPAGE.TEDIT b/docs/porter-irm/IRM-VOL3-TITLEPAGE.TEDIT new file mode 100644 index 00000000..848b1a37 Binary files /dev/null and b/docs/porter-irm/IRM-VOL3-TITLEPAGE.TEDIT differ diff --git a/docs/porter-irm/MASTERSCOPEBITMAPS.TEDIT b/docs/porter-irm/MASTERSCOPEBITMAPS.TEDIT new file mode 100644 index 00000000..9193add8 Binary files /dev/null and b/docs/porter-irm/MASTERSCOPEBITMAPS.TEDIT differ diff --git a/docs/porter-irm/MSCOPEDEMO b/docs/porter-irm/MSCOPEDEMO new file mode 100644 index 00000000..c76dae77 --- /dev/null +++ b/docs/porter-irm/MSCOPEDEMO @@ -0,0 +1,66 @@ +(DEFINE-FILE-INFO READTABLE "XCL" PACKAGE "INTERLISP") +(FILECREATED "20-Feb-92 22:24:01" |{DSK}local>users>welch>primer>MSCOPEDEMO.;1| 2192 + + |changes| |to:| (VARS MSCOPEDEMOCOMS) + (FNS PTOI |PrintError| |ParseList| |ConcatList| |PrintWarning| |ReadBeginEnd| + |GetMyProp| |GetBeginTagString| |ProcessEND|)) + + +; Copyright (c) 1992 by Venue. All rights reserved. + +(PRETTYCOMPRINT MSCOPEDEMOCOMS) + +(RPAQQ MSCOPEDEMOCOMS ((FNS |ConcatList| |GetBeginTagString| |GetMyProp| PTOI |ParseList| + |PrintError| |PrintWarning| |ProcessEND| |ReadBeginEnd|))) +(DEFINEQ + +(|ConcatList| + (LAMBDA NIL (* \; "Edited 20-Feb-92 22:18 by welch") + (|ConcatList|))) + +(|GetBeginTagString| + (LAMBDA NIL (* \; "Edited 20-Feb-92 22:07 by welch") + (|ConcatListWithSpaces|) + (|GetMyProp|))) + +(|GetMyProp| + (LAMBDA (|PropName|) (* \; "Edited 20-Feb-92 22:14 by welch") + (|GetAncestorProp| |PropName| (CAR |TOIstack|)))) + +(PTOI + (LAMBDA NIL (* \; "Edited 20-Feb-92 22:19 by welch") + (|PrintWarning|))) + +(|ParseList| + (LAMBDA NIL (* \; "Edited 20-Feb-92 22:19 by welch") + (|GetCTType|))) + +(|PrintError| + (LAMBDA NIL (* \; "Edited 20-Feb-92 22:20 by welch") + (PTOI))) + +(|PrintWarning| + (LAMBDA NIL)) + +(|ProcessEND| + (LAMBDA (ARGS) (* \; "Edited 20-Feb-92 22:20 by welch") + (|GetBeginTagString|) + (|GetMyProp|) + (|ConcatListWithSpaces|) + (|ReadBeginEnd|) + (|PrintWarning|))) + +(|ReadBeginEnd| + (LAMBDA NIL (* \; "Edited 20-Feb-92 22:16 by welch") + (|ConcatList|) + (|ParseList|) + (|GetCTType|) + (|PrintError|) + (|apply|))) +) +(PUTPROPS MSCOPEDEMO COPYRIGHT ("Venue" 1992)) +(DECLARE\: DONTCOPY + (FILEMAP (NIL (653 2122 (|ConcatList| 663 . 810) (|GetBeginTagString| 812 . 994) (|GetMyProp| 996 . +1171) (PTOI 1173 . 1314) (|ParseList| 1316 . 1457) (|PrintError| 1459 . 1598) (|PrintWarning| 1600 . +1635) (|ProcessEND| 1637 . 1892) (|ReadBeginEnd| 1894 . 2120))))) +STOP diff --git a/docs/porter-irm/resource-manag-bitmaps.tedit b/docs/porter-irm/resource-manag-bitmaps.tedit new file mode 100644 index 00000000..456fb16b Binary files /dev/null and b/docs/porter-irm/resource-manag-bitmaps.tedit differ diff --git a/docs/porter-irm/reviewmemo.tedit b/docs/porter-irm/reviewmemo.tedit new file mode 100644 index 00000000..3118db51 Binary files /dev/null and b/docs/porter-irm/reviewmemo.tedit differ diff --git a/docs/porter-irm/rsugindex.tedit b/docs/porter-irm/rsugindex.tedit new file mode 100644 index 00000000..6d24d0f9 --- /dev/null +++ b/docs/porter-irm/rsugindex.tedit @@ -0,0 +1,692 @@ +1 Medley for the RS/6000 User's Guide, Release 2.0 1 Medley for the RS/6000 User's Guide, Release 2.0 INDEX 1 INDEX 1 INDEX 6 A Access key 11 AIX console messages 26 directory notations 28 file attributes 34 file system 27 username 23 AIX process identifying username of 24 AIX Windows 16 Asterisk 32 AUTHOR (File Attribute) 34 B Back trace 38 BACKGROUNDPAGEFREQ (Variable) 23 BEEPOFF (Function) 25 BEEPON (Function) 25 \BeginDST (Variable) 20 Binary files 4,34 Brackets left angle 29 right angle 27 square 27 C C-Shell 28 Carriage return 4,34 Case sensitivity 29,31,37,38 CHANGEBACKGROUNDBORDER (Function) 25 Characters, special 28 CHDIR (Function) 31,33,41 checksum B-1; 2 chmod (UNIX Command) 34 CLOCK (Function) 26 Clocks 25 CNDIR (Function) 33 Compatibility compiled-file 4 sysout 4 Configuration, changing 11 CONN (Command) 33 Console messages 26 Conventions common {DSK} and {UNIX} 28 {DSK} naming 29 fonts 3 Medley devices 3 notation 3 {UNIX} naming 31 URAID 37 Copy protection 11 CREATEDSKDIRECTORY (Function) D-1 CREATIONDATE (File Attribute) 34 .cshrc file A-1 D Daylight Savings Time setting values for 20 DEFAULTFILETYPE (Variable) 34,35 DEFAULTFILETYPELIST (Variable) 34,35 DEFAULTPRINTERTYPE (Variable) 19 DEFAULTPRINTINGHOST (Variable) 19 DIRECTORIES (Variable) 19 Directory changing 28,33 creation 32,33 deletion 33 enumeration 32 home 28,33 name delimiting 27 parent 28 DISKFREEPAGES (Function) D-1 DISKPARTITION (Function) D-1 Display functions 25 Display fonts how to find 19 DISPLAYFONTDIRECTORIES (Variable) 19 DLRS232C D-2 DLTTY D-2 {DSK}INIT.DFASL 16 {DSK}INIT.LCOM 16 {DSK}INIT.LISP 16 DSKDISPLAY (Function) D-1 E Emulator 2 End-of-line convention 4,34 \EndDST (Variable) 20 Environment inquiry functions 24 Environment variables A-1 LDEDESTSYSOUT 21 LDEINIT 16 LDESOURCESYSOUT 15 LDESHELL 17,24 EOL (File Attribute) 34 Errors fatal 40 file system 35,37 Lisp 40 running Medley 40 Teleraid errors 37 virtual memory 41 Xerox worstation-specific 41 Exiting Medley 21 F fg (UNIX Command) 23 File attributes 34 File name conventions 27 recognition 28 File protection bits, changing 34 File resources exceeding 40 File streams 28 File system errors 35 File types 35 File variables 35 FILENAMEFROMID (Function) D-1 Files binary 4,34 finding 19,35 open 40 text 34 versionless 30 FINDFILE (Function) 31 FLOPPY.ARCHIVE (Function) D-1 FLOPPY.CAN.READP (Function) D-1 FLOPPY.CAN.WRITEP (Function) D-1 FLOPPY.FORMAT (Function) D-1 FLOPPY.FREE.PAGES (Function) D-1 FLOPPY.FROM.FILE (Function) D-1 FLOPPY.MODE (Function) D-1 FLOPPY.NAME (Function) D-1 FLOPPY.SCAVENGE (Function) D-1 FLOPPY.TO.FILE (Function) D-1 FLOPPY.UNARCHIVE (Function) D-1 FLOPPY.WAIT.FOR.FLOPPY (Function) D-1 Font directories C-2 Fonts 2, 3 InterPress 19 Frames, viewing 39 Functions system environment 22,23,24,25 timer and clock 25 FUNCTIONS (Functions) FX-80DRIVER D-2 G GETFILEINFO (Function) 34 H Hardware requirements 1,22 Host ID 11 identifying 22 Host name, identifying 22 Hosts supported by Medley {CORE} 27 {DSK} 27 {LPT} 27 {NULL} 27 {UNIX} 27 I Input/output devices, requirements 1 install.X 1 Installation preparation C-1; 7,8,9,11 script 9 software 8 tape layout C-1 extracting files from C-3 Installation Options Menu 9 Interpress files 35 fonts, finding 19 package 37 INTERPRESSFONTDIRECTORIES (Variable) 19 K KERMIT D-2 KEYACTION (Function) 18 Keyboard 18 functions 25 templates 18 tone generator 25 KEYBOARDEDITOR D-2 KEYDOWNP (Function) 18 kill (UNIX Command) 40 L lde 20 killing 40 ldechecksum (Command) B-1 LDEDESTSYSOUT 21 LDEDESTSYSOUT (Variable) 21 LDEINIT 16 LDEKBDTYPE (Variable) 17 LDESHELL (Variable) 17 LDESOURCESYSOUT 15 Left angle bracket 29,31 Library files, finding 19 Line feed 4,34 Lisp symbols set in site initialization file 19 LISP-RELEASE-VERSION (Variable) 22 ~/lisp.virtualmem 15,21 LISPDIRECTORYP (Function) D-1 LispUsers' Modules 5 finding 19 LISPUSERSDIRECTORIES (Variable) 19 litatom 37 Local disk functions D-1 LOGIN (Function) 23,24 .login file 13 Login functions 23 LOGOUT (Function) 20,21 LONG-SITE-NAME (Function) 20 *LONG-SITE-NAME* (Variable) 20 M MACHINE-INSTANCE (Function) 22 MACHINE-TYPE (Function) 22 MACHINE-VERSION (Function) 22 MACHINETYPE (Function) 22 Medley, exiting 21 Memory, requirements 1 MP errors 37 N NFS service 35 NIS A-3 Notation conventions 3 O OPENFILE (Function) 34 OPENSTREAM (Function) 34 Operating system requirements 1 Options, adding 11 P Packages 3 Passwords, maintaining for access 24 Pathname, Lisp 27,31 Period single 28,33 double 28,33 Personal init file setup 20 PLAYTUNE (Function) 25 PostScript 1 Postscriptstream Module 5 Press files 35 Printers 1 default 19 PROTECTION (File Attribute) 34 pstat (UNIX Command) 8 PURGEDSKDIRECTORY (Function) D-1 R READSYS 40 REALMEMORYSIZE (Function) 22 Relative pathnames 29 Release contents 4, 5 Right angle bracket 27 RINGBELLS (Function) 25 rmdir (UNIX Command) 33 RPC 5 RS232 D-2 RS232CHAT D-2 S SAVEVM (Function) 20,21 SCAVENGEDSKDIRECTORY (Function) D-1 setenv (UNIX Command) 21 SETMAINTPANEL (Function) 25 SETPASSWORD (Function) 23 SETTIME (Function) 26 setuid (UNIX Command) 24 SETUSERNAME (Function) 23 Shared use 2 installing for 8 SHORT-SITE-NAME (Function) 20 *SHORT-SITE-NAME* (Variable) 20 Site initialization file 16 how to find 19 site-init.lisp 16 SIZE (File Attribute) 34 Sketch files 35 Slash 27 Software requirements 1 Special characters 28 Square brackets 27 Stack 38 SUSPEND-LISP (Function) 23 Swap space, allocating additional 8 Symbols set in site initialization file 19 Sysout 2,37 locations of 15 SYSOUT (Function) 22,41 T TCP D-1 TCPCHAT D-1 Teleraid errors 37 Text files 4, 34 tilde 28 tilde-slash 31,33 TIME (Function) 26 Timers 25 TYPE (File Attribute) 34,35 U UNIX process, suspending 23 UNIX-FULLNAME (Function) 24 UNIX-GETENV (Function) 24 UNIX-GETPARM (Function) 24 UNIX-USERNAME (Function) 24 URAID 37 commands 38 quit 38 User IDs, maintaining for access 24 USERGREETFILES (Variable) 19 /usr/share/lde 2, B-1 USERNAME (Function) 23 V Version identifying machine 22 numbering 29 numbers 28,31 VIDEOCOLOR (Function) 25 VIDEORATE (Function) 25 Virtual memory saving 21-23 saving with URAID 40 VIRTUALKEYBOARD D-2 VMEM.PURE.STATE (Variable) 23 VMEMSIZE (Function) 21,23 VOLUMES (Function) D-1 VOLUMESIZE (Function) D-1 VRAID 40 W WRITEDATE (File Attribute) 34 X X Windows 16 [ [] 27 \ \BeginDST (Variable) 20 \EndDST (Variable) 20 \RCLKMILLISECOND (Variable) 26 { {CORE} 27 {DSK} 24,28,32,34 special characters 28 naming conventions 29 {DSK}INIT.DFASL 16 {DSK}INIT.LCOM 16 {DSK}INIT.LISP 16 {LPT} 27 {NULL} 27 {UNIX} D-2; 24,32-34 naming conventions 31 {UNIX} (Function) 28 ~ ~ 28 ~/ 31,33 ~/lisp.virtualmem 15,20,21 * * 32 *LONG-SITE-NAME* (Variable) 20 *SHORT-SITE-NAME* (Variable) 20 . . 28,33 .. 28,33 .cshrc file A-1 .login file 13 / / 27 /usr/share/lde 2, B-1 4 4045XLPSTREAM D-2 < < 29,31 > > 27 (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "INDEX-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "INDEX-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE TITLEHEAD) (54 762 558 36) NIL) (HEADING NIL (HEADINGTYPE TITLEHEADRULE) (54 753 558 36) NIL) (TEXT NIL NIL (54 54 241 666) NIL) (TEXT NIL NIL (320 54 241 666) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "INDEX-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "INDEX-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 241 684) NIL) (TEXT NIL NIL (320 54 241 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "INDEX-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "INDEX-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 241 684) NIL) (TEXT NIL NIL (319 54 241 684) NIL))))) .KTT-KT2$$TJ PAGEHEADING TITLEHEADRULEF PAGEHEADING TITLEHEADF PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR,K-K T,K MODERN +MODERN +TITAN +TITAN +CLASSIC +CLASSIC + HELVETICA HELVETICA +OPTIMA +CLASSIC +MODERN +  HRULE.GETFNMODERN +  0 + HRULE.GETFNCLASSIC + +1 + HRULE.GETFNCLASSIC + + + HRULE.GETFNCLASSIC + + HRULE.GETFNOPTIMA +    + + +  + + + + + + + + + + + + + + + + + + + + + + + + + + + +  +  + + +   +   +   +   +  + + + + + + + + + + + + + +  + + +  + +  + +  +  + +   + + + +  +   +  + +   +  + + + + + + + +  + +   +  + + + + + + + + + + + + + + + + + + + + + + + + + +  + +   +  +     +  +  + + + + +   +   +   +   +   + + + + + + + + + + + + + + + + + + + + + + + + + + +   +   +  + +  + + + + +   + + + +  + +  + +  + +  + + +  +  + + + +  + +   +  + +  + + +  + + + + + + + + + + +  +  + + + + + + + + + + + + + + + + + + + + + + +  + +   +  + + + + + + + + + + + + +  + + + + +  + +  + + + +  + +   +  + + + + + + + + + + + + + + + + +   +   +   +   +   +   +   +   +   +   +   +   +   +  + +  + + + + +  + + + + + + + + + +  + + +  +   +  + + + + + + + + + + + +  + +  + + + + + + + + + + +  + $ + + + + +  + + + + + + + + + + + + + + + + +  + + + + + + + + + + +   +  +  + +   + + + + + + + + + + + + +  + +   +  +   + + + + +   +  + +   +  + + +  +   +  + +  + +  + + + +  + + +! + +   +  + +   +  + + + + +   +  + +  + +   +  + +  + +   +   +   +    +   +   +   +  + +  + + + +  + + + + +  +  +    + +  +  + +  + + + + + # + +  + +  + + + + + + + +  + +   + + +  + + + + + + + + + + + +  +  +   + +   +  + +  + +  + +   +  +  + +  + + + +    +   +  +   +   +   +  +   +  + + + + +   +   +  + + + + +  + +  +  + +  + +  + +  + +  + +  + +   + # + + + + +! + +  + + + + +   +  +  + + + +  + + + +  + + + +   +  + +  +  +  + +   +   +   +   +  + + + + + + + + + " + +   +      +  + + + + + + + + + + + + +  +   +  + + + + + + + +  + +   +   +   + +  +  + +   +  +  + +  + +   +   +   +  + +  + + + + + + + +  + +  + +  + +  + +  + +  + + + + +   +  + +  + +  + +   + +   +   +                                 _z \ No newline at end of file diff --git a/docs/porter-irm/sample.sketch b/docs/porter-irm/sample.sketch new file mode 100644 index 00000000..4946f5a5 --- /dev/null +++ b/docs/porter-irm/sample.sketch @@ -0,0 +1,3 @@ +((SKETCH a% figure% from% a% document VERSION 3 PRIRANGE (87 . 0) SKETCHCONTEXT ((ROUND 1 BLACK) (GACHA 12 (BOLD REGULAR REGULAR)) (CENTER BASELINE) (CURVE 18.0 8) NIL NIL (CENTER CENTER) (NIL NIL NIL) T NIL NIL 1 NIL)) ((0.136 88.0 (PRI 1)) (BOX (144.0 296.0 176.0 136.0) (ROUND 2 BLACK) NIL 1 (NIL NIL NIL))) ((0.112 80.0 (PRI 2)) (BOX (152.0 304.0 160.0 112.0) (ROUND 2 BLACK) NIL 1 (NIL NIL NIL))) ((0.05 10.0 (PRI 3)) (TEXT (232.0 . 408.0) ("CVs") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((225.0 403.0 14.0 10.0)) NIL)) ((0.05 10.0 (PRI 4)) (TEXT (232.0 . 360.0) ("IVs") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((226.5 355.0 11.0 10.0)) NIL)) ((0.05 10.0 (PRI 5)) (TEXT (192.0 . 400.0) ("LeftButtomItems") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((161.0 395.0 62 10)) BLACK)) ((0.0 80.0 (PRI 6)) (WIRE ((152 . 368) (312 . 368)) (ROUND 2 BLACK) NIL NIL 1 (150 366 164 4) NIL)) ((0.05 10.0 (PRI 7)) (TEXT (192.0 . 384.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((191.0 379.0 2 10)) BLACK)) ((0.05 10.0 (PRI 8)) (TEXT (192.0 . 384.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((191.0 379.0 2 10)) BLACK)) ((0.05 10.0 (PRI 9)) (TEXT (192.0 . 392.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((191.0 387.0 2 10)) BLACK)) ((0.05 10.0 (PRI 10)) (TEXT (272.0 . 400.0) ("((Update &)&)") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((244.5 395.0 55 10)) BLACK)) ((0.05 10.0 (PRI 11)) (TEXT (272.0 . 392.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((271.0 387.0 2 10)) BLACK)) ((0.05 10.0 (PRI 12)) (TEXT (272.0 . 384.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((271.0 379.0 2 10)) BLACK)) ((0.05 10.0 (PRI 13)) (TEXT (232.0 . 424.0) ("Window") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((216.0 419.0 32 10)) BLACK)) ((0.05 10.0 (PRI 14)) (TEXT (192.0 . 352.0) ("width") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((181.0 347.0 22.0 10.0)) NIL)) ((0.05 10.0 (PRI 15)) (TEXT (192.0 . 344.0) ("height") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((180.0 339.0 24.0 10.0)) NIL)) ((0.05 10.0 (PRI 16)) (TEXT (192.0 . 336.0) ("menus") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((179.5 331.0 25.0 10.0)) NIL)) ((0.05 10.0 (PRI 17)) (TEXT (192.0 . 320.0) (".") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((191.0 315.0 2.0 10.0)) NIL)) ((0.05 10.0 (PRI 18)) (TEXT (192.0 . 328.0) (".") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((191.0 323.0 2.0 10.0)) NIL)) ((0.05 10.0 (PRI 19)) (TEXT (272.0 . 352.0) ("12") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((268.0 347.0 8.0 10.0)) NIL)) ((0.05 10.0 (PRI 20)) (TEXT (272.0 . 344.0) ("12") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((268.0 339.0 8.0 10.0)) NIL)) ((0.05 10.0 (PRI 21)) (TEXT (272.0 . 336.0) ("T") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((270.0 331.0 4.0 10.0)) NIL)) ((0.05 10.0 (PRI 22)) (TEXT (272.0 . 328.0) (".") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((271.0 323.0 2.0 10.0)) NIL)) ((0.05 10.0 (PRI 23)) (TEXT (272.0 . 320.0) (".") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((271.0 315.0 2.0 10.0)) NIL)) ((0.136 88.0 (PRI 24)) (BOX (144.0 144.0 176.0 136.0) (ROUND 2 BLACK) NIL 1 (NIL NIL NIL))) ((0.112 80.0 (PRI 25)) (BOX (152.0 152.0 160.0 112.0) (ROUND 2 BLACK) NIL 1 (NIL NIL NIL))) ((0.05 10.0 (PRI 26)) (TEXT (232.0 . 256.0) ("CVs") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((225.0 251.0 14.0 10.0)) NIL)) ((0.05 10.0 (PRI 27)) (TEXT (232.0 . 208.0) ("IVs") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((226.5 203.0 11.0 10.0)) NIL)) ((0.05 10.0 (PRI 28)) (TEXT (192.0 . 248.0) ("LeftButtomItems") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((161.0 243.0 62 10)) BLACK)) ((0.0 80.0 (PRI 29)) (WIRE ((152.0 . 216.0) (312.0 . 216.0)) (ROUND 2 BLACK) NIL NIL 1 (150.0 214.0 164.0 4.0) NIL)) ((0.05 10.0 (PRI 30)) (TEXT (192.0 . 232.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((191.0 227.0 2 10)) BLACK)) ((0.05 10.0 (PRI 31)) (TEXT (192.0 . 232.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((191.0 227.0 2 10)) BLACK)) ((0.05 10.0 (PRI 32)) (TEXT (192.0 . 240.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((191.0 235.0 2 10)) BLACK)) ((0.05 10.0 (PRI 33)) (TEXT (272.0 . 248.0) ("((Boxnode &)&)") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((242.5 243.0 59 10)) BLACK)) ((0.05 10.0 (PRI 34)) (TEXT (272.0 . 240.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((271.0 235.0 2 10)) BLACK)) ((0.05 10.0 (PRI 35)) (TEXT (272.0 . 232.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((271.0 227.0 2 10)) BLACK)) ((0.05 10.0 (PRI 36)) (TEXT (192.0 . 200.0) ("width") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((181.0 195.0 22.0 10.0)) NIL)) ((0.05 10.0 (PRI 37)) (TEXT (192.0 . 192.0) ("height") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((180.0 187.0 24.0 10.0)) NIL)) ((0.05 10.0 (PRI 38)) (TEXT (192.0 . 184.0) ("title") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((185.0 179.0 14.0 10.0)) NIL)) ((0.05 10.0 (PRI 39)) (TEXT (192.0 . 168.0) (".") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((191.0 163.0 2.0 10.0)) NIL)) ((0.05 10.0 (PRI 40)) (TEXT (192.0 . 176.0) (".") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((191.0 171.0 2.0 10.0)) NIL)) ((0.05 10.0 (PRI 41)) (TEXT (272.0 . 200.0) ("64") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((268.0 195.0 8.0 10.0)) NIL)) ((0.05 10.0 (PRI 42)) (TEXT (272.0 . 192.0) ("32") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((268.0 187.0 8.0 10.0)) NIL)) ((0.05 10.0 (PRI 43)) (TEXT (272.0 . 184.0) ("%"Lattice Browser%"") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((239.5 179.0 65.0 10.0)) NIL)) ((0.05 10.0 (PRI 44)) (TEXT (272.0 . 176.0) (".") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((271.0 171.0 2.0 10.0)) NIL)) ((0.05 10.0 (PRI 45)) (TEXT (272.0 . 168.0) (".") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((271.0 163.0 2.0 10.0)) NIL)) ((0.12 88.0 (PRI 46)) (BOX (48.0 8.0 176.0 120.0) (ROUND 2 BLACK) NIL 1 (NIL NIL NIL))) ((0.096 80.0 (PRI 47)) (BOX (56.0 16.0 160.0 96.0) (ROUND 2 BLACK) NIL 1 (NIL NIL NIL))) ((0.05 10.0 (PRI 48)) (TEXT (136.0 . 104.0) ("CVs") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((129.0 99.0 14.0 10.0)) NIL)) ((0.05 10.0 (PRI 49)) (TEXT (136.0 . 56.0) ("IVs") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((130.5 51.0 11.0 10.0)) NIL)) ((0.05 10.0 (PRI 50)) (TEXT (96.0 . 96.0) ("LeftButtomItems") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((65.0 91.0 62 10)) BLACK)) ((0.0 80.0 (PRI 51)) (WIRE ((56.0 . 64.0) (216.0 . 64.0)) (ROUND 2 BLACK) NIL NIL 1 (54.0 62.0 164.0 4.0) NIL)) ((0.05 10.0 (PRI 52)) (TEXT (96.0 . 80.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((95.0 75.0 2 10)) BLACK)) ((0.05 10.0 (PRI 53)) (TEXT (96.0 . 80.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((95.0 75.0 2 10)) BLACK)) ((0.05 10.0 (PRI 54)) (TEXT (96.0 . 88.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((95.0 83.0 2 10)) BLACK)) ((0.05 10.0 (PRI 55)) (TEXT (176.0 . 88.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((175.0 83.0 2 10)) BLACK)) ((0.05 10.0 (PRI 56)) (TEXT (176.0 . 80.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((175.0 75.0 2 10)) BLACK)) ((0.12 88.0 (PRI 57)) (BOX (240.0 8.0 176.0 120.0) (ROUND 2 BLACK) NIL 1 (NIL NIL NIL))) ((0.096 80.0 (PRI 58)) (BOX (248.0 16.0 160.0 96.0) (ROUND 2 BLACK) NIL 1 (NIL NIL NIL))) ((0.05 10.0 (PRI 59)) (TEXT (328.0 . 104.0) ("CVs") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((321.0 99.0 14.0 10.0)) NIL)) ((0.05 10.0 (PRI 60)) (TEXT (328.0 . 56.0) ("IVs") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((322.5 51.0 11.0 10.0)) NIL)) ((0.05 10.0 (PRI 61)) (TEXT (288.0 . 96.0) ("LeftButtomItems") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((257.0 91.0 62 10)) BLACK)) ((0.0 80.0 (PRI 62)) (WIRE ((248.0 . 64.0) (408.0 . 64.0)) (ROUND 2 BLACK) NIL NIL 1 (246.0 62.0 164.0 4.0) NIL)) ((0.05 10.0 (PRI 63)) (TEXT (288.0 . 80.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((287.0 75.0 2 10)) BLACK)) ((0.05 10.0 (PRI 64)) (TEXT (288.0 . 80.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((287.0 75.0 2 10)) BLACK)) ((0.05 10.0 (PRI 65)) (TEXT (288.0 . 88.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((287.0 83.0 2 10)) BLACK)) ((0.05 10.0 (PRI 66)) (TEXT (368.0 . 96.0) ("((Recompute &)&)") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((334.0 91.0 68 10)) BLACK)) ((0.05 10.0 (PRI 67)) (TEXT (368.0 . 88.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((367.0 83.0 2 10)) BLACK)) ((0.05 10.0 (PRI 68)) (TEXT (368.0 . 80.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((367.0 75.0 2 10)) BLACK)) ((0.05 10.0 (PRI 69)) (TEXT (328.0 . 120.0) ("InstanceBrowser") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((297.0 115.0 62 10)) BLACK)) ((0.05 10.0 (PRI 70)) (TEXT (232.0 . 272.0) ("LatticeBrowser") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((204.5 267.0 55 10)) BLACK)) ((0.05 10.0 (PRI 71)) (TEXT (136.0 . 120.0) ("ClassBrowser") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((111.0 115.0 50 10)) BLACK)) ((0.05 10.0 (PRI 72)) (TEXT (176.0 . 96.0) ("((PrintSummary &)&)") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((137.5 91.0 77 10)) BLACK)) ((0.05 10.0 (PRI 73)) (TEXT (96.0 . 48.0) ("title") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((89.0 43.0 14.0 10.0)) NIL)) ((0.05 10.0 (PRI 74)) (TEXT (176.0 . 48.0) ("%"Class Browser%"") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((146.0 43.0 60.0 10.0)) NIL)) ((0.05 10.0 (PRI 75)) (TEXT (96.0 . 40.0) (".") 1.0 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((95.0 35.0 2.0 10.0)) NIL)) ((0.05 10.0 (PRI 76)) (TEXT (96.0 . 32.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((95.0 27.0 2 10)) BLACK)) ((0.05 10.0 (PRI 77)) (TEXT (176.0 . 40.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((175.0 35.0 2 10)) BLACK)) ((0.05 10.0 (PRI 78)) (TEXT (176.0 . 32.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((175.0 27.0 2 10)) BLACK)) ((0.05 10.0 (PRI 79)) (TEXT (288.0 . 48.0) ("title") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((281.0 43.0 14 10)) BLACK)) ((0.05 10.0 (PRI 80)) (TEXT (368.0 . 48.0) ("%"Instance Browser%"") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((332.0 43.0 72 10)) BLACK)) ((0.05 10.0 (PRI 81)) (TEXT (288.0 . 40.0) ("subIV") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((277.5 35.0 21 10)) BLACK)) ((0.05 10.0 (PRI 82)) (TEXT (368.0 . 40.0) ("NIL") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((362.0 35.0 12 10)) BLACK)) ((0.05 10.0 (PRI 83)) (TEXT (288.0 . 32.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((287.0 27.0 2 10)) BLACK)) ((0.05 10.0 (PRI 84)) (TEXT (368.0 . 32.0) (".") 1 (CENTER CENTER) (MODERN 8 (BOLD REGULAR REGULAR)) ((367.0 27.0 2 10)) BLACK)) ((0.0 8.0 (PRI 85)) (WIRE ((232 . 296) (232 . 280)) (ROUND 2 BLACK) NIL NIL 1 (230 278 4 20) NIL)) ((0.0 48.0 (PRI 86)) (WIRE ((232 . 144) (136 . 128)) (ROUND 2 BLACK) NIL NIL 1 (134 126 100 20) NIL)) ((0.0 48.0 (PRI 87)) (WIRE ((232 . 144) (328 . 128)) (ROUND 2 BLACK) NIL NIL 1 (230 126 100 20) NIL))) (46.0 6.0 372.0 428.0) 1.0 8 (LIST ((PAGE NIL (PAPERSIZE NIL) (0 0 612 792) ((TEXT NIL NIL (0 0 612 792) NIL))) (PAGE NIL (PAPERSIZE NIL) (0 0 612 792) ((TEXT NIL NIL (0 0 612 792) NIL))) (PAGE NIL (PAPERSIZE NIL) (0 0 612 792) ((TEXT NIL NIL (0 0 612 792) NIL))))),VH(DEFAULTFONT 1 (GACHA 10) (GACHA 8) (TERMINAL 8) (POSTSCRIPT (GACHA 8))) +1I SKIO.GETFN.2VH(DEFAULTFONT 1 (GACHA 10) (GACHA 8) (TERMINAL 8) (POSTSCRIPT (GACHA 8))) +1Iz \ No newline at end of file diff --git a/docs/porter-irm/vol1/002-TOC.INTERPRESS b/docs/porter-irm/vol1/002-TOC.INTERPRESS new file mode 100644 index 00000000..6245ac2f Binary files /dev/null and b/docs/porter-irm/vol1/002-TOC.INTERPRESS differ diff --git a/docs/porter-irm/vol1/002-TOC.TEDIT b/docs/porter-irm/vol1/002-TOC.TEDIT new file mode 100644 index 00000000..997cd0db Binary files /dev/null and b/docs/porter-irm/vol1/002-TOC.TEDIT differ diff --git a/docs/porter-irm/vol1/003-LOF.INTERPRESS b/docs/porter-irm/vol1/003-LOF.INTERPRESS new file mode 100644 index 00000000..3243eb77 Binary files /dev/null and b/docs/porter-irm/vol1/003-LOF.INTERPRESS differ diff --git a/docs/porter-irm/vol1/003-LOF.TEDIT b/docs/porter-irm/vol1/003-LOF.TEDIT new file mode 100644 index 00000000..f334c0fd Binary files /dev/null and b/docs/porter-irm/vol1/003-LOF.TEDIT differ diff --git a/docs/porter-irm/vol1/01-INTRO.INTERPRESS b/docs/porter-irm/vol1/01-INTRO.INTERPRESS new file mode 100644 index 00000000..88335863 Binary files /dev/null and b/docs/porter-irm/vol1/01-INTRO.INTERPRESS differ diff --git a/docs/porter-irm/vol1/01-INTRO.TEDIT b/docs/porter-irm/vol1/01-INTRO.TEDIT new file mode 100644 index 00000000..bf661e85 --- /dev/null +++ b/docs/porter-irm/vol1/01-INTRO.TEDIT @@ -0,0 +1,33 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 1. INTRODUCTION 1 1. INTRODUCTION 1 "1"1. INTRODUCTION 6 Medley is a programming system. A programming system consists of a programming language, a large number of predefined programs (or functions, to use the Lisp terminology) that can be used either as direct user commands or as subroutines in user programs, and an environment that supports the programmer by providing a variety of specialized programming tools. The language and predefined functions of Interlisp are rich, but similar to those of other modern programming languages. The Medley programming environment, on the other hand, is very distinctive. Its most salient characteristic is an integrated set of programming tools which know enough about Interlisp and Common Lisp programming so that they can act as semi-autonomous, intelligent "assistants" to the programmer. In addition, the environment provides a completely self-contained world for creating, debugging and maintaining Interlisp programs. This manual describes all three components of the Medley system. There are discussions about the content and structure of the language, about the pieces of the system that can be incorporated into user programs, and about the environment. The line between user code and the environment is thin and changing. Most users extend the environment with some special features of their own. Because Interlisp is so easily extended, the system has grown over time to incorporate many different ideas about effective and useful ways to program. This gradual accumulation over many years has resulted in a rich and diverse system. That is the reason this manual is so large. Whereas the rest of this manual describes the individual pieces of the Interlisp system, this chapter attempts to describe the whole system---language, environment, tools, and the otherwise unstated philosophies that tie it all together. It is intended to give a global view of Interlisp to readers approaching it for the first time. Interlisp as a Programming Language 1 This manual does not contain an introduction to programming in Lisp. In this section, we simply highlight a few key points about Lisp on which much of the later material depends. The Lisp family of languages shares a common structure in which large programs (or functions) are built up by composing the results of smaller ones. Although Medley, like most modern Lisps, allows programming in almost any style one can imagine, the natural style of Lisp is functional and recursive, in that each function computes its result by selecting from or building upon the values given to it and then passing that result back to its caller (rather than by producing "side-effects" on external data structures, for example). A great many applications can be written in Lisp in this purely functional style, which is encouraged by the simplicity with which Lisp functions can be composed together. Lisp is also a list-manipulation language. The essential primitive data objects of any Lisp are "atoms" (symbols or identifiers) and "lists" (sequences of atoms or lists), rather than the "characters" or "numbers" of more conventional programming languages (although these are also present in all modern Lisps). Each Lisp dialect has a set of operations that act on atoms and lists, and these operations comprise the core of the language. Invisible in the programs, but essential to the Lisp style of programming, is an automatic memory management system (an "allocator" and a "garbage collector"). Allocation of new storage occurs automatically whenever a new data object is created. Conversely, that storage is automatically reclaimed for reuse when no other object makes reference to it. Automatic allocation and deallocation of memory is essential for rapid, large scale program development because it frees the programmer from the task of maintaining the details of memory administration, which change constantly during rapid program evolution. A key property of Lisp is that it can represent Lisp function definitions as pieces of Lisp list data. Each subfunction "call" (or function application) is written as a list in which the function is written first, followed by its arguments. Thus, (PLUS 1 2) is a list structure representation of the expression 1 + 2. Each program can be written as a list of such function applications. This representation of program as data allows one to apply the same operations to programs that one uses to manipulate data, which makes it very straightforward to write Lisp programs which look at and change other Lisp programs. This, in turn, makes it easy to develop programming tools and translators, which was essential in enabling the development of the Interlisp environment. One result of this ability to have one program examine another is that one can extend the Lisp programming language itself. If some desired programming idiom is not supported, it can be added simply by defining a function that translates the desired expression into simpler Lisp. Interlisp provides extensive facilities for users to make this type of language extension. Using this ability to extend itself, Interlisp has incorporated many of the constructs that have been developed in other modern programming languages (e.g. if-then-else, do loops, etc.). Interlisp as an Interactive Environment 1 Medley programs should not be thought of as autonomous, external files of source code. All Interlisp programming takes place within the Interlisp environment, which is a completely self-sufficient environment for developing and using Interlisp programs. Not only does the environment contain the obvious programming facilities (e.g., program editors, compilers, debuggers, etc.), but it also contains a variety of tools which assist the user by "keeping track" of what happens, so the user doesn't have to. For example, the Medley File Manager notices when programs or data have been changed, so that the system will know what needs to be saved at the end of the session. The "residential" style, where one stays within the environment throughout the development, from initial program definition through final debugging, is essential for these tools to operate. Furthermore, this same environment is available to support the final production version, some parts providing run time support and other parts being ignored until the need arises for further debugging or development. For terminal interaction with the user, Interlisp provides a top level "Read-Eval-Print" executive, which reads whatever the user types in, evaluates it, and prints the result. (This interaction is also recorded by the programmer's assistant, described below, so you can ask to do an action again, or even to undo the effects of a previous action.) Although each interactive executive defines a few specialized commands, most of the interaction will consist of simple evaluations of ordinary Lisp expressions. Thus, instead of specialized terminal commands for operations like manipulating your files, actions like this are carried out simply by typing the same expressions that you would use to accomplish them inside a Lisp program. This creates a very rich, simple and uniform set of interactive commands, since any Lisp expression can be typed at a command executive and evaluated immediately. In normal use, one writes a program (or rather, "defines a function") simply by typing in an expression that invokes the "function defining" function (DEFINEQ), giving it the name of the function being defined and its new definition. The newly defined function can be executed immediately, simply by using it in a Lisp expression. Although most Interlisp code is normally run compiled (for reasons of efficiency), the initial versions of most programs, and all of the user's terminal interactions, will be run interpreted. Eventually, as a function gets larger or is used in many places, it becomes more effective to compile it. Usually, by that stage, the function has been stored on a file and the whole file (which may contain many functions) is compiled at once. DEFINEQ, the compiler (COMPILE), and the interpreter (EVAL), are all themselves Lisp functions that use the ability to treat other Lisp expressions and programs as data. In addition to these basic programming tools, Interlisp also provides a wide variety of programming support mechanisms: List structure editor Since Lisp programs are represented as list structure, Medley provides an editor which allows one to change the list structure of a function's definition directly. See Chapter 16. Pretty-printer The pretty printer is a function that prints Lisp function definitions so that their syntactic structure is displayed by the indentation and fonts used. See page Chapter 26. Debugger When errors occur, the debugger is called, allowing you to examine and modify the context at the point of the error. Often, this lets you continued execution without starting over from the beginning. Within a break, the full power of Interlisp is available to you. Thus, the broken function can be edited, data structures can be inspected and changed, other computations carried out, and so on. All of this occurs in the context of the suspended computation, which will remain available to be resumed. See Chapter 14. DWIM The "Do What I Mean" package automatically fixes misspellings and errors in typing. See Chapter 20. Programmer's Assistant Medley keeps track of your actions during a session and allows each one to be replayed, undone, or altered. See Chapter 13. Masterscope Masterscope is a program analysis and management tool which can analyze users' functions and build (and automatically maintain) a data base of the results. This allows you to ask questions like "WHO CALLS ARCTAN" or "WHO USES COEF1 FREELY" or to request systematic changes like "EDIT WHERE ANY (function) FETCHES ANY FIELD OF (the data structure) FOO". See Chapter 19. Record/Datatype Package Medley allows you to define new data structures. This enables one to separate the issues of data access from the details of how the data is actually stored. See Chapter 8. File Package Files in Medley are managed by the system, removing the problem of ensuring timely file updates from the user. The file package can be modified and extended to accomodate new types of data. See Chapter 17. Performance Analysis These tools allow statistics on program operation to be collected and analyzed. See Chapter 22. These facilities are tightly integrated, so they know about and use each other, just as they can be used by user programs. For example, Masterscope uses the structural editor to make systematic changes. By combining the program analysis features of Masterscope with the features of the structural editor, large scale system changes can be made with a single command. For example, when the lowest-level interface of the Interlisp-D I/O system was changed to a new format, the entire edit was made by a single call to Masterscope of the form EDIT WHERE ANY CALLS '(BIN BOUT ...). [Burton et al., 1980] This caused Masterscope to invoke the editor at each point in the system where any of the functions in the list '(BIN BOUT ...) were called. This ensured that no functions used in input or output were overlooked during the modification. The personal machine implementations of Interlisp, such as Interlisp-D, provide some additional facilities, and interactive graphic interfaces to some of the older Interlisp programming tools: Multiple Processes Multiple and independent processes simplify problems which require logically separate pieces of code to operate in parallel. See Chapter 23. Windows The ability to have multiple, independent windows on the display allows many different processes or activities to be active on the screen at once. See Chapter 28. Inspector The inspector is a display tool for examining complex data structures encountered during debugging. See Chapter 26. Philosophy 1 The extensive environmental support that the Interlisp system provides has developed over the years in order to support a particular style of programming called "exploratory programming" [Sheil, 1983]. For many complex programming problems, the task of program creation is not simply one of writing a program to fulfill pre-identified specifications. Instead, it is a matter of exploring the problem (trying out various solutions expressed as partial programs) until one finds a good solution (or sometimes, any solution at all!). Such programs are by their very nature evolutionary; they are transformed over time from one realization into another in response to a growing understanding of the problem. This point of view has lead to an emphasis on having the tools available to analyze, alter, and test programs easily. One important aspect of this is that the tools be designed to work together in an integrated fashion, so that knowledge about the user's programs, once gained, is available throughout the environment. The development of programming tools to support exploratory programming is itself an exploration. No one knows all the tools that will eventually be found useful, and not all programmers want all of the tools to behave the same way. In response to this diversity, Interlisp has been shaped, by its implementors and by its users, to be easily extensible in several different ways. First, there are many places in the system where its behavior can be adjusted by the user. One way that this can be done is by changing the value of various "flags" or variables whose values are examined by system code to enable or suppress certain behavior. The other is where the user can provide functions or other behavioral specifications of what is to happen in certain contexts. For example, the format used for each type of list structure when it is printed by the pretty-printer is determined by specifications that are found on the list PRETTYPRINTMACROS. Thus, this format can be changed for a given type simply by putting a printing specification for it on that list. Another way in which users can effect Interlisp's behavior is by redefining or changing system functions. The "Advise" capability, for instance, permits the user to modify the operation of virtually any function in the system by wrapping user code "around" the selected function. (This same philosophy extends to the break package and tracing, so almost any function in the system can be broken or traced.) Experimentation is thus encouraged and actively facilitated, which allows the user to find useful pieces of the Interlisp system which can be configured to assist with application development. Since the entire system is implemented in Interlisp, there are extremely few places where the system's behavior depends on anything that the user cannot modify (such as a low level system implementation language). While these techniques provide a fair amount of tailorability, the price paid is that Interlisp presents an overall appearance of complexity. There are many flags, parameters and controls that affect the behavior one sees. Because of this complexity, Interlisp tends to be more comfortable for experts, rather than casual users. Beginning users of Interlisp should depend on the default settings of parameters until they learn what dimensions of flexibility are available. At that point, they can begin to "tune" the system to their preferences. Appropriately enough, even Interlisp's underlying philosophy was itself discovered during Interlisp's development, rather than laid out beforehand. The Interlisp environment and its interactive style were first analyzed in Sandewall's excellent paper [Sandewall, 1978]. The notion of "exploratory programming" and the genesis of the Interlisp programming tools in terms of the characteristic demands of this style of programming was developed in [Sheil, 1983]. The evolution and structure of the Interlisp programming environment are discussed in greater depth in [Teitelman & Masinter, 1981]. How to Use this Manual 1 This document is a reference manual, not a primer. We have tried to provide a manual that is complete, and that lets you find particular items as easily as possible. Sometimes, these goals have been achieved at the expense of simplicity. For example, many functions have a number of arguments that are rarely used. In the interest of providing a complete reference, these arguments are fully explained, even though you will normally let them default. There is a lot of information in this manual that is of interest only to experts. Do not try to read straight through this manual, like a novel. In general, the chapters are organized with overview explanations and the most useful functions at the beginning of the chapter, and implementation details towards the end. If you are interested in becoming acquainted with Interlisp using this manual, the best way would be to skim through the whole book, reading the beginning of each chapter. A few comments about the notational conventions used in this manual: Lisp object notation: All Interlisp objects in this manual are printed in the same font: Functions (AND, PLUS, DEFINEQ, LOAD); Variables (MAX.INTEGER, FILELST, DFNFLG); and arbitrary Interlisp expressions: (PLUS 2 3), (PROG ((A 1)) ...), etc. Case is significant: An important piece of information, often missed by newcomers to Interlisp, is that upper and lower case is significant. The variable FOO is not the same as the variable foo or the variable Foo. By convention, most Interlisp system functions and variables are all uppercase, but users are free to use upper and lower case for their own functions and variables as they wish. One exception to the case-significance rule is provided by the Interlisp CLISP facility, which allows iterative statement operators and record operations to be typed in either all uppercase or all lowercase letters: (for X from 1 to 5 ...) is the same as (FOR X FROM 1 TO 5 ...). The few situations where this is the case are explicitly mentioned in the manual. Generally, one should assume that case is significant. This manual contains a large number of descriptions of functions, variables, commands, etc, which are printed in the following standard format: (FOO BAR BAZ %) [Function] 1 This is a description for the function named FOO. FOO has two arguments, BAR and BAZ. Some system functions have extra optional arguments that are not documented and should not be used. These extra arguments are indicated by "%". The descriptor [Function] indicates that this is a function, rather than a [Variable], [Macro], etc. For function definitions only, this can also indicate the "function type" (see page X.XX): [NLambda Function], [NoSpread Function], or [NLambda NoSpread Function], which describes whether the function takes a fixed or variable number of arguments, and whether the arguments are evaluated or not. [Function] indicates a lambda spread function (the most common function type). References 1 [Burton, et al., 1980] Burton, R. R., L. M. Masinter, A. Bell, D. G. Bobrow, W. S. Haugeland, R.M. Kaplan and B.A. Sheil, "Interlisp-D: Overview and Status" --- in [Sheil & Masinter, 1983]. [Sandewall, 1978] Sandewall, Erik, "Programming in the Interactive Environmnet: The LISP Experience" --- ACM Computing Surveys, vol 10, no 1, pp 35-72, (March 1978). [Sheil, 1983] Sheil, B.A., "Environments for Exploratory Programming" --- Datamation, (February, 1983) --- also in [Sheil & Masinter, 1983]. [Sheil & Masinter, 1983] Sheil, B.A. and L. M. Masinter, "Papers on Interlisp-D", Xerox PARC Technical Report CIS-5 (Revised), (January, 1983). [Teitelman & Masinter, 1981] Teitelman, W. and L. M. Masinter, "The Interlisp Programming Environment" --- Computer, vol 14, no 4, pp 25-34, (April 1981) --- also in [Sheil & Masinter, 1983]. (SEQUENCE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE NIL) (0 0 612 792) ((HEADING NIL (HEADINGTYPE RECTOFOOT) (58 22 554 36) NIL) (HEADING NIL (HEADINGTYPE RECTOFOOTRULE) (58 30 554 36) NIL) (HEADING NIL (HEADINGTYPE DRAFTMESSAGE) (200 5 412 36) NIL) (TEXT NIL NIL (58 54 500 726) NIL))) (ALTERNATE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE NIL) (0 0 612 792) ((HEADING NIL (HEADINGTYPE DRAFTMESSAGE) (200 775 412 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (58 761 554 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEADRULE) (58 757 554 36) NIL) (HEADING NIL (HEADINGTYPE VERSOFOOT) (58 22 554 36) NIL) (HEADING NIL (HEADINGTYPE VERSOFOOTRULE) (58 30 554 36) NIL) (HEADING NIL (HEADINGTYPE DRAFTMESSAGE) (200 5 412 36) NIL) (TEXT NIL NIL (58 54 500 684) NIL))) (PAGE NIL (PAPERSIZE NIL) (0 0 612 792) ((HEADING NIL (HEADINGTYPE DRAFTMESSAGE) (200 775 412 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (58 761 554 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEADRULE) (58 757 554 36) NIL) (HEADING NIL (HEADINGTYPE RECTOFOOT) (58 22 554 36) NIL) (HEADING NIL (HEADINGTYPE RECTOFOOTRULE) (58 30 554 36) NIL) (HEADING NIL (HEADINGTYPE DRAFTMESSAGE) (200 5 412 36) NIL) (TEXT NIL NIL (58 54 500 684) NIL)))))))5-T3(T,HH +,ll,HH,HH,2F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR-KKT3KT5-T2 TITAN +TITAN + HELVETICACLASSIC +CLASSIC + HELVETICA +CLASSIC +MODERN +MODERN +MODERNMODERN +  HRULE.GETFNCLASSIC +   HRULE.GETFNCLASSIC +    HRULE.GETFNCLASSIC + + + HRULE.GETFNCLASSIC + +  IM.CHAP.GETFNMODERN + HRULE.GETFNMODERN +  + 2, z O$ HRULE.GETFN HELVETICA +f` +61( HRULE.GETFNMODERN + ; e oyk) +w$p  HRULE.GETFNMODERN + u3&V HRULE.GETFN HELVETICA +Ef * +i$!Z. .  +   HRULE.GETFNMODERN +  S  (  +  ( 8  HRULE.GETFNMODERN + j(K +9lMRz \ No newline at end of file diff --git a/docs/porter-irm/vol1/02-LITATOM.INTERPRESS b/docs/porter-irm/vol1/02-LITATOM.INTERPRESS new file mode 100644 index 00000000..354fbb16 Binary files /dev/null and b/docs/porter-irm/vol1/02-LITATOM.INTERPRESS differ diff --git a/docs/porter-irm/vol1/02-LITATOM.TEDIT b/docs/porter-irm/vol1/02-LITATOM.TEDIT new file mode 100644 index 00000000..635b019f --- /dev/null +++ b/docs/porter-irm/vol1/02-LITATOM.TEDIT @@ -0,0 +1,75 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 2. LITATOMS (SYMBOLS) 1 2. LITATOMS (SYMBOLS) 1 "2"2. LITATOMS (SYMBOLS) 6 A litatom (for "literal atom") is an object which conceptually consists of a print name, a value, a function definition, and a property list. Litatoms are also known as "symbols" in Common Lisp. For clarity, we will use the term "symbol". A symbol is read as any string of non-delimiting characters that cannot be interpreted as a number. The syntactic characters that delimit symbols are called "separator" or "break" characters (see Chapter 25) and normally are space, end-of-line, line-feed, left parenthesis (, right parenthesis ), double quote ", left square bracket [, and right square bracket ]. However, any character may be included in a symbol by preceding it with the character %. Here are some examples of symbols: A wxyz 23SKIDDOO %] 3.14125 + 17 Long%Litatom%With%Embedded%Spaces (LITATOM(LITATOM (Function) NIL NIL ("2") 1) X) [Function] Returns T if X is a symbol, NIL otherwise. Note that a number is not a symbol. (LITATOM NIL) = T (ATOM (ATOM% (Function) NIL NIL ("2") 1)X) [Function] Returns T if X is an atom (i.e., a symbol or a number); NIL otherwise. Warning: (ATOM X) is NIL if X is an array, string, etc. In many dialects of Lisp, the function ATOM is defined equivalent to the Interlisp function NLISTP. (LISTP NIL) = NIL Symbols are printed by PRINT and PRIN2 as a sequence of characters with %s inserted before all delimiting characters (so that the symbol will read back in properly). Symbols are printed by PRIN1 as a sequence of characters without these extra %s. For example, the symbol consisting of the five characters A, B, C, (, and D will be printed as ABC%(D by PRINT and ABC(D by PRIN1. Symbols can also be constructed by PACK, PACK*, SUBATOM, MKATOM, and GENSYM (which uses MKATOM). Symbols are unique. In other words, if two symbols print the same, they will always be EQ. Note that this is not true for strings, large integers, floating-point numbers, and lists; they all can print the same without being EQ. Thus, if PACK or MKATOM is given a list of characters corresponding to a symbol that already exists, they return a pointer to that symbol, and do not make a new symbol. Similarly, if the read program is given as input a sequence of characters for which a symbol already exists,it returns a pointer to that symbol. Symbols are limited to 255 characters . Attempting to create a larger symbol either via PACK or by typing one in (or readying from a file) will cause an error: ATOM TOO LONG. Using Symbols as Variables 1 Symbols are commonly used as variables. Each symbol has a "top level" variable binding, which can be an arbitrary Interlisp object. Symbols may also be given special variable bindings within PROGs or function called, which only exist for the duration of the function. When a symbol is evaluated, the "current" variable binding is returned. This is the most recent special variable binding, or the top level binding if the symbol has not been rebound. SETQ is used to change the current binding.. For more information on variable bindings in Interlisp, see Chapter 11. Note: The compiler (see Chapter 18) treats variables somewhat differently than the interpreter, and the user has to be aware of these differences when writing functions that will be compiled. For example, variable references in compiled code are not checked for NOBIND, so compiled code will not generate unbound atom errors. In general, it is better to debug interpreted code, before compiling it for speed. The compiler offers some facilities to increase the efficiency of variable use in compiled functions. Global variables can be defined so that the entire stack is not searched at each variable reference. Local variables allow compiled functions to access variable binds which are not on the stack, which reduces variable conflicts, and also makes variable lookup faster. By convention, a symbol whose top level binding is to the symbol NOBIND is considered to have no top level binding. If a symbol has no local variable bindings, and its top level value is NOBIND, attemptint to evaluate it will cause an unbound atom error. The two symbols T and NIL always evaluate to themselves. Attempting to change the binding of T or NIL with the functions below will generate the error ATTEMPT TO SET T or ATTEMPT TO SET NIL. The following functions (except BOUNDP) will also generate the error ARG NOT LITATOM, if not given a symbol. (BOUNDP(BOUNDP (Function) NIL NIL ("2") 2) VAR) [Function] Returns T if VAR has a special variable binding (even if bound to NOBIND), or if VAR has a top level value other than NOBIND; otherwise NIL. In other words, if X is a symbol, (EVAL X) will cause an UNBOUND ATOM error if and only if (BOUNDP X) returns NIL. (SET(SET (Function) NIL NIL ("2") 2) VAR VALUE) [NoSpread Function] Sets the "current" variable binding of VAR to VALUE, and returns VALUE. SET is a normal function, so both VAR and VALUE are evaluated before it is called. Thus, if the value of X is B, and value of Y is C, then (SET X Y) would result in B being set to C, and C being returned as the value of SET. (SETQ(SETQ (Function) NIL NIL ("2") 2) VAR VALUE) [NoSpread Function] Nlambda version of SET. VAR is not evaluated, VALUE is. Thus, if the value of X is B and the value of Y is C, (SETQ X Y) would result in X (not B) being set to C, and C being returned. Note: Since SETQ is an nlambda, neither argument is evaluated during the calling process. However, SETQ itself calls EVAL on its second argument. As a result, typing (SETQ VAR FORM) and SETQ(VAR FORM) to the Interlisp Executive are equivalent: in both cases VAR is not evaluated, and FORM is. (SETQQ(SETQQ (Function) NIL NIL ("2") 2) VAR VALUE) [NoSpread Function] Like SETQ except that neither argument is evaluated, e.g., (SETQQ X(A B C)) sets X to (A B C). (PSETQ(PSETQ (Macro) NIL NIL ("2") 3) VAR1 VALUE1 ... VARN VALUEN) [Macro] Does a SETQ in parallel of VAR1 (unevaluated) to the value of VALUE1, VAR2 to the value of VALUE2, etc. All of the VALUE1terms are evaluated before any of the assignments. Therefore, (PSETQ A B B A) can be used to swap the values of the variables A and B. (GETTOPVAL(GETTOPVAL (Function) NIL NIL ("2") 3) VAR) [Function] Returns the top level value of VAR (even if NOBIND), regardless of any intervening local bindings. (SETTOPVAL(SETTOPVAL (Function) NIL NIL ("2") 3) VAR VALUE) [Function] Set the top level value of VAR to VALUE, regardless of any intervening bindings, and returns VALUE. A major difference betweenvarious Interlisp implementations is the way that variable bindings are implemented. Interlisp-10 and Interlisp-Jerico use what is called "shallow" binding. Interlisp-D and Interlisp-VAX use what is called "deep" binding. In a deep binding system, a variable is bound by saving on the stack the variable's new value. When a variable is accessed, its value is found by searching the stack for the most recent binding. If the variable is not found on the stack, the top level binding is retrieved from a "value cell" associated with the variable. In a "shallow" binding system, a variable is bound by saving on the stack the variable name and the variable's old value, and putting the value in the variable's value cell. When a variable is accessed, its value is always found in its value cell. GETTOPVAL and SETTOPVAL are less efficient in a shallow binding system, because they have to search the stack for rebindings. It is more economical to simply rebind variables. In a deep binding system, GETTOPVAL and SETTOPVAL are very efficient since they do not have to search the stack, but can simply access the value cell directly. GETTOPVAL and SETTOPVAL can be used to access a variable's value cell, in either a shallow or deep binding system. (GETATOMVAL(GETTOPVAL (Function) NIL NIL ("2") 3) VAR) [Function] Returns the value in the value cell of VAR. In a shallow binding system, this is the same as (EVAL ATM), or simply VAR. In a deep binding system, this is the same as (GETTOPVAL VAR). (SETATOMVAL(SETATOMVAL (Function) NIL NIL ("2") 3) VAR VALUE) [Function] Sets the value cell of VAR to VALUE. In a shallow binding system, this is the same as SET. In a deep binding system, this is the same as SETTOPVAL. Function Definition Cells 1 Each symbol has a function definition cell, which is accessed when a symbol is used as a function. The mechanism for accessing and setting the function definition cell of a symbol is described in Chapter 10. Property Lists 1 Each symbol has an associated property list, which allows a set of named objects to be associated with the symbol. A property list associates a name (known as a "property name" or "property") with an arbitrary object (the "property value" or "value"). Sometimes the phrase "to store on the property X" is used, meaning to place the indicated information on a property list under the property name X. Property names are usually symbols or numbers, although no checks are made. However, the standard property list functions all use EQ to search for property names, so they may not work with non-atomic property names. The same object can be used as both a property name and a property value. Note: Many symbols in the system already have property lists, with properties used by the compiler, the break package, DWIM, etc. Be careful not to clobber such system properties. The variable SYSPROPS is a list of property names used by the system. The functions below are used to manipulate the property lists of symbols. Except when indicated, they generate the error ARG NOT LITATOM, if given an object that is not a symbol. (GETPROP(GETPROP (Function) NIL NIL ("2") 4) ATM PROP) [Function] Returns the property value for PROP from the property list of ATM. Returns NIL if ATM is not a symbol, or PROP is not found. GETPROP also returns NIL if there is an occurrence of PROP but the corresponding property value is NIL. This can be a source of program errors. Note: GETPROP used to be called GETP. (PUTPROP(PUTPROP (Function) NIL NIL ("2") 4) ATM PROP VAL) [Function] Puts the property PROP with value VAL on the property list of ATM. VAL replaces any previous value for the property PROP on this property list. Returns VAL. (ADDPROP(ADDPROP (Function) NIL NIL ("2") 4) ATM PROP NEW FLG) [Function] Adds the value NEW to the list which is the value of property PROP on the property list of the ATM. If FLG is T, NEW is CONSed onto the front of the property value of PROP; otherweise, it is NCONCed on the end (using NCONC1). If ATM does not have a property PROP, or the value is not a list, then the effect is the same as (PUTPROP ATM PROP (LIST NEW)). ADDPROP returns the (new) property value. Example: (PUTPROP 'POCKET 'CONTENTS NIL) (NIL) (ADDPROP 'POCKET 'CONTENTS 'COMB) (COMB) (ADDPROP 'POCKET 'CONTENTS 'WALLET) (COMB WALLET) (REMPROP(REMPROP (Function) NIL NIL ("2") 4) ATM PROP) [Function] Removes all occurrences of the property PROP (and its value) from the property list of ATM. Returns PROP if any were found, otherwise NIL. (REMPROPLIST(REMPROPLIST (Function) NIL NIL ("2") 4) ATM PROPS) [Function] Removes all occurrences of all properties on the list PROPS (and their corresponding property values) from the property list of ATM. Returns NIL. (CHANGEPROP(CHANGEPROP (Function) NIL NIL ("2") 4) X PROP1 PROP2) [Function] Changes the property name of property PROP1 to PROP2 on the property list of X (but does not affect the value of the property). Returns X, unless PROP1 is not found, in which case it returns NIL. (PROPNAMES(PROPNAMES (Function) NIL NIL ("2") 5) ATM) [Function] Returns a list of the property names on the property list of ATM. (DEFLIST(DEFLIST (Function) NIL NIL ("2") 5) L PROP) [Function] Used to put values under the same property name on the property lists of several symbols. L is a list of two-element lists. The first element of each is a symbol, and the second element is the property vqalue of the property PROP. Returns NIL. For example: (DEFLIST '(FOO MA)(BAR CA)(BAZ RI)) 'STATE) puts MA on FOO's STATE property, CA on BAR's STATE property, and RI on BAZ's STATE property. Property lists are conventionally implemented as lists of the form (NAME1 VALUE1 NAME2 VALUE2...) although the user can store anything as the property list of a symbol. However, thge functions which manipulate property lists observe this convention by searching down the property lists two CDRs at a time. Most of these functions also generate the error ARG NOT LITATOM if given an argument which is not a symbol, so they cannot be used directly on lists. (LISTPUT, LISTPUT1, LISTGET, and LISTGET1 are functions similar to PUTPROP and GETPROP that work directly on lists. See Chapter 3.) The property lists of symbols can be directly accessed with the following functions. (GETPROPLIST(GETPROPLIST (Function) NIL NIL ("2") 5) ATM) [Function] Returns the property list of ATM. (SETPROPLIST(SETPROPLIST (Function) NIL NIL ("2") 5) ATM LST) [Function] If ATM is a symbol, sets the property list of ATM to be LST, and returns LST as its value. (GETLIS(GETLIS (Function) NIL NIL ("2") 5) X PROPS) [Function] Searches the property list of X, and returns the property list as of the first property on PROPS that it finds. For example: (GETPROPLIST 'X) (PROP1 A PROP3 B A C) (GETLIS 'X '(PROP2 PROP3)) (PROP3 B A C) Returns NIL if no element on props is found. X can also be a list itself, in which case it is searched as described above. If X is not a symbol or a list, returns NIL. Print Names 1 Each symbol has a print name, a string of characters that uniquely identifies that symbol. The term "print name" has been extended, however, to refer to the characters that are output when any object is printed. In Interlisp, all objects have print names, although only symbols and strings have their print name explicitly stored. This section describes a set of functions which can be used to access and manipulate the print names of any object, though they are primarily used with the print names of symbols. The print name of an object is those characters that are output when the object is printed using PRIN1, e.g., the print name of the symbol ABC%(D consists of the five characters ABC(D. The print name of the list (A B C) consists of the seven characters (A B C) (two of the characters are spaces). Sometimes we have occasion to refer to a "PRIN2-name". The PRIN2-name of an object is those characters output when the object is printed using PRIN2. Thus the PRIN2-name of the symbol ABC%(D is the six characters ABC%(D. The PRIN2-name depends on what readtable is being used (see Chapter 25), since this determines where %s will be inserted. Many of the functions below allow either print names of PRIN2-names to be used, as specified by FLG and RDTBL arguments. If FLG is NIL, print names are used. Otherwise, PRIN2-names are used, computed with respect to the readtable RDTBL (or the current readtable, if RDTBL = NIL). Note: The print name of an integer depends on the setting of RADIX (see Chapter 25). The functions described in this section (UNPACK, NCHARS, etc.) define the print name of an integer as though the radix was 10, so that (PACK (UNPACK 'X9)) will always be X9 (and not X11, if RADIX is set to 8). However, integers will still be printed by PRIN1 using the current radix. The user can force these functions to use print names in the current radix by changing the setting of the variable PRXFLG (see Chapter 25). (MKATOM(MKATOM (Function) NIL NIL ("2") 6) X) [Function] Creates and returns an atom whose print name is the name as that of the string X or, if X is not a string, the same as that of (MKSTRING X). Examples: (MKATOM '(A B C)) => %(A% B% C%) (MKATOM "1.5" => 1.5 Note that the last example returns a number, not a symbol. It is a deeply-ingrained feature of Interlisp that no symbol can have the print name of a number. (SUBATOM(SUBATOM (Function) NIL NIL ("2") 6) X N M) [Function] Equivalent to (MKATOM (SUBSTRING X N M)), but does not make a string pointer (see Chapter 4). Returns an atom made from the Nth through Mth characters of the print name of X. If N or M are negative, they specify positions counting backwards from the end of the print name. Examples: (SUBATOM "FOO1.5BAR" 4 6) => 1.5 (SUBATOM '(A B C) 2 -2) => A% B% C (PACK(PACK (Function) NIL NIL ("2") 6) X) [Function] If X is a list of atoms, PACK returns a single atom whose print name is the concatenation of the print names of the atoms in X. If the concatenated print name is the same as that of a number, PACK returns that number. For example: (PACK '(A BC DEF G)) => ABCDEFG (PACK '(1 3.4)) => 13.4 (PACK '(1 E -2)) => .01 Although X is usually a list of atoms, it can be a list of arbitrary Interlisp objects. The value of PACK is still a single atom whose print name is the concatenation of the print names of all the elements of X, e.g., (PACK '((A B) "CD")) => %(A% B%)CD If X is not a list or NIL, PACK generates the error ILLEGAL ARG. (PACK* X1 X2... XN(PACK* (Function) NIL NIL ("2") 6) ) [NoSpread Function] Nospread version of PACK that takes an arbitrary number of arguments, instead of a list. Examples: (PACK* 'A 'BC 'DEF 'G => ABCDEFG (PACK* 1 3.4)) => 13.4 (UNPACK(UNPACK (Function) NIL NIL ("2") 7) X FLG RDTBL) [Function] Returns the print name of X as a list of single-character atoms. Examples: (UNPACK 'ABC5D) => (A B C 5 D) (UNPACK "ABC(D") => (A B C %( D) If FLG = T, the PRIN2-name of X is used (computed with respect to RDTBL). Examples: (UNPACK 'ABC5D) => (A B C 5 D) (UNPACK "ABC(D") => (A B C %( D) Note: (UNPACK X) performs N CONSes, where N is the number of characters in the print name of X. (DUNPACK X SCRATCHLIST FLG RDTBL(DUNPACK (Function) NIL NIL ("2") 7) ) [Function] A destructive version of UNPACK that does not perform any CONSes but instead reuses the list SCRATCHLIST. If the print name is too long to fit in SCRATCHLIST, DUNPACK will extend it. If SCRATCHLIST is not a list, DUNPACK returns (UNPACK X FLG RDTBL). (NCHARS X FLG RDTBL(NCHARS (Function) NIL NIL ("2") 7) ) [Function] Returns the number of characters in the print name of X. If FLG = T, the PRIN2-name is used. Examples: (NCHARS 'ABC) => 3 (NCHARS "ABC" T) => 5 Note: NCHARS works most efficiently on symbols and strings, but can be given any object. (NTHCHAR X N FLG RDTBL(NTHCAR (Function) NIL NIL ("2") 7)) [Function] Returns X, if X is a tail of the list Y; otherwise NIL. X is a tail of Y if it is EQ to 0 or more CDRs of Y. (NTHCHAR 'ABC 2) => B (NTHCHAR 15.6 2) => 5 (NTHCHAR 'ABC%(D -3 T) => %% (NTHCHAR "ABC" 2) => B (NTHCHAR "ABC" 2 T) => A Note: NTHCAR and NCHARS work much faster on objects that actually have an internal representation of their print name, i.e., symbols and strings, than they do on numbers and lists, as they do not have to simulate printing. (L-CASE(L-CASE (Function) NIL NIL ("2") 7) X FLG) [Function] Returns a lowercase version of X. If FLG is T, the first letter is capitalized. If X is a string, the value of L-CASE is also a string. If X is a list, L-CASE returns a new list in which L-CASE is computed for each corresponding element and non-NIL tail of the original list. Examples: (L-CASE 'FOO) => foo (L-CASE 'FOO T) => Foo (L-CASE "FILE NOT FOUND" T) => "File not found" (L-CASE '(JANUARY FEBRUARY (MARCH "APRIL")) T) => '(January February (March "April")) (U-CASE(U-CASE (Function) NIL NIL ("2") 8) X ) [Function] Similar to L-CASE, except returns the uppercase version of X. (U-CASEP(U-CASEP (Function) NIL NIL ("2") 8) X) [Function] Returns T if X contains no lowercase letters; NIL otherwise. (GENSYM(GENSYM (Function) NIL NIL ("2") 8) PREFIX % % % %) [Function] Returns a symbol of the form Xnnnn, where X = PREFIX (or A if PREFIX is NIL) and nnnn is an integer. Thus, the first one generated is A0001, the second A0002, etc. The integer suffix is always at least four characters long, but it can grow beyond that. For example, the next symbol produced after A9999 would be A10000. GENSYM provides a way of generating symbols for various uses within the system . GENNUM(GENNUM (Variable) NIL NIL ("2") 8) [Variable] The value of GENNUM, initially 0, determines the next GENSYM, e.g., if GENNUM is set to 23, (GENSYM) = A0024. The term "gensym" is used to indicate a symbol that was produced by the function GENSYM. Symbols generated by GENSYM are the same as any other symbols: they have property lists, and can be given function definitions. The symbols are not guaranteed to be new. For example, if the user has previously created A0012, either by typing it in, or via PACK or GENSYM itself, then if GENNUM is set to 11, the next symbol returned by GENSYM will be the A0012 already in existence. (MAPATOMS(MAPATOMS (Function) NIL NIL ("2") 8) FN) [Function] Applies FN (a function or lambda expression) to every symbol in the system. Returns NIL. For example: (MAPATOMS (FUNCTION (LAMBDA(X) (if (GETD X) then (PRINTX)] will print every symbol with a function definition. Note: In some implementations of Interlisp, unused symbols may be garbage collected, which can effect the action of MAPATOMS. (APROPOS(APROPOS (Function) NIL NIL ("2") 8) STRING ALLFLG QUITFLG OUTPUT) [Function] Apropos scans all symbols in the system for those which have STRING as a substring and prints them on the terminal along with a line for each relevant item defined for each selected atom. Relevant items are: f functions definitions, for which only the arglist is printed f dynamic variable values f non-null property lists PRINTLEVEL (see Chapter 25) is set to (3 . 5) when APROPOS is printing. If ALLFLG is NIL, then atoms with no relevant items and "internal" atoms are omitted ("internal" currently means those symbols whose print name begins with a \ or those symbols produced by GENSYM). If ALLFLG is a function (i.e., (FNTYP ALLFLG) is non-NIL), then it is used as a predicate on atoms selected by the substring match, with value NIL meaning to omit the atom. If ALLFLG is any other non-NIL value, then no atoms are omitted. Characters and Character Codes 1 Characters may be represented in two ways: as single-character atoms, or as integer character codes. In many situations, it is more efficient to use character codes, so Interlisp provides parallel functions for both representations. Interlisp-D uses the 16-bit NS character set, described in the document Character Code Standard (Xerox System Integration Standards, XSIS 058404, April 1984). Legal character codes range from 0 to 65535. The NS (Network Systems) character encoding encompasses a much wider set of available characters than the 8-bit character standards (such as ASCII), including characters comprising many foreign alphabets and special symbols. For instance, Interlisp-D supports the display and printing of the following: f Le systme d'information Xerox 11xx est remarqueablement polyglotte f Das Xerox 11xx Kommunikationssystem bietet merkwrdige multilinguale Nutzmglichkeiten f M 8 "" [w] N v with Rwv: M 8 [v] These characters can be used in strings, symbol print names, symbolic files, or anywhere else 8-bit characters could be used. All of the standard string and print name functions (RPLSTRING, GNC, NCHARS, STRPOS, etc.) accept symbols and strings containing NS characters. For example: (STRPOS "char""this is an 8-bit character string") 18 (STRPOS "char""celui-ci comports des characteres NS") 23 In almost all cases, a program does not have to distinguish between NS characters or 8-bit characters. The exception to this rule is the handling of input/output operations (see Chapter 25). The function CHARCODE (see below) provides a simple way to create individual NS character codes. The VirtualKeyboards library package provides a set of virtual keyboards that allows keyboard or mouse entry of NS characters. (PACKC(PACKC (Function) NIL NIL ("2") 9) X) [Function] Similar to PACK except X is a list of character codes. For example, (PACKC '(70 79 79)) => FOO (CHCON(CHCON (Function) NIL NIL ("2") 10) X FLG RDTBL) [Function] Like UNPACK, except returns the print name of X as a list of character codes. If FLG = T, the PRIN2-name is used. For example: (CHCON 'FOO) => (70 79 79) (DCHCON(DCHCON (Function) NIL NIL ("2") 10) X SCRATCHLIST FLG RDTBL) [Function] Similar to DUNPACK. (NTHCHARCODE(NTHCHARCODE (Function) NIL NIL ("2") 10) X N FLG RDTBL) [Function] Similar to NTHCHAR, except returns the character code of the Nth character of the print name of X. If N is negative, it is interpreted as a count backwards from the end of X. If the absolute value of N is greater than the number of characters in X, or 0, then the value of NTHCHARCODE is NIL. If FLG is T, then the PRIN2-name of X is used, computed with respect to the readtable RDTBL. (CHCON1(CHCON1 (Function) NIL NIL ("2") 10) X) [Function] Returns the character code of the first character of the print name of X; equal to (NTHCHARCODE X 1). (CHARACTER(CHARACTER (Function) NIL NIL ("2") 10) N) [Function] N is a character code. Returns the atom having the corresponding single character as its print name. (CHARACTER 70) => F (FCHARACTER(FCHARACTER (Function) NIL NIL ("2") 10) N) [Function] Fast version of CHARACTER that compiles open. The following function makes it possible to gain the efficiency that comes from dealinig with character codes without losing the symbolic advantages of character atoms. (CHARCODE(CHARCODE (Function) NIL NIL ("2") 10) CHAR) [Function] Returns the character code specified by CHAR (unevaluated). If CHAR is a one-character atom or string, the corresponding character code is simply returned. Thus, (CHARCODE A) is 65, (CHARCODE 0) is 48. If CHAR is a multi-character symbol or string, it specifies a character code as described below. If CHAR is NIL, CHARCODE simply returns NIL. Finally, if CHAR is a list structure, the value is a copy of CHAR with all the leaves replaced by the corresponding character codes. For instance, (CHARCODE (A (B C))). If a character is specified by a multi-character symbol or string, CHARCODE interprets it as follows: CR, SPACE, etc. The variable CHARACTERNAMES contains an association list mapping special symbols to character codes. Among the characters defined this way are CR (13), LF (10), SPACE or SP (32), ESCAPE or ESC (27), BELL (7), BS (8), TAB (9), NULL (0), and DEL (127). The symbol EOL maps into the appropriate end-of-line character code in the different Interlisp implementations (31 in Interlisp-10, 13 in Interlisp-D, 10 in Interlisp-VAX). Examples: (CHARCODE SPACE) => 32 (CHARCODE CR) => 13 CHARSET, CHARNUM, CHARSET-CHARNUM If the character specification is a symbol or string of the form CHARSET, CHARNUM, or CHARSET-CHARNUM, the character code for the character number CHARNUM in the character set CHARSET is returned. The 16-bit NS character encoding is divided into a large number of "character sets". Each 16-bit character can be decoded into a character set (an integer from 0 to 254 inclusive) and a character number (also an integer from 0 to 254 inclusive). CHARSET is either an octal number, or a symbol in the association list CHARACTERSETNAMES (which defines the character sets for GREEK, CYRILLIC, etc.). CHARNUM is either an octal number, a single-character symbol, or a symbol from the association list CHARACTERNAMES. If CHARNUM is a single-digit number, it is interpreted as the character "2", rather than as the octal number 2. Examples: (CHARCODE 12,6) => 2566 (CHARCODE 12,SPACE) => 2592 (CHARCODE GREEK,A) => 9793 CHARSPEC (control chars) If the character specification is a symbol or string of one of the forms above, preceded by the character , this indicates a "control character," derived from the normal character code by clearing the seventh bit of the character code (normally set). Examples: (CHARCODE A) => 1 (CHARCODE GRTEEK,A) => 9729 #CHARSPEC (meta chars) If the character specification is a symbol or string of one of the forms above, preceded by the charactger #, this indicates a meta character, derived from the normal character code by setting the eighth bit of the character code (normally cleared). and # can both be set at once. Examples: (CHARCODE # A) => 193 (CHARCODE # GRTEEK,A) => 9857 A CHARCODE form can be used wherever a structure of character codes would be appropriate. For example: (FMEMB (NTHCHARCODE X 1)(CHARCODE (CR LF SPACE A))) (EQ (READCCODE FOO)(CHARCODE GREEK,A)) There is a macro for CHARCODE which causes the character-code structure to be constructed at compile-time. Thus, the compiled code for these examples is exactly as efficient as the less readable: (FMEMB (NTHCHARCODE X 1)(QUOTE (13 10 32 1))) (EQ (READCCODE FOO)9793) (SELCHARQ(FLENGTH (Function) NIL NIL ("2") 11) E CLAUSE1... CLAUSEN DEFAULT) [Function] Similar to SELECTQ (see Chapter 9), except that the selection keys are determined by applying CHARCODE (instead of QUOTE) to the key-expressions. If the value of E is a character code or NIL, and it is EQ or MEMB to the result of applying CHARCODE to the first element of a clause, the remaining forms of that clause are evaluated. Otherwise, the default is evaluated. Thus (SELCHARQ (BIN FOO)) ((SPACE TAB)(FUM)) (( D NIL)(BAR)) (a (BAZ)) (ZIP))) is exactly equivalent to (SELECTQ (BIN FOO)) ((32 9)(FUM)) ((4 NIL)(BAR)) (97 (BAZ)) (ZIP))) Furthermore, SELCHARQ has a macro definition such that it always compiles as an equivalent SELECTQ. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "2-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "2-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "2-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "2-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "2-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "2-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))))),/,ll,,,4HZ +TT5Hll2HH2HH +2ll22lxx2lxx3HHT6HT +TT3HH +T22ll2l2lll/ll/ll3T,l2l2Hll,ll,,-T3(T,,HH +,,ll,HH,HH3HZ +T,2F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR CLASSIC +TITAN +TITAN + HELVETICACLASSIC +CLASSIC +CLASSIC + HELVETICA +CLASSIC +MODERNMODERN +, HRULE.GETFNCLASSIC + , + HRULE.GETFNCLASSIC + +* * HRULE.GETFNCLASSIC + ) ) HRULE.GETFNCLASSIC +  +( IM.CHAP.GETFNMODERN +' HRULE.GETFNMODERN + % $ #!#"&$IM.INDEX.GETFN #   1 &#IM.INDEX.GETFN #  *  # +   C 1  "   " u 1 >          #       X    $ Y D   HRULE.GETFNMODERN +   q   A u >         &#IM.INDEX.GETFN #  2  "  &     & IM.INDEX.GETFN  #'    #  ;        % &!IM.INDEX.GETFN  #        +       T  .  ;   &"IM.INDEX.GETFN  # 2    IM.INDEX.GETFN #     ? 1   & +&IM.INDEX.GETFN #  + 1 & +&IM.INDEX.GETFN  #  6  ! $E $ $    o $  \ & &IM.INDEX.GETFN #' 4  1 +  & 'IM.INDEX.GETFN  #  4 1   HRULE.GETFNMODERN + !  HRULE.GETFNMODERN + !- a  $  y H 1 $z + &$IM.INDEX.GETFN #        ) + #   &$IM.INDEX.GETFN  #    . !  &$IM.INDEX.GETFN # ,      +     =  /   " $&$IM.INDEX.GETFN #( +    & (IM.INDEX.GETFN  6 E   & 'IM.INDEX.GETFN  #&   ;  (  & +&IM.INDEX.GETFN =  &$IM.INDEX.GETFN #   ,#          !C # $ > Y       & (IM.INDEX.GETFN #  & (IM.INDEX.GETFN # (    &#IM.INDEX.GETFN # <     # Q $    HRULE.GETFNMODERN + ! $a % !  " % $*  O      #    $ 8    ? =  P   +  ;   &#IM.INDEX.GETFNCLASSIC + #O  & + ! # &$IM.INDEX.GETFNCLASSIC + # U  #   d !" &!IM.INDEX.GETFNCLASSIC + #   \ h  !      &"IM.INDEX.GETFNCLASSIC + # K !&#IM.INDEX.GETFNCLASSIC +   0 !   -      +   + 2  &$IM.INDEX.GETFNCLASSIC + #   +       & #IM.INDEX.GETFNCLASSIC + #6       O &  #IM.INDEX.GETFNCLASSIC + #        +    "        & #IM.INDEX.GETFNCLASSIC + #   '        4 ' "     /  . )&#IM.INDEX.GETFNCLASSIC + # *  &$IM.INDEX.GETFNCLASSIC + #  !  &#IM.INDEX.GETFNCLASSIC +  #       2    +  K &#IM.INDEX.GETFNCLASSIC + #     #Q   !       & %IM.INDEX.GETFNCLASSIC + # K  "   +4 v  &$IM.INDEX.GETFNCLASSIC + #=   M  5  4  4 #    #      W   # #  HRULE.GETFNMODERN +        +       K  3 6   &"IM.INDEX.GETFNCLASSIC + #  - " #IM.INDEX.GETFNCLASSIC +  # G    "&$IM.INDEX.GETFNCLASSIC + #  & )IM.INDEX.GETFNCLASSIC +  # + "  E  -    #   1   &$IM.INDEX.GETFNCLASSIC + #G     & +'IM.INDEX.GETFNCLASSIC + e  +    & (IM.INDEX.GETFNCLASSIC + #  ! & &IM.INDEX.GETFNCLASSIC + #(  `   ^     - R  #C  #  " u           +   " #!"A   ,   " @ '   "^  q "  #  " " +  + #  "  ," +  + " ] "/  '"  ". & %IM.INDEX.GETFNCLASSIC + + # L  S   { #    #    # F   #uz \ No newline at end of file diff --git a/docs/porter-irm/vol1/03-lists.INTERPRESS b/docs/porter-irm/vol1/03-lists.INTERPRESS new file mode 100644 index 00000000..5ec48e11 Binary files /dev/null and b/docs/porter-irm/vol1/03-lists.INTERPRESS differ diff --git a/docs/porter-irm/vol1/03-lists.tedit b/docs/porter-irm/vol1/03-lists.tedit new file mode 100644 index 00000000..96e40373 --- /dev/null +++ b/docs/porter-irm/vol1/03-lists.tedit @@ -0,0 +1,1189 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 3. LISTS 1 3. LISTS 1 "3"3. LISTS 6 One of the most useful datatypes in Interlisp is the list cell, a data structure which contains pointers to two other objects, known as the CAR and the CDR of the list cell (after the accessing functions). Very complicated structures can be built out of list cells, including lattices and trees, but list cells are most frequently used for representing simple linear lists of objects. The following functions are used to manipulate list cells: (CONS(CONS (Function) NIL NIL (NIL) 1) X Y) [Function] CONS is the primary list construction function. It creates and returns a new list cell containing pointers to X and Y. If Y is a list, this returns a list with X added at the beginning of Y. (LISTP(LISTP (Function) NIL NIL (NIL) 1) X) [Function] Returns X if X is a list cell, e.g., something created by CONS; NIL otherwise. (LISTP NIL) = NIL (NLISTP(NLISTP (Function) NIL NIL (NIL) 1) X) [Function] (NOT (LISTP X)). Returns T if X is not a list cell, NIL otherwise. (NLISTP NIL) = T (CAR(CAR (Function) NIL NIL (NIL) 1) X) [Function] Returns the first element of the list X. CAR of NIL is always NIL. For all other nonlists (e.g., symbols, numbers, strings, arrays), the value returned is controlled by CAR/CDRERR (below). (CDR(CDR (Function) NIL NIL (NIL) 1) X) [Function] Returns all but the first element of the list X. CDR of NIL is always NIL. The value of CDR for other nonlists is controlled by CAR/CDRERR (below). CAR/CDRERR(CAR/CDRERR (Variable) NIL NIL (NIL) 1) [Variable] The variable CAR/CDRERR controls the behavior of CAR and CDR when they are passed non-lists (other than NIL). If CAR/CDRERR=NIL (the current default), then CAR or CDR of a non-list (other than NIL) return the string "{car of non-list}" or "{cdr of non-list}". If CAR/CDRERR=T, then CAR and CDR of a non-list (other than NIL) causes an error. If CAR/CDRERR=ONCE, then CAR or CDR of a string causes an error, but CAR or CDR of anything else returns the string "{car of non-list}" or "{cdr of non-list}" as above. This catches loops which repeatedly take CAR or CDR of an object, but it allows one-time errors to pass undetected. If CAR/CDRERR=CDR, then CAR of a non-list returns "{car of non-list}" as above, but CDR of a non-list causes an error. This setting is based on the observation that nearly all infinite loops involving non-lists occur from taking CDRs, but a fair amount of careless code takes CAR of something it has not tested to be a list. Often, combinations of the CAR and CDR functions are used to extract various components of complex list structures. Functions of the form C...R may be used for some of these combinations: (CAAR X) ==> (CAR (CAR X)) (CADR X) ==> (CAR (CDR X)) (CDDDDR X) ==> (CDR (CDR (CDR (CDR X)))) All 30 combinations of nested CARs and CDRs up to 4 deep are included in the system. (RPLACD(RPLACD (Function) NIL NIL (NIL) 2) X Y) [Function] Replaces the CDR of the list cell X with Y. This physically changes the internal structure of X, as opposed to CONS, which creates a new list cell. It is possible to construct a circular list by using RPLACD to place a pointer to the beginning of a list in a spot at the end of the list. The value of RPLACD is X. An attempt to RPLACD NIL will cause an error, ATTEMPT TO RPLAC NIL (except for (RPLACD NIL NIL)). An attempt to RPLACD any other non-list will cause an error, ARG NOT LIST. (RPLACA(RPLACD (Function) NIL NIL (NIL) 2) X Y) [Function] Similar to RPLACD, but replaces the CAR of X with Y. The value of RPLACA is X. An attempt to RPLACA NIL will cause an error, ATTEMPT TO RPLAC NIL, (except for (RPLACA NIL NIL)). An attempt to RPLACA any other non-list will cause an error, ARG NOT LIST. (RPLNODE(RPLNODE (Function) NIL NIL (NIL) 2) X A D) [Function] Performs (RPLACA X A), (RPLACD X D), and returns X. (RPLNODE2(RPLNODE2 (Function) NIL NIL (NIL) 2) X Y) [Function] Performs (RPLACA X (CAR Y)), (RPLACD X (CDR Y)) and returns X. (FRPLACD(FRPLACD (Function) NIL NIL (NIL) 2) X Y) [Function] (FRPLACA(FRPLACA (Function) NIL NIL (NIL) 2) X Y) [Function] (FRPLNODE(FRPLNODE (Function) NIL NIL (NIL) 2) X A D) [Function] (FRPLNODE2(FRPLNODE2 (Function) NIL NIL (NIL) 2) X Y) [Function] Faster versions of RPLACD, etc. Usually, single list cells are not manipulated in isolation, but in structures known as "lists". By convention, a list is represented by a list cell whose CAR is the first element of the list, and whose CDR is the rest of the list (usually another list cell or the "empty list," NIL). List elements may be any Interlisp objects, including other lists. The input syntax for a list is a sequence of Interlisp data objects (symbols, numbers, other lists, etc.) enclosed in parentheses or brackets. Note that () is read as the symbol 6897 NIL. A right bracket can be used to match all left parenthesis back to the last left bracket, or terminate the lists, e.g. (A (B (C]. If there are two or more elements in a list, the final element can be preceded by a period delimited on both sides, indicating that CDR of the final list cell in the list is to be the element immediately following the period, e.g. (A . B) or (A B C . D), otherwise CDR of the last list cell in a list will be NIL. Note that a list does not have to end in NIL. It is simply a structure composed of one or more list cells. The input sequence (A B C . NIL) is equivalent to (A B C), and (A B . (C D)) is equivalent to (A B C D). Note, however, that (A B . C D) will create a list containing the five symbols A, B, %., C, and D. Lists are printed by printing a left parenthesis, and then printing the first element of the list, a space, the second element, etc., until the final list cell is reached. The individual elements of a list are printed by PRIN1 if the list is being printed by PRIN1, and by PRIN2 if the list is being printed by PRINT or PRIN2. Lists are considered to terminate when CDR of some node is not a list. If CDR of this terminal node is NIL (the usual case), CAR of the terminal node is printed followed by a right parenthesis. If CDR of the terminal node is not NIL, CAR of the terminal node is printed, followed by a space, a period, another space, CDR of the terminal node, and then the right parenthesis. Note that a list input as (A B C . NIL) will print as (A B C), and a list input as (A B . (C D)) will print as (A B C D). Note also that PRINTLEVEL affects the printing of lists (see the PRINTLEVEL section of Chapter 25), and that carriage returns may be inserted where dictated by LINELENGTH (see the Output Functions section of Chapter 25). Note: One must be careful when testing the equality of list structures. EQ will be true only when the two lists are the exact same list. For example, (SETQ A '(1 2)) (1 2) (SETQ B A) (1 2) (EQ A B) T (SETQ C '(1 2)) (1 2) (EQ A C) NIL (EQUAL A C) T In the example above, the values of A and B are the exact same list, so they are EQ. However, the value of C is a totally different list, although it happens to have the same elements. EQUAL should be used to compare the elements of two lists. In general, one should notice whether list manipulation functions use EQ or EQUAL for comparing lists. This is a frequent source of errors. Interlisp provides an extensive set of list manipulation functions, described in the following sections. Creating Lists 1 (MKLIST(MKLIST (Function) NIL NIL (NIL) 3) X) [Function] "Make List." If X is a list or NIL, returns X; Otherwise, returns (LIST X). (LIST(MKLIST (Function) NIL NIL (NIL) 3) X1 X2 ... XN) [NoSpread Function] Returns a list of its arguments, e.g. (LIST 'A 'B '(C D)) => (A B (C D)) (LIST*(LIST* (Function) NIL NIL (NIL) 3) X1 X2 ... XN) [NoSpread Function] Returns a list of its arguments, using the last argument for the tail of the list. This is like an iterated CONS: (LIST* A B C) == (CONS A (CONS B C)). For example, (LIST* 'A 'B 'C) => (A B . C) (LIST* 'A 'B '(C D)) => (A B C D) (APPEND(APPEND (Function) NIL NIL (NIL) 4) X1 X2 ... XN) [NoSpread Function] Copies the top level of the list X1 and appends this to a copy of the top level of the list X2 appended to ... appended to XN, e.g., (APPEND '(A B) '(C D E) '(F G)) => (A B C D E F G) Only the first N-1 lists are copied. However N=1 is treated specially; (APPEND X) copies the top level of a single list. To copy a list to all levels, use COPY. The following examples illustrate the treatment of non-lists: (APPEND '(A B C) 'D) => (A B C . D) (APPEND 'A '(B C D)) => (B C D) (APPEND '(A B C . D) '(E F G)) => (A B C E F G) (APPEND '(A B C . D)) => (A B C . D) (NCONC(NCONC (Function) NIL NIL (NIL) 4) X1 X2 ... XN) [NoSpread Function] Returns the same value as APPEND, but actually modifies the list structure of X1 ... Xn-1. NCONC cannot change NIL to a list: (SETQ FOO NIL) NIL (NCONC FOO '(A B C)) (A B C) FOO NIL Although the value of the NCONC is (A B C), FOO has not been changed. The "problem" is that while it is possible to alter list structure with RPLACA and RPLACD, there is no way to change the non-list NIL to a list. (NCONC1(NCONC1 (Function) NIL NIL (NIL) 4) LST X) [Function] (NCONC LST (LIST X)) (ATTACH(ATTACH (Function) NIL NIL (NIL) 4) X L) [Function] "Attaches" X to the front of L by doing a RPLACA and RPLACD. The value is EQUAL to (CONS X L), but EQ to L, which it physically changes (except if L is NIL). (ATTACH X NIL) is the same as (CONS X NIL). Otherwise, if L is not a list, an error is generated, ARG NOT LIST. Building Lists From Left to Right 1 (TCONC(TCONC (Function) NIL NIL (NIL) 4) PTR X) [Function] TCONC is similar to NCONC1; it is useful for building a list by adding elements one at a time at the end. Unlike NCONC1, TCONC does not have to search to the end of the list each time it is called. Instead, it keeps a pointer to the end of the list being assembled, and updates this pointer after each call. This can be considerably faster for long lists. The cost is an extra list cell, PTR. (CAR PTR) is the list being assembled, (CDR PTR) is (LAST (CAR PTR)). TCONC returns PTR, with its CAR and CDR appropriately modified. PTR can be initialized in two ways. If PTR is NIL, TCONC will create and return a PTR. In this case, the program must set some variable to the value of the first call to TCONC. After that, it is unnecessary to reset the variable, since TCONC physically changes its value. Example: (SETQ FOO (TCONC NIL 1)) ((1) 1) (for I from 2 to 5 do (TCONC FOO I)) NIL FOO ((1 2 3 4 5) 5) If PTR is initially (NIL), the value of TCONC is the same as for PTR=NIL. but TCONC changes PTR. This method allows the program to initialize the TCONC variable before adding any elements to the list. Example: (SETQ FOO (CONS)) (NIL) (for I from 1 to 5 do (TCONC FOO I)) NIL FOO ((1 2 3 4 5) 5) (LCONC(LCONC (Function) NIL NIL (NIL) 5) PTR X) [Function] Where TCONC is used to add elements at the end of a list, LCONC is used for building a list by adding lists at the end, i.e., it is similar to NCONC instead of NCONC1. Example: (SETQ FOO (CONS)) (NIL) (LCONC FOO '(1 2)) ((1 2) 2) (LCONC FOO '(3 4 5)) ((1 2 3 4 5) 5) (LCONC FOO NIL) ((1 2 3 4 5) 5) LCONC uses the same pointer conventions as TCONC for eliminating searching to the end of the list, so that the same pointer can be given to TCONC and LCONC interchangeably. Therefore, continuing from above, (TCONC FOO NIL) ((1 2 3 4 5 NIL) NIL) (TCONC FOO '(3 4 5)) ((1 2 3 4 5 NIL (3 4 5)) (3 4 5)) The functions DOCOLLECT and ENDCOLLECT also permit building up lists from left-to-right like TCONC, but without the overhead of an extra list cell. The list being maintained is kept as a circular list. DOCOLLECT adds items; ENDCOLLECT replaces the tail with its second argument, and returns the full list. (DOCOLLECT(DOCOLLECT (Function) NIL NIL (NIL) 5) ITEM LST) [Function] "Adds" ITEM to the end of LST. Returns the new circular list. Note that LST is modified, but it is not EQ to the new list. The new list should be stored and used as LST to the next call to DOCOLLECT. (ENDCOLLECT(DOCOLLECT (Function) NIL NIL (NIL) 5) LST TAIL) [Function] Takes LST, a list returned by DOCOLLECT, and returns it as a non-circular list, adding TAIL as the terminating CDR. Here is an example using DOCOLLECT and ENDCOLLECT. HPRINT is used to print the results because they are circular lists. Notice that FOO has to be set to the value of DOCOLLECT as each element is added. (SETQ FOO NIL] NIL (HPRINT (SETQ FOO (DOCOLLECT 1 FOO] (1 . {1}) (HPRINT (SETQ FOO (DOCOLLECT 2 FOO] (2 1 . {1}) (HPRINT (SETQ FOO (DOCOLLECT 3 FOO] (3 1 2 . {1}) (HPRINT (SETQ FOO (DOCOLLECT 4 FOO] (4 1 2 3 . {1}) (SETQ FOO (ENDCOLLECT FOO 5] (1 2 3 4 . 5) The following two functions are useful writing programs that wish to reuse a scratch list to collect together some result (both of these compile open): (SCRATCHLIST(SCRATCHLIST (Function) NIL NIL (NIL) 6) LST X1 X2 ... XN) [NLambda NoSpread Function] SCRATCHLIST sets up a context in which the value of LST is used as a "scratch" list. The expressions X1, X2, ... XN are evaluated in turn. During the course of evaluation, any value passed to ADDTOSCRATCHLIST will be saved, reusing CONS cells from the value of LST. If the value of LST is not long enough, new CONS cells will be added onto its end. If the value of LST is NIL, the entire value of SCRATCHLIST will be "new" (i.e., no CONS cells will be reused). (ADDTOSCRATCHLIST(ADDTOSCRATCHLIST (Function) NIL NIL (NIL) 6) VALUE) [Function] For use under calls to SCRATCHLIST. VALUE is added on to the end of the value being collected by SCRATCHLIST. When SCRATCHLIST returns, its value is a list containing all of the things that ADDTOSCRATCHLIST has added. Copying Lists 1 (COPY(COPY (Function) NIL NIL (NIL) 6) X) [Function] Creates and returns a copy of the list X. All levels of X are copied down to non-lists, so that if X contains arrays and strings, the copy of X will contain the same arrays and strings, not copies. COPY is recursive in the CAR direction only, so very long lists can be copied. Note: To copy just the top level of X, do (APPEND X). (COPYALL(COPYALL (Function) NIL NIL (NIL) 6) X) [Function] Like COPY except copies down to atoms. Arrays, hash-arrays, strings, user data types, etc., are all copied. Analagous to EQUALALL (see the Equality Predicates section of Chapter 9). This will not work if given a data structure with circular pointers; in this case, use HCOPYALL. (HCOPYALL(HCOPYALL (Function) NIL NIL (NIL) 6) X) [Function] Similar to COPYALL, except that it will work even if the data structure contains circular pointers. Extracting Tails of Lists 1 (TAILP(TAILP (Function) NIL NIL (NIL) 6) X Y) [Function] Returns X, if X is a tail of the list Y; otherwise NIL. X is a tail of Y if it is EQ to 0 or more CDRs of Y. Note: If X is EQ to 1 or more CDRs of Y, X is called a "proper tail." (NTH(NTH (Function) NIL NIL (NIL) 7) X N) [Function] Returns the tail of X beginning with the Nth element. Returns NIL if X has fewer than N elements. Examples: (NTH '(A B C D) 1) => (A B C D) (NTH '(A B C D) 3) => (C D) (NTH '(A B C D) 9) => NIL (NTH '(A . B) 2) => B For consistency, if N=0, NTH returns (CONS NIL X): (NTH '(A B) 0) => (NIL A B) (FNTH(FNTH (Function) NIL NIL (NIL) 7) X N) [Function] Faster version of NTH that terminates on a null-check. (LAST(LAST (Function) NIL NIL (NIL) 7) X) [Function] Returns the last list cell in the list X. Returns NIL if X is not a list. Examples: (LAST '(A B C)) => (C) (LAST '(A B . C)) => (B . C) (LAST 'A) => NIL (FLAST(FLAST (Function) NIL NIL (NIL) 7) X) [Function] Faster version of LAST that terminates on a null-check. (NLEFT(NLEFT (Function) NIL NIL (NIL) 7) L N TAIL) [Function] NLEFT returns the tail of L that contains N more elements than TAIL. If L does not contain N more elements than TAIL, NLEFT returns NIL. If TAIL is NIL or not a tail of L, NLEFT returns the last N list cells in L. NLEFT can be used to work backwards through a list. Example: (SETQ FOO '(A B C D E)) (A B C D E) (NLEFT FOO 2) (D E) (NLEFT FOO 1 (CDDR FOO)) (B C D E) (NLEFT FOO 3 (CDDR FOO)) NIL (LASTN(LASTN (Function) NIL NIL (NIL) 7) L N) [Function] Returns (CONS X Y), where Y is the last N elements of L, and X is the initial segment, e.g., (LASTN '(A B C D E) 2) => ((A B C) D E) (LASTN '(A B) 2) => (NIL A B) Returns NIL if L is not a list containing at least N elements. Counting List Cells 1 (LENGTH(LENGTH (Function) NIL NIL (NIL) 8) X) [Function] Returns the length of the list X, where "length" is defined as the number of CDRs required to reach a non-list. Examples: (LENGTH '(A B C)) => 3 (LENGTH '(A B C . D)) => 3 (LENGTH 'A) => 0 (FLENGTH(FLENGTH (Function) NIL NIL (NIL) 8) X) [Function] Faster version of LENGTH that terminates on a null-check. (EQLENGTH(EQLENGTH (Function) NIL NIL (NIL) 8) X N) [Function] Equivalent to (EQUAL (LENGTH X) N), but more efficient, because EQLENGTH stops as soon as it knows that X is longer than N. EQLENGTH is safe to use on (possibly) circular lists, since it is "bounded" by N. (COUNT(COUNT (Function) NIL NIL (NIL) 8) X) [Function] Returns the number of list cells in the list X. Thus, COUNT is like a LENGTH that goes to all levels. COUNT of a non-list is 0. Examples: (COUNT '(A)) => 1 (COUNT '(A . B)) => 1 (COUNT '(A (B) C)) => 4 In this last example, the value is 4 because the list (A X C) uses three list cells for any object X, and (B) uses another list cell. (COUNTDOWN(COUNT (Function) NIL NIL (NIL) 8) X N) [Function] Counts the number of list cells in X, decrementing N for each one. Stops and returns N when it finishes counting, or when N reaches 0. COUNTDOWN can be used on circular structures since it is "bounded" by N. Examples: (COUNTDOWN '(A) 100) => 99 (COUNTDOWN '(A . B) 100) => 99 (COUNTDOWN '(A (B) C) 100) => 96 (COUNTDOWN (DOCOLLECT 1 NIL) 100) => 0 (EQUALN(EQUALN (Function) NIL NIL (NIL) 8) X Y DEPTH) [Function] Similar to EQUAL, for use with (possibly) circular structures. Whenever the depth of CAR recursion plus the depth of CDR recursion exceeds DEPTH, EQUALN does not search further along that chain, and returns the symbol ?. If recursion never exceeds DEPTH, EQUALN returns T if the expressions X and Y are EQUAL; otherwise NIL. (EQUALN '(((A)) B) '(((Z)) B) 2) => ? (EQUALN '(((A)) B) '(((Z)) B) 3) => NIL (EQUALN '(((A)) B) '(((A)) B) 3) => T Logical Operations 1 (LDIFFERENCE(LDIFFERENCE (Function) NIL NIL (NIL) 9) X Y) [Function] "List Difference." Returns a list of those elements in X that are not members of Y (using EQUAL to compare elements). Note: If X and Y share no elements, LDIFFERENCE returns a copy of X. (INTERSECTION(INTERSECTION (Function) NIL NIL (NIL) 9) X Y) [Function] Returns a list whose elements are members of both lists X and Y (using EQUAL to compare elements). Note that (INTERSECTION X X) gives a list of all members of X without any duplications. (UNION(INTERSECTION (Function) NIL NIL (NIL) 9) X Y) [Function] Returns a (new) list consisting of all elements included on either of the two original lists (using EQUAL to compare elements). It is more efficient to make X be the shorter list. The value of UNION is Y with all elements of X not in Y CONSed on the front of it. Therefore, if an element appears twice in Y, it will appear twice in (UNION X Y). Since (UNION '(A) '(A A)) = (A A), while (UNION '(A A) '(A)) = (A), UNION is non-commutative. (LDIFF(INTERSECTION (Function) NIL NIL (NIL) 9) LST TAIL ADD) [Function] TAIL must be a tail of LST, i.e., EQ to the result of applying some number of CDRs to LST. (LDIFF LST TAIL) returns a list of all elements in LST up to TAIL. If ADD is not NIL, the value of LDIFF is effectively (NCONC ADD (LDIFF LST TAIL)), i.e., the list difference is added at the end of ADD. If TAIL is not a tail of LST, LDIFF generates an error, LDIFF: NOT A TAIL. LDIFF terminates on a null-check, so it will go into an infinite loop if LST is a circular list and TAIL is not a tail. Example: (SETQ FOO '(A B C D E F)) (A B C D E F) (CDDR FOO) (C D E F) (LDIFF FOO (CDDR FOO)) (A B) (LDIFF FOO (CDDR FOO) '(1 2)) (1 2 A B) (LDIFF FOO '(C D E F)) LDIFF: not a tail (C D E F) Note that the value of LDIFF is always new list structure unless TAIL=NIL, in which case the value is LST itself. Searching Lists 1 (MEMB(MEMB (Function) NIL NIL (NIL) 10) X Y) [Function] Determines if X is a member of the list Y. If there is an element of Y EQ to X, returns the tail of Y starting with that element. Otherwise, returns NIL. Examples: (MEMB 'A '(A (W) C D)) => (A (W) C D) (MEMB 'C '(A (W) C D)) => (C D) (MEMB 'W '(A (W) C D)) => NIL (MEMB '(W) '(A (W) C D)) => NIL (FMEMB(FMEMB (Function) NIL NIL (NIL) 10) X Y) [Function] Faster version of MEMB that terminates on a null-check. (MEMBER(MEMBER (Function) NIL NIL (NIL) 10) X Y) [Function] Identical to MEMB except that it uses EQUAL instead of EQ to check membership of X in Y. Examples: (MEMBER 'C '(A (W) C D)) => (C D) (MEMBER 'W '(A (W) C D)) => NIL (MEMBER '(W) '(A (W) C D)) => ((W) C D) (EQMEMB(EQMEMB (Function) NIL NIL (NIL) 10) X Y) [Function] Returns T if either X is EQ to Y, or else Y is a list and X is an FMEMB of Y. Substitution Functions 1 (SUBST(SUBST (Function) NIL NIL (NIL) 10) NEW OLD EXPR) [Function] Returns the result of substituting NEW for all occurrences of OLD in the expression EXPR. Substitution occurs whenever OLD is EQUAL to CAR of some subexpression of EXPR, or when OLD is atomic and EQ to a non-NIL CDR of some subexpression of EXPR. For example: (SUBST 'A 'B '(C B (X . B))) => (C A (X . A)) (SUBST 'A '(B C) '((B C) D B C)) => (A D B C) not (A D . A) SUBST returns a copy of EXPR with the appropriate changes. Furthermore, if NEW is a list, it is copied at each substitution. (DSUBST(DSUBST (Function) NIL NIL (NIL) 10) NEW OLD EXPR) [Function] Similar to SUBST, except it does not copy EXPR, but changes the list structure EXPR itself. Like SUBST, DSUBST substitutes with a copy of NEW. More efficient than SUBST. (LSUBST(LSUBST (Function) NIL NIL (NIL) 10) NEW OLD EXPR) [Function] Like SUBST except NEW is substituted as a segment of the list EXPR rather than as an element. For instance, (LSUBST '(A B) 'Y '(X Y Z)) => (X A B Z) If NEW is not a list, LSUBST returns a copy of EXPR with all OLD's deleted: (LSUBST NIL 'Y '(X Y Z)) => (X Z) (SUBLIS(SUBLIS (Function) NIL NIL (NIL) 11) ALST EXPR FLG) [Function] ALST is a list of pairs: ((OLD1 . NEW1) (OLD2 . NEW2) ... (OLDN . NEWN)) Each OLDi is an atom. SUBLIS returns the result of substituting each NEWi for the corresponding OLDi in EXPR, e.g., (SUBLIS '((A . X) (C . Y)) '(A B C D)) => (X B Y D) If FLG=NIL, new structure is created only if needed, so if there are no substitutions, the value is EQ to EXPR. If FLG=T, the value is always a copy of EXPR. (DSUBLIS(DSUBLIS (Function) NIL NIL (NIL) 11) ALST EXPR FLG) [Function] Similar to SUBLIS, except it changes the list structure EXPR itself instead of copying it. (SUBPAIR(SUBPAIR (Function) NIL NIL (NIL) 11) OLD NEW EXPR FLG) [Function] Similar to SUBLIS, except that elements of NEW are substituted for corresponding atoms of OLD in EXPR, e.g., (SUBPAIR '(A C) '(X Y) '(A B C D)) => (X B Y D) As with SUBLIS, new structure is created only if needed, or if FLG=T, e.g., if FLG=NIL and there are no substitutions, the value is EQ to EXPR. If OLD ends in an atom other than NIL, the rest of the elements on NEW are substituted for that atom. For example, if OLD=(A B . C) and NEW=(U V X Y Z), U is substituted for A, V for B, and (X Y Z) for C. Similarly, if OLD itself is an atom (other than NIL), the entire list NEW is substituted for it. Examples: (SUBPAIR '(A B . C) '(W X Y Z) '(C A B B Y)) => ((Y Z) W X X Y) SUBST, DSUBST, and LSUBST all substitute copies of the appropriate expression, whereas SUBLIS, and DSUBLIS, and SUBPAIR substitute the identical structure (unless FLG=T). For example: (SETQ FOO '(A B)) (A B) (SETQ BAR '(X Y Z)) (X Y Z) (DSUBLIS (LIST (CONS 'X FOO)) BAR) ((A B) Y Z) (DSUBLIS (LIST (CONS 'Y FOO)) BAR T) ((A B) (A B) Z) (EQ (CAR BAR) FOO) T (EQ (CADR BAR) FOO) NIL Association Lists and Property Lists 1 It is often useful to associate a set of property names (NAME1, NAME2, etc.), with a set of property values (VALUE1, VALUE2, etc.). Two list structures commonly used to store such associations are called "property lists" and "association lists." A list in "association list" format is a list where each element is a dotted pair whose CAR is a property name, and whose CDR is the value: ( (NAME1 . VALUE1) (NAME2 . VALUE2) ...) A list in "property list" format is a list where the first, third, etc. elements are the property names, and the second, forth, etc. elements are the associated values: ( NAME1 VALUE1 NAME2 VALUE2 ...) The functions below provide facilities for searching and changing lists in property list or association list format. Note: Property lists are contained within many Interlisp-D system datatypes. There are special functions that can be used to set and retrieve values from the property lists of symbols (see the Property Lists section of Chapter 2), from properties of windows (see the Window Properties section of Chapter 28), etc. Note: Another data structure that offers some of the advantages of association lists and property lists is the hash array data type (see the first page of Chapter 6). (ASSOC(ASSOC (Function) NIL NIL (NIL) 12) KEY ALST) [Function] ALST is a list of lists. ASSOC returns the first sublist of ALST whose CAR is EQ to KEY. If such a list is not found, ASSOC returns NIL. Example: (ASSOC 'B '((A . 1) (B . 2) (C . 3))) => (B . 2) (FASSOC(FASSOC (Function) NIL NIL (NIL) 12) KEY ALST) [Function] Faster version of ASSOC that terminates on a null-check. (SASSOC(SASSOC (Function) NIL NIL (NIL) 12) KEY ALST) [Function] Same as ASSOC but uses EQUAL instead of EQ when searching for KEY. (PUTASSOC(PUTASSOC (Function) NIL NIL (NIL) 12) KEY VAL ALST) [Function] Searches ALST for a sublist CAR of which is EQ to KEY. If one is found, the CDR is replaced (using RPLACD) with VAL. If no such sublist is found, (CONS KEY VAL) is added at the end of ALST. Returns VAL. If ALST is not a list, generates an error, ARG NOT LIST. The argument order for ASSOC, PUTASSOC, etc. is different from that of LISTGET, LISTPUT, etc. (LISTGET(LISTGET (Function) NIL NIL (NIL) 12) LST PROP) [Function] Searches LST two elements at a time, by CDDR, looking for an element EQ to PROP. If one is found, returns the next element of LST, otherwise NIL. Returns NIL if LST is not a list. Example: (LISTGET '(A 1 B 2 C 3) 'B) => 2 (LISTGET '(A 1 B 2 C 3) 'W) => NIL (LISTPUT(LISTGET (Function) NIL NIL (NIL) 12) LST PROP VAL) [Function] Searches LST two elements at a time, by CDDR, looking for an element EQ to PROP. If PROP is found, replaces the next element of LST with VAL. Otherwise, PROP and VAL are added to the end of LST. If LST is a list with an odd number of elements, or ends in a non-list other than NIL, PROP and VAL are added at its beginning. Returns VAL. If LST is not a list, generates an error, ARG NOT LIST. (LISTGET1(LISTGET1 (Function) NIL NIL (NIL) 13) LST PROP) [Function] Like LISTGET, but searches LST one CDR at a time, i.e., looks at each element. Returns the next element after PROP. Examples: (LISTGET1 '(A 1 B 2 C 3) 'B) => 2 (LISTGET1 '(A 1 B 2 C 3) '1) => B (LISTGET1 '(A 1 B 2 C 3) 'W) => NIL (LISTPUT1(LISTPUT1 (Function) NIL NIL (NIL) 13) LST PROP VAL) [Function] Like LISTPUT, except searches LST one CDR at a time. Returns the modified LST. Example: (SETQ FOO '(A 1 B 2)) (A 1 B 2) (LISTPUT1 FOO 'B 3) (A 1 B 3) (LISTPUT1 FOO 'C 4) (A 1 B 3 C 4) (LISTPUT1 FOO 1 'W) (A 1 W 3 C 4) FOO (A 1 W 3 C 4) If LST is not a list, no error is generated. However, since a non-list cannot be changed into a list, LST is not modified. In this case, the value of LISTPUT1 should be saved. Example: (SETQ FOO NIL) NIL (LISTPUT1 FOO 'A 5) (A 5) FOO NIL Sorting Lists 1 (SORT(SORT (Function) NIL NIL (NIL) 13) DATA COMPAREFN) [Function] DATA is a list of items to be sorted using COMPAREFN, a predicate function of two arguments which can compare any two items on DATA and return T if the first one belongs before the second. If COMPAREFN is NIL, ALPHORDER is used; thus (SORT DATA) will alphabetize a list. If COMPAREFN is T, CAR's of items that are lists are given to ALPHORDER, otherwise the items themselves; thus (SORT A-LIST T) will alphabetize an assoc list by the CAR of each item. (SORT X 'ILESSP) will sort a list of integers. The value of SORT is the sorted list. The sort is destructive and uses no extra storage. The value returned is EQ to DATA but elements have been switched around. Interrupting with Control D, E, or B may cause loss of data, but Control H may be used at any time, and SORT will break at a clean state from which ^ or control characters are safe. The algorithm used by SORT is such that the maximum number of compares is N*log2N, where N is (LENGTH DATA). Note: If (COMPAREFN A B) = (COMPAREFN B A), then the ordering of A and B may or may not be preserved. For example, if (FOO . FIE) appears before (FOO . FUM) in X, (SORT X T) may or may not reverse the order of these two elements. (MERGE(MERGE (Function) NIL NIL (NIL) 14) A B COMPAREFN) [Function] A and B are lists which have previously been sorted using SORT and COMPAREFN. Value is a destructive merging of the two lists. It does not matter which list is longer. After merging both A and B are equal to the merged list. (In fact, (CDR A) is EQ to (CDR B)). (ALPHORDER(ALPHORDER (Function) NIL NIL (NIL) 14) A B CASEARRAY) [Function] A predicate function of two arguments, for alphabetizing. Returns a non-NIL value if its arguments are in lexicographic order, i.e., if B does not belong before A. Numbers come before literal atoms, and are ordered by magnitude (using GREATERP). Literal atoms and strings are ordered by comparing the character codes in their print names. Thus (ALPHORDER 23 123) is T, whereas (ALPHORDER 'A23 'A123) is NIL, because the character code for the digit 2 is greater than the code for 1. Atoms and strings are ordered before all other data types. If neither A nor B are atoms or strings, the value of ALPHORDER is T, i.e., in order. If CASEARRAY is non-NIL, it is a casearray (see the Random Access File Operations section of Chapter 25) that the characters of A and B are translated through before being compared. Numbers are not passed through CASEARRAY. Note: If either A or B is a number, the value returned in the "true" case is T. Otherwise, ALPHORDER returns either EQUAL or LESSP to discriminate the cases of A and B being equal or unequal strings/atoms. Note: ALPHORDER does no UNPACKs, CHCONs, CONSes or NTHCHARs. It is several times faster for alphabetizing than anything that can be written using these other functions. (UALPHORDER(UALPHORDER (Function) NIL NIL (NIL) 14) A B) [Function] Defined as (ALPHORDER A B UPPERCASEARRAY). UPPERCASEARRAY (see the Random Access File Operations section of Chapter 25) is a casearray that maps every lowercase character into the corresponding uppercase character. (MERGEINSERT(MERGEINSERT (Function) NIL NIL (NIL) 14) NEW LST ONEFLG) [Function] LST is NIL or a list of partially sorted items. MERGEINSERT tries to find the "best" place to (destructively) insert NEW, e.g., (MERGEINSERT 'FIE2 '(FOO FOO1 FIE FUM)) => (FOO FOO1 FIE FIE2 FUM) Returns LST. MERGEINSERT is undoable. If ONEFLG=T and NEW is already a member of LST, MERGEINSERT does nothing and returns LST. MERGEINSERT is used by ADDTOFILE (see the Functions for Manipulating File Command Lists section of Chapter 17) to insert the name of a new function into a list of functions. The algorithm is essentially to look for the item with the longest common leading sequence of characters with respect to NEW, and then merge NEW in starting at that point. Other List Functions 1 (REMOVE(REMOVE (Function) NIL NIL (NIL) 15) X L) [Function] Removes all top-level occurrences of X from list L, returning a copy of L with all elements EQUAL to X removed. Example: (REMOVE 'A '(A B C (A) A)) => (B C (A)) (REMOVE '(A) '(A B C (A) A)) => (A B C A) (DREMOVE(DREMOVE (Function) NIL NIL (NIL) 15) X L) [Function] Similar to REMOVE, but uses EQ instead of EQUAL, and actually modifies the list L when removing X, and thus does not use any additional storage. More efficient than REMOVE. DREMOVE cannot change a list to NIL: (SETQ FOO '(A)) (A) (DREMOVE 'A FOO) NIL FOO (A) The DREMOVE above returns NIL, and does not perform any CONSes, but the value of FOO is still (A), because there is no way to change a list to a non-list. See NCONC. (REVERSE(REVERSE (Function) NIL NIL (NIL) 15) L) [Function] Reverses (and copies) the top level of a list, e.g., (REVERSE '(A B (C D))) => ((C D) B A) If L is not a list, REVERSE just returns L. (DREVERSE(DREVERSE (Function) NIL NIL (NIL) 15) L) [Function] Value is the same as that of REVERSE, but DREVERSE destroys the original list L and thus does not use any additional storage. More efficient than REVERSE. (COMPARELISTS(COMPARELISTS (Function) NIL NIL (NIL) 15) X Y) [Function] Compares the list structures X and Y and prints a description of any differences to the terminal. If X and Y are EQUAL lists, COMPARELISTS simply prints out SAME. Returns NIL. COMPARELISTS prints a terse description of the differences between the two list structures, highlighting the items that have changed. This printout is not a complete and perfect comparison. If X and Y are radically different list structures, the printout will not be very useful. COMPARELISTS is meant to be used as a tool to help users isolate differences between similar structures. When a single element has been changed for another, COMPARELISTS prints out items such as (A -> B), for example: (COMPARELISTS '(A B C D) '(X B E D)) (A -> X) (C -> E) NIL When there are more complex differences between the two lists, COMPARELISTS prints X and Y, highlighting differences and abbreviating similar elements as much as possible. "&" is used to signal a single element that is present in the same place in the two lists; "--" signals an arbitrary number of elements in one list but not in the other; "-2-," "-3-", etc. signal a sequence of two, three, etc. elements that are the same in both lists. Examples: (COMPARELISTS '(A B C D) '(A D)) (A B C --) (A D) (COMPARELISTS '(A B C D E F G H) '(A B C D X)) (A -3- E F --) (A -3- X) (COMPARELISTS '(A B C (D E F (G) H) I) '(A B (G) C (D E F H) I)) (A & & (D -2- (G) &) &) (A & (G) & (D -2- &) &) (NEGATE(NEGATE (Function) NIL NIL (NIL) 16) X) [Function] For a form X, returns a form which computes the negation of X . For example: (NEGATE '(MEMBER X Y)) => (NOT (MEMBER X Y)) (NEGATE '(EQ X Y)) => (NEQ X Y) (NEGATE '(AND X (NLISTP X))) => (OR (NULL X) (LISTP X)) (NEGATE NIL) => T (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "3-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "3-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "3-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "3-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "3-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "3-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))2l-llT2Hll,ll,ll,,ll,ll,,,-T3(T,,,3HZT,ll,HH +,,ll,HH,HH3HZ +T,2F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR, TITAN +CLASSIC +TITAN +TITAN + HELVETICACLASSIC +CLASSIC +CLASSIC + HELVETICA +CLASSIC +MODERNMODERN + HRULE.GETFNCLASSIC + +  + HRULE.GETFNCLASSIC + +   + HRULE.GETFNCLASSIC + +  + HRULE.GETFNCLASSIC + +  IM.CHAP.GETFNMODERN +  HRULE.GETFNMODERN +  + + +; +!IM.INDEX.GETFNCLASSIC + +k + + +% + + +"IM.INDEX.GETFNCLASSIC + + + +, + + +#IM.INDEX.GETFNCLASSIC + + + + + + IM.INDEX.GETFNCLASSIC + +& + + + +i + + + + IM.INDEX.GETFNCLASSIC + +. + + + + +% + + + + +'IM.INDEX.GETFNCLASSIC + + + + + +, + + + + + + + + + + + + + + + + + + + + + +" + +% + +5 + +A + + + + + + + +, +. + + +e +- +) + ++ +#IM.INDEX.GETFNCLASSIC + + + + +5 + +W +Q + + + + + + + +) +  +#IM.INDEX.GETFNCLASSIC + + + + + + + + + + + + +) +  +$IM.INDEX.GETFNCLASSIC + + + + + +  %IM.INDEX.GETFNCLASSIC + + + + + +$IM.INDEX.GETFNCLASSIC + +$IM.INDEX.GETFNCLASSIC + + %IM.INDEX.GETFNCLASSIC + + +&IM.INDEX.GETFNCLASSIC + + + + +- +I +G + + +y +  + +` + +  +) +, +T + + + + + + + +  + + +0 + + + + + + +! + +! + +* +! + + +E + + + +P +R + + + + + + + +  + + + +3 +K +. + + + +  + + + +  + $ + +& + +N +} + +< +i +   HRULE.GETFNMODERN + #IM.INDEX.GETFNCLASSIC + + + + + + + +#IM.INDEX.GETFNCLASSIC + +& +%"IM.INDEX.GETFNCLASSIC + +m ++ + $#IM.INDEX.GETFNCLASSIC + +! +9 + + +5 + + + +K + +> +&"2& +"IM.INDEX.GETFNCLASSIC + + +. + + + + + + + + + + + +X + +) + +#IM.INDEX.GETFNCLASSIC + + +#IM.INDEX.GETFNCLASSIC + + + + + + + + + +) + + + +  +' +  +  "  HRULE.GETFNMODERN + "IM.INDEX.GETFNCLASSIC + + +X + + + + + + + + + + + +% + + + +V +> +) +  + + + +% + + + + + + + + + + +4 +< +  + + + +% + + + + +"IM.INDEX.GETFNCLASSIC + + + + +' +$ + + +  + + + + + + + + + + + +& +[ + +4 +  + + + + +! + +  + +7 +j +  + +H + +&IM.INDEX.GETFNCLASSIC + + + +- + += + +  + &IM.INDEX.GETFNCLASSIC + + + + 0 + + + +  + + +L + +  + + +$ + + +$ +  +$ + +$ + + + (IM.INDEX.GETFNCLASSIC + + ) +/ + + +N + + + + +4 + + +  + + -IM.INDEX.GETFNCLASSIC + + +  +8 +  + @ + +   HRULE.GETFNMODERN + !IM.INDEX.GETFNCLASSIC + +' + +* +* +8 + +3 + +  + + +$IM.INDEX.GETFNCLASSIC + + +r + + + %IM.INDEX.GETFNCLASSIC + + +Q +    HRULE.GETFNMODERN + "IM.INDEX.GETFNCLASSIC + + + + + + + + + + + + + + + + + + + + + IM.INDEX.GETFNCLASSIC + + + + + + + +" + + +  + !IM.INDEX.GETFNCLASSIC + + +" +!IM.INDEX.GETFNCLASSIC + +' + + + + "IM.INDEX.GETFNCLASSIC + + +" +"IM.INDEX.GETFNCLASSIC + + + + + + + + + + + + + + + + +9 + +  + + + + "IM.INDEX.GETFNCLASSIC + + + + + + + + +*  + +" + + +    HRULE.GETFNMODERN + #IM.INDEX.GETFNCLASSIC + + +- ++ + +$IM.INDEX.GETFNCLASSIC + + +! +  %IM.INDEX.GETFNCLASSIC + + + + + + +G + +"IM.INDEX.GETFNCLASSIC + +- + + + + +6 +& + + + +"IM.INDEX.GETFNCLASSIC + +# + +" +$ + + = + +!#( #IM.INDEX.GETFNCLASSIC +  + +F + + + +B + + + + + + + + +(*'    HRULE.GETFNMODERN +  (IM.INDEX.GETFNCLASSIC + +8 + + + + + + + +  + + )IM.INDEX.GETFNCLASSIC + +8 + + + + + + + +)IM.INDEX.GETFNCLASSIC + +d +5 + + + + + + +B + + + + + +)IM.INDEX.GETFNCLASSIC +  + + +* + + +# + + + + + + +3 + + + + + + +D + + + + + +  + + + + + + +% + + + +   HRULE.GETFNMODERN + "IM.INDEX.GETFNCLASSIC + + + + + + + +1 + +(" ! #IM.INDEX.GETFNCLASSIC + + +" +$IM.INDEX.GETFNCLASSIC + + + + + + + +$") $IM.INDEX.GETFNCLASSIC + + + + + + + + + + + +    HRULE.GETFNMODERN + #IM.INDEX.GETFNCLASSIC +  +# + + + + + + + + + + + + + + +0!  +0 +/ +$IM.INDEX.GETFNCLASSIC +  + + +! + + + + + +$IM.INDEX.GETFNCLASSIC +  + + +) ++ ++ + + + + + +# $IM.INDEX.GETFNCLASSIC +  + + + +) + + + +6 + +Z + + + + + +%IM.INDEX.GETFNCLASSIC +  + +' + +%IM.INDEX.GETFNCLASSIC + + + +, + + +2 +1 + + + +. + + + + + +1 + +  + + + + + + + + + + +# +@ + +> + + +, + + + + + +$  +& + + %  HRULE.GETFNMODERN + 9 + +( + + + + + +w += + +#IM.INDEX.GETFNCLASSIC + + + + + + + + + +3$IM.INDEX.GETFNCLASSIC + + +" +$IM.INDEX.GETFNCLASSIC + + + + + + + + &IM.INDEX.GETFNCLASSIC +  + + + + + + + + + + + +$ +  + + +! + + +%IM.INDEX.GETFNCLASSIC + + + + + +0 + + + + +#%%IM.INDEX.GETFNCLASSIC +  + + + + + +( + + + + + +L + + +& + +$ +  + &IM.INDEX.GETFNCLASSIC + + + + +I + +$$& &IM.INDEX.GETFNCLASSIC +  + + + +" + +  + + + + + + + + + + + + + + + + +a +. + +  + + + + + + + +    HRULE.GETFNMODERN + "IM.INDEX.GETFNCLASSIC + +' + K + +1 +  + +  + +  + +( + ' +' + + + +` + + +a +0 + + + + + +  +  + + + +  +  + + +; +#IM.INDEX.GETFNCLASSIC +  + +2 + + p + +) + + + + + +'IM.INDEX.GETFNCLASSIC +  +I += + +J +g + + + + +M +F + +$ +  + + +  +i + +O +  + + +7 + +  + + + +' + + + + + + +p + (IM.INDEX.GETFNCLASSIC + + + + + + )IM.INDEX.GETFNCLASSIC + + +% + : + +(# + +  + + + + + +  + +  +  + + +   HRULE.GETFNMODERN + $IM.INDEX.GETFNCLASSIC + +% + + + + + +* + +%IM.INDEX.GETFNCLASSIC + + + + +! + +E + + + + + + + + + + + + + +? + +%IM.INDEX.GETFNCLASSIC + +5 +( + + + + &IM.INDEX.GETFNCLASSIC + + + + +D + + *IM.INDEX.GETFNCLASSIC + + + +B + + + +  + + +  + +Q + ] +4 +  + + +%? +  + +T +Z +M + +c +!  +/ + +A $IM.INDEX.GETFNCLASSIC + + +0 + +- 8  z \ No newline at end of file diff --git a/docs/porter-irm/vol1/04-STRINGS.INTERPRESS b/docs/porter-irm/vol1/04-STRINGS.INTERPRESS new file mode 100644 index 00000000..e990b4be Binary files /dev/null and b/docs/porter-irm/vol1/04-STRINGS.INTERPRESS differ diff --git a/docs/porter-irm/vol1/04-STRINGS.TEDIT b/docs/porter-irm/vol1/04-STRINGS.TEDIT new file mode 100644 index 00000000..40952c5a --- /dev/null +++ b/docs/porter-irm/vol1/04-STRINGS.TEDIT @@ -0,0 +1,51 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 4. STRINGS 1 4. STRINGS 1 "4"4. STRINGS 6 A string is an object which represents a sequence of characters. Interlisp provides functions for creating strings, concatenating strings, and creating sub-strings of a string. The input syntax for a string is a double quote ("), followed by a sequence of any characters except double quote and %, terminated by a double quote. The % and double quote characters may be included in a string by preceding them with the character %. Strings are printed by PRINT and PRIN2 with initial and final double quotes, and %s inserted where necessary for it to read back in properly. Strings are printed by PRIN1 without the delimiting double quotes and extra %s. A "null string" containing no characters is input as "". The null string is printed by PRINT and PRIN2 as "". (PRIN1 "") doesn't print anything. Internally a string is stored in two parts; a "string pointer" and the sequence of characters. Several string pointers may reference the same character sequence, so a substring can be made by creating a new string pointer, without copying any characters. Functions that refer to "strings" actually manipulate string pointers. Some functions take an "old string" argument, and re-use the string pointer. (STRINGP X) [Function] Returns X if X is a string, NIL otherwise. (STREQUAL X Y) [Function] Returns T if X and Y are both strings and they contain the same sequence of characters, otherwise NIL. EQUAL uses STREQUAL. Note that strings may be STREQUAL without being EQ. For instance, (STREQUAL "ABC" "ABC") => T (EQ "ABC" "ABC") => NIL STREQUAL returns T if X and Y are the same string pointer, or two different string pointers which point to the same character sequence, or two string pointers which point to different character sequences which contain the same characters. Only in the first case would X and Y be EQ. (STRING-EQUAL X Y) [Function] Returns T if X and Y are either strings or symbols, and they contain the same sequence of characters, ignoring case. For instance, (STRING-EQUAL "FOO" "Foo") => T (STRING-EQUAL "FOO" 'Foo) => T This is useful for comparing things that might want to be considered "equal" even though they're not both symbols in a consistent case, such as file names and user names. (ALLOCSTRING N INITCHAR OLD FATFLG) [Function] Creates a string of length N characters of INITCHAR (which can be either a character code or something coercible to a character). If INITCHAR is NIL, it defaults to character code 0. if OLD is supplied, it must be a string pointer, which is modified and returned. If FATFLG is non-NIL, the string is allocated using full 16-bit NS characters (see Chapter 2) instead of 8-bit characters. This can speed up some string operations if NS characters are later inserted into the string. This has no other effect on the operation of the string functions. (MKSTRING X FLG RDTBL) [Function] If X is a string, returns X. Otherwise, creates and returns a string containing the print name of X. Examples: (MKSTRING "ABC") => "ABC" (MKSTRING '(A B C)) => "(A B C)" (MKSTRING NIL) => "NIL" Note that the last example returns the string "NIL", not the atom NIL. If FLG is T, then the PRIN2-name of X is used, computed with respect to the readtable RDTBL. For example, (MKSTRING "ABC" T) => "%"ABC%"" (NCHARS X FLG RDTBL) [Function] Returns the number of characters in the print name of X. If FLG=T, the PRIN2-name is used. For example, (NCHARS 'ABC) => 3 (NCHARS "ABC" T) => 5 Note: NCHARS works most efficiently on symbols and strings, but can be given any object. (SUBSTRING(SUBSTRING (Function) NIL NIL (NIL) 2) X N M OLDPTR) [Function] Returns the substring of X consisting of the Nth through Mth characters of X. If M is NIL, the substring contains the Nth character thru the end of X. N and M can be negative numbers, which are interpreted as counts back from the end of the string, as with NTHCHAR (Chapter 2). SUBSTRING returns NIL if the substring is not well defined, e.g., N or M specify character positions outside of X, or N corresponds to a character in X to the right of the character indicated by M). Examples: (SUBSTRING "ABCDEFG" 4 6) => "DEF" (SUBSTRING "ABCDEFG" 3 3) => "C" (SUBSTRING "ABCDEFG" 3 NIL) => "CDEFG" (SUBSTRING "ABCDEFG" 4 -2) => "DEF" (SUBSTRING "ABCDEFG" 6 4) => NIL (SUBSTRING "ABCDEFG" 4 9) => NIL If X is not a string, it is converted to one. For example, (SUBSTRING '(A B C) 4 6) => "B C" SUBSTRING does not actually copy any characters, but simply creates a new string pointer to the characters in X. If OLDPTR is a string pointer, it is modified and returned. (GNC(GNC (Function) NIL NIL (NIL) 2) X) [Function] "Get Next Character." Returns the next character of the string X (as an atom); also removes the character from the string, by changing the string pointer. Returns NIL if X is the null string. If X isn't a string, a string is made. Used for sequential access to characters of a string. Example: (SETQ FOO "ABCDEFG") "ABCDEFG" (GNC FOO) A (GNC FOO) B FOO "CDEFG" Note that if A is a substring of B, (GNC A) does not remove the character from B. (GLC(GLC (Function) NIL NIL (NIL) 3) X) [Function] "Get Last Character." Returns the last character of the string X (as an atom); also removes the character from the string. Similar to GNC. Example: (SETQ FOO "ABCDEFG") "ABCDEFG" (GLC FOO) G (GLC FOO) F FOO "ABCDE" (CONCAT(CONCAT (Function) NIL NIL (NIL) 3) X1 X2 ... XN) [NoSpread Function] Returns a new string which is the concatenation of (copies of) its arguments. Any arguments which are not strings are transformed to strings. Examples: (CONCAT "ABC" 'DEF "GHI") => "ABCDEFGHI" (CONCAT '(A B C) "ABC") => "(A B C)ABC" (CONCAT) returns the null string, "" (CONCATLIST(CONCATLIST (Function) NIL NIL (NIL) 3) L) [Function] L is a list of strings and/or other objects. The objects are transformed to strings if they aren't strings. Returns a new string which is the concatenation of the strings. Example: (CONCATLIST '(A B (C D) "EF")) => "AB(C D)EF" (RPLSTRING(RPLSTRING (Function) NIL NIL (NIL) 3) X N Y) [Function] Replaces the characters of string X beginning at character position N with string Y. X and Y are converted to strings if they aren't already. N may be positive or negative, as with SUBSTRING. Characters are smashed into (converted) X. Returns the string X. Examples: (RPLSTRING "ABCDEF" -3 "END") => "ABCEND" (RPLSTRING "ABCDEFGHIJK" 4 '(A B C)) => "ABC(A B C)K" Generates an error if there is not enough room in X for Y, i.e., the new string would be longer than the original. If Y was not a string, X will already have been modified since RPLSTRING does not know whether Y will "fit" without actually attempting the transfer. Warning: In some implementations of Interlisp, if X is a substring of Z, Z will also be modified by the action of RPLSTRING or RPLCHARCODE. However, this is not guaranteed to be true in all cases, so programmers should not rely on RPLSTRING or RPLCHARCODE altering the characters of any string other than the one directly passed as argument to those functions. (RPLCHARCODE(RPLCHARCODE (Function) NIL NIL (NIL) 4) X N CHAR) [Function] Replaces the Nth character of the string X with the character code CHAR. N may be positive or negative. Returns the new X. Similar to RPLSTRING. Example: (RPLCHARCODE "ABCDE" 3 (CHARCODE F)) => "ABFDE" (STRPOS(STRPOS (Function) NIL NIL (NIL) 4) PAT STRING START SKIP ANCHOR TAIL CASEARRAY BACKWARDSFLG) [Function] STRPOS is a function for searching one string looking for another. PAT and STRING are both strings (or else they are converted automatically). STRPOS searches STRING beginning at character number START, (or 1 if START is NIL) and looks for a sequence of characters equal to PAT. If a match is found, the character position of the first matching character in STRING is returned, otherwise NIL. Examples: (STRPOS "ABC" "XYZABCDEF") => 4 (STRPOS "ABC" "XYZABCDEF" 5) => NIL (STRPOS "ABC" "XYZABCDEFABC" 5) => 10 SKIP can be used to specify a character in PAT that matches any character in STRING. Examples: (STRPOS "A&C&" "XYZABCDEF" NIL '&) => 4 (STRPOS "DEF&" "XYZABCDEF" NIL '&) => NIL If ANCHOR is T, STRPOS compares PAT with the characters beginning at position START (or 1 if START is NIL). If that comparison fails, STRPOS returns NIL without searching any further down STRING. Thus it can be used to compare one string with some portion of another string. Examples: (STRPOS "ABC" "XYZABCDEF" NIL NIL T) => NIL (STRPOS "ABC" "XYZABCDEF" 4 NIL T) => 4 If TAIL is T, the value returned by STRPOS if successful is not the starting position of the sequence of characters corresponding to PAT, but the position of the first character after that, i.e., the starting position plus (NCHARS PAT). Examples: (STRPOS "ABC" "XYZABCDEFABC" NIL NIL NIL T) => 7 (STRPOS "A" "A" NIL NIL NIL T) => 2 If TAIL=NIL, STRPOS returns NIL, or a character position within STRING which can be passed to SUBSTRING. In particular, (STRPOS "" "") => NIL. However, if TAIL=T, STRPOS may return a character position outside of STRING. For instance, note that the second example above returns 2, even though "A" has only one character. If CASEARRAY is non-NIL, this should be a casearray like that given to FILEPOS (Chapter 25). The casearray is used to map the string characters before comparing them to the search string. If BACKWARDSFLG is non-NIL, the search is done backwards from the end of the string. (STRPOSL(STRPOSL (Function) NIL NIL (NIL) 4) A STRING START NEG BACKWARDSFLG) [Function] STRING is a string (or else it is converted automatically to a string), A is a list of characters or character codes. STRPOSL searches STRING beginning at character number START (or else 1 if START=NIL) for one of the characters in A. If one is found, STRPOSL returns as its value the corresponding character position, otherwise NIL. Example: (STRPOSL '(A B C) "XYZBCD") => 4 If NEG=T, STRPOSL searches for a character not on A. Example: (STRPOSL '(A B C) "ABCDEF" NIL T) => 4 If any element of A is a number, it is assumed to be a character code. Otherwise, it is converted to a character code via CHCON1. Therefore, it is more efficient to call STRPOSL with A a list of character codes. If A is a bit table, it is used to specify the characters (see MAKEBITTABLE below) If BACKWARDSFLG is non-NIL, the search is done backwards from the end of the string. STRPOSL uses a "bit table" data structure to search efficiently. If A is not a bit table, it is converted to a bit table using MAKEBITTABLE. If STRPOSL is to be called frequently with the same list of characters, a considerable savings can be achieved by converting the list to a bit table once, and then passing the bit table to STRPOSL as its first argument. (MAKEBITTABLE(MAKEBITTABLE (Function) NIL NIL (NIL) 5) L NEG A) [Function] Returns a bit table suitable for use by STRPOSL. L is a list of characters or character codes, NEG is the same as described for STRPOSL. If A is a bit table, MAKEBITTABLE modifies and returns it. Otherwise, it will create a new bit table. Note: If NEG=T, STRPOSL must call MAKEBITTABLE whether A is a list or a bit table. To obtain bit table efficiency with NEG=T, MAKEBITTABLE should be called with NEG=T, and the resulting "inverted" bit table should be given to STRPOSL with NEG=NIL. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "4-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "4-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "4-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "4-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "4-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "4-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))2l3H` +T-T222l,,,,3H` +T,ll3H +T,HH,HH,2F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR CLASSIC +CLASSIC +CLASSIC +TITAN + HELVETICA +CLASSIC +MODERN +MODERNMODERN + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC +  HRULE.GETFNCLASSIC +  HRULE.GETFNCLASSIC + IM.CHAP.GETFNMODERN  HRULE.GETFNMODERN + 1D%^+T05!   +      N     + p"!    S'K  +    H  +# .   1"    6 +M +&IM.INDEX.GETFN  +   c  -(,%#)&## 8$ e3  IM.INDEX.GETFN +  @dd +  +   +    $  IM.INDEX.GETFN  @G  + +  +   +    #IM.INDEX.GETFN   ++*    'IM.INDEX.GETFN   +0 +&IM.INDEX.GETFN   "! 3& +  +,8 2>' 6 3(  ^  j (IM.INDEX.GETFN  /   +2 #IM.INDEX.GETFNMODERN +8  >? + 2R  +"&( '  +*,  ++ + $7 +.* [W  +3&  ! ,K  0o  3  $IM.INDEX.GETFNMODERN +  B. +F  +#   +) h+ ;   ; >: $ )IM.INDEX.GETFNMODERN +  (- F +    3 <-z \ No newline at end of file diff --git a/docs/porter-irm/vol1/05-ARRAY.INTERPRESS b/docs/porter-irm/vol1/05-ARRAY.INTERPRESS new file mode 100644 index 00000000..12a5cae3 Binary files /dev/null and b/docs/porter-irm/vol1/05-ARRAY.INTERPRESS differ diff --git a/docs/porter-irm/vol1/05-ARRAY.TEDIT b/docs/porter-irm/vol1/05-ARRAY.TEDIT new file mode 100644 index 00000000..45b4b3d9 Binary files /dev/null and b/docs/porter-irm/vol1/05-ARRAY.TEDIT differ diff --git a/docs/porter-irm/vol1/06-HASHARRAYS.INTERPRESS b/docs/porter-irm/vol1/06-HASHARRAYS.INTERPRESS new file mode 100644 index 00000000..6ab85cf1 Binary files /dev/null and b/docs/porter-irm/vol1/06-HASHARRAYS.INTERPRESS differ diff --git a/docs/porter-irm/vol1/06-HASHARRAYS.TEDIT b/docs/porter-irm/vol1/06-HASHARRAYS.TEDIT new file mode 100644 index 00000000..9547c5ac --- /dev/null +++ b/docs/porter-irm/vol1/06-HASHARRAYS.TEDIT @@ -0,0 +1,52 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 6. HASH ARRAYS 1 6. HASH ARRAYS 1 "6"6. HASH ARRAYS 6 Hash arrays(HASH% ARRAYS NIL Hash% arrays NIL ("6") 1) provide a mechanism for associating arbitrary Lisp objects ("hash keys") with other objects ("hash values"), such that the hash value associated with a particular hash key can be quickly obtained. A set of associations could be represented as a list or array of pairs, but these schemes are very inefficient when the number of associations is large. There are functions for creating hash arrays, putting a hash key/value pair in a hash array, and quickly retrieving the hash value associated with a given hash key. By default, the hash array functions use EQ for comparing hash keys. This means that if non-atoms are used as hash keys, the exact same object (not a copy) must be used to retrieve the hash value. However, you can override this default for any hash array by specifying the functions used to compare hash keys and to "hash" a hash key to a number. This can be used, for example, to create hash arrays where EQUAL but non-EQ strings will hash to the same value. Specifying alternative hashing algorithms is described below. In the description of the functions below, the argument HARRAY should be a value of the function HASHARRAY, which is used to create hash arrays. For convenience in interactive program development, it may also be NIL, in which case a hash array (SYSHASHARRAY) provided by the system is used; you must watch out for confusions if this form is used to associate more than one kind of value with the same key. Note: For backwards compatibility, the hash array functions will accept a list whose CAR is a hash array, and whose CDR is the "overflow method" for the hash array (see below). However, hash array functions are guaranteed to perform with maximum efficiency only if a direct value of HASHARRAY is given. (HASHARRAY(HASHARRAY (Function) NIL NIL ("6") 1) MINKEYS OVERFLOW HASHBITSFN EQUIVFN) [Function] Creates a hash array containing at least MINKEYS hash keys, with overflow method OVERFLOW. See discussion of overflow behavior below. If HASHBITSFN and EQUIVFN are non-NIL, they specify the hashing function and comparison function used to interpret hash keys. This is described in the section on user-specified hashing functions below. If HASHBITSFN and EQUIVFN are NIL, the default is to hash EQ hash keys to the same value. (HARRAY(HARRAY (Function) NIL NIL ("6") 1) MINKEYS) [Function] Provided for backward compatibility, this is equivalent to (HASHARRAY MINKEYS 'ERROR). (HARRAYP(HARRAYP (Function) NIL NIL ("6") 1) X) [Function] Returns X if it is a hash array object; otherwise NIL. HARRAYP returns NIL if X is a list whose CAR is an HARRAYP, even though this is accepted by the hash array functions. (HARRAYPROP(HARRAYPROP (Function) NIL NIL ("6") 1) HARRAY PROP NEWVALUE) [NoSpread Function] Returns the property PROP of HARRAY; PROP can have the system-defined values SIZE (returns the maximum occupancy of HARRAY), NUMKEYS (number of occupied slots), OVERFLOW (overflow method), HASHBITSFN (hashing function) and EQUIVFN (comparison function). Except for SIZE and NUMKEYS, a new value may be specified as NEWVALUE. By using other values for PROP, the user may also set and get arbitrary property values, to associate additional information with a hash array. Note: The HASHBITSFN or EQUIVFN properties can only be changed if the hash array is empty. (HARRAYSIZE(HARRAYSIZE (Function) NIL NIL ("6") 2) HARRAY) [Function] Equivalent to (HARRAYPROP HARRAY 'SIZE); returns the number of slots in HARRAY. (CLRHASH(CLRHASH (Function) NIL NIL ("6") 2) HARRAY) [Function] Clears all hash keys/values from HARRAY. Returns HARRAY. (PUTHASH(PUTHASH (Function) NIL NIL ("6") 2) KEY VAL HARRAY) [Function] Associates the hash value VAL with the hash key KEY in HARRAY. Replaces the previous hash value, if any. If VAL is NIL, any old association is removed (hence a hash value of NIL is not allowed). If HARRAY is full when PUTHASH is called with a key not already in the hash array, the function HASHOVERFLOW is called, and the PUTHASH is applied to the value returned (see below). Returns VAL. ((GETHASH (Function) NIL NIL ("6") 2)GETHASH KEY HARRAY) [Function] Returns the hash value associated with the hash key KEY in HARRAY. Returns NIL, if KEY is not found. (REHASH(REHASH (Function) NIL NIL ("6") 2) OLDHARRAY NEWHARRAY) [Function] Hashes all hash keys and values in OLDHARRAY into NEWHARRAY. The two hash arrays do not have to be (and usually aren't) the same size. Returns NEWHARRAY. (MAPHASH(MAPHASH (Function) NIL NIL ("6") 2) HARRAY MAPHFN) [Function] MAPHFN is a function of two arguments. For each hash key in HARRAY, MAPHFN will be applied to the hash value, and the hash key. For example: [MAPHASH A (FUNCTION (LAMBDA (VAL KEY) (if (LISTP KEY) then (PRINT VAL)] will print the hash value for all hash keys that are lists. MAPHASH returns HARRAY. (DMPHASH(DMPHASH (Function) NIL NIL ("6") 2) HARRAY1 HARRAY2 ... HARRAYN) [NLambda NoSpread Function] Prints on the primary output file LOADable forms which will restore the hash-arrays contained as the values of the atoms HARRAY1, HARRAY2, ... HARRAYN. Example: (DMPHASH SYSHASHARRAY) will dump the system hash-array. Note: All EQ identities except atoms and small integers are lost by dumping and loading because READ will create new structure for each item. Thus if two lists contain an EQ substructure, when they are dumped and loaded back in, the corresponding substructures while EQUAL are no longer EQ. The HORRIBLEVARS file package command (Chapter 17) provides a way of dumping hash tables such that these identities are preserved. Hash Overflow 1 When a hash array(HASH% ARRAY NIL Hash% array NIL ("6") 3 SUBNAME OVERFLOW SUBTEXT overflow) becomes full, attempting to add another hash key will cause the function HASHOVERFLOW to be called. This will either automatically enlarge the hash array, or cause the error HASH TABLE FULL. How hash overflow is handled is determined by the value of the OVERFLOW property of the hash array (which can be accessed by HARRAYPROP). The possibilities for the overflow method are: the litatom ERROR The error HASH ARRAY FULL is generated when the hash array overflows. This is the default overflow behavior for hash arrays returned by HARRAY. NIL The array is automatically enlarged by 1.5. This is the default overflow behavior for hash arrays returned by HASHARRAY. a positive integer N The array is enlarged to include N more slots than it currently has. a floating point number F The array is changed to include F times the number of current slots. a function or lambda expression FN Upon hash overflow, FN is called with the hash array as its argument. If FN returns a number, that will become the size of the array. Otherwise, the new size defaults to 1.5 times its previous size. FN could be used to print a message, or perform some monitor function. Note: For backwards compatibility, the hash array functions accept a list whose CAR is the hash array, and whose CDR is the overflow method. In this case, the overflow method specified in the list overrides the overflow method set in the hash array. Note that hash array functions are guaranteed to perform with maximum efficiency only if a direct value of HASHARRAY is given. User-Specified Hashing Functions 1 In general terms, when a key is looked up in a hash array(HASH% ARRAY NIL Hash% array NIL ("6") 3 SUBNAME USER-SPECIFIED% FUNCTIONS SUBTEXT user-specified% functions), it is converted to an integer, which is used to index into a linear array. If the key is not the same as the one found at that index, other indices are tried until it the desired key is found. The value stored with that key is then returned (from GETHASH) or replaced (from PUTHASH). The important features of this algorithm, for purposes of customizing hash arrays, are the "hashing function" used to convert a key to an integer; and the comparison function used to compare the key found in the array with the key being looked up. In order for hash arrays to work correctly, any two objects which are equal according to the comparison function must "hash" to equal integers. By default, the Interlisp hash array functions use a hashing function that computes an integer from the internal address of a key, and use EQ for comparing keys. This means that if non-atoms are used as hash keys, the exact same object (not a copy) must be used to retrieve the hash value. There are some applications for which the EQ constraint is too restrictive. For example, it may be useful to use strings as hash keys, without the restriction that EQUAL but not EQ strings are considered to be different hash keys. The user can override this default behavior for any hash array by specifying the functions used to compare keys and to "hash" a key to a number. This can be done by giving the HASHBITSFN and EQUIVFN arguments to HASHARRAY (see above). The EQUIVFN argument is a function of two arguments that returns non-NIL when its arguments are considered equal. The HASHBITSFN argument is a function of one argument that produces a positive small integer (in the range [0..2^16-1]) with the property that objects that are considered equal by the EQUIVFN produce the same hash bits. For an existing hash array, the function HARRAYPROP (see above) can be used to examine the hashing and equivalence functions as the HASHBITSFN and EQUIVFN hash array properties. These properties are read-only for non-empty hash arrays, as it makes no sense to change the equivalence relationship once some keys have been hashed. The following function is useful for creating hash arrays that take strings as hash keys: (STRINGHASHBITS(STRINGHASHBITS (Function) NIL NIL ("6") 4) STRING) [Function] Hashes the string STRING into an integer that can be used as a HASHBITSFN for a hash array. Strings which are STREQUAL hash to the same integer. Example: (HASHARRAY MINKEYS OVERFLOW 'STRINGHASHBITS 'STREQUAL) creates a hash array where you can use strings as hash keys. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "6-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "6-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "6-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "6-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "6-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "6-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))))),ll2l52Hll-T3(T2l,HH +,ll3HH +T,HH,HH,2F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR CLASSIC + HELVETICATITAN +TITAN +CLASSIC +CLASSIC + HELVETICA +CLASSIC +MODERN +MODERN +MODERNMODERN + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC +  IM.CHAP.GETFNMODERN   HRULE.GETFNMODERN +  +IM.INDEX.GETFN )n e 8# k V   + +&IM.INDEX.GETFNMODERN +#  )!-  +  + +#IM.INDEX.GETFNMODERN +  ; +$IM.INDEX.GETFNMODERN +  )  < + 'IM.INDEX.GETFNMODERN +  $# +$" r + +< + 'IM.INDEX.GETFN  ! +$IM.INDEX.GETFN  !  +$IM.INDEX.GETFN  18B 8 +$IM.INDEX.GETFN +  4 +#IM.INDEX.GETFN  #  V  +$IM.INDEX.GETFN   7D ) =  +$IM.INDEX.GETFN   "S " +TH^ s HRULE.GETFNMODERN + KIM.INDEX.GETFNJ ZB6 +3  p  n "#!$!4~ER  ! HRULE.GETFNMODERN + 9mIM.INDEX.GETFN   *y 3  +  :/ + ) +Q + Z ++IM.INDEX.GETFNMODERN + +   ' +&   = 'z \ No newline at end of file diff --git a/docs/porter-irm/vol1/07-NUMBERS.INTERPRESS b/docs/porter-irm/vol1/07-NUMBERS.INTERPRESS new file mode 100644 index 00000000..a017fa3f Binary files /dev/null and b/docs/porter-irm/vol1/07-NUMBERS.INTERPRESS differ diff --git a/docs/porter-irm/vol1/07-NUMBERS.TEDIT b/docs/porter-irm/vol1/07-NUMBERS.TEDIT new file mode 100644 index 00000000..0a149377 --- /dev/null +++ b/docs/porter-irm/vol1/07-NUMBERS.TEDIT @@ -0,0 +1,66 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 7. NUMBERS AND ARITHMETIC FUNCTIONS 1 7. NUMBERS AND ARITHMETIC FUNCTIONS 1 "7"7. NUMBERS AND ARITHMETIC FUNCTIONS 6 There are four different types of numbers(NUMBERS NIL Numbers NIL NIL 1) in Interlisp: small integers(NUMBERS NIL Numbers NIL NIL 1 SUBNAME SMALL% INTEGERS SUBTEXT small% integers), large integers(NUMBERS NIL Numbers NIL NIL 1 SUBNAME LARGE% INTEGERS SUBTEXT large% integers), bignums (arbitrary-size integers)(NUMBERS NIL Numbers NIL NIL 1 SUBNAME BIGNUMS SUBTEXT bignums), and floating-point numbers(NUMBERS NIL Numbers NIL NIL 1 SUBNAME FLOATING-POINT SUBTEXT floating-point). Small integers are those integers that can be directly stored within a pointer value (implementation-dependent). Large integers and floating-point numbers are full-word quantities that are stored by "boxing" the number (see below). Bignums are "boxed" as a series of words. Large integers and floating-point numbers can be any full word quantity. In order to distinguish between those full word quantities that represent large integers or floating-point numbers, and other Interlisp pointers, these numbers are "boxed": When a large integer or floating-point number is created (via an arithmetic operation or by READ), Interlisp gets a new word from "number storage" and puts the large integer or floating-point number into that word. Interlisp then passes around the pointer to that word, i.e., the "boxed number(BOXED% NUMBERS NIL Boxed% numbers NIL NIL 1)", rather than the actual quantity itself. Then when a numeric function needs the actual numeric quantity, it performs the extra level of addressing to obtain the "value" of the number. This latter process is called "unboxing". Note that unboxing does not use any storage, but that each boxing operation uses one new word of number storage. Thus, if a computation creates many large integers or floating-point numbers, i.e., does lots of boxes, it may cause a garbage collection of large integer space, or of floating-point number space. The following functions can be used to distinguish the different types of numbers: (SMALLP(SMALLP (Function) NIL NIL NIL 1) X) [Function] Returns X, if X is a small integer; NIL otherwise. Does not generate an error if X is not a number. (FIXP(FIXP (Function) NIL NIL NIL 1) X) [Function] Returns X, if X is an integer; NIL otherwise. Note that FIXP is true for small integers, large integers, and bignums. Does not generate an error if X is not a number. (FLOATP(FLOATP (Function) NIL NIL NIL 1) X) [Function] Returns X if X is a floating-point number; NIL otherwise. Does not give an error if X is not a number. (NUMBERP(NUMBERP (Function) NIL NIL NIL 1) X) [Function] Returns X, if X is a number of any type (FIXP or FLOATP); NIL otherwise. Does not generate an error if X is not a number. If (NUMBERP X) is true, then either (FIXP X) or (FLOATP X) is true. Each small integer has a unique representation, so EQ may be used to check equality. EQ should not be used for large integers, bignums, or floating-point numbers, EQP, IEQP, or EQUAL must be used instead. (EQP(EQP (Function) NIL NIL NIL 2) X Y) [Function] Returns T, if X and Y are EQ, or equal numbers; NIL otherwise. EQ may be used if X and Y are known to be small integers. EQP does not convert X and Y to integers, e.g., (EQP 2000 2000.3) => NIL, but it can be used to compare an integer and a floating-point number, e.g., (EQP 2000 2000.0) => T. EQP does not generate an error if X or Y are not numbers. EQP can also be used to compare stack pointers (see Chapter 11) and compiled code objects (see Chapter 10). The action taken on division by zero and floating-point overflow is determined with the following function: (OVERFLOW(OVERFLOW (Function) NIL NIL NIL 2) FLG) [Function] Sets a flag that determines the system response to arithmetic overflow (for floating-point arithmetic) and division by zero; returns the previous setting. For integer arithmetic: If FLG=T, an error occurs on division by zero. If FLG=NIL or 0, integer division by zero returns zero. Integer overflow cannot occur, because small integers are converted to bignums (see the beginning of this chapter). For floating-point arithmetic: If FLG=T, an error occurs on floating overflow or floating division by zero. If FLG=NIL or 0, the largest (or smallest) floating-point number is returned as the result of the overflowed computation or floating division by zero. The default value for OVERFLOW is T, meaning to cause an error on division by zero or floating overflow. Generic Arithmetic(GENERIC% ARITHMETIC NIL Generic% arithmetic NIL NIL 2)(ARITHMETIC NIL Arithmetic NIL NIL 2 SUBNAME GENERIC SUBTEXT generic) 1 The functions in this section are "generic" arithmetic functions. If any of the arguments are floating-point numbers (see the Floating-Point Arithmetic section below), they act exactly like floating-point functions, and float all arguments, and return a floating-point number as their value. Otherwise, they act like the integer functions (see the Integer Arithmetic section below). If given a non-numeric argument, they generate an error, NON-NUMERIC ARG. (PLUS(PLUS (Function) NIL NIL NIL 2) X1 X2 ... XN) [NoSpread Function] X1 + X2 + ... + XN. (MINUS(MINUS (Function) NIL NIL NIL 2) X) [Function] - X (DIFFERENCE(DIFFERENCE (Function) NIL NIL NIL 2) X Y) [Function] X - Y ((TIMES (Function) NIL NIL NIL 2)TIMES X1 X2 ... XN) [NoSpread Function] X1 * X2 * ... * XN (QUOTIENT(QUOTIENT (Function) NIL NIL NIL 3) X Y) [Function] If X and Y are both integers, returns the integer division of X and Y. Otherwise, converts both X and Y to floating-point numbers, and does a floating-point division. The results of division by zero and floating-point overflow is determined by the function OVERFLOW (see the section above). (REMAINDER(REMAINDER (Function) NIL NIL NIL 3) X Y) [Function] If X and Y are both integers, returns (IREMAINDER X Y), otherwise (FREMAINDER X Y). (GREATERP (GREATERP% (Function) NIL NIL NIL 3)X Y) [Function] T, if X > Y, NIL otherwise. (LESSP(LESSP (Function) NIL NIL NIL 3) X Y) [Function] T if X < Y, NIL otherwise. (GEQ(GEQ (Function) NIL NIL NIL 3) X Y) [Function] T, if X >= Y, NIL otherwise. (LEQ(LEQ (Function) NIL NIL NIL 3) X Y) [Function] T, if X <= Y, NIL otherwise. (ZEROP(ZEROP (Function) NIL NIL NIL 3) X) [Function] (EQP X 0). (MINUSP(MINUSP (Function) NIL NIL NIL 3) X) [Function] T, if X is negative; NIL otherwise. Works for both integers and floating-point numbers. (MIN(MIN (Function) NIL NIL NIL 3) X1 X2 ... XN) [NoSpread Function] Returns the minimum of X1, X2, ..., XN. (MIN) returns the value of MAX.INTEGER (see the Integer Arithmetic section below). (MAX(MAX (Function) NIL NIL NIL 3) X1 X2 ... XN) [NoSpread Function] Returns the maximum of X1, X2, ..., XN. (MAX) returns the value of MIN.INTEGER (see the Integer Arithmetic section below). (ABS(ABS (Function) NIL NIL NIL 3) X) [Function] X if X > 0, otherwise -X. ABS uses GREATERP and MINUS (not IGREATERP and IMINUS). Integer Arithmetic(ARITHMETIC NIL Arithmetic NIL NIL 3 SUBNAME INTEGER SUBTEXT integer)(INTEGER% ARITHMETIC NIL Integer% arithmetic NIL NIL 3) 1 The input syntax for an integer is an optional sign (+ or -) followed by a sequence of decimal digits, and terminated by a delimiting character. Integers entered with this syntax are interpreted as decimal integers. Integers in other radices can be entered as follows: 123Q |o123 If an integer is followed by the letter Q, or proceeded by a vertical bar and the letter "o", the digits are interpreted an octal (base 8) integer. |b10101 If an integer is proceeded by a vertical bar and the letter "b", the digits are interpreted as a binary (base 2) integer. |x1A90 If an integer is proceeded by a vertical bar and the letter "x", the digits are interpreted as a hexadecimal (base 16) integer. The uppercase letters A though F are used as the digits after 9. |5r1243 If an integer is proceeded by a vertical bar, a positive decimal integer BASE, and the letter "r", the digits are interpreted as an integer in the base BASE. For example, |8r123 = 123Q, and |16r12A3 = |x12A3. When inputting a number in a radix above ten, the uppercase letters A through Z can be used as the digits after 9 (but there is no digit above Z, so it is not possible to type all base-99 digits). Note that 77Q and 63 both correspond to the same integers, and in fact are indistinguishable internally since no record is kept of the syntax used to create an integer. The function RADIX (see Chapter 25), sets the radix used to print integers. Integers are created by PACK and MKATOM when given a sequence of characters observing the above syntax, e.g. (PACK '(1 2 Q)) => 10. Integers are also created as a result of arithmetic operations. The range of integers of various types is implementation-dependent. This information is accessible to the user through the following variables: MIN.SMALLP(MIN.SMALLP (Variable) NIL NIL NIL 4) [Variable] MAX.SMALLP(MAX.SMALLP (Variable) NIL NIL NIL 4) [Variable] The smallest/largest possible small integer. MIN.FIXP(MIN.FIXP (Variable) NIL NIL NIL 4) [Variable] MAX.FIXP(MAX.FIXP (Variable) NIL NIL NIL 4) [Variable] The smallest/largest possible large integer. MIN.INTEGER(MIN.INTEGER (Variable) NIL NIL NIL 4) [Variable] MAX.INTEGER(MAX.INTEGER (Variable) NIL NIL NIL 4) [Variable] The smallest/largest possible integers. For some algorithms, it is useful to have an integer that is larger than any other integer. Therefore, the values of MAX.INTEGER and MIN.INTEGER are two special bignums; the value of MAX.INTEGER is GREATERP than any other integer, and the value of MIN.INTEGER is LESSP than any other integer. Trying to do arithmetic using these special bignums, other than comparison, will cause an error. All of the functions described below work on integers. Unless specified otherwise, if given a floating-point number, they first convert the number to an integer by truncating the fractional bits, e.g., (IPLUS 2.3 3.8)=5; if given a non-numeric argument, they generate an error, NON-NUMERIC ARG. (IPLUS X1 X2 ... XN) [NoSpread Function] Returns the sum X1 + X2 + ... + XN. (IPLUS)=0. (IMINUS X) [Function] -X (IDIFFERENCE X Y) [Function] X - Y (ADD1(ADD1 (Function) NIL NIL NIL 5) X) [Function] X + 1 (SUB1(SUB1 (Function) NIL NIL NIL 5) X) [Function] X - 1 (ITIMES(ITIMES (Function) NIL NIL NIL 5) X1 X2 ... XN) [NoSpread Function] Returns the product X1 * X2 * ... * XN. (ITIMES)=1. (IQUOTIENT(IQUOTIENT (Function) NIL NIL NIL 5) X Y) [Function] X / Y truncated. Examples: (IQUOTIENT 3 2) => 1 (IQUOTIENT -3 2) => -1 If Y is zero, the result is determined by the function OVERFLOW . (IREMAINDER(IREMAINDER (Function) NIL NIL NIL 5) X Y) [Function] Returns the remainder when X is divided by Y. Example: (IREMAINDER 3 2) => 1 (IMOD(IMOD (Function) NIL NIL NIL 5) X N) [Function] Computes the integer modulus; this differs from IREMAINDER in that the result is always a non-negative integer in the range [0,N). (IGREATERP(IGREATERP (Function) NIL NIL NIL 5) X Y) [Function] T, if X > Y; NIL otherwise. (ILESSP(ILESSP (Function) NIL NIL NIL 5) X Y) [Function] T, if X < Y; NIL otherwise. (IGEQ(IGEQ (Function) NIL NIL NIL 5) X Y) [Function] T, if X >= Y; NIL otherwise. (ILEQ(ILEQ (Function) NIL NIL NIL 5) X Y) [Function] T, if X <= Y; NIL otherwise. (IMIN(IMIN (Function) NIL NIL NIL 5) X1 X2 ... XN) [NoSpread Function] Returns the minimum of X1, X2, ..., XN. (IMIN) returns the largest possible large integer, the value of MAX.INTEGER. (IMAX(IMAX (Function) NIL NIL NIL 5) X1 X2 ... XN) [NoSpread Function] Returns the maximum of X1, X2, ..., XN. (IMAX) returns the smallest possible large integer, the value of MIN.INTEGER. (IEQP(EQP (Function) NIL NIL NIL 6) X Y) [Function] Returns T if X and Y are EQ or equal integers; NIL otherwise. Note that EQ may be used if X and Y are known to be small integers. IEQP converts X and Y to integers, e.g., (IEQP 2000 2000.3) => T. Causes NON-NUMERIC ARG error if either X or Y are not numbers. (FIX(FIX (Function) NIL NIL NIL 6) N) [Function] If N is an integer, returns N. Otherwise, converts N to an integer by truncating fractional bits For example, (FIX 2.3) => 2, (FIX -1.7) => -1. Since FIX is also a programmer's assistant command (see Chapter 13), typing FIX directly to Interlisp will not cause the function FIX to be called. (FIXR(FIXR (Function) NIL NIL NIL 6) N) [Function] If N is an integer, returns N. Otherwise, converts N to an integer by rounding. For example, (FIXR 2.3) => 2, (FIXR -1.7) => -2, (FIXR 3.5) => 4). (GCD(GCD (Function) NIL NIL NIL 6) N1 N2) [Function] Returns the greatest common divisor of N1 and N2, e.g., (GCD 72 64)=8. Logical Arithmetic(ARITHMETIC NIL Arithmetic NIL NIL 6 SUBNAME LOGICAL% FUNCTIONS SUBTEXT logical% functions) Functions(LOGICAL% ARITHMETIC% FUNCTIONS NIL Logical% arithmetic% functions NIL NIL 6) 1 (LOGAND(LOGAND (Function) NIL NIL NIL 6) X1 X2 ... XN) [NoSpread Function] Returns the logical AND of all its arguments, as an integer. Example: (LOGAND 7 5 6) => 4 (LOGOR(LOGOR (Function) NIL NIL NIL 6) X1 X2 ... XN) [NoSpread Function] Returns the logical OR of all its arguments, as an integer. Example: (LOGOR 1 3 9) => 11 (LOGXOR(LOGXOR (Function) NIL NIL NIL 6) X1 X2 ... XN) [NoSpread Function] Returns the logical exclusive OR of its arguments, as an integer. Example: (LOGXOR 11 5) => 14 (LOGXOR 11 5 9) = (LOGXOR 14 9) => 7 (LSH(LSH (Function) NIL NIL NIL 6) X N) [Function] (arithmetic) "Left Shift." Returns X shifted left N places, with the sign bit unaffected. X can be positive or negative. If N is negative, X is shifted right -N places. (RSH(RSH (Function) NIL NIL NIL 6) X N) [Function] (arithmetic) "Right Shift." Returns X shifted right N places, with the sign bit unaffected, and copies of the sign bit shifted into the leftmost bit. X can be positive or negative. If N is negative, X is shifted left -N places. Warning: Be careful if using RSH to simulate division; RSHing a negative number is not generally equivalent to dividing by a power of two. (LLSH(LLSH (Function) NIL NIL NIL 7) X N) [Function] (LRSH(LLSH (Function) NIL NIL NIL 7) X N) [Function] "Logical Left Shift" and "Logical Right Shift". The difference between a logical and arithmetic right shift lies in the treatment of the sign bit. Logical shifting treats it just like any other bit; arithmetic shifting will not change it, and will "propagate" rightward when actually shifting rightwards. Note that shifting (arithmetic) a negative number "all the way" to the right yields -1, not 0. Note: LLSH and LRSH are currently implemented using mod-232 arithmetic. Passing a bignum to either of these will cause an error. LRSH of negative numbers will shift in 0s in the high bits. (INTEGERLENGTH(LLSH (Function) NIL NIL NIL 7) X) [Function] Returns the number of bits needed to represent X (coerced to an integer). This is equivalent to: 1+floor[log2[abs[X]]]. (INTEGERLENGTH 0) = 0. (POWEROFTWOP(POWEROFTWOP (Function) NIL NIL NIL 7) X) [Function] Returns non-NIL if X (coerced to an integer) is a power of two. (EVENP(EVENP (Function) NIL NIL NIL 7) X Y) [NoSpread Function] If Y is not given, equivalent to (ZEROP (IMOD X 2)); otherwise equivalent to (ZEROP (IMOD X Y)). (ODDP(ODDP (Function) NIL NIL NIL 7) N MODULUS) [NoSpread Function] Equivalent to (NOT (EVENP N MODULUS)). MODULUS defaults to 2. (LOGNOT (LOGNOT% (Macro) NIL NIL NIL 7)N) [Macro] Logical negation of the bits in N. Equivalent to (LOGXOR N -1) (BITTEST(BITTEST (Macro) NIL NIL NIL 7) N MASK) [Macro] Returns T if any of the bits in MASK are on in the number N. Equivalent to (NOT (ZEROP (LOGAND N MASK))) (BITCLEAR(BITCLEAR (Macro) NIL NIL NIL 7) N MASK) [Macro] Turns off bits from MASK in N. Equivalent to (LOGAND N (LOGNOT MASK)) (BITSET(BITSET (Macro) NIL NIL NIL 7) N MASK) [Macro] Turns on the bits from MASK in N. Equivalent to (LOGOR N MASK) (MASK.1'S(MASK.1'S (Macro) NIL NIL NIL 7) POSITION SIZE) [Macro] Returns a bit-mask with SIZE one-bits starting with the bit at POSITION. Equivalent to (LLSH (SUB1 (EXPT 2 SIZE)) POSITION) (MASK.0'S(MASK.0'S (Macro) NIL NIL NIL 7) POSITION SIZE) [Macro] Returns a bit-mask with all one bits, except for SIZE bits starting at POSITION. Equivalent to (LOGNOT (MASK.1'S POSITION SIZE)) (LOADBYTE(LOADBYTE (Function) NIL NIL NIL 7) N POS SIZE) [Function] Extracts SIZE bits from N, starting at position POS. Equivalent to (LOGAND (RSH N POS) (MASK.1'S 0 SIZE)) (DEPOSITBYTE(DEPOSITBYTE (Function) NIL NIL NIL 8) N POS SIZE VAL) [Function] Insert SIZE bits of VAL at position POS into N, returning the result. Equivalent to (LOGOR (BITCLEAR N (MASK.1'S POS SIZE)) (LSH (LOGAND VAL (MASK.1'S 0 SIZE)) POS)) (ROT(ROT (Function) NIL NIL NIL 8) X N FIELDSIZE) [Function] "Rotate bits in field". It performs a bitwise left-rotation of the integer X, by N places, within a field of FIELDSIZE bits wide. Bits being shifted out of the position selected by (EXPT 2 (SUB1 FIELDSIZE)) will flow into the "units" position. The notions of position and size can be combined to make up a "byte specifier", which is constructed by the macro BYTE [note reversal of arguments as compare with above functions]: (BYTE(BYTE (Macro) NIL NIL NIL 8) SIZE POSITION) [Macro] Constructs and returns a "byte specifier" containing SIZE and POSITION. (BYTESIZE(BYTESIZE (Macro) NIL NIL NIL 8) BYTESPEC) [Macro] Returns the SIZE componant of the "byte specifier" BYTESPEC. (BYTEPOSITION(BYTEPOSITION (Macro) NIL NIL NIL 8) BYTESPEC) [Macro] Returns the POSITION componant of the "byte specifier" BYTESPEC. (LDB(LDB (Macro) NIL NIL NIL 8) BYTESPEC VAL) [Macro] Equivalent to (LOADBYTE VAL (BYTEPOSITION BYTESPEC) (BYTESIZE BYTESPEC)) (DPB(DPB (Macro) NIL NIL NIL 8) N BYTESPEC VAL) [Macro] Equivalent to (DEPOSITBYTE VAL BYTEPOSITION BYTESPEC) (BYTESIZE BYTESPEC) N) Floating-Point Arithmetic(ARITHMETIC NIL Arithmetic NIL NIL 8 SUBNAME FLOATING% POINT SUBTEXT floating% point)(FLOATING% POINT% ARITHMETIC NIL Floating% point% arithmetic NIL NIL 8) 1 A floating-point number is input as a signed integer, followed by a decimal point, followed by another sequence of digits called the fraction, followed by an exponent (represented by E followed by a signed integer) and terminated by a delimiter. Both signs are optional, and either the fraction following the decimal point, or the integer preceding the decimal point may be omitted. One or the other of the decimal point or exponent may also be omitted, but at least one of them must be present to distinguish a floating-point number from an integer. For example, the following will be recognized as floating-point numbers: 5. 5.00 5.01 .3 5E2 5.1E2 5E-3 -5.2E+6 Floating-point numbers are printed using the format control specified by the function FLTFMT (see Chapter 25). FLTFMT is initialized to T, or free format. For example, the above floating-point numbers would be printed free format as: 5.0 5.0 5.01 .3 500.0 510.0 .005 -5.2E6 Floating-point numbers are created by the read program when a "." or an E appears in a number, e.g., 1000 is an integer, 1000. a floating-point number, as are 1E3 and 1.E3. Note that 1000D, 1000F, and 1E3D are perfectly legal literal atoms. Floating-point numbers are also created by PACK and MKATOM, and as a result of arithmetic operations. PRINTNUM (see Chapter 25) permits greater controls on the printed appearance of floating-point numbers, allowing such things as left-justification, suppression of trailing decimals, etc. The floating-point number range is stored in the following variables: MIN.FLOAT(MIN.FLOAT (Variable) NIL NIL NIL 9) [Variable] The smallest possible floating-point number. MAX.FLOAT(MAX.FLOAT (Variable) NIL NIL NIL 9) [Variable] The largest possible floating-point number. All of the functions described below work on floating-point numbers. Unless specified otherwise, if given an integer, they first convert the number to a floating-point number, e.g., (FPLUS 1 2.3) <=> (FPLUS 1.0 2.3) => 3.3; if given a non-numeric argument, they generate an error, NON-NUMERIC ARG. (FPLUS(FPLUS (Function) NIL NIL NIL 9) X1 X2 ... XN) [NoSpread Function] X1 + X2 + ... + XN (FMINUS(FMINUS (Function) NIL NIL NIL 9) X) [Function] - X (FDIFFERENCE(FDIFFERENCE (Function) NIL NIL NIL 9) X Y) [Function] X - Y (FTIMES(FTIMES (Function) NIL NIL NIL 9) X1 X2 ... XN) [NoSpread Function] X1 * X2 * ... * XN (FQUOTIENT(FQUOTIENT (Function) NIL NIL NIL 9) X Y) [Function] X / Y. The results of division by zero and floating-point overflow is determined by the function OVERFLOW. (FREMAINDER(FREMAINDER (Function) NIL NIL NIL 9) X Y) [Function] Returns the remainder when X is divided by Y. Equivalent to: (FDIFFERENCE X (FTIMES Y (FIX (FQUOTIENT X Y)))) Example: (FREMAINDER 7.5 2.3) => 0.6 (FGREATERP(FGREATERP (Function) NIL NIL NIL 10) X Y) [Function] T, if X > Y, NIL otherwise. (FLESSP(FLESSP (Function) NIL NIL NIL 10) X Y) [Function] T, if X < Y, NIL otherwise. (FEQP(FEQP (Function) NIL NIL NIL 10) X Y) [Function] Returns T if N and M are equal floating-point numbers; NIL otherwise. FEQP converts N and M to floating-point numbers.Causes NON-NUMERIC ARG error if either N or M are not numbers. (FMIN(FMIN (Function) NIL NIL NIL 10) X1 X2 ... XN) [NoSpread Function] Returns the minimum of X1, X2, ..., XN. (FMIN) returns the largest possible floating-point number, the value of MAX.FLOAT. (FMAX(FMAX (Function) NIL NIL NIL 10) X1 X2 ... XN) [NoSpread Function] Returns the maximum of X1, X2, ..., XN. (FMAX) returns the smallest possible floating-point number, the value of MIN.FLOAT. (FLOAT(FLOAT (Function) NIL NIL NIL 10) X) [Function] Converts X to a floating-point number. Example: (FLOAT 0) => 0.0 Other Arithmetic Functions 1 (EXPT(EXPT (Function) NIL NIL NIL 10) A N) [Function] Returns AN. If A is an integer and N is a positive integer, returns an integer, e.g, (EXPT 3 4) => 81, otherwise returns a floating-point number. If A is negative and N fractional, an error is generated: ILLEGAL EXPONENTIATION. If N is floating and either too large or too small, an error is generated: VALUE OUT OF RANGE EXPT. (SQRT(SQRT (Function) NIL NIL NIL 10) N) [Function] Returns the square root of N as a floating-point number. N may be fixed or floating-point. Generates an error if N is negative. (LOG(LOG (Function) NIL NIL NIL 10) X) [Function] Returns the natural logarithm of X as a floating-point number. X can be integer or floating-point. (ANTILOG(ANTILOG (Function) NIL NIL NIL 10) X) [Function] Returns the floating-point number whose logarithm is X. X can be integer or floating-point. Example: (ANTILOG 1) = e => 2.71828... (SIN(SIN (Function) NIL NIL NIL 10) X RADIANSFLG) [Function] Returns the sine of X as a floating-point number. X is in degrees unless RADIANSFLG=T. (COS(COS (Function) NIL NIL NIL 11) X RADIANSFLG) [Function] Similar to SIN. ((TAN (Function) NIL NIL NIL 11)TAN X RADIANSFLG) [Function] Similar to SIN. (ARCSIN (ARCSIN% (Function) NIL NIL NIL 11)X RADIANSFLG) [Function] X is a number between -1 and 1 (or an error is generated). The value of ARCSIN is a floating-point number, and is in degrees unless RADIANSFLG=T. In other words, if (ARCSIN X RADIANSFLG)=Z then (SIN Z RADIANSFLG)=X. The range of the value of ARCSIN is -90 to +90 for degrees, -~PI~/2 to ~PI~/2 for radians. (ARCCOS(ARCCOS (Function) NIL NIL NIL 11) X RADIANSFLG) [Function] Similar to ARCSIN. Range is 0 to 180, 0 to ~PI~. (ARCTAN(ARCTAN (Function) NIL NIL NIL 11) X RADIANSFLG) [Function] Similar to ARCSIN. Range is 0 to 180, 0 to ~PI~. (ARCTAN2(ARCTAN2 (Function) NIL NIL NIL 11) Y X RADIANSFLG) [Function] Computes (ARCTAN (FQUOTIENT Y X) RADIANSFLG), and returns a corresponding value in the range -180 to 180 (or -~PI~ to ~PI~), i.e. the result is in the proper quadrant as determined by the signs of X and Y. (RAND(RAND (Function) NIL NIL NIL 11) LOWER UPPER) [Function] Returns a pseudo-random number between LOWER and UPPER inclusive, i.e., RAND can be used to generate a sequence of random numbers. If both limits are integers, the value of RAND is an integer, otherwise it is a floating-point number. The algorithm is completely deterministic, i.e., given the same initial state, RAND produces the same sequence of values. The internal state of RAND is initialized using the function RANDSET described below. (RANDSET(RANDSET (Function) NIL NIL NIL 11) X) [Function] Returns the internal state of RAND. If X=NIL, just returns the current state. If X=T, RAND is initialized using the clocks, and RANDSET returns the new state. Otherwise, X is interpreted as a previous internal state, i.e., a value of RANDSET, and is used to reset RAND. For example, (SETQ OLDSTATE (RANDSET)) ... (for X from 1 to 10 do (PRIN1 (RAND 1 10))) 2847592748NIL (RANDSET OLDSTATE) ... (for X from 1 to 10 do (PRIN1 (RAND 1 10))) 2847592748NIL (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "7-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "7-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "7-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "7-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "7-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "7-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))2l6T3HZ +T55,3HZT,HH +-T3(T3HZ +T,,,ll,/,HH,HHF PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGRTITAN +TITAN +TITAN +TITAN +CLASSIC +CLASSIC + HELVETICA HELVETICACLASSIC + HELVETICA +CLASSIC +MODERN +MODERNMODERN + HRULE.GETFNCLASSIC +  +  HRULE.GETFNCLASSIC +  +$ +  HRULE.GETFNCLASSIC + $ +  HRULE.GETFNCLASSIC +   IM.CHAP.GETFN HELVETICA& HRULE.GETFNMODERN +) IM.INDEX.GETFN OIM.INDEX.GETFN OIM.INDEX.GETFN# ?IM.INDEX.GETFN MIM.INDEX.GETFN T  -IM.INDEX.GETFN S  !IM.INDEX.GETFN    +    IM.INDEX.GETFN    * @    !IM.INDEX.GETFN         "IM.INDEX.GETFN                 3 ! K    IM.INDEX.GETFN               N       i l  #IM.INDEX.GETFN     +    #  I      E  +7IM.INDEX.GETFNEIM.INDEX.GETFN  HRULE.GETFNMODERN +   IM.INDEX.GETFN      IM.INDEX.GETFN    %IM.INDEX.GETFN     IM.INDEX.GETFN     #IM.INDEX.GETFN   4    @ Z   +$IM.INDEX.GETFN              %IM.INDEX.GETFN       IM.INDEX.GETFNMODERN +       IM.INDEX.GETFNMODERN +       IM.INDEX.GETFNCLASSIC +        IM.INDEX.GETFNMODERN +      !IM.INDEX.GETFNMODERN +    A  IM.INDEX.GETFNMODERN +       ,  IM.INDEX.GETFNMODERN +       ,  IM.INDEX.GETFNCLASSIC +          +EIM.INDEX.GETFN7IM.INDEX.GETFN  HRULE.GETFNMODERN +5      ) k  {       J K    G  !  5  +   :   F C   +%IM.INDEX.GETFN  +%IM.INDEX.GETFN -  #IM.INDEX.GETFN #IM.INDEX.GETFN -  &IM.INDEX.GETFN  &IM.INDEX.GETFN   '  *  y   ;                   IM.INDEX.GETFN   IM.INDEX.GETFN   !IM.INDEX.GETFN       + +$IM.INDEX.GETFN     3   %IM.INDEX.GETFN      IM.INDEX.GETFN  0 +B   +$IM.INDEX.GETFN       !IM.INDEX.GETFN       IM.INDEX.GETFNCLASSIC +       IM.INDEX.GETFN       IM.INDEX.GETFN      :   IM.INDEX.GETFN       ;  IM.INDEX.GETFN             +    +     IM.INDEX.GETFN     ;    C 3   IM.INDEX.GETFN     *  IM.INDEX.GETFN  '    +[IM.INDEX.GETFN +MIM.INDEX.GETFN  HRULE.GETFNMODERN + !IM.INDEX.GETFN  G   IM.INDEX.GETFN  F  !IM.INDEX.GETFN  L ' IM.INDEX.GETFN  $  ( "      IM.INDEX.GETFN  %  b "       Q  IM.INDEX.GETFN  IM.INDEX.GETFN     p 8  IM.INDEX.GETFN  / C     &IM.INDEX.GETFN    ,   IM.INDEX.GETFN           IM.INDEX.GETFN         IM.INDEX.GETFN       IM.INDEX.GETFN            IM.INDEX.GETFN        IM.INDEX.GETFN          IM.INDEX.GETFN   #       IM.INDEX.GETFN  1       #IM.INDEX.GETFN +            &IM.INDEX.GETFN      '   +           IM.INDEX.GETFN  K   @  & r ?  IM.INDEX.GETFN  5     IM.INDEX.GETFN   #   $IM.INDEX.GETFN   #   IM.INDEX.GETFN          IM.INDEX.GETFN           +UIM.INDEX.GETFNGIM.INDEX.GETFN  HRULE.GETFNMODERN + > |  V   b ?    !     P  ,  F  $IM.INDEX.GETFN -  $IM.INDEX.GETFN ,  ,;     IM.INDEX.GETFN      !IM.INDEX.GETFN     &IM.INDEX.GETFN     !IM.INDEX.GETFN      +$IM.INDEX.GETFN    Z   %IM.INDEX.GETFN           +%IM.INDEX.GETFN       "IM.INDEX.GETFN        IM.INDEX.GETFN     #   +  "      IM.INDEX.GETFN       B    IM.INDEX.GETFN       C   !IM.INDEX.GETFN   '   +  HRULE.GETFNMODERN +  IM.INDEX.GETFN      1 1  $  G    IM.INDEX.GETFN    8   IM.INDEX.GETFN  !  #  #IM.INDEX.GETFN  5  - " IM.INDEX.GETFNMODERN +     +   IM.INDEX.GETFNMODERN +     IM.INDEX.GETFN      $IM.INDEX.GETFN  H 6 +    +    +  ;  "IM.INDEX.GETFN   !  "IM.INDEX.GETFN   !  #IM.INDEX.GETFN      +B  K     IM.INDEX.GETFN  '   b  > #   #IM.INDEX.GETFN     &   & $ ?        -      -  bz \ No newline at end of file diff --git a/docs/porter-irm/vol1/08-RECORDPACKAGE.INTERPRESS b/docs/porter-irm/vol1/08-RECORDPACKAGE.INTERPRESS new file mode 100644 index 00000000..692cca4c Binary files /dev/null and b/docs/porter-irm/vol1/08-RECORDPACKAGE.INTERPRESS differ diff --git a/docs/porter-irm/vol1/08-RECORDPACKAGE.TEDIT b/docs/porter-irm/vol1/08-RECORDPACKAGE.TEDIT new file mode 100644 index 00000000..21c4c032 --- /dev/null +++ b/docs/porter-irm/vol1/08-RECORDPACKAGE.TEDIT @@ -0,0 +1,138 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 8. RECORD PACKAGE 1 8. RECORD PACKAGE 1 "8"8. RECORD PACKAGE 6 The advantages of "data abstraction" have long been known: more readable code, fewer bugs, the ability to change the data structure without having to make major modifications to the program, etc. The record package encourages and facilitates this good programming practice by providing a uniform syntax for creating, accessing and storing data into many different types of data structures (arrays, list structures, association lists, etc.) as well as removing from you the task of writing the various manipulation routines. You declare (once) the data structures used by your programs, and thereafter indicate the manipulations of the data in a data-structure-independent manner. Using the declarations, the record package automatically computes the corresponding Interlisp expressions necessary to accomplish the indicated access/storage operations. If the data structure is changed by modifying the declarations, the programs automatically adjust to the new conventions. You describe the format of a data structure (record) by making a "record declaration" (see the Record Declarations section below). The record declaration is a description of the record, associating names with its various parts, or "fields". For example, the record declaration (RECORD MSG (FROM TO . TEXT)) describes a data structure called MSG, which contains three fields: FROM, TO, and TEXT. You can reference these fields by name, to retrieve their values or to store new values into them, by using the FETCH and REPLACE operators (see the FETCH and REPLACE section below). The CREATE operator (see the CREATE section below) is used for creating new instances of a record, and TYPE? (see the TYPE? section below) is used for testing whether an object is an instance of a particular record. (Note: all record operators can be in either upper- or lowercase.) Records may be implemented in a variety of different ways, as determined by the first element ("record type") of the record declaration. RECORD (used to specify elements and tails of a list structure) is just one of several record types currently implemented. You can specify a property list format by using the record type PROPRECORD, or that fields are to be associated with parts of a data structure via a specified hash array by using the record type HASHLINK, or that an entirely new data type be allocated (as described in the Built-In and User Data Types section below) by using the record-type DATATYPE. The record package is implemented through the DWIM/CLISP facilities, so it contains features such as spelling correction on field names, record types, etc. Record operations are translated using all CLISP declarations in effect (standard/fast/undoable); it is also possible to declare local record declarations that override global ones (see Chapter 21). The file package includes a RECORDS file package command for dumping record declarations (see Chapter 17), and FILES? and CLEANUP will inform you about records that need to be dumped. FETCH and REPLACE 1 The fields of a record are accessed and changed with the FETCH and REPLACE operators. If the record MSG has the record declaration (RECORD MSG (FROM TO . TEXT)), and X is a MSG data structure, (fetch FROM of X) will return the value of the FROM field of X, and (replace FROM of X with Y) will replace this field with the value of Y. In general, the value of a REPLACE operation is the same as the value stored into the field. Note that the form (fetch FROM of X) implicitly states that X is an instance of the record MSG, or at least it should to be treated as such for this particular operation. In other words, the interpretation of (fetch FROM of X) never depends on the value of X. Therefore, if X is not a MSG record, this may produce incorrect results. The TYPE? record operation (see the TYPES? section below) may be used to test the types of objects. If there is another record declaration, (RECORD REPLY (TEXT . RESPONSE)), then (fetch TEXT of X) is ambiguous, because X could be either a MSG or a REPLY record. In this case, an error will occur, AMBIGUOUS RECORD FIELD. To clarify this, FETCH and REPLACE can take a list for their "field" argument: (fetch (MSG TEXT) of X) will fetch the TEXT field of an MSG record. Note that if a field has an identical interpretation in two declarations, e.g., if the field TEXT occurred in the same location within the declarations of MSG and REPLY, then (fetch TEXT of X) would not be considered ambiguous. An exception to this rule is that "user" record declarations take precedence over "system" record declarations, in cases where an unqualified field name would be considered ambiguous. System records are declared by including (SYSTEM) in the record declaration (see the Record Declarations section below). All of the records defined in the standard Interlisp-D system are defined as system records. Another complication can occur if the fields of a record are themselves records. The fields of a record can be further broken down into sub-fields by a "subdeclaration" within the record declaration (see the Record Declarations section below). For example, (RECORD NODE (POSITION . LABEL) (RECORD POSITION (XLOC . YLOC))) permits you to access the POSITION field with (fetch POSITION of X), or its subfield XLOC with (fetch XLOC of X). You may also elaborate a field by declaring that field name in a separate record declaration (as opposed to an embedded subdeclaration). For instance, the TEXT field in the MSG and REPLY records above may be subdivided with the seperate record declaration (RECORD TEXT (HEADER . TXT)). Fields of subfields (to any level of nested subfields) are accessed by specifying the "data path" as a list of record/field names, where there is some path from each record to the next in the list. For instance, (fetch (MSG TEXT HEADER) of X) indicates that X is to be treated as a MSG record, its TEXT field should be accessed, and its HEADER field should be accessed. Only as much of the data path as is necessary to disambiguate it needs to be specified. In this case, (fetch (MSG HEADER) of X) is sufficient. The record package interprets a data path by performing a tree search among all current record declarations for a path from each name to the next, considering first local declarations (see Chapter 21) and then global ones. The central point of separate declarations is that the (sub)record is not tied to another record (as with embedded declarations), and therefore can be used in many different contexts. If a data-path rather than a single field is ambiguous, (e.g., if there were yet another declaration (RECORD TO (NAME . HEADER)) and you specified (fetch (MSG HEADER) of X)), the error AMBIGUOUS DATA PATH is generated. FETCH and REPLACE forms are translated using the CLISP declarations in effect (see Chapter 21). FFETCH and FREPLACE are versions which insure fast CLISP declarations will be in effect, /REPLACE insures undoable declarations. CREATE 1 Record operations can be applied to arbitrary structures, i.e., you can explicitely create a data structure (using CONS, etc), and then manipulate it with FETCH and REPLACE. However, to be consistant with the idea of data abstraction, new data should be created using the same declarations that define its data paths. This can be done with an expression of the form: (create RECORD-NAME . ASSIGNMENTS) A CREATE expression translates into an appropriate Interlisp form using CONS, LIST, PUTHASH, ARRAY, etc., that creates the new datum with the various fields initialized to the appropriate values. ASSIGNMENTS is optional and may contain expressions of the following form: FIELD-NAME FORM Specifies initial value for FIELD-NAME. USING FORM Specifies that for all fields not explicitly given a value, the value of the corresponding field in FORM is to be used. COPYING FORM Similar to USING except the corresponding values are copied (with COPYALL). REUSING FORM Similar to USING, except that wherever possible, the corresponding structure in FORM is used. SMASHING FORM A new instance of the record is not created at all; rather, the value of FORM is used and smashed. The record package goes to great pains to insure that the order of evaluation in the translation is the same as that given in the original CREATE expression if the side effects of one expression might affect the evaluation of another. For example, given the declaration (RECORD CONS (CAR . CDR)), the expression (create CONS CDRX CARY) will translate to (CONS Y X), but (create CONS CDR(FOO) CAR(FIE)) will translate to ((LAMBDA ($$1) (CONS (PROGN (SETQ $$1 (FOO)) (FIE)) $$1))) because FOO might set some variables used by FIE. Note that (create RECORD REUSING FORM ...) does not itself do any destructive operations on the value of FORM. The distinction between USING and REUSING is that (create RECORD reusing FORM ...) will incorporate as much as possible of the old data structure into the new one being created, while (create RECORD using FORM ...) will create a completely new data structure, with only the contents of the fields re-used. For example, REUSING a PROPRECORD just CONSes the new property names and values onto the list, while USING copies the top level of the list. Another example of this distinction occurs when a field is elaborated by a subdeclaration (see the Record Declarations section below): USING will create a new instance of the sub-record, while REUSING will use the old contents of the field (unless some field of the subdeclaration is assigned in the CREATE expression.) If the value of a field is neither explicitly specified, nor implicitly specified via USING, COPYING or REUSING, the default value in the declaration is used, if any, otherwise NIL. (For BETWEEN fields in DATATYPE records, N1 is used; for other non-pointer fields zero is used.) For example, following (RECORD A (B C D) D 3) (create A BT) ==> (LIST T NIL 3) (create A BT using X) ==> (LIST T (CADR X) (CADDR X)) (create A BT copying X)) ==> [LIST T (COPYALL (CADR X)) (COPYALL (CADDR X] (create A BT reusing X) ==> (CONS T (CDR X)) TYPE? 1 The record package allows you to test if a given datum "looks like" an instance of a record. This can be done via an expression of the form (type? RECORD-NAME FORM) TYPE? is mainly intended for records with a record type of DATATYPE or TYPERECORD. For DATATYPEs, the TYPE? check is exact; i.e. the TYPE? expression will return non-NIL only if the value of FORM is an instance of the record named by RECORD-NAME. For TYPERECORDs, the TYPE? expression will check that the value of FORM is a list beginning with RECORD-NAME. For ARRAYRECORDs, it checks that the value is an array of the correct size. For PROPRECORDs and ASSOCRECORDs, a TYPE? expression will make sure that the value of FORM is a property/association list with property names among the field-names of the declaration. There is no built-in type test for records of type ACCESSFNS, HASHLINK or RECORD. Type tests can be defined for these kinds of records, or redefined for the other kinds, by including an expression of the form (TYPE? COM) in the record declaration (see the Record Declarations section below). Attempting to execute a TYPE? expression for a record that has no type test causes an error, TYPE? NOT IMPLEMENTED FOR THIS RECORD. WITH 1 Often one wants to write a complex expression that manipulates several fields of a single record. The WITH construct can make it easier to write such expressions by allowing one to refer to the fields of a record as if they were variables within a lexical scope: (with RECORD-NAME RECORD-INSTANCE FORM1 ... FORMN) RECORD-NAME is the name of a record, and RECORD-INSTANCE is an expression which evaluates to an instance of that record. The expressions FORM1 ... FORMN are evaluated so that references to variables which are field-names of RECORD-NAME are implemented via FETCH and SETQs of those variables are implemented via REPLACE. For example, given (RECORD RECN (FLD1 FLD2)) (SETQ INST (create RECN FLD1 10 FLD2 20)) Then the construct (with RECN INST (SETQ FLD2 (PLUS FLD1 FLD2] is equivalent to (replace FLD2 of INST with (PLUS (fetch FLD1 of INST) (fetch FLD2 of INST] Warning: WITH is implemented by doing simple substitutions in the body of the forms, without regard for how the record fields are used. This means, for example, if the record FOO is defined by (RECORD FOO (POINTER1 POINTER2)), then the form (with FOO X (SELECTQ Y (POINTER1 POINTER1) NIL] will be translated as (SELECTQ Y ((CAR X) (CAR X)) NIL] Be careful that record field names are not used except as variables in the WITH forms. Record Declarations 1 A record is defined by evaluating a record declaration, which is an expression of the form: (RECORD-TYPE RECORD-NAME RECORD-FIELDS . RECORD-TAIL) RECORD-TYPE specifies the "type" of data being described by the record declaration, and thereby implicitly specifies how the corresponding access/storage operations are performed. The different record types are described below. RECORD-NAME is a litatom used to identify the record declaration for creating instances of the record via CREATE, testing via TYPE?, and dumping to files via the RECORDS file package command (see Chapter 17). DATATYPE and TYPERECORD declarations also use RECORD-NAME to identify the data structure (as described below). RECORD-FIELDS describes the structure of the record. Its exact interpretation varies with RECORD-TYPE. For most record types it defines the names of the fields within the record that can be accessed with FETCH and REPLACE. RECORD-TAIL is an optional list that can be used to specify default values for record fields, special CREATE and TYPE? forms, and subdeclarations (described below). Normally, record declaration forms are typed in to the top-level executive or read from a file, and they define the structure of the record globally. Local record declarations within the context of a function are defined by including a record declaration form in the CLISP declaration for the function, rather than evaluating the expression itself (see Chapter 21). Note: Although record declarations are evaluatable forms, and thus can be included in functions, changing a record declaration dynamically (at run-time) is not recommended. When a FETCH or REPLACE operation is interpreted, and the record declaration has changed, the form has to be retranslated. If a function containing FETCH or REPLACE operations has been compiled, it may be necessary to re-compile. For applications which need to change record declarations dynamically, consider using more flexible data structures, such as association lists or property lists. Record Types Records can be used to describe a large variety of data objects, that are manipulated in different ways. The RECORD-TYPE field of the record declaration specifies how the data object is created, and how the various record fields are accessed. Depending on the record type, the record fields may be stored in a list, or in an array, or on the property list of a litatom. The following record types are defined: RECORD(RECORD (Record Type) NIL NIL NIL NIL) [Record Type] The RECORD record type is used to describe list structures. RECORD-FIELDS is interpreted as a list structure whose non-NIL literal atoms are taken as field-names to be associated with the corresponding elements and tails of a list structure. For example, with the record declaration (RECORD MSG (FROM TO . TEXT)), (fetch FROM of X) translates as (CAR X). NIL can be used as a place marker to fill an unnamed field, e.g., (A NIL B) describes a three element list, with B corresponding to the third element. A number may be used to indicate a sequence of NILs, e.g. (A 4 B) is interpreted as (A NIL NIL NIL NIL B). TYPERECORD (TYPERECORD (Record Type) NIL NIL NIL NIL) [Record Type] The TYPERECORD record type is similar to RECORD, except that the record name is added to the front of the list structure to signify what "type" of record it is. This type field is used by the record package in the translation of TYPE? expressions. CREATE will insert an extra field containing RECORD-NAME at the beginning of the structure, and the translation of the access and storage functions will take this extra field into account. For example, for (TYPERECORD MSG (FROM TO . TEXT)), (fetch FROM of X) translates as (CADR X), not (CAR X). ASSOCRECORD(ASSOCRECORD (Record Type) NIL NIL NIL NIL) [Record Type] The ASSOCRECORD record type is used to describe list structures where the fields are stored in association list format: ((FIELDNAME1 . VALUE1) (FIELDNAME2 . VALUE2) ...) RECORD-FIELDS is a list of literal atoms, interpreted as the permissable list of field names in the association list. Accessing is performed with ASSOC (or FASSOC, depending on current CLISP declarations, see Chapter 21), storing with PUTASSOC. PROPRECORD(PROPRECORD (Record Type) NIL NIL NIL NIL) [Record Type] The PROPRECORD record type is used to describe list structures where the fields are stored in property list format: (FIELDNAME1 VALUE1 FIELDNAME2 VALUE2 ...) RECORD-FIELDS is a list of literal atoms, interpreted as the permissable list of field names in the property list. Accessing is performed with LISTGET, storing with LISTPUT. Both ASSOCRECORD and PROPRECORD are useful for defining data structures in which it is often the case that many of the fields are NIL. A CREATE expression for these record types only stores those fields which are non-NIL. Note, however, that with the record declaration (PROPRECORD FIE (H I J)) the expression (create FIE) would still construct (H NIL), since a later operation of (replace J of X with Y) could not possibly change the instance of the record if it were NIL. ARRAYRECORD(ARRAYRECORD (Record Type) NIL NIL NIL NIL) [Record Type] The ARRAYRECORD record type is used to describe arrays. RECORD-FIELDS is interpreted as a list of field names that are associated with the corresponding elements of an array. NIL can be used as a place marker for an unnamed field (element). Positive integers can be used as abbreviation for the corresponding number of NILs. For example, (ARRAYRECORD (ORG DEST NIL ID 3 TEXT)) describes an eight element array, with ORG corresponding to the first element, ID to the fourth, and TEXT to the eighth. Note that ARRAYRECORD only creates arrays of pointers. Other kinds of arrays must be implemented with the ACCESSFNS record type (see below). HASHLINK(HASHLINK (Record Type) NIL NIL NIL NIL) [Record Type] The HASHLINK record type can be used with any type of data object: it specifies that the value of a single field can be accessed by hashing the data object in a given hash array. Since the HASHLINK record type describes an accessing method, rather than a data structure, the CREATE expression is meaningless for HASHLINK records. RECORD-FIELDS is either an atom FIELD-NAME, or a list (FIELD-NAME HARRAYNAME HARRAYSIZE). HARRAYNAME is a variable whose value is the hash array to be used; if not given, SYSHASHARRAY is used. If the value of the variable HARRAYNAME is not a hash array (at the time of the record declaration), it will be set to a new hash array with a size of HARRAYSIZE. HARRAYSIZE defaults to 100. The HASHLINK record type is useful as a subdeclaration to other records to add additional fields to already existing data structures (see the Optional Record Specifications section below). For example, suppose that FOO is a record declared with (RECORD FOO (A B C)). To add an aditional field BAR, without modifying the already existing data strutures, redeclare FOO with: (RECORD FOO (A B C) (HASHLINK FOO (BAR BARHARRAY))) Now, (fetch BAR of X) will translate into (GETHASH X BARHARRAY), hashing off the existing list X. ATOMRECORD(ATOMRECORD (Record Type) NIL NIL NIL NIL) [Record Type] The ATOMRECORD record type is used to describe property lists of litatoms. RECORD-FIELDS is a list of property names. Accessing is performed with GETPROP, storing with PUTPROP. The CREATE expression is not initially defined for ATOMRECORD records. DATATYPE(DATATYPE (Record Type) NIL NIL NIL NIL) [Record Type] The DATATYPE record type is used to define a new user data type with type name RECORD-NAME (by calling DECLAREDATATYPE, see the Built-In and User Data Types section below). Unlike other record types, the records of a DATATYPE declaration are represented with a completely new Interlisp type, and not in terms of other existing types. RECORD-FIELDS is interpreted as a list of field specifications, where each specification is either a list (FIELDNAME FIELDTYPE), or an atom FIELDNAME. If FIELDTYPE is omitted, it defaults to POINTER. Possible values for FIELDTYPE are: POINTER Field contains a pointer to any arbitrary Interlisp object. INTEGER FIXP Field contains a signed integer. Note that an INTEGER field is not capable of holding everything that satisfies FIXP, such as bignums (see Chapter 7). FLOATING FLOATP Field contains a floating point number. SIGNEDWORD Field contains a 16-bit signed integer. FLAG Field is a one bit field that "contains" T or NIL. BITS N Field contains an N-bit unsigned integer. BYTE Equivalent to BITS 8. WORD Equivalent to BITS 16. XPOINTER Field contains a pointer like POINTER, except that the field is not reference counted by the Interlisp-D garbage collector. XPOINTER fields are useful for implementing back-pointers in structures that would be circular and not otherwise collected by the reference-counting garbage collector. Warning: XPOINTER fields should be used with great care. It is possible to damage the integrity of the storage allocation system by using pointers to objects that have been garbage collected. Code that uses XPOINTER fields should be sure that the objects pointed to have not been garbage collected. This can be done in two ways: The first is to maintain the object in a global structure, so that it is never garbage collected until explicitly deleted from the structure, at which point the program must invalidate all the XPOINTER fields of other objects pointing at it. The second is to declare the object as a DATATYPE beginning with a POINTER field that the program maintains as a pointer to an object of another type (e.g., the object containing the XPOINTER pointing back at it), and test that field for reasonableness whenever using the contents of the XPOINTER field. For example, the declaration (DATATYPE FOO ((FLG BITS 12) TEXT HEAD (DATE BITS 18) (PRIO FLOATP) (READ? FLAG))) would define a data type FOO with two pointer fields, a floating point number, and fields for a 12 and 18 bit unsigned integers, and a flag (one bit). Fields are allocated in such a way as to optimize the storage used and not necessarily in the order specified. Generally, a DATATYPE record is much more storage compact than the corresponding RECORD structure would be; in addition, access is faster. Since the user data type must be set up at run-time, the RECORDS file package command will dump a DECLAREDATATYPE expression as well as the DATATYPE declaration itself. If the record declaration is otherwise not needed at runtime, it can be kept out of the compiled file by using a (DECLARE: DONTCOPY --) expression (see Chapter 17), but it is still necessary to ensure that the datatype is properly initialized. For this, one can use the INITRECORDS file package command (see Chapter 17), which will dump only the DECLAREDATATYPE expression. Note: When defining a new data type, it is sometimes useful to call the function DEFPRINT (see Chapter 25) to specify how instances of the new data type should be printed. This can be specified in the record declaration by including an INIT record specification (see the Optional Record Specifications section below), e.g. (DATATYPE QV.TYPE ... (INIT (DEFPRINT 'QV.TYPE (FUNCTION PRINT.QV.TYPE)))). Note: DATATYPE declarations cannot be used within local record declarations (see Chapter 21). BLOCKRECORD(BLOCKRECORD (Record Type) NIL NIL NIL NIL) [Record Type] The BLOCKRECORD record type is used in low-level system programming to "overlay" an organized structure over an arbitrary piece of "unboxed" storage. RECORD-FIELDS is interpreted exactly as with a DATATYPE declaration, except that fields are not automatically rearranged to maximize storage efficiency. Like an ACCESSFNS record, a BLOCKRECORD does not have concrete instances; it merely provides a way of interpreting some existing block of storage. Thus, one cannot create an instance of a BLOCKRECORD (unless the declaration includes an explicit CREATE expression), nor is there a default type? expression for a BLOCKRECORD. Warning: The programmer should exercise caution in using BLOCKRECORD declarations, as they enable one to write expressions that fetch and store arbitrary data in arbitrary locations, thereby evading the normal type system. Except in very specialized situations, a BLOCKRECORD should never contain POINTER or XPOINTER fields, nor be used to overlay an area of storage that contains pointers. Such use could compromise the garbage collector and storage allocation system. The programmer is responsible for ensuring that all FETCH and REPLACE expressions are performed only on suitable objects, as no type testing is performed. A typical use for the BLOCKRECORD type in user code is to overlay a non-pointer portion of an existing DATATYPE. For this use, the LOCF macro is useful. (LOCF (fetch FIELD of DATUM)) can be used to refer to the storage that begins at the first word that contains FIELD of DATUM. For example, to define a new kind of Ethernet packet (see Chapter 31), one could overlay the "body" portion of the ETHERPACKET datatype declaration as follows: (ACCESSFNS MYPACKET ((MYBASE (LOCF (fetch (ETHERPACKET EPBODY) of DATUM)))) (BLOCKRECORD MYBASE ((MYTYPE WORD) (MYLENGTH WORD) (MYSTATUS BYTE) (MYERRORCODE BYTE) (MYDATA INTEGER))) (TYPE? (type? ETHERPACKET DATUM))) With this declaration in effect, the expression (fetch MYLENGTH of PACKET) would retrieve the second 16-bit field beyond the offset inside PACKET where the EPBODY field starts. For more examples, see the EtherRecords library package. ACCESSFNS(ACCESSFNS (Record Type) NIL NIL NIL NIL) [Record Type] The ACCESSFNS record type is used to define data structures with user-defined access functions. For each field name, you specify how it is to be accessed and set. This allows the use of the record package with arbitrary data structures, with complex access methods. RECORD-FIELDS is interpreted as a list of elements of the form (FIELD-NAME ACCESSDEF SETDEF). ACCESSDEF should be a function of one argument, the datum, and will be used for accessing the value of the field. SETDEF should be a function of two arguments, the datum and the new value, and will be used for storing a new value in a field. SETDEF may be omitted, in which case, no storing operations are allowed. ACCESSDEF and/or SETDEF may also be a form written in terms of variables DATUM and (in SETDEF) NEWVALUE. For example, given the declaration [ACCESSFNS FOO ((FIRSTCHAR (NTHCHAR DATUM 1) (RPLSTRING DATUM 1 NEWVALUE)) (RESTCHARS (SUBSTRING DATUM 2] (replace (FOO FIRSTCHAR) of X with Y) would translate to (RPLSTRING X 1 Y). Since no SETDEF is given for the RESTCHARS field, attempting to perform (replace (FOO RESTCHARS) of X with Y) would generate an error, REPLACE UNDEFINED FOR FIELD. Note that ACCESSFNS do not have a CREATE definition. However, you may supply one in the defaults or subdeclarations of the declaration, as described below. Attempting to CREATE an ACCESSFNS record without specifying a create definition will cause an error CREATE NOT DEFINED FOR THIS RECORD. ACCESSDEF and SETDEF can also be a property list which specify FAST, STANDARD and UNDOABLE versions of the ACCESSFNS forms, e.g. [ACCESSFNS LITATOM ((DEF (STANDARD GETD FAST FGETD) (STANDARD PUTD UNDOABLE /PUTD] means if FAST declaration is in effect, use FGETD for fetching, if UNDOABLE, use /PUTD for saving (see CLISP declarations, see Chapter 21). Note: SETDEF forms should be written so that they return the new value, to be consistant with REPLACE operations for other record types. The REPLACE record operator does not enforce this, though. The ACCESSFNS facility allows the use of data structures not specified by one of the built-in record types. For example, one possible representation of a data structure is to store the fields in parallel arrays, especially if the number of instances required is known, and they do not need to be garbage collected. Thus, to implement a data structure called LINK with two fields FROM and TO, one would have two arrays FROMARRAY and TOARRAY. The representation of an "instance" of the record would be an integer which is used to index into the arrays. This can be accomplished with the declaration: [ACCESSFNS LINK ((FROM (ELT FROMARRAY DATUM) (SETA FROMARRAY DATUM NEWVALUE)) (TO (ELT TOARRAY DATUM) (SETA TOARRAY DATUM NEWVALUE))) (CREATE (PROG1 (SETQ LINKCNT (ADD1 LINKCNT)) (SETA FROMARRAY LINKCNT FROM) (SETA TOARRAY LINKCNT TO))) (INIT (PROGN (SETQ FROMARRAY (ARRAY 100)) (SETQ TOARRAY (ARRAY 100)) (SETQ LINKCNT 0)] To create a new LINK, a counter is incremented and the new elements stored. (Note: The CREATE form given the declaration probably should include a test for overflow.) Optional Record Specifications After the RECORD-FIELDS item in a record declaration expression there can be an arbitrary number of additional expressions in RECORD-TAIL. These expressions can be used to specify default values for record fields, special CREATE and TYPE? forms, and subdeclarations. The following expressions are permitted: FIELD-NAME FORM Allows you to specify within the record declaration the default value to be stored in FIELD-NAME by a CREATE (if no value is given within the CREATE expression itself). Note that FORM is evaluated at CREATE time, not when the declaration is made. (CREATE FORM) Defines the manner in which CREATE of this record should be performed. This provides a way of specifying how ACCESSFNS should be created or overriding the usual definition of CREATE. If FORM contains the field-names of the declaration as variables, the forms given in the CREATE operation will be substituted in. If the word DATUM appears in the create form, the original CREATE definition is inserted. This effectively allows you to "advise" the create. Note: (CREATE FORM) may also be specified as "RECORD-NAME FORM". (INIT FORM) Specifies that FORM should be evaluated when the record is declared. FORM will also be dumped by the INITRECORDS file package command (see Chapter 17). For example, see the example of an ACCESSFNS record declaration above. In this example, FROMARRAY and TOARRAY are initialized with an INIT form. (TYPE? FORM) Defines the manner in which TYPE? expressions are to be translated. FORM may either be an expression in terms of DATUM or a function of one argument. (SUBRECORD NAME . DEFAULTS) NAME must be a field that appears in the current declaration and the name of another record. This says that, for the purposes of translating CREATE expressions, substitute the top-level declaration of NAME for the SUBRECORD form, adding on any defaults specified. For example: Given (RECORD B (E F G)), (RECORD A (B C D) (SUBRECORD B)) would be treated like (RECORD A (B C D) (RECORD B (E F G))) for the purposes of translating CREATE expressions. a subdeclaration If a record declaration expression occurs among the record specifications of another record declaration, it is known as a "subdeclaration." Subdeclarations are used to declare that fields of a record are to be interpreted as another type of record, or that the record data object is to be interpreted in more than one way. The RECORD-NAME of a subdeclaration must be either the RECORD-NAME of its immediately superior declaration or one of the superior's field-names. Instead of identifying the declaration as with top level declarations, the record-name of a subdeclaration identifies the parent field or record that is being described by the subdeclaration. Subdeclarations can be nested to an arbitrary depth. Giving a subdeclaration (RECORD NAME1 NAME2) is a simple way of defining a synonym for the field NAME1. It is possible for a given field to have more than one subdeclaration. For example, in (RECORD FOO (A B) (RECORD A (C D)) (RECORD A (Q R))) (Q R) and (C D) are "overlayed," i.e. (fetch Q of X) and (fetch C of X) would be equivalent. In such cases, the first subdeclaration is the one used by CREATE. (SYNONYM FIELD (SYN1 ... SYNN)) FIELD must be a field that appears in the current declaration. This defines SYN1 ... SYNN all as synonyms of FIELD. If there is only one synonym, this can be written as (SYNONYM FIELD SYN). (SYSTEM) If (SYSTEM) is included in a record declaration, this indicates that the record is a "system" record rather than a "user" record. The only distinction between the two types of records is that "user" record declarations take precedence over "system" record declarations, in cases where an unqualified field name would be considered ambiguous. All of the records defined in the standard Interlisp-D system are defined as system records. Defining New Record Types 1 In addition to the built-in record types, you can declare your own record types by performing the following steps: 1. Add the new record-type to the value of CLISPRECORDTYPES. 2. Perform (MOVD 'RECORD RECORD-TYPE), i.e. give the record-type the same definition as that of the function RECORD. 3. Put the name of a function which will return the translation on the property list of RECORD-TYPE, as the value of the property USERRECORDTYPE. Whenever a record declaration of type RECORD-TYPE is encountered, this function will be passed the record declaration as its argument, and should return a new record declaration which the record package will then use in its place. Record Manipulation Functions 1 (EDITREC(EDITREC (Function) NIL NIL NIL NIL) NAME COM1 ... COMN) [NLambda NoSpread Function] EDITREC calls the editor on a copy of all declarations in which NAME is the record name or a field name. On exit, it redeclares those that have changed and undeclares any that have been deleted. If NAME is NIL, all declarations are edited. COM1 ... COMN are (optional) edit commands. When you redeclare a global record, the translations of all expressions involving that record or any of its fields are automatically deleted from CLISPARRAY, and thus will be recomputed using the new information. If you change a local record declaration (see Chapter 21), or change some other CLISP declaration (see Chapter 21), e.g., STANDARD to FAST, and wish the new information to affect record expressions already translated, you must make sure the corresponding translations are removed, usually either by CLISPIFYing or using the DW edit macro. (RECLOOK(RECLOOK (Function) NIL NIL NIL NIL) RECNAME % % % %) [Function] Returns the entire declaration for the record named RECNAME; NIL if there is no record declaration with name RECNAME. Note that the record package maintains internal state about current record declarations, so performing destructive operations (e.g. NCONC) on the value of RECLOOK may leave the record package in an inconsistent state. To change a record declaration, use EDITREC. (FIELDLOOK(FIELDLOOK (Function) NIL NIL NIL NIL) FIELDNAME) [Function] Returns the list of declarations in which FIELDNAME is the name of a field. (RECORDFIELDNAMES(RECORDFIELDNAMES (Function) NIL NIL NIL NIL) RECORDNAME %) [Function] Returns the list of fields declared in record RECORDNAME. RECORDNAME may either be a name or an entire declaration. (RECORDACCESS(RECORDACCESS (Function) NIL NIL NIL NIL) FIELD DATUM DEC TYPE NEWVALUE) [Function] TYPE is one of FETCH, REPLACE, FFETCH, FREPLACE, /REPLACE or their lowercase equivalents. TYPE=NIL means FETCH. If TYPE corresponds to a fetch operation, i.e. is FETCH, or FFETCH, RECORDACCESS performs (TYPE FIELD of DATUM). If TYPE corresponds to a replace, RECORDACCESS performs (TYPE FIELD of DATUM with NEWVALUE). DEC is an optional declaration; if given, FIELD is interpreted as a field name of that declaration. Note that RECORDACCESS is relatively inefficient, although it is better than constructing the equivalent form and performing an EVAL. (RECORDACCESSFORM(RECORDACCESS (Function) NIL NIL NIL NIL) FIELD DATUM TYPE NEWVALUE) [Function] Returns the form that would be compiled as a result of a record access. TYPE is one of FETCH, REPLACE, FFETCH, FREPLACE, /REPLACE or their lowercase equivalents. TYPE=NIL means FETCH. Changetran 1 A very common programming construction consists of assigning a new value to some datum that is a function of the current value of that datum. Some examples of such read-modify-write sequences include: Incrementing a counter: (SETQ X (IPLUS X 1)) Pushing an item on the front of a list: (SETQ X (CONS Y X)) Popping an item off a list: (PROG1 (CAR X) (SETQ X (CDR X))) It is easier to express such computations when the datum in question is a simple variable as above than when it is an element of some larger data structure. For example, if the datum to be modified was (CAR X), the above examples would be: (CAR (RPLACA X (IPLUS (CAR X) 1))) (CAR (RPLACA X (CONS Y (CAR X))) (PROG1 (CAAR X) (RPLACA X (CDAR X))) and if the datum was an element in an array, (ELT A N), the examples would be: (SETA A N (IPLUS (ELT A N) 1))) (SETA A N (CONS Y (ELT A N)))) (PROG1 (CAR (ELT A N)) (SETA A N (CDR (ELT A N)))) The difficulty in expressing (and reading) modification idioms is in part due to the well-known asymmetry of setting versus accessing operations on structures: RPLACA is used to set what CAR would return, SETA corresponds to ELT, and so on. The "Changetran" facility is designed to provide a more satisfactory notation in which to express certain common (but user-extensible) structure modification operations. Changetran defines a set of CLISP words that encode the kind of modification that is to take place, e.g. pushing on a list, adding to a number, etc. More important, the expression that indicates the datum whose value is to be modified needs to be stated only once. Thus, the "change word" ADD is used to increase the value of a datum by the sum of a set of numbers. Its arguments are an expression denoting the datum, and a set of items to be added to its current value. The datum expression must be a variable or an accessing expression (envolving FETCH, CAR, LAST, ELT, etc) that can be translated to the appropriate setting expression. For example, (ADD (CADDR X) (FOO)) is equivalent to: (CAR (RPLACA (CDDR X) (PLUS (FOO) (CADDR X))) If the datum expression is a complicated form involving subsidiary function calls, such as (ELT (FOO X) (FIE Y))), Changetran goes to some lengths to make sure that those subsidiary functions are evaluated only once (it binds local variables to save the results), even though they logically appear in both the setting and accessing parts of the translation. Thus, in thinking about both efficiency and possible side effects, you can rely on the fact that the forms will be evaluated only as often as they appear in the expression. For ADD and all other changewords, the lowercase version (add, etc.) may also be specified. Like other CLISP words, change words are translated using all CLISP declarations in effect (see Chapter 21). The following is a list of those change words recognized by Changetran. Except for POP, the value of all built-in changeword forms is defined to be the new value of the datum. (ADD(ADD (Change Word) NIL NIL NIL NIL) DATUM ITEM1 ITEM2 ...) [Change Word] Adds the specified items to the current value of the datum, stores the result back in the datum location. The translation will use IPLUS, PLUS, or FPLUS according to the CLISP declarations in effect (see Chapter 21). (PUSH(PUSH (Change Word) NIL NIL NIL NIL) DATUM ITEM1 ITEM2 ...) [Change Word] CONSes the items onto the front of the current value of the datum, and stores the result back in the datum location. For example, (PUSH X A B) would translate as (SETQ X (CONS A (CONS B X))). (PUSHNEW(PUSH (Change Word) NIL NIL NIL NIL) DATUM ITEM) [Change Word] Like PUSH (with only one item) except that the item is not added if it is already FMEMB of the datum's value. Note that, whereas (CAR (PUSH X 'FOO)) will always be FOO, (CAR (PUSHNEW X 'FOO)) might be something else if FOO already existed in the middle of the list. (PUSHLIST(PUSHLIST (Change Word) NIL NIL NIL NIL) DATUM ITEM1 ITEM2 ...) [Change Word] Similar to PUSH, except that the items are APPENDed in front of the current value of the datum. For example, (PUSHLIST X A B) translates as (SETQ X (APPEND A B X)). (POP(POP (Change Word) NIL NIL NIL NIL) DATUM) [Change Word] Returns CAR of the current value of the datum after storing its CDR into the datum. The current value is computed only once even though it is referenced twice. Note that this is the only built-in changeword for which the value of the form is not the new value of the datum. (SWAP(SWAP (Change Word) NIL NIL NIL NIL) DATUM1 DATUM2) [Change Word] Sets DATUM1 to DATUM2 and vice versa. (CHANGE(CHANGE (Change Word) NIL NIL NIL NIL) DATUM FORM) [Change Word] This is the most flexible of all change words, since it enables you to provide an arbitrary form describing what the new value should be, but it still highlights the fact that structure modification is to occur, and still enables the datum expression to appear only once. CHANGE sets DATUM to the value of FORM*, where FORM* is constructed from FORM by substituting the datum expression for every occurrence of the litatom DATUM. For example, (CHANGE (CAR X) (ITIMES DATUM 5)) translates as (CAR (RPLACA X (ITIMES (CAR X) 5))). CHANGE is useful for expressing modifications that are not built-in and are not sufficiently common to justify defining a user-changeword. As for other changeword expressions, you need not repeat the datum-expression and need not worry about multiple evaluation of the accessing form. You can define new change words. To define a change word, say sub, that subtracts items from the current value of the datum, you must put the property CLISPWORD, value (CHANGETRAN . sub) on both the upper- and lowercase versions of sub: (PUTPROP 'SUB 'CLISPWORD '(CHANGETRAN . sub)) (PUTPROP 'sub 'CLISPWORD '(CHANGETRAN . sub)) Then, you must put (on the lower-case version of sub only) the property CHANGEWORD, with value FN. FN is a function that will be applied to a single argument, the whole sub form, and must return a form that Changetran can translate into an appropriate expression. This form should be a list structure with the atom DATUM used whenever you want an accessing expression for the current value of the datum to appear. The form (DATUM FORM) (note that DATUM is a single atom) should occur once in the expression; this specifies that an appropriate storing expression into the datum should occur at that point. For example, sub could be defined with: (PUTPROP 'sub 'CHANGEWORD '(LAMBDA (FORM) (LIST 'DATUM (LIST 'IDIFFERENCE 'DATUM (CONS 'IPLUS (CDDR FORM)))))) If the expression (sub (CAR X) A B) were encountered, the arguments to SUB would first be dwimified, and then the CHANGEWORD function would be passed the list (sub (CAR X) A B), and return (DATUM (IDIFFERENCE DATUM (IPLUS A B))), which Changetran would convert to (CAR (RPLACA X (IDIFFERENCE (CAR X) (IPLUS A B)))). Note: The sub changeword as defined above will always use IDIFFERENCE and IPLUS; add uses the correct addition operation depending on the current CLISP declarations (see Chapter 21). Built-In and User Data Types 1 Interlisp is a system for the manipulation of various kinds of data; it provides a large set of built-in data types, which may be used to represent a variety of abstract objects, and you can also define additional "user data types" which can be manipulated exactly like built-in data types. Each data type in Interlisp has an associated "type name," a litatom. Some of the type names of built-in data types are: LITATOM, LISTP, STRINGP, ARRAYP, STACKP, SMALLP, FIXP, and FLOATP. For user data types, the type name is specified when the data type is created. (DATATYPES(DATATYPES (Function) NIL NIL NIL NIL) %) [Function] Returns a list of all type names currently defined. (USERDATATYPES(USERDATATYPES (Function) NIL NIL NIL NIL)) [Function] Returns list of names of currently declared user data types. (TYPENAME(TYPENAME (Function) NIL NIL NIL NIL) DATUM) [Function] Returns the type name for the data type of DATUM. (TYPENAMEP(TYPENAMEP (Function) NIL NIL NIL NIL) DATUM TYPE) [Function] Returns T if DATUM is an object with type name equal to TYPE, otherwise NIL. Note: TYPENAME and TYPENAMEP distinguish the logical data types ARRAYP, CCODEP and HARRAYP, even though they may be implemented as ARRAYPs in some Interlisp implementations. In addition to built-in data-types such as atoms, lists, arrays, etc., Interlisp provides a way of defining completely new classes of objects, with a fixed number of fields determined by the definition of the data type. In order to define a new class of objects, you must supply a name for the new data type and specifications for each of its fields. Each field may contain either a pointer (i.e., any arbitrary Interlisp datum), an integer, a floating point number, or an N-bit integer. Note: The most convenient way to define new user data types is via DATATYPE record declarations (see Chapter 8) which call the following functions. (DECLAREDATATYPE(DECLAREDATATYPE (Function) NIL NIL NIL NIL) TYPENAME FIELDSPECS % %) [Function] Defines a new user data type, with the name TYPENAME. FIELDSPECS is a list of "field specifications." Each field specification may be one of the following: POINTER Field may contain any Interlisp datum. FIXP Field contains an integer. FLOATP Field contains a floating point number. (BITS N) Field contains a non-negative integer less than 2N. BYTE Equivalent to (BITS 8). WORD Equivalent to (BITS 16). SIGNEDWORD Field contains a 16 bit signed integer. DECLAREDATATYPE returns a list of "field descriptors," one for each element of FIELDSPECS. A field descriptor contains information about where within the datum the field is actually stored. If FIELDSPECS is NIL, TYPENAME is "undeclared." If TYPENAME is already declared as a data type, it is undeclared, and then re-declared with the new FIELDSPECS. An instance of a data type that has been undeclared has a type name of **DEALLOC**. (FETCHFIELD(FETCHFIELD (Function) NIL NIL NIL NIL) DESCRIPTOR DATUM) [Function] Returns the contents of the field described by DESCRIPTOR from DATUM. DESCRIPTOR must be a "field descriptor" as returned by DECLAREDATATYPE or GETDESCRIPTORS. If DATUM is not an instance of the datatype of which DESCRIPTOR is a descriptor, causes error DATUM OF INCORRECT TYPE. (REPLACEFIELD(REPLACEFIELD (Function) NIL NIL NIL NIL) DESCRIPTOR DATUM NEWVALUE) [Function] Store NEWVALUE into the field of DATUM described by DESCRIPTOR. DESCRIPTOR must be a field descriptor as returned by DECLAREDATATYPE. If DATUM is not an instance of the datatype of which DESCRIPTOR is a descriptor, causes error DATUM OF INCORRECT TYPE. Value is NEWVALUE. (NCREATE(NCREATE (Function) NIL NIL NIL NIL) TYPE OLDOBJ) [Function] Creates and returns a new instance of datatype TYPE. If OLDOBJ is also a datum of datatype TYPE, the fields of the new object are initialized to the values of the corresponding fields in OLDOBJ. NCREATE will not work for built-in datatypes, such as ARRAYP, STRINGP, etc. If TYPE is not the type name of a previously declared user data type, generates an error, ILLEGAL DATA TYPE. (GETFIELDSPECS(GETFIELDSPECS (Function) NIL NIL NIL NIL) TYPENAME) [Function] Returns a list which is EQUAL to the FIELDSPECS argument given to DECLAREDATATYPE for TYPENAME; if TYPENAME is not a currently declared data-type, returns NIL. (GETDESCRIPTORS(GETDESCRIPTORS (Function) NIL NIL NIL NIL) TYPENAME) [Function] Returns a list of field descriptors, EQUAL to the value of DECLAREDATATYPE for TYPENAME. If TYPENAME is not an atom, (TYPENAME TYPENAME) is used. Note that you can define how user data types are to be printed via DEFPRINT (see Chapter 25), how they are to be evaluated by the interpreter via DEFEVAL (see Chapter 10), and how they are to be compiled by the compiler via COMPILETYPELST (see Chapter 18). (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "8-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "8-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "8-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "8-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "8-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "8-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))(5,2llZ2llZ,ll2HHZ2HH +Z2HZZ,,55,,2Hll/HH3HZ +T,ll,ll,ll53$$(T3$$(T,HH +,HH-T3(T,HH,/F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR56T,5,,TITAN +TITAN + HELVETICA CLASSIC +CLASSIC +CLASSIC + HELVETICACLASSIC + HELVETICA HELVETICA +CLASSIC +MODERN +MODERNMODERN +" HRULE.GETFNCLASSIC + " + ! HRULE.GETFNCLASSIC + ! +  +   HRULE.GETFNCLASSIC +  +  HRULE.GETFNCLASSIC +   IM.CHAP.GETFN HELVETICA  HRULE.GETFNMODERN +  #    s  ; ]    +y   d  L  7  HRULE.GETFNMODERN +9         +  +  ;    t    + 2 [ (     -   .   & 8 :        A     A S   F        7      P  F   HRULE.GETFNMODERN +s $      @    c @  +  +  e    2    3   +  J   ~     +    : "   + ?    f j  + :  5 d  V   B    + N      (  :   HRULE.GETFNMODERN +  6  +     '  + )   B +  - ^ 3    a @ %  HRULE.GETFNMODERN +g     R  H   )      , K +    0 "K   HRULE.GETFNMODERN +\        _   )  + 6  N h    [  / o   ~    n $ &IM.INDEX.GETFNMODERN +  3 .  0  ? & U     +*IM.INDEX.GETFNMODERN +  +   '  !     +IM.INDEX.GETFNMODERN +  i       -   +*IM.INDEX.GETFNMODERN +  +f         +c  J 3    A   +IM.INDEX.GETFNMODERN +  * k   &( %    + V  (IM.INDEX.GETFNMODERN +   N   +   +  + + + +G ( +p + +     C   4     +*IM.INDEX.GETFNMODERN +  +> ;   ) + (IM.INDEX.GETFNMODERN +  C  d o  ]          = $  0 ; # $ +#)   +)   *                   :    5 S  m a  ( '%% % %%%   < 4  -  "    A   T  S J ( P  +IM.INDEX.GETFNMODERN +   " % C   . %   9      U  F   Q  v "  + =       (0 A  H  )IM.INDEX.GETFNMODERN +    2  +  j { C   2   &  + ! - "%    %     C "   +      + $ (    6  R ) 0          +  -  * 1 6 4  , * ! D J  + g V  H  + W + "   )   L 9  R 0 !  N       3  ( % -      $ )   +   6  )  4 %!  V  ( F     Y 4   * #     H   8      HRULE.GETFNMODERN +s -    H  Z  ) j I  HRULE.GETFNMODERN +$IM.INDEX.GETFNCLASSIC + 9        +J e  2 ?   "  $IM.INDEX.GETFNCLASSIC +  4  -   ]   +&IM.INDEX.GETFNCLASSIC +  *   "-IM.INDEX.GETFNCLASSIC +  . + +/  )IM.INDEX.GETFNCLASSIC +      "    +    +    +  ' 5  + j  )IM.INDEX.GETFNCLASSIC + I      "      HRULE.GETFNMODERN +  (  !  #!%-   3          E   %[   3  T Z #IM.INDEX.GETFNCLASSIC + +    A $IM.INDEX.GETFNCLASSIC + +    $IM.INDEX.GETFNCLASSIC + +  I      ,  (IM.INDEX.GETFNCLASSIC + +   =   #IM.INDEX.GETFNCLASSIC +  5  $IM.INDEX.GETFNCLASSIC +    &IM.INDEX.GETFNCLASSIC + +      J  ! #  ? V  .  ..   +  D  h      %6 $ ( +#    $ 2  -   b  HRULE.GETFNMODERN +# L a  +        &IM.INDEX.GETFNCLASSIC +  4 *IM.INDEX.GETFN =  %IM.INDEX.GETFNCLASSIC + +   +&IM.INDEX.GETFNCLASSIC + +   &     $   ) % w     ,  O  ,IM.INDEX.GETFNCLASSIC +.  ,  +]  (    )  2     &   & +) @ +f  +   Y +J   'IM.INDEX.GETFNCLASSIC + / +  +-   - +   )IM.INDEX.GETFNCLASSIC +    + ++  - +   $IM.INDEX.GETFNCLASSIC +  /    \  /   /   *IM.INDEX.GETFNCLASSIC +   +   0  +IM.INDEX.GETFNCLASSIC + %       + C G G   Kz \ No newline at end of file diff --git a/docs/porter-irm/vol1/09-conditionals.INTERPRESS b/docs/porter-irm/vol1/09-conditionals.INTERPRESS new file mode 100644 index 00000000..23936bc6 Binary files /dev/null and b/docs/porter-irm/vol1/09-conditionals.INTERPRESS differ diff --git a/docs/porter-irm/vol1/09-conditionals.TEDIT b/docs/porter-irm/vol1/09-conditionals.TEDIT new file mode 100644 index 00000000..5d27b1d5 --- /dev/null +++ b/docs/porter-irm/vol1/09-conditionals.TEDIT @@ -0,0 +1,63 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 9. CONDITIONALS AND ITERATIVE STATEMENTS 1 9. CONDITIONALS AND INTERATIVE STATEMENTS 1 "9"9. CONDITIONALS AND ITERATIVE STATEMENTS 6 In order to do any but the simplest computations, it is necessary to test values and execute expressions conditionally, and to execute a series of expressions. Interlisp supplies a large number of predicates, conditional functions, and control functions. Also, there is a complex "iterative statement" facility which allows you to easily create complex loops and iterative constructs. Data Type Predicates 1 Interlisp provides separate functions for testing whether objects are of certain commonly-used types: (LITATOM(LITATOM (Function) NIL NIL NIL 1) X) [Function] Returns T if X is a litatom; NIL otherwise. Note that a number is not a litatom. (SMALLP(SMALLP (Function) NIL NIL NIL 1) X) [Function] Returns X if X is a small integer; NIL otherwise. (The range of small integers is implementation-dependent. See Chapter 7.) (FIXP(FIXP (Function) NIL NIL NIL 1) X) [Function] Returns X if X is a small or large integer; NIL otherwise. (FLOATP(FLOATP (Function) NIL NIL NIL 1) X) [Function] Returns X if X is a floating point number; NIL otherwise. (NUMBERP(NUMBERP (Function) NIL NIL NIL 1) X) [Function] Returns X if X is a number of any type (FIXP or FLOATP), NIL otherwise. (ATOM(ATOM (Function) NIL NIL NIL 1) X) [Function] Returns T if X is an atom (i.e. a litatom or a number); NIL otherwise. Warning: (ATOM X) is NIL if X is an array, string, etc. In many dialects of Lisp, the function ATOM is defined equivalent to the Interlisp function NLISTP. (LISTP(LISTP (Function) NIL NIL NIL 1) X) [Function] Returns X if X is a list cell, e.g., something created by CONS; NIL otherwise. (NLISTP(NLISTP (Function) NIL NIL NIL 1) X) [Function] (NOT (LISTP X)). Returns T if X is not a list cell, NIL otherwise. (STRINGP(STRINGP (Function) NIL NIL NIL 1) X) [Function] Returns X if X is a string, NIL otherwise. (ARRAYP X) [Function] Returns X if X is an array, NIL otherwise. Note: In some implementations of Interlisp (but not Interlisp-D), ARRAYP(ARRAYP (Function) NIL NIL NIL 1) may also return X if it is of type CCODEP or HARRAYP. (HARRAYP X) [Function] Returns X if it is a hash array object; otherwise NIL. HARRAYP(HARRAYP (Function) NIL NIL NIL 2) returns NIL if X is a list whose CAR is an HARRAYP, even though this is accepted by the hash array functions. Note: The empty list, () or NIL, is considered to be a litatom, rather than a list. Therefore, (LITATOM NIL) = (ATOM NIL) = T and (LISTP NIL) = NIL. Care should be taken when using these functions if the object may be the empty list NIL. Equality Predicates 1 A common operation when dealing with data objects is to test whether two objects are equal. In some cases, such as when comparing two small integers, equality can be easily determined. However, sometimes there is more than one type of equality. For instance, given two lists, one can ask whether they are exactly the same object, or whether they are two distinct lists which contain the same elements. Confusion between these two types of equality is often the source of program errors. Interlisp supplies an extensive set of functions for testing equality: (EQ(EQ (Function) NIL NIL NIL 2) X Y) [Function] Returns T if X and Y are identical pointers; NIL otherwise. EQ should not be used to compare two numbers, unless they are small integers; use EQP instead. (NEQ(NEQ (Function) NIL NIL NIL 2) X Y) [Function] (NOT (EQ X Y)) (NULL(NULL (Function) NIL NIL NIL 2) X) [Function] (NOT(NOT (Function) NIL NIL NIL 2) X) [Function] (EQ X NIL) (EQP(EQP (Function) NIL NIL NIL 2) X Y) [Function] Returns T if X and Y are EQ, or if X and Y are numbers and are equal in value; NIL otherwise. For more discussion of EQP and other number functions, see Chapter 7. EQP also can be used to compare stack pointers (Section 11) and compiled code (Chapter 10). (EQUAL(EQUAL (Function) NIL NIL NIL 2) X Y) [Function] EQUAL returns T if X and Y are one of the following: 1. EQ 2. EQP, i.e., numbers with equal value 3. STREQUAL, i.e., strings containing the same sequence of characters 4. Lists and CAR of X is EQUAL to CAR of Y, and CDR of X is EQUAL to CDR of Y EQUAL returns NIL otherwise. Note that EQUAL can be significantly slower than EQ. A loose description of EQUAL might be to say that X and Y are EQUAL if they print out the same way. (EQUALALL(EQUALALL (Function) NIL NIL NIL 3) X Y) [Function] Like EQUAL, except it descends into the contents of arrays, hash arrays, user data types, etc. Two non-EQ arrays may be EQUALALL if their respective componants are EQUALALL. Logical Predicates 1 (AND X1 X2 ... XN)(AND% X1% X2% ...% XN%) (Function) %(AND% X1% X2% ...% XN%) NIL NIL 3) [NLambda NoSpread Function] Takes an indefinite number of arguments (including zero), that are evaluated in order. If any argument evaluates to NIL, AND immediately returns NIL (without evaluating the remaining arguments). If all of the arguments evaluate to non-NIL, the value of the last argument is returned. (AND)=>T. (OR X1 X2 ... XN)(OR% X1% X2% ...% XN%) (Function) %(OR% X1% X2% ...% XN%) NIL NIL 3) [NLambda NoSpread Function] Takes an indefinite number of arguments (including zero), that are evaluated in order. If any argument is non-NIL, the value of that argument is returned by OR (without evaluating the remaining arguments). If all of the arguments evaluate to NIL, NIL is returned. (OR) => NIL. AND and OR can be used as simple logical connectives, but note that they may not evaluate all of their arguments. This makes a difference if the evaluation of some of the arguments causes side-effects. Another result of this implementation of AND and OR is that they can be used as simple conditional statements. For example: (AND (LISTP X) (CDR X)) returns the value of (CDR X) if X is a list cell, otherwise it returns NIL without evaluating (CDR X). In general, this use of AND and OR should be avoided in favor of more explicit conditional statements in order to make programs more readable. COND Conditional Function 1 (COND CLAUSE1 CLAUSE2 ... CLAUSEK)(COND% CLAUSE1% CLAUSE2% ...% CLAUSEK%) (Function) %(COND% CLAUSE1% CLAUSE2% ...% CLAUSEK%) NIL NIL 3) [NLambda NoSpread Function] The conditional function of Interlisp, COND, takes an indefinite number of arguments, called clauses. Each CLAUSEi is a list of the form (Pi Ci1 ... CiN), where Pi is the predicate, and Ci1 ... CiN are the consequents. The operation of COND can be paraphrased as: IF P1 THEN C11 ... C1N ELSEIF P2 THEN C21 ... C2N ELSEIF P3 ... The clauses are considered in sequence as follows: the predicate P1 of the clause CLAUSEi is evaluated. If the value of P1 is "true" (non-NIL), the consequents Ci1 ... CiN are evaluated in order, and the value of the COND is the value of CiN, the last expression in the clause. If P1 is "false" (EQ to NIL), then the remainder of CLAUSEi is ignored, and the next clause, CLAUSEi+1, is considered. If no Pi is true for any clause, the value of the COND is NIL. Note: If a clause has no consequents, and has the form (Pi), then if Pi evaluates to non-NIL, it is returned as the value of the COND. It is only evaluated once. Example: (DEFINEQ (DOUBLE (X) (COND ((NUMBERP X) (PLUS X X)) ((STRINGP X) (CONCAT X X)) ((ATOM X) (PACK* X X)) (T (PRINT "unknown") X) ((HORRIBLE-ERROR))] (DOUBLE) (DOUBLE 5) 10 (DOUBLE "FOO") "FOOFOO" (DOUBLE 'BAR) BARBAR (DOUBLE '(A B C)) "unknown" (A B C) A few points about this example: Notice that 5 is both a number and an atom, but it is "caught" by the NUMBERP clause before the ATOM clause. Also notice the predicate T, which is always true. This is the normal way to indicate a COND clause which will always be executed (if none of the preceeding clauses are true). (HORRIBLE-ERROR) will never be executed. The IF Statement 1 The IF statement(IF% STATEMENT NIL IF% statement NIL NIL 4) provides a way of way of specifying conditional expressions that is much easier and readable than using the COND function directly. CLISP translates expressions employing IF, THEN, ELSEIF, or ELSE (or their lowercase versions) into equivalent COND expressions. In general, statements of the form: (if AAA then BBB elseif CCC then DDD else EEE) are translated to: (COND (AAA BBB) (CCC DDD) (T EEE) ) The segment between IF or ELSEIF and the next THEN corresponds to the predicate of a COND clause, and the segment between THEN and the next ELSE or ELSEIF as the consequent(s). ELSE is the same as ELSEIF T THEN. These words are spelling corrected using the spelling list CLISPIFWORDSPLST. Lower case versions (if, then, elseif, else) may also be used. If there is nothing following a THEN, or THEN is omitted entirely, then the resulting COND clause has a predicate but no consequent. For example, (if X then elseif ...) and (if X elseif ...) both translate to (COND (X) ...), which means that if X is not NIL, it is returned as the value of the COND. Note that only one expression is allowed as the predicate, but multiple expressions are allowed as the consequents after THEN or ELSE. Multiple consequent expressions are implicitely wrapped in a PROGN, and the value of the last one is returned as the value of the consequent. For example: (if X then (PRINT "FOO") (PRINT "BAR") elseif Y then (PRINT "BAZ")) CLISP considers IF, THEN, ELSE, and ELSEIF to have lower precedence than all infix and prefix operators, as well as Interlisp forms, so it is sometimes possible to omit parentheses around predicate or consequent forms. For example, (if FOO X Y then ...) is equivalent to (if (FOO X Y) then ...), and (if X then FOO X Y else ...) as equivalent to (if X then (FOO X Y) else ...). Essentially, CLISP determines whether the segment between THEN and the next ELSE or ELSEIF corresponds to one form or several and acts accordingly, occasionally interacting with the user to resolve ambiguous cases. Note that if FOO is bound as a variable, (if FOO then ...) is translated as (COND (FOO ...)), so if a call to the function FOO is desired, use (if (FOO) then ...). Selection Functions 1 (SELECTQ X CLAUSE1 CLAUSE2 ... CLAUSEK DEFAULT) [NLambda NoSpread Function] Selects a form or sequence of forms based on the value of X. Each clause CLAUSEi is a list of the form (Si Ci1 ... CiN) where Si is the selection key. The operation of SELECTQ can be paraphrased as: IF X = S1 THEN C11 ... C1N ELSEIF X = S2 THEN ... ELSE DEFAULT If Si is an atom, the value of X is tested to see if it is EQ to Si (which is not evaluated). If so, the expressions Ci1 ... CiN are evaluated in sequence, and the value of the SELECTQ is the value of the last expression evaluated, i.e., CiN. If Si is a list, the value of X is compared with each element (not evaluated) of Si, and if X is EQ to any one of them, then Ci1 ... CiN are evaluated as above. If CLAUSEi is not selected in one of the two ways described, CLAUSEi+1 is tested, etc., until all the clauses have been tested. If none is selected, DEFAULT is evaluated, and its value is returned as the value of the SELECTQ. DEFAULT must be present. An example of the form of a SELECTQ is: [SELECTQ MONTH (FEBRUARY (if (LEAPYEARP) then 29 else 28)) ((APRIL JUNE SEPTEMBER NOVEMBER) 30) 31] If the value of MONTH is the litatom FEBRUARY, the SELECTQ returns 28 or 29 (depending on (LEAPYEARP)); otherwise if MONTH is APRIL, JUNE, SEPTEMBER, or NOVEMBER, the SELECTQ returns 30; otherwise it returns 31. SELECTQ compiles open, and is therefore very fast; however, it will not work if the value of X is a list, a large integer, or floating point number, since SELECTQ uses EQ for all comparisons. Note: SELCHARQ (Chapter 2) is a version of SELECTQ that recognizes CHARCODE litatoms. (SELECTC X CLAUSE1 CLAUSE2 ... CLAUSEK DEFAULT) [NLambda NoSpread Function] "SELECTQ-on-Constant." Similar to SELECTQ except that the selection keys are evaluated, and the result used as a SELECTQ-style selection key. SELECTC is compiled as a SELECTQ, with the selection keys evaluated at compile-time. Therefore, the selection keys act like compile-time constants (see Chapter 18). For example: [SELECTC NUM ( (for X from 1 to 9 collect (TIMES X X)) "SQUARE" ) "HIP"] compiles as: [SELECTQ NUM ( (1 4 9 16 25 36 49 64 81) "SQUARE" ) "HIP"] PROG and Associated Control Functions 1 (PROG1 X1 X2 ... XN) [NLambda NoSpread Function] Evaluates its arguments in order, and returns the value of its first argument X1. For example, (PROG1 X (SETQ X Y)) sets X to Y, and returns X's original value. (PROG2 X1 X2 ... XN) [NoSpread Function] Similar to PROG1. Evaluates its arguments in order, and returns the value of its second argument X2. (PROGN X1 X2 ... XN) [NLambda NoSpread Function] PROGN evaluates each of its arguments in order, and returns the value of its last argument. PROGN is used to specify more than one computation where the syntax allows only one, e.g., (SELECTQ ... (PROGN ...)) allows evaluation of several expressions as the default condition for a SELECTQ. (PROG VARLST E1 E2 ... EN) [NLambda NoSpread Function] This function allows the user to write an ALGOL-like program containing Interlisp expressions (forms) to be executed. The first argument, VARLST, is a list of local variables (must be NIL if no variables are used). Each atom in VARLST is treated as the name of a local variable and bound to NIL. VARLST can also contain lists of the form (LITATOM FORM). In this case, LITATOM is the name of the variable and is bound to the value of FORM. The evaluation takes place before any of the bindings are performed, e.g., (PROG ((X Y) (Y X)) ...) will bind local variable X to the value of Y (evaluated outside the PROG) and local variable Y to the value of X (outside the PROG). An attempt to use anything other than a litatom as a PROG variable will cause an error, ARG NOT LITATOM. An attempt to use NIL or T as a PROG variable will cause an error, ATTEMPT TO BIND NIL OR T. The rest of the PROG is a sequence of non-atomic statements (forms) and litatoms (labels). The forms are evaluated sequentially; the labels serve only as markers. The two special functions GO and RETURN alter this flow of control as described below. The value of the PROG is usually specified by the function RETURN. If no RETURN is executed before the PROG "falls off the end," the value of the PROG is NIL. (GO U) [NLambda NoSpread Function] GO is used to cause a transfer in a PROG. (GO L) will cause the PROG to evaluate forms starting at the label L (GO does not evaluate its argument). A GO can be used at any level in a PROG. If the label is not found, GO will search higher progs within the same function, e.g., (PROG ... A ... (PROG ... (GO A))). If the label is not found in the function in which the PROG appears, an error is generated, UNDEFINED OR ILLEGAL GO. (RETURN X) [Function] A RETURN is the normal exit for a PROG. Its argument is evaluated and is immediately returned the value of the PROG in which it appears. Note: If a GO or RETURN is executed in an interpreted function which is not a PROG, the GO or RETURN will be executed in the last interpreted PROG entered if any, otherwise cause an error. GO or RETURN inside of a compiled function that is not a PROG is not allowed, and will cause an error at compile time. As a corollary, GO or RETURN in a functional argument, e.g., to SORT, will not work compiled. Also, since NLSETQ's and ERSETQ's compile as separate functions, a GO or RETURN cannot be used inside of a compiled NLSETQ or ERSETQ if the corresponding PROG is outside, i.e., above, the NLSETQ or ERSETQ. (LET VARLST E1 E2 ... EN) [Macro] LET is essentially a PROG that can't contain GO's or RETURN's, and whose last form is the returned value. (LET* VARLST E1 E2 ... EN) [Macro] (PROG* VARLST E1 E2 ... EN) [Macro] LET* and PROG* differ from LET and PROG only in that the binding of the bound variables is done "sequentially." Thus (LET* ((A (LIST 5)) (B (LIST A A))) (EQ A (CADR B))) would evaluate to T; whereas the same form with LET might even find A an unbound variable when evaluating (LIST A A). The Iterative Statement 1 The iterative statement(ITERATIVE% STATEMENT NIL Iterative% statement NIL NIL 7) (i.s.) in its various forms permits the user to specify complicated iterative statements in a straightforward and visible manner. Rather than the user having to perform the mental transformations to an equivalent Interlisp form using PROG, MAPC, MAPCAR, etc., the system does it for him. The goal was to provide a robust and tolerant facility which could "make sense" out of a wide class of iterative statements. Accordingly, the user should not feel obliged to read and understand in detail the description of each operator given below in order to use iterative statements. An iterative statement is a form consisting of a number of special words (known as i.s. operators or i.s.oprs), followed by operands. Many i.s.oprs (FOR, DO, WHILE, etc.) are similar to iterative statements in other programming languages; other i.s.oprs (COLLECT, JOIN, IN, etc.) specify useful operations in a Lisp environment. Lower case versions of i.s.oprs (do, collect, etc.) can also be used. Here are some examples of iterative statements: (for X from 1 to 5 do (PRINT 'FOO)) FOO FOO FOO FOO FOO NIL (for X from 2 to 10 by 2 collect (TIMES X X)) (4 16 36 64 100) (for X in '(A B 1 C 6.5 NIL (45)) count (NUMBERP X)) 2 Iterative statements are implemented through CLISP, which translates the form into the appropriate PROG, MAPCAR, etc. Iterative statement forms are translated using all CLISP declarations in effect (standard/fast/undoable/ etc.); see Chapter 21. Misspelled i.s.oprs are recognized and corrected using the spelling list CLISPFORWORDSPLST. The order of appearance of operators is never important; CLISP scans the entire statement before it begins to construct the equivalent Interlisp form. New i.s.oprs can be defined as described in the Defining New Iterative Statement Operators section in this chapter. If the user defines a function by the same name as an i.s.opr (WHILE, TO, etc.), the i.s.opr will no longer have the CLISP interpretation when it appears as CAR of a form, although it will continue to be treated as an i.s.opr if it appears in the interior of an iterative statement. To alert the user, a warning message is printed, e.g., (WHILE DEFINED, THEREFORE DISABLED IN CLISP). I.S. Types(I.S.% TYPES NIL I.S.% Types NIL NIL 8) The following i.s.oprs are examples of a certain kind of iterative statement operator called an i.s.type. The i.s.type specifies what is to be done at each iteration. Its operand is called the "body" of the iterative statement. Each iterative statement must have one and only one i.s.type. DO FORM(DO% FORM (I.S. Operator) NIL NIL NIL 8) [I.S. Operator] Specifies what is to be done at each iteration. DO with no other operator specifies an infinite loop. If some explicit or implicit terminating condition is specified, the value of the i.s. is NIL. Translates to MAPC or MAP whenever possible. COLLECT FORM(COLLECT% FORM (I.S. Operator) NIL NIL NIL 8) [I.S. Operator] Specifies that the value of FORM at each iteration is to be collected in a list, which is returned as the value of the i.s. when it terminates. Translates to MAPCAR, MAPLIST or SUBSET whenever possible. When COLLECT translates to a PROG (e.g., if UNTIL, WHILE, etc. appear in the i.s.), the translation employs an open TCONC using two pointers similar to that used by the compiler for compiling MAPCAR. To disable this translation, perform (CLDISABLE 'FCOLLECT). JOIN FORM (JOIN% FORM% (I.S. Operator) NIL NIL NIL 8) [I.S. Operator] Similar to COLLECT, except that the values of FORM at each iteration are NCONCed. Translates to MAPCONC or MAPCON whenever possible. /NCONC, /MAPCONC, and /MAPCON are used when the CLISP declaration UNDOABLE is in effect. SUM FORM(SUM% FORM (I.S. Operator) NIL NIL NIL 8) [I.S. Operator] Specifies that the values of FORM at each iteration be added together and returned as the value of the i.s., e.g., (for I from 1 to 5 sum (TIMES I I)) returns 1+4+9+16+25 = 55. IPLUS, FPLUS, or PLUS will be used in the translation depending on the CLISP declarations in effect. COUNT FORM(COUNT% FORM (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] Counts the number of times that FORM is true, and returns that count as its value. ALWAYS FORM(ALWAYS% FORM (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] Returns T if the value of FORM is non-NIL for all iterations. (Note: returns NIL as soon as the value of FORM is NIL). NEVER FORM(NEVER% FORM (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] Similar to ALWAYS, except returns T if the value of FORM is never true. (Note: returns NIL as soon as the value of FORM is non-NIL). The following i.s.types explicitly refer to the iteration variable (i.v.) of the iterative statement, which is a variable set at each iteration. This is explained below under FOR. THEREIS FORM(THEREIS% FORM (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] Returns the first value of the i.v. for which FORM is non-NIL, e.g., (for X in Y thereis (NUMBERP X)) returns the first number in Y. (Note: returns the value of the i.v. as soon as the value of FORM is non-NIL). LARGEST FORM(LARGEST% FORM (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] SMALLEST FORM(SMALLEST% FORM (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] Returns the value of the i.v. that provides the largest/smallest value of FORM. $$EXTREME is always bound to the current greatest/smallest value, $$VAL to the value of the i.v. from which it came. Iteration Variable I.s.oprs FOR VAR(FOR% VAR (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] Specifies the iteration variable (i.v.) which is used in conjunction with IN, ON, FROM, TO, and BY. The variable is rebound within the i.s., so the value of the variable outside the i.s. is not effected. Example: (SETQ X 55) 55 (for X from 1 to 5 collect (TIMES X X)) (1 4 9 16 25) (X 55 FOR VARS(FOR% VARS (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] VARS a list of variables, e.g., (for (X Y Z) in ...). The first variable is the i.v., the rest are dummy variables. See BIND below. FOR OLD VAR(FOR% OLD% VAR (I.S. Operator) NIL NIL NIL 9) [I.S. Operator] Similar to FOR, except that VAR is not rebound within the i.s., so the value of the i.v. outside of the i.s. is changed. Example: (SETQ X 55) 55 (for old X from 1 to 5 collect (TIMES X X)) (1 4 9 16 25) X 6 BIND VAR(BIND% VAR (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] BIND VARS(BIND% VARS (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Used to specify dummy variables, which are bound locally within the i.s. Note: FOR, FOR OLD, and BIND variables can be initialized by using the form VAR_FORM: (for old (XFORM) bind (YFORM) ...) IN FORM(IN% FORM (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Specifies that the i.s. is to iterate down a list with the i.v. being reset to the corresponding element at each iteration. For example, (for X in Y do ...) corresponds to (MAPC Y (FUNCTION (LAMBDA (X) ...))). If no i.v. has been specified, a dummy is supplied, e.g., (in Y collect CADR) is equivalent to (MAPCAR Y (FUNCTION CADR)). ON FORM(ON% FORM (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Same as IN except that the i.v. is reset to the corresponding tail at each iteration. Thus IN corresponds to MAPC, MAPCAR, and MAPCONC, while ON corresponds to MAP, MAPLIST, and MAPCON. Note: For both IN and ON, FORM is evaluated before the main part of the i.s. is entered, i.e. outside of the scope of any of the bound variables of the i.s. For example, (for X bind (Y_'(1 2 3)) in Y ...) will map down the list which is the value of Y evaluated outside of the i.s., not (1 2 3). IN OLD VAR(IN% OLD% VAR (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Specifies that the i.s. is to iterate down VAR, with VAR itself being reset to the corresponding tail at each iteration, e.g., after (for X in old L do ... until ...) finishes, L will be some tail of its original value. IN OLD (VAR_FORM)(IN% OLD% %(VAR_FORM%) (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Same as IN OLD VAR, except VAR is first set to value of FORM. ON OLD VAR(ON% OLD% VAR (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Same as IN OLD VAR except the i.v. is reset to the current value of VAR at each iteration, instead of to (CAR VAR). ON OLD (VAR_FORM)(ON% OLD% %(VAR_FORM%) (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Same as ON OLD VAR, except VAR is first set to value of FORM. INSIDE FORM(INSIDE% FORM (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Similar to IN, except treats first non-list, non-NIL tail as the last element of the iteration, e.g., INSIDE '(A B C D . E) iterates five times with the i.v. set to E on the last iteration. INSIDE 'A is equivalent to INSIDE '(A), which will iterate once. FROM FORM(FROM% FORM (I.S. Operator) NIL NIL NIL 10) [I.S. Operator] Used to specify an initial value for a numerical i.v. The i.v. is automatically incremented by 1 after each iteration (unless BY is specified). If no i.v. has been specified, a dummy i.v. is supplied and initialized, e.g., (from 2 to 5 collect SQRT) returns (1.414 1.732 2.0 2.236). TO FORM(TO% FORM (I.S. Operator) NIL NIL NIL 11) [I.S. Operator] Used to specify the final value for a numerical i.v. If FROM is not specified, the i.v. is initialized to 1. If no i.v. has been specified, a dummy i.v. is supplied and initialized. If BY is not specified, the i.v. is automatically incremented by 1 after each iteration. When the i.v. is definitely being incremented, i.e., either BY is not specified, or its operand is a positive number, the i.s. terminates when the i.v. exceeds the value of FORM. Similarly, when the i.v. is definitely being decremented the i.s. terminates when the i.v. becomes less than the value of FORM (see description of BY). Note: FORM is evaluated only once, when the i.s. is first entered, and its value bound to a temporary variable against which the i.v. is checked each interation. If the user wishes to specify an i.s. in which the value of the boundary condition is recomputed each iteration, he should use WHILE or UNTIL instead of TO. Note: When both the operands to TO and FROM are numbers, and TO's operand is less than FROM's operand, the i.v. is decremented by 1 after each iteration. In this case, the i.s. terminates when the i.v. becomes less than the value of FORM. For example, (from 10 to 1 do PRINT) prints the numbers from 10 down to 1. BY FORM (with IN/ON) (BY% FORM% %(WITH% IN/ON%)% (I.S. Operator) BY% FORM% %(with% IN/ON%)% NIL NIL 11) [I.S. Operator] If IN or ON have been specified, the value of FORM determines the tail for the next iteration, which in turn determines the value for the i.v. as described earlier, i.e., the new i.v. is CAR of the tail for IN, the tail itself for ON. In conjunction with IN, the user can refer to the current tail within FORM by using the i.v. or the operand for IN/ON, e.g., (for Z in L by (CDDR Z) ...) or (for Z in L by (CDDR L) ...). At translation time, the name of the internal variable which holds the value of the current tail is substituted for the i.v. throughout FORM. For example, (for X in Y by (CDR (MEMB 'FOO (CDR X))) collect X) specifies that after each iteration, CDR of the current tail is to be searched for the atom FOO, and (CDR of) this latter tail to be used for the next iteration. BY FORM (without IN/ON) (BY% FORM% %(WITHOUT% IN/ON%)% (I.S. Operator) BY% FORM% %(without% IN/ON%)% NIL NIL 11) [I.S. Operator] If IN or ON have not been used, BY specifies how the i.v. itself is reset at each iteration. If FROM or TO have been specified, the i.v. is known to be numerical, so the new i.v. is computed by adding the value of FORM (which is reevaluated each iteration) to the current value of the i.v., e.g., (for N from 1 to 10 by 2 collect N) makes a list of the first five odd numbers. If FORM is a positive number (FORM itself, not its value, which in general CLISP would have no way of knowing in advance), the i.s. terminates when the value of the i.v. exceeds the value of TO's operand. If FORM is a negative number, the i.s. terminates when the value of the i.v. becomes less than TO's operand, e.g., (for I from N to M by -2 until (LESSP I M) ...). Otherwise, the terminating condition for each iteration depends on the value of FORM for that iteration: if FORM<0, the test is whether the i.v. is less than TO's operand, if FORM>0 the test is whether the i.v. exceeds TO's operand, otherwise if FORM=0, the i.s. terminates unconditionally. If FROM or TO have not been specified and FORM is not a number, the i.v. is simply reset to the value of FORM after each iteration, e.g., (for I from N by M ...) is equivalent to (for I_N by (PLUS I M) ...). AS VAR(AS% VAR (I.S. Operator) NIL NIL NIL 12) [I.S. Operator] Used to specify an iterative statement involving more than one iterative variable, e.g., (for X in Y as U in V do ...) corresponds to MAP2C. The i.s. terminates when any of the terminating conditions are met, e.g., (for X in Y as I from 1 to 10 collect X) makes a list of the first ten elements of Y, or however many elements there are on Y if less than 10. The operand to AS, VAR, specifies the new i.v. For the remainder of the i.s., or until another AS is encountered, all operators refer to the new i.v. For example, (for I from 1 to N1 as J from 1 to N2 by 2 as K from N3 to 1 by -1 ...) terminates when I exceeds N1, or J exceeds N2, or K becomes less than 1. After each iteration, I is incremented by 1, J by 2, and K by -1. OUTOF FORM(OUTOF% FORM (I.S. Operator) NIL NIL NIL 12) [I.S. Operator] For use with generators. On each iteration, the i.v. is set to successive values returned by the generator. The i.s. terminates when the generator runs out. Condition I.S. Oprs WHEN FORM(WHEN% FORM (I.S. Operator) NIL NIL NIL 12) [I.S. Operator] Provides a way of excepting certain iterations. For example, (for X in Y collect X when (NUMBERP X)) collects only the elements of Y that are numbers. UNLESS FORM(UNLESS% FORM (I.S. Operator) NIL NIL NIL 12) [I.S. Operator] Same as WHEN except for the difference in sign, i.e., WHEN Z is the same as UNLESS (NOT Z). WHILE FORM(WHILE% FORM (I.S. Operator) NIL NIL NIL 12) [I.S. Operator] Provides a way of terminating the i.s. WHILE FORM evaluates FORM before each iteration, and if the value is NIL, exits. UNTIL FORM(UNTIL% FORM (I.S. Operator) NIL NIL NIL 12) [I.S. Operator] Same as WHILE except for difference in sign, i.e., WHILE X is equivalent to UNTIL (NOT X). UNTIL N (N a number)(UNTIL% N% %(N% A% NUMBER%) (I.S. Operator) UNTIL% N% %(N% a% number%) NIL NIL 12) [I.S. Operator] Equivalent to UNTIL I.V. > N. REPEATWHILE FORM(REPEATWHILE% FORM (I.S. Operator) NIL NIL NIL 12) [I.S. Operator] Same as WHILE except the test is performed after the evalution of the body, but before the i.v. is reset for the next iteration. REPEATUNTIL FORM(REPEATUNTIL% FORM (I.S. Operator) NIL NIL NIL 12) [I.S. Operator] Same as UNTIL, except the test is performed after the evaluation of the body. REPEATUNTIL N (N a number)(REPEATUNTIL% N% %(N% A% NUMBER%) (I.S. Operator) REPEATUNTIL% N% %(N% a% number%) NIL NIL 12) [I.S. Operator] Equivalent to REPEATUNTIL I.V. > N. Other I.S. Oprs FIRST FORM(FIRST% FORM (I.S. Operator) NIL NIL NIL 13) [I.S. Operator] FORM is evaluated once before the first iteration, e.g., (for X Y Z in L first (FOO Y Z) ...), and FOO could be used to initialize Y and Z. FINALLY FORM(FINALLY% FORM (I.S. Operator) NIL NIL NIL 13) [I.S. Operator] FORM is evaluated after the i.s. terminates. For example, (for X in L bind Y_0 do (if (ATOM X) then (SETQ Y (PLUS Y 1))) finally (RETURN Y)) will return the number of atoms in L. EACHTIME FORM(EACHTIME% FORM (I.S. Operator) NIL NIL NIL 13) [I.S. Operator] FORM is evaluated at the beginning of each iteration before, and regardless of, any testing. For example, consider, (for I from 1 to N do (... (FOO I) ...) unless (... (FOO I) ...) until (... (FOO I) ...)) The user might want to set a temporary variable to the value of (FOO I) in order to avoid computing it three times each iteration. However, without knowing the translation, he would not know whether to put the assignment in the operand to DO, UNLESS, or UNTIL, i.e., which one would be executed first. He can avoid this problem by simply writing EACHTIME (SETQ J (FOO I)). DECLARE: DECL(DECLARE:% DECL (I.S. Operator) NIL NIL NIL 13) [I.S. Operator] Inserts the form (DECLARE DECL) immediately following the PROG variable list in the translation, or, in the case that the translation is a mapping function rather than a PROG, immediately following the argument list of the lambda expression in the translation. This can be used to declare variables bound in the iterative statement to be compiled as local or special variables. For example (for X in Y declare: (LOCALVARS X) ...). Several DECLARE:s can apppear in the same i.s.; the declarations are inserted in the order they appear. DECLARE DECL(DECLARE% DECL (I.S. Operator) NIL NIL NIL 13) [I.S. Operator] Same as DECLARE:. Since DECLARE is also the name of a function, DECLARE cannot be used as an i.s. operator when it appears as CAR of a form, i.e. as the first i.s. operator in an iterative statement. However, declare (lowercase version) can be the first i.s. operator. ORIGINAL I.S.OPR OPERAND(ORIGINAL% I.S.OPR% OPERAND (I.S. Operator) NIL NIL NIL 13) [I.S. Operator] I.S.OPR will be translated using its original, built-in interpretation, independent of any user defined i.s. operators. There are also a number of i.s.oprs that make it easier to create iterative statements that use the clock, looping for a given period of time. See timers, Chapter 12. Miscellaneous Hints on I.S.Oprs f Lowercase versions of all i.s. operators are equivalent to the uppercase, e.g., (for X in Y ...) is equivalent to (FOR X IN Y ...). f Each i.s. operator is of lower precedence than all Interlisp forms, so parentheses around the operands can be omitted, and will be supplied where necessary, e.g., BIND (X Y Z) can be written BIND X Y Z, OLD (X_FORM) as OLD X_FORM, WHEN (NUMBERP X) as WHEN NUMBERP X, etc. f RETURN or GO may be used in any operand. (In this case, the translation of the iterative statement will always be in the form of a PROG, never a mapping function.) RETURN means return from the i.s. (with the indicated value), not from the function in which the i.s appears. GO refers to a label elsewhere in the function in which the i.s. appears, except for the labels $$LP, $$ITERATE, and $$OUT which are reserved, as described below. f In the case of FIRST, FINALLY, EACHTIME, DECLARE: or one of the i.s.types, e.g., DO, COLLECT, SUM, etc., the operand can consist of more than one form, e.g., COLLECT (PRINT (CAR X)) (CDR X), in which case a PROGN is supplied. f Each operand can be the name of a function, in which case it is applied to the (last) i.v., e.g., (for X in Y do PRINT when NUMBERP) is the same as (for X in Y do (PRINT X) when (NUMBERP X)). Note that the i.v. need not be explicitly specified, e.g., (in Y do PRINT when NUMBERP) will work. For i.s.types, e.g., DO, COLLECT, JOIN, the function is always applied to the first i.v. in the i.s., whether explicity named or not. For example, (in Y as I from 1 to 10 do PRINT) prints elements on Y, not integers between 1 and 10. Note that this feature does not make much sense for FOR, OLD, BIND, IN, or ON, since they "operate" before the loop starts, when the i.v. may not even be bound. In the case of BY in conjunction with IN, the function is applied to the current tail e.g., (for X in Y by CDDR ...) is the same as (for X in Y by (CDDR X) ...). f While the exact form of the translation of an iterative statement depends on which operators are present, a PROG will always be used whenever the i.s. specifies dummy variables, i.e., if a BIND operator appears, or there is more than one variable specified by a FOR operator, or a GO, RETURN, or a reference to the variable $$VAL appears in any of the operands. When a PROG is used, the form of the translation is: (PROG VARIABLES {initialize} $$LP {eachtime} {test} {body} $$ITERATE {aftertest} {update} (GO $$LP) $$OUT {finalize} (RETURN $$VAL)) where {test} corresponds to that portion of the loop that tests for termination and also for those iterations for which {body} is not going to be executed, (as indicated by a WHEN or UNLESS); {body} corresponds to the operand of the i.s.type, e.g., DO, COLLECT, etc.; {aftertest} corresponds to those tests for termination specified by REPEATWHILE or REPEATUNTIL; and {update} corresponds to that part that resets the tail, increments the counter, etc. in preparation for the next iteration. {initialize}, {finalize}, and {eachtime} correspond to the operands of FIRST, FINALLY, and EACHTIME, if any. Note that since {body} always appears at the top level of the PROG, the user can insert labels in {body}, and GO to them from within {body} or from other i.s. operands, e.g., (for X in Y first (GO A) do (FOO) A (FIE)). However, since {body} is dwimified as a list of forms, the label(s) should be added to the dummy variables for the iterative statement in order to prevent their being dwimified and possibly "corrected", e.g., (for X in Y bind A first (GO A) do (FOO) A (FIE)). The user can also GO to $$LP, $$ITERATE, or $$OUT, or explicitly set $$VAL. Errors in Iterative Statements An error will be generated and an appropriate diagnostic printed if any of the following conditions hold: 1. Operator with null operand, i.e., two adjacent operators, as in (for X in Y until do ...) 2. Operand consisting of more than one form (except as operand to FIRST, FINALLY, or one of the i.s.types), e.g., (for X in Y (PRINT X) collect ...). 3. IN, ON, FROM, TO, or BY appear twice in same i.s. 4. Both IN and ON used on same i.v. 5. FROM or TO used with IN or ON on same i.v. 6. More than one i.s.type, e.g., a DO and a SUM. In 3, 4, or 5, an error is not generated if an intervening AS occurs. If an error occurs, the i.s. is left unchanged. If no DO, COLLECT, JOIN or any of the other i.s.types are specified, CLISP will first attempt to find an operand consisting of more than one form, e.g., (for X in Y (PRINT X) when ATOM X ...), and in this case will insert a DO after the first form. (In this case, condition 2 is not considered to be met, and an error is not generated.) If CLISP cannot find such an operand, and no WHILE or UNTIL appears in the i.s., a warning message is printed: NO DO, COLLECT, OR JOIN: followed by the i.s. Similarly, if no terminating condition is detected, i.e., no IN, ON, WHILE, UNTIL, TO, or a RETURN or GO, a warning message is printed: POSSIBLE NON-TERMINATING ITERATIVE STATEMENT: followed by the iterative statement. However, since the user may be planning to terminate the i.s. via an error, Control-E, or a RETFROM from a lower function, the i.s. is still translated. Note: The error message is not printed if the value of CLISPI.S.GAG is T (initially NIL). Defining New Iterative Statement Operators The following function is available for defining new or redefining existing iterative statement operators: (I.S.OPR NAME FORM OTHERS EVALFLG)(I.S.OPR% NAME% FORM% OTHERS% EVALFLG%) (Function) %(I.S.OPR% NAME% FORM% OTHERS% EVALFLG%) NIL NIL 15) [Function] NAME is the name of the new i.s.opr. If FORM is a list, NAME will be a new i.s.type, and FORM its body. OTHERS is an (optional) list of additional i.s. operators and operands which will be added to the i.s. at the place where NAME appears. If FORM is NIL, NAME is a new i.s.opr defined entirely by OTHERS. In both FORM and OTHERS, the atom $$VAL can be used to reference the value to be returned by the i.s., I.V. to reference the current i.v., and BODY to reference NAME's operand. In other words, the current i.v. will be substituted for all instances of I.V. and NAME's operand will be substituted for all instances of BODY throughout FORM and OTHERS. If EVALFLG is T, FORM and OTHERS are evaluated at translation time, and their values used as described above. A dummy variable for use in translation that does not clash with a dummy variable already used by some other i.s. operators can be obtained by calling (GETDUMMYVAR). (GETDUMMYVAR T) will return a dummy variable and also insure that it is bound as a PROG variable in the translation. If NAME was previously an i.s.opr and is being redefined, the message (NAME REDEFINED) will be printed (unless DFNFLG=T), and all expressions using the i.s.opr NAME that have been translated will have their translations discarded. The following are some examples of how I.S.OPR could be called to define some existing i.s.oprs, and create some new ones: COLLECT (I.S.OPR 'COLLECT '(SETQ $$VAL (NCONC1 $$VAL BODY))) SUM (I.S.OPR 'SUM '(SETQ $$VAL_ (PLUS $$VAL BODY) '(FIRST (SETQ $$VAL0)) NEVER (I.S.OPR 'NEVER '(if BODY then (SETQ $$VAL NIL) (GO $$OUT)) Note: (if BODY then (RETURN NIL)) would exit from the i.s. immediately and therefore not execute the operations specified via a FINALLY (if any). THEREIS (I.S.OPR 'THEREIS '(if BODY then (SETQ $$VAL I.V.) (GO $$OUT))) RCOLLECT To define RCOLLECT, a version of COLLECT which uses CONS instead of NCONC1 and then reverses the list of values: (I.S.OPR 'RCOLLECT '(SETQ $$VAL (CONS BODY $$VAL)) '(FINALLY (RETURN (DREVERSE $$VAL)))] TCOLLECT To define TCOLLECT, a version of COLLECT which uses TCONC: (I.S.OPR 'TCOLLECT '(TCONC $$VAL BODY) '(FIRST (SETQ $$VAL (CONS)) FINALLY (RETURN (CAR $$VAL)))] PRODUCT (I.S.OPR 'PRODUCT '(SETQ $$VAL $$VAL*BODY) '(FIRST ($$VAL 1))] UPTO To define UPTO, a version of TO whose operand is evaluated only once: (I.S.OPR 'UPTO NIL '(BIND $$FOOBODY TO $$FOO)] TO To redefine TO so that instead of recomputing FORM each iteration, a variable is bound to the value of FORM, and then that variable is used: (I.S.OPR 'TO NIL '(BIND $$END FIRST (SETQ $$END BODY) ORIGINAL TO $$END)] Note the use of ORIGINAL to redefine TO in terms of its original definition. ORIGINAL is intended for use in redefining built-in operators, since their definitions are not accessible, and hence not directly modifiable. Thus if the operator had been defined by the user via I.S.OPR, ORIGINAL would not obtain its original definition. In this case, one presumably would simply modify the i.s.opr definition. I.S.OPR can also be used to define synonyms for already defined i.s. operators by calling I.S.OPR with FORM an atom, e.g., (I.S.OPR 'WHERE 'WHEN) makes WHERE be the same as WHEN. Similarly, following (I.S.OPR 'ISTHERE 'THEREIS), one can write (ISTHERE ATOM IN Y), and following (I.S.OPR 'FIND 'FOR) and (I.S.OPR 'SUCHTHAT 'THEREIS), one can write (find X in Y suchthat X member Z). In the current system, WHERE is synonymous with WHEN, SUCHTHAT and ISTHERE with THEREIS, FIND with FOR, and THRU with TO. If FORM is the atom MODIFIER, then NAME is defined as an i.s.opr which can immediately follow another i.s. operator (i.e., an error will not be generated, as described previously). NAME will not terminate the scope of the previous operator, and will be stripped off when DWIMIFY is called on its operand. OLD is an example of a MODIFIER type of operator. The MODIFIER feature allows the user to define i.s. operators similar to OLD, for use in conjunction with some other user defined i.s.opr which will produce the appropriate translation. The file package command I.S.OPRS (Chapter 17) will dump the definition of i.s.oprs. (I.S.OPRS PRODUCT UPTO) as a file package command will print suitable expressions so that these iterative statement operators will be (re)defined when the file is loaded. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "9-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "9-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "9-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "9-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "9-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "9-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))03HT +T-llT,,,,,2l2ll2l~~3Hl +lT55,2HZZ,HH,ll2Hll2l +,ll,ll0$$T3HHT-T,22HT +T2HT +T,ll +,HH/HH/ll,ll3HH +T,HH +3(T,HHF PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR5,,3KT-T,2TITAN +TITAN +CLASSIC + HELVETICA TITAN +CLASSIC +CLASSIC + HELVETICA HELVETICA HELVETICA +CLASSIC +MODERN +MODERN +MODERNMODERN +) HRULE.GETFNCLASSIC + ) + ( HRULE.GETFNCLASSIC + ( +') + ' HRULE.GETFNCLASSIC + &* + & HRULE.GETFNCLASSIC + 0 IM.CHAP.GETFN HELVETICA+ HRULE.GETFNMODERN +% $  HRULE.GETFNMODERN +#f ""IM.INDEX.GETFN !   3 "!IM.INDEX.GETFN !   X "IM.INDEX.GETFN !    "!IM.INDEX.GETFN !    ""IM.INDEX.GETFN !      "IM.INDEX.GETFN !  *  ! +   C 1  " IM.INDEX.GETFN !  ,   "!IM.INDEX.GETFN !    ""IM.INDEX.GETFN !    " !    C !IM.INDEX.GETFN    "  ! )  !"IM.INDEX.GETFN    <   A  +   W  $ . HRULE.GETFNMODERN +#3 "IM.INDEX.GETFN !     P  "IM.INDEX.GETFN / "IM.INDEX.GETFN + IM.INDEX.GETFN /IM.INDEX.GETFN !      % $ , !Y " IM.INDEX.GETFN !     +   + "  + ;  +           !  "  !    ! " #IM.INDEX.GETFN ! ^  $  $  HRULE.GETFNMODERN +"FIM.INDEX.GETFN !u   X .  "DIM.INDEX.GETFN !o , T    #   J   &    m $  HRULE.GETFNMODERN +" fIM.INDEX.GETFN !' A     (           !A     .  )    "      8  +  %    ##        +!. 9  $ > U  $  HRULE.GETFNMODERN +#  + +IM.INDEX.GETFNm <    / 3        # !     >        ) 9     %  y  @ Z D        =          $  HRULE.GETFNMODERN +    !:    )          !    3  1 6  !  2      ! 3 P =   !  /(!          ' !V =           !  H  !    8 +!  * +$&  HRULE.GETFNMODERN +" !N      " ! R  "  !X V I  " ! ( * 9  &  : N        9       !   B &   '   "  !"   )  %     "9 !  "  !  I    7   * + ! - : !  $ '            "    !   / "        !   O !   % + $  HRULE.GETFNMODERN + 9IM.INDEX.GETFN   E    ]   [  K  $ . 5c    ?  U  ,  +'IM.INDEX.GETFN +#% - (IM.INDEX.GETFN !1     " -IM.INDEX.GETFN !     !    < G (  "  ,IM.INDEX.GETFN !        %  " )IM.INDEX.GETFN ! R #   P " +IM.INDEX.GETFN  ! / ",IM.INDEX.GETFN !   %    "+IM.INDEX.GETFN !        #  "-IM.INDEX.GETFN !.    @   " -IM.INDEX.GETFN  .IM.INDEX.GETFN  !J  9 .  +" (IM.INDEX.GETFN !J     u   ( " )IM.INDEX.GETFN  ! F  " -IM.INDEX.GETFN  !   ^   , " *IM.INDEX.GETFN  +IM.INDEX.GETFN I    0      "  )IM.INDEX.GETFN   $=   " )IM.INDEX.GETFN  4              @ E #.     " -IM.INDEX.GETFN +  L " * " 6IM.INDEX.GETFN     " -IM.INDEX.GETFN  2 "  " 6IM.INDEX.GETFN     " -IM.INDEX.GETFN  $ 2 *    " +IM.INDEX.GETFN  `   " )IM.INDEX.GETFN 9  w  o f             x   ' "   TIM.INDEX.GETFN   #  u    0 &     3& 4  9 "   ZIM.INDEX.GETFN    ?  l O #-      N   (R  .  (  )    ;    " (IM.INDEX.GETFN X  L (+ (    I B G     -    " ,IM.INDEX.GETFN   +" +IM.INDEX.GETFN > '  " -IM.INDEX.GETFN  *   " ,IM.INDEX.GETFN (   %  " ,IM.INDEX.GETFN  &   "   RIM.INDEX.GETFN   "  2IM.INDEX.GETFN  t "  2IM.INDEX.GETFN  A "    ^IM.INDEX.GETFN   " ,IM.INDEX.GETFN 5 $    " .IM.INDEX.GETFN 6 R$  " /IM.INDEX.GETFN q @    X  " /IM.INDEX.GETFN   l  ' X " .IM.INDEX.GETFN    ! 7 Q   " ;IM.INDEX.GETFN r #  + S     +        x  8 . ^   )        =    e " *>     m " ! 5     T   )     o M E   ! ) *     +   9  L      +  (   ? *  1       +#j ] C  " "               #   ;  0     &!   4   \   - o    * +#k - gIM.INDEX.GETFN  %      t    &      @ $  W  4           D    ?  +  ) C ' M * ,+'*,+$+*,+0 _  *,+2     ' ,+$+*  +   ,++J*,++  +  ' ,++    5 " , ++F  '   u S          !              V    = n  5   z \ No newline at end of file diff --git a/docs/porter-irm/vol1/10-FUNC-DEF.INTERPRESS b/docs/porter-irm/vol1/10-FUNC-DEF.INTERPRESS new file mode 100644 index 00000000..a06cfec6 Binary files /dev/null and b/docs/porter-irm/vol1/10-FUNC-DEF.INTERPRESS differ diff --git a/docs/porter-irm/vol1/10-FUNC-DEF.TEDIT b/docs/porter-irm/vol1/10-FUNC-DEF.TEDIT new file mode 100644 index 00000000..28e0d233 --- /dev/null +++ b/docs/porter-irm/vol1/10-FUNC-DEF.TEDIT @@ -0,0 +1,95 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 10. FUNCTION DEFINITION, MANIPULATION, AND EVALUATION 1 10. FUNCTION DEFINITION, MANIPULATION, AND EVALUATION 1 "10"10. FUNCTION DEFINITION, MANIPULATION, AND EVALUATION 6 The Interlisp programming system is designed to help the user define and debug functions. Developing an applications program in Interlisp involves defining a number of functions in terms of the system primitives and other user-defined functions. Once defined, the user's functions may be referenced exactly like Interlisp primitive functions, so the programming process can be viewed as extending the Interlisp language to include the required functionality. Functions are defined with a list expressions known as an "expr definition." An expr definition specifies if the function has a fixed or variable number of arguments, whether these arguments are evaluated or not, the function argument names, and a series of forms which define the behavior of the function. For example: (LAMBDA (X Y) (PRINT X) (PRINT Y)) A function defined with this expr definition would have two evaluated arguments, X and Y, and it would execute (PRINT X) and (PRINT Y) when evaluated. Other types of expr definitions are described below. A function is defined by putting an expr definition in the function definition cell of a litatom. There are a number of functions for accessing and setting function definition cells, but one usually defines a function with DEFINEQ (see the Defining Functions section below). For example: (DEFINEQ (FOO (LAMBDA (X Y) (PRINT X) (PRINT Y)))) (FOO) The expression above will define the function FOO to have the expr definition (LAMBDA (X Y) (PRINT X) (PRINT Y)). After being defined, this function may be evaluated just like any system function: (FOO 3 (IPLUS 3 4)) 3 7 7 All function definition cells do not contain expr definitions. The compiler (see the first page of Chapter 18) translates expr definitions into compiled code objects, which execute much faster. Interlisp provides a number of "function type functions" which determine how a given function is defined, the number and names of function arguments, etc. See the Function Type Functions section below. Usually, functions are evaluated automatically when they appear within another function or when typed into Interlisp. However, sometimes it is useful to envoke the Interlisp interpreter explicitly to apply a given "functional argument" to some data. There are a number of functions which will apply a given function repeatedly. For example, MAPCAR will apply a function (or an expr definition) to all of the elements of a list, and return the values returned by the function: (MAPCAR '(1 2 3 4 5) '(LAMBDA (X) (ITIMES X X)) (1 4 9 16 25) When using functional arguments, there are a number of problems which can arise, related with accessing free variables from within a function argument. Many times these problems can be solved using the function FUNCTION to create a FUNARG object. The macro facility provides another way of specifying the behavior of a function (see the Macros section below). Macros are very useful when developing code which should run very quickly, which should be compiled differently than it is interpreted, or which should run differently in different implementations of Interlisp. Function Types 1 Interlisp functions are defined using list expressions called "expr definitions." An expr definition is a list of the form (LAMBDA-WORD ARG-LIST FORM1 ... FORMN). LAMBDA-WORD determines whether the arguments to this function will be evaluated or not. ARG-LIST determines the number and names of arguments. FORM1 ... FORMN are a series of forms to be evaluated after the arguments are bound to the local variables in ARG-LIST. If LAMBDA-WORD is the litatom LAMBDA, then the arguments to the function are evaluated. If LAMBDA-WORD is the litatom NLAMBDA, then the arguments to the function are not evaluated. Functions which evaluate or don't evaluate their arguments are therefore known as "lambda" or "nlambda" functions, respectively. If ARG-LIST is NIL or a list of litatoms, this indicates a function with a fixed number of arguments. Each litatom is the name of an argument for the function defined by this expression. The process of binding these litatoms to the individual arguments is called "spreading" the arguments, and the function is called a "spread" function. If the argument list is any litatom other than NIL, this indicates a function with a variable number of arguments, known as a "nospread" function. If ARG-LIST is anything other than a litatom or a list of litatoms, such as (LAMBDA "FOO" ...), attempting to use this expr definition will generate an ARG NOT LITATOM error. In addition, if NIL or T is used as an argument name, the error ATTEMPT TO BIND NIL OR T is generated. These two parameters (lambda/nlambda and spread/nospread) may be specified independently, so there are four nain function types, known as lambda-spread, nlanbda-spread, lanbda-nospread, and nlambda-nospread functions. Each one has a different form and is used for a different purpose. These four function types are described more fully below. For lambda-spread, lanbda-nospread, or nlambda-spread functions, there is an upper limit to the number of arguments that a function can have, based on the number of arguments that can be stored on the stack on any one function call. Currently, the limit is 80 arguments. If a function is called with more than that many arguments, the error TOO MANY ARGUMENTS occurs. However, nlambda-nospread functions can be called with an arbitrary number of arguments, since the arguments are not individually saved on the stack. Lambda-Spread Functions(LAMBDA-SPREAD% FUNCTIONS NIL Lambda-Spread% Functions NIL ("10") 2) Lambda-spread functions take a fixed number of evaluated arguments. This is the most common function type. A lambda-spread expr definition has the form: (LAMBDA (ARG1 ... ARGM) FORM1 ... FORMN) The argument list (ARG1 ... ARGM) is a list of litatoms that gives the number and names of the formal arguments to the function. If the argument list is ( ) or NIL, this indicates that the function takes no arguments. When a lambda-spread function is applied to some arguments, the arguments are evaluated, and boud to the local variables ARG1 ... ARGM. Then, FORM1 ... FORMN are evaluated in order, and the value of the function is the value of FORMN. (DEFINEQ (FOO (LAMBDA (X Y) (LIST X Y)))) (FOO) (FOO 99 (PLUS 3 4)) (99 7) In the above example, the function FOO defined by (LAMBDA (X Y) (LIST X Y)) is applied to the arguments 99 and (PLUS 3 4). These arguments are evaluated (giving 99 and 7), the local variable X is bound to 99 and Y to 7, (LIST X Y) is evaluated, returning (99 7), and this is returned as the value of the function. A standard feature of the Interlisp system is that no error occurs if a spread function is called with too many or too few arguments. If a function is called with too many argumnents, the extra arguments are evaluated but ignored. If a function is called with too few arguments, the unsupplied ones will be delivered as NIL. In fact, a spread function cannot distinguish between being given NIL as an argument, and not being given that argument, e.g., (FOO) and (FOO NIL)are exactly the same for spread functions. If it is necessary to distinguish between these two cases, use an nlambda function and explicitly evaluate the arguments with the EVAL function. Nlambda-Spread Functions(NLAMBDA-SPREAD% FUNCTIONS NIL Nlambda-Spread% Functions NIL ("10") 3) Nlambda-spread functions take a fixed number of unevaluated arguments. An nlambda-spread expr definition has the form: (NLAMBDA (ARG1 ... ARGM) FORM1 ... FORMN) Nlambda-spread functions are evaluated similarly to lanbda-spread functions, except that the arguments are not evaluated before being bound to the variables ARG1 ... ARGM. (DEFINEQ (FOO (NLAMBDA (X Y) (LIST X Y)))) (FOO) (FOO 99 (PLUS 3 4)) (99 (PLUS 3 4)) In the above example, the function FOO defined by (LNAMBDA (X Y) (LIST X Y)) is applied to the arguments 99 and (PLUS 3 4). These arguments are unevaluated to X and Y. (LIST X Y) is evaluated, returning (99 (PLUS 3 4)), and this is returned as the value of the function. Functions can be defined so that all of their arguments are evaluated (lambda functions) or none are evaluated (nlambda functions). If it is desirable to write a function which only evaluates some of its arguments (e.g., SETQ), the functions should be defined as an nlambda, with some arguments explicitly evaluated using the function EVAL. If this is done, the user should put the litatom EVAL on the property list of the function under the property INFO. This informs various system packages, such as DWIM, CLISP, and Masterscope, that this function in fact does evaluate its arguments, even though it is an nlambda. Warning: A frequent problem that occurs when evaluating arguments to nlambda functions with EVAL is that the form being evaluated may reference variables that are not accessible within the nlambda function. This is usually not a problem when interpreting code, but when the code is compiled, the values of "local" variables may not be accessible on the stack (see Chapter 18). The system nlambda functions that evaluate their arguments (such as SETQ) are expanded in-line by the compiler, so this is not a problem. Using the macro facility is recommended in cases where it is necessary to evaluate some arguments to an nlambda function. Lambda-Nospread Functions(LAMBDA-NOSPREAD% FUNCTIONS NIL Lambda-Nospread% Functions NIL ("10") 4) Lambda-nospread functions take a variable number of evaluated arguments. A lambda-nospread expr definition has the form: (LAMBDA VAR FORM1 ... FORMN) VAR may be any litatome, except NIL and T. When a lambda-nospread function is applied to some arguments, each of these arguments is evaluated and the values stored on the stack. VAR is then bound to the number of arguments which have been evaluated. For example, if FOO is defined by (LAMBDA X ...), when (FOO A B C) is evaluated, A, B, and C are evaluated and X is bound to 3. VAR should never be reset The following functions are used for accessing the arguments of lambda-nospread functions. (ARG(ARG (Function) NIL NIL ("10") 4) VAR M) [NLambda Function] Returns the Mth argument for the lambda-nospread function whose argument list is VAR. VAR is the name of the atomic argument list to a lambda-nospread function, and is not evaluated. M is the number of the desired argument, and is evaluated. The value of ARG is undefined for M less than or equal to 0 or greater than the value of VAR. (SETARG(SETARG (Function) NIL NIL ("10") 4) VAR M X) [NLambda Function] Sets the Mth argument for the lambda-nospread function whose argument list is VAR to X. VAR is not evaluated; M and X are evaluated. M ahouls be between 1 and the value of VAR. In the example below, the function FOO is defined to collect and return a list of all of the evaluated arguments it is given (the value of the for statement). (DEFINEQ (FOO (LAMBDA X (for ARGNUM from 1 to X collect (ARG X ARGNUM)] (FOO) (FOO 99 (PLUS 3 4)) (99 7) (FOO 99 (PLUS 3 4)(TIMES 3 4))) (99 7 12) NLambda-Nospread Functions(NLAMBDA-NOSPREAD% FUNCTIONS NIL NLambda-Nospread% Functions NIL ("10") 4) Nlambda-nospread functions take a variable number of unevaluated arguments. An nlambda-nospread expr definition has the form: (NLAMBDA VAR FORM1 ... FORMN) VAR may be any litatome, except NIL and T. Though similar in form to lambda-nospread expr definitions, an nlambda-nospread is evaluated quite differently. When an nlambda-nospread function is applied to some arguments, VAR is simply bound to a list of the unevaluated arguments. The user may pick apart this list, and evaluate different arguments. In the example below, FOO is defined to return the reverse of the list of arguments it is given (unevaluated): (DEFINEQ (FOO (NLAMBDA X (REVERSE X)))) (FOO) (FOO 99 (PLUS 3 4)) ((PLUS 3 4) 99) (FOO 99 (PLUS 3 4)(TIMES 3 4)) (TIMES 3 4)(PLUS 3 4) 99) The warning about evaluating arguments to nlambda functions also applies to nlambda-nospread function. Compiled Functions(COMPILED% FUNCTIONS NIL Compiled% Functions NIL ("10") 5) Functions defined by expr definitions can be compiled by the Interlisp compiler (see Chapter 18). The compiler produces compiled code objects (of data type CCODEP) which execute more quickly than the corresponding expr definition code. Functions defined by compiled code objects may have the samne four types as expr definitions (lambda/nlambda, spread/nospread). Functions created by the compiler are referred to as compiled functions. Function Type Functions There are a variety of functions used for examining the type, argument list, etc. of functions. These functions may be given either a litatom (in which case they obtain the function definition from the definition cell), or a function devinition itself. (FNTYP(FNTYP (Function) NIL NIL ("10") 5) FN) [Function] Returns NIL if FN is not a function definition or the name of a defined function. Otherwise, FNTYP returns one of the following litatoms, depending on the type of function definition. EXPR Lambda-spread expr definition CEXPR Lambda-spread compiled definition FEXPR Nlambda-spread expr definition CFEXPR Nlambda-spread compiled definition EXPR* Lambda-nospread expr definition CEXPR* Lambda-nospread compiled definition FEXPR* Nlambda-nospread expr definition CFEXPR* Nlambda-nospread compiled definition FUNARG FNTYP returns the litatom FUNARG if FN is a FUNARG expression. EXP, FEXPR, EXPR*, and FEXPR* indicate that FN is defined by an expr definition. CEXPR, CFEXPR, CEXPR*, and CFEXPR* indicate that FN is defined by sa compiled definition, as indicated by the prefix C. The suffix * indicates that FN has an indefinite number of arguments, i.e., is a nospread function. The prefix F indicates unevaluated arguments. Thus, for example, a CFEXPR* is a compiled nospread nlambda function. (EXPRP(EXPRP (Function) NIL NIL ("10") 5) FN) [Function] Returns T if (FNTYP FN) is EXPR, FEXPR, EXPR*, or FEXPR*; NIL otherwise. However, (EXPRP FN) is also true if FN is (has) a list definition, even if it does not begin with LAMBDA or NLAMBDA. In other words, EXPRP is not quite as selective as FNTYP. (CCODEP(CCODEP (Function) NIL NIL ("10") 5) FN) [Function] Returns T if (FNTYP FN) is either CEXPR, CFEXPR, CEXPR*, or CFEXPR*; NIL otherwise. (ARGTYPE(ARGTYPE (Function) NIL NIL ("10") 6) FN) [Function] FN is the name of a function or its definition. ARGTYPE returns 0, 1, 2,, or 3, or NIL if FN is not a function. ARGTYPE corresponds to the rows of FNTYPs. The interpretation of this value is as follows: 0 Lambda-spread function (EXPR, CEXPR) 1 Nlambda-spread function (FEXPR, CFEXPR) 2 Lambda-nospread function (EXPR*, CEXPR*) 3 Nlambda-nospread function (FEXPR*, CFEXPR*) (NARGS(NARGS (Function) NIL NIL ("10") 6) FN) [Function] Returns the number of arguments of FN, or NIL if FN is not a function. If FN is a nospread function, the value of NARGS is 1. (ARGLIST(ARGLIST (Function) NIL NIL ("10") 6) FN) [Function] Returns the "argument list" for FN. Note that the "argument list" is a litatom for nospread functions. Since NIL is a possible value for ARGLIST, the error ARGS NOT AVAILABLE is generated if FN is not a function. If FN is a compiled function, the argument list is constructed, i.e., each call to ARGLIST requires making a new list. For functions defined by expr definitions, lists beginning with LAMBDA or NLAMBDA, the argument list is simply CADR of GETD. If FN has an expr definition, and CAR of the definition is not LAMBDA or NLAMBDA, ARGLIST will check to see if CAR of the definition is a member of LAMBDASPLST (see Chapter 20). If it is, ARGLIST presumes this is a function object the user is defining via DWIMUSERFORMS, and simply returns CADR of the definition as its argument list. Otherwise ARGLIST generates an error as described above. (SMARTARGLIST(SMARTARGLIST (Function) NIL NIL ("10") 6) FN EXPLAINFLG TAIL) [Function] A "smart" version of ARGLIST that tries various strategies to get the arglist of FN. First SMARTARGLIST checks the property list of FN under the property ARGNAMES. For spread functions, the argument list itself is stored. For nospread functions, the form is (NIL ARGLIST1 . ARGLIST 2), where ARGLIST1 is the value SMARTARGLIST should return when EXPLAINFLG = T, and ARGLIST 2 the value when EXPLAINFLG = NIL. For example, (GETPROP 'DEFINEQ 'ARGNAMES) = (NIL (X1 Xl ... XN) . X). This allows the user to specify special argument lists. Second, if FN is not defined as a function, SMARTARGLIST attempts spelling correction on FN by calling FNCHECK (see Chapter 20), passing TAIL to be used for the call to FIXSPELL. If unsuccessful, the FN NOT A FUNCTION error will be generated. Third, if FN is known to the file package (see Chapter 17) but not loaded in, SMARTARGLIST will obtain the arglist information from the file. Otherwise, SMARTARGLIST simply returns (ARGLIST FN). SMARTARGLIST is used by BREAK (see Chapter 15) and ADVISE with EXPLAINFLG = NIL for constructing equivalent expr definitions, and by the TTYIN in-line command ?= (see Chapter 26), with EXPLAINFLG = T. Defining Functions 1 Function definitions are stored in a "function definition cell" associated with each litatom. This cell is directly accessible via the two functions PUTD and GETD (see below), but it is usually easier to define functions with DEFINEQ: (DEFINEQ(DEFINEQ (Function) NIL NIL ("10") 7) X1 X2 ... XN) [NLambda NoSpread Function] DEFINEQ is the function normally used for defining functions. It takes an indefinite number of arguments which are not evaluated. Each Xi must be a list defining one function, of the form (NAME DEFINITION). For example: (DEFINEQ (DOUBLE (LAMBDA (X) (IPLUS X X))) ) The above expression will define the function DOUBLE with the expr definition (LAMBDA (X) (IPLUS X X)). Xi may also have the form (NAME ARGS . DEF-BODY), in which case an appropriate lambda expr definition will be constructed. Therefore, the above expression is exactly the same as: (DEFINEQ (DOUBLE (X) (IPLUS X X)) ) Note that this alternate form can only be used for Lambda functions. The first form must be used to define an nlambda function. DEFINEQ returns a list of the names of the functions defined. (DEFINE(DEFINE (Function) NIL NIL ("10") 7) X %) [Function] Lambda-spread version of DEFINEQ. Each element of the list X is itself a list either of the form (NAME DEFINITION) or (NAME ARGS . DEF-BODY). DEFINE will generate an error, INCORRECT DEFINING FORM, on encountering an atom where a defining list is expected. DEFINE and DEFINEQ operate correctly if the function is already defined and BROKEN, ADVISED, or BROKEN-IN. For expressions involving type-in only, if the time stamp facility is enabled (see the Time Stamps section of Chapter 16), both DEFINE and DEFINEQ stamp the definition with your initials and date. UNSAFE.TO.MODIFY.FNS(UNSAFE.TO.MODIFY.FNS (Variable) NIL NIL ("10") 7) [Variable] Value is a list of functions that should not be redefined, because doing so may cause unusual bugs (or crash the system!). If you try to modify a function on this list (using DEFINEQ, TRACE, etc), the system prints Warning: XXX may be safe to modify -- continue? If you type Yes, the function is modified, otherwise an error occurs. This provides a measure of safety for novices who may accidently redefine important system functions. You can add your own functions onto this list. By convention, all functions starting with the character backslash ("\") are system internal functions, which you should never redefine or modify. Backslash functions are not on UNSAFE.TO.MODIFY.FNS, so trying to redefine them will not cause a warning. DFNFLG(DFNFLG (Variable) NIL NIL ("10") 7) [Variable] DFNFLG is a global variable that effects the operation of DEFINEQ and DEFINE. If DFNFLG=NIL, an attempt to redefine a function FN will cause DEFINE to print the message (FN REDEFINED) and to save the old definition of FN using SAVEDEF (see the Functions for Manipulating Typed Definitions section of Chapter 17) before redefining it (except if the old and new definitions are EQUAL, inwhich case the effect is simply a no-op). If DFNFLG=T, the function is simply redefined. If DFNFLG=PROP or ALLPROP, the new definition is stored on the property list under the property EXPR. ALLPROP also affects the operation of RPAQQ and RPAQ (see the Functions Used Within Source Files section of Chapter 17). DFNFLG is initially NIL. DFNFLG is reset by LOAD (see the Loading Files section of Chapter 17) to enable various ways of handling the defining of functions and setting of variables when loading a file. For most applications, the user will not reset DFNFLG directly. Note: The compiler does not respect the value of DFNFLG when it redefines functions to their compiled definitions (see the first page of Chapter 18). Therefore, if you set DFNFLG to PROP to completely avoid inadvertantly redefining something in your running system, you must use compile mode F, not ST. Note that the functions SAVEDEF and UNSAVEDEF (see the Functions for Manipulating Typed Definitions section of Chapter 17) can be useful for "saving" and restoring function definitions from property lists. (GETD(GETD (Function) NIL NIL ("10") 8) FN) [Function] Returns the function definition of FN. Returns NIL if FN is not a litatom, or has no definition. GETD of a compiled function constructs a pointer to the definition, with the result that two successive calls do not necessarily produce EQ results. EQP or EQUAL must be used to compare compiled definitions. (PUTD(PUTD (Function) NIL NIL ("10") 8) FN DEF %) [Function] Puts DEF into FN's function cell, and returns DEF. Generates an error, ARG NOT LITATOM, if FN is not a litatom. Generates an error, ILLEGAL ARG, if DEF is a string, number, or a litatom other than NIL. (MOVD(MOVD (Function) NIL NIL ("10") 8) FROM TO COPYFLG %) [Function] Moves the definition of FROM to TO, i.e., redefines TO. If COPYFLG=T, a COPY of the definition of FROM is used. COPYFLG=T is only meaningful for expr definitions, although MOVD works for compiled functions as well. MOVD returns TO. COPYDEF (see the Functions for Manipulating Typed Definitions section of Chapter 17) is a higher-level function that only moves expr definitions, but works also for variables, records, etc. (MOVD?(MOVD? (Function) NIL NIL ("10") 8) FROM TO COPYFLG %) [Function] If TO is not defined, same as (MOVD FROM TO COPYFLG). Otherwise, does nothing and returns NIL. Function Evaluation 1 Usually, function application is done automatically by the Interlisp interpreter. If a form is typed into Interlisp whose CAR is a function, this function is applied to the arguments in the CDR of the form. These arguments are evaluated or not, and bound tothe funcion parameters, as determined by the type of the function, and the body of the function is evaluated. This sequence is repeated as each form in the body of the function is evaluated. There are some situations where it is necessary to explicitly call the evaluator, and Interlisp supplies a number of functions that will do this. These functions take "functional arguments," which may either be litatoms with function definitions, or expr definition forms such as (LAMBDA (X)...), or FUNARG expressions. (APPLY(APPLY (Function) NIL NIL ("10") 9) FN ARGLIST %) [Function] Applies the function FN to the arguments in the list ARGLIST, and returns its value. APPLY is a lambda function, so its arguments are evaluated, but the individual elements of ARGLIST are not evaluated. Therefore, lambda and nlambda functions are treated the same by APPLY%lambda functions take their arguments from ARGLIST without evaluating them. For example: (APPLY 'APPEND '((PLUS 1 2 3)(4 5 6))) (PLUS 1 2 3 4 5 6) Note that FN may explicitly evaluate one or more of its arguments itself. For example, the system function SETQ is an nlambda function that explicitly evaluates its second argument. Therefore, (APPLY 'SETQ '(FOO (ADD1 3)))will set FOO to 4, instead of setting it to the expression (ADD1 3). APPLY can be used for manipulating expr definitions. For example: (APPLY '(LAMBDA (X Y)(lTIMES X Y)) '(3 4))) 12 (APPLY*(APPLY* (Function) NIL NIL ("10") 9) FN ARG1 ARG2 ... ARGN ) [NoSpread Function] Nospread version of Apply. Applies the function FN to the arguments ARG1 ARG2 ... ARGN. For example: (APPLY 'APPEND '(PLUS 1 2 3)(4 5 6)) (PLUS 1 2 3 4 5 6) (EVAL(EVAL (Function) NIL NIL ("10") 9) X%) [Function] EVAL evaluates the expression X and returns this value, i.e., EVAL provides a way of calling the Interlisp interpreter. Note that EVAL is itself a lambda function, so its argument is first evaluated, e.g.: (SETQ FOO 'ADD1 3))) (ADD1 3) (EVAL FOO) 4 (EVAL 'FOO) (ADD1 3) (QUOTE(QUOTE (Function) NIL NIL ("10") 9) X) [Nlambda NoSpread Function] QUOTE prevents its arguments from being evaluated. Its value is X itself, e.g., (QUOTE FOO) is FOO. Interlisp functions can either evaluate or not evaluate their arguments. QUOTE can be used in those cases where it is desirable to specify arguments unevaluated. The single-quote character (') is defined with a read macro so it returns the next expression, wrapped in a call to QUOTE (see Chapter 25). For example, 'FOO reads as (QUOTE FOO). This is the form used for examples in this manual. Since giving QUOTE more than one argument is almost always a parenthese error, and one that would otherewise go undetected, QUOTE itself generates an error in this case, PARENTHESIS ERROR. (KWOTE(KWOTE (Function) NIL NIL ("10") 10) X) [Function] Value is an expression which, when evaluated, yields X. If X is NIL or a number, this is X itself. Otherwise (LIST (QUOTE QUOTE) X). For example: (KWOTE 5) => 5 (KWOTE (CONS 'A 'B)) => (QUOTE (A.B)) (NLAMBDA.ARGS(NLAMBDA.ARGS (Function) NIL NIL ("10") 10) X) [Function] This function interprets its argument as a list of unevaluated Nlambda arguments. If any of the elements in this list are of the form (QUOTE...), the enclosing QUOTE is stripped off. Actually, NLAMBDA.ARGS stops processing the list after the first non-quoted argument. Therefore, whereas (NLAMBDA.ARGS '((QUOTE FOO) BAR)) -> (FOO BAR), (NLAMBDA.ARGS '(FOO (QUOTE BAR))) -> (FOO (QUOTE BAR)). NLAMBDA.ARGS is alled by a number of nlambda functions in the system, to interpret their arguments. For instance, the function BREAK calls NLAMBDA.ARGS so that (BREAK 'FOO) will break the function FOO, rather than the function QUOTE. (EVALA(EVALA (Function) NIL NIL ("10") 10) X A) [Function] Simulates association list variable lookup. X is a form, A is a list of the form: ((NAME1 . VAL1) (NAME2 . VAL2)... (NAMEN . VALN)) The variable names and values in A are "spread" on the stack, and then X is evaluated. Therefore, any variables appearing free in X that also appears as CAR of an element of A will be given the value on the CDR of that element. (DEFEVAL(DEFEVAL (Function) NIL NIL ("10") 10) TYPE FN) [Function] Specifies how a datum of a particular type is to be evaluated. Inteded primarily for user-defined data types, but works for all data types except lists, literal atoms, and numbers. TYPE is a type name. FN is a function object, i.e., name of a function or a lambda expression. Whenever the interpreter encunters a datum of the indicated type, FN is applied to the datum and its value returned as the result of the evaluation. DEFEVAL returns the previous evaling function for this type. If FN = NIL, DEFEVAL returns the current evaling function without chaqnging it. If FN = T, the evaling functions is set back to the system default (which for all data types except lists is to return the datum itself). COMPILETYPELST (see Chapter 18) permits the user to specify how a datum of a particular type is to be compiled. (EVALHOOK(EVALHOOK (Function) NIL NIL ("10") 10) FORM EVALHOOKFN) [Function] EVALHOOK evaluates the expression FORM, and returns its value. While evaluating FORM, the function EVAL behaves in a special way. Whenever a list other than FORM3 itself is to be evaluated, whether implicitly or via an explicit call to EVAL, EVALHOOKFN is invoked (it should be a function), with the form to be evaluated as its argument. EVALHOOKFN is then responsible for evaluating the form. Whatever is returned is assume to be the result of evaluating the form. During the execution of EVALHOOKFN, this special evaluation is turned off. (Note that EVALHOOK does not effect the evaluations of variables, only of lists). Here is an example of a simple tracing routine that uses the EVALHOOK feature: (DEFINEQ (PRINTHOOK (FORM) (printout T "eval: "FORM T) (EVALHOOK FORM (FUNCTION PRINTHOOK] (PRINTHOOK) Using PRINTHOOK, one might see the following interaction: (EVALHOOK '(LIST (CONS 1 2)(CONS 3 4)) 'PRINTHOOK) eval: (CONS 1 2) eval: (CONS 3 4) ((1.2)(3.4)) Iterating and Mapping Functions 1 The functions below are used to evaluate a form or apply a function repeatedly. RPT, RPTQ, and FRPTQ evaluate an expression a specified number of time. MAP, MAPCAR, MAPLIST, etc., apply a given function repeatedly to different elements of a list, possibly constructing another list. These functions allow efficient iterative computations, but they are difficult to use. For programming iterative computations, it is usually better to use the CLISP Iterative Statement facility (see Chapter 9), which provides a more general and complete facility for expressing iterative statements. Whenever possible, CLISP transltes iterative statements into expressions using the functions below, so there is no efficiency loss. (RPT N FORM) [Function] Evaluates the expression FORM, N times. Returns the value of the last evaluation. If N is less than or equal to 0, FORM is not evaluated, and RPT returns NIL. Before each evaluation, the local variable RPTN is bound to the number of evaluations yet to take place. This variable can be referenced within FORM. For example, (RPT 10 '(PRINT RPTN)) will print the numbers 10, 9...1, and return 1. (RPTQ N FORM1 FORM2... FORMN ) [NLambda NoSpread Function] Nlambda-nospread version of RPT: N is evaluated, FORMi are not. Returns the value of the last evaluation of FORMN. (FRPTQ N FORM1 FORM2... FORMN ) [NLambda NoSpread Function] Faster version of RPTQ. Does not bind RPTN. (MAP MAPX MAPFN1 MAPFN2) [Function] If MAPFN2 is NIL, MAP applies the function MAPFN1 to successive tails of the list MAPX. That is, first it computes (MAPFN1 MAPX), and then (MAPFN1 (CDR MAPX)), etc., until MAPX becomes a non-list. If MAPFN2 is provided, (MAPFN2 MAPX) is used instead of (CDR MAPX) for the next call for MAPFN1, e.g., if MAPFN2 were CDDR, alternate elements of the list would be skipped. MAP returns NIL. (MAPC MAPX MAPFN1 MAPFN2) [Function] Identical to MAP, except that (MAPFN1 (CAR MAPX)) is computed at each iteration instead of (MAPFN1 MAPX), i.e., MAPC works on elements, MAP on tails. MAPC returns NIL. (MAPLIST MAPX MAPFN1 MAPFN2) [Function] Successively computes the same values that MAP would compute, and returns a list consisting of those values. (MAPCAR MAPX MAPFN1 MAPFN2) [Function] Computes the same values that MAPC would compute, and returns a list consisting of those values, e.g., (MAPCAR X 'FNTYP) is a list of FNTYPs for each element on X. (MAPCON MAPX MAPFN1 MAPFN2) [Function] Computes the same values that MAP and MAPLIST but NCONCs these values to form a list which it returns. (MAPCONC MAPX MAPFN1 MAPFN2) [Function] Computes the same values that MAPC and MAPCAR, but NCONCs the values to form a list which it returns. Note that MAPCAR creates a new list which is a mapping of the old list in that each element of the new list is the result of applying a function to the corresponding element on the original list. MAPCONC is used when there are a variable number of elements (including none) to be inserted at each iteration. Examples: (MAPCONC '(A B C NIL D NIL) '(LAMBDA (Y)(if (NULL Y) then NIL else (LIST Y)))) = > (A B C D) This MAPCONC returns a list consisting of MAPX with all NILs removed. (MAPCONC '((A B) C (D E F)(G) H I) '(LAMBDA (Y)(if (LISP Y) then Y else NIL))) = > (A B D E F G) This MAPCONC returns a linear list consisting of all the lists on MAPX. Since MAPCONC uses NCONC to string the corresponding lists together, in this example the original list will be altered to be ((A B C D E F G) C (D E F G)(G) H I). If this is an undesirable side effect, the functional argument to MAPCONC should return instead a top level copy of the lists, i.e., (LAMBDA (Y) (if (LISTP Y) then (APPERND Y) else NIL))). (MAP2C MAPX MAPY MAPFN1 MAPFN2) [Function] Identical to MAPC except MAPFN1 is a function of two arguments, and (MAPFN1 (CAR MAPX)(CAR MAPY)) is computed at each iteration. Terminates when either MAPX or MAPY is a non-list. MAPFN2 is still a function of one argument, and is applied twice on each iteration; (MAPFN2 MAPX) gives the new MAPX, (MAPFN2 MAPY) the new MAPY. CDR is used if MAPFN2 is not supplied, i.e., is NIL. (MAP2CAR MAPX MAPY MAPFN1 MAPFN2) [Function] Identical to MAPCAR except MAPFN1 is a function of two arguments, and (MAPFN1 (CAR MAPX)(CAR MAPY)) is used to assemble the new list. Terminates when either MAPX or MAPY is a non-list. (SUBSET MAPX MAPFN1 MAPFN2) [Function] Applies MAPFN1 to elements of MAPX and returns a list of those elements for which this application is non-NIL, e.g.: (SUBSERT '(A B 3 C 4) 'NUMBERP) = (3 4) MAPFN2 plays the same role as with MAP, MAPC, et al. (EVERY EVERYX EVERYFN1 EVERYFN2) [Function] Returns T if the result of applying EVERYFN1 to each element in EVERYX is true, otherwise NIL. For example, (EVERY '(X Y Z) 'ATOM) => T. EVERY operates by evaluating (EVERYFN1 (CAR EVERYX) EVERYX). The second argument is passed to EVERYFN1 so that it can look at the next element on EVERYX if necessary. If EVERYFN1 yields NIL, EVERY immediately returns NIL. Otherwise, EVERY computes (EVERYFN2 EVERYX), or (CDR EVERYX) if EVERYFN2=NIL, and uses this as the "new" EVERYX, and the process continues. For example (EVERY X 'ATOM 'CDDR) is true if every other element of X is atomic. (SOME SOMEX SOMEFN1 SOMEFN2) [Function] Returns the tail of SOMEX beginning with the first element that satisfies SOMEFN1, i.e., for which SOMEFN1 applied to that element is true. Value is NIL if no such element exists. (SOME X '(LAMBDA (Z) (EQUAL Z Y))) is equivalent to (MEMBER Y X). SOME operates analogously to EVERY. At each stage, (SOMEFN1 (CAR SOMEX) SOMEX) is computed, and if this not NIL, SOMEX is returned as the value of SOME. Otherwise, (SOMEFN2 SOMEX) is computed, or (CDR SOMEX) if SOMEFN2 = NIL, and used for the next SOMEX. (NOTANY SOMEX SOMEFN1 SOMEFN2) [Function] (NOT (SOME SOMEX SOMEFN1 SOMEFN2)). (NOTEVERY EVERYX EVERYFN1 EVERYFN2) [Function] (NOT (EVERY EVERYX EVERYFN1 EVERYFN2)). (MAPRINT LST FILE LEFT RIGHT SEP PFN LISPXPRINTFLG) [Function] A general printing function. For each element of the list LST, applies PFN to the element, and FILE. If PFN is NIL, PRIN1 is used. Between each application MAPRINT performs PRIN1 of SEP (or "" if SEP = NIL). If LEFT is given, it is printed (using PRIN1) initially; if RIGHT is given, it is printed (using PRIN1) at the end. For example, (MAPRINT X NIL '%( '%)) is equivalent to PRIN1 for lists. To print a list with commas between each element and a final "." one could use (MAPRINT X T NIL '%. '%,). If LISPXPRINTFLG = T, LISPXPRIN1 (see Chapter 13) is used instead of PRIN1. Functional Arguments 1 The functions that call the Interlisp-D evaluator take "functional arguments," which may be litatoms with function definitions, or expr definition forms such as (LAMBDA (X) ...), or FUNARG expressions (below). The following functions are useful when one want to supply a functional argument which will always return NIL, T, or 0. Note that the arguments X1 ... XN to these functions are evaluated, though they are not used. (NILL X1 ... XN ) [NoSpread Function] Returns NIL. (TRUE X1 ... XN ) [NoSpread Function] Returns T. (ZERO X1 ... XN ) [NoSpread Function] Returns 0. When using expr definitions as function arguments, they should be enclosed within the function FUNCTION rather than QUOTE, so that they will be compiled as separate functions. FUNCTION can also be used to create FUNARG expressions, which can be used to solve some problems with referencing free variables, or to create functional arguments which carry "state" along with them. (FUNCTION FN ENV ) [NLambda Function] If ENV = NIL, FUNCTION is the same as QUOTE, except that it is treated differently when compiled. Consider the function definition: (DEFINEQ (FOO (LST) (FIE LST (FUNCTION (LAMBDA (Z)(ITIMES Z Z))] FOO calls the function FIE with the value of LST and the expr definition (LAMBDA (Z)(LIST (CAR Z))). If FOO is run interpreted, it does not make any difference whether FUNCTION or QUOTE is used. However, when FOO is compiled, if FUNCTION is used the compiler will define and compile the expr definition as an auxiliary function (see Chapter 18). The compiled expr definition will run considerably faster, which can make a big difference if it is applied repeatedly. Compiling FUNCTION will not create an auxiliary function if it is a functional argument to a function that compiles open, such as most of the mapping functions (MAPCAR, MAPLIST, etc.). If ENV is not NIL, it can be a list of variables that are (presumably) used freely by FN. In this case, the value of FUNCTION is an expression of the form (FUNARG FN POS), where POS is a stack pointer to a frame that contains the variable binds for those variables on ENV. ENV can also be a stack pointer itself, in which case the value of FUNCTION is (FUNARG FN ENV). Finally, ENV can be an atom, in which case it is evaluated, and the value interpreted as described above. As explained above, one of the possible values that FUNCTION can return is the form (FUNARG FN POS), where FN is a function and POS is a stack pointer. FUNARG is not a function itself. Like LAMBDA and NLAMBDA, it has meaning and is specially recognized by Interlisp only in the context of applying a function to arguments. In other words, the expression (FUNARG FN POS) is used exactly like a function. When a FUNARG expression is applied or is CAR of a form being EVALed, the APPLY or EVAL takes place in the access environment specified by ENV (see Chapter 11). Consider the following example: (DEFINEQ (DO.TWICE (FN VAL) (APPLY* FN (APPLY* FN VAL))) ) (DO.TWICE) (DO.TWICE [FUNCTION (LAMBDA (X) (IPLUS X X))] 5) 20 (SETQ VAL 1) 1 (DO.TWICE [FUNCTION (LAMBDA (X) (IPLUS X VAL))] 5) 15 (DO.TWICE [FUNCTION (LAMBDA (X) (IPLUS X VAL))(VAL)] 5) 7 DO.TWICE is defined to apply a function FN to a value VAL, and apply FN again to the value returned; in other words, it calculates (FN (FN VAL)). Given the expr definition (LAMBDA (X)(IPLUS X X)), which doubles a given value, it correctly calculates (FN (FN 5)) = (FN 10) = 20. However, when given (LAMBDA (X) (IPLUS X VAL)), which should add the value of the global variable VAL to the argument X, it does something unexpected, returning 15, rather than 5 + 1 + 1 = 7. The problem is that when the expr definition is evaluated, it is evaluated in the context of DO.TWICE, where VAL is bound to the second argument of DO.TWICE, namely 5. In this case, one solution is to use the ENV argument to FUNCTION to construct a FUNARG expression which contains the value of VAL at the time that the function is executed. Now, when (LAMBDA (X) (IPLUS X VAL)) is evaluated, it is evaluated in an environment where the global value of VAL is accessable. Admittedly, this is a somewhat contrived example (it would be easy enough to change the argument names to DO.TWICE so there would be no conflict), but this situation arises occasionally with large systems of programs that construct functions, and pass them around. System functions with functional arguments (APPLY, MAPCAR, etc) are compiled so that their arguments are local, and not accessible (see Chapter 18). This reduces problems with conflicts with free variables used in functional arguments. FUNARG expressions can be used for more than just circumventing the clashing of variables. For example, a FUNARG expression can be returned as the value of a computation, and then used "higher up". Furthermore, if the function in a FUNARG expression sets any of the variables contained in the frame, only the frame would be changed. For example, consider the following function: (DEFINEQ (MAKECOUNTER (CNT) (FUNCTION [LAMBDA NIL (PROG1 CNT (SETQ CNT (ADD1 CNT (CNT)))] The function MAKECOUNTER returns a FUNARG that increments and returns the previous value of the counter CNT. However, this is done within the environment of the call to MAKECOUNTER where FUNCTION was executed, which the FUNARG expression "carries around" with it, even after MAKECOUNTER has finished executing. Each call to MAKECOUNTER creates a FUNARG expression with a new, independent environment, so that multiple counters can be generated and used: (SETQ C1 (MAKECOUNTER 1)) (FUNARG (LAMBDA NIL (PROG1 CNT (SETQ CNT (ADD1 CNT)))) #1,13724/*FUNARG) (APPLY C1) 1 (APPLY C1) 2 (SETQ C2 (MAKECOUNTER 17)) (FUNARG (LAMBDA NIL (PROG1 CNT (ADD1 CNT)))) #1,13736/*FUNART) (APPLY C2) 17 (APPLY C2) 18 (APPLY C1) 3 (APPLY C2) 19 By creating a FUNARG expression with FUNCTION, a program can create a function object which has updateable binding(s) associated with the object which last between calls to it, but are only accessible through that instance of the function. For example, using the FUNARG device, a program could maintain two different instances of the same random number generator in different states, and run them independently. Macros 1 Macros provide an alternative way of specifying the action of a function. Whereas function definitions are evaluated with a "function call", which involves binding variables and other housekeeping tasks, macros are evaluated by translating one Interlisp form into another, which is then evaluated. A litatom may have both a function definition and a macro definition. When a form is evaluated by the interpreter, if the CAR has a function definition, it is used (with a function call), otherwise if it has a macro definition, then that is used. However, when a form is compiled, the CAR is checked for a macro definition first, and only if there isn't one is the function definition compiled. This allows functions that behave differently when compiled and interpreted. For example, it is possible to define a function that, when interpreted, has a function definition that is slow and has a lot of error checks, for use when debugging a system. This function could also have a macro definition that defines a fast version of the function, which is used when the debugged system is compiled. Macro definitions are represented by lists that are stored on the property list of a litatom. Macros are often used for functions that should be compiled differently in different Interlisp implementations, and the exact property name a macro definition is stored under determines whether it should be used in a particular implementation. The global variable MACROPROPS contains a list of all possible macro property names which should be saved by the MACROS file package command. Typical macro property names are DMACRO for Interlisp-D, 10MACRO for Interlisp-10, VAXMACRO for Interlisp-VAX, JMACRO for Interlisp-Jerico, and MACRO for "implementation independent" macros. The global variable COMPILERMACROPROPS is a list of macro property names. Interlisp determines whether a litatom has a macro definition by checking these property names, in order, and using the first non-NIL property value as the macro definition. In Interlisp-D this list contains DMACRO and MACRO in that order so that DMACROs will override the implementation-independent MACRO properties. In general, use a DMACRO property for macros that are to be used only in Interlisp-D, use 10MACRO for macros that are to be used only in Interlisp-10, and use MACRO for macros that are to affect both systems. Macro definitions can take the following forms: (LAMBDA ...) (NLAMBDA ...) A function can be made to compile open by giving it a macro definition of the form (LAMBDA ...) or (NLAMBDA ...), e.g., (LAMBDA (X) (COND ((GREATERP X 0) X) (T (MINUS X)))) for ABS. The effect is as if the macro definition were written in place of the function wherever it appears in a function being compiled, i.e., it compiles as a lambda or nlambda expression. This saves the time necessary to call the function at the price of more compiled code generated in-line. (NIL EXPRESSION) (LIST EXPRESSION) "Substitution" macro. Each argument in the form being evaluated or compiled is substituted for the corresponding atom in LIST, and the result of the substitution is used instead of the form. For example, if the macro definition of ADD1 is ((X) (IPLUS X 1)), then, (ADD1 (CAR Y)) is compiled as (IPLUS (CAR Y) 1). Note that ABS could be defined by the substitution macro ((X) (COND ((GREATERP X 0) X) (T (MINUS X)))). In this case, however, (ABS (FOO X)) would compile as (COND ((GREATERP (FOO X) 0) (FOO X)) (T (MINUS (FOO X)))) and (FOO X) would be evaluated two times. (Code to evaluate (FOO X) would be generated three times.) (OPENLAMBDA ARGS BODY) This is a cross between substitution and LAMBDA macros. When the compiler processes an OPENLAMBDA, it attempts to substitute the actual arguments for the formals wherever this preserves the frequency and order of evaluation that would have resulted from a LAMBDA expression, and produces a LAMBDA binding only for those that require it. Note: OPENLAMBDA assumes that it can substitute literally the actual arguments for the formal arguments in the body of the macro if the actual is side-effect free or a constant. Thus, you should be careful to use names in ARGS which don't occur in BODY (except as variable references). For example, if FOO has a macro definition of (OPENLAMBDA (ENV) (FETCH (MY-RECORD-TYPE ENV) OF BAR)) then (FOO NIL) will expand to (FETCH (MY-RECORD-TYPE NIL) OF BAR) T When a macro definition is the atom T, it means that the compiler should ignore the macro, and compile the function definition; this is a simple way of turning off other macros. For example, the user may have a function that runs in both Interlisp-D and Interlisp-10, but has a macro definition that should only be used when compiling in Interlisp-10. If the MACRO property has the macro specification, a DMACRO of T will cause it to be ignored by the Interlisp-D compiler. This DMACRO would not be necessary if the macro were specified by a 10MACRO instead of a MACRO. (= . OTHER-FUNCTION) A simple way to tell the compiler to compile one function exactly as it would compile another. For example, when compiling in Interlisp-D, FRPLACAs are treated as RPLACAs. This is achieved by having FRPLACA have a DMACRO of (= . RPLACA). (LITATOM EXPRESSION) If a macro definition begins with a litatom other than those given above, this allows computation of the Interlisp expression to be evaluated or compiled in place of the form. LITATOM is bound to the CDR of the calling form, EXPRESSION is evaluated, and the result of this evaluation is evaluated or compiled in place of the form. For example, LIST could be compiled using the computed macro: [X (LIST 'CONS (CAR X) (AND (CDR X) (CONS 'LIST (CDR X] This would cause (LIST X Y Z) to compile as (CONS X (CONS Y (CONS Z NIL))). Note the recursion in the macro expansion. If the result of the evaluation is the litatom IGNOREMACRO, the macro is ignored and the compilation of the expression proceeds as if there were no macro definition. If the litatom in question is normally treated specially by the compiler (CAR, CDR, COND, AND, etc.), and also has a macro, if the macro expansion returns IGNOREMACRO, the litatom will still be treated specially. In Interlisp-10, if the result of the evaluation is the atom INSTRUCTIONS, no code will be generated by the compiler. It is then assumed the evaluation was done for effect and the necessary code, if any, has been added. This is a way of giving direct instructions to the compiler if you understand it. It is often useful, when constructing complex macro expressions, to use the BQUOTE facility (see the Read Macros section of Chapter 25). The following function is quite useful for debugging macro definitions: (EXPANDMACRO(EXPANDMACRO (Function) NIL NIL ("10") 18) EXP QUIETFLG % %) [Function] Takes a form whose CAR has a macro definition and expands the form as it would be compiled. The result is prettyprinted, unless QUIETFLG=T, in which case the result is simply returned. DEFMACRO Macros defined with the function DEFMACRO are much like "computed" macros (see the above section), in that they are defined with a form that is evaluated, and the result of the evaluation is used (evaluated or compiled) in place of the macro call. However, DEFMACRO macros support complex argument lists with optional arguments, default values, and keyword arguments. In addition, argument list destructuring is supported. (DEFMACRO(DEFMACRO (Function) NIL NIL ("10") 18) NAME ARGS FORM) [NLambda NoSpread Function] Defines NAME as a macro with the arguments ARGS and the definition form FORM (NAME, ARGS, and FORM are unevaluated). If an expression starting with NAME is evaluated or compiled, arguments are bound according to ARGS, FORM is evaluated, and the value of FORM is evaluated or compiled instead. The interpretation of ARGS is described below. Note: Unlike the function DEFMACRO in Common Lisp, this function currently does not remove any function definition for NAME. ARGS is a list that defines how the argument list passed to the macro NAME is interpreted. Specifically, ARGS defines a set of variables that are set to various arguments in the macro call (unevaluated), that FORM can reference to construct the macro form. In the simplest case, ARGS is a simple list of variable names that are set to the corresponding elements of the macro call (unevaluated). For example, given: (DEFMACRO FOO (A B) (LIST 'PLUS A B B)) The macro call (FOO X (BAR Y Z)) will expand to (PLUS X (BAR Y Z) (BAR Y Z)). "&-keywords" (beginning with the character "&") that are used to set variables to particular items from the macro call form, as follows: &OPTIONAL Used to define optional arguments, possibly with default values. Each element on ARGS after &OPTIONAL until the next &-keyword or the end of the list defines an optional argument, which can either be a litatom or a list, interpreted as follows: VAR If an optional argument is specified as a litatom, that variable is set to the corresponding element of the macro call (unevaluated). (VAR DEFAULT) If an optional argument is specified as a two element list, VAR is the variable to be set, and DEFAULT is a form that is evaluated and used as the default if there is no corresponding element in the macro call. (VAR DEFAULT VARSETP) If an optional argument is specified as a three element list, VAR and DEFAULT are the variable to be set and the default form, and VARSETP is a variable that is set to T if the optional argument is given in the macro call, NIL otherwise. This can be used to determine whether the argument was not given, or whether it was specified with the default value. For example, after (DEFMACRO FOO (&OPTIONAL A (B 5) (C 6 CSET)) FORM) expanding the macro call (FOO) would cause FORM to be evaluated with A set to NIL, B set to 5, C set to 6, and CSET set to NIL. (FOO 4 5 6) would be the same, except that A would be set to 4 and CSET would be set to T. &REST &BODY Used to get a list of all additional arguments from the macro call. Either &REST or &BODY should be followed by a single litatom, which is set to a list of all arguments to the macro after the position of the &-keyword. For example, given (DEFMACRO FOO (A B &REST C) FORM) expanding the macro call (FOO 1 2 3 4 5) would cause FORM to be evaluated with A set to 1, B set to 2, and C set to (3 4 5). If the macro calling form contains keyword arguments (see &KEY below), these are included in the &REST list. &KEY Used to define keyword arguments, that are specified in the macro call by including a "keyword" (a litatom starting with the character ":") followed by a value. Each element on ARGS after &KEY until the next &-keyword or the end of the list defines a keyword argument, which can either be a litatom or a list, interpreted as follows: VAR (VAR) ((KEYWORD VAR)) If a keyword argument is specified by a single litatom VAR, or a one-element list containing VAR, it is set to the value of a keyword argument, where the keyword used is created by adding the character ":" to the front of VAR. If a keyword argument is specified by a single-element list containing a two-element list, KEYWORD is interpreted as the keyword (which should start with the letter ":"), and VAR is the variable to set. (VAR DEFAULT) ((KEYWORD VAR) DEFAULT) (VAR DEFAULT VARSETP) ((KEYWORD VAR) DEFAULT VARSETP) If a keyword argument is specified by a two- or three-element list, the first element of the list specifies the keyword and variable to set as above. Similar to &OPTIONAL (above), the second element DEFAULT is a form that is evaluated and used as the default if there is no corresponding element in the macro call, and the third element VARSETP is a variable that is set to T if the optional argument is given in the macro call, NIL otherwise. For example, the form (DEFMACRO FOO (&KEY A (B 5 BSET) ((:BAR C) 6 CSET)) FORM) Defines a macro with keys :A, :B (defaulting to 5), and :BAR. Expanding the macro call (FOO :BAR 2 :A 1) would cause FORM to be evaluated with A set to 1, B set to 5, BSET set to NIL, C set to 2, and CSET set to T. &ALLOW-OTHER-KEYS It is an error for any keywords to be suplied in a macro call that are not defined as keywords in the macro argument list, unless either the &-keyword &ALLOW-OTHER-KEYS appears in ARGS, or the keyword :ALLOW-OTHER-KEYS (with a non-NIL value) appears in the macro call. &AUX Used to bind and initialize auxiliary varables, using a syntax similar to PROG (see the PROG and Associated Control Functions section of Chapter 9). Any elements after &AUX should be either litatoms or lists, interpreted as follows: VAR Single litatoms are interpreted as auxiliary variables that are initially bound to NIL. (VAR EXP) If an auxiliary variable is specified as a two element list, VAR is a variable initially bound to the result of evaluating the form EXP. For example, given (DEFMACRO FOO (A B &AUX C (D 5)) FORM) C will be bound to NIL and D to 5 when FORM is evaluated. &WHOLE Used to get the whole macro calling form. Should be the first element of ARGS, and should be followed by a single litatom, which is set to the entire macro calling form. Other &-keywords or arguments can follow. For example, given (DEFMACRO FOO (&WHOLE X A B) FORM) Expanding the macro call (FOO 1 2) would cause FORM to be evaluated with X set to (FOO 1 2), A set to 1, and B set to 2. DEFMACRO macros also support argument list "destructuring," a facility for accessing the structure of individual arguments to a macro. Any place in an argument list where a litatom is expected, an argument list (in the form described above) can appear instead. Such an embedded argument list is used to match the corresponding parts of that particular argument, which should be a list structure in the same form. In the simplest case, where the embedded argument list does not include &-keywords, this provides a simple way of picking apart list structures passed as arguments to a macro. For example, given (DEFMACRO FOO (A (B (C . D)) E) FORM) Expanding the macro call (FOO 1 (2 (3 4 5)) 6) would cause FORM to be evaluated with with A set to 1, B set to 2, C set to 3, D set to (4 5), and E set to 6. Note that the embedded argument list (B (C . D)) has an embedded argument list (C . D). Also notice that if an argument list ends in a dotted pair, that the final litatom matches the rest of the arguments in the macro call. An embedded argument list can also include &-keywords, for interpreting parts of embedded list structures as if they appeared in a top-level macro call. For example, given (DEFMACRO FOO (A (B &OPTIONAL (C 6)) D) FORM) Expanding the macro call (FOO 1 (2) 3) would cause FORM to be evaluated with with A set to 1, B set to 2, C set to 6 (because of the default value), and D set to 3. Warning: Embedded argument lists can only appear in positions in an argument list where a list is otherwise not accepted. In the above example, it would not be possible to specify an embedded argument list after the &OPTIONAL keyword, because it would be interpreted as an optional argument specification (with variable name, default value, set variable). However, it would be possible to specify an embedded argument list as the first element of an optional argument specification list, as so: (DEFMACRO FOO (A (B &OPTIONAL ((X (Y) Z) '(1 (2) 3))) D) FORM) In this case, X, Y, and Z default to 1, 2, and 3, respectively. Note that the "default" value has to be an appropriate list structure. Also, in this case either the whole structure (X (Y) Z) can be supplied, or it can be defaulted (i.e., is not possible to specify X while letting Y default). Interpreting Macros When the interpreter encounters a form CAR of which is an undefined function, it tries interpreting it as a macro. If CAR of the form has a macro definition, the macro is expanded, and the result of this expansion is evaluated in place of the original form. CLISPTRAN (see the Miscellaneous Functions and Variables section of Chapter21) is used to save the result of this expansion so that the expansion only has to be done once. On subsequent occasions, the translation (expansion) is retrieved from CLISPARRAY the same as for other CLISP constructs. Note: Because of the way that the evaluator processes macros, if you have a macro on FOO, then typing (FOO 'A 'B) will work, but FOO(A B) will not work. Sometimes, macros contain calls to functions that assume that the macro is being compiled. The variable SHOULDCOMPILEMACROATOMS is a list of functions that should be compiled to work correctly (initially (OPCODES) in Interlisp-D, (ASSEMBLE LOC) in Interlisp-10). UNSAFEMACROATOMS is a list of functions which effect the operation of the compiler, so such macro forms shouldn't even be expanded except by the compiler (initially NIL in Interlisp-D, (C2EXP STORIN CEXP COMP) in Interlisp-10). If the interpreter encounters a macro containing calls to functions on these two lists, instead of the macro being expanded, a dummy function is created with the form as its definition, and the dummy function is then compiled. A form consisting of a call to this dummy function with no arguments is then evaluated in place of the original form, and CLISPTRAN is used to save the translation as described above. There are some situations for which this procedure is not amenable, e.g. a GO inside the form which is being compiled will cause the compiler to give an UNDEFINED TAG error message because it is not compiling the entire function, just a part of it. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "10-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "10-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "10-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "10-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "10-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "10-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))%-T,ll/ll,ll +,,,/$,/ll0T/HH2,2l2Hll55,ll3HZ +T,ll,ll3$$(T,HH +,HH-T3(T,HH,F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR6T,,,CLASSIC +TITAN +TITAN +TITAN + HELVETICA CLASSIC +CLASSIC +CLASSIC + HELVETICA HELVETICA HELVETICA +CLASSIC +MODERNMODERN +! HRULE.GETFNCLASSIC + !    HRULE.GETFNCLASSIC +   6   HRULE.GETFNCLASSIC + 6   HRULE.GETFNCLASSIC +   IM.CHAP.GETFN HELVETICA7 + HRULE.GETFNMODERN + B #Q    G  ;  4.  V    X   1   E   HRULE.GETFNMODERN +}   N 0  _    8       A :   (  Y W  DIM.INDEX.GETFN  +     5   G   +  #  $ +G     + 5  B E :    FIM.INDEX.GETFN x  +      ,  #  $ +1 + 5  n 4 9   ] _  HIM.INDEX.GETFN z  +       V           [ !IM.INDEX.GETFN  + D  _ H  6  $IM.INDEX.GETFN  + D     8   # i   9  ! +JIM.INDEX.GETFN   +          V  *   i :IM.INDEX.GETFN   #IM.INDEX.GETFN  +  M U    #    $  ! % "  &        +    $     B   R 8 * #IM.INDEX.GETFN  +         <     $IM.INDEX.GETFN  +         %IM.INDEX.GETFN  +/         5                 #IM.INDEX.GETFN  +#    &   %IM.INDEX.GETFN  + M      + N ^    %     "  =  4 (  *IM.INDEX.GETFN  + 5   + 3 b  +   +   +  6<  +  !        + + B 4  +     +     P  +     HRULE.GETFNMODERN +  @  %IM.INDEX.GETFNCLASSIC +  3  + -.     $ 6  $IM.INDEX.GETFNCLASSIC +    %  +   =  :      3 2IM.INDEX.GETFNCLASSIC +    #   7 $IM.INDEX.GETFNCLASSIC + 4         +#   2  (   G    F        v  T       "IM.INDEX.GETFNCLASSIC + #   )    .   +"IM.INDEX.GETFNCLASSIC +       (  .    +"IM.INDEX.GETFNCLASSIC +"           3 (      #IM.INDEX.GETFNCLASSIC +"    '    HRULE.GETFNMODERN +{ A     #IM.INDEX.GETFN +      [ P  ) + ` S  /  >  . $IM.INDEX.GETFN   E   '"IM.INDEX.GETFN     A H      #IM.INDEX.GETFN    ;   J R t !  5  j ) $IM.INDEX.GETFN  5      &+IM.INDEX.GETFN  $   5  t      $IM.INDEX.GETFN  S    ! % ;      &IM.INDEX.GETFN      R :   @   b  +'IM.INDEX.GETFN    +  7 K  +W + +5 ? =  +  &  +  3   HRULE.GETFNMODERN +Q   5   o       7     + b  1         7                 !     +        4          *           + ?     E          0        .  +  t 7 : #0 6    e $E < 6      %    8   O                %    ;       g '                 $ ,       +       *   #      1  ,  !                +  +       *  ;  +     $  +           K      +%    HRULE.GETFNMODERN +   j  !  <                        _  8       +       Z 2     =      +       E      W  @    ^ 4      !    *     4 4  $  .  0 5   =  7     $ 6   ( *   K {  ,   e y   $   ? ?   1 '  f  7   -          HRULE.GETFNMODERN + ; {   h +S 9     ?  L   /  B > - 0 "   T   4 # "  +   +{ k      , (  $## 2 "  * ) +  )  +  3  9  # % D )  @ 9            +W P   +n - ####  . 1     > / ?      '    *IM.INDEX.GETFNTITAN +    k  . !    'IM.INDEX.GETFN       3 <   :   U  B  d ,    (   , \  S      >  m  @  6  6   -                "   M            < #       %     9 # ~ ^ M  %           6   3                    #  K [ = % U  % ? D  % $ %      K            +\ $             (    )        %     ;       K    ' M   +) W    i M     r  L S 8 "z \ No newline at end of file diff --git a/docs/porter-irm/vol1/11-VAR-BINDINGS.INTERPRESS b/docs/porter-irm/vol1/11-VAR-BINDINGS.INTERPRESS new file mode 100644 index 00000000..457e3876 Binary files /dev/null and b/docs/porter-irm/vol1/11-VAR-BINDINGS.INTERPRESS differ diff --git a/docs/porter-irm/vol1/11-VAR-BINDINGS.TEDIT b/docs/porter-irm/vol1/11-VAR-BINDINGS.TEDIT new file mode 100644 index 00000000..d4f54c78 --- /dev/null +++ b/docs/porter-irm/vol1/11-VAR-BINDINGS.TEDIT @@ -0,0 +1,118 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 11. VARIABLE BINDINGS AND THE INTERLISP STACK 1 11. VARIABLE BINDINGS AND THE INTERLISP STACK 1 "11"11. VARIABLE BINDINGS AND THE INTERLISP STACK 6 A number of schemes have been used in different implementations of Lisp for storing the values of variables. These include: 1. Storing values on an association list paired with the variable names. 2. Storing values on the property list of the atom which is the name of the variable. 3. Storing values in a special value cell associated with the atom name, putting old values on the function call stack, and restoring these values when exiting from a function. 4. Storing values on on the function call stack. Interlisp-10 uses the third scheme, so called "shallow binding". When a function is entered, the value of each variable bound by the function (function argument) is stored in a value cell associated with that variable name. The value that was in the value cell is stored in a block of storage called the basic frame for this function call. In addition, on exit from the function each variable must be individually unbound; that is, the old value saved in the basic frame must be restored to the value cell. Thus there is a higher cost for binding and unbinding a variable than in the fourth scheme, "deep binding". However, to find the current value of any variable, it is only necessary to access the variable's value cell, thus making variable reference considerably cheaper under shallow binding than under deep binding, especially for free variables. However, the shallow binding scheme used does require an additional overhead in switching contexts when doing "spaghetti stack" operations. Interlisp-D uses the forth scheme, "deep binding." Every time a function is entered, a basic frame containing the new variables is put on top of the stack. Therefore, any variable reference requires searching the stack for the first instance of that variable, which makes free variable use somewhat more expensive than in a shallow binding scheme. On the other hand, spaghetti stack operations are considerably faster. Some other tricks involving copying freely-referenced variables to higher frames on the stack are also used to speed up the search. The basic frames are allocated on a stack; for most user purposes, these frames should be thought of as containing the variable names associated with the function call, and the current values for that frame. The descriptions of the stack functions in below are presented from this viewpoint. Both interpreted and compiled functions store both the names and values of variables so that interpreted and compiled functions are compatible and can be freely intermixed, i.e., free variables can be used with no SPECVAR declarations necessary. However, it is possible to suppress storing of names in compiled functions, either for efficiency or to avoid a clash, via a LOCALVAR declaration (see the Local Variables and Special Variables section of Chapter 18). The names are also very useful in debugging, for they make possible a complete symbolic backtrace in case of error. In addition to the binding information, additional information is associated with each function call: access information indicating the path to search the basic frames for variable bindings, control information, and temporary results are also stored on the stack in a block called the frame extension. The interpreter also stores information about partially evaluated expressions as described in the Stack and Interpreter section of Chapter 11. Spaghetti Stack 1 The Bobrow/Wegbreit paper, "A Model and Stack Implementation for Multiple Environments" (Communications of the ACM, Vol. 16, 10, October 1973.), describes an access and control mechanism more general than a simple linear stack. The access and control mechanism used by Interlisp is a slightly modified version of the one proposed by Bobrow and Wegbreit. This mechanism is called the "spaghetti stack." The spaghetti system presents the access and control stack as a data structure composed of "frames." The functions described below operate on this structure. These primitives allow user functions to manipulate the stack in a machine independent way. Backtracking, coroutines, and more sophisticated control schemes can be easily implemented with these primitives. The evaluation of a function requires the allocation of storage to hold the values of its local variables during the computation. In addition to variable bindings, an activation of a function requires a return link (indicating where control is to go after the completion of the computation) and room for temporaries needed during the computation. In the spaghetti system, one "stack" is used for storing all this information, but it is best to view this stack as a tree of linked objects called frame extensions (or simply frames). A frame extension is a variable sized block of storage containing a frame name, a pointer to some variable bindings (the BLINK), and two pointers to other frame extensions (the ALINK and CLINK). In addition to these components, a frame extension contains other information (such as temporaries and reference counts) that does not interest us here. The block of storage holding the variable bindings is called a basic frame. A basic frame is essentially an array of pairs, each of which contains a variable name and its value. The reason frame extensions point to basic frames (rather than just having them "built in") is so that two frame extensions can share a common basic frame. This allows two processes to communicate via shared variable bindings. The chain of frame extensions which can be reached via the successive ALINKs from a given frame is called the "access chain" of the frame. The first frame in the access chain is the starting frame. The chain through successive CLINKs is called the "control chain". A frame extension completely specifies the variable bindings and control information necessary for the evaluation of a function. Whenever a function (or in fact, any form which generally binds local variables) is evaluated, it is associated with some frame extension. In the beginning there is precisely one frame extension in existence. This is the frame in which the top-level call to the interpreter is being run. This frame is called the "top-level" frame. Since precisely one function is being executed at any instant, exactly one frame is distinguished as having the "control bubble" in it. This frame is called the active frame. Initially, the top-level frame is the active frame. If the computation in the active frame invokes another function, a new basic frame and frame extension are built. The frame name of this basic frame will be the name of the function being called. The ALINK, BLINK, and CLINK of the new frame all depend on precisely how the function is invoked. The new function is then run in this new frame by passing control to that frame, i.e., it is made the active frame. Once the active computation has been completed, control normally returns to the frame pointed to by the CLINK of the active frame. That is, the frame in the CLINK becomes the active frame. In most cases, the storage associated with the basic frame and frame extension just abandoned can be reclaimed. However, it is possible to obtain a pointer to a frame extension and to "hold on" to this frame even after it has been exited. This pointer can be used later to run another computation in that environment, or even "continue" the exited computation. A separate data type, called a stack pointer, is used for this purpose. A stack pointer is just a cell that literally points to a frame extension. Stack pointers print as #ADR/FRAMENAME, e.g., #1,13636/COND. Stack pointers are returned by many of the stack manipulating functions described below. Except for certain abbreviations (such as "the frame with such-and-such a name"), stack pointers are the only way the user can reference a frame extension. As long as the user has a stack pointer which references a frame extension, that frame extension (and all those that can be reached from it) will not be garbage collected. Two stack pointers referencing the same frame extension are not necessarily EQ, i.e., (EQ (STKPOS 'FOO) (STKPOS 'FOO))=NIL. However, EQP can be used to test if two different stack pointers reference the same frame extension (see the Equality Predicates section of Chapter 9). It is possible to evaluate a form with respect to an access chain other than the current one by using a stack pointer to refer to the head of the access chain desired. Note, however, that this can be very expensive when using a shallow binding scheme such as that in Interlisp-10. When evaluating the form, since all references to variables under the shallow binding scheme go through the variable's value cell, the values in the value cells must be adjusted to reflect the values appropriate to the desired access chain. This is done by changing all the bindings on the current access chain (all the name-value pairs) so that they contain the value current at the time of the call. Then along the new access path, all bindings are made to contain the previous value of the variable, and the current value is placed in the value cell. For that part of the access path which is shared by the old and new chain, no work has to be done. The context switching time, i.e. the overhead in switching from the current, active, access chain to another one, is directly proportional to the size of the two branches that are not shared between the access contexts. This cost should be remembered in using generators and coroutines (see the Generators section below). Stack Functions 1 In the descriptions of the stack functions below, when we refer to an argument as a stack descriptor, we mean that it is one of the following: A stack pointer An object that points to a frame on the stack. Stack pointers are returned by many of the stack manipulating functions described below. NIL Specifies the active frame; that is, the frame of the stack function itself. T Specifies the top-level frame. A litatom Specifies the first frame (along the control chain from the active frame) that has the frame name LITATOM. Equivalent to (STKPOS LITATOM -1). A list of litatoms Specifies the first frame (along the control chain from the active frame) whose frame name is included in the list. A number N Specifies the Nth frame back from the active frame. If N is negative, the control chain is followed, otherwise the access chain is followed. Equivalent to (STKNTH N) In the stack functions described below, the following errors can occur: The error ILLEGAL STACK ARG occurs when a stack descriptor is expected and the supplied argument is either not a legal stack descriptor (i.e., not a stack pointer, litatom, or number), or is a litatom or number for which there is no corresponding stack frame, e.g., (STKNTH -1 'FOO) where there is no frame named FOO in the active control chain or (STKNTH -10 'EVALQT). The error STACK POINTER HAS BEEN RELEASED occurs whenever a released stack pointer is supplied as a stack descriptor argument for any purpose other than as a stack pointer to re-use. Note: The creation of a single stack pointer can result in the retention of a large amount of stack space. Therefore, one should try to release stack pointers when they are no longer needed (see the Releasing and Reusing Stack Pointers section below). Searching the Stack (STKPOS(STKPOS (Function) NIL NIL NIL 4) FRAMENAME N POS OLDPOS) [Function] Returns a stack pointer to the Nth frame with frame name FRAMENAME. The search begins with (and includes) the frame specified by the stack descriptor POS. The search proceeds along the control chain from POS if N is negative, or along the access chain if N is positive. If N is NIL, -1 is used. Returns a stack pointer to the frame if such a frame exists, otherwise returns NIL. If OLDPOS is supplied and is a stack pointer, it is reused. If OLDPOS is supplied and is a stack pointer and STKPOS returns NIL, OLDPOS is released. If OLDPOS is not a stack pointer it is ignored. Note: (STKPOS 'STKPOS) causes an error, ILLEGAL STACK ARG; it is not permissible to create a stack pointer to the active frame. (STKNTH(STKNTH (Function) NIL NIL NIL 4) N POS OLDPOS) [Function] Returns a stack pointer to the Nth frame back from the frame specified by the stack descriptor POS. If N is negative, the control chain from POS is followed. If N is positive the access chain is followed. If N equals 0, STKNTH returns a stack pointer to POS (this provides a way to copy a stack pointer). Returns NIL if there are fewer than N frames in the appropriate chain. If OLDPOS is supplied and is a stack pointer, it is reused. If OLDPOS is not a stack pointer it is ignored. Note: (STKNTH 0) causes an error, ILLEGAL STACK ARG; it is not possible to create a stack pointer to the active frame. (STKNAME(STKNAME (Function) NIL NIL NIL 4) POS) [Function] Returns the frame name of the frame specified by the stack descriptor POS. (SETSTKNAME(SETSTKNAME (Function) NIL NIL NIL 4) POS NAME) [Function] Changes the frame name of the frame specified by POS to be NAME. Returns NAME. (STKNTHNAME(STKNTHNAME (Function) NIL NIL NIL 4) N POS) [Function] Returns the frame name of the Nth frame back from POS. Equivalent to (STKNAME (STKNTH N POS)) but avoids creation of a stack pointer. In summary, STKPOS converts function names to stack pointers, STKNTH converts numbers to stack pointers, STKNAME converts stack pointers to function names, and STKNTHNAME converts numbers to function names. Variable Bindings in Stack Frames The following functions are used for accessing and changing bindings. Some of functions take an argument, N, which specifies a particular binding in the basic frame. If N is a literal atom, it is assumed to be the name of a variable bound in the basic frame. If N is a number, it is assumed to reference the Nth binding in the basic frame. The first binding is 1. If the basic frame contains no binding with the given name or if the number is too large or too small, the error ILLEGAL ARG occurs. (STKSCAN(STKSCAN (Function) NIL NIL NIL 5) VAR IPOS OPOS) [Function] Searches beginning at IPOS for a frame in which a variable named VAR is bound. The search follows the access chain. Returns a stack pointer to the frame if found, otherwise returns NIL. If OPOS is a stack pointer it is reused, otherwise it is ignored. (FRAMESCAN(FRAMESCAN (Function) NIL NIL NIL 5) ATOM POS) [Function] Returns the relative position of the binding of ATOM in the basic frame of POS. Returns NIL if ATOM is not found. (STKARG(STKARG (Function) NIL NIL NIL 5) N POS %) [Function] Returns the value of the binding specified by N in the basic frame of the frame specified by the stack descriptor POS. N can be a literal atom or number. (STKARGNAME(STKARGNAME (Function) NIL NIL NIL 5) N POS) [Function] Returns the name of the binding specified by N, in the basic frame of the frame specified by the stack descriptor POS. N can be a literal atom or number. (SETSTKARG(SETSTKARG (Function) NIL NIL NIL 5) N POS VAL) [Function] Sets the value of the binding specified by N in the basic frame of the frame specified by the stack descriptor POS. N can be a literal atom or a number. Returns VAL. (SETSTKARGNAME(SETSTKARGNAME (Function) NIL NIL NIL 5) N POS NAME) [Function] Sets the variable name to NAME of the binding specified by N in the basic frame of the frame specified by the stack descriptor POS. N can be a literal atom or a number. Returns NAME. (STKNARGS(STKNARGS (Function) NIL NIL NIL 5) POS %) [Function] Returns the number of arguments bound in the basic frame of the frame specified by the stack descriptor POS. (VARIABLES(VARIABLES (Function) NIL NIL NIL 5) POS) [Function] Returns a list of the variables bound at POS. (STKARGS(STKARGS (Function) NIL NIL NIL 5) POS %) [Function] Returns a list of the values of the variables bound at POS. Evaluating Expressions in Stack Frames The following functions are used to evaluate an expression in a different environment: (ENVEVAL(ENVEVAL (Function) NIL NIL NIL 6) FORM APOS CPOS AFLG CFLG) [Function] Evaluates FORM in the environment specified by APOS and CPOS. That is, a new active frame is created with the frame specified by the stack descriptor APOS as its ALINK, and the frame specified by the stack descriptor CPOS as its CLINK. Then FORM is evaluated. If AFLG is not NIL, and APOS is a stack pointer, then APOS will be released. Similarly, if CFLG is not NIL, and CPOS is a stack pointer, then CPOS will be released. (ENVAPPLY(ENVAPPLY (Function) NIL NIL NIL 6) FN ARGS APOS CPOS AFLG CFLG) [Function] APPLYs FN to ARGS in the environment specified by APOS and CPOS. AFLG and CFLG have the same interpretation as with ENVEVAL. (EVALV(EVALV (Function) NIL NIL NIL 6) VAR POS RELFLG) [Function] Evaluates VAR, where VAR is assumed to be a litatom, in the access environment specifed by the stack descriptor POS. If VAR is unbound, EVALV returns NOBIND and does not generate an error. If RELFLG is non-NIL and POS is a stack pointer, it will be released after the variable is looked up. While EVALV could be defined as (ENVEVAL VAR POS NIL RELFLG) it is in fact somewhat faster. (STKEVAL(STKEVAL (Function) NIL NIL NIL 6) POS FORM FLG %) [Function] Evaluates FORM in the access environment of the frame specified by the stack descriptor POS. If FLG is not NIL and POS is a stack pointer, releases POS. The definition of STKEVAL is (ENVEVAL FORM POS NIL FLG). (STKAPPLY(STKAPPLY (Function) NIL NIL NIL 6) POS FN ARGS FLG) [Function] Similar to STKEVAL but applies FN to ARGS. Altering Flow of Control The following functions are used to alter the normal flow of control, possibly jumping to a different frame on the stack. RETEVAL and RETAPPLY allow evaluating an expression in the specified environment first. (RETFROM(RETFROM (Function) NIL NIL NIL 6) POS VAL FLG) [Function] Return from the frame specified by the stack descriptor POS, with the value VAL. If FLG is not NIL, and POS is a stack pointer, then POS is released. An attempt to RETFROM the top level (e.g., (RETFROM T)) causes an error, ILLEGAL STACK ARG. RETFROM can be written in terms of ENVEVAL as follows: (RETFROM (LAMBDA (POS VAL FLG) (ENVEVAL (LIST 'QUOTE VAL) NIL (if (STKNTH -1 POS (if FLG then POS)) else (ERRORX (LIST 19 POS))) NIL T))) (RETTO(RETTO (Function) NIL NIL NIL 6) POS VAL FLG) [Function] Like RETFROM, except returns to the frame specified by POS. (RETEVAL(RETEVAL (Function) NIL NIL NIL 7) POS FORM FLG %) [Function] Evaluates FORM in the access environment of the frame specified by the stack descriptor POS, and then returns from POS with that value. If FLG is not NIL and POS is a stack pointer, then POS is released. The definition of RETEVAL is equivalent to (ENVEVAL FORM POS (STKNTH -1 POS) FLG T), except that RETEVAL does not create a stack pointer. (RETAPPLY(RETAPPLY (Function) NIL NIL NIL 7) POS FN ARGS FLG) [Function] Similar to RETEVAL except applies FN to ARGS. Releasing and Reusing Stack Pointers The following functions and variables are used for manipulating stack pointers: (STACKP(STACKP (Function) NIL NIL NIL 7) X) [Function] Returns X if X is a stack pointer, otherwise returns NIL. (RELSTK(RELSTK (Function) NIL NIL NIL 7) POS) [Function] Release the stack pointer POS (see below). If POS is not a stack pointer, does nothing. Returns POS. (RELSTKP(RELSTKP (Function) NIL NIL NIL 7) X) [Function] Returns T is X is a released stack pointer, NIL otherwise. (CLEARSTK(CLEARSTK (Function) NIL NIL NIL 7) FLG) [Function] If FLG is NIL, releases all active stack pointers, and returns NIL. If FLG is T, returns a list of all the active (unreleased) stack pointers. CLEARSTKLST(CLEARSTKLST (Variable) NIL NIL NIL 7) [Variable] A variable used by the top-level executive. Every time the top-level executive is re-entered (e.g., following errors, or Control-D), CLEARSTKLST is checked. If its value is T, all active stack pointers are released using CLEARSTK. If its value is a list, then all stack pointers on that list are released. If its value is NIL, nothing is released. CLEARSTKLST is initially T. NOCLEARSTKLST(NOCLEARSTKLST (Variable) NIL NIL NIL 7) [Variable] A variable used by the top-level executive. If CLEARSTKLST is T (see above) all active stack pointers except those on NOCLEARSTKLST are released. NOCLEARSTKLST is initially NIL. If one wishes to use multiple environments that survive through Control-D, either CLEARSTKLST should be set to NIL, or else those stack pointers to be retained should be explicitly added to NOCLEARSTKLST. The creation of a single stack pointer can result in the retention of a large amount of stack space. Furthermore, this space will not be freed until the next garbage collection, even if the stack pointer is no longer being used, unless the stack pointer is explicitly released or reused. If there is sufficient amount of stack space tied up in this fashion, a STACK OVERFLOW condition can occur, even in the simplest of computations. For this reason, the user should consider releasing a stack pointer when the environment referenced by the stack pointer is no longer needed. The effects of releasing a stack pointer are: 1. The link between the stack pointer and the stack is broken by setting the contents of the stack pointer to the "released mark" (currently unboxed 0). A released stack pointer prints as #ADR/#0. 2. If this stack pointer was the last remaining reference to a frame extension; that is, if no other stack pointer references the frame extension and the extension is not contained in the active control or access chain, then the extension may be reclaimed, and is reclaimed immediately. The process repeats for the access and control chains of the reclaimed extension so that all stack space that was reachable only from the released stack pointer is reclaimed. A stack pointer may be released using the function RELSTK, but there are some cases for which RELSTK is not sufficient. For example, if a function contains a call to RETFROM in which a stack pointer was used to specify where to return to, it would not be possible to simultaneously release the stack pointer. (A RELSTK appearing in the function following the call to RETFROM would not be executed!) To permit release of a stack pointer in this situation, the stack functions that relinquish control have optional flag arguments to denote whether or not a stack pointer is to be released (AFLG and CFLG). Note that in this case releasing the stack pointer will not cause the stack space to be reclaimed immediately because the frame referenced by the stack pointer will have become part of the active environment. Another way of avoiding creating new stack pointers is to reuse stack pointers that are no longer needed. The stack functions that create stack pointers (STKPOS, STKNTH, and STKSCAN) have an optional argument which is a stack pointer to reuse. When a stack pointer is reused, two things happen. First the stack pointer is released (see above). Then the pointer to the new frame extension is deposited in the stack pointer. The old stack pointer (with its new contents) is the value of the function. Note that the reused stack pointer will be released even if the function does not find the specified frame. Note that even if stack pointers are explicitly being released, creation of many stack pointers can cause a garbage collection of stack pointer space. Thus, if the user's application requires creating many stack pointers, he definitely should take advantage of reusing stack pointers. Backtrace Functions The following functions perform a "backtrace," printing information about every frame on the stack. Arguments allow only backtracing a selected range of the stack, skipping selected frames, and printing different amounts of information about each frame. (BACKTRACE(BACKTRACE (Function) NIL NIL NIL 8) IPOS EPOS FLAGS FILE PRINTFN) [Function] Performs a backtrace beginning at the frame specified by the stack descriptor IPOS, and ending with the frame specified by the stack descriptor EPOS. FLAGS is a number in which the options of the BACKTRACE are encoded. If a bit is set, the corresponding information is included in the backtrace. bit 0 - print arguments of non-SUBRs bit 1 - print temporaries of the interpreter bit 2 - print SUBR arguments and local variables bit 3 - omit printing of UNTRACE: and function names bit 4 - follow access chain instead of control chain bit 5 - print temporaries, i.e. the blips (see the stack and interpreter section below) For example: If FLAGS=47Q, everything is printed. If FLAGS=21Q, follows the access chain, prints arguments. FILE is the file that the backtrace is printed to. FILE must be open. PRINTFN is used when printing the values of variables, temporaries, blips, etc. PRINTFN=NIL defaults to PRINT. (BAKTRACE(BAKTRACE (Function) NIL NIL NIL 9) IPOS EPOS SKIPFNS FLAGS FILE) [Function] Prints a backtrace from IPOS to EPOS onto FILE. FLAGS specifies the options of the backtrace, e.g., do/don't print arguments, do/don't print temporaries of the interpreter, etc., and is the same as for BACKTRACE. SKIPFNS is a list of functions. As BAKTRACE scans down the stack, the stack name of each frame is passed to each function in SKIPFNS, and if any of them return non-NIL, POS is skipped (including all variables). BAKTRACE collapses the sequence of several function calls corresponding to a call to a system package into a single "function" using BAKTRACELST as described below. For example, any call to the editor is printed as **EDITOR**, a break is printed as **BREAK**, etc. BAKTRACE is used by the BT, BTV, BTV+, BTV*, and BTV! break commands, with FLAGS=0, 1, 5, 7, and 47Q respectively. Note: BAKTRACE calls BACKTRACE with a PRINTFN of SHOWPRINT (see the Output Functions section of Chapter 25), so that if SYSPRETTYFLG=T, the values will be prettyprinted. BAKTRACELST(BAKTRACELST (Variable) NIL NIL NIL 9) [Variable] Used for telling BAKTRACE (therefore, the BT, BTV, etc. commands) to abbreviate various sequences of function calls on the stack by a single key, e.g. **BREAK**, **EDITOR**, etc. The operation of BAKTRACE and format of BAKTRACELST is described so that the user can add his own entries to BAKTRACELST. Each entry on BAKTRACELST is a list of the form (FRAMENAME KEY . PATTERN) or (FRAMENAME (KEY1 . PATTERN1) ... (KEYN . PATTERNN)), where a pattern is a list of elements that are either atoms, which match a single frame, or lists, which are interpreted as a list of alternative patterns, e.g. (PROGN **BREAK** EVAL ((ERRORSET BREAK1A BREAK1) (BREAK1))) BAKTRACE operates by scanning up the stack and, at each point, comparing the current frame name, with the frame names on BAKTRACELST, i.e. it does an ASSOC. If the frame name does appear, BAKTRACE attempts to match the stack as of that point with (one of) the patterns. If the match is successful, BAKTRACE prints the corresponding key, and continues with where the match left off. If the frame name does not appear, or the match fails, BAKTRACE simply prints the frame name and continues with the next higher frame (unless the SKIPFNS applied to the frame name are non-NIL as described above). Matching is performed by comparing atoms in the pattern with the current frame name, and matching lists as patterns, i.e. sequences of function calls, always working up the stack. For example, either of the sequence of function calls "... BREAK1 BREAK1A ERRORSET EVAL PROGN ..." or "... BREAK1 EVAL PROGN ..." would match with the sample entry given above, causing **BREAK** to be printed. Special features: f The litatom & can be used to match any frame. f The pattern "-" can be used to match nothing. - is useful for specifying an optional match, e.g. the example above could also have been written as (PROGN **BREAK** EVAL ((ERRORSET BREAK1A) -) BREAK1). f It is not necessary to provide in the pattern for matching dummy frames, i.e. frames for which DUMMYFRAMEP (see below) is true, e.g. in Interlisp-10, *PROG*LAM, *ENV*, NOLINKDEF1, etc. When working on a match, the matcher automatically skips over these frames when they do not match. f If a match succeeds and the KEY is NIL, nothing is printed. For example, (*PROG*LAM NIL EVALA *ENV). This sequence will occur following an error which then causes a break if some of the function's arguments are LOCALVARS. Other Stack Functions (DUMMYFRAMEP(DUMMYFRAMEP (Function) NIL NIL NIL 10) POS) [Function] Returns T if the user never wrote a call to the function at POS, e.g. in Interlisp-10, DUMMYFRAMEP is T for *PROG*LAM, *ENV*, and FOOBLOCK frames (see the Block Compiling section of Chapter 18). REALFRAMEP and REALSTKNTH can be used to write functions which manipulate the stack and work on either interpreted or compiled code: (REALFRAMEP(REALFRAMEP (Function) NIL NIL NIL 10) POS INTERPFLG) [Function] Returns POS if POS is a "real" frame, i.e. if POS is not a dummy frame and POS is a frame that does not disappear when compiled (such as COND); otherwise NIL. If INTERPFLG=T, returns POS if POS is not a dummy frame. For example, if (STKNAME POS)=COND, (REALFRAMEP POS) is NIL, but (REALFRAMEP POS T) is POS. (REALSTKNTH(REALFRAMEP (Function) NIL NIL NIL 10) N POS INTERPFLG OLDPOS) [Function] Returns a stack pointer to the Nth (or -Nth) frames for which (REALFRAMEP POS INTERPFLG) is POS. (MAPDL(REALFRAMEP (Function) NIL NIL NIL 10) MAPDLFN MAPDLPOS) [Function] Starts at MAPDLPOS and applies the function MAPDLFN to two arguments (the frame name and a stack pointer to the frame), for each frame until the top of the stack is reached. Returns NIL. For example, [MAPDL (FUNCTION (LAMBDA (X POS) (if (IGREATERP (STKNARGS POS) 2) then (PRINT X)] will print all functions of more than two arguments. (SEARCHPDL(SEARCHPDL (Function) NIL NIL NIL 10) SRCHFN SRCHPOS) [Function] Similar to MAPDL, except searches the stack starting at position SRCHPOS until it finds a frame for which SRCHFN, a function of two arguments applied to the name of the frame and the frame itself, is not NIL. Returns (NAME . FRAME) if such a frame is found, otherwise NIL. The Stack and the Interpreter 1 In addition to the names and values of arguments for functions, information regarding partially-evaluated expressions is kept on the push-down list. For example, consider the following definition of the function FACT (intentionally faulty): (FACT [LAMBDA (N) (COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N]) In evaluating the form (FACT 1), as soon as FACT is entered, the interpreter begins evaluating the implicit PROGN following the LAMBDA. The first function entered in this process is COND. COND begins to process its list of clauses. After calling ZEROP and getting a NIL value, COND proceeds to the next clause and evaluates T. Since T is true, the evaluation of the implicit PROGN that is the consequent of the T clause is begun. This requires calling the function ITIMES. However before ITIMES can be called, its arguments must be evaluated. The first argument is evaluated by retrieving the current binding of N from its value cell; the second involves a recursive call to FACT, and another implicit PROGN, etc. At each stage of this process, some portion of an expression has been evaluated, and another is awaiting evaluation. The output below (from Interlisp-10) illustrates this by showing the state of the push-down list at the point in the computation of (FACT 1) when the unbound atom L is reached. FACT(1) u.b.a. L {in FACT} in ((ZEROP N) L) (L broken) :BTV! *TAIL* (L) *ARG1 (((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N))))) COND *FORM* (COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N))))) *TAIL* ((COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N)))))) N 0 FACT *FORM* (FACT (SUB1 N)) *FN* ITIMES *TAIL* ((FACT (SUB1 N))) *ARGVAL* 1 *FORM* (ITIMES N (FACT (SUB1 N))) *TAIL* ((ITIMES N (FACT (SUB1 N)))) *ARG1 (((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N))))) COND *FORM* (COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N))))) *TAIL* ((COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N)))))) N 1 FACT **TOP** Internal calls to EVAL, e.g., from COND and the interpreter, are marked on the push-down list by a special mark or blip which the backtrace prints as *FORM*. The genealogy of *FORM*'s is thus a history of the computation. Other temporary information stored on the stack by the interpreter includes the tail of a partially evaluated implicit PROGN (e.g., a cond clause or lambda expression) and the tail of a partially evaluated form (i.e., those arguments not yet evaluated), both indicated on the backtrace by *TAIL*, the values of arguments that have already been evaluated, indicated by *ARGVAL*, and the names of functions waiting to be called, indicated by *FN*. *ARG1, ..., *ARGn are used by the backtrace to indicate the (unnamed) arguments to SUBRs. Note that a function is not actually entered and does not appear on the stack, until its arguments have been evaluated (except for nlambda functions, of course). Also note that the *ARG1, *FORM*, *TAIL*, etc. "bindings" comprise the actual working storage. In other words, in the above example, if a (lower) function changed the value of the *ARG1 binding, the COND would continue interpreting the new binding as a list of COND clauses. Similarly, if the *ARGVAL* binding were changed, the new value would be given to ITIMES as its first argument after its second argument had been evaluated, and ITIMES was actually called. *FORM*, *TAIL*, *ARGVAL*, etc., do not actually appear as variables on the stack, i.e., evaluating *FORM* or calling STKSCAN to search for it will not work. However, the functions BLIPVAL, SETBLIPVAL, and BLIPSCAN described below are available for accessing these internal blips. These functions currently know about four different types of blips: *FN* The name of a function about to be called *ARGVAL* An argument for a function about to be called *FORM* A form in the process of evaluation *TAIL* The tail of a COND clause, implicit PROGN, PROG, etc. (BLIPVAL(BLIPVAL (Function) NIL NIL NIL 12) BLIPTYP IPOS FLG) [Function] Returns the value of the specified blip of type BLIPTYP. If FLG is a number N, finds the Nth blip of the desired type, searching the control chain beginning at the frame specified by the stack descriptor IPOS. If FLG is NIL, 1 is used. If FLG is T, returns the number of blips of the specified type at IPOS. (SETBLIPVAL(SETBLIPVAL (Function) NIL NIL NIL 12) BLIPTYP IPOS N VAL) [Function] Sets the value of the specified blip of type BLIPTYP. Searches for the Nth blip of the desired type, beginning with the frame specified by the stack descriptor IPOS, and following the control chain. (BLIPSCAN(BLIPSCAN (Function) NIL NIL NIL 12) BLIPTYP IPOS) [Function] Returns a stack pointer to the frame in which a blip of type BLIPTYP is located. Search begins at the frame specified by the stack descriptor IPOS and follows the control chain. Generators 1 A generator is like a subroutine except that it retains information about previous times it has been called. Some of this state may be data (for example, the seed in a random number generator), and some may be in program state (as in a recursive generator which finds all the atoms in a list structure). For example, if LISTGEN is defined by: (DEFINEQ (LISTGEN (L) (if L then (PRODUCE (CAR L)) (LISTGEN (CDR L)))) we can use the function GENERATOR (described below) to create a generator that uses LISTGEN to produce the elements of a list one at a time, e.g., (SETQ GR (GENERATOR (LISTGEN '(A B C)))) creates a generator, which can be called by (GENERATE GR) to produce as values on successive calls, A, B, C. When GENERATE (not GENERATOR) is called the first time, it simply starts evaluating (LISTGEN '(A B C)). PRODUCE gets called from LISTGEN, and pops back up to GENERATE with the indicated value after saving the state. When GENERATE gets called again, it continues from where the last PRODUCE left off. This process continues until finally LISTGEN completes and returns a value (it doesn't matter what it is). GENERATE then returns GR itself as its value, so that the program that called GENERATE can tell that it is finished, i.e., there are no more values to be generated. (GENERATOR(GENERATOR (Function) NIL NIL NIL 13) FORM COMVAR) [NLambda Function] An nlambda function that creates a generator which uses FORM to compute values. GENERATOR returns a generator handle which is represented by a dotted pair of stack pointers. COMVAR is optional. If its value (EVAL of) is a generator handle, the list structure and stack pointers will be reused. Otherwise, a new generator handle will be constructed. GENERATOR compiles open. (PRODUCE(PRODUCE (Function) NIL NIL NIL 13) VAL) [Function] Used from within a generator to return VAL as the value of the corresponding call to GENERATE. (GENERATE(GENERATE (Function) NIL NIL NIL 13) HANDLE VAL) [Function] Restarts the generator represented by HANDLE. VAL is returned as the value of the PRODUCE which last suspended the operation of the generator. When the generator runs out of values, GENERATE returns HANDLE itself. Examples: The following function will go down recursively through a list structure and produce the atoms in the list structure one at a time. (DEFINEQ (LEAVESG (L) (if (ATOM L) then (PRODUCE L) else (LEAVESG (CAR L)) (if (CDR L) then (LEAVESG (CDR L)] The following function prints each of these atoms as it appears. It illustrates how a loop can be set up to use a generator. (DEFINEQ (PLEAVESG1 (L) (PROG (X LHANDLE) (SETQ LHANDLE (GENERATOR (LEAVESG L))) LP (SETQ X (GENERATE LHANDLE)) (if (EQ X LHANDLE) then (RETURN NIL)) (PRINT X) (GO LP))] The loop terminates when the value of the generator is EQ to the dotted pair which is the value produced by the call to GENERATOR. A CLISP iterative operator, OUTOF, is provided which makes it much easier to write the loop in PLEAVESG1. OUTOF (or outof) can precede a form which is to be used as a generator. On each iteration, the iteration variable will be set to successive values returned by the generator; the loop will be terminated automatically when the generator runs out. Therefore, the following is equivalent to the above program PLEAVESG1: (DEFINEQ (PLEAVESG2 (L) (for X outof (LEAVESG L) do (PRINT x))] Here is another example; the following form will print the first N atoms. (for X outof (MAPATOMS (FUNCTION PRODUCE)) as I from 1 to N do (PRINT X)) Coroutines 1 This package provides facilities for the creation and use of fully general coroutine structures. It uses a stack pointer to preserve the state of a coroutine, and allows arbitrary switching between N different coroutines, rather than just a call to a generator and return. This package is slightly more efficient than the generator package described above, and allows more flexibility on specification of what to do when a coroutine terminates. (COROUTINE(COROUTINE (Function) NIL NIL NIL 14) CALLPTR COROUTPTR COROUTFORM ENDFORM) [NLambda Function] This nlambda function is used to create a coroutine and initialize the linkage. CALLPTR and COROUTPTR are the names of two variables, which will be set to appropriate stack pointers. If the values of CALLPTR or COROUTPTR are already stack pointers, the stack pointers will be reused. COROUTFORM is the form which is evaluated to start the coroutine; ENDFORM is a form to be evaluated if COROUTFORM actually returns when it runs out of values. COROUTINE compiles open. (RESUME(RESUME (Function) NIL NIL NIL 14) FROMPTR TOPTR VAL) [Function] Used to transfer control from one coroutine to another. FROMPTR should be the stack pointer for the current coroutine, which will be smashed to preserve the current state. TOPTR should be the stack pointer which has preserved the state of the coroutine to be transferred to, and VAL is the value that is to be returned to the latter coroutine as the value of the RESUME which suspended the operation of that coroutine. For example, the following is the way one might write the LEAVES program using the coroutine package: (DEFINEQ (LEAVESC (L COROUTPTR CALLPTR) (if (ATOM L) then (RESUME COROUTPTR CALLPTR L) else (LEAVESC (CAR L) COROUTPTR CALLPTR) (if (CDR L) then (LEAVESC (CDR L) COROUTPTR CALLPTR))))] A function PLEAVESC which uses LEAVESC can be defined as follows: (DEFINEQ (PLEAVESC (L) (bind PLHANDLE LHANDLE first (COROUTINE PLHANDLE LHANDLE (LEAVESC L LHANDLE PLHANDLE) (RETFROM 'PLEAVESC)) do (PRINT (RESUME PLHANDLE LHANDLE))))] By RESUMEing LEAVESC repeatedly, this function will print all the leaves of list L and then return out of PLEAVESC via the RETFROM. The RETFROM is necessary to break out of the non-terminating do-loop. This was done to illustrate the additional flexibility allowed through the use of ENDFORM. We use two coroutines working on two trees in the example EQLEAVES, defined below. EQLEAVES tests to see whether two trees have the same leaf set in the same order, e.g., (EQLEAVES '(A B C) '(A B (C))) is true. (DEFINEQ (EQLEAVES (L1 L2) (bind LHANDLE1 LHANDLE2 PE EL1 EL2 first (COROUTINE PE LHANDLE1 (LEAVESC L1 LHANDLE1 PE) 'NO-MORE) (COROUTINE PE LHANDLE2 (LEAVESC L2 LHANDLE2 PE) 'NO-MORE) do (SETQ EL1 (RESUME PE LHANDLE1)) (SETQ EL2 (RESUME PE LHANDLE2)) (if (NEQ EL1 EL2) then (RETURN NIL)) repeatuntil (EQ EL1 'NO-MORE) finally (RETURN T)))] Possibilities Lists 1 A possibilities list is the interface between a generator and a consumer. The possibilities list is initialized by a call to POSSIBILITIES, and elements are obtained from it by using TRYNEXT. By using the spaghetti stack to maintain separate environments, this package allows a regime in which a generator can put a few items in a possibilities list, suspend itself until they have been consumed, and be subsequently aroused and generate some more. (POSSIBILITIES FORM) [NLambda Function] This nlambda function is used for the initial creation of a possibilities list. FORM will be evaluated to create the list. It should use the functions NOTE and AU-REVOIR described below to generate possibilities. Normally, one would set some variable to the possibilities list which is returned, so it can be used later, e.g.: (SETQ PLIST (POSSIBILITIES (GENERFN V1 V2))). POSSIBILITIES compiles open. (NOTE(NOTE (Function) NIL NIL NIL 15) VAL LSTFLG) [Function] Used within a generator to put items on the possibilities list being generated. If LSTFLG is equal to NIL, VAL is treated as a single item. If LSTFLG is non-NIL, then the list VAL is NCONCed on the end of the possibilities list. Note that it is perfectly reasonable to create a possibilities list using a second generator, and NOTE that list as possibilities for the current generator with LSTFLG equal to T. The lower generator will be resumed at the appropriate point. (AU-REVOIR(AU-REVOIR (Function) NIL NIL NIL 15) VAL) [NoSpread Function] Puts VAL on the possibilities list if it is given, and then suspends the generator and returns to the consumer in such a fashion that control will return to the generator at the AU-REVOIR if the consumer exhausts the possibilities list. NIL is not put on the possibilities list unless it is explicitly given as an argument to AU-REVOIR, i.e., (AU-REVOIR) and (AU-REVOIR NIL) are not the same. AU-REVOIR and ADIEU are lambda nospreads to enable them to distinguish these two cases. (ADIEU(ADIEU (Function) NIL NIL NIL 15) VAL) [NoSpread Function] Like AU-REVOIR except releases the generator instead of suspending it. (TRYNEXT(TRYNEXT (Function) NIL NIL NIL 16) PLST ENDFORM VAL) [NLambda Function] This nlambda function allows a consumer to use a possibilities list. It removes the first item from the possibilities list named by PLST (i.e. PLST must be an atom whose value is a possiblities list), and returns that item, provided it is not a generator handle. If a generator handle is encountered, the generator is reawakened. When it returns a possibilities list, this list is added to the front of the current list. When a call to TRYNEXT causes a generator to be awakened, VAL is returned as the value of the AU-REVOIR which put that generator to sleep. If PLST is empty, it evaluates ENDFORM in the caller's environment. TRYNEXT compiles open. (CLEANPOSLST(CLEANPOSLST (Function) NIL NIL NIL 16) PLST) [Function] This function is provided to release any stack pointers which may be left in the PLST which was not used to exhaustion. For example, FIB is a generator for fibonnaci numbers. It starts out by NOTEing its two arguments, then suspends itself. Thereafter, on being re-awakened, it will NOTE two more terms in the series and suspends again. PRINTFIB uses FIB to print the first N fibonacci numbers. (DEFINEQ (FIB (F1 F2) (do (NOTE F1) (NOTE F2) (SETQ F1 (IPLUS F1 F2)) (SETQ F2 (IPLUS F1 F2)) (AU-REVOIR)] Note that this AU-REVOIR just suspends the generator and adds nothing to the possibilities list except the generator. (DEFINEQ (PRINTFIB (N) (PROG ((FL (POSSIBILITIES (FIB 0 1)))) (RPTQ N (PRINT (TRYNEXT FL))) (CLEANPOSLST FL)] Note that FIB itself will never terminate. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "11-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "11-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "11-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "11-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "11-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "11-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))#-llT4$$(TT-HHT2l2llZ2Z2HZ +Z6lT2HTT2HHx2HH +x,,,ll,ll3HZ +T3K +T3$$(T,ll2Hll5,HH +-T2HZZ,HH,HH3(T,/F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR,5TITAN +TITAN +CLASSIC +CLASSIC  HELVETICA CLASSIC +CLASSIC + HELVETICA HELVETICA HELVETICA +CLASSIC +MODERNMODERNMODERN +! HRULE.GETFNCLASSIC + ! +   HRULE.GETFNCLASSIC +   +. +  HRULE.GETFNCLASSIC + . +  HRULE.GETFNCLASSIC +   IM.CHAP.GETFN HELVETICA/  HRULE.GETFNMODERN +} I V  1  -  D 5 Z    HRULE.GETFNMODERN +Y ! o  ]       k     + <        HRULE.GETFNMODERN +   N   m     +  ) d  S        +!IM.INDEX.GETFNCLASSIC +   U 4  +    Z  7 (    '   G !IM.INDEX.GETFNCLASSIC +   ?  %  /   9  & 7 '   E "IM.INDEX.GETFNCLASSIC + F   %IM.INDEX.GETFNCLASSIC + 1     %IM.INDEX.GETFNCLASSIC +    )  , % 0 +% !k ? ] -   "IM.INDEX.GETFNCLASSIC +   ' s  ;  +$IM.INDEX.GETFNCLASSIC + 0      !IM.INDEX.GETFNCLASSIC +  . C  "  %IM.INDEX.GETFNCLASSIC + - D  "  +$IM.INDEX.GETFNCLASSIC +  + C  -  (IM.INDEX.GETFNCLASSIC + +   C  -  #IM.INDEX.GETFNCLASSIC + +  h   +$IM.INDEX.GETFNCLASSIC + )   "IM.INDEX.GETFNCLASSIC + +  7  &W "IM.INDEX.GETFNCLASSIC +  + !  [ ?      "      #IM.INDEX.GETFNCLASSIC +   !    &   IM.INDEX.GETFNCLASSIC +  +  X    %   Q   "IM.INDEX.GETFNCLASSIC +   + J         #IM.INDEX.GETFNCLASSIC +     {  D "IM.INDEX.GETFNCLASSIC +  8                 4 .   IM.INDEX.GETFNCLASSIC +       "IM.INDEX.GETFNCLASSIC +   + J      !   + "  #IM.INDEX.GETFNCLASSIC +     $P !IM.INDEX.GETFNCLASSIC +   '  !IM.INDEX.GETFNCLASSIC +   0  "IM.INDEX.GETFNCLASSIC +      #IM.INDEX.GETFNCLASSIC +   2   @  &IM.INDEX.GETFNCLASSIC +   / _     (IM.INDEX.GETFN 0  '  +    R  L   1  .    3 % C  1   <  : \    @    +$IM.INDEX.GETFNCLASSIC + N >  ) \   -     5 X     . 0  J     #IM.INDEX.GETFNCLASSIC +        R   ' } H +                  >  $  &IM.INDEX.GETFNCLASSIC +    f  +    :       <  +q  " g  S #   + ) 9   +    !    ! d 4   b ,   +k     $ q   'IM.INDEX.GETFNCLASSIC +  3      8  + +l  &IM.INDEX.GETFNCLASSIC +      ;     +  (        &IM.INDEX.GETFNCLASSIC +       &IM.INDEX.GETFNCLASSIC +  +     ! ( 5  +%IM.INDEX.GETFNCLASSIC +  1 " - +  %    HRULE.GETFNMODERN +   &  <  1  7   +  )  6  w >       $ 8>@%'8">@  o    I @    B       :  7 I    K  9  +  # + # / # % #     #IM.INDEX.GETFNCLASSIC + 0    r     7   &IM.INDEX.GETFNCLASSIC + -  X #  $IM.INDEX.GETFNCLASSIC +  = K    HRULE.GETFNMODERN + 7  #% 3 8 ), *     8    8 5 1 @  6 O  +%IM.INDEX.GETFNCLASSIC +  8   :     #IM.INDEX.GETFNCLASSIC + ' +   $IM.INDEX.GETFNCLASSIC + + &  ! ^    +        #~    0 %    7 ?  >   $   +A   + "  HRULE.GETFNMODERN +   +%IM.INDEX.GETFNCLASSIC +$ Q  d  A +8  +.   "IM.INDEX.GETFNCLASSIC + : n f Q 1 : & (&-B   %1)+  =      :  P  + 'EE((# HRULE.GETFNMODERN +~ -   Q D    .   IM.INDEX.GETFNCLASSIC + + T   "     ;  + A  +%IM.INDEX.GETFNCLASSIC +   2 V      E !IM.INDEX.GETFNCLASSIC +  9 #IM.INDEX.GETFNCLASSIC +   $ $ ! (     'IM.INDEX.GETFNCLASSIC + Q #  9 X 3     ^ ,* +  k,z \ No newline at end of file diff --git a/docs/porter-irm/vol1/12-MISC.INTERPRESS b/docs/porter-irm/vol1/12-MISC.INTERPRESS new file mode 100644 index 00000000..71938fa1 Binary files /dev/null and b/docs/porter-irm/vol1/12-MISC.INTERPRESS differ diff --git a/docs/porter-irm/vol1/12-MISC.TEDIT b/docs/porter-irm/vol1/12-MISC.TEDIT new file mode 100644 index 00000000..097f550d Binary files /dev/null and b/docs/porter-irm/vol1/12-MISC.TEDIT differ diff --git a/docs/porter-irm/vol2/13-EXECUTIVE.INTERPRESS b/docs/porter-irm/vol2/13-EXECUTIVE.INTERPRESS new file mode 100644 index 00000000..b8b3ee75 Binary files /dev/null and b/docs/porter-irm/vol2/13-EXECUTIVE.INTERPRESS differ diff --git a/docs/porter-irm/vol2/13-EXECUTIVE.TEDIT b/docs/porter-irm/vol2/13-EXECUTIVE.TEDIT new file mode 100644 index 00000000..926eb9bb --- /dev/null +++ b/docs/porter-irm/vol2/13-EXECUTIVE.TEDIT @@ -0,0 +1,306 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 13. INTERLISP EXECUTIVE 1 13. INTERLISP EXECUTIVE 1 "13"13. INTERLISP EXECUTIVE 6 With any interactive computer language, you interact with the system through an "executive", which interprets and executes typed-in commands. In most implementations of Lisp, the executive is a simple "read-eval-print" loop, which repeatedly reads a Lisp expression, evaluates it, and prints out the value of the expression. Interlisp has an executive which allows a much greater range of inputs, other than just regular Interlisp expressions. In particular, the Interlisp executive(INTERLISP% EXECUTIVE NIL Interlisp% executive NIL ("13") 1) implements a facility known as the "programmer's assistant" (or "p.a."). The central idea of the programmer's assistant is that you are addressing an active intermediary, namely your assistant. Normally, the assistant is invisible, and simply carries out your requests. However, the assistant remembers what you have done, so you can give commands to repeat a particular operation or sequence of operations, with possible modifications, or to undo the effect of specified operations. Like DWIM, the programmer's assistant embodies an approach to system design whose ultimate goal is to construct an environment that "cooperates" with you in the development of your programs, and frees you to concentrate more fully on the conceptual difficulties and creative aspects of the problem at hand. The programmer's assistant facility features the use of memory structures called "history lists." A history list is a list of the information associated with each of the individual "events" that have occurred in the system, where each event corresponds to one user input. Associated with each event on the history list is the input and its value, plus other optional information such as side-effects, formatting information, etc. The following dialogue, taken from an actual session at the terminal, contains illustrative examples and gives the flavor of the programmer's assistant facility in Interlisp. The number before each prompt is the "event number" (see the Format and Use of the History List section below). 12(SETQ FOO 5) 5 13(SETQ FOO 10) (FOO reset) 10 The p.a. notices that the user has reset the value of FOO and informs the user. 14UNDO SETQ undone. 15FOOcr 5 This is the first example of direct communication with the p.a. The user has said to UNDO the previous input to the executive. . . . 25SET(LST1 (A B C)) (A B C) 26(SETQ LST2 '(D E F)) (D E F) 27(FOR X IN LST1 DO (REMPROP X 'MYPROP] NIL The user asked to remove the property MYPROP from the atoms A, B, and C. Now lets assume that is not what he wanted to do, but rather use the elements of LST2. 28UNDO FOR FOR undone. First he undoes the REMPROP, by undoing the iterative statement. Notice the UNDO accepted an "argument," although in this case UNDO by itself would be sufficient. 29USE LST2 FOR LST1 IN 27 NIL The user just instructed to go back to event number 27 and substitute LST2 for LST1 and then reexecute the expression. The user could have also specified -2 instead of 27 to specify a relative address. . . . 47(PUTHASH 'FOO (MKSTRING 'FOO) MYHASHARRAY) "FOO" If MKSTRING was a computationally expensive function (which it is not), then the user might be cacheing its value for later use. 48USE FIE FUM FOE FOR FOO IN MKSTRING "FIE" "FUM" "FOE" The user now decides to redo the PUTHASH several times with different values. He specifies the event by "IN MKSTRING" rather than PUTHASH. 49?? USE 48. USE FIE FUM FOE FOR FOO IN MKSTRING (PUTHASH (QUOTE FIE) (MKSTRING (QUOTE FIE)) MYHASHARRAY) "FIE" (PUTHASH (QUOTE FUM) (MKSTRING (QUOTE FUM)) MYHASHARRAY) "FUM" (PUTHASH (QUOTE FOE) (MKSTRING (QUOTE FOE)) MYHASHARRAY) "FOE" Here the user asks the p.a. (using the ?? command) what it has on its history list for the last input to the executive. Since the event corresponds to a programmer's assistant command that evaluates several forms, these forms are saved as the input, although the user's actual input, the p.a. command, is also saved in order to clarify the printout of that event. As stated earlier, the most common interaction with the programmer's assistant occurs at the top level read-eval-print loop, or in a break, where the user types in expressions for evaluation, and sees the values printed out. In this mode, the assistant acts much like a standard Lisp executive, except that before attempting to evaluate an input, the assistant first stores it in a new entry on the history list. Thus if the operation is aborted or causes an error, the input is still saved and available for modification and/or reexecution. The assistant also notes new functions and variables to be added to its spelling lists to enable future corrections. Then the assistant executes the computation (i.e., evaluates the form or applies the function to its arguments), saves the value in the entry on the history list corresponding to the input, and prints the result, followed by a prompt character to indicate it is again ready for input. If the input typed by the user is recognized as a p.a. command, the assistant takes special action. Commands such as UNDO and ?? are immediately performed. Commands that involved reexecution of previous inputs, such as REDO and USE, are achieved by computing the corresponding input expression(s) and then unreading them. The effect of this unreading operation is to cause the assistant's input routine, LISPXREAD, to act exactly as though these expressions were typed in by the user. These expressions are processed exactly as though they had been typed, except that they are not saved on new and separate entries on the history list, but associated with the history command that generated them. Input Formats(INTERLISP% EXECUTIVE NIL Interlisp% executive NIL ("13") 3 SUBNAME INPUT% FORMATS SUBTEXT input% formats) 1 The Interlisp-D executive accepts inputs in the following formats: EVALV-format input If you type a single litatom, followed by a carriage-return, the value of the litatom is returned. For example, if the value of the variable FOO is the list (A B C): FOOcr (A B C) EVAL-format input If you type a regular Interlisp expression, beginning with a left parenthesis or square bracket and terminated by a matching right parenthesis or square bracket, the form is simply passed to EVAL for evaluation. A right bracket matches any number of left parentheses, back to the last left bracket or the entire expression. It is not necessary to type a Return at the end of such a form; Interlisp supplies one automatically. If a Return is typed before the final matching right parenthesis or bracket, it is treated as a space, and input continues. The following examples are all interpreted the same: (PLUS 1 (TIMES 2 3)) 7 (PLUS 1 (TIMES 2 3] 7 (PLUS 1 (TIMEScr 2 3] 7 APPLY-format input Often, the user, typing at the keyboard, calls functions with constant argument values, which would have to be quoted if you typed it in "EVAL-format." For convience, if you type a litatom immediately followed by a list form, the litatom is APPLYed to the elements within the list, unevaluated. The input is terminated by the matching right parenthesis or bracket. For example, LOAD(FOO) is equivalent to typing (LOAD 'FOO), and GETPROP(X COLOR) is equivalent to (GETPROP 'X 'COLOR). APPLY-format input is useful in some situations, but note that it may produce unexpected results when an nlambda function is called that explicitly evaluates its arguments (see Chapter 10). For example, typing SETQ(FOO BAR) will set FOO to the value of BAR, not to the litatom BAR itself. Other input Sometimes, you may not want to terminate the input when a closing parenthesis is typed. For example, some programmer's assistant commands take several arguments, some of which can be lists. If the user types a sequence of litatoms and lists beginning with a litatom and a space (to distinguish it from APPLY-format), terminated by a carriage return or an extra right parenthesis or bracket, the Interlisp-D executive interprets it differently depending on the number of expressions typed. If only one expression is typed (a litatom), it is interpreted as an EVALV-format input, and the value of the litatom is returned: FOOcr (A B C) If exactly two expressions are typed, it is interpreted as APPLY-format input: LIST (A B)cr (A B) If three or more expressions are typed, it is interpreted as EVAL-format input. To warn you, the full expression is printed out before it is executed. For example: PLUS (TIMES 2 3) 1cr = (PLUS (TIMES 2 3) 1) 7 If LISPXREADFN (see the Programmer's Assistant Functiions section below) is set to READ (rather than the default, TTYINREAD), then whenever one of the elements typed is a list and the list is terminated by a right parenthesis or bracket, Interlisp will type a carriage-return and "..." to indicate that further input will be accepted. You can type further expressions or terminate the whole expression by a Return. Programmer's Assistant Commands(INTERLISP% EXECUTIVE NIL Interlisp% executive NIL ("13") 4 SUBNAME PROGRAMMER'S% ASSISTANT% COMMANDS SUBTEXT programmer's% assistant% commands) 1 The programmer's assistant recognizes a number of commands, which usually refer to past events on the history list. These commands are treated specially; for example, they may not be put on the history list. Note: If you define a function by the same name as a p.a. command, a warning message is printed to remind you that the p.a. command interpretation will take precedence for type-in. All programmer's assistant commands use the same conventions and syntax for indicating which event or events on the history list the command refers to, even though different commands may be concerned with different aspects of the corresponding event(s), e.g., side-effects, value, input, etc. Therefore, before discussing the various p.a. commands, the following section describes the types of event specifications currently implemented. Event Specification An event address identifies one event on the history list. It consists of a sequence of "commands" for moving an imaginary cursor up or down the history list, much in the manner of the arguments to the @ break command (see Chapter 14). The event identified is the one "under" the imaginary cursor when there are no more commands. (If any command fails, an error is generated and the history command is aborted.) For example, the event address 42 refers to the event with event number 42, 42 FOO refers to the first event (searching back from event 42) whose input contains the word FOO, and 42 FOO -1 refers to the event preceeding that event. Usually, an event address will contain only one or two commands. Most of the event address commands perform searches for events which satisfy some condition. Unless the command is given (see below), this search always goes backwards through the history list, from the most recent event specified to the oldest. Each search skips the current event. For example, if FOO refers to event N, FOO FIE will refer to some event before event N, even if there is a FIE in event N. The event address commands are interpreted as follows: N (an integer) If N is the first command in an event address, refers to the event with event number N. Otherwise, refers to the event N events forward (in direction of increasing event number). If N is negative, it always refers to the event -N events backwards. For example, -1 refers to the previous event, 42 refers to event number 42 (if the first command in an event address), and 42 3 refers to the event with event number 45. LITATOM Specifies the last event with an APPLY-format input whose function matches LITATOM. Note: There must not be a space between and LITATOM. Specifies that the next search is to go forward instead of backward. If given as the first event address command, the next search begins with last (oldest) event on the history list. F Specifies that the next object in the event address is to be searched for, regardless of what it is. For example, F -2 looks for an event containing -2. = Specifies that the next object (presumably a pattern) is to be matched against the values of events, instead of the inputs. \ Specifies the event last located. SUCHTHAT PRED Specifies an event for which the function PRED returns true. PRED should be a function of two arguments, the input portion of the event, and the event itself. See the Format and Use of the History List section below for a discussion of the format of events on the history list. PAT Any other event address command specifies an event whose input contains an expression that matches PAT as described in Chapter 16. The matching is performed by the function HISTORYMATCH (see the Programmer's Assistant Functions section below), which is initially defined to call EDITFINDP but can be advised or redefined for specialized applications. Symbols used below of the form EventAddressi refer to event addresses, described above. Since an event address may contain multiple words, the event address is parsed by searching for the words which delimit it. For example, in FROM EventAddress1 THRU EventAddress2, the symbol EventAddress1 corresponds to all words between FROM and THRU in the event specification, and EventAddress2 to all words from THRU to the end of the event specification. FROM EventAddress1 THRU EventAddress2 EventAddress1 THRU EventAddress2 Specifies the sequence of events from the event with address EventAddress1 through the event with address EventAddress2. For example, FROM 47 THRU 49 specifies events 47, 48, and 49. EventAddress1 can be more recent than EventAddress2. For example, FROM 49 THRU 47 specifies events 49, 48, and 47 (note reversal of order). FROM EventAddress1 TO EventAddress2 EventAddress1 TO EventAddress2 Same as THRU but does not include event EventAddress2. FROM EventAddress1 Same as FROM EventAddress1 THRU -1. For example, if the current event is number 53, then FROM 49 specifies events 49, 50, 51, and 52. THRU EventAddress2 Same as FROM -1 THRU EventAddress2. For example, if the current event is number 53, then THRU 49 specifies events 52, 51, 50, and 49 (note reversal of order). TO EventAddress2 Same as FROM -1 TO EventAddress2. ALL EventAddress1 Specifies all events satisfying EventAddress1. For example, ALL LOAD, ALL SUCHTHAT FOO. empty If nothing is specified, it is the same as specifying -1. In the special case that the last event was an UNDO, it is the same as specifying -2. For example, if you type (NCONC FOO FIE), you can then type UNDO, followed by USE NCONC1. EventSpec1 AND EventSpec2 AND ... AND EventSpecN Each of the EventSpeci is an event specification. The lists of events are concatenated. For example, FROM 30 THRU 32 AND 35 THRU 37 is the same as 30 AND 31 AND 32 AND 35 AND 36 AND 37. @ LITATOM If LITATOM is the name of a command defined via the NAME command (see the Commands section below), specifies the event(s) defining LITATOM. @@ EventSpec EventSpec is an event specification interpreted as above, but with respect to the archived history list (see the ARCHIVE command, in the Commands section below). If no events can be found that satisfy the event specification, spelling correction on each word in the event specification is performed using LISPXFINDSPLST as the spelling list. For example, REDO 3 THRUU 6 will work correctly. If the event specification still fails to specify any events after spelling correction, an error is generated. Commands All programmer's assistant commands can be input as list forms, or as lines (see the Preogrammer's Assistant Functions section below). For example, typing REDO 5cr and (REDO 5) are equivalent. EventSpec is used to denote an event specification. Unless specified otherwise, omitting EventSpec is the same as specifying EventSpec=-1. For example, REDO and REDO -1 are the same. REDO(REDO (Command) NIL NIL ("13") 7) EventSpec [Prog. Asst. Command] Redoes the event or events specified by EventSpec. For example, REDO FROM -3 redoes the last three events. REDO EventSpec N TIMES [Prog. Asst. Command] Redoes the event or events specified by EventSpec N times. For example, REDO 10 TIMES redoes the last event ten times. REDO EventSpec WHILE FORM [Prog. Asst. Command] Redoes the specified events as long as the value of FORM is true. FORM is evaluated before each iteration so if its initial value is NIL, nothing will happen. REDO EventSpec UNTIL FORM [Prog. Asst. Command] Same as REDO EventSpec WHILE (NOT FORM). REPEAT(REPEAT (Command) NIL NIL ("13") 7) EventSpec [Prog. Asst. Command] Same as REDO EventSpec WHILE T. The event(s) are repeated until an error occurs, or the user types Control-E or Control-D. REPEAT EventSpec WHILE FORM [Prog. Asst. Command] REPEAT EventSpec UNTIL FORM [Prog. Asst. Command] Same as REDO. For all history commands that perform multiple repetitions, the variable REDOCNT is initialized to 0 and incremented each iteration. If the event terminates gracefully, i.e., is not aborted by an error or Control-D, the number of iterations is printed. RETRY(RETRY (Command) NIL NIL ("13") 7) EventSpec [Prog. Asst. Command] Similar to REDO except sets HELPCLOCK (see Chapter 14) so that any errors that occur while executing EventSpec will cause breaks. USE(USE (Command) NIL NIL ("13") 7) EXPRS FOR ARGS IN EventSpec [Prog. Asst. Command] Substitutes EXPRS for ARGS in EventSpec, and redoes the result. Substitution is done by ESUBST (see Chapter 16), and is carried out as described below. EXPRS and ARGS can include non-atomic members. For example, USE LOG (MINUS X) FOR ANTILOG X IN -2 AND -1 will substitute LOG for every occurrence of ANTILOG in the previous two events, and substitute (MINUS X) for every occurrence of X, and reexecute them. Note that these substitutions do not change the information saved about these events on the history list. Any expression to be substituted can be preceded by a !, meaning that the expression is to be substituted as a segment, e.g., LIST(A B C) followed by USE ! (X Y Z) FOR B will produce LIST(A X Y Z C), and USE ! NIL FOR B will produce LIST(A C). If IN EventSpec is omitted, the first member of ARGS is used for EventSpec. For example, USE PUTD FOR @UTD is equivalent to USE PUTD FOR @UTD IN F @UTD. The F is inserted to handle correctly the case where the first member of ARGS could be interpreted as an event address command. USE EXPRS IN EventSpec [Prog. Asst. Command] If ARGS are omitted, and the event referred to was itself a USE command, the arguments and expression substituted into are the same as for the indicated USE command. In effect, this USE command is thus a continuation of the previous USE command. For example, following USE X FOR Y IN 50, typing USE Z IN -1 is equivalent to USE Z FOR Y IN 50. If ARGS are omitted and the event referred to was not a USE command, substitution is for the "operator" in that command. For example ARGLIST(FF) followed by USE CALLS IN -1 is equivalent to USE CALLS FOR ARGLIST IN -1. If IN EventSpec is omitted, it is the same as specifying IN -1. USE EXPRS1 FOR ARGS1 AND ... AND EXPRSN FOR ARGSN IN EventSpec [Prog. Asst. Command] More general form of USE command. See description of the substitution algorithm below. Note: The USE command is parsed by a small finite state parser to distinguish the expressions and arguments. For example, USE FOR FOR AND AND AND FOR FOR will be parsed correctly. Every USE command involves three pieces of information: the expressions to be substituted, the arguments to be substituted for, and an event specification, which defines the input expression in which the substitution takes place. If the USE command has the same number of expressions as arguments, the substitution procedure is straightforward. For example, USE X Y FOR U V means substitute X for U and Y for V, and is equivalent to USE X FOR U AND Y FOR V. However, the USE command also permits distributive substitutions, for substituting several expressions for the same argument. For example, USE A B C FOR X means first substitute A for X then substitute B for X (in a new copy of the expression), then substitute C for X. The effect is the same as three separate USE commands. Similarly, USE A B C FOR D AND X Y Z FOR W is equivalent to USE A FOR D AND X FOR W, followed by USE B FOR D AND Y FOR W, followed by USE C FOR D AND Z FOR W. USE A B C FOR D AND X FOR Y also corresponds to three substitutions, the first with A for D and X for Y, the second with B for D, and X for Y, and the third with C for D, and again X for Y. However, USE A B C FOR D AND X Y FOR Z is ambiguous and will cause an error. Essentially, the USE command operates by proceeding from left to right handling each "AND" separately. Whenever the number of expressions exceeds the number of expressions available, multiple USE expressions are generated. Thus USE A B C D FOR E F means substitute A for E at the same time as substituting B for F, then in another copy of the indicated expression, substitute C for E and D for F. This is also equivalent to USE A C FOR E AND B D FOR F. Note: Parsing the USE command gets more complicated when one of the arguments and one of the expressions are the same, e.g., USE X Y FOR Y X, or USE X FOR Y AND Y FOR X. This situation is noticed when parsing the command, and handled correctly. ... VARS [Prog. Asst. Command] Similar to USE except substitutes for the (first) operand. For example, EXPRP(FOO) followed by ... FIE FUM is equivalent to USE FIE FUM FOR FOO. In the following discussion, $ is used to represent the character "escape," since this is how this character is echoed. $ X FOR Y IN EventSpec [Prog. Asst. Command] $ (escape) is a special form of the USE command for conveniently specifying character substitutions in litatoms or strings. In addition, it has a number of useful properties in connection with events that involve errors (see below). Equivalent to USE $X$ FOR $Y$ IN EventSpec, which will do a character substitution of the characters in X for the characters in Y. For example, if you type MOVD(FOO FOOSAVE T), you can then type $ FIE FOR FOO IN MOVD to perform MOVD(FIE FIESAVE T). USE FIE FOR FOO would perform MOVD(FIE FOOSAVE T). $ Y X IN EventSpec [Prog. Asst. Command] $ Y TO X IN EventSpec [Prog. Asst. Command] $ Y = X IN EventSpec [Prog. Asst. Command] $ Y -> X IN EventSpec [Prog. Asst. Command] Abbreviated forms of the $ (escape) command: the same as $ X FOR Y IN EventSpec, which changes Ys to Xs. $ does event location the same as the USE command, i.e., if IN EventSpec is not specified, $ searches for Y. However, unlike USE, $ can only be used to specify one substitution at a time. After $ finds the event, it looks to see if an error was involved in that event, and if the indicated character substitution can be performed in the object of the error message, called the offender. If so, $ assumes the substitution refers to the offender, performs the indicated character substitution in the offender only, and then substitutes the result for the original offender throughout the event. For example, suppose the user types (PRETTYDEF FOOFNS 'FOO FOOOVARS) causing a U.B.A. FOOOVARS error message. The user can now type $ OO O, which will change FOOOVARS to FOOVARS, but not change FOOFNS or FOO. If an error did occur in the specified event, you can also omit specifying the object of the substitution, Y, in which case the offender itself is used. Thus, you could have corrected the above example by simply typing $ FOOVARS. Since ESUBST is used for performing the substitution (see Chapter 16), $ can be used in X to refer to the characters in Y. For example, if you type LOAD(PRSTRUC PROP), causing the error FILE NOT FOUND PRSTRUC, you can request the file to be loaded from LISP's directory by simply typing $ $. This is equivalent to performing (R PRSTRUC $) on the event, and therefore replaces PRSTRUC by PRSTRUC. $ never searches for an error. Thus, if you type LOAD(PRSTRUC PROP) causing a FILE NOT FOUND error, type CLOSEALL(), and then type $ $, LISPX will complain that there is no error in CLOSEALL(). In this case, you would have to type $ $ IN LOAD, or $ PRS PRS (which would cause a search for PRS). $ operates on input, not on programs. If you type FOO(), and within the call to FOO gets a U.D.F. CONDD error, you cannot repair this by $ COND. LISPX will type CONDD NOT FOUND IN FOO(). FIX(FIX (Command) NIL NIL ("13") 9) EventSpec [Prog. Asst. Command] Invokes the default program editor (Dedit or the teletype editor) on a copy of the input(s) for EventSpec. Whenever the user exits via OK, the result is unread and reexecuted exactly as with REDO. FIX is provided for those cases when the modifications to the input(s) are not simple substitutions of the type that can be specified by USE. For example, if the default editor is the teletype editor, then: (DEFINEQ FOO (LAMBDA (X) (FIXSPELL SPELLINGS2 X 70] INCORRECT DEFINING FORM FOO FIX EDIT *P (DEFINEQ FOO (LAMBDA & &)) *(LI 2) *P (DEFINEQ (FOO &)) *OK (FOO) You can also specify the edit command(s) to LISPX, by typing - followed by the command(s) after the event specification, e.g., FIX - (LI 2). In this case, the editor will not type EDIT, or wait for an OK after executing the commands. FIX calls the editor on the "input sequence" of an event, adjusting the editor so it is initially editing the expression typed. However, the entire input sequence is being edited, so it is possible to give editor commands that examine this structure further. For more information on the format of an event's input, see the Format and Use of the History List section below. ?? EventSpec [Prog. Asst. Command] Prints the specified events from the history list. If EventSpec is omitted, ?? prints the entire history list, beginning with most recent events. Otherwise ?? prints only those events specified in EventSpec (in the order specified). For example, ?? -1, ?? 10 THRU 15, etc. For each event specified, ?? prints the event number, the prompt, the input line(s), and the value(s). If the event input was a p.a. command that "unread" some other input lines, the p.a. command is printed without a preceding prompt, to show that they are not stored as the input, and the input lines are printed with prompts. Events are initially stored on the history list with their value field equal to the character "bell" (Control-G). Thefore, if an operation fails to complete for any reason, e.g., causes an error, is aborted, etc., ?? will print a bell as its "value". ?? commands are not entered on the history list, and so do not affect relative event numbers. In other words, an event specification of -1 typed following a ?? command will refer to the event immediately preceding the ?? command. ?? is implemented via the function PRINTHISTORY (see the Programmer's Assistant Functions section below) which can also be called directly by the user. Printing is performed via the function SHOWPRIN2 (see Chapter 25), so that if the value of SYSPRETTYFLG=T, events will be prettyprinted. UNDO(UNDO (Command) NIL NIL ("13") 10) EventSpec [Prog. Asst. Command] Undoes the side effects of the specified events. For each event undone, UNDO prints a message: RPLACA undone, REDO undone etc. If nothing is undone because nothing was saved, UNDO types nothing saved. If nothing was undone because the events were already undone, UNDO types already undone. If EventSpec is not given, UNDO searches back for the last event that contained side effects, was not undone, and itself was not an UNDO command. Note that the user can undo UNDO commands themselves by specifying the corresponding event address, e.g., UNDO -7 or UNDO UNDO. To restore all pointers correctly, you should UNDO events in the reverse order from which they were executed. For example, to undo all the side effects of the last five events, perform UNDO THRU -5, not UNDO FROM -5. Undoing out of order may have unforseen effects if the operations are dependent. For example, if you performed (NCONC1 FOO FIE), followed by (NCONC1 FOO FUM), and then undoes the (NCONC1 FOO FIE), he will also have undone the (NCONC1 FOO FUM). If he then undoes the (NCONC1 FOO FUM), he will cause the FIE to reappear, by virtue of restoring FOO to its state before the execution of (NCONC1 FOO FUM). For more details, see the Undoing section below. UNDO EventSpec : X1 ... XN [Prog. Asst. Command] Each Xi is a pattern that is matched to a message printed by DWIM in the event(s) specified by EventSpec. The side effects of the corresponding DWIM corrections, and only those side effects, are undone. For example, if DWIM printed the message PRINTT [IN FOO] -> PRINT, then UNDO : PRINTT or UNDO : PRINT would undo the correction. Some portions of the messages printed by DWIM are strings, e.g., the message FOO UNSAVED is printed by printing FOO and then " UNSAVED". Therefore, if the user types UNDO : UNSAVED, the DWIM correction will not be found. He should instead type UNDO : FOO or UNDO : $UNSAVED$ (UNSAVED, see R command in editor, Chapter 16). NAME LITATOM EventSpec [Prog. Asst. Command] Saves the event(s) (including side effects) specified by EventSpec on the property list of LITATOM (under the property HISTORY). For example, NAME FOO 10 THRU 15. NAME commands are undoable. Events saved on a litatom can be retrieved with the event specification @ LITATOM. For example, ?? @ FOO, REDO @ FOO, etc. Commands defined by NAME can also be typed in directly as though they were built-in commands, e.g., FOOcr is equivalent to REDO @ FOO. However, if FOO is the name of a variable, it would be evaluated, i.e., FOOcr would return the value of FOO. Commands defined by NAME can also be defined to take arguments: NAME(NAME (Command) NIL NIL ("13") 11) LITATOM (ARG1 ... ARGN) : EventSpec [Prog. Asst. Command] NAME LITATOM ARG1 ... ARGN : EventSpec [Prog. Asst. Command] The arguments ARGi are interpreted the same as the arguments for a USE command. When LITATOM is invoked, the argument values are substituted for ARG1 ... ARGN using the same substitution algorithm as for USE. NAME FOO EventSpec is equivalent to NAME FOO : EventSpec. In either case, if FOO is invoked with arguments, an error is generated. For example, following the event (PUTD 'FOO (COPY (GETPROP 'FIE 'EXPR))), the user types NAME MOVE FOO FIE : PUTD. Then typing MOVE TEST1 TEST2 would cause (PUTD 'TEST1 (COPY (GETPROP 'TEST2 'EXPR))) to be executed, i.e., would be equivalent to typing USE TEST1 TEST2 FOR FOO FIE IN MOVE. Typing MOVE A B C D would cause two PUTD's to be executed. Note that !'s and $'s can also be employed the same as with USE. For example, if following PREPINDEX(14LISP.XGP) FIXFILE(14LISP.XGPIDX) the user performed NAME FOO $14$ : -2 AND -1, then FOO $15$ would perform the indicated two operations with 14 replaced by 15. RETRIEVE(RETRIEVE (Command) NIL NIL ("13") 12) LITATOM [Prog. Asst. Command] Retrieves and reenters on the history list the events named by LITATOM. Causes an error if LITATOM was not named by a NAME command. For example, if the user performs NAME FOO 10 THRU 15, and at some time later types RETRIEVE FOO, six new events will be recorded on the history list (whether or not the corresponding events have been forgotten yet). Note that RETRIEVE does not reexecute the events, it simply retrieves them. The user can then REDO, UNDO, FIX, etc. any or all of these events. Note that the user can combine the effects of a RETRIEVE and a subsequent history command in a single operation, e.g., REDO FOO is equivalent to RETRIEVE FOO, followed by an appropriate REDO. Actually, REDO FOO is better than RETRIEVE followed by REDO since in the latter case, the corresponding events would be entered on the history list twice, once for the RETRIEVE and once for the REDO. Note that UNDO FOO and ?? FOO are permitted. BEFORE(BEFORE (Command) NIL NIL ("13") 12) LITATOM [Prog. Asst. Command] Undoes the effects of the events named by LITATOM. AFTER(AFTER (Command) NIL NIL ("13") 12) LITATOM [Prog. Asst. Command] Undoes a BEFORE LITATOM. BEFORE and AFTER provide a convenient way of flipping back and forth between two states, namely the state before a specified event or events were executed, and that state after execution. For example, if the user has a complex data structure which he wants to be able to interrogate before and after certain modifications, he can execute the modifications, name the corresponding events with the NAME command, and then can turn these modifications off and on via BEFORE or AFTER commands. Both BEFORE and AFTER are no-ops if the LITATOM was already in the corresponding state; both generate errors if LITATOM was not named by a NAME command. The alternative to BEFORE and AFTER for repeated switching back and forth involves typing UNDO, UNDO of the UNDO, UNDO of that etc. At each stage, the user would have to locate the correct event to undo, and furthermore would run the risk of that event being "forgotten" if he did not switch at least once per time-slice. Since UNDO, NAME, RETRIEVE, BEFORE, and AFTER are recorded as inputs they can be referenced by REDO, USE, etc. in the normal way. However, the user must again remember that the context in which the command is reexecuted is different than the original context. For example, if the user types NAME FOO DEFINEQ THRU COMPILE, then types ... FIE, the input that will be reread will be NAME FIE DEFINEQ THRU COMPILE as was intended, but both DEFINEQ and COMPILE, will refer to the most recent event containing those atoms, namely the event consisting of NAME FOO DEFINEQ THRU COMPILE. ARCHIVE(ARCHIVE (Command) NIL NIL ("13") 13) EventSpec [Prog. Asst. Command] Records the events specified by EventSpec on a permanent "archived" history list, ARCHIVELST (see the Format and Use of the History List section below). This history list can be referenced by preceding a standard event specification with @@ (see the Event Specification section above). For example, ?? @@ prints the archived history list, REDO @@ -1 will recover the corresponding event from the archived history list and redo it, etc. The user can also provide for automatic archiving of selected events by appropriately defining ARCHIVEFN (see the Changing the Programmer's Assistant section below), or by putting the history list property *ARCHIVE*, with value T, on the event (see the Format and Use of the History List section below). Events that are referenced by history commands are automatically marked for archiving in this fashion. FORGET(FORGET (Command) NIL NIL ("13") 13) EventSpec [Prog. Asst. Command] Permanently erases the record of the side effects for the events specified by EventSpec. If EventSpec is omitted, forgets side effects for entire history list. FORGET is provided for users with space problems. For example, if the user has just performed SETs, RPLACAs, RPLACDs, PUTD, REMPROPs, etc. to release storage, the old pointers would not be garbage collected until the corresponding events age sufficiently to drop off the end of the history list and be forgotten. FORGET can be used to force immediate forgetting (of the side-effects only). FORGET is not undoable (obviously). REMEMBER(REMEMBER (Command) NIL NIL ("13") 13) EventSpec [Prog. Asst. Command] Instructs the file package to "remember" the events specified by EventSpec. These events will be marked as changed objects of file package type EXPRESSIONS, which can be written out via the file package command P. For example, after the user types: MOVD?(DELFILE /DELFILE) DELFILE REMEMBER -1 (MOVD? (QUOTE DELFILE) (QUOTE /DELFILE)) If the user calls FILES?, MAKEFILES, or CLEANUP, the command (P (MOVD? (QUOTE DELFILE) (QUOTE /DELFILE))) will be constructed by the file package and added to the filecoms indicated by the user, unless the user has already explicitly added the corresponding expression to some P command himself. Note that "remembering" an event like (PUTPROP 'FOO 'CLISPTYPE EXPRESSION) will not result in a (PROP CLISPTYPE FOO) command, because this will save the current (at the time of the MAKEFILE) value for the CLISPTYPE property, which may or may not be EXPRESSION. Thus, even if there is a PROP command which saves the CLISPTYPE property for FOO in some FILECOMS, remembering this event will still require a (P (PUTPROP 'FOO 'CLISPTYPE EXPRESSION)) command to appear. PL(PL (Command) NIL NIL ("13") 13) LITATOM [Prog. Asst. Command] "Print Property List." Prints out the property list of LITATOM in a nice format, with PRINTLEVEL reset to (2 . 3). For example, PL + CLISPTYPE: 12 ACCESSFNS: (PLUS IPLUS FPLUS) PL is implemented via the function PRINTPROPS. PB(PB (Command) NIL NIL ("13") 14) LITATOM [Prog. Asst. Command] "Print Bindings." Prints the value of LITATOM with PRINTLEVEL reset to (2 . 3). If LITATOM is not bound, does not attempt spelling correction or generate an error. PB is implemented via the function PRINTBINDINGS. PB is also a break command (see Chapter 14). As a break command, it ascends the stack and, for each frame in which LITATOM is bound, prints the frame name and value of LITATOM. If typed in to the programmer's assistant when not at the top level, e.g. in the editor, etc., PB will also ascend the stack as it does with a break. However, as a programmer's assistant command, it is primarily used to examine the top level value of a variable that may or may not be bound, or to examine a variable whose value is a large list. ;(; (Command) NIL NIL ("13") 14) FORM [Prog. Asst. Command] Allows you to type a line of text without having the programmer's assistant process it. Useful when linked to other users, or to annotate a dribble file (see Chapter 30). SHH(SHH (Command) NIL NIL ("13") 14) FORM [Prog. Asst. Command] Allows you to evaluate an expression without having the programmer's assistant process it or record it on a history list. Useful when you want to bypass a programmer's assistant command or to keep the evaluation off the history list. TYPE-AHEAD(TYPE-AHEAD (Command) NIL NIL ("13") 14) [Prog. Asst. Command] A command that allows you to type-ahead an indefinite number of inputs. The assistant responds to TYPE-AHEAD with a prompt character of >. The user can now type in an indefinite number of lines of input, under ERRORSET protection. The input lines are saved and unread when the user exits the type-ahead loop with the command $GO (escape-GO). While in the type-ahead loop, ?? can be used to print the type-ahead, FIX to edit the type-ahead, and $Q (escape-Q) to erase the last input (may be used repeatedly). The TYPE-AHEAD command may be aborted by $STOP (escape-STOP); Control-E simply aborts the current line of input. For example: TYPE-AHEAD >SYSOUT(TEM) >MAKEFILE(EDIT) >BRECOMPILE((EDIT WEDIT)) >F >$Q \\F >$Q \\BRECOMPILE >LOAD(WEDIT PROP) >BRECOMPILE((EDIT WEDIT)) >F >MAKEFILE(BREAK) >LISTFILES(EDIT BREAK) >SYSOUT(CURRENT) >LOGOUT] >?? >SYSOUT(TEM) >MAKEFILE(EDIT) >LOAD(WEDIT PROP) >BRECOMPILE((EDIT WEDIT)) >F >MAKEFILE(BREAK) >LISTFILES(EDIT BREAK) >SYSOUT(CURRENT) >LOGOUT] >FIX EDIT *(R BRECOMPILE BCOMPL) *P ((LOGOUT) (SYSOUT &) (LISTFILES &) (MAKEFILE &) (F) (BCOMPL &) (LOAD &) (MAKEFILE &) (SYSOUT &)) *(DELETE LOAD) *OK >$GO Type-ahead can be addressed to the compiler, since it uses LISPXREAD for input. Type-ahead can also be directed to the editor, but type-ahead to the editor and to LISPX cannot be intermixed. The following are some useful functions and variables: (VALUEOF(VALUEOF (Function) NIL NIL ("13") 15) LINE) [NLambda NoSpread Function] An nlambda function for obtaining the value of a particular event, e.g., (VALUEOF -1), (VALUEOF FOO -2). The value of an event consisting of several operations is a list of the values for each of the individual operations. The value field of a history entry is initialized to bell (Control-G). Thus a value of bell indicates that the corresponding operation did not complete, i.e., was aborted or caused an error (or else it returned bell). Note: Although the input for VALUEOF is entered on the history list before VALUEOF is called, (VALUEOF -1) still refers to the value of the expression immediately before the VALUEOF input, because VALUEOF effectively backs the history list up one entry when it retrieves the specified event. Similarly, (VALUEOF FOO) will find the first event before this one that contains a FOO. IT (IT% (Variable) NIL NIL ("13") 15) [Variable] The value of the variable IT is always the value of the last event executed, i.e. (VALUEOF -1). For example, (SQRT 2) 1.414214 (SQRT IT) 1.189207 If the last event was a multiple event, e.g. REDO -3 THRU -1, IT is set to value of the last of these events. Following a ?? command, IT is set to value of the last event printed. In other words, in all cases, IT is set to the last value printed on the terminal. P.A. Commands Applied to P.A. Commands Programmer's assistant commands that unread expressions, such as REDO, USE, etc. do not appear in the input portion of events, although they are stored elsewhere in the event. They do not interfere with or affect the searching operations of event specifications. As a result, p.a. commands themselves cannot be recovered for execution in the normal way. For example, if you type USE A B C FOR D, followed by USE E FOR D, you will not produce the effect of USE A B C FOR E, but instead will simply cause E to be substituted for D in the last event containing a D. To produce the desired effect, you should type USE D FOR E IN USE. The appearance of the word REDO, USE or FIX in an event address specifies a search for the corresponding programmer's assistant command. It also specifies that the text of the programmer's assistant command itself be treated as though it were the input. However, remember that the context in which a history command is reexecuted is that of the current history, not the original context. For example, if you type USE FOO FOR FIE IN -1, and then later type REDO USE, the -1 will refer to the event before the REDO, not before the USE. The one exception to the statement that programmer's assistant commands "do not interfere with or affect the searching operations of event specifications" occurs when a p.a. command fails to produce any input. For example, suppose you type USE LOG FOR ANTILOG AND ANTILOG FOR LOGG, mispelling the second LOG. This will cause an error, LOGG ?. Since the USE command did not produce any input, the user can repair it by typing USE LOG FOR LOGG, without having to specify IN USE. This latter USE command will invoke a search for LOGG, which will find the bad USE command. The programmer's assistant then performs the indicated substitution, and unreads USE LOG FOR ANTILOG AND ANTILOG FOR LOG. In turn, this USE command invokes a search for ANTILOG, which, because it was not typed in but reread, ignores the bad USE command which was found by the earlier search for LOGG, and which is still on the history list. In other words, p.a. commands that fail to produce input are visible to searches arising from event specifications typed in by the user, but not to secondary event specifications. In addition, if the most recent event is a history command which failed to produce input, a secondary event specification will effectively back up the history list one event so that relative event numbers for that event specification will not count the bad p.a. command. For example, suppose the user types USE LOG FOR ANTILOG AND ANTILOG FOR LOGG IN -2 AND -1, and after the p.a. types LOGG ?, the user types USE LOG FOR LOGG. He thus causes the command USE LOG FOR ANTILOG AND ANTILOG FOR LOG IN -2 AND -1 to be constructed and unread. In the normal case, -1 would refer to the last event, i.e., the "bad" USE command, and -2 to the event before it. However, in this case, -1 refers to the event before the bad USE command, and the -2 to the event before that. In short, the caveat above that "the user must remember that the context in which a history command is reexecuted is that of the current history, not the original context" does not apply if the correction is performed immediately. Changing The Programmer's Assistant 1 (CHANGESLICE(CHANGESLICE (Function) NIL NIL ("13") 16) N HISTORY %) [Function] Changes the time-slice of the history list HISTORY to N (see the Format and Use of the History List section below). If HISTORY is NIL, changes both the top level history list LISPXHISTORY and the edit history list EDITHISTORY. The effect of increasing the time-slice is gradual: the history list is simply allowed to grow to the corresponding length before any events are forgotten. Decreasing the time-slice will immediately remove a sufficient number of the older events to bring the history list down to the proper size. However, CHANGESLICE is undoable, so that these events are (temporarily) recoverable. Therefore, if you want to recover the storage associated with these events without waiting N more events until the CHANGESLICE event drops off the history list, you must perform a FORGET command (see the Commands section above). PROMPT#FLG(PROMPT#FLG (Variable) NIL NIL ("13") 16) [Variable] When this variable is set to T, the current event number to be printed before each prompt character. See PROMPTCHAR in the Programmer's Assistant Functions section below. PROMPT#FLG is initially T. PROMPTCHARFORMS(PROMPTCHARFORMS (Variable) NIL NIL ("13") 17) [Variable] The value of PROMPTCHARFORMS is a list of expression which are evaluated each time PROMPTCHAR (see the Programmer's Assistant Functions section below) is called to print the prompt character. If PROMPTCHAR is going to print something, it first maps down PROMPTCHARFORMS evaluating each expression under an ERRORSET. These expressions can access the special variables HISTORY (the current history list), ID (the prompt character to be printed), and PROMPTSTR, which is what PROMPTCHAR will print before ID, if anything. When PROMPT#FLG is T, PROMPTSTR will be the event number. The expressions on PROMPTCHARFORMS can change the shape of a cursor, update a clock, check for mail, etc. or change what PROMPTCHAR is about to print by resetting ID and/or PROMPTSTR. After the expressions on PROMPTCHARFORMS have been evaluated, PROMPTSTR is printed if it is (still) non-NIL, and then ID is printed, if it is (still) non-NIL. HISTORYSAVEFORMS(HISTORYSAVEFORMS (Variable) NIL NIL ("13") 17) [Variable] The value of HISTORYSAVEFORMS is a list of expressions that are evaluated under errorset protection each time HISTORYSAVE (see the Programmer's Assistant Functions section below) creates a new event. This happens each time there is an interaction with the user, but not when performing an operation that is being redone. The expressions on HISTORYSAVEFORMS are presumably executed for effect, and can access the special variables HISTORY (the current history list), ID (the current prompt character), and EVENT (the current event which HISTORYSAVE is going to return). PROMPTCHARFORMS and HISTORYSAVEFORMS together enable bracketing each interaction with the user. These can be used to measure how long the user takes to respond, to use a different readtable or terminal table, etc. RESETFORMS(RESETFORMS (Variable) NIL NIL ("13") 17) [Variable] The value of RESETFORMS is a list of forms that are evaluated at each RESET, i.e., when you type Control-D, or call function RESET. ARCHIVEFN(ARCHIVEFN (Variable) NIL NIL ("13") 17) [Variable] If the value of ARCHIVEFN is T, and an event is about to drop off the end of the history list and be forgotten, ARCHIVEFN is called as a function with two arguments: the input portion of the event, and the entire event (seethe Format and Use of the History List section for the format of events). If ARCHIVEFN returns T, the event is archived on a permanent history list (see the Command section above). ARCHIVEFN must be both set and defined. ARCHIVEFN is initially NIL and undefined. For example, defining ARCHIVEFN as (LAMBDA (X Y) (EQ (CAR X) 'LOAD)) will keep a record of all calls to LOAD. ARCHIVEFLG(ARCHIVEFLG (Variable) NIL NIL ("13") 17) [Variable] If the value of ARCHIVEFLG is non-NIL, the system automatically marks all events that are referenced by history commands so that they will be archived when they drop off the history list. ARCHIVEFLG is initially T, so once an event is redone, it is guaranteed to be saved. An event is "marked for archiving" by putting the property *ARCHIVE*, value T, on the event (see the Format and Use of the History List section). You could do this by means of an appropriately defined LISPXUSERFN (see below). LISPXMACROS(LISPXMACROS (Variable) NIL NIL ("13") 18) [Variable] LISPXMACROS provides a macro facility that allows you to define your own programmer's assistant commands. It is a list of elements of the form (COMMAND DEF). Whenever COMMAND appears as the first expression on a line in a LISPX input, the variable LISPXLINE is bound to the rest of the line, the event is recorded on the history list, DEF is evaluated, and DEF's value is stored as the value of the event. Similarly, whenever COMMAND appears as CAR of a form in a LISPX input, the variable LISPXLINE is bound to CDR of the form, the event is recorded, and DEF is evaluated. An element of the form (COMMAND NIL DEF) is interpreted to mean bind LISPXLINE and evaluate DEF as described above, except do not save the event on the history list. LISPXHISTORYMACROS(LISPXHISTORYMACROS (Variable) NIL NIL ("13") 18) [Variable] LISPXHISTORYMACROS allows the user to define programmer's assistant commands that re-execute other events. LISPXHISTORYMACROS is interpreted the same as LISPXMACROS, except that the result of evaluating DEF is treated as a list of expressions to be unread, exactly as though the expressions had been retrieved by a REDO command, or computed by a USE command. Note that returning NIL means nothing else is done. This provides a mechanism for defining LISPX commands which are executed for effect only. Many programmer's assistant commands, such as RETRIEVE, BEFORE, AFTER, etc. are implemented through LISPXMACROS or LISPXHISTORYMACROS. Definitions of commands on LISPXMACROS or LISPXHISTORYMACROS can be saved on files with the file package command LISPXMACROS (see Chapter 17). LISPXUSERFN(LISPXUSERFN (Variable) NIL NIL ("13") 18) [Variable] When LISPXUSERFN is set to T, it is applied as a function to all inputs not recognized as a programmer's assistant command, or on LISPXMACROS or LISPXHISTORYMACROS. If LISPXUSERFN decides to handle this input, it simply processes it (the event was already stored on the history list before LISPXUSERFN was called), sets LISPXVALUE to the value for the event, and returns T. The programmer's assistant will then know not to call EVAL or APPLY, and will simply store LISPXVALUE into the value slot for the event, and print it. If LISPXUSERFN returns NIL, EVAL or APPLY is called in the usual way. Note that LISPXUSERFN must be both set and defined. LISPXUSERFN is given two arguments: X and LINE. X is the first expression typed, and LINE is the rest of the line, as read by READLINE (see the Programmer's Assistant Functions section). For example, if the user typed FOO(A B C), X=FOO, and LINE=((A B C)); if the user typed (FOO A B C), X=(FOO A B C), and LINE=NIL; and if the user typed FOO A B C, X=FOO and LINE=(A B C). By appropriately defining (and setting) LISPXUSERFN, the user can with a minimum of effort incorporate the features of the programmer's assistant into his own executive (actually it is the other way around). For example, LISPXUSERFN could be defined to parse all input (other than p.a. commands) in an alternative way. Note that since LISPXUSERFN is called for each input (except for p.a. commands), it can also be used to monitor some condition or gather statistics. (LISPXPRINT(LISPXPRINT (Function) NIL NIL ("13") 19) X Y Z NODOFLG) [Function] (LISPXPRIN1 X Y Z NODOFLG) [Function] (LISPXPRIN2 X Y Z NODOFLG) [Function] (LISPXSPACES(LISPXSPACES (Function) NIL NIL ("13") 19) X Y Z NODOFLG) [Function] (LISPXTERPRI(LISPXTERPRI (Function) NIL NIL ("13") 19) X Y Z NODOFLG) [Function] (LISPXTAB(LISPXTAB (Function) NIL NIL ("13") 19) X Y Z NODOFLG) [Function] (LISPXPRINTDEF(LISPXPRINTDEF (Function) NIL NIL ("13") 19) EXPR FILE LEFT DEF TAIL NODOFLG) [Function] In addition to saving inputs and values, the programmer's assistant saves most system messages on the history list. For example, FILE CREATED ..., (FN REDEFINED), (VAR RESET), output of TIME, BREAKDOWN, STORAGE, DWIM messages, etc. When ?? prints the event, the output is also printed. This facility is implemented via these functions. These functions print exactly the same as their non-LISPX counterparts. Then, they put the output on the history list under the property *LISPXPRINT* (see the Format and Use of the History List section below). If NODOFLG is non-NIL, these fuctions do not print, but only put their output on the history list. To perform output operations from user programs so that the output will appear on the history list, the program needs simply to call the corresponding LISPX printing function. (USERLISPXPRINT(USERLISPXPRINT (Function) NIL NIL ("13") 19) X FILE Z NODOFLG) [Function] The function USERLISPXPRINT is available to permit the user to define additional LISPX printing functions. If the user has a function FN that takes three or fewer arguments, and the second argument is the file name, he can define a LISPX printing function by simply giving LISPXFN the definition of USERLISPXPRINT, for example, with MOVD(USERLISPXPRINT LISPXFN). USERLISPXPRINT is defined to look back on the stack, find the name of the calling function, strip off the leading "LISPX", perform the appropriate saving information, and then call the function to do the actual printing. LISPXPRINTFLG(LISPXPRINTFLG (Variable) NIL NIL ("13") 19) [Variable] If LISPXPRINTFLG=NIL, the LISPX printing functions will not store their output on the history list. LISPXPRINTFLG is initially T. Undoing 1 Note: This discussion only applies to undoing under the executive and break; the editors handles undoing itself in a slightly different fashion. The UNDO capability of the programmer's assistant is implemented by requiring that each operation that is to be undoable be responsible itself for saving on the history list enough information to enable reversal of its side effects. In other words, the assistant does not "know" when it is about to perform a destructive operation, i.e., it is not constantly checking or anticipating. Instead, it simply executes operations, and any undoable changes that occur are automatically saved on the history list by the responsible functions. The UNDO command, which involves recovering the saved information and performing the corresponding inverses, works the same way, so that the user can UNDO an UNDO, and UNDO that etc. At each point, until the user specifically requests an operation to be undone, the assistant does not know, or care, whether information has been saved to enable the undoing. Only when the user attempts to undo an operation does the assistant check to see whether any information has been saved. If none has been saved, and the user has specifically named the event he wants undone, the assistant types nothing saved. (When the user simply types UNDO, the assistant searches for the last undoable event, ignoring events already undone as well as UNDO operations themselves.) This implementation minimizes the overhead for undoing. Only those operations which actually make changes are affected, and the overhead is small: two or three cells of storage for saving the information, and an extra function call. However, even this small price may be too expensive if the operation is sufficiently primitive and repetitive, i.e., if the extra overhead may seriously degrade the overall performance of the program. Hence not every destructive operation in a program should necessarily be undoable; the programmer must be allowed to decide each case individually. Therefore for each primitive destructive function, Interlisp has defined an undoable version which always saves information. By convention, the name of the undoable version of a function is the function name, preceeded by "/." For example, there is RPLACA and /RPLACA, REMPROP and /REMPROP, etc. The "slash" functions that are currently implemented can be found as the value of /FNS. The various system packages use the appropriate undoable functions. For example, BREAK uses /PUTD and /REMPROP so as to be undoable, and DWIM uses /RPLACA and /RPLACD, when it makes a correction. Similarly, the user can simply use the corresponding / function if he wants to make a destructive operation in his own program undoable. When the / function is called, it will save the UNDO information in the current event on the history list. The effects of the following functions are always undoable: DEFINE, DEFINEQ, DEFC (used to give a function a compiled code definition), DEFLIST, LOAD, SAVEDEF, UNSAVEDEF, BREAK, UNBREAK, REBREAK, TRACE, BREAKIN, UNBREAKIN, CHANGENAME, EDITFNS, EDITF, EDITV, EDITP, EDITE, EDITL, ESUBST, ADVISE, UNADVISE, READVISE, plus any changes caused by DWIM. The programmer's assistant cannot know whether efficiency and overhead are serious considerations for the execution of an expression in a user program, so the user must decide if he wants these operations undoable by explicitly calling /MAPCONC, etc. However, typed-in expressions rarely involve iterations or lengthy computations directly. Therefore, before evaluating the user input, the programmer's assistant substitutes the corresponding undoable function for any destructive function (using LISPX/, see the Programmer's Assistant Functions section below). For example, if the user types (MAPCONC NASDIC ...), it is actually (/MAPCONC NASDIC ...) that is evaluated. Obviously, with a more sophisticated analysis of both user input and user programs, the decision concerning which operations to make undoable could be better advised. However, we have found the configuration described here to be a very satisfactory one. You pay a very small price for being able to undo what you type in, and if you want to protect yourself from malfunctioning in your own programs, he can have his program explicitly call undoable functions. You can define new "slash" functions to be translated on type-in by calling NEW/FN (see the Programmer's Assistant Functions section below). Undoing Out of Order /RPLACA operates undoably by saving (on the history list) the list cell that is to be changed and its original CAR. Undoing a /RPLACA simply restores the saved CAR. This implementation can produce unexpected results when multiple /RPLACAs are done on the same list cell, and then undone out of order. For example, if the user types (RPLACA FOO 1), followed by (RPLACA FOO 2), then undoes both events by undoing the most recent event first, then undoing the older event, FOO will be restored to its state before either RPLACA operated. However if you undo the first event, then the second event, (CAR FOO) will be 1, since this is what was in CAR of FOO before (RPLACA FOO 2) was executed. Similarly, if the you type (NCONC1 FOO 1), followed by (NCONC1 FOO 2), undoing just (NCONC1 FOO 1) will remove both 1 and 2 from FOO. The problem in both cases is that the two operations are not "independent." In general, operations are always independent if they affect different lists or different sublists of the same list. Undoing in reverse order of execution, or undoing independent operations, is always guaranteed to do the "right" thing. However, undoing dependent operations out of order may not always have the predicted effect. Property list operations, (i.e., PUTPROP, ADDPROP and REMPROP) are handled specially, so that operations that affect different properties on the same property list are always independent. For example, if the user types (PUTPROP 'FOO 'BAR 1) then (PUTPROP 'FOO 'BAZ 2), then undoes the first event, the BAZ property will remain, even though it may not have been on the property list of FOO at the time the first event was executed. SAVESET Typed-in SETs are made undoable by substituting a call to SAVESET. SETQ is made undoable by substituting SAVESETQ, and SETQQ by SAVESETQQ, both of which are implemented in terms of SAVESET. In addition to saving enough information on the history list to enable undoing, SAVESET operates in a manner analogous to SAVEDEF (see Chapter 17) when it resets a top level value: when it changes a top level binding from a value other than NOBIND to a new value that is not EQUAL to the old one, SAVESET saves the old value of the variable being set on the variable's property list under the property VALUE, and prints the message (VARIABLE RESET). The old value can be restored via the function UNSET, which also saves the current value (but does not print a message). Thus UNSET can be used to flip back and forth between two values. Of course, UNDO can be used as long as the event containing this call to SAVESET is still active. Note however that the old value will remain on the property list, and therefore be recoverable via UNSET, even after the original event has been forgotten. RPAQ and RPAQQ are implemented via calls to SAVESET. Thus old values will be saved and messages printed for any variables that are reset as the result of loading a file. For top level variables, SAVESET also adds the variable to the appropriate spelling list, thereby noticing variables set in files via RPAQ or RPAQQ, as well as those set via type-in. (SAVESET(SAVESET (Function) NIL NIL ("13") 21) NAME VALUE TOPFLG FLG) [Function] An undoable SET. SAVESET scans the stack looking for the last binding of NAME, sets NAME to VALUE, and returns VALUE. If the binding changed was a top level binding, NAME is added to the spelling list SPELLINGS3 (see Chapter 20). Furthermore, if the old value was not NOBIND, and was also not EQUAL to the new value, SAVESET calls the file package to update the necessary file records. Then, if DFNFLG is not equal to T, SAVESET prints (NAME RESET), and saves the old value on the property list of NAME, under the property VALUE. If TOPFLG=T, SAVESET operates as above except that it always uses NAME's top-level value cell. When TOPFLG is T, and DFNFLG is ALLPROP and the old value was not NOBIND, SAVESET simply stores VALUE on the property list of NAME under the property VALUE, and returns VALUE. This option is used for loading files without disturbing the current value of variables (see Chapter 10). If FLG=NOPRINT, SAVESET saves the old value, but does not print the message. This option is used by UNSET. If FLG=NOSAVE, SAVESET does not save the old value on the property list, nor does it add NAME to SPELLINGS3. However, the call to SAVESET is still undoable. This option is used by /SET. If FLG=NOSTACKUNDO, SAVESET is undoable only if the binding being changed is a top-level binding, i.e. this says when resetting a variable that has been rebound, don't bother to make it undoable. (UNSET(UNSET (Function) NIL NIL ("13") 22) NAME) [Function] If NAME does not contain a property VALUE, UNSET generates an error. Otherwise UNSET calls SAVESET with NAME, the property value, TOPFLG=T, and FLG=NOPRINT. UNDONLSETQ and RESETUNDO The function UNDONLSETQ provides a limited form of backtracking: if an error occurs under the UNDONLSETQ, all undoable side effects executed under the UNDONLSETQ are undone. RESETUNDO, used in conjunction with RESETLST and RESETSAVE (see Chapter 14), provides a more general undo capability where the user can specify that the side effects be undone after the specified computation finishes, is aborted by an error, or by a Control-D. (UNDONLSETQ(UNDONLSETQ (Function) NIL NIL ("13") 22) UNDOFORM %) [NLambda Function] An nlambda function similar to NLSETQ (see Chapter 14). UNDONLSETQ evaluates UNDOFORM, and if no error occurs during the evaluation, returns (LIST (EVAL UNDOFORM)) and passes the undo information from UNDOFORM (if any) upwards. If an error does occur, the UNDONLSETQ returns NIL, and any undoable changes made during the evaluation of UNDOFORM are undone. Any undo information is stored directly on the history event (if LISPXHIST is not NIL), so that if the user Control-D's out of the UNDONLSETQ, the event is still undoable. UNDONLSETQ will operate correctly if #UNDOSAVES is or has been exceeded for this event, or is exceeded while under the scope of the UNDONLSETQ. Exercise caution when using coroutines or other non-standard means of exiting while under an UNDONLSETQ. See discussion in Chapter 14. (RESETUNDO X STOPFLG) [Function] For use in conjunction with RESETLST (see Chapter 14). (RESETUNDO) initializes the saving of undo information and returns a value which when given back to RESETUNDO undoes the intervening side effects. For example, (RESETLST (RESETSAVE (RESETUNDO)) . FORMS) will undo the side effects of FORMS on normal exit, or if an error occurs or a Control-D is typed. If STOPFLG=T, RESETUNDO stops accumulating undo information it is saving on X. This has no bearing on the saving of undo information on higher RESETUNDO's, or on being able to undo the entire event. For example, (RESETLST (SETQ FOO (RESETUNDO)) (RESETSAVE NIL (LIST 'RESETUNDO FOO)) (ADVISE ...) (RESETUNDO FOO T) . FORMS) would cause the advice to be undone, but not any of the side effects in FORMS. Format and Use of the History List 1 The system currently uses three history lists, LISPXHISTORY for the top-level Interlisp executive, EDITHISTORY for the editors, and ARCHIVELST for archiving events (see the Commands section above). All history lists have the same format, use the same functions, HISTORYSAVE, for recording events, and use the same set of functions for implementing commands that refer to the history list, e.g., HISTORYFIND, PRINTHISTORY, UNDOSAVE, etc. Each history list is a list of the form (L EVENT# SIZE MOD), where L is the list of events with the most recent event first, EVENT# is the event number for the most recent event on L, SIZE is the size of the time-slice (below), i.e., the maximum length of L, and MOD is the highest possible event number. LISPXHISTORY and EDITHISTORY are both initialized to (NIL 0 100 100). Setting LISPXHISTORY or EDITHISTORY to NIL disables all history features, so LISPXHISTORY and EDITHISTORY act like flags as well as repositories of events. One of the reasons you may want to disable the history list facility is to allow the garbage collector to reclaim objects stored on the history list. Simply setting LISPXHISTORY to NIL will not necessarily remove all pointers to the history list. GAINSPACE (see Chapter 22) is a useful function when trying to reclaim memory space. Each history list has a maximum length, called its "time-slice." As new events occur, existing events are aged, and the oldest events are "forgotten." For efficiency, the storage used to represent the forgotten event is reused in the representation of the new event, so the history list is actually a ring buffer. The time-slice of a history list can be changed with the function CHANGESLICE (see the Changing the Programmer's Assistant section above). Larger time-slices enable longer "memory spans," but tie up correspondingly greater amounts of storage. Since the user seldom needs really "ancient history," and a facility is provided for saving and remembering selected events (see NAME and RETRIEVE, in the Commands section above), a relatively small time-slice such as 30 events is more than adequate, although some users prefer to set the time-slice as large as 100 events. If PROMPT#FLG (see the Changing the Programmer's Assistant section above) is set to T, an "event number" will be printed before each prompt. More recent events have higher numbers. When the event number of the current event is 100, the next event will be given number 1. If the time-slice is greater than 100, the "roll-over" occurs at the next highest hundred, so that at no time will two events ever have the same event number. For example, if the time-slice is 150, event number 1 will follow event number 200. Each individual event on L is a list of the form (INPUT ID VALUE . PROPS). ID is the prompt character for this event, e.g., , :, *, etc. VALUE is the value of the event, and is initialized to bell. On EDITHISTORY, this field is used to save the side effects of each command (see the Editor and the Programmer's Assistant section below). PROPS is a property list used to associate other information with the event (described below). INPUT is the input sequence for the event. Normally, this is just the input that the user typed-in. For an APPLY-format input (see the Input Formats section above), this is a list consisting of two expressions; for an EVAL-format input, this is a list of just one expression; for an input entered as list of atoms, INPUT is simply that list. For example, User Input INPUT is: PLUS[1 1] (PLUS (1 1)) (PLUS 1 1) ((PLUS 1 1)) PLUS 1 1cr (PLUS 1 1) If the user types in a programmer's assistant command that "unreads" and reexecutes other events (REDO, USE,, etc.), INPUT contains a "sequence" of the inputs from the redone events. Specifically, the INPUT fields from the specified events are concatenated into a single list, seperated by special markers called "pseudo-carriage returns," which print out as the string "". When the result of this concatenation is "reread," the pseudo-carriage-returns are treated by LISPXREAD and READLINE exactly as real carriage returns, i.e., they serve to distinguish between APPLY-format and EVAL-format inputs to LISPX, and to delimit line commands to the editor. The value of the variable HISTSTR0 is used to represent a pseudo-carriage return. This is initially the string "". The functions that recognize pseudo-carriage returns compare them to HISTSTR0 using EQ, so this marker will never be confused with a string that was typed in by the user. The same convention is used for representing multiple inputs when a USE command involves sequential substitutions. For example, if the user types GETD(FOO) and then USE FIE FUM FOR FOO, the input sequence that will be constructed is (GETD (FIE) "" GETD (FUM)), which is the result of substituting FIE for FOO in (GETD (FOO)) concatenated with the result of substituting FUM for FOO in (GETD (FOO)). Once a multiple input has been entered as the input portion of a new event, that event can be treated exactly the same as one resulting from type-in. In other words, no special checks have to be made when referencing an event, to see if it is simple or multiple. This implementation permits an event specification to refer to a single simple event, or to several events, or to a single event originally constructed from several events (which may themselves have been multiple input events, etc.) without having to treat each case separately. REDO, RETRY, USE, ..., and FIX commands, i.e., those commands that reexecute previous events, are not stored as inputs, because the input portion for these events are the expressions to be "reread". The history commands UNDO, NAME, RETRIEVE, BEFORE, and AFTER are recorded as inputs, and ?? prints them exactly as they were typed. PROPS is a property list of the form (PROPERTY1 VALUE1 PROPERTY2 VALUE2 ...), that can be used to associate arbitrary information with a particular event. Currently, the following properties are used by the programmer's assistant: SIDE A list of the side effects of the event. See UNDOSAVE, in the Programmer's Assistant Functions section below. *PRINT* Used by the ?? command when special formatting is required, for example, when printing events corresponding to the break commands OK, GO, EVAL, and ?=. USE-ARGS ...ARGS The USE-ARGS and ...ARGS properties are used to save the arguments and expression for the corresponding history command. *ERROR* *CONTEXT* *ERROR* and *CONTEXT* are used to save information when errors occur for subsequent use by the $ command. Whenever an error occurs, the offender is automatically saved on that event's entry in the history list, under the *ERROR* property. *LISPXPRINT* Used to record calls to LISPXPRINT, LISPXPRIN1, etc. (see the Changing the Programmer's Assistant section above). *ARCHIVE* The property *ARCHIVE* on an event causes the event to be automatically archived when it "falls off the end" of the history list (see the Commands section above). *GROUP* *HISTORY* The *HISTORY* and *GROUP* properties are used for commands that reexecute previous events, i.e., REDO, RETRY, USE, ..., and FIX. The value of the *HISTORY* property is the history command that the user actually typed, e.g., REDO FROM F. This is used by the ?? command when printing the event. The value of the *GROUP* property is a structure containing the side effects, etc. for the individual inputs being reexecuted. This structure is described below. When LISPX is given an input, it calls HISTORYSAVE (see the Programmer's Assistant Functions section below) to record the input in a new event (except for the commands ??, FORGET, TYPE-AHEAD, $BUFS, and ARCHIVE, that are executed immediately and are not recorded on the history list). Normally, HISTORYSAVE creates and returns a new event. LISPX binds the variable LISPXHIST to the value of HISTORYSAVE, so that when the operation has completed, LISPX knows where to store the value. Note that by the time it completes, the operation may no longer correspond to the most recent event on the history list. For example, all inputs typed to a lower break will appear later on the history list. After binding LISPXHIST, LISPX executes the input, stores its value in the value field of the LISPXHIST event, prints the value, and returns. When the input is a REDO, RETRY, USE, ..., or FIX command, the procedure is similar, except that the event is also given a *GROUP* property, initially NIL, and a *HISTORY* property, and LISPX simply unreads the input and returns. When the input is "reread", it is HISTORYSAVE, not LISPX, that notices this fact, and finds the event from which the input originally came. If HISTORYSAVE cannot find the event, for example if a user program unreads the input directly, and not via a history command, HISTORYSAVE proceeds as though the input were typed. HISTORYSAVE then adds a new (INPUT ID VALUE . PROPS) entry to the *GROUP* property for this event, and returns this entry as the "new event." LISPX then proceeds exactly as when its input was typed directly, i.e., it binds LISPXHIST to the value of HISTORYSAVE, executes the input, stores the value in CADDR of LISPXHIST, prints the value, and returns. In fact, LISPX never notices whether it is working on freshly typed input, or input that was reread. Similarly, UNDOSAVE will store undo information on LISPXHIST the same as always, and does not know or care that LISPXHIST is not the entire event, but one of the elements of the *GROUP* property. Thus when the event is finished, its entry will look like: (INPUT ID VALUE *HISTORY* COMMAND *GROUP* ((INPUT1 ID1 VALUE1 SIDE SIDE1) (INPUT2 ID2 VALUE2 SIDE SIDE2) ...)) In this case, the value field of the event with the *GROUP* property is not being used; VALUEOF instead returns a list of the values from the *GROUP* property. Similarly, UNDO operates by collecting the SIDE properties from each of the elements of the *GROUP* property, and then undoing them in reverse order. This implementation removes the burden from the function calling HISTORYSAVE of distinguishing between new input and reexecution of input whose history entry has already been set up. Programmer's Assistant Functions 1 (LISPX(LISPX (Function) NIL NIL ("13") 26) LISPXX LISPXID LISPXXMACROS LISPXXUSERFN LISPXFLG) [Function] LISPX is the primary function of the programmer's assistant. LISPX takes one user input, saves it on the history list, evaluates it, saves its value, and prints and returns it. LISPX also interpretes p.a. commands, LISPXMACROS, LISPXHISTORYMACROS, and LISPXUSERFN. If LISPXX is a list, it is interpreted as the input expression. Otherwise, LISPX calls READLINE, and uses LISPXX plus the value of READLINE as the input for the event. If LISPXX is a list CAR of which is LAMBDA or NLAMBDA, LISPX calls LISPXREAD to obtain the arguments. LISPXID is the prompt character to print before accepting user input. The user should be careful about using the prompt characters "," "*," or ":," because in certain cases LISPX uses the value of LISPXID to tell whether or not it was called from the break package or editor. If LISPXXMACROS is not NIL, it is used as the list of LISPX macros, otherwise the top level value of the variable LISPXMACROS is used. If LISPXXUSERFN is not NIL, it is used as the LISPXUSERFN. In this case, it is not necessary to both set and define LISPXUSERFN as described in the Changing the Programmer's Assistant section above. LISPXFLG is used by the E command in the editor (see the Editor and Programmer's Assistant section below). The history is not one of the arguments to LISPX, i.e., the editor must bind (reset) LISPXHISTORY to EDITHISTORY before calling LISPX to carry out a history command. LISPX will continue to operate as an EVAL/APPLY function if LISPXHISTORY is NIL. Only those functions and commands that involve the history list will be affected. LISPX performs spelling corrections using LISPXCOMS, a list of its commands, as a spelling list whenever it is given an unbound atom or undefined function, before attempting to evaluate the input. LISPX is responsible for rebinding HELPCLOCK, used by BREAKCHECK (see Chapter 14) for computing the amount of time spent in a computation, in order to determine whether to go into a break if and when an error occurs. (USEREXEC(USEREXEC (Function) NIL NIL ("13") 26) LISPXID LISPXXMACROS LISPXXUSERFN) [Function] Repeatedly calls LISPX under errorset protection specifying LISPXXMACROS and LISPXXUSERFN, and using LISPXID (or if LISPXID=NIL) as a prompt character. USEREXEC is exited via the command OK, or else with a RETFROM. (LISPXEVAL(LISPXEVAL (Function) NIL NIL ("13") 26) LISPXFORM LISPXID) [Function] Evaluates LISPXFORM (using EVAL) the same as though it were typed in to LISPX, i.e., the event is recorded, and the evaluation is made undoable by substituting the slash functions for the corresponding destructive functions (see below). LISPXEVAL returns the value of the form, but does not print it. When LISPX recieves an "input," it may come from the user typing it in, or it may be an input that has been "unread." LISPX handles these two cases by getting inputs with LISPXREAD and READLINE, described below. These functions use the following variable to store the expressions that have been unread: READBUF(READBUF (Variable) NIL NIL ("13") 27) [Variable] This variable is used by LISPXREAD and READLINE to store the expressions that have been unread. When READBUF is not NIL, READLINE and LISPXREAD "read" expressions from READBUF until READBUF is NIL, or until they read a pseudo-carriage return (see the Format and Use of the History List section above). Both functions return a list of the expressions that have been "read." (The pseudo-carriage return is not included in the list.) When READBUF is NIL, both LISPXREAD and READLINE actually obtain their input by performing (APPLY* LISPXREADFN FILE), where LISPXREADFN is initially set to TTYINREAD (Chapter 26). The user can make LISPX, the editor, break, etc. do their reading via a different input function by simply setting LISPXREADFN to the name of that function (or an appropriate LAMBDA expression). You should only add expressions to READBUF using the function LISPXUNREAD (see below), which knows about the format of READBUF. (READLINE(READLINE (Function) NIL NIL ("13") 27) RDTBL % %) [Function] Reads a line from the terminal, returning it as a list. If (READP T) is NIL, READLINE returns NIL. Otherwise it reads expressions until it encounters one of the following: f an EOL (typed by the user) that is not preceded by any spaces, e.g., A B Ccr and READLINE returns (A B C) f a list terminating in a "]", in which case the list is included in the value of READLINE, e.g., A B (C D] and READLINE returns (A B (C D)). f an unmatched right parentheses or right square bracket, which is not included in the value of READLINE, e.g., A B C] and READLINE returns (A B C). In the case that one or more spaces precede a carriage-return, or a list is terminated with a ")", READLINE will type "..." and continue reading on the next line, e.g., A B Ccr ...(D E F) ...(X Y Z] and READLINE returns (A B C (D E F) (X Y Z)). If the user types another carriage-return after the "...", the line will terminate, e.g., A B Ccr ...cr and READLINE returns (A B C). Carriage-return, i.e., the EOL character, can be redefined with SETSYNTAX (Chapter 25). READLINE actually checks for the EOL character, whatever that may be. The same is true for right parenthesis and right bracket. When READLINE is called from LISPX, it operates differently in two respects: 1. If the line consists of a single ) or ], READLINE returns (NIL) instead of NIL, i.e., the ) or ] is included in the line. This permits the user to type FOO) or FOO], meaning call the function FOO with no arguments, as opposed to FOOcr (FOO), meaning evaluate the variable FOO. 2. If the first expression on the line is a list that is not preceded by any spaces, the list terminates the line regardless of whether or not it is terminated by ]. This permits the user to type EDITF(FOO) as a single input. If any spaces are inserted between the atom and the left parentheses or bracket, READLINE assumes that the list does not terminate the line. This enables you to type a line command such as USE (FOO) FOR FOO. Therefore, if you accidentially put an extra space between a function and its arguments, you have to complete the input with another carriage return, e.g., EDITF (FOO) ...cr EDIT * Note: READLINE reads expressions by performing (APPLY* LISPXREADFN T). LISPXREADFN (above) is initially set to TTYINREAD (Chapter 26). (LISPXREAD(LISPXREAD (Function) NIL NIL ("13") 28) FILE RDTBL) [Function] A generalized READ. If READBUF=NIL, LISPXREAD performs (APPLY* LISPXREADFN FILE), which it returns as its value. If READBUF is not NIL, LISPXREAD "reads" and returns the next expression on READBUF. LISPXREAD also sets REREADFLG (see below) to NIL when it reads via LISPXREADFN, and sets REREADFLG to the value of READBUF when rereading. (LISPXREADP(LISPXREADP (Function) NIL NIL ("13") 28) FLG) [Function] A generalized READP. If FLG=T, LISPXREADP returns T if there is any input waiting to be "read", in the manner of LISPXREAD. If FLG=NIL, LISPXREADP returns T only if there is any input waiting to be "read" on this line. In both cases, leading spaces are ignored, i.e., skipped over with READC, so that if only spaces have been typed, LISPXREADP will return NIL. (LISPXUNREAD LST %) [Function] Unreads LST, a list of expressions. (PROMPTCHAR ID FLG HISTORY) [Function] Called by LISPX to print the prompt character ID before each input. PROMPTCHAR will not print anything when the next input will be "reread", i.e., when READBUF is not NIL. PROMPTCHAR will not print when (READP)=T, unless FLG is T. The editor calls PROMPTCHAR with FLG=NIL so that extra *'s are not printed when the user types several commands on one line. However, EVALQT calls PROMPTCHAR with FLG=T, since it always wants the printed (except when "rereading"). If PROMPT#FLG (see the Changing the Programmer's Assistant section above) is T and HISTORY is not NIL, PROMPTCHAR prints the current event number (of HISTORY) before printing ID. The value of PROMPTCHARFORMS (see the Changing the Programmer's Assistant section above) is a list of expressions that are evaluated by PROMPTCHAR before, and if, it does any printing. (HISTORYSAVE(HISTORYSAVE (Function) NIL NIL ("13") 29) HISTORY ID INPUT1 INPUT2 INPUT3 PROPS) [Function] Records one event on HISTORY. If INPUT1 is not NIL, the input is of the form (INPUT1 INPUT2 . INPUT3). If INPUT1 is NIL, and INPUT2 is not NIL, the input is of the form (INPUT2 . INPUT3). Otherwise, the input is just INPUT3. HISTORYSAVE creates a new event with the corresponding input, ID, value field initialized to bell, and PROPS. If the HISTORY has reached its full size, the last event is removed and cannibalized. The value of HISTORYSAVE is the new event. However, if REREADFLG is not NIL, and the most recent event on the history list contains the history command that produced this input, HISTORYSAVE does not create a new event, but simply adds an (INPUT ID bell . PROPS) entry to the *GROUP* property for that event and returns that entry. See the discussion in the prior section. HISTORYSAVEFORMS (see the Changing the Programmer's Assistant section above) is a list of expressions that are evaluated under errorset protection each time HISTORYSAVE creates a new event. (LISPXSTOREVALUE(LISPXSTOREVALUE (Function) NIL NIL ("13") 29) EVENT VALUE) [Function] Used by LISPX for storing the value of an event. Can be advised by user to watch for particular values or perform other monitoring functions. (LISPXFIND(LISPXFIND (Function) NIL NIL ("13") 29) HISTORY LINE TYPE BACKUP %) [Function] LINE is an event specification, TYPE specifies the format of the value to be returned by LISPXFIND, and can be either ENTRY, ENTRIES, COPY, COPIES, INPUT, or REDO. LISPXFIND parses LINE, and uses HISTORYFIND (see below) to find the corresponding events. LISPXFIND then assembles and returns the appropriate structure. LISPXFIND incorporates the following special features: 1. if BACKUP=T, LISPXFIND interprets LINE in the context of the history list before the current event was added. This feature is used, for example, by VALUEOF, so that (VALUEOF -1) will not refer to the VALUEOF event itself. 2. if LINE=NIL and the last event is an UNDO, the next to the last event is taken. This permits the user to type UNDO followed by REDO or USE. 3. LISPXFIND recognizes @@, and searches the archived history list instead of HISTORY (see the ARCHIVE command in the Commands section above). 4. LISPXFIND recognizes @, and retrieves the corresponding event(s) from the property list of the atom following @ (see the Commands section above). (HISTORYFIND(HISTORYFIND (Function) NIL NIL ("13") 29) LST INDEX MOD EVENTADDRESS %) [Function] Searches LST and returns the tails of LST beginning with the event corresponding to EVENTADDRESS. LST, INDEX, and MOD are the first three elements of a "history list" structure (see the Format and Use of the History List section above). EVENTADDRESS is an event address (see the Event Specification section abo e) e.g., (43), (-1), (FOO FIE), (LOAD FOO), etc. If HISTORYFIND cannot find EVENTADDRESS, it generates an error. (HISTORYMATCH(HISTORYMATCH (Function) NIL NIL ("13") 30) INPUT PAT EVENT) [Function] Used by HISTORYFIND for "matching" when EVENTADDRESS specifies a pattern. Matches PAT against INPUT, the input portion of the history event EVENT, as matching is defined in Chapter 16. Initially defined as (EDITFINDP INPUT PAT T), but can be advised or redefined by the user. (ENTRY#(ENTRY# (Function) NIL NIL ("13") 30) HIST X) [Function] HIST is a history list (see the Format and Use of the History List section above). X is EQ to one of the events on HIST. ENTRY# returns the event number for X. (UNDOSAVE(UNDOSAVE (Function) NIL NIL ("13") 30) UNDOFORM HISTENTRY) [Function] UNDOSAVE adds the "undo information" UNDOFORM to the SIDE property of the history event HISTENTRY. If there is no SIDE property, one is created. If the value of the SIDE property is NOSAVE, the information is not saved. HISTENTRY specifies an event. If HISTENTRY=NIL, the value of LISPXHIST is used. If both HISTENTRY and LISPXHIST are NIL, UNDOSAVE is a no-op. HISTENTRY (or LISPXHIST) can either be a "real" event, or an event within the *GROUP* property of another event (see the Format and Use of the History List section above). The form of UNDOFORM is (FN . ARGS). Undoing is done by performing (APPLY (CAR UNDOFORM) (CDR UNDOFORM)). For example, if the definition of FOO is DEF, (/PUTD FOO NEWDEF) will cause a call to UNDOSAVE with UNDOFORM=(/PUTD FOO DEF). In the special case of /RPLNODE and /RPLNODE2, the format of UNDOFORM is (X OLDCAR . OLDCDR). When UNDOFORM is undone, this form is recognized and handled specially. This implementation saves space. CAR of the SIDE property of an event is a count of the number of UNDOFORMs saved for this event. Each call to UNDOSAVE increments this count. If this count is set to -1, then it is never incremented, and any number of UNDOFORMs can be saved. If this count is a positive number, UNDOSAVE restricts the number of UNDOFORMs saved to the value of #UNDOSAVES, described below. LOAD initializes the count to -1, so that regardless of the value of #UNDOSAVES, no message will be printed, and the LOAD will be undoable. #UNDOSAVES(#UNDOSAVES (Variable) NIL NIL ("13") 30) [Variable] The value of #UNDOSAVES is the maximum number of UNDOFORMs to be saved for a single event. When the count of UNDOFORMs reaches this number, UNDOSAVE prints the message CONTINUE SAVING?, asking the user if he wants to continue saving. If the user answers NO or defaults, UNDOSAVE discards the previously saved information for this event, and makes NOSAVE be the value of the property SIDE, which disables any further saving for this event. If the user answers YES, UNDOSAVE changes the count to -1, which is then never incremented, and continues saving. The purpose of this feature is to avoid tying up large quantities of storage for operations that will never need to be undone. If #UNDOSAVES is negative, then when the count reaches -#UNDOSAVES, UNDOSAVE simply stops saving without printing any messages or interacting with the user. #UNDOSAVES=NIL is equivalent to #UNDOSAVES=infinity. #UNDOSAVES is initially NIL. (NEW/FN FN) [Function] NEW/FN performs the necessary housekeeping operations to make FN be translated to the undoable version /FN when typed-in. For example, RADIX can be made undoable when typed-in by performing: (DEFINEQ (/RADIX (X) (UNDOSAVE (LIST '/RADIX (RADIX X)) (/RADIX) (NEW/FN 'RADIX) (LISPX/(LISPX/ (Function) NIL NIL ("13") 31) X FN VARS) [Function] LISPX/ performs the substitution of / functions for destructive functions that are typed-in. If FN is not NIL, it is the name of a function, and X is its argument list. If FN is NIL, X is a form. In both cases, LISPX/ returns X with the appropriate substitutions. VARS is a list of bound variables (optional). LISPX/ incorporates information about the syntax and semantics of Interlisp expressions. For example, it does not bother to make undoable operations involving variables bound in X. It does not perform substitution inside of expressions CAR of which is an nlambda function (unless CAR of the form has the property INFO value EVAL, see Chapter 21). For example, (BREAK PUTD) typed to LISPX, will break on PUTD, not /PUTD. Similarly, substitution should be performed in the arguments for functions like MAPC, RPTQ, etc., since these contain expressions that will be evaluated or applied. For example, if the user types (MAPC '(FOO1 FOO2 FOO3) 'PUTD) the PUTD must be replaced by /PUTD. (UNDOLISPX(UNDOLISPX (Function) NIL NIL ("13") 31) LINE) [Function] LINE is an event specification. UNDOLISPX is the function that executes UNDO commands by calling UNDOLISPX1 on the appropriate entry(s). (UNDOLISPX1(UNDOLISPX (Function) NIL NIL ("13") 31) EVENT FLG %) [Function] Undoes one event. UNDOLISPX1 returns NIL if there is nothing to be undone. If the event is already undone, UNDOLISPX1 prints already undone and returns T. Otherwise, UNDOLISPX1 undoes the event, prints a message, e.g., SETQ undone, and returns T. If FLG=T and the event is already undone, or is an undo command, UNDOLISPX1 takes no action and returns NIL. UNDOLISPX uses this option to search for the last event to undo. Thus when LINE=NIL, UNDOLISPX simply searches history until it finds an event for which UNDOLISPX1 returns T. Undoing an event consists of mapping down (CDR of) the property value for SIDE, and for each element, applying CAR to CDR, and then marking the event undone by attaching (with /ATTACH) a NIL to the front of its SIDE property. Note that the undoing of each element on the SIDE property will usually cause undosaves to be added to the current LISPXHIST, thereby enabling the effects of UNDOLISPX1 to be undone. (PRINTHISTORY(PRINTHISTORY (Function) NIL NIL ("13") 31) HISTORY LINE SKIPFN NOVALUES FILE) [Function] LINE is an event specification. PRINTHISTORY prints the events on HISTORY specified by LINE, e.g., (-1 THRU -10). Printing is performed via the function SHOWPRIN2, so that if the value of SYSPRETTYFLG=T, events will be prettyprinted. SKIPFN is an (optional) functional argument that is applied to each event before printing. If it returns non-NIL, the event is skipped, i.e., not printed. If NOVALUES=T, or NOVALUES applied to the corresponding event is true, the value is not printed. For example, NOVALUES is T when printing events on EDITHISTORY. For example, the following LISPXMACRO will define ??' as a command for printing the history list while skipping all "large events" and not printing any values. (??' (PRINTHISTORY LISPXHISTORY LISPXLINE (FUNCTION (LAMBDA (X) (IGREATERP (COUNT (CAR X)) 5))) T T)) The Editor and the Programmer's Assistant 1 As mentioned earlier, all of the remarks concerning "the programmer's assistant" apply equally well to user interactions with EVALQT, BREAK or the editor. The differences between the editor's implementation of these features and that of LISPX are mostly obvious or inconsequential. However, for completeness, this section discusses the editor's implementation of the programmer's assistant. The editor uses PROMPTCHAR to print its prompt character, and LISPXREAD, LISPXREADP, and READLINE for obtaining inputs. When the editor is given an input, it calls HISTORYSAVE to record the input in a new event on its history list, EDITHISTORY, except that the atomic commands OK, STOP, SAVE, P, ?, PP and E are not recorded. In addition, number commands are grouped together in a single event. For example, 3 3 -1 is considered as one command for changing position. EDITHISTORY follows the same conventions and format as LISPXHISTORY (see the Format and Use of the History List section above). However, since edit commands have no value, the editor uses the value field for saving side effects, rather than storing them under the property SIDE. The editor recognizes and processes the four commands DO, !E, !F, and !N which refer to previous events on EDITHISTORY. The editor also processes UNDO itself, as described below. All other history commands are simply given to LISPX for execution, after first binding (resetting) LISPXHISTORY to EDITHISTORY. The editor also calls LISPX when given an E command (see Chapter 16). In this case, the editor uses the fifth argument to LISPX, LISPXFLG, to specify that any history commands are to be executed by a recursive call to LISPX, rather than by unreading. For example, if the user types E REDO in the editor, he wants the last event on LISPXHISTORY processed as LISPX input, and not to be unread and processed by the editor. Note: The editor determines which history commands to pass to LISPX by looking at HISTORYCOMS, a list of the history commands. EDITDEFAULT (see Chapter 16) interrogates HISTORYCOMS before attempting spelling correction. (All of the commands on HISTORYCOMS are also on EDITCOMSA and EDITCOMSL so that they can be corrected if misspelled in the editor.) Thus if the user defines a LISPXMACRO and wishes it to operate in the editor as well, he need simply add it to HISTORYCOMS. For example, RETRIEVE is implemented as a LISPXMACRO and works equally well in LISPX and the editor. The major implementation difference between the editor and LISPX occurs in undoing. EDITHISTORY is a list of only the last N commands, where N is the value of the time-slice. However the editor provides for undoing all changes made in a single editing session, even if that session consisted of more than N edit commands. Therefore, the editor saves undo information independently of the EDITHISTORY on a list called UNDOLST, (although it also stores each entry on UNDOLST in the field of the corresponding event on EDITHISTORY.) Thus, the commands UNDO, !UNDO, and UNBLOCK, are not dependent on EDITHISTORY, and in fact will work if EDITHISTORY=NIL, or even in a system which does not contain LISPX at all. For example, UNDO specifies undoing the last command on UNDOLST, even if that event no longer appears on EDITHISTORY. The only interaction between UNDO and the history list occurs when the user types UNDO followed by an event specification. In this case, the editor calls LISPXFIND to find the event, and then undoes the corresponding entry on UNDOLST. Thus the user can only undo a specified command within the scope of the EDITHISTORY. (Note that this is also the only way UNDO commands themselves can be undone, that is, by using the history feature, to specify the corresponding event, e.g., UNDO UNDO.) The implementation of the actual undoing is similar to the way it is done in LISPX: each command that makes a change in the structure being edited does so via a function that records the change on a variable. After the command has completed, this variable contains a list of all the pointers that have been changed and their original contents. Undoing that command simply involves mapping down that list and restoring the pointers. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "13-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "13-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "13-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "13-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "13-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "13-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))/2l2l,ll/HH52HH +l2l~~2l~~3HZ +T-llT6T-llT5lxx2lxx2lxx,,/ll6HT6HT6HT52Hll5,,3HZT3HZ +T3$$(T,ll,ll,ll,23(T-T3(T,HH +,HH,HHF PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR5,,CLASSIC +TITAN +TITAN +TITAN +TITAN + HELVETICA  HELVETICACLASSIC +CLASSIC +CLASSIC +TITAN + HELVETICA HELVETICA +CLASSIC +MODERNMODERN +MODERN +MODERNMODERN +, HRULE.GETFNCLASSIC +, + HRULE.GETFNCLASSIC ++ * * HRULE.GETFNCLASSIC +) ) HRULE.GETFNCLASSIC +" IM.CHAP.GETFN HELVETICA! HRULE.GETFNMODERN +('&<IM.INDEX.GETFN''         O                     &                  +     $        (  9   9   9   ( D ''v  \  K Z % jIM.INDEX.GETFNCLASSIC + $ HRULE.GETFNMODERN +&C   /  .   / . . . .  . .        d c +    *  G 9/ +  . = / +  . [ L/  . .  E   +# IM.INDEX.GETFNCLASSIC + $ HRULE.GETFNMODERN +&'&  + X  n'0   '  + '7  Q " ? -   K + "   0  t   T #%  + +   d , ^ ?&   +  +  +   "  !  (       >    #   ;       +      +  + 8 &    + 8 ?   +  +      < 1  +       +   Q %   +  * K    h * % &' Q      !IM.INDEX.GETFN (    (   " +   4  ?       +  + +  +   #IM.INDEX.GETFN   +  + ^       I  "IM.INDEX.GETFN    @  IM.INDEX.GETFN         2 ;  ! ,   ,  6 8        + !     D 2   +  5 Z  0 !    +  K     + *   +            +  @  n &  v      '  |     4  ,      9             )' B h "   "  ?      h  N   $   +   & Z       # %   + >          +      +      +      +    +  +  +  +  +    %   +     @    '      k p  ;    -  $ &    "   +    ) + '  !           IM.INDEX.GETFN `  6   D4             ,  A *   t  7  O ' )  +   - #   ; + !  +   "IM.INDEX.GETFN I   7  A  +  e ' J  .    I !      % & 4  + +  +   X d)  L +  + + +  A +   ,  9     H  +   +  +   +  L  +  9  & ( "IM.INDEX.GETFN           + +  1  5  .   +  +  +   #!'    ++ 4 +#    + +)     1   &IM.INDEX.GETFN ?   +"   {  D   S ?      Y      $IM.INDEX.GETFN + *  #IM.INDEX.GETFN    + &  Z ;  ?     A  +'  7    '     2    (   ]   %IM.INDEX.GETFN   ) +  < #  + W_ f   $IM.INDEX.GETFN N  ; Y      H  &IM.INDEX.GETFN A G 8 %   )    ,  &  + +    A  # +      .  + +   IM.INDEX.GETFN 8  + +     ! +  IM.INDEX.GETFN '  + +  K !  q . b  IM.INDEX.GETFN !IM.INDEX.GETFN  + (IM.INDEX.GETFNH +  J l  " &   9 +   6                               ? "    ; ` &7 &IM.INDEX.GETFN  I  y '  D  d ;  #IM.INDEX.GETFN  6 / . . + . -  ; + K 3& &A  4 %    2    ~   $  '(    E   "   \  +   A 3 '45 +4 4 0  +1 $  +%$ $ HRULE.GETFNMODERN + *IM.INDEX.GETFNTITAN +  +  A  *   +  +    6 * + )IM.INDEX.GETFNMODERN +  L + 9 +   .IM.INDEX.GETFN   7 + g + 1 % 3  +  +   +   / W +     !  "  /IM.INDEX.GETFN   Q  J  %  &   + )IM.INDEX.GETFN   + / 2  (IM.INDEX.GETFN    R   V     ! $  + )IM.INDEX.GETFN  +   +  >;  }  *IM.INDEX.GETFN     +   0  N  B     )    +  +     $ 1IM.INDEX.GETFN  Z  ' + <   E ..       5  *IM.INDEX.GETFN   f   o  + ) 9   + 6    (     $ % U +               (  h y )IM.INDEX.GETFN       +    + *IM.INDEX.GETFN   + *IM.INDEX.GETFN    'IM.INDEX.GETFN   + ,IM.INDEX.GETFN       + +    +     b4 Q =  N  -IM.INDEX.GETFN     6 1 ` $       e d ,IM.INDEX.GETFN    F  %$ HRULE.GETFNMODERN +&'     '  ` 'J'     Z 'R   %  T ] & 7'<   7           +           #' V  ?  [  'L ;& h   C `  ` - 0      +      '!     # P +&  .  "   , 'P # q   b    + 2 K 8'  : v 4'   x' f  $ &IM.INDEX.GETFN      1    0  + :   H      + 2     .           m   N     :  +  ,     $IM.INDEX.GETFN             &    ^  6 + c )IM.INDEX.GETFNTITAN +  &  +  8  +  & 0 +  9 A  . +  +  + U + ] +      Y 4  +  +   @   5 C /  +  )      )  %# $ HRULE.GETFNMODERN +&/ (  + y z   '(   +  +  +   9 2  D  (      #  3'  @ L' )  ' + G '    +  +  +  +   2   < ~ Z' 8 $-  - - -  'b  + P  a  r .' N H  U'D L + 1  + &   .   ' G'            )'  +   +  +  +  +  . 7   t     +    a  +   J ~   +  + C      H      E  4   v   +   V #   ,   @ '     J    J  X q +    +  +  +  +   F L  *  + c  4 9 G   +  +  + +  + +  + +  + +   +  +  +  +   + +   +  +  +  +   + '4  /   - 3'A j%! $ HRULE.GETFNMODERN + $IM.INDEX.GETFNMODERN +2    9 p !    C    !           H   7 +   < H  R  %   "  +   T %   + +  'IM.INDEX.GETFN!    &   +      + (IM.INDEX.GETFNMODERN +    +  )  7 m 0  n &IM.INDEX.GETFNMODERN +   7           +  +    " ] 1 #  .  'IM.INDEX.GETFNMODERN +  <    L ?      6    a     _   +/      5 +"       "   ]  +$        6   "  2   + Q e       !$   + (IM.INDEX.GETFNMODERN + +       +  +  %   ,        )IM.INDEX.GETFN       +  >    +  1 E * +           +   + J   +      +    O  +   A + @    + %    l + & *IM.INDEX.GETFN&         +  +         +  +    3 ' + H    g 1   +  +  +   [   .IM.INDEX.GETFN     (IM.INDEX.GETFNTITAN +4    5          0 7 .    $ E +     F     4 + )  X # *IM.INDEX.GETFNTITAN +8     +    y G        +IM.INDEX.GETFN       ) > +  +  +  + / %IM.INDEX.GETFN    P      'IM.INDEX.GETFN        0            7 W   +  +  +  !    +  %   +  +     +  +        +  +  +   ]  2 & e 5   +  @ + &  + )IM.INDEX.GETFNMODERN +   +  5  G  E  I   + + +  R +   +  +       8 '   3 (    %IM.INDEX.GETFN     <  $      & *  : )   ! +    2  l    + (IM.INDEX.GETFNMODERN +       +  (IM.INDEX.GETFNMODERN +   +  D +    + +    9 +   C   ; +  +  !  6   9 :  " +  +IM.INDEX.GETFN"        )    h +   U    +  k    ,   %* $ HRULE.GETFNMODERN +&~  c ' + $  +  C 9 "       g 6 ,  '6    #  M 0    P  Q <  +  :?  #  A   Y + J   +  ';    J W S  ) ,       -  ' *  1 E ? ! ! ( u 'M `' z \ No newline at end of file diff --git a/docs/porter-irm/vol2/14-ERRORS.INTERPRESS b/docs/porter-irm/vol2/14-ERRORS.INTERPRESS new file mode 100644 index 00000000..98060a00 Binary files /dev/null and b/docs/porter-irm/vol2/14-ERRORS.INTERPRESS differ diff --git a/docs/porter-irm/vol2/14-ERRORS.TEDIT b/docs/porter-irm/vol2/14-ERRORS.TEDIT new file mode 100644 index 00000000..cdfcc8da Binary files /dev/null and b/docs/porter-irm/vol2/14-ERRORS.TEDIT differ diff --git a/docs/porter-irm/vol2/15-BREAKING.INTERPRESS b/docs/porter-irm/vol2/15-BREAKING.INTERPRESS new file mode 100644 index 00000000..b9a2d578 Binary files /dev/null and b/docs/porter-irm/vol2/15-BREAKING.INTERPRESS differ diff --git a/docs/porter-irm/vol2/15-BREAKING.TEDIT b/docs/porter-irm/vol2/15-BREAKING.TEDIT new file mode 100644 index 00000000..5cbe3169 Binary files /dev/null and b/docs/porter-irm/vol2/15-BREAKING.TEDIT differ diff --git a/docs/porter-irm/vol2/16-LIST-STRUCTURE.INTERPRESS b/docs/porter-irm/vol2/16-LIST-STRUCTURE.INTERPRESS new file mode 100644 index 00000000..43215354 Binary files /dev/null and b/docs/porter-irm/vol2/16-LIST-STRUCTURE.INTERPRESS differ diff --git a/docs/porter-irm/vol2/16-LIST-STRUCTURE.TEDIT b/docs/porter-irm/vol2/16-LIST-STRUCTURE.TEDIT new file mode 100644 index 00000000..f94fa02a Binary files /dev/null and b/docs/porter-irm/vol2/16-LIST-STRUCTURE.TEDIT differ diff --git a/docs/porter-irm/vol2/17-FILEPACKAGE.INTERPRESS b/docs/porter-irm/vol2/17-FILEPACKAGE.INTERPRESS new file mode 100644 index 00000000..a04897e1 Binary files /dev/null and b/docs/porter-irm/vol2/17-FILEPACKAGE.INTERPRESS differ diff --git a/docs/porter-irm/vol2/17-FILEPACKAGE.TEDIT b/docs/porter-irm/vol2/17-FILEPACKAGE.TEDIT new file mode 100644 index 00000000..1efa3dce --- /dev/null +++ b/docs/porter-irm/vol2/17-FILEPACKAGE.TEDIT @@ -0,0 +1,422 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 17. FILE PACKAGE 1 17. FILE PACKAGE 1 "17"17. FILE PACKAGE 6 Warning: The subsystem within the Interlisp-D environment used for managing collections of definitions (of functions, variables, etc.) is known as the "File Package." This terminology is confusing, because the word "file" is also used in the more conventional sense as meaning a collection of data stored some physical media. Unfortunately, it is not possible to change this terminology at this time, because many functions and variables (MAKEFILE, FILEPKGTYPES, etc.) incorporate the word "file" in their names. Eventually, the system and the documentation will be revamped to consistantly use the term "module" or "definition group" or "defgroup." Most implementations of Lisp treat symbolic files as unstructured text, much as they are treated in most conventional programming environments. Function definitions are edited with a character-oriented text editor, and then the changed definitions (or sometimes the entire file) is read or compiled to install those changes in the running memory image. Interlisp incorporates a different philosophy. A symbolic file is considered as a database of information about a group of data objects---function definitions, variable values, record declarations, etc. The text in a symbolic file is never edited directly. Definitions are edited only after their textual representations on files have been converted to data-structures that reside inside the Lisp address space. The programs for editing definitions inside Interlisp can therefore make use of the full set of data-manipulation capabilities that the environment already provides, and editing operations can be easily intermixed with the processes of evaluation and compilation. Interlisp is thus a "resident" programming environment, and as such it provides facilities for moving definitions back and forth between memory and the external databases on symbolic files, and for doing the bookkeeping involved when definitions on many symbolic files with compiled counterparts are being manipulated. The file package provides those capabilities. It removes from you the burden of keeping track of where things are and what things have changed. The file package also keeps track of which files have been modified and need to be updated and recompiled. The file package is integrated into many other system packages. For example, if only the compiled version of a file is loaded and you attempt to edit a function, the file package will attempt to load the source of that function from the appropriate symbolic file. In many cases, if a datum is needed by some program, the file package will automatically retrieve it from a file if it is not already in your working environment. Some of the operations of the file package are rather complex. For example, the same function may appear in several different files, or the symbolic or compiled files may be in different directories, etc. Therefore, this chapter does not document how the file package works in each and every situation, but instead makes the deliberately vague statement that it does the "right" thing with respect to keeping track of what has been changed, and what file operations need to be performed in accordance with those changes. For a simple illustration of what the file package does, suppose that the symbolic file FOO contains the functions FOO1 and FOO2, and that the file BAR contains the functions BAR1 and BAR2. These two files could be loaded into the environment with the function LOAD: (LOAD 'FOO) FILE CREATED 4-MAR-83 09:26:55 FOOCOMS {DSK}FOO.;1 (LOAD 'BAR) FILE CREATED 4-MAR-83 09:27:24 BARCOMS {DSK}BAR.;1 Now, suppose that we change the definition of FOO2 with the editor, and we define two new functions, NEW1 and NEW2. At that point, the file package knows that the in-memory definition of FOO2 is no longer consistent with the definition in the file FOO, and that the new functions have been defined but have not yet been associated with a symbolic file and saved on permanent storage. The function FILES? summarizes this state of affairs and enters into an interactive dialog in which we can specify what files the new functions are to belong to. (FILES?) FOO...to be dumped. plus the functions: NEW1,NEW2 want to say where the above go ? Yes (functions) NEW1 File name: BAR NEW2 File name: ZAP new file ? Yes NIL The file package knows that the file FOO has been changed, and needs to be dumped back to permanent storage. This can be done with MAKEFILE. (MAKEFILE 'FOO) {DSK}FOO.;2 Since we added NEW1 to the old file BAR and established a new file ZAP to contain NEW2, both BAR and ZAP now also need to be dumped. This is confirmed by a second call to FILES?: (FILES?) BAR, ZAP...to be dumped. FOO...to be listed. FOO...to be compiled NIL We are also informed that the new version we made of FOO needs to be listed (sent to a printer) and that the functions on the file must be compiled. Rather than doing several MAKEFILEs to dump the files BAR and ZAP, we can simply call CLEANUP. Without any further user interaction, this will dump any files whose definitions have been modified. CLEANUP will also send any unlisted files to the printer and recompile any files which need to be recompiled. CLEANUP is a useful function to use at the end of a debugging session. It will call FILES? if any new objects have been defined, so you do not lose the opportunity to say explicitly where those belong. In effect, the function CLEANUP executes all the operations necessary to make the your permanent files consistent with the definitions in the current core-image. (CLEANUP) FOO...compiling {DSK}FOO.;2 . . . BAR...compiling {DSK}BAR.;2 . . . ZAP...compiling {DSK}ZAP.;1 . . . In addition to the definitions of functions, symbolic files in Interlisp can contain definitions of a variety of other types, e.g. variable values, property lists, record declarations, macro definitions, hash arrays, etc. In order to treat such a diverse assortment of data uniformly from the standpoint of file operations, the file package uses the concept of a typed definition, of which a function definition is just one example. A typed definition associates with a name (usually a litatom), a definition of a given type (called the file package type). Note that the same name may have several definitions of different types. For example, a litatom may have both a function definition and a variable definition. The file package also keeps track of the files that a particular typed definition is stored on, so one can think of a typed definition as a relation between four elements: a name, a definition, a type, and a file. Symbolic files on permanent storage devices are referred to by names that obey the naming conventions of those devices, usually including host, directory, and version fields. When such definition groups are noticed by the file package, they are assigned simple root names and these are used by all file package operations to refer to those groups of definitions. The root name for a group is computed from its full permanent storage name by applying the function ROOTFILENAME; this strips off the host, directory, version, etc., and returns just the simple name field of the file. For each file, the file package also has a data structure that describes what definitions it contains. This is known as the commands of the file, or its "filecoms". By convention, the filecoms of a file whose root name is X is stored as the value of the litatom XCOMS. For example, the value of FOOCOMS is the filecoms for the file FOO. This variable can be directly manipulated, but the file package contains facilities such as FILES? which make constructing and updating filecoms easier, and in some cases automatic. See the Functions for Manipulating File Command Lists section. The file package is able to maintain its databases of information because it is notified by various other routines in the system when events take place that may change that database. A file is "noticed" when it is loaded, or when a new file is stored (though there are ways to explicitly notice files without completely loading all their definitions). Once a file is noticed, the file package takes it into account when modifying filecoms, dumping files, etc. The file package also needs to know what typed definitions have been changed or what new definitions have been introduced, so it can determine which files need to be updated. This is done by "marking changes". All the system functions that perform file package operations (LOAD, TCOMPL, PRETTYDEF, etc.), as well as those functions that define or change data, (EDITF, EDITV, EDITP, DWIM corrections to user functions) interact with the file package. Also, typed-in assignment of variables or property values is noticed by the file package. (Note that modifications to variable or property values during the execution of a function body are not noticed.) In some cases the marking procedure can be subtle, e.g. if you edit a property list using EDITP, only those properties whose values are actually changed (or added) are marked. All file package operations can be disabled with FILEPKGFLG. FILEPKGFLG(FILEPKGFLG (Variable) NIL NIL ("17") 3) [Variable] The file package can be disabled by setting FILEPKGFLG to NIL. This will turn off noticing files and marking changes. FILEPKGFLG is initially T. The rest of this chapter goes into further detail about the file package. Functions for loading and storing symbolic files are presented first, followed by functions for adding and removing typed definitions from files, moving typed definitions from one file to another, determining which file a particular definition is stored in, and so on. Loading Files 1 The functions below load information from symbolic files into the Interlisp environment. A symbolic file contains a sequence of Interlisp expressions that can be evaluated to establish specified typed definitions. The expressions on symbolic files are read using FILERDTBL as the read table. The loading functions all have an argument LDFLG. LDFLG affects the operation of DEFINE, DEFINEQ, RPAQ, RPAQ?, and RPAQQ. While a source file is being loaded, DFNFLG (Chapter 10) is rebound to LDFLG. Thus, if LDFLG=NIL, and a function is redefined, a message is printed and the old definition saved. If LDFLG=T, the old definition is simply overwritten. If LDFLG=PROP, the functions are stored as "saved" definitions on the property lists under the property EXPR instead of being installed as the active definitions. If LDFLG=ALLPROP, not only function definitions but also variables set by RPAQQ, RPAQ, RPAQ? are stored on property lists (except when the variable has the value NOBIND, in which case they are set to the indicated value regardless of DFNFLG). Another option is available for loading systems for others to use and who wish to suppress the saving of information used to aid in development and debugging. If LDFLG=SYSLOAD, LOAD will: 1. Rebind DFNFLG to T, so old definitions are simply overwritten 2. Rebind LISPXHIST to NIL, thereby making the LOAD not be undoable and eliminating the cost of saving undo information (Chapter 13) 3. Rebind ADDSPELLFLG to NIL, to suppress adding to spelling lists 4. Rebind FILEPKGFLG to NIL, to prevent the file from being "noticed" by the file package 5. Rebind BUILDMAPFLG to NIL, to prevent a file map from being constructed 6. After the load has completed, set the filecoms variable and any filevars variables to NOBIND 7. Add the file name to SYSFILES rather than FILELST A filevars variable is any variable appearing in a file package command of the form (FILECOM * VARIABLE) (see the FileVars section). Therefore, if the filecoms includes (FNS * FOOFNS), FOOFNS is set to NOBIND. If you want the value of such a variable to be retained, even when the file is loaded with LDFLG=SYSLOAD, then you should replace the variable with an equivalent, non-atomic expression, such as (FNS * (PROGN FOOFNS)). All functions that have LDFLG as an argument perform spelling correction using LOADOPTIONS as a spelling list when LDFLG is not a member of LOADOPTIONS. LOADOPTIONS is initially (NIL T PROP ALLPROP SYSLOAD). (LOAD(LOAD (Function) NIL NIL ("17") 4) FILE LDFLG PRINTFLG) [Function] Reads successive expressions from FILE (with FILERDTBL as read table) and evaluates each as it is read, until it reads either NIL, or the single atom STOP. Note that LOAD can be used to load both symbolic and compiled files. Returns FILE (full name). If PRINTFLG=T, LOAD prints the value of each expression; otherwise it does not. (LOAD?(LOAD? (Function) NIL NIL ("17") 4) FILE LDFLG PRINTFLG) [Function] Similar to LOAD except that it does not load FILE if it has already been loaded, in which case it returns NIL. LOAD? loads FILE except when the same version of the file has been loaded (either from the same place, or from a copy of it from a different place). Specifically, LOAD? considers that FILE has already been loaded if the full name of FILE is on LOADEDFILELST (see the Noticing Files section) or the date stored on the FILEDATES property of the root file name of FILE is the same as the FILECREATED expression on FILE. (LOADFNS(LOADFNS (Function) NIL NIL ("17") 5) FNS FILE LDFLG VARS) [Function] Permits selective loading of definitions. FNS is a list of function names, a single function name, or T, meaning to load all of the functions on the file. FILE can be either a compiled or symbolic file. If a compiled definition is loaded, so are all compiler-generated subfunctions. The interpretation of LDFLG is the same as for LOAD. If FILE=NIL, LOADFNS will use WHEREIS (see the Storing Files section) to determine where the first function in FNS resides, and load from that file. Note that the file must previously have been "noticed". If WHEREIS returns NIL, and the WHEREIS library package has been loaded, LOADFNS will use the WHEREIS data base to find the file containing FN. VARS specifies which non-DEFINEQ expressions are to be loaded (i.e., evaluated). It is interpreted as follows: T Means to load all non-DEFINEQ expressions. NIL Means to load none of the non-DEFINEQ expressions. VARS Means to evaluate all variable assignment expressions (beginning with RPAQ, RPAQQ, or RPAQ?, see the Functions Used Within Source Files section). Any other litatom Means the same as specifying a list containing that atom. A list If VARS is a list that is not a valid function definition, each element in VARS is "matched" against each non-DEFINEQ expression, and if any elements in VARS "match" successfully, the expression is evaluated. "Matching" is defined as follows: If an element of VARS is an atom, it matches an expression if it is EQ to either the CAR or the CADR of the expression. If an element of VARS is a list, it is treated as an edit pattern (see Chapter 16), and matched with the entire expression (using EDIT4E, described in Chapter 16). For example, if VARS was (FOOCOMS DECLARE: (DEFLIST & (QUOTE MACRO))), this would cause (RPAQQ FOOCOMS ...), all DECLARE:s, and all DEFLISTs which set up MACROs to be read and evaluated. A function definition If VARS is a list and a valid function definition ((FNTYP VARS) is true), then LOADFNS will invoke that function on every non-DEFINEQ expression being considered, applying it to two arguments, the first and second elements in the expression. If the function returns NIL, the expression will be skipped; if it returns a non-NIL litatom (e.g., T), the expression will be evaluated; and if it returns a list, this list is evaluated instead of the expression. The file pointer is set to the very beginning of the expression before calling the VARS function definition, so it may read the entire expression if necessary. If the function returns a litatom, the file pointer is reset and the expression is READ or SKREAD. However, the file pointer is not reset when the function returns a list, so the function must leave it set immediately after the expression that it has presumably read. LOADFNS returns a list of: 1. The names of the functions that were found 2. A list of those functions not found (if any) headed by the litatom NOT-FOUND: 3. All of the expressions that were evaluated 4. A list of those members of VARS for which no corresponding expressions were found (if any), again headed by the litatom NOT-FOUND: For example: (LOADFNS '(FOO FIE FUM) FILE NIL '(BAZ (DEFLIST &))) (FOO FIE (NOT-FOUND: FUM) (RPAQ BAZ ...) (NOT-FOUND: (DEFLIST &))) (LOADVARS(LOADVARS (Function) NIL NIL ("17") 6) VARS FILE LDFLG) [Function] Same as (LOADFNS NIL FILE LDFLG VARS). (LOADFROM(LOADFROM (Function) NIL NIL ("17") 6) FILE FNS LDFLG) [Function] Same as (LOADFNS FNS FILE LDFLG T). Once the file package has noticed a file, you can edit functions contained in the file without explicitly loading them. Similarly, those functions which have not been modified do not have to be loaded in order to write out an updated version of the file. Files are normally noticed (i.e., their contents become known to the file package) when either the symbolic or compiled versions of the file are loaded. If the file is not going to be loaded completely, the preferred way to notice it is with LOADFROM. You can also load some functions at the same time by giving LOADFROM a second argument, but it is normally used simply to inform the file package about the existence and contents of a particular file. (LOADBLOCK(LOADBLOCK (Function) NIL NIL ("17") 6) FN FILE LDFLG) [Function] Calls LOADFNS on those functions contained in the block declaration containing FN (see Chapter 18). LOADBLOCK is designed primarily for use with symbolic files, to load the EXPRs for a given block. It will not load a function which already has an in-core EXPR definition, and it will not load the block name, unless it is also one of the block functions. (LOADCOMP(LOADCOMP (Function) NIL NIL ("17") 6) FILE LDFLG) [Function] Performs all operations on FILE associated with compilation, i.e. evaluates all expressions under a DECLARE: EVAL@COMPILE, and "notices" the function and variable names by adding them to the lists NOFIXFNSLST and NOFIXVARSLST (see Chapter 21). Thus, if building a system composed of many files with compilation information scattered among them, all that is required to compile one file is to LOADCOMP the others. (LOADCOMP?(LOADCOMP? (Function) NIL NIL ("17") 6) FILE LDFLG) [Function] Similar to LOADCOMP, except it does not load if file has already been loaded (with LOADCOMP), in which case its value is NIL. LOADCOMP? will load the file even if it has been loaded with LOAD, LOADFNS, etc. The only time it will not load the file is if the file has already been loaded with LOADCOMP. FILESLOAD provides an easy way for you to load a series of files, setting various options: (FILESLOAD(FILESLOAD (Function) NIL NIL ("17") 6) FILE1 ... FILEN) [NLambda NoSpread Function] Loads the files FILE1 ... FILEN (all arguments unevaluated). If any of these arguments are lists, they specify certain loading options for all following files (unless changed by another list). Within these lists, the following commands are recognized: FROM DIR Search the specified directories for the file. DIR can either be a single directory, or a list of directories to search in order. For example, (FILESLOAD (FROM {ERIS}SOURCES>) ...) will search the directory {ERIS}SOURCES> for the files. If this is not specified, the default is to search the contents of DIRECTORIES (see Chapter 24). If FROM is followed by the key word VALUEOF, the following word is evaluated, and the value is used as the list of directories to search. For example, (FILESLOAD (FROM VALUEOF FOO) ...) will search the directory list that is the value of the variable FOO. As a special case, if DIR is a litatom, and the litatom DIRDIRECTORIES is bound, the value of this variable is used as the directory search list. For example, since the variable LISPUSERSDIRECTORIES (see Chapter 24) is commonly used to contain a list of directories containing "library" packages, (FILESLOAD (FROM LISPUSERS) ...) can be used instead of (FILESLOAD (FROM VALUEOF LISPUSERSDIRECTORIES) ...) If a FILESLOAD is read and evaluated while loading a file, and it doesn't contain a FROM expression, the default is to search the directory containing the FILESLOAD expression before the value of DIRECTORIES. FILESLOAD expressions can be dumped on files using the FILES file package command. SOURCE Load the source version of the file rather than the compiled version. COMPILED Load the compiled version of the file. If COMPILED is specified, the compiled version will be loaded, if it is found. The source will not be loaded. If neither SOURCE or COMPILED is specified, the compiled version of the file will be loaded if it is found, otherwise the source will be loaded if it is found. LOAD Load the file by calling LOAD, if it has not already been loaded. This is the default unless LOADCOMP or LOADFROM is specified. If LOAD is specified, FILESLOAD considers that the file has already been loaded if the root name of the file has a non-NIL FILEDATES property. This is a somewhat different algorithm than LOAD? uses. In particular, FILESLOAD will not load a newer version of a file that has already been loaded. LOADCOMP Load the file with LOADCOMP? rather than LOAD. Automatically implies SOURCE. LOADFROM Load the file with LOADFROM rather than LOAD. NIL, T, PROP ALLPROP SYSLOAD The loading function is called with its LDFLG argument set to the specified token. LDFLG affects the operation of the loading functions by resetting DFNFLG (see Chapter 10) to LDFLG during the loading. If none of these tokens are specified, the value of the variable LDFLG is used if it is bound, otherwise NIL is used. NOERROR If NOERROR is specified, no error occurs when a file is not found. Each list determines how all further files in the lists are loaded, unless changed by another list. The tokens above can be joined together in a single list. For example, (FILESLOAD (LOADCOMP) NET (SYSLOAD FROM VALUEOF NEWDIRECTORIES) CJSYS) will call LOADCOMP? to load the file NET searching the value of DIRECTORIES, and then call LOADCOMP? to load the file CJSYS with LDFLG set to SYSLOAD, searching the directory list that is the value of the variable NEWDIRECTORIES. FILESLOAD expressions can be dumped on files using the FILES file package command. Storing Files 1 (MAKEFILE(MAKEFILE (Function) NIL NIL ("17") 8) FILE OPTIONS REPRINTFNS SOURCEFILE) [Function] Makes a new version of the file FILE, storing the information specified by FILE's filecoms. Notices FILE if not previously noticed. Then, it adds FILE to NOTLISTEDFILES and NOTCOMPILEDFILES. OPTIONS is a litatom or list of litatoms which specify options. By specifying certain options, MAKEFILE can automatically compile or list FILE. Note that if FILE does not contain any function definitions, it is not compiled even when OPTIONS specifies C or RC. The options are spelling corrected using the list MAKEFILEOPTIONS. If spelling correction fails, MAKEFILE generates an error. The options are interpreted as follows: C RC After making FILE, MAKEFILE will compile FILE by calling TCOMPL (if C is specified) or RECOMPILE (if RC is specified). If there are any block declarations specified in the filecoms for FILE, BCOMPL or BRECOMPILE will be called instead. If F, ST, STF, or S is the next item on OPTIONS following C or RC, it is given to the compiler as the answer to the compiler's question LISTING? (see Chapter 18). For example, (MAKEFILE 'FOO '(C F LIST)) will dump FOO, then TCOMPL or BCOMPL it specifying that functions are not to be redefined, and finally list the file. LIST After making FILE, MAKEFILE calls LISTFILES to print a hardcopy listing of FILE. CLISPIFY MAKEFILE calls PRETTYDEF with CLISPIFYPRETTYFLG=T (see Chapter 21). This causes CLISPIFY to be called on each function defined as an EXPR before it is prettyprinted. Alternatively, if FILE has the property FILETYPE with value CLISP or a list containing CLISP, PRETTYDEF is called with CLISPIFYPRETTYFLG reset to CHANGES, which will cause CLISPIFY to be called on all functions marked as having been changed. If FILE has property FILETYPE with value CLISP, the compiler will DWIMIFY its functions before compiling them (see Chapter 18). FAST MAKEFILE calls PRETTYDEF with PRETTYFLG=NIL (see Chapter 26). This causes data objects to be printed rather than prettyprinted, which is much faster. REMAKE MAKEFILE "remakes" FILE: The prettyprinted definitions of functions that have not changed are copied from an earlier version of the symbolic file. Only those functions that have changed are prettyprinted. NEW MAKEFILE does not remake FILE. If MAKEFILEREMAKEFLG=T (the initial setting), the default for all calls to MAKEFILE is to remake. The NEW option can be used to override this default. REPRINTFNS and SOURCEFILE are used when remaking a file. FILE is not added to NOTLISTEDFILES if FILE has on its property list the property FILETYPE with value DON'TLIST, or a list containing DON'TLIST. FILE is not added to NOTCOMPILEDFILES if FILE has on its property list the property FILETYPE with value DON'TCOMPILE, or a list containing DON'TCOMPILE. Also, if FILE does not contain any function definitions, it is not added to NOTCOMPILEDFILES, and it is not compiled even when OPTIONS specifies C or RC. If a remake is not being performed, MAKEFILE checks the state of FILE to make sure that the entire source file was actually LOADed. If FILE was loaded as a compiled file, MAKEFILE prints the message CAN'T DUMP: ONLY THE COMPILED FILE HAS BEEN LOADED. Similarly, if only some of the symbolic definitions were loaded via LOADFNS or LOADFROM, MAKEFILE prints CAN'T DUMP: ONLY SOME OF ITS SYMBOLICS HAVE BEEN LOADED. In both cases, MAKEFILE will then ask you if it should dump anyway; if you decline, MAKEFILE does not call PRETTYDEF, but simply returns (FILE NOT DUMPED) as its value. You can indicate that FILE must be block compiled together with other files as a unit by putting a list of those files on the property list of each file under the property FILEGROUP. If FILE has a FILEGROUP property, the compiler will not be called until all files on this property have been dumped that need to be. MAKEFILE operates by rebinding PRETTYFLG, PRETTYTRANFLG, and CLISPIFYPRETTYFLG, evaluating each expression on MAKEFILEFORMS (under errorset protection), and then calling PRETTYDEF. PRETTYDEF calls PRETTYPRINT with its second argument PRETTYDEFLG=T, so whenever PRETTYPRINT (and hence MAKEFILE) start printing a new function, the name of that function is printed if more than 30 seconds (real time) have elapsed since the last time it printed the name of a function. (MAKEFILES(MAKEFILES (Function) NIL NIL ("17") 9) OPTIONS FILES) [Function] Performs (MAKEFILE FILE OPTIONS) for each file on FILES that needs to be dumped. If FILES=NIL, FILELST is used. For example, (MAKEFILES 'LIST) will make and list all files that have been changed. In this case, if any typed definitions for any items have been defined or changed and they are not contained in one of the files on FILELST, MAKEFILES calls ADDTOFILES? to allow you to specify where these go. MAKEFILES returns a list of all files that are made. (CLEANUP(CLEANUP (Function) NIL NIL ("17") 10) FILE1 FILE2 ... FILEN) [NLambda NoSpread Function] Dumps, lists, and recompiles (with RECOMPILE or BRECOMPILE) any of the specified files (unevaluated) requiring the corresponding operation. If no files are specified, FILELST is used. CLEANUP returns NIL. CLEANUP uses the value of the variable CLEANUPOPTIONS as the OPTIONS argument to MAKEFILE. CLEANUPOPTIONS is initially (RC), to indicate that the files should be recompiled. If CLEANUPOPTIONS is set to (RC F), no listing will be performed, and no functions will be redefined as the result of compiling. Alternatively, if FILE1 is a list, it will be interpreted as the list of options regardless of the value of CLEANUPOPTIONS. (FILES?(FILES? (Function) NIL NIL ("17") 10)) [Function] Prints on the terminal the names of those files that have been modified but not dumped, dumped but not listed, dumped but not compiled, plus the names of any functions and other typed definitions (if any) that are not contained in any file. If there are any, FILES? then calls ADDTOFILES? to allow you to specify where these go. (ADDTOFILES?(ADDTOFILES? (Function) NIL NIL ("17") 10) %) [Function] Called from MAKEFILES, CLEANUP, and FILES? when there are typed definitions that have been marked as changed which do not belong to any file. ADDTOFILES? lists the names of the changed items, and asks if you want to specify where these items should be put. If you answer N(o), ADDTOFILES? returns NIL without taking any action. If you answer ], this is taken to be an answer to each question that would be asked, and all the changed items are marked as dummy items to be ignored. Otherwise, ADDTOFILES? prints the name of each changed item, and accepts one of the following responses: A file name A filevar If you give a file name or a variable whose value is a list (a filevar), the item is added to the corresponding file or list, using ADDTOFILE. If your response is not the name of a file on FILELST or a variable whose value is a list, you will be asked whether it is a new file. If you say no, then ADDTOFILES? will check whether the item is the name of a list, i.e., whether its value is a list. If not, youwill be asked whether it is a new list. line-feed Same as your previous response. space carriage return Take no action. ] The item is marked as a dummy item by adding it to NILCOMS. This tells the file package simply to ignore this item. [ The "definition" of the item in question is prettyprinted to the terminal, and then you are asked again about its disposition. ( ADDTOFILES? prompts with "LISTNAME: (", you type in the name of a list, i.e. a variable whose value is a list, terminated by a ). The item will then only be added to (under) a command in which the named list appears as a filevar. If none are found, a message is printed, and you are asked again. For example, you define a new function FOO3. When asked where it goes, you type (FOOFNS). If the command (FNS * FOOFNS) is found, FOO3 will be added to the value of FOOFNS. If instead you type (FOOCOMS), and the command (COMS * FOOCOMS) is found, then FOO3 will be added to a command for dumping functions that is contained in FOOCOMS. If the named list is not also the name of a file, you can simply type it in without parenthesis as described above. @ ADDTOFILES? prompts with "Near: (", you type in the name of an object, and the item is then inserted in a command for dumping objects (of its type) that contains the indicated name. The item is inserted immediately after the indicated name. (LISTFILES FILE1 FILE2 ... FILEN) [NLambda NoSpread Function] Lists each of the specified files (unevaluated). If no files are given, NOTLISTEDFILES is used. Each file listed is removed from NOTLISTEDFILES if the listing is completed. For each file not found, LISTFILES prints the message FILENAME NOT FOUND and proceeds to the next file. LISTFILES calls the function LISTFILES1 on each file to be listed. Normally, LISTFILES1 is defined to simply call SEND.FILE.TO.PRINTER (see Chapter 29), but you can advise or redefine LISTFILES1 for more specialized applications. Any lists inside the argument list to LISTFILES are interpreted as property lists that set the various printing options, such as the printer, number of copies, banner page name, etc (see see Chapter 29). Later properties override earlier ones. For example, (LISTFILES FOO (HOST JEDI) FUM (#COPIES 3) FIE) will cause one copy of FOO to be printed on the default printer, and one copy of FUM and three copies of FIE to be printed on the printer JEDI. (COMPILEFILES(COMPILEFILES (Function) NIL NIL ("17") 11) FILE1 FILE2 ... FILEN) [NLambda NoSpread Function] Executes the RC and C options of MAKEFILE for each of the specified files (unevaluated). If no files are given, NOTCOMPILEDFILES is used. Each file compiled is removed from NOTCOMPILEDFILES. If FILE1 is a list, it is interpreted as the OPTIONS argument to MAKEFILES. This feature can be used to supply an answer to the compiler's LISTING? question, e.g., (COMPILEFILES (STF)) will compile each file on NOTCOMPILEDFILES so that the functions are redefined without the EXPRs definitions being saved. (WHEREIS(WHEREIS (Function) NIL NIL ("17") 11) NAME TYPE FILES FN) [Function] TYPE is a file package type. WHEREIS sweeps through all the files on the list FILES and returns a list of all files containing NAME as a TYPE. WHEREIS knows about and expands all file package commands and file package macros. TYPE=NIL defaults to FNS (to retrieve function definitions). If FILES is not a list, the value of FILELST is used. If FN is given, it should be a function (with arguments NAME, FILE, and TYPE) which is applied for every file in FILES that contains NAME as a TYPE. In this case, WHEREIS returns NIL. If the WHEREIS library package has been loaded, WHEREIS is redefined so that FILES=T means to use the whereis package data base, so WHEREIS will find NAME even if the file has not been loaded or noticed. FILES=NIL always means use FILELST. Remaking a Symbolic File 1 Most of the time that a symbolic file is written using MAKEFILE, only a few of the functions that it contains have been changed since the last time the file was written. Rather than prettprinting all of the functions, it is often considerably faster to "remake" the file, copying the prettprinted definitions of unchanged functions from an earlier version of the symbolic file, and only prettyprinting those functions that have been changed. MAKEFILE will remake the symbolic file if the REMAKE option is specified. If the NEW option is given, the file is not remade, and all of the functions are prettprinted. The default action is specified by the value of MAKEFILEREMAKEFLG: if T (its initial value), MAKEFILE will remake files unless the NEW option is given; if NIL, MAKEFILE will not remake unless the REMAKE option is given. Note: If the file has never been loaded or dumped, for example if the filecoms were simply set up in memory, then MAKEFILE will never attempt to remake the file, regardless of the setting of MAKEFILEREMAKEFLG, or whether the REMAKE option was specified. When MAKEFILE is remaking a symbolic file, you can explicitly indicate the functions which are to be prettyprinted and the file to be used for copying the rest of the function definitions from via the REPRINTFNS and SOURCEFILE arguments to MAKEFILE. Normally, both of these arguments are defaulted to NIL. In this case, REPRINTFNS will be set to those functions that have been changed since the last version of the file was written. For SOURCEFILE, MAKEFILE obtains the full name of the most recent version of the file (that it knows about) from the FILEDATES property of the file, and checks to make sure that the file still exists and has the same file date as that stored on the FILEDATES property. If it does, MAKEFILE uses that file as SOURCEFILE. This procedure permits you to LOAD or LOADFROM a file in a different directory, and still be able to remake the file with MAKEFILE. In the case where the most recent version of the file cannot be found, MAKEFILE will attempt to remake using the original version of the file (i.e., the one first loaded), specifying as REPRINTFNS the union of all changes that have been made since the file was first loaded, which is obtained from the FILECHANGES property of the file. If both of these fail, MAKEFILE prints the message "CAN'T FIND EITHER THE PREVIOUS VERSION OR THE ORIGINAL VERSION OF FILE, SO IT WILL HAVE TO BE WRITTEN ANEW", and does not remake the file, i.e. will prettyprint all of the functions. When a remake is specified, MAKEFILE also checks to see how the file was originally loaded. If the file was originally loaded as a compiled file, MAKEFILE will call LOADVARS to obtain those DECLARE: expressions that are contained on the symbolic file, but not the compiled file, and hence have not been loaded. If the file was loaded by LOADFNS (but not LOADFROM), then LOADVARS is called to obtain any non-DEFINEQ expressions. Before calling LOADVARS to re-load definitions, MAKEFILE asks you, e.g. "Only the compiled version of FOO was loaded, do you want to LOADVARS the (DECLARE: .. DONTCOPY ..) expressions from {DSK}FOO.;3?". You can respond Yes to execute the LOADVARS and continue the MAKEFILE, No to proceed with the MAKEFILE without performing the LOADVARS, or Abort to abort the MAKEFILE. You may wish to skip the LOADVARS if you had circumvented the file package in some way, and loading the old definitions would overwrite new ones. Remaking a symbolic file is considerably faster if the earlier version has a file map indicating where the function definitions are located (see the File Maps section), but it does not depend on this information. Loading Files in a Distributed Environment 1 Each Interlisp source and compiled code file contains the full filename of the file, including the host and directory names, in a FILECREATED expression at the beginning of the file. The compiled code file also contains the full file name of the source file it was created from. In earlier versions of Interlisp, the file package used this information to locate the appropriate source file when "remaking" or recompiling a file. This turned out to be a bad feature in distributed environments, where users frequently move files from one place to another, or where files are stored on removable media. For example, suppose you MAKEFILE to a floppy, and then copy the file to a file server. If you loaded and edited the file from a file server, and tried to do MAKEFILE, it would try to locate the source file on the floppy, which is probably no longer loaded. Currently, the file package searches for sources file on the connected directory, and on the directory search path (on the variable DIRECTORIES). If it is not found, the host/directory information from the FILECREATED expression be used. Warning: One situation where the new algorithm does the wrong thing is if you explicitly LOADFROM a file that is not on your directory search path. Future MAKEFILEs and CLEANUPs will search the connected directory and DIRECTORIES to find the source file, rather than using the file that the LOADFROM was done from. Even if the correct file is on the directory search path, you could still create a bad file if there is another version of the file in an earlier directory on the search path. In general, you should either explicitly specify the SOURCEFILE argument to MAKEFILE to tell it where to get the old source, or connect to the directory where the correct source file is. Marking Changes 1 The file package needs to know what typed definitions have been changed, so it can determine which files need to be updated. This is done by "marking changes". All the system functions that perform file package operations (LOAD, TCOMPL, PRETTYDEF, etc.), as well as those functions that define or change data, (EDITF, EDITV, EDITP, DWIM corrections to user functions) interact with the file package by marking changes. Also, typed-in assignment of variables or property values is noticed by the file package. (If a program modifies a variable or property value, this is not noticed.) In some cases the marking procedure can be subtle, e.g. if you edit a property list using EDITP, only those properties whose values are actually changed (or added) are marked. The various system functions which create or modify objects call MARKASCHANGED to mark the object as changed. For example, when a function is defined via DEFINE or DEFINEQ, or modified via EDITF, or a DWIM correction, the function is marked as being a changed object of type FNS. Similarly, whenever a new record is declared, or an existing record redeclared or edited, it is marked as being a changed object of type RECORDS, and so on for all of the other file package types. You can also call MARKASCHANGED directly to mark objects of a particular file package type as changed: (MARKASCHANGED(MARKASCHANGED (Function) NIL NIL ("17") 14) NAME TYPE REASON) [Function] Marks NAME of type TYPE as being changed. MARKASCHANGED returns NAME. MARKASCHANGED is undoable. REASON is a litatom that indicated how NAME was changed. MARKASCHANGED recognizes the following values for REASON: DEFINED Used to indicate the creation of NAME, e.g. from DEFINEQ (Chapter 10). CHANGED Used to indicate a change to NAME, e.g. from the editor. DELETED Used to indicate the deletion of NAME, e.g. by DELDEF. CLISP Used to indicate the modification of NAME by CLISP translation. For backwards compatibility, MARKASCHANGED also accepts a REASON of T (=DEFINED) and NIL (=CHANGED). New programs should avoid using these values. The variable MARKASCHANGEDFNS is a list of functions that MARKASCHANGED calls (with arguments NAME, TYPE, and REASON). Functions can be added to this list to "advise" MARKASCHANGED to do additional work for all types of objects. The WHENCHANGED file package type property (see the Defining New File Package Types section) can be used to specify additional actions when MARKASCHANGED gets called on specific types of objects. (UNMARKASCHANGED(UNMARKASCHANGED (Function) NIL NIL ("17") 14) NAME TYPE) [Function] Unmarks NAME of type TYPE as being changed. Returns NAME if NAME was marked as changed and is now unmarked, NIL otherwise. UNMARKASCHANGED is undoable. (FILEPKGCHANGES(FILEPKGCHANGES (Function) NIL NIL ("17") 14) TYPE LST) [NoSpread Function] If LST is not specified (as opposed to being NIL), returns a list of those objects of type TYPE that have been marked as changed but not yet associated with their corresponding files (see the File Package Types section). If LST is specified, FILEPKGCHANGES sets the corresponding list. (FILEPKGCHANGES) returns a list of all objects marked as changed as a list of elements of the form (TYPENAME . CHANGEDOBJECTS). Some properties (e.g. EXPR, ADVICE, MACRO, I.S.OPR, etc.) are used to implement other file package types. For example, if you change the value of the property I.S.OPR, you are really changing an object of type I.S.OPR. The effect is the same as though you had redefined the i.s.opr via a direct call to the function I.S.OPR. If a property whose value has been changed or added does not correspond to a specific file package type, then it is marked as a changed object of type PROPS whose name is (VARIABLENAME PROPNAME) (except if the property name has a property PROPTYPE with value IGNORE). Similarly, if you change a variable which implements the file package type ALISTS (as indicated by the appearance of the property VARTYPE with value ALIST on the variable's property list), only those entries that are actually changed are marked as being changed objects of type ALISTS. The "name" of the object will be (VARIABLENAME KEY) where KEY is CAR of the entry on the alist that is being marked. If the variable corresponds to a specific file package type other than ALISTS, e.g., USERMACROS, LISPXMACROS, etc., then an object of that type is marked. In this case, the name of the changed object will be CAR of the corresponding entry on the alist. For example, if you edit LISPXMACROS and change a definition for PL, then the object PL of type LISPXMACROS is marked as being changed. Noticing Files 1 Already existing files are "noticed" by LOAD or LOADFROM (or by LOADFNS or LOADVARS when the VARS argument is T. New files are noticed when they are constructed by MAKEFILE, or when definitions are first associated with them via FILES? or ADDTOFILES?. Noticing a file updates certain lists and properties so that the file package functions know to include the file in their operations. For example, CLEANUP will only dump files that have been noticed. You can explicitly tell the file package to notice a newly-created file by defining the filecoms for the file, and calling ADDFILE: (ADDFILE(ADDFILE (Function) NIL NIL ("17") 15) FILE % % %) [Function] Tells the file package that FILE should be recognized as a file; it adds FILE to FILELST, and also sets up the FILE property of FILE to reflect the current set of changes which are "registered against" FILE. The file package uses information stored on the property list of the root name of noticed files. The following property names are used: FILE (FILE% (Property) NIL NIL ("17") 15) [Property Name] When a file is noticed, the property FILE, value ((FILECOMS . LOADTYPE)) is added to the property list of its root name. FILECOMS is the variable containing the filecoms of the file. LOADTYPE indicates how the file was loaded, e.g., completely loaded, only partially loaded as with LOADFNS, loaded as a compiled file, etc. The property FILE is used to determine whether or not the corresponding file has been modified since the last time it was loaded or dumped. CDR of the FILE property records by type those items that have been changed since the last MAKEFILE. Whenever a file is dumped, these items are moved to the property FILECHANGES, and CDR of the FILE property is reset to NIL. FILECHANGES(FILECHANGES (Property) NIL NIL ("17") 15) [Property Name] The property FILECHANGES contains a list of all changed items since the file was loaded (there may have been several sequences of editing and rewriting the file). When a file is dumped, the changes in CDR of the FILE property are added to the FILECHANGES property. FILEDATES(FILEDATES (Property) NIL NIL ("17") 15) [Property Name] The property FILEDATES contains a list of version numbers and corresponding file dates for this file. These version numbers and dates are used for various integrity checks in connection with remaking a file. FILEMAP(FILEMAP (Property) NIL NIL ("17") 15) [Property Name] The property FILEMAP is used to store the filemap for the file. This is used to directly load individual functions from the middle of a file. To compute the root name, ROOTFILENAME is applied to the name of the file as indicated in the FILECREATED expression appearing at the front of the file, since this name corresponds to the name the file was originally made under. The file package detects that the file being noticed is a compiled file (regardless of its name), by the appearance of more than one FILECREATED expressions. In this case, each of the files mentioned in the following FILECREATED expressions are noticed. For example, if you perform (BCOMPL '(FOO FIE)), and subsequently loads FOO.DCOM, both FOO and FIE will be noticed. When a file is noticed, its root name is added to the list FILELST: FILELST(FILELST (Variable) NIL NIL ("17") 16) [Variable] Contains a list of the root names of the files that have been noticed. LOADEDFILELST(LOADEDFILELST (Variable) NIL NIL ("17") 16) [Variable] Contains a list of the actual names of the files as loaded by LOAD, LOADFNS, etc. For example, if you perform (LOAD 'EDITA.COM;3), EDITA will be added to FILELST, but EDITA.COM;3 is added to LOADEDFILELST. LOADEDFILELST is not used by the file package; it is maintained solely for your benefit. Distributing Change Information 1 Periodically, the function UPDATEFILES is called to find which file(s) contain the elements that have been changed. UPDATEFILES is called by FILES?, CLEANUP, and MAKEFILES, i.e., any procedure that requires the FILE property to be up to date. This procedure is followed rather than updating the FILE property after each change because scanning FILELST and examining each file package command can be a time-consuming process; this is not so noticeable when performed in conjunction with a large operation like loading or writing a file. UPDATEFILES operates by scanning FILELST and interrogating the file package commands for each file. When (if) any files are found that contain the corresponding typed definition, the name of the element is added to the value of the property FILE for the corresponding file. Thus, after UPDATEFILES has completed operating, the files that need to be dumped are simply those files on FILELST for which CDR of their FILE property is non-NIL. For example, if you load the file FOO containing definitions for FOO1, FOO2, and FOO3, edit FOO2, and then call UPDATEFILES, (GETPROP 'FOO 'FILE) will be ((FOOCOMS . T) (FNS FOO2)). If any objects marked as changed have not been transferred to the FILE property for some file, e.g., you define a new function but forget (or declines) to add it to the file package commands for the corresponding file, then both FILES? and CLEANUP will print warning messages, and then call ADDTOFILES? to permit you to specify on which files these items belong. You can also invoke UPDATEFILES directly: (UPDATEFILES(UPDATEFILES (Function) NIL NIL ("17") 16) % %) [Function] (UPDATEFILES) will update the FILE properties of the noticed files. File Package Types 1 In addition to the definitions of functions and values of variables, source files in Interlisp can contain a variety of other information, e.g. property lists, record declarations, macro definitions, hash arrays, etc. In order to treat such a diverse assortment of data uniformly from the standpoint of file operations, the file package uses the concept of a typed definition, of which a function definition is just one example. A typed definition associates with a name (usually a litatom), a definition of a given type (called the file package type). Note that the same name may have several definitions of different types. For example, a litatom may have both a function definition and a variable definition. The file package also keeps track of the file that a particular typed definition is stored on, so one can think of a typed definition as a relation between four elements: a name, a definition, a type, and a file. A file package type is an abstract notion of a class of objects which share the property that every object of the same file package type is stored, retrieved, edited, copied etc., by the file package in the same way. Each file package type is identified by a litatom, which can be given as an argument to the functions that manipulate typed definitions. You may define new file package types, as described in the Defining New Package Types section. FILEPKGTYPES(FILEPKGTYPES (Variable) NIL NIL ("17") 17) [Variable] The value of FILEPKGTYPES is a list of all file package types, including any that you may have defined. The file package is initialized with the following built-in file package types: ADVICE(ADVICE (File Package Type) NIL NIL ("17") 17) [File Package Type] Used to access "advice" modifying a function (see Chapter 15). ALISTS(ALISTS (File Package Type) NIL NIL ("17") 17) [File Package Type] Used to access objects stored on an association list that is the value of a litatom (see Chapter 3). A variable is declared to have an association list as its value by putting on its property list the property VARTYPE with value ALIST. In this case, each dotted pair on the list is an object of type ALISTS. When the value of such a variable is changed, only those entries in the association list that are actually changed or added are marked as changed objects of type ALISTS (with "name" (LITATOM KEY)). Objects of type ALISTS are dumped via the ALISTS or ADDVARS file package commands. Note that some association lists are used to "implement" other file package types. For example, the value of the global variable USERMACROS implements the file package type USERMACROS and the values of LISPXMACROS and LISPXHISTORYMACROS implement the file package type LISPXMACROS. This is indicated by putting on the property list of the variable the property VARTYPE with value a list of the form (ALIST FILEPKGTYPE). For example, (GETPROP 'LISPXHISTORYMACROS 'VARTYPE) => (ALIST LISPXMACROS). COURIERPROGRAMS(COURIERPROGRAMS (File Package Type) NIL NIL ("17") 17) [File Package Type] Used to access Courier programs (see Chapter 31). EXPRESSIONS(EXPRESSIONS (File Package Type) NIL NIL ("17") 17) [File Package Type] Used to access lisp expressions that are put on a file by using the REMEMBER programmers assistant command (Chapter 13), or by explicitly putting the P file package command on the filecoms. FIELDS(FIELDS (File Package Type) NIL NIL ("17") 17) [File Package Type] Used to access fields of records. The "definition" of an object of type FIELDS is a list of all the record declarations which contain the name. See Chapter 8. FILEPKGCOMS (FILEPKGCOMS% (File Package Type) NIL NIL ("17") 17) [File Package Type] Used to access file package commands and types. A single name can be defined both as a file package type and a file package command. The "definition" of an object of type FILEPKGCOMS is a list structure of the form ((COM . COMPROPS) (TYPE . TYPEPROPS)), where COMPROPS is a property list specifying how the name is defined as a file package command by FILEPKGCOM (see the Defining New File Package Commands section), and TYPEPROPS is a property list specifying how the name is defined as a file package type by FILEPKGTYPE (see the Defining New File Package Types section). FILES(FILES (File Package Type) NIL NIL ("17") 18) [File Package Type] Used to access files. This file package type is most useful for renaming files. The "definition" of a file is not a useful structure. FILEVARS(FILEVARS (File Package Type) NIL NIL ("17") 18) [File Package Type] Used to access Filevars (see the FileVars section). FNS(FNS (File Package Type) NIL NIL ("17") 18) [File Package Type] Used to access function definitions. I.S.OPRS(I.S.OPRS (File Package Type) NIL NIL ("17") 18) [File Package Type] Used to access the definitions of iterative statement operators (see Chapter 9). LISPXMACROS(LISPXMACROS (File Package Type) NIL NIL ("17") 18) [File Package Type] Used to access programmer's assistant commands defined on the variables LISPXMACROS and LISPXHISTORYMACROS (see Chapter 13). MACROS(MACROS (File Package Type) NIL NIL ("17") 18) [File Package Type] Used to access macro definitions (see Chapter 10). PROPS(PROPS (File Package Type) NIL NIL ("17") 18) [File Package Type] Used to access objects stored on the property list of a litatom (see Chapter 2). When a property is changed or added, an object of type PROPS, with "name" (LITATOM PROPNAME) is marked as being changed. Note that some litatom properties are used to implement other file package types. For example, the property MACRO implements the file package type MACROS, the property ADVICE implements ADVICE, etc. This is indicated by putting the property PROPTYPE, with value of the file package type on the property list of the property name. For example, (GETPROP 'MACRO 'PROPTYPE) => MACROS. When such a property is changed or added, an object of the corresponding file package type is marked. If (GETPROP PROPNAME 'PROPTYPE) => IGNORE, the change is ignored. The FILE, FILEMAP, FILEDATES, etc. properties are all handled this way. (IGNORE cannot be the name of a file package type implemented as a property). RECORDS(RECORDS (File Package Type) NIL NIL ("17") 18) [File Package Type] Used to access record declarations (see Chapter 8). RESOURCES(RESOURCES (File Package Type) NIL NIL ("17") 18) [File Package Type] Used to access resources (see Chapter 12). TEMPLATES(TEMPLATES (File Package Type) NIL NIL ("17") 18) [File Package Type] Used to access Masterscope templates (see Chapter 19). USERMACROS(USERMACROS (File Package Type) NIL NIL ("17") 18) [File Package Type] Used to access user edit macros (see Chapter 16). VARS(VARS (File Package Type) NIL NIL ("17") 18) [File Package Type] Used to access top-level variable values. Functions for Manipulating Typed Definitions The functions described below can be used to manipulate typed definitions, without needing to know how the manipulations are done. For example, (GETDEF 'FOO 'FNS) will return the function definition of FOO, (GETDEF 'FOO 'VARS) will return the variable value of FOO, etc. All of the functions use the following conventions: 1. All functions which make destructive changes are undoable. 2. Any argument that expects a list of litatoms will also accept a single litatom, operating as though it were enclosed in a list. For example, if the argument FILES should be a list of files, it may also be a single file. 3. TYPE is a file package type. TYPE=NIL is equivalent to TYPE=FNS. The singular form of a file package type is also recognized, e.g. TYPE=VAR is equivalent to TYPE=VARS. 4. FILES=NIL is equivalent to FILES=FILELST. 5. SOURCE is used to indicate the source of a definition, that is, where the definition should be found. SOURCE can be one of: CURRENT Get the definition currently in effect. SAVED Get the "saved" definition, as stored by SAVEDEF. FILE Get the definition contained on the (first) file determined by WHEREIS. WHEREIS is called with FILES=T, so that if the WHEREIS library package is loaded, the WHEREIS data base will be used to find the file containing the definition. ? Get the definition currently in effect if there is one, else the saved definition if there is one, otherwise the definition from a file determined by WHEREIS. Like specifying CURRENT, SAVED, and FILE in order, and taking the first definition that is found. a file name a list of file names Get the definition from the first of the indicated files that contains one. NIL In most cases, giving SOURCE=NIL (or not specifying it at all) is the same as giving ?, to get either the current, saved, or filed definition. However, with HASDEF, SOURCE=NIL is interpreted as equal to SOURCE=CURRENT, which only tests if there is a current definition. The operation of most of the functions described below can be changed or extended by modifying the appropriate properties for the corresponding file package type using the function FILEPKGTYPE, described in the Defining New File Package Types section. (GETDEF(GETDEF (Function) NIL NIL ("17") 19) NAME TYPE SOURCE OPTIONS) [Function] Returns the definition of NAME, of type TYPE, from SOURCE. For most types, GETDEF returns the expression which would be pretty printed when dumping NAME as TYPE. For example, for TYPE=FNS, an EXPR definition is returned, for TYPE=VARS, the value of NAME is returned, etc. OPTIONS is a list which specifies certain options: NOERROR GETDEF causes an error if an appropriate definition cannot be found, unless OPTIONS is or contains NOERROR. In this case, GETDEF returns the value of the NULLDEF file package type property (see the Defining New File Package Types section), usually NIL. a string If OPTIONS is or contains a string, that string will be returned if no definition is found (and NOERROR is not among the options). The caller can thus determine whether a definition was found, even for types for which NIL or NOBIND are acceptable definitions. NOCOPY GETDEF returns a copy of the definition unless OPTIONS is or contains NOCOPY. EDIT If OPTIONS is or contains EDIT, GETDEF returns a copy of the definition unless it is possible to edit the definition "in place." With some file package types, such as functions, it is meaningful (and efficient) to edit the definition by destructively modifying the list structure, without calling PUTDEF. However, some file package types (like records) need to be "installed" with PUTDEF after they are edited. The default EDITDEF (see the Defining New File Package Types section) calls GETDEF with OPTIONS of (EDIT NOCOPY), so it doesn't use a copy unless it has to, and only calls PUTDEF if the result of editing is not EQUAL to the old definition. NODWIM A FNS definition will be dwimified if it is likely to contain CLISP unless OPTIONS is or contains NODWIM. (PUTDEF(PUTDEF (Function) NIL NIL ("17") 20) NAME TYPE DEFINITION REASON) [Function] Defines NAME of type TYPE with DEFINITION. For TYPE=FNS, does a DEFINE; for TYPE=VARS, does a SAVESET, etc. For TYPE=FILES, PUTDEF establishes the command list, notices NAME, and then calls MAKEFILE to actually dump the file NAME, copying functions if necessary from the "old" file (supplied as part of DEFINITION). PUTDEF calls MARKASCHANGED (see the Mrking Changes section) to mark NAME as changed, giving a reason of REASON. If REASON is NIL, the default is DEFINED. If TYPE=FNS, PUTDEF prints a warning if you try to redefine a function on the list UNSAFE.TO.MODIFY.FNS (see Chapter 10). (HASDEF(HASDEF (Function) NIL NIL ("17") 20) NAME TYPE SOURCE SPELLFLG) [Function] Returns (OR NAME T) if NAME is the name of something of type TYPE. If not, attempts spelling correction if SPELLFLG=T, and returns the spelling-corrected NAME. Otherwise returns NIL. (HASDEF NIL TYPE) returns T if NIL has a valid definition. If SOURCE=NIL, HASDEF interprets this as equal to SOURCE=CURRENT, which only tests if there is a current definition. (TYPESOF(TYPESOF (Function) NIL NIL ("17") 20) NAME POSSIBLETYPES IMPOSSIBLETYPES SOURCE) [Function] Returns a list of the types in POSSIBLETYPES but not in IMPOSSIBLETYPES for which NAME has a definition. FILEPKGTYPES is used if POSSIBLETYPES is NIL. (COPYDEF(COPYDEF (Function) NIL NIL ("17") 21) OLD NEW TYPE SOURCE OPTIONS) [Function] Defines NEW to have a copy of the definition of OLD by doing PUTDEF on a copy of the definition retrieved by (GETDEF OLD TYPE SOURCE OPTIONS). NEW is substituted for OLD in the copied definition, in a manner that may depend on the TYPE. For example, (COPYDEF 'PDQ 'RST 'FILES) sets up RSTCOMS to be a copy of PDQCOMS, changes things like (VARS * PDQVARS) to be (VARS * RSTVARS) in RSTCOMS, and performs a MAKEFILE on RST such that the appropriate definitions get copied from PDQ. COPYDEF disables the NOCOPY option of GETDEF, so NEW will always have a copy of the definition of OLD. COPYDEF substitutes NEW for OLD throughout the definition of OLD. This is usually the right thing to do, but in some cases, e.g., where the old name appears within a quoted expression but was not used in the same context, you must re-edit the definition. (DELDEF(DELDEF (Function) NIL NIL ("17") 21) NAME TYPE) [Function] Removes the definition of NAME as a TYPE that is currently in effect. (SHOWDEF(SHOWDEF (Function) NIL NIL ("17") 21) NAME TYPE FILE) [Function] Prettyprints the definition of NAME as a TYPE to FILE. This shows you how NAME would be written to a file. Used by ADDTOFILES? (see the Storing Files section). (EDITDEF(EDITDEF (Function) NIL NIL ("17") 21) NAME TYPE SOURCE EDITCOMS) [Function] Edits the definition of NAME as a TYPE. Essentially performs (PUTDEF NAME TYPE (EDITE (GETDEF NAME TYPE SOURCE) EDITCOMS)) (SAVEDEF(SAVEDEF (Function) NIL NIL ("17") 21) NAME TYPE DEFINITION) [Function] Sets the "saved" definition of NAME as a TYPE to DEFINITION. If DEFINITION=NIL, the current definition of NAME is saved. If TYPE=FNS (or NIL), the function definition is saved on NAME's property list under the property EXPR, or CODE (depending on the FNTYP of the function definition). If (GETD NAME) is non-NIL, but (FNTYP FN)=NIL, SAVEDEF saves the definition on the property name LIST. This can happen if a function was somehow defined with an illegal expr definition, such as (LAMMMMDA (X) ...). If TYPE=VARS, the definition is stored as the value of the VALUE property of NAME. For other types, the definition is stored in an internal data structure, from where it can be retrieved by GETDEF or UNSAVEDEF. (UNSAVEDEF(UNSAVEDEF (Function) NIL NIL ("17") 21) NAME TYPE %) [Function] Restores the "saved" definition of NAME as a TYPE, making it be the current definition. Returns PROP. If TYPE=FNS (or NIL), UNSAVEDEF unsaves the function definition from the EXPR property if any, else CODE, and returns the property name used. UNSAVEDEF also recognizes TYPE=EXPR, CODE, or LIST, meaning to unsave the definition only from the corresponding property only. If DFNFLG is not T (see Chapter 10), the current definition of NAME, if any, is saved using SAVEDEF. Thus one can use UNSAVEDEF to switch back and forth between two definitions. (LOADDEF(LOADDEF (Function) NIL NIL ("17") 22) NAME TYPE SOURCE) [Function] Equivalent to (PUTDEF NAME TYPE (GETDEF NAME TYPE SOURCE)). LOADDEF is essentially a generalization of LOADFNS, e.g. it enables loading a single record declaration from a file. (LOADDEF FN) will give FN an EXPR definition, either obtained from its property list or a file, unless it already has one. (CHANGECALLERS(CHANGECALLERS (Function) NIL NIL ("17") 22) OLD NEW TYPES FILES METHOD) [Function] Finds all of the places where OLD is used as any of the types in TYPES and changes those places to use NEW. For example, (CHANGECALLERS 'NLSETQ 'ERSETQ) will change all calls to NLSETQ to be calls to ERSETQ. Also changes occurrences of OLD to NEW inside the filecoms of any file, inside record declarations, properties, etc. CHANGECALLERS attempts to determine if OLD might be used as more than one type; for example, if it is both a function and a record field. If so, rather than performing the transformation OLD -> NEW automatically, you are allowed to edit all of the places where OLD occurs. For each occurrence of OLD, you are asked whether you want to make the replacement. If you respond with anything except Yes or No, the editor is invoked on the expression containing that occurrence. There are two different methods for determining which functions are to be examined. If METHOD=EDITCALLERS, EDITCALLERS is used to search FILES (see Chapter 16). If METHOD=MASTERSCOPE, then the Masterscope database is used instead. METHOD=NIL defaults to MASTERSCOPE if the value of the variable DEFAULTRENAMEMETHOD is MASTERSCOPE and a Masterscope database exists, otherwise it defaults to EDITCALLERS. (RENAME(RENAME (Function) NIL NIL ("17") 22) OLD NEW TYPES FILES METHOD) [Function] First performs (COPYDEF OLD NEW TYPE) for all TYPE inside TYPES. It then calls CHANGECALLERS to change all occurrences of OLD to NEW, and then "deletes" OLD with DELDEF. For example, if you have a function FOO which you now wish to call FIE, simply perform (RENAME 'FOO 'FIE), and FIE will be given FOO's definition, and all places that FOO are called will be changed to call FIE instead. METHOD is interpreted the same as the METHOD argument to CHANGECALLERS, above. (COMPARE(COMPARE (Function) NIL NIL ("17") 22) NAME1 NAME2 TYPE SOURCE1 SOURCE2) [Function] Compares the definition of NAME1 with that of NAME2, by calling COMPARELISTS (Chapter 3) on (GETDEF NAME1 TYPE SOURCE1) and (GETDEF NAME2 TYPE SOURCE2), which prints their differences on the terminal. For example, if the current value of the variable A is (A B C (D E F) G), and the value of the variable B on the file FOO is (A B C (D F E) G), then: (COMPARE 'A 'B 'VARS 'CURRENT 'FOO) A from CURRENT and B from TEST differ: (E -> F) (F -> E) T (COMPAREDEFS(COMPAREDEFS (Function) NIL NIL ("17") 23) NAME TYPE SOURCES) [Function] Calls COMPARELISTS (Chapter 3) on all pairs of definitions of NAME as a TYPE obtained from the various SOURCES (interpreted as a list of source specifications). Defining New File Package Types All manipulation of typed definitions in the file package is done using the type-independent functions GETDEF, PUTDEF, etc. Therefore, to define a new file package type, it is only necessary to specify (via the function FILEPKGTYPE) what these functions should do when dealing with a typed definition of the new type. Each file package type has the following properties, whose values are functions or lists of functions: These functions are defined to take a TYPE argument so that you may have the same function for more than one type. GETDEF(GETDEF (File Package Type Property) NIL NIL ("17") 23) [File Package Type Property] Value is a function of three arguments, NAME, TYPE, and OPTIONS, which should return the current definition of NAME as a type TYPE. Used by GETDEF (see the Functions for Manipulating Typed Definitions section), which passes its OPTIONS argument. If there is no GETDEF property, a file package command for dumping NAME is created (by MAKENEWCOM). This command is then used to write the definition of NAME as a type TYPE onto the file FILEPKG.SCRATCH (in Interlisp-D, this file is created on the {CORE} device). This expression is then read back in and returned as the current definition. In some situations, the function HASDEF needs to call GETDEF to determine whether a definition exists. In this case, OPTIONS will include the litatom HASDEF, and it is permissable for a GETDEF function to return T or NIL, rather than creating a complex structure which will not be used. NULLDEF(NULLDEF (File Package Type Property) NIL NIL ("17") 23) [File Package Type Property] The value of the NULLDEF property is returned by GETDEF (see the Functions for Manipulating Typed Definitions section) when there is no definition and the NOERROR option is supplied. For example, the NULLDEF of VARS is NOBIND. FILEGETDEF(FILEGETDEF (File Package Type Property) NIL NIL ("17") 23) [File Package Type Property] This enables you to provide a way of obtaining definitions from a file that is more efficient than the default procedure used by GETDEF (see the Functions for Manipulating Typed Definitions section). Value is a function of four arguments, NAME, TYPE, FILE, and OPTIONS. The function is applied by GETDEF when it is determined that a typed definition is needed from a particular file. The function must open and search the given file and return any TYPE definition for NAME that it finds. CANFILEDEF(CANFILEDEF (File Package Type Property) NIL NIL ("17") 23) [File Package Type Property] If the value of this property is non-NIL, this indicates that definitions of this file package type are not loaded when a file is loaded with LOADFROM (see the Loading Files section). The default is NIL. Initially, only FNS has this property set to non-NIL. PUTDEF(PUTDEF (File Package Type Property) NIL NIL ("17") 24) [File Package Type Property] Value is a function of three arguments, NAME, TYPE, and DEFINITION, which should store DEFINITION as the definition of NAME as a type TYPE. Used by PUTDEF (see the Functions for Manipulating Typed Definitions section). HASDEF(HASDEF (File Package Type Property) NIL NIL ("17") 24) [File Package Type Property] Value is a function of three arguments, NAME, TYPE, and SOURCE, which should return (OR NAME T) if NAME is the name of something of type TYPE. SOURCE is as interpreted by HASDEF (see the Functions for Manipulating Typed Definitions section), which uses this property. EDITDEF(EDITDEF (File Package Type Property) NIL NIL ("17") 24) [File Package Type Property] Value is a function of four arguments, NAME, TYPE, SOURCE, and EDITCOMS, which should edit the definition of NAME as a type TYPE from the source SOURCE, interpreting the edit commands EDITCOMS. If sucessful, should return NAME (or a spelling-corrected NAME). If it returns NIL, the "default" editor is called. Used by EDITDEF (see the Functions for Manipulating Typed Definitions section). DELDEF(DELDEF (File Package Type Property) NIL NIL ("17") 24) [File Package Type Property] Value is a function of two arguments, NAME, and TYPE, which removes the definition of NAME as a TYPE that is currently in effect. Used by DELDEF (see the Functions for Manipulating Typed Definitions section). NEWCOM(NEWCOM (File Package Type Property) NIL NIL ("17") 24) [File Package Type Property] Value is a function of four arguments, NAME, TYPE, LISTNAME, and FILE. Specifies how to make a new (instance of a) file package command to dump NAME, an object of type TYPE. The function should return the new file package command. Used by ADDTOFILE and SHOWDEF. If LISTNAME is non-NIL, this means that you specified LISTNAME as the filevar in interaction with ADDTOFILES? (see the FileVars section). If no NEWCOM is specified, the default is to call DEFAULTMAKENEWCOM, which will construct and return a command of the form (TYPE NAME). You can advise or redefine DEFAULTMAKENEWCOM . WHENCHANGED(WHENCHANGED (File Package Type Property) NIL NIL ("17") 24) [File Package Type Property] Value is a list of functions to be applied to NAME, TYPE, and REASON when NAME, an instance of type TYPE, is changed or defined (see MARKASCHANGED, in the Marking Changes section). Used for various applications, e.g. when an object of type I.S.OPRS changes, it is necessary to clear the corresponding translatons from CLISPARRAY. The WHENCHANGED functions are called before the object is marked as changed, so that it can, in fact, decide that the object is not to be marked as changed, and execute (RETFROM 'MARKASCHANGED). For backwards compatibility, the REASON argument passed to WHENCHANGED functions is either T (for DEFINED) and NIL (for CHANGED). WHENFILED (WHENFILED% (File Package Type Property) NIL NIL ("17") 24) [File Package Type Property] Value is a list of functions to be applied to NAME, TYPE, and FILE when NAME, an instance of type TYPE, is added to FILE. WHENUNFILED(WHENUNFILED (File Package Type Property) NIL NIL ("17") 25) [File Package Type Property] Value is a list of functions to be applied to NAME, TYPE, and FILE when NAME, an instance of type TYPE, is removed from FILE. DESCRIPTION(DESCRIPTION (File Package Type Property) NIL NIL ("17") 25) [File Package Type Property] Value is a string which describes instances of this type. For example, for type RECORDS, the value of DESCRIPTION is the string "record declarations". The function FILEPKGTYPE is used to define new file package types, or to change the properties of existing types. It is possible to redefine the attributes of system file package types, such as FNS or PROPS. (FILEPKGTYPE(FILEPKGTYPE (Function) NIL NIL ("17") 25) TYPE PROP1 VAL1 ... PROPN VALN) [NoSpread Function] Nospread function for defining new file package types, or changing properties of existing file package types. PROPi is one of the property names given above; VALi is the value to be given to that property. Returns TYPE. (FILEPKGTYPE TYPE PROP) returns the value of the property PROP, without changing it. (FILEPKGTYPE TYPE) returns an alist of all of the defined properties of TYPE, using the property names as keys. Specifying TYPE as the litatom TYPE can be used to define one file package type as a synonym of another. For example, (FILEPKGTYPE 'R 'TYPE 'RECORDS) defines R as a synonym for the file package type RECORDS. File Package Commands 1 The basic mechanism for creating symbolic files is the function MAKEFILE (see the Storing Files section). For each file, the file package has a data structure known as the "filecoms", which specifies what typed descriptions are contained in the file. A filecoms is a list of file package commands, each of which specifies objects of a certain file package type which should be dumped. For example, the filecoms ( (FNS FOO) (VARS FOO BAR BAZ) (RECORDS XYZZY) ) has a FNS, a VARS, and a RECORDS file package command. This filecoms specifies that the function definition for FOO, the variable values of FOO, BAR, and BAZ, and the record declaration for XYZZY should be dumped. By convention, the filecoms of a file X is stored as the value of the litatom XCOMS. For example, (MAKEFILE 'FOO.;27) will use the value of FOOCOMS as the filecoms. This variable can be directly manipulated, but the file package contains facilities which make constructing and updating filecoms easier, and in some cases automatic (see the Functions for Manipulating File Command Lists section). A file package command is an instruction to MAKEFILE to perform an explicit, well-defined operation, usually printing an expression. Usually there is a one-to-one correspondence between file package types and file package commands; for each file package type, there is a file package command which is used for writing objects of that type to a file, and each file package command is used to write objects of a particular type. However, in some cases, the same file package type can be dumped by several different file package commands. For example, the file package commands PROP, IFPROP, and PROPS all dump out objects with the file package type PROPS. This means if you change an object of file package type PROPS via EDITP, a typed-in call to PUTPROP, or via an explicit call to MARKASCHANGED, this object can be written out with any of the above three commands. Thus, when the file package attempts to determine whether this typed object is contained on a particular file, it must look at instances of all three file package commands PROP, IFPROP, and PROPS, to see if the corresponding atom and property are specified. It is also permissible for a single file package command to dump several different file package types. For example, you can define a file package command which dumps both a function definition and its macro. Conversely, some file package comands do not dump any file package types at all, such as the E command. For each file package command, the file package must be able to determine what typed definitions the command will cause to be printed so that the file package can determine on what file (if any) an object of a given type is contained (by searching through the filecoms). Similarly, for each file package type, the file package must be able to construct a command that will print out an object of that type. In other words, the file package must be able to map file package commands into file package types, and vice versa. Information can be provided to the file package about a particular file package command via the function FILEPKGCOM (see the Defining New File Package Commands section), and information about a particular file package type via the function FILEPKGTYPE (see the prior section). In the absence of other information, the default is simply that a file package command of the form (X NAME) prints out the definition of NAME as a type X, and, conversely, if NAME is an object of type X, then NAME can be written out by a command of the form (X NAME). If a file package function is given a command or type that is not defined, it attempts spelling correction using FILEPKGCOMSPLST as a spelling list (unless DWIMFLG or NOSPELLFLG=NIL; see Chapter 20). If successful, the corrected version of the list of file package commands is written (again) on the output file, since at this point, the uncorrected list of file package commands would already have been printed on the output file. When the file is loaded, this will result in FILECOMS being reset, and may cause a message to be printed, e.g., (FOOCOMS RESET). The value of FOOCOMS would then be the corrected version. If the spelling correction is unsuccessful, the file package functions generate an error, BAD FILE PACKAGE COMMAND. File package commands can be used to save on the output file definitions of functions, values of variables, property lists of atoms, advised functions, edit macros, record declarations, etc. The interpretation of each file package command is documented in the following sections. (USERMACROS(USERMACROS (Command) NIL NIL ("17") 26) LITATOM1 ... LITATOMN) [File Package Command] Each litatom LITATOMi is the name of a user edit macro. Writes expressions to add the edit macro definitions of LITATOMi to USERMACROS, and adds the names of the commands to the appropriate spelling lists. If LITATOMi is not a user macro, a warning message "no EDIT MACRO for LITATOMi" is printed. Functions and Macros (FNS(FNS (Command) NIL NIL ("17") 26)(FNS (Command) NIL NIL ("17") 26) FN1 ... FNN) [File Package Command] Writes a DEFINEQ expression with the function definitions of FN1 ... FNN. You should never print a DEFINEQ expression directly onto a file (by using the P file package command, for example), because MAKEFILE generates the filemap of function definitions from the FNS file package commands (see the File Maps section). (ADVISE(ADVISE (Command) NIL NIL ("17") 27) FN1 ... FNN) [File Package Command] For each function FNi, writes expressions to reinstate the function to its advised state when the file is loaded. See Chapter 15. When advice is applied to a function programmatically or by hand, it is additive. That is, if a function already has some advice, further advice is added to the already-existing advice. However, when advice is applied to a function as a result of loading a file with an ADVISE file package command, the new advice replaces any earlier advice. ADVISE works this way to prevent problems with loading different versions of the same advice. If you really want to apply additive advice, a file package command such as (P (ADVISE ...)) should be used (see the Miscellaneous File Package Commands section). (ADVICE(ADVICE (Command) NIL NIL ("17") 27) FN1 ... FNN) [File Package Command] For each function FNi, writes a PUTPROPS expression which will put the advice back on the property list of the function. You can then use READVISE (see Chapter 15) to reactivate the advice. (MACROS(MACROS (Command) NIL NIL ("17") 27) LITATOM1 ... LITATOMN) [File Package Command] Each LITATOMi is a litatom with a MACRO definition (and/or a DMACRO, 10MACRO, etc.). Writes out an expression to restore all of the macro properties for each LITATOMi, embedded in a DECLARE: EVAL@COMPILE so the macros will be defined when the file is compiled. See Chapter 10. Variables (VARS(VARS (Command) NIL NIL ("17") 27) VAR1 ... VARN) [File Package Command] For each VARi, writes an expression to set its top level value when the file is loaded. If VARi is atomic, VARS writes out an expression to set VARi to the top-level value it had at the time the file was written. If VARi is non-atomic, it is interpreted as (VAR FORM), and VARS write out an expression to set VAR to the value of FORM (evaluated when the file is loaded). VARS prints out expressions using RPAQQ and RPAQ, which are like SETQQ and SETQ except that they also perform some special operations with respect to the file package (see the Functions Used within Source Files section). VARS cannot be used for putting arbitrary variable values on files. For example, if the value of a variable is an array (or many other data types), a litatom which represents the array is dumped in the file instead of the array itself. The HORRIBLEVARS file package command provides a way of saving and reloading variables whose values contain re-entrant or circular list structure, user data types, arrays, or hash arrays. (INITVARS(INITVARS (Command) NIL NIL ("17") 27) VAR1 ... VARN) [File Package Command] INITVARS is used for initializing variables, setting their values only when they are currently NOBIND. A variable value defined in an INITVARS command will not change an already established value. This means that re-loading files to get some other information will not automatically revert to the initialization values. The format of an INITVARS command is just like VARS. The only difference is that if VARi is atomic, the current value is not dumped; instead NIL is defined as the initialization value. Therefore, (INITVARS FOO (FUM 2)) is the same as (VARS (FOO NIL)(FUM 2)), if FOO and FUM are both NOBIND. INITVARS writes out an RPAQ? expression on the file instead of RPAQ or RPAQQ. (ADDVARS(ADDVARS (Command) NIL NIL ("17") 28) (VAR1 . LST1)...(VARN . LSTN)) [File Package Command] For each (VARi . LSTi), writes an ADDTOVAR (see the Functions Used Within Source Files section) to add each element of LSTi to the list that is the value of VARi at the time the file is loaded. The new value of VARi will be the union of its old value and LSTi. If the value of VARi is NOBIND, it is first set to NIL. For example, (ADDVARS (DIRECTORIES LISP LISPUSERS)) will add LISP and LISPUSERS to the value of DIRECTORIES. If LSTi is not specified, VARi is initialized to NIL if its current value is NOBIND. In other words, (ADDVARS (VAR)) will initialize VAR to NIL if VAR has not previously been set. (APPENDVARS(APPENDVARS (Command) NIL NIL ("17") 28) (VAR1 . LST1) ... (VARN . LSTN)) [File Package Command] The same as ADDVARS, except that the values are added to the end of the lists (using APPENDTOVAR, in the Functions Used Within Source Files section), rather than at the beginning. (UGLYVARS(UGLYVARS (Command) NIL NIL ("17") 28) VAR1 ... VARN) [File Package Command] Like VARS, except that the value of each VARi may contain structures for which READ is not an inverse of PRINT, e.g. arrays, readtables, user data types, etc. Uses HPRINT (see Chapter 25). ((HORRIBLEVARS (Command) NIL NIL ("17") 28)HORRIBLEVARS VAR1 ... VARN) [File Package Command] Like UGLYVARS, except structures may also contain circular pointers. Uses HPRINT (see Chapter 25). The values of VAR1 ... VARN are printed in the same operation, so that they may contain pointers to common substructures. UGLYVARS does not do any checking for circularities, which results in a large speed and internal-storage advantage over HORRIBLEVARS. Thus, if it is known that the data structures do not contain circular pointers, UGLYVARS should be used instead of HORRIBLEVARS. (ALISTS(ALISTS (Command) NIL NIL ("17") 28) (VAR1 KEY1 KEY2 ...)...(VARN KEY3 KEY4 ...)) [File Package Command] VARi is a variable whose value is an association list, such as EDITMACROS, BAKTRACELST, etc. For each VARi, ALISTS writes out expressions which will restore the values associated with the specified keys. For example, (ALISTS (BREAKMACROS BT BTV)) will dump the definition for the BT and BTV commands on BREAKMACROS. Some association lists (USERMACROS, LISPXMACROS, etc.) are used to implement other file package types, and they have their own file package commands. (SPECVARS(SPECVARS (Command) NIL NIL ("17") 29) VAR1 ... VARN) [File Package Command] (LOCALVARS(LOCALVARS (Command) NIL NIL ("17") 29) VAR1 ... VARN) [File Package Command] (GLOBALVARS(GLOBALVARS (Command) NIL NIL ("17") 29) VAR1 ... VARN) [File Package Command] Outputs the corresponding compiler declaration embedded in a DECLARE: DOEVAL@COMPILE DONTCOPY. See Chapter 18. (CONSTANTS(CONSTANTS (Command) NIL NIL ("17") 29) VAR1 ... VARN) [File Package Command] Like VARS, for each VARi writes an expression to set its top level value when the file is loaded. Also writes a CONSTANTS expression to declare these variables as constants (see Chapter 18). Both of these expressions are wrapped in a (DECLARE: EVAL@COMPILE ...) expression, so they can be used by the compiler. Like VARS, VARi can be non-atomic, in which case it is interpreted as (VAR FORM), and passed to CONSTANTS (along with the variable being initialized to FORM). Litatom Properties (PROP(PROP (Command) NIL NIL ("17") 29) PROPNAME LITATOM1 ... LITATOMN) [File Package Command] Writes a PUTPROPS expression to restore the value of the PROPNAME property of each litatom LITATOMi when the file is loaded. If PROPNAME is a list, expressions will be written for each property on that list. If PROPNAME is the litatom ALL, the values of all user properties (on the property list of each LITATOMi) are saved. SYSPROPS is a list of properties used by system functions. Only properties not on that list are dumped when the ALL option is used. If LITATOMi does not have the property PROPNAME (as opposed to having the property with value NIL), a warning message "NO PROPNAME PROPERTY FOR LITATOMi" is printed. The command IFPROP can be used if it is not known whether or not an atom will have the corresponding property. (IFPROP(IFPROP (Command) NIL NIL ("17") 29) PROPNAME LITATOM1 ... LITATOMN) [File Package Command] Same as the PROP file package command, except that it only saves the properties that actually appear on the property list of the corresponding atom. For example, if FOO1 has property PROP1 and PROP2, FOO2 has PROP3, and FOO3 has property PROP1 and PROP3, then (IFPROP (PROP1 PROP2 PROP3) FOO1 FOO2 FOO3) will save only those five property values. (PROPS(PROPS (Command) NIL NIL ("17") 29) (LITATOM1 PROPNAME1)...(LITATOMN PROPNAMEN)) [File Package Command] Similar to PROP command. Writes a PUTPROPS expression to restore the value of PROPNAMEi for each LITATOMi when the file is loaded. As with the PROP command, if LITATOMi does not have the property PROPNAME (as opposed to having the property with NIL value), a warning message "NO PROPNAMEi PROPERTY FOR LITATOMi" is printed. Miscellaneous File Package Commands (RECORDS(RECORDS (Command) NIL NIL ("17") 30) REC1 ... RECN) [File Package Command] Each RECi is the name of a record (see Chapter 8). Writes expressions which will redeclare the records when the file is loaded. (INITRECORDS(INITRECORDS (Command) NIL NIL ("17") 30) REC1 ... RECN) [File Package Command] Similar to RECORDS, INITRECORDS writes expressions on a file that will, when loaded, perform whatever initialization/allocation is necessary for the indicated records. However, the record declarations themselves are not written out. This facility is useful for building systems on top of Interlisp, in which the implementor may want to eliminate the record declarations from a production version of the system, but the allocation for these records must still be done. (LISPXMACROS(LISPXMACROS (Command) NIL NIL ("17") 30) LITATOM1 ... LITATOMN) [File Package Command] Each LITATOMi is defined on LISPXMACROS or LISPXHISTORYMACROS (see Chapter 13). Writes expressions which will save and restore the definition for each macro, as well as making the necessary additions to LISPXCOMS (I.S.OPRS(I.S.OPRS (Command) NIL NIL ("17") 30) OPR1 ... OPRN) [File Package Command] Each OPRi is the name of a user-defined i.s.opr (see Chapter 9). Writes expressions which will redefine the i.s.oprs when the file is loaded. (RESOURCES(RESOURCES (Command) NIL NIL ("17") 30) RESOURCE1 ... RESOURCEN) [File Package Command] Each RESOURCESi is the name of a resource (see Chapter 12). Writes expressions which will redeclare the resource when the file is loaded. (INITRESOURCES(INITRESOURCES (Command) NIL NIL ("17") 30) RESOURCE1 ... RESOURCEN) [File Package Command] Parallel to INITRECORDS, INITRESOURCES writes expressions on a file to perform whatever initialization/allocation is necessary for the indicated resources, without writing the resource declaration itself. (COURIERPROGRAMS(COURIERPROGRAMS (Command) NIL NIL ("17") 30) NAME1 ... NAMEN) [File Package Command] Each NAMEi is the name of a Courier program (see Chapter 31). Writes expressions which will redeclare the Courier program when the file is loaded. (TEMPLATES(TEMPLATES (Command) NIL NIL ("17") 30) LITATOM1 ... LITATOMN) [File Package Command] Each LITATOMi is a litatom which has a Masterscope template (see Chapter 19). Writes expressions which will restore the templates when the file is loaded. (FILES(FILES (Command) NIL NIL ("17") 30) FILE1 ... FILEN) [File Package Command] Used to specify auxiliary files to be loaded in when the file is loaded. Dumps an expression calling FILESLOAD (see the Loading Files section), with FILE1 ... FILEN as the arguments. FILESLOAD interprets FILE1 ... FILEN as files to load, possibly interspersed with lists used to specify certain loading options. (FILEPKGCOMS(FILEPKGCOMS (Command) NIL NIL ("17") 31) LITATOM1 ... LITATOMN) [File Package Command] Each litatom LITATOMi is either the name of a user-defined file package command or a user-defined file package type (or both). Writes expressions which will restore each command/type. If LITATOMi is not a file package command or type, a warning message "no FILE PACKAGE COMMAND for LITATOMi" is printed. ((* (Command) NIL NIL ("17") 31)* . TEXT) [File Package Command] Used for inserting comments in a file. The file package command is simply written on the output file; it will be ignored when the file is loaded. If the first element of TEXT is another *, a form-feed is printed on the file before the comment. (P(P (Command) NIL NIL ("17") 31) EXP1 ... EXPN) [File Package Command] Writes each of the expressions EXP1 ... EXPN on the output file, where they will be evaluated when the file is loaded. (E(E (Command) NIL NIL ("17") 31) FORM1 ... FORMN) [File Package Command] Each of the forms FORM1 ... FORMN is evaluated at output time, when MAKEFILE interpretes this file package command. (COMS(COMS (Command) NIL NIL ("17") 31) COM1 ... COMN) [File Package Command] Each of the commands COM1 ... COMN is interpreted as a file package command. (ORIGINAL(ORIGINAL (Command) NIL NIL ("17") 31) COM1 ... COMN) [File Package Command] Each of the commands COMi will be interpreted as a file package command without regard to any file package macros (as defined by the MACRO property of the FILEPKGCOM function, in the Defining New File Package Commands section). Useful for redefining a built-in file package command in terms of itself. Some of the "built-in" file package commands are defined by file package macros, so interpreting them (or new user-defined file package commands) with ORIGINAL will fail. ORIGINAL was never intended to be used outside of a file package command macro. DECLARE: (DECLARE:(DECLARE: (Command) NIL NIL ("17") 31) . FILEPKGCOMS/FLAGS) [File Package Command] Normally expressions written onto a symbolic file are evaluated when loaded; copied to the compiled file when the symbolic file is compiled (see Chapter 18); and not evaluated at compile time. DECLARE: allows you to override these defaults. FILEPKGCOMS/FLAGS is a list of file package commands, possibly interspersed with "tags". The output of those file package commands within FILEPKGCOMS/FLAGS is embedded in a DECLARE: expression, along with any tags that are specified. For example, (DECLARE: EVAL@COMPILE DONTCOPY (FNS ...) (PROP ...)) would produce (DECLARE: EVAL@COMPILE DONTCOPY (DEFINEQ ...) (PUTPROPS ...)). DECLARE: is defined as an nlambda nospread function, which processes its arguments by evaluating or not evaluating each expression depending on the setting of internal state variables. The initial setting is to evaluate, but this can be overridden by specifying the DONTEVAL@LOAD tag. DECLARE: expressions are specially processed by the compiler. For the purposes of compilation, DECLARE: has two principal applications: to specify forms that are to be evaluated at compile time, presumably to affect the compilation, e.g., to set up macros; and/or to indicate which expressions appearing in the symbolic file are not to be copied to the output file. (Normally, expressions are not evaluated and are copied.) Each expression in CDR of a DECLARE: form is either evaluated/not-evaluated and copied/not-copied depending on the settings of two internal state variables, initially set for copy and not-evaluate. These state variables can be reset for the remainder of the expressions in the DECLARE: by means of the tags DONTCOPY, EVAL@COMPILE, etc. The tags are: EVAL@LOAD DOEVAL@LOAD Evaluate the following forms when the file is loaded (unless overridden by DONTEVAL@LOAD). DONTEVAL@LOAD Do not evaluate the following forms when the file is loaded. EVAL@LOADWHEN This tag can be used to provide conditional evaluation. The value of the expression immediately following the tag determines whether or not to evaluate subsequent expressions when loading. ... EVAL@LOADWHEN T ... is equivalent to ... EVAL@LOAD ... COPY DOCOPY When compiling, copy the following forms into the compiled file. DONTCOPY When compiling, do not copy the following forms into the compiled file. Note: If the file package commands following DONTCOPY include record declarations for datatypes, or records with initialization forms, it is necessary to include a INITRECORDS file package command (see the prior section) outside of the DONTCOPY form so that the initialization information is copied. For example, if FOO was defined as a datatype, (DECLARE: DONTCOPY (RECORDS FOO)) (INITRECORDS FOO) would copy the data type declaration for FOO, but would not copy the whole record declaration. COPYWHEN When compiling, if the next form evaluates to non-NIL, copy the following forms into the compiled file. EVAL@COMPILE DOEVAL@COMPILE When compiling, evaluate the following forms. DONTEVAL@COMPILE When compiling, do not evaluate the following forms. EVAL@COMPILEWHEN When compiling, if the next form evaluates to non-NIL, evaluate the following forms. FIRST For expressions that are to be copied to the compiled file, the tag FIRST can be used to specify that the following expressions in the DECLARE: are to appear at the front of the compiled file, before anything else except the FILECREATED expressions (see the Symbolic File Format section). For example, (DECLARE: COPY FIRST (P (PRINT MESS1 T)) NOTFIRST (P (PRINT MESS2 T))) will cause (PRINT MESS1 T) to appear first in the compiled file, followed by any functions, then (PRINT MESS2 T). NOTFIRST Reverses the effect of FIRST. The value of DECLARETAGSLST is a list of all the tags used in DECLARE: expressions. If a tag not on this list appears in a DECLARE: file package command, spelling correction is performed using DECLARETAGSLST as a spelling list. Note that the function LOADCOMP (see the Loading Files section) provides a convenient way of obtaining information from the DECLARE: expressions in a file, without reading in the entire file. This information may be used for compiling other files. (BLOCKS(BLOCKS (Command) NIL NIL ("17") 33) BLOCK1 ... BLOCKN) [File Package Command] For each BLOCKi, writes a DECLARE: expression which the block compile functions interpret as a block declaration. See Chapter 18. Exporting Definitions When building a large system in Interlisp, it is often the case that there are record definitions, macros and the like that are needed by several different system files when running, analyzing and compiling the source code of the system, but which are not needed for running the compiled code. By using the DECLARE: file package command with tag DONTCOPY (see the prior section), these definitions can be kept out of the compiled files, and hence out of the system constructed by loading the compiled files files into Interlisp. This saves loading time, space in the resulting system, and whatever other overhead might be incurred by keeping those definitions around, e.g., burden on the record package to consider more possibilities in translating record accesses, or conflicts between system record fields and user record fields. However, if the implementor wants to debug or compile code in the resulting system, the definitions are needed. And even if the definitions had been copied to the compiled files, a similar problem arises if one wants to work on system code in a regular Interlisp environment where none of the system files had been loaded. One could mandate that any definition needed by more than one file in the system should reside on a distinguished file of definitions, to be loaded into any environment where the system files are worked on. Unfortunately, this would keep the definitions away from where they logically belong. The EXPORT mechanism is designed to solve this problem. To use the mechanism, the implementor identifies any definitions needed by files other than the one in which the definitions reside, and wraps the corresponding file package commands in the EXPORT file package command. Thereafter, GATHEREXPORTS can be used to make a single file containing all the exports. (EXPORT COM1 ... COMN) [File Package Command] This command is used for "exporting" definitions. Like COM, each of the commands COM1 ... COMN is interpreted as a file package command. The commands are also flagged in the file as being "exported" commands, for use with GATHEREXPORTS. (GATHEREXPORTS(GATHEREXPORTS (Function) NIL NIL ("17") 34) FROMFILES TOFILE FLG) [Function] FROMFILES is a list of files containing EXPORT commands. GATHEREXPORTS extracts all the exported commands from those files and produces a loadable file TOFILE containing them. If FLG = EVAL, the expressions are evaluated as they are gathered; i.e., the exports are effectively loaded into the current environment as well as being written to TOFILE. (IMPORTFILE(IMPORTFILE (Function) NIL NIL ("17") 34) FILE RETURNFLG) [Function] If RETURNFLG is NIL, this loads any exported definitions from FILE into the current environment. If RETURNFLG is T, this returns a list of the exported definitions (evaluable expressions) without actually evaluating them. (CHECKIMPORTS(CHECKIMPORTS (Function) NIL NIL ("17") 34) FILES NOASKFLG) [Function] Checks each of the files in FILES to see if any exists in a version newer than the one from which the exports in memory were taken (GATHEREXPORTS and IMPORTFILE note the creation dates of the files involved), or if any file in the list has not had its exports loaded at all. If there are any such files, you are asked for permission to IMPORTFILE each such file. If NOASKFLG is non-NIL, IMPORTFILE is performed without asking. For example, suppose file FOO contains records R1, R2, and R3, macros BAR and BAZ, and constants CON1 and CON2. If the definitions of R1, R2, BAR, and BAZ are needed by files other than FOO, then the file commands for FOO might contain the command (DECLARE: EVAL@COMPILE DONTCOPY (EXPORT (RECORDS R1 R2) (MACROS BAR BAZ)) (RECORDS R3) (CONSTANTS BAZ)) None of the commands inside this DECLARE: would appear on FOO's compiled file, but (GATHEREXPORTS '(FOO) 'MYEXPORTS) would copy the record definitions for R1 and R2 and the macro definitions for BAR and BAZ to the file MYEXPORTS. FileVars In each of the file package commands described above, if the litatom * follows the command type, the form following the *, i.e., CADDR of the command, is evaluated and its value used in executing the command, e.g., (FNS * (APPEND FNS1 FNS2)). When this form is a litatom, e.g. (FNS * FOOFNS), we say that the variable is a "filevar". Note that (COMS * FORM) provides a way of computing what should be done by MAKEFILE. Example: (SETQ FOOFNS '(FOO1 FOO2 FOO3)) (FOO1 FOO2 FOO3) (SETQ FOOCOMS '( (FNS * FOOFNS) (VARS FIE) (PROP MACRO FOO1 FOO2) (P (MOVD 'FOO1 'FIE1))] (MAKEFILE 'FOO) would create a file FOO containing: (FILECREATED "time and date the file was made" . "other information") (PRETTYCOMPRINT FOOCOMS) (RPAQQ FOOCOMS ((FNS * FOOFNS) ...) (RPAQQ FOOFNS (FOO1 FOO3 FOO3)) (DEFINEQ "definitions of FOO1, FOO2, and FOO3") (RPAQQ FIE "value of FIE") (PUTPROPS FOO1 MACRO PROPVALUE) (PUTPROPS FOO2 MACRO PROPVALUE) (MOVD (QUOTE FOO1) (QUOTE FIE1)) STOP For the PROP and IFPROP commands (see the Litatom Properties section), the * follows the property name instead of the command, e.g., (PROP MACRO * FOOMACROS). Also, in the form (* * comment ...), the word comment is not treated as a filevar. Defining New File Package Commands A file package command is defined by specifying the values of certain properties. You can specify the various attributes of a file package command for a new command, or respecify them for an existing command. The following properties are used: MACRO(MACRO (Property) NIL NIL ("17") 35) [File Package Command Property] Defines how to dump the file package command. Used by MAKEFILE. Value is a pair (ARGS . COMS). The "arguments" to the file package command are substituted for ARGS throughout COMS, and the result treated as a list of file package commands. For example, following (FILEPKGCOM 'FOO 'MACRO '((X Y) . COMS)), the file package command (FOO A B) will cause A to be substituted for X and B for Y throughout COMS, and then COMS treated as a list of commands. The substitution is carried out by SUBPAIR (see Chapter 3), so that the "argument list" for the macro can also be atomic. For example, if (X . COMS) was used instead of ((X Y) . COMS), then the command (FOO A B) would cause (A B) to be substituted for X throughout COMS. Filevars are evaluated before substitution. For example, if the litatom * follows NAME in the command, CADDR of the command is evaluated substituting in COMS. ADD(ADD (Property) NIL NIL ("17") 35) [File Package Command Property] Specifies how (if possible) to add an instance of an object of a particular type to a given file package command. Used by ADDTOFILE. Value is FN, a function of three arguments, COM, a file package command CAR of which is EQ to COMMANDNAME, NAME, a typed object, and TYPE, its type. FN should return T if it (undoably) adds NAME to COM, NIL if not. If no ADD property is specified, then the default is (1) if (CAR COM)=TYPE and (CADR COM)=*, and (CADDR COM) is a filevar (i.e. a literal atom), add NAME to the value of the filevar, or (2) if (CAR COM)=TYPE and (CADR COM) is not *, add NAME to (CDR COM). Actually, the function is given a fourth argument, NEAR, which if non-NIL, means the function should try to add the item after NEAR. See discussion of ADDTOFILES?, in the Storing Files section. DELETE(DELETE (Property) NIL NIL ("17") 35) [File Package Command Property] Specifies how (if possible) to delete an instance of an object of a particular type from a given file package command. Used by DELFROMFILES. Value is FN, a function of three arguments, COM, NAME, and TYPE, same as for ADD. FN should return T if it (undoably) deletes NAME from COM, NIL if not. If no DELETE property is specified, then the default is either (CAR COM)=TYPE and (CADR COM)=*, and (CADDR COM) is a filevar (i.e. a literal atom), and NAME is contained in the value of the filevar, then remove NAME from the filevar, or if (CAR COM)=TYPE and (CADR COM) is not *, and NAME is contained in (CDR COM), then remove NAME from (CDR COM). If FN returns the value of ALL, it means that the command is now "empty", and can be deleted entirely from the command list. CONTENTS(CONTAIN (Property) NIL NIL ("17") 36) [File Package Command Property] CONTAIN(CONTAIN (Property) NIL NIL ("17") 36) [File Package Command Property] Determines whether an instance of an object of a given type is contained in a given file package command. Used by WHEREIS and INFILECOMS?. Value is FN, a function of three arguments, COM, a file package command CAR of which is EQ to COMMANDNAME, NAME, and TYPE. The interpretation of NAME is as follows: if NAME is NIL, FN should return a list of elements of type TYPE contained in COM. If NAME is T, FN should return T if there are any elements of type TYPE in COM. If NAME is an atom other than T or NIL, return T if NAME of type TYPE is contained in COM. Finally, if NAME is a list, return a list of those elements of type TYPE contained in COM that are also contained in NAME. It is sufficient for the CONTENTS function to simply return the list of items of type TYPE in command COM, i.e. it can in fact ignore the NAME argument. The NAME argument is supplied mainly for those situations where producing the entire list of items involves significantly more computation or creates more storage than simply determining whether a particular item (or any item) of type TYPE is contained in the command. If a CONTENTS property is specified and the corresponding function application returns NIL and (CAR COM)=TYPE, then the operation indicated by NAME is performed on the value of (CADDR COM), if (CADR COM)=*, otherwise on (CDR COM). In other words, by specifying a CONTENTS property that returns NIL, e.g. the function NILL, you specify that a file package command of name FOO produces objects of file package type FOO and only objects of type FOO. If the CONTENTS property is not provided, the command is simply expanded according to its MACRO definition, and each command on the resulting command list is then interrogated. If COMMANDNAME is a file package command that is used frequently, its expansion by the various parts of the system that need to interrogate files can result in a large number of CONSes and garbage collections. By informing the file package as to what this command actually does and does not produce via the CONTENTS property, this expansion is avoided. For example, suppose you have a file package command called GRAMMARS which dumps various property lists but no functions. The file package could ignore this command when seeking information about FNS. The function FILEPKGCOM is used to define new file package commands, or to change the properties of existing commands. It is possible to redefine the attributes of system file package commands, such as FNS or PROPS, and to cause unpredictable results. (FILEPKGCOM(FILEPKGCOM (Function) NIL NIL ("17") 36) COMMANDNAME PROP1 VAL1 ... PROPN VALN) [NoSpread Function] Nospread function for defining new file package commands, or changing properties of existing file package commands. PROPi is one of of the property names described above; VALi is the value to be given that property of the file package command COMMANDNAME. Returns COMMANDNAME. (FILEPKGCOM COMMANDNAME PROP) returns the value of the property PROP, without changing it. (FILEPKGCOM COMMANDNAME) returns an alist of all of the defined properties of COMMANDNAME, using the property names as keys. Specifying TYPE as the litatom COM can be used to define one file package command as a synonym of another. For example, (FILEPKGCOM 'INITVARIABLES 'COM 'INITVARS) defines INITVARIABLES as a synonym for the file package command INITVARS. Functions for Manipulating File Command Lists 1 The following functions may be used to manipulate filecoms. The argument COMS does not have to correspond to the filecoms for some file. For example, COMS can be the list of commands generated as a result of expanding a user-defined file package command. The following functions will accept a file package command as a valid value for their TYPE argument, even if it does not have a corresponding file package type. User-defined file package commands are expanded as necessary. (INFILECOMS?(INFILECOMS? (Function) NIL NIL ("17") 37) NAME TYPE COMS %) [Function] COMS is a list of file package commands, or a variable whose value is a list of file package commands. TYPE is a file package type. INFILECOMS? returns T if NAME of type TYPE is "contained" in COMS. If NAME=NIL, INFILECOMS? returns a list of all elements of type TYPE. If NAME=T, INFILECOMS? returns T if there are any elements of type TYPE in COMS. (ADDTOFILE(INFILECOMS? (Function) NIL NIL ("17") 37) NAME TYPE FILE NEAR LISTNAME) [Function] Adds NAME of type TYPE to the file package commands for FILE. If NEAR is given and it is the name of an item of type TYPE already on FILE, then NAME is added to the command that dumps NEAR. If LISTNAME is given and is the name of a list of items of TYPE items on FILE, then NAME is added to that list. Uses ADDTOCOMS and MAKENEWCOM. Returns FILE. ADDTOFILE is undoable. (DELFROMFILES(DELFROMFILES (Function) NIL NIL ("17") 37) NAME TYPE FILES) [Function] Deletes all instances of NAME of type TYPE from the filecoms for each of the files on FILES. If FILES is a non-NIL litatom, (LIST FILES) is used. FILES=NIL defaults to FILELST. Returns a list of files from which NAME was actually removed. Uses DELFROMCOMS. DELFROMFILES is undoable. Deleting a function will also remove the function from any BLOCKS declarations in the filecoms. (ADDTOCOMS(ADDTOCOMS (Function) NIL NIL ("17") 37) COMS NAME TYPE NEAR LISTNAME) [Function] Adds NAME as a TYPE to COMS, a list of file package commands or a variable whose value is a list of file package commands. Returns NIL if ADDTOCOMS was unable to find a command appropriate for adding NAME to COMS. NEAR and LISTNAME are described in the discussion of ADDTOFILE. ADDTOCOMS is undoable. The exact algorithm for adding commands depends the particular command itself. See discussion of the ADD property, in the description of FILEPKGCOM. ADDTOCOMS will not attempt to add an item to any command which is inside of a DECLARE: unless the user specified a specific name via the LISTNAME or NEAR option of ADDTOFILES?. (DELFROMCOMS(DELFROMCOMS (Function) NIL NIL ("17") 38) COMS NAME TYPE) [Function] Deletes NAME as a TYPE from COMS. Returns NIL if DELFROMCOMS was unable to modify COMS to delete NAME. DELFROMCOMS is undoable. (MAKENEWCOM(MAKENEWCOM (Function) NIL NIL ("17") 38) NAME TYPE % %) [Function] Returns a file package command for dumping NAME of type TYPE. Uses the procedure described in the discussion of NEWCOM, in the Defining New File Package Types section. (MOVETOFILE(MOVETOFILE (Function) NIL NIL ("17") 38) TOFILE NAME TYPE FROMFILE) [Function] Moves the definition of NAME as a TYPE from FROMFILE to TOFILE by modifying the file commands in the appropriate way (with DELFROMFILES and ADDTOFILE). Note that if FROMFILE is specified, the definition will be retrieved from that file, even if there is another definition currently in your environment. (FILECOMSLST(FILECOMSLST (Function) NIL NIL ("17") 38) FILE TYPE %) [Function] Returns a list of all objects of type TYPE in FILE. (FILEFNSLST(FILEFNSLST (Function) NIL NIL ("17") 38) FILE) [Function] Same as (FILECOMSLST FILE 'FNS). (FILECOMS(FILECOMS (Function) NIL NIL ("17") 38) FILE TYPE) [Function] Returns (PACK* FILE (OR TYPE 'COMS)). Note that (FILECOMS 'FOO) returns the litatom FOOCOMS, not the value of FOOCOMS. (SMASHFILECOMS(SMASHFILECOMS (Function) NIL NIL ("17") 38) FILE) [Function] Maps down (FILECOMSLST FILE 'FILEVARS) and sets to NOBIND all filevars (see the FileVars section), i.e., any variable used in a command of the form (COMMAND * VARIABLE). Also sets (FILECOMS FILE) to NOBIND. Returns FILE. Symbolic File Format 1 The file package manipulates symbolic files in a particular format. This format is defined so that the information in the file is easily readable when the file is listed, as well as being easily manipulated by the file package functions. In general, there is no reason for you to manually change the contents of a symbolic file. However, to allow you to extend the file package, this section describes some of the functions used to write symbolic files, and other matters related to their format. (PRETTYDEF(PRETTYDEF (Function) NIL NIL ("17") 38) PRTTYFNS PRTTYFILE PRTTYCOMS REPRINTFNS SOURCEFILE CHANGES) [Function] Writes a symbolic file in PRETTYPRINT format for loading, using FILERDTBL as its read table. PRETTYDEF returns the name of the symbolic file that was created. PRETTYDEF operates under a RESETLST (see Chapter 14), so if an error occurs, or a Control-D is typed, all files that PRETTYDEF has opened will be closed, the (partially complete) file being written will be deleted, and any undoable operations executed will be undone. The RESETLST also means that any RESETSAVEs executed in the file package commands will also be protected. PRTTYFNS is an optional list of function names. It is equivalent to including (FNS * PRTTYFNS) in the file package commands in PRTTYCOMS. PRTTYFNS is an anachronism from when PRETTYDEF did not use a list of file package commands, and should be specified as NIL. PRTTYFILE is the name of the file on which the output is to be written. If PRTTYFILE=NIL, the primary output file is used. If PRTTYFILE is atomic the file is opened if not already open, and it becomes the primary output file. PRTTYFILE is closed at end of PRETTYDEF, and the primary output file is restored. Finally, if PRTTYFILE is a list, CAR of PRTTYFILE is assumed to be the file name, and is opened if not already open. In this case, the file is left open at end of PRETTYDEF. PRTTYCOMS is a list of file package commands interpreted as described in the File Package Commands section. If PRTTYCOMS is atomic, its top level value is used and an RPAQQ is written which will set that atom to the list of commands when the file is subsequently loaded. A PRETTYCOMPRINT expression (see below) will also be written which informs the user of the named atom or list of commands when the file is subsequently loaded. In addition, if any of the functions in the file are nlambda functions, PRETTYDEF will automatically print a DECLARE: expression suitable for informing the compiler about these functions, in case you recompile the file without having first loaded the nlambda functions (see Chapter 18). REPRINTFNS and SOURCEFILE are for use in conjunction with remaking a file (see the Remaking a Symbolic File section). REPRINTFNS can be a list of functions to be prettyprinted, or EXPRS, meaning prettyprint all functions with EXPR definitions, or ALL meaning prettyprint all functions either defined as EXPRs, or with EXPR properties. Note that doing a remake with REPRINTFNS=NIL makes sense if there have been changes in the file, but not to any of the functions, e.g., changes to variables or property lists. SOURCEFILE is the name of the file from which to copy the definitions for those functions that are not going to be prettyprinted, i.e., those not specified by REPRINTFNS. SOURCEFILE=T means to use most recent version (i.e., highest number) of PRTTYFILE, the second argument to PRETTYDEF. If SOURCEFILE cannot be found, PRETTYDEF prints the message "FILE NOT FOUND, SO IT WILL BE WRITTEN ANEW", and proceeds as it does when REPRINTFNS and SOURCEFILE are both NIL. PRETTYDEF calls PRETTYPRINT with its second argument PRETTYDEFLG=T, so whenever PRETTYPRINT starts a new function, it prints (on the terminal) the name of that function if more than 30 seconds (real time) have elapsed since the last time it printed the name of a function. Note that normally if PRETTYPRINT is given a litatom which is not defined as a function but is known to be on one of the files noticed by the file package, PRETTYPRINT will load in the definition (using LOADFNS) and print it. This is not done when PRETTYPRINT is called from PRETTYDEF. (PRINTFNS(PRINTFNS (Function) NIL NIL ("17") 39) X %) [Function] X is a list of functions. PRINTFNS prettyprints a DEFINEQ epression that defines the functions to the primary output stream using the primary read table. Used by PRETTYDEF to implement the FNS file package command. (PRINTDATE(PRINTDATE (Function) NIL NIL ("17") 40) FILE CHANGES) [Function] Prints the FILECREATED expression at beginning of PRETTYDEF files. CHANGES used by the file package. (FILECREATED(FILECREATED (Function) NIL NIL ("17") 40) X) [NLambda NoSpread Function] Prints a message (using LISPXPRINT) followed by the time and date the file was made, which is (CAR X). The message is the value of PRETTYHEADER, initially "FILE CREATED". If PRETTYHEADER=NIL, nothing is printed. (CDR X) contains information about the file, e.g., full name, address of file map, list of changed items, etc. FILECREATED also stores the time and date the file was made on the property list of the file under the property FILEDATES and performs other initialization for the file package. (PRETTYCOMPRINT(PRETTYCOMPRINT (Function) NIL NIL ("17") 40) X) [NLambda Function] Prints X (unevaluated) using LISPXPRINT, unless PRETTYHEADER=NIL. PRETTYHEADER (PRETTYHEADER% (Variable) NIL NIL ("17") 40) [Variable] Value is the message printed by FILECREATED. PRETTYHEADER is initially "FILE CREATED". If PRETTYHEADER=NIL, neither FILECREATED nor PRETTYCOMPRINT will print anything. Thus, setting PRETTYHEADER to NIL will result in "silent loads". PRETTYHEADER is reset to NIL during greeting (see Chapter 12). (FILECHANGES(FILECHANGES (Function) NIL NIL ("17") 40) FILE TYPE) [Function] Returns a list of the changed objects of file package type TYPE from the FILECREATED expression of FILE. If TYPE=NIL, returns an alist of all of the changes, with the file package types as the CARs of the elements.. (FILEDATE(FILEDATE (Function) NIL NIL ("17") 40) FILE %) [Function] Returns the file date contained in the FILECREATED expression of FILE. (LISPSOURCEFILEP(LISPSOURCEFILEP (Function) NIL NIL ("17") 40) FILE) [Function] Returns a non-NIL value if FILE is an Interlisp source file, NIL otherwise. Copyright Notices The system has a facility for automatically printing a copyright notice near the front of files, right after the FILECREATED expression, specifying the years it was edited and the copyright owner. The format of the copyright notice is: (* Copyright (c) 1981 by Foo Bars Corporation) Once a file has a copyright notice then every version will have a new copyright notice inserted into the file without your intervention. (The copyright information necessary to keep the copyright up to date is stored at the end of the file.). Any year the file has been edited is considered a "copyright year" and therefore kept with the copyright information. For example, if a file has been edited in 1981, 1982, and 1984, then the copyright notice would look like: (* Copyright (c) 1981,1982,1984 by Foo Bars Corporation) When a file is made, if it has no copyright information, the system will ask you to specify the copyright owner (if COPYRIGHTFLG=T). You may specify one of the names from COPYRIGHTOWNERS, or give one of the following responses: f Type a left-square-bracket. The system will then prompt for an arbitrary string which will be used as the owner-string f Type a right-square-bracket, which specifies that you really do not want a copyright notice. f Type "NONE" which specifies that this file should never have a copyright notice. For example, if COPYRIGHTOWNERS has the value ((BBN "Bolt Beranek and Newman Inc.") (XEROX "Xerox Corporation")) then for a new file FOO the following interaction will take place: Do you want to Copyright FOO? Yes Copyright owner: (user typed ?) one of: BBN - Bolt Beranek and Newman Inc. XEROX - Xerox Corporation NONE - no copyright ever for this file [ - new copyright owner -- type one line of text ] - no copyright notice for this file now Copyright owner: BBN Then "Foo Bars Corporation" in the above copyright notice example would have been "Bolt Beranek and Newman Inc." The following variables control the operation of the copyright facility: COPYRIGHTFLG(COPYRIGHTFLG (Variable) NIL NIL ("17") 41) [Variable] The value of COPYRIGHTFLG determines whether copyright information is maintained in files. Its value is interpreted as follows: NIL The system will preserve old copyright information, but will not ask you about copyrighting new files. This is the default value of COPYRIGHTFLG. T When a file is made, if it has no copyright information, the system will ask you to specify the copyright owner. NEVER The system will neither prompt for new copyright information nor preserve old copyright information. DEFAULT The value of DEFAULTCOPYRIGHTOWNER (below) is used for putting copyright information in files that don't have any other copyright. The prompt "Copyright owner for file xx:" will still be printed, but the default will be filled in immediately. COPYRIGHTOWNERS(COPYRIGHTOWNERS (Variable) NIL NIL ("17") 41) [Variable] COPYRIGHTOWNERS is a list of entries of the form (KEY OWNERSTRING), where KEY is used as a response to ASKUSER and OWNERSTRING is a string which is the full identification of the owner. DEFAULTCOPYRIGHTOWNER(DEFAULTCOPYRIGHTOWNER (Variable) NIL NIL ("17") 41) [Variable] If you do not respond in DWIMWAIT seconds to the copyright query, the value of DEFAULTCOPYRIGHTOWNER is used. Functions Used Within Source Files The following functions are normally only used within symbolic files, to set variable values, property values, etc. Most of these have special behavior depending on file package variables. (RPAQ(RPAQ (Function) NIL NIL ("17") 42) VAR VALUE) [NLambda Function] An nlambda function like SETQ that sets the top level binding of VAR (unevaluated) to VALUE. (RPAQQ(RPAQQ (Function) NIL NIL ("17") 42) VAR VALUE) [NLambda Function] An nlambda function like SETQQ that sets the top level binding of VAR (unevaluated) to VALUE (unevaluated). (RPAQ?(RPAQ? (Function) NIL NIL ("17") 42) VAR VALUE) [NLambda Function] Similar to RPAQ, except that it does nothing if VAR already has a top level value other than NOBIND. Returns VALUE if VAR is reset, otherwise NIL. RPAQ, RPAQQ, and RPAQ? generate errors if X is not a litatom. All are affected by the value of DFNFLG (see Chapter 10). If DFNFLG=ALLPROP (and the value of VAR is other than NOBIND), instead of setting X, the corresponding value is stored on the property list of VAR under the property VALUE. All are undoable. (ADDTOVAR(ADDTOVAR (Function) NIL NIL ("17") 42) VAR X1 X2 ... XN) [NLambda NoSpread Function] Each Xi that is not a member of the value of VAR is added to it, i.e. after ADDTOVAR completes, the value of VAR will be (UNION (LIST X1 X2 ... XN) VAR). ADDTOVAR is used by PRETTYDEF for implementing the ADDVARS command. It performs some file package related operations, i.e. "notices" that VAR has been changed. Returns the atom VAR (not the value of VAR). (APPENDTOVAR VAR X1 X2 ... XN) [NLambda NoSpread Function] Similar to ADDTOVAR, except that the values are added to the end tof the list, rather than at the beginning. (PUTPROPS(PUTPROPS (Function) NIL NIL ("17") 42) ATM PROP1 VAL1 ... PROPN VALN) [NLambda NoSpread Function] Nlambda nospread version of PUTPROP (none of the arguments are evaluated). For i=1...N, puts property PROPi, value VALi, on the property list of ATM. Performs some file package related operations, i.e., "notices" that the corresponding properties have been changed. (SAVEPUT(SAVEPUT (Function) NIL NIL ("17") 42) ATM PROP VAL) [Function] Same as PUTPROP, but marks the corresponding property value as having been changed (used by the file package). File Maps A file map is a data structure which contains a symbolic 'map' of the contents of a file. Currently, this consists of the begin and end byte address (see GETFILEPTR, in Chapter 25) for each DEFINEQ expression in the file, the begin and end address for each function definition within the DEFINEQ, and the begin and end address for each compiled function. MAKEFILE, PRETTYDEF, LOADFNS, RECOMPILE, and numerous other system functions depend heavily on the file map for efficient operation. For example, the file map enables LOADFNS to load selected function definitions simply by setting the file pointer to the corresponding address using SETFILEPTR, and then performing a single READ. Similarly, the file map is heavily used by the "remake" option of MAKEFILE (see the Remaking a Symbolic File section): those function definitions that have been changed since the previous version are prettyprinted; the rest are simply copied from the old file to the new one, resulting in a considerable speedup. Whenever a file is written by MAKEFILE, a file map for the new file is built. Building the map in this case essentially comes for free, since it requires only reading the current file pointer before and after each definition is written or copied. However, building the map does require that PRETTYPRINT know that it is printing a DEFINEQ expression. For this reason, you should never print a DEFINEQ expression onto a file yourself, but should instead always use the FNS file package command (see the Functions and Macros section). The file map is stored on the property list of the root name of the file, under the property FILEMAP. In addition, MAKEFILE writes the file map on the file itself. For cosmetic reasons, the file map is written as the last expression in the file. However, the address of the file map in the file is (over)written into the FILECREATED expression that appears at the beginning of the file so that the file map can be rapidly accessed without having to scan the entire file. In most cases, LOAD and LOADFNS do not have to build the file map at all, since a file map will usually appear in the corresponding file, unless the file was written with BUILDMAPFLG=NIL, or was written outside of Interlisp. Currently, file maps for compiled files are not written onto the files themselves. However, LOAD and LOADFNS will build maps for a compiled file when it is loaded, and store it on the property FILEMAP. Similary, LOADFNS will obtain and use the file map for a compiled file, when available. The use and creation of file maps is controlled by the following variables: BUILDMAPFLG(BUILDMAPFLG (Variable) NIL NIL ("17") 43) [Variable] Whenever a file is read by LOAD or LOADFNS, or written by MAKEFILE, a file map is automatically built unless BUILDMAPFLG=NIL. (BUILDMAPFLG is initially T.) While building the map will not help the first reference to a file, it will help in future references. For example, if you perform (LOADFROM 'FOO) where FOO does not contain a file map, the LOADFROM will be (slightly) slower than if FOO did contain a file map, but subsequent calls to LOADFNS for this version of FOO will be able to use the map that was built as the result of the LOADFROM, since it will be stored on FOO's FILEMAP property. USEMAPFLG(USEMAPFLG (Variable) NIL NIL ("17") 43) [Variable] If USEMAPFLG=T (the initial setting), the functions that use file maps will first check the FILEMAP property to see if a file map for this file was previously obtained or built. If not, the first expression on the file is checked to see if it is a FILECREATED expression that also contains the address of a file map. If the file map is not on the FILEMAP property or in the file, a file map will be built (unless BUILDMAPFLG=NIL). If USEMAPFLG=NIL, the FILEMAP property and the file will not be checked for the file map. This allows you to recover in those cases where the file and its map for some reason do not agree. For example, if you use a text editor to change a symbolic file that contains a map (not recommended), inserting or deleting just one character will throw that map off. The functions which use file maps contain various integrity checks to enable them to detect that something is wrong, and to generate the error FILEMAP DOES NOT AGREE WITH CONTENTS OF FILE. In such cases, you can set USEMAPFLG to NIL, causing the map contained in the file to be ignored, and then reexecute the operation. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "17-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "17-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "17-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "17-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "17-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "17-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))22HZZ,,,ll,ll,ll3HZT,,5552555l~~5l~~,,-T,ll,ll552ll(5HZ +Z2HH0HZT2HH +52ll3HZ +T3(T3HZ +T3$$(T,ll,HH,HH +,HHF PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR,256T-T,,CLASSIC +TITAN +CLASSIC +TITAN +TITAN + HELVETICA HELVETICATITAN + HELVETICA CLASSIC +CLASSIC +CLASSIC + HELVETICA +CLASSIC +MODERN +MODERN +MODERNMODERN ++ HRULE.GETFNCLASSIC ++ * HRULE.GETFNCLASSIC +* ) ) HRULE.GETFNCLASSIC +( ( HRULE.GETFNCLASSIC +-- IM.CHAP.GETFN HELVETICA, HRULE.GETFNMODERN +%   ' '='' 'XJ      '.3J9 +"% '%\ ' D '5]'ihN +1'l +' +  K ' _' AM Q'1 + +(IM.INDEX.GETFN , +; +&[! HRULE.GETFNMODERN +&  '+  (  V 0 [; :FB'   +,  R + ' + +? + /Y 'T    B ^ ; + ' 2     "IM.INDEX.GETFNMODERN +  "  H @  = #IM.INDEX.GETFNMODERN +    9   -  < #    %IM.INDEX.GETFNMODERN +  + 95    +J ` 3<  P  G7M  D $ i /& m- ,  +  (6 .F +. Y + B" &IM.INDEX.GETFN         " &IM.INDEX.GETFN          G?" +'IM.INDEX.GETFNCLASSIC + B  @O`" &IM.INDEX.GETFNCLASSIC + +  EL   " +'IM.INDEX.GETFNCLASSIC + +  @ 4\ T" +'IM.INDEX.GETFNCLASSIC +   +  +  +  +  1 ^/T m"B  mc 4 FC    .F(pA X 8 G    +( ' = W# +; G +    A .! HRULE.GETFNMODERN +" &IM.INDEX.GETFNCLASSIC + "   '  +  Y#  I 5!>    S  +  G" R       -    +B  7  l    5.  +   '     '     ?#   7 2G0 =       n    .      " +'IM.INDEX.GETFNCLASSIC +          "  * ,"&IM.INDEX.GETFNCLASSIC +   +  +  +#  +n    7 r  +U"%IM.INDEX.GETFN   ("*IM.INDEX.GETFNTITAN +  e w  + S  + 0g  +/ !3;  Z %  Gv " +  +  +  +I,8      +' +2 +$ 07" +IM.INDEX.GETFN  +  +  +  H.  +%  B1"&IM.INDEX.GETFN   * ,  M  )  + 3   %    0 0  3 ! HRULE.GETFNMODERN +7|&sE +  + 6 + l + ] {  + !LJ" A + j /B$Mo  + qM !+ HRULE.GETFNMODERN + "~\ @ Z;* > + g! HRULE.GETFNMODERN + A` QA MF7 H",IM.INDEX.GETFNCLASSIC +  $      $ !  % "  " +& $  2$     4 6 } *".IM.INDEX.GETFN  $    , "-IM.INDEX.GETFN $ '+    =   &n,e    . 'K1 |$    y + eD   !0 HRULE.GETFNMODERN +&( + 69.\'"&IM.INDEX.GETFNTITAN + $ )   F &"%IM.INDEX.GETFN$%    2 7  M"$ |LD " *IM.INDEX.GETFNMODERN +$    +" (IM.INDEX.GETFNMODERN +$   "&IM.INDEX.GETFNMODERN +$ z& 8  J 7';"&IM.INDEX.GETFNMODERN + $F" ,IM.INDEX.GETFNMODERN + $>$#   K!  HRULE.GETFNMODERN +& O  (Q-' * U  +%   >  +Xa'%"*IM.INDEX.GETFNTITAN +$ !! HRULE.GETFNMODERN +&h *'" +IM.INDEX.GETFN $  N&P".IM.INDEX.GETFN$>".IM.INDEX.GETFN$e$m C   $ +" + ! R ?"7IM.INDEX.GETFNMODERN +$0" 3IM.INDEX.GETFNMODERN +$DJ'".IM.INDEX.GETFNMODERN +$IR" 5IM.INDEX.GETFN$ !    T +; Q 3"-IM.INDEX.GETFNMODERN +$"0IM.INDEX.GETFNMODERN +$3"+IM.INDEX.GETFNMODERN +$$"0IM.INDEX.GETFNMODERN +$P" 3IM.INDEX.GETFNMODERN +$H ".IM.INDEX.GETFNMODERN +$2"-IM.INDEX.GETFNMODERN +$   $m" 2_&m    .H"/IM.INDEX.GETFNMODERN +$3" 1IM.INDEX.GETFNMODERN +$*" 1IM.INDEX.GETFNMODERN +$6" +2IM.INDEX.GETFNMODERN +$1",IM.INDEX.GETFNMODERN +$)#, (#<? :   E     a )*@ :b 5H  5$ <"%IM.INDEX.GETFN  $ +  C     $ ,F W  Vt)  O%9  <!F "%IM.INDEX.GETFNMODERN +     +      '  J +  *    @"%IM.INDEX.GETFNMODERN +      " + %        5"&IM.INDEX.GETFNMODERN +)        "&IM.INDEX.GETFNMODERN +   % +*          >   7        "%IM.INDEX.GETFNMODERN +  $  "&IM.INDEX.GETFNMODERN +  $    & !"&IM.INDEX.GETFNMODERN +  $                 "&IM.INDEX.GETFNMODERN +  $   +  +  $ ' $"    +^$ /  n "(IM.INDEX.GETFNMODERN + $#  0 $  *'  N$-  2"&IM.INDEX.GETFNMODERN +  $           $C    Y",IM.INDEX.GETFNMODERN +  $  !   Q$      @ ! _  F$X      2    = "%IM.INDEX.GETFNCLASSIC +  $             '#$ +$   "&IM.INDEX.GETFNMODERN +!  $               2$2    *-" *IM.INDEX.GETFNMODERN +  $ ,   3# &gh '& I"7IM.INDEX.GETFNMODERN +$(   0  R $.  +9  .X$!: B"8IM.INDEX.GETFNMODERN +$d'" +;IM.INDEX.GETFNMODERN +$i      " +;IM.INDEX.GETFNMODERN +$%f2"7IM.INDEX.GETFNMODERN +$(   +  +   @"7IM.INDEX.GETFNMODERN +$(       "  Z"8IM.INDEX.GETFNMODERN +$'    &   !   +@"7IM.INDEX.GETFNMODERN +$&  "  'A"7IM.INDEX.GETFNMODERN +$'    L  E $   $ $&8   " <IM.INDEX.GETFNMODERN +$.      _F +$ q &$!  " <IM.INDEX.GETFN$.      " <IM.INDEX.GETFNMODERN +$.      " <IM.INDEX.GETFNMODERN +$Q  &  " *IM.INDEX.GETFNMODERN +  +  +  +  +$o  +,  +5 $     # $   6 $$  T (! HRULE.GETFNMODERN +&@V2 11'Q!'& ' ',1; o 'w +} ~        -   'q ++ ;'" (IM.INDEX.GETFN   +  +$   +\  + +H$  +)   + # "!IM.INDEX.GETFN!IM.INDEX.GETFNMODERN +  +  +$ -  +  +$/-83"$IM.INDEX.GETFNMODERN +  +  +$  +n$DF"$IM.INDEX.GETFN   +  +$  + c+"$IM.INDEX.GETFN  $  +S  +J# ""IM.INDEX.GETFN  +  +$   +O  + !  +E  +%      &$$ " &IM.INDEX.GETFN   +  +$W"$"  +55  +$#"%IM.INDEX.GETFN  +    +  +    +$   +   + M  +"  +3  +(  +  +$ & +  $  +  + +   " (IM.INDEX.GETFN  +    +  +    +$ B S" &IM.INDEX.GETFN  +  +$   +"6"*IM.INDEX.GETFN   +  +$>"  +  +`$p 3  "$IM.INDEX.GETFN  +   +   +   +   +   + $  +; +   +h"  $ + f" &IM.INDEX.GETFN  +  + +'IM.INDEX.GETFN  +  + (IM.INDEX.GETFN  +  +$=  " +'IM.INDEX.GETFN  +  +$   +Y r2$  +7    / # ""IM.INDEX.GETFN   +  +$ (   +$ L B  +D "$  + /      +\"$IM.INDEX.GETFN  +  +$ ++"#IM.INDEX.GETFN  +  +  +  +$ $  + +  +$    + )   +    + ##  "%IM.INDEX.GETFN  +  +$  +w" )IM.INDEX.GETFN   +  +$  " )IM.INDEX.GETFN  +  +$  +  " &IM.INDEX.GETFN  +  +$  +" +'IM.INDEX.GETFN  +  +$  +{"+IM.INDEX.GETFN  +  +$   "-IM.INDEX.GETFN  +  +$  +" +'IM.INDEX.GETFN  +  +$  +"#IM.INDEX.GETFN  +  +$f '  +  +    +  +\" )IM.INDEX.GETFN  +  +$   +$  +:    + "IM.INDEX.GETFN $$ 8"IM.INDEX.GETFN  +  +$  +  +J"IM.INDEX.GETFN  +  +$  +  + '""IM.INDEX.GETFN  +  +$  +  +*" &IM.INDEX.GETFNMODERN +  +  +$  +l +$ G#  " &IM.INDEX.GETFNMODERN + $($ z C5H  $X =   $  + K  > / +?I0o =I2"1+333/ ..53 E>R C          G$ #6>$] s"$IM.INDEX.GETFNMODERN +   + +  +$   + `# &4' .'$ ?" $8  +  + ",IM.INDEX.GETFN  $   R   " )IM.INDEX.GETFNMODERN + $ + # k" +IM.INDEX.GETFNMODERN + $ c  + +  +&   '!!'  # &E2Q%6   ' ! ' $  +    !'49 #"  &"$IM.INDEX.GETFNMODERN +!$7    C  U!       $#a        $ ,  - ""IM.INDEX.GETFN!${   !        3       ) (         $3 6  "%IM.INDEX.GETFN $   !      3       ) 7            $ ^"&IM.INDEX.GETFN&IM.INDEX.GETFN $s   !        *             4   $5  !   $J   #       #2'$KR$ ~c&  +&" )IM.INDEX.GETFNMODERN +' $u  +2  +D  $    # $  6 $$  W*  +!. HRULE.GETFNMODERN +&J  A e'V "*IM.INDEX.GETFNTITAN +% $ e      $  ( $      " +*IM.INDEX.GETFN  $  "  0   $  0 +    +    " +IM.INDEX.GETFNMODERN +  $  ,  + +     &   $;" +(IM.INDEX.GETFNMODERN +  $   i 5    $  $f! +$ E3  " *IM.INDEX.GETFN  $        ")IM.INDEX.GETFNTITAN + $+  41" )IM.INDEX.GETFN  $    =  $  "*IM.INDEX.GETFNTITAN + $&  " )IM.INDEX.GETFN  $    " 'IM.INDEX.GETFN  $       ",IM.INDEX.GETFN  $ +    + [          ! HRULE.GETFNMODERN +&" +(IM.INDEX.GETFN ;  +$   9$ R  @$ G  !   I$ C ' \  8  s $ g /f $ +  + ^ + 4)5 , +  + Y 9 +  + <   +    & +  + +$    $ { $'  "'IM.INDEX.GETFNTITAN + $ j " +(IM.INDEX.GETFN  $     " *IM.INDEX.GETFN $ +<       i e 8"-IM.INDEX.GETFNMODERN + $  +  " -IM.INDEX.GETFN $     + % !  "" *IM.INDEX.GETFNMODERN + +  $; +   M"'IM.INDEX.GETFNCLASSIC +  $'  ".IM.INDEX.GETFNCLASSIC +  $ +  #  &q q$/''$9't **{` +G'&',"#'1*'q'I" +IM.INDEX.GETFNMODERN + $  h rfn G".IM.INDEX.GETFN $"    ="4IM.INDEX.GETFN $. #"  &"#IM.INDEX.GETFNMODERN + $$  "$IM.INDEX.GETFNMODERN + $$  "$IM.INDEX.GETFNMODERN + $ ! *   & 5  < " 'IM.INDEX.GETFN   +  +  +$  +&      +   +   +    Q %  "    +  +  +$ Z" 'IM.INDEX.GETFN  +  +  +  +$-    +  + v"&IM.INDEX.GETFN  $_#  & +[<'  m +D'  8D>'] 7  '' <U G'L" *IM.INDEX.GETFNMODERN + $+  $"#1A +" (IM.INDEX.GETFNMODERN + +$ N Y; $ '   Y$ z \ No newline at end of file diff --git a/docs/porter-irm/vol2/18-COMPILER.INTERPRESS b/docs/porter-irm/vol2/18-COMPILER.INTERPRESS new file mode 100644 index 00000000..f316233d Binary files /dev/null and b/docs/porter-irm/vol2/18-COMPILER.INTERPRESS differ diff --git a/docs/porter-irm/vol2/18-COMPILER.TEDIT b/docs/porter-irm/vol2/18-COMPILER.TEDIT new file mode 100644 index 00000000..7aea8e0d --- /dev/null +++ b/docs/porter-irm/vol2/18-COMPILER.TEDIT @@ -0,0 +1,94 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 18. COMPILER 1 18. COMPILER 1 "18"18. COMPILER 6 The compiler is contained in the standard Interlisp system. It may be used to compile functions defined in your Interlisp system, or to compile definitions stored in a file. The resulting compiled code may be stored as it is compiled, so as to be available for immediate use, or it may be written onto a file for subsequent loading. The most common way to use the compiler is to use one of the file package functions, such as MAKEFILE (Chapter 17), which automatically updates source files, and produces compiled versions. However, it is also possible to compile individual functions defined in your Interlisp system, by directly calling the compiler using functions such as COMPILE. No matter how the compiler is called, the function COMPSET is called which asks you certain questions concerning the compilation. (COMPSET sets the free variables LAPFLG, STRF, SVFLG, LCFIL and LSTFIL which determine various modes of operation.) Those that can be answered "yes" or "no" can be answered with YES, Y, or T for "yes"; and NO, N, or NIL for "no". The questions are: LISTING? This asks whether to generate a listing of the compiled code. The LAP and machine code are usually not of interest but can be helpful in debugging macros. Possible answers are: 1 Prints output of pass 1, the LAP macro code 2 Prints output of pass 2, the machine code YES Prints output of both passes NO Prints no listings The variable LAPFLG is set to the answer. FILE: This question (which only appears if the answer to LISTING? is affirmative) ask where the compiled code listing(s) should be written. Answering T will print the listings at the terminal. The variable LSTFIL is set to the answer. REDEFINE? This question asks whether the functions compiled should be redefined to their compiled definitions. If this is answered YES, the compiled code is stored and the function definition changed, otherwise the function definition remains unchanged. The compiler does not respect the value of DFNFLG (Chapter 10) when it redefines functions to their compiled definitions. Therefore, if you set DFNFLG to PROP to completely avoid inadvertantly redefining something in your running system, you must not answer YES to this question. The variable STRF is set to T (if this is answered YES) or NIL. SAVE EXPRS? This question asks whether the original defining EXPRs of functions should be saved. If answered YES, then before redefining a function to its compiled definition, the EXPR definition is saved on the property list of the function name. Otherwise they are discarded. It is very useful to save the EXPR definitions, just in case the compiled function needs to be changed. The editing functions will retrieve this saved definition if it exists, rather than reading from a source file. The variable SVFLG is set to T (if this is answered YES) or NIL. OUTPUT FILE? This question asks whether (and where) the compiled definitions should be written into a file for later loading. If you answer with the name of a file, that file will be used. If you answer Y or YES, you will be asked the name of the file. If the file named is already open, it will continue to be used. If you answer T or TTY:, the output will be typed on the teletype (not particularly useful). If you answer N, NO, or NIL, output will not be done. The variable LCFIL is set to the name of the file. To make answering these questions easier, there are four other possible answers to the LISTING? question, which specify common compiling modes: S Same as last setting. Uses the same answers to compiler questions as given for the last compilation. F Compile to File, without redefining functions. ST STore new definitions, saving EXPR definitions. STF STore new definitions; Forget EXPR definitions. Implicit in these answers are the answers to the questions on disposition of compiled code and EXPR definitions, so the questions REDEFINE? and SAVE EXPRS? would not be asked if these answers were given. OUTPUT FILE? would still be asked, however. For example: COMPILE((FACT FACT1 FACT2)) LISTING? ST OUTPUT FILE? FACT.DCOM (FACT COMPILING) . . (FACT REDEFINED) . . (FACT2 REDEFINED) (FACT FACT1 FACT2) This process caused the functions FACT, FACT1, and FACT2 to be compiled, redefined, and the compiled definitions also written on the file FACT.DCOM for subsequent loading. Compiler(COMPILER NIL Compiler NIL ("18") 2 SUBNAME PRINTOUT SUBTEXT printout) Printout 1 In Interlisp-D, for each function FN compiled, whether by TCOMPL, RECOMPILE, or COMPILE, the compiler prints: (FN (ARG1 ... ARGN) (uses: VAR1 ... VARN) (calls: FN1 ... FNN)) The message is printed at the beginning of the second pass of the compilation of FN. (ARG1 ... ARGN) is the list of arguments to FN; following uses: are the free variables referenced or set in FN (not including global variables); following calls: are the undefined functions called within FN. If the compilation of FN causes the generation of one or more auxilary functions, a compiler message will be printed for these functions before the message for FN, e.g., (FOOA0027 (X) (uses: XX)) (FOO (A B)) When compiling a block, the compiler first prints (BLKNAME BLKFN1 BLKFN2 ...). Then the normal message is printed for the entire block. The names of the arguments to the block are generated by suffixing # and a number to the block name, e.g., (FOOBLOCK (FOOBLOCK#0 FOOBLOCK#1) FREE-VARIABLES). Then a message is printed for each entry to the block. In addition to the above output, both RECOMPILE and BRECOMPILE print the name of each function that is being copied from the old compiled file to the new compiled file. The normal compiler message is printed for each function that is actually compiled. The compiler prints out error messages when it encounters problems compiling a function. For example: ----- In BAZ: ***** (BAZ - illegal RETURN) ----- The above error message indicates that an illegal RETURN compiler error occurred while trying to compile the function BAZ. Some compiler errors cause the compilation to terminate, producing nothing; however, there are other compiler errors which do not stop compilation. The compiler error messages are described in the last section of this chapter. Compiler printout and error messages go to the file COUTFILE, initially T. COUTFILE can also be set to the name of a file opened for output, in which case all compiler printout will go to COUTFILE, i.e. the compiler will compile "silently." However, any error messages will be printed to both COUTFILE as well as T. Global(COMPILER NIL Compiler NIL ("18") 3 SUBNAME GLOBAL% VARIABLES SUBTEXT global% variables) Variables(VARIABLES NIL Variables NIL ("18") 3 SUBNAME GLOBAL SUBTEXT global) 1 Variables that appear on the list GLOBALVARS, or have the property GLOBALVAR with value T, or are declared with the GLOBALVARS file package command, are called global variables. Such variables are always accessed through their top level value when they are used freely in a compiled function. In other words, a reference to the value of a global variable is equivalent to calling GETTOPVAL on the variable, regardless of whether or not it is bound in the current access chain. Similarly, (SETQ VARIABLE VALUE) will compile as (SETTOPVAL (QUOTE VARIABLE) VALUE). All system parameters, unless otherwise specified, are declared as global variables. Thus, rebinding these variables in a deep bound system (like Interlisp-D) will not affect the behavior of the system: instead, the variables must be reset to their new values, and if they are to be restored to their original values, reset again. For example, you might write (SETQ GLOBALVARIABLE NEWVALUE) FORM (SETQ GLOBALVARIABLE OLDVALUE) In this case, if an error occurred during the evaluation of FORM, or a Control-D was typed, the global variable would not be restored to its original value. The function RESETVAR provides a convenient way of resetting global variables in such a way that their values are restored even if an error occurred or Control-D is typed. Note: The variables that a given function accesses as global variables can be determined by using the function CALLS. Local Variables(VARIABLES NIL Variables NIL ("18") 4 SUBNAME LOCAL SUBTEXT local)(COMPILER NIL Compiler NIL ("18") 4 SUBNAME LOCAL% VARIABLES SUBTEXT local% variables) and Special Variables(VARIABLES NIL Variables NIL ("18") 4 SUBNAME SPECIAL SUBTEXT special)(COMPILER NIL Compiler NIL ("18") 4 SUBNAME SPECIAL% VARIABLES SUBTEXT special% variables) 1 In normal compiled and interpreted code, all variable bindings are accessible by lower level functions because the variable's name is associated with its value. We call such variables special variables, or specvars. As mentioned earlier, the block compiler normally does not associate names with variable values. Such unnamed variables are not accessible from outside the function which binds them and are therefore local to that function. We call such unnamed variables local variables, or localvars. The time economies of local variables can be achieved without block compiling by use of declarations. Using local variables will increase the speed of compiled code; the price is the work of writing the necessary specvar declarations for those variables which need to be accessed from outside the block. LOCALVARS and SPECVARS are variables that affect compilation. During regular compilation, SPECVARS is normally T, and LOCALVARS is NIL or a list. This configuration causes all variables bound in the functions being compiled to be treated as special except those that appear on LOCALVARS. During block compilation, LOCALVARS is normally T and SPECVARS is NIL or a list. All variables are then treated as local except those that appear on SPECVARS. Declarations to set LOCALVARS and SPECVARS to other values, and therefore affect how variables are treated, may be used at several levels in the compilation process with varying scope. 1. The declarations may be included in the filecoms of a file, by using the LOCALVARS and SPECVARS file package commands. The scope of the declaration is then the entire file: ... (LOCALVARS . T) (SPECVARS X Y) ... 2. The declarations may be included in block declarations; the scope is then the block, e.g., (BLOCKS ((FOOBLOCK FOO FIE (SPECVARS . T) (LOCALVARS X))) 3. The declarations may also appear in individual functions, or in PROG's or LAMBDA's within a function, using the DECLARE function. In this case, the scope of the declaration is the function or the PROG or LAMBDA in which it appears. LOCALVARS and SPECVARS declarations must appear immediately after the variable list in the function, PROG, or LAMBDA, but intervening comments are permitted. For example: (DEFINEQ ((FOO (LAMBDA (X Y) (DECLARE (LOCALVARS Y)) (PROG (X Y Z) (DECLARE (LOCALVARS X)) ... ] If the above function is compiled (non-block), the outer X will be special, the X bound in the PROG will be local, and both bindings of Y will be local. Declarations for LOCALVARS and SPECVARS can be used in two ways: either to cause variables to be treated the same whether the function(s) are block compiled or compiled normally, or to affect one compilation mode while not affecting the default in the other mode. For example: (LAMBDA (X Y) (DECLARE (SPECVARS . T)) (PROG (Z) ... ] will cause X, Y, and Z to be specvars for both block and normal compilation while (LAMBDA (X Y) (DECLARE (SPECVARS X)) ... ] will make X a specvar when block compiling, but when regular compiling the declaration will have no effect, because the default value of specvars would be T, and therefore both X and Y will be specvars by default. Although LOCALVARS and SPECVARS declarations have the same form as other components of block declarations such as (LINKFNS . T), their operation is somewhat different because the two variables are not independent. (SPECVARS . T) will cause SPECVARS to be set to T, and LOCALVARS to be set to NIL. (SPECVARS V1 V2 ...) will have no effect if the value of SPECVARS is T, but if it is a list (or NIL), SPECVARS will be set to the union of its prior value and (V1 V2 ...). The operation of LOCALVARS is analogous. Thus, to affect both modes of compilation one of the two (LOCALVARS or SPECVARS) must be declared T before specifying a list for the other. Note: The variables that a given function binds as local variables or accesses as special variables can be determined by using the function CALLS. Note: LOCALVARS and SPECVARS declarations affect the compilation of local variables within a function, but the arguments to functions are always accessible as specvars. This can be changed by redefining the following function: (DASSEM.SAVELOCALVARS(DASSEM.SAVELOCALVARS (Function) NIL NIL ("18") 5) FN) [Function] This function is called by the compiler to determine whether argument information for FN should be written on the compiled file for FN. If it returns NIL, the argument information is not saved, and the function is stored with arguments U, V, W, etc instead of the originals. Initially, DASSEM.SAVELOCALVARS is defined to return T. (MOVD 'NILL 'DASSEM.SAVELOCALVARS) causes the compiler to retain no local variable or argument names. Alternatively, DASSEM.SAVELOCALVARS could be redefined as a more complex predicate, to allow finer discrimination. Constants(COMPILER NIL Compiler NIL ("18") 5 SUBNAME CONSTANTS SUBTEXT constants) 1 Interlisp allows the expression of constructions which are intended to be description of their constant values. The following functions are used to define constant values. The function SELECTC provides a mechanism for comparing a value to a number of constants. (CONSTANT(CONSTANT (Function) NIL NIL ("18") 5) X) [Function] This function enables you to define that the expression X should be treated as a "constant" value. When CONSTANT is interpreted, X is evaluted each time it is encountered. If the CONSTANT form is compiled, however, the expression will be evaluated only once. If the value of X has a readable print name, then it will be evaluated at compile-time, and the value will be saved as a literal in the compiled function's definition, as if (QUOTE VALUE-OF-EXPRESSION) had appeared instead of (CONSTANT EXPRESSION). If the value of X does not have a readable print name, then the expression X itself will be saved with the function, and it will be evaluated when the function is first loaded. The value will then be stored in the function's literals, and will be retrieved on future references. If a program needed a list of 30 NILs, you could specify (CONSTANT (to 30 collect NIL)) instead of (QUOTE (NIL NIL ...)). The former is more concise and displays the important parameter much more directly than the latter. CONSTANT can also be used to denote values that cannot be quoted directly, such as (CONSTANT (PACK NIL)), (CONSTANT (ARRAY 10)). It is also useful to parameterize quantities that are constant at run time but may differ at compile time, e.g., (CONSTANT BITSPERWORD) in a program is exactly equivalent to 36, if the variable BITSPERWORD is bound to 36 when the CONSTANT expression is evaluated at compile time. Whereas the function CONSTANT attempts to evaluate the expression as soon as possible (compile-time, load-time, or first-run-time), other options are available, using the folowing two function: (LOADTIMECONSTANT(LOADTIMECONSTANT (Function) NIL NIL ("18") 6) X) [Function] Similar to CONSTANT, except that the evaluation of X is deferred until the compiled code for the containing function is loaded in. For example, (LOADTIMECONSTANT (DATE)) will return the date the code was loaded. If LOADTIMECONSTANT is interpreted, it merely returns the value of X. (DEFERREDCONSTANT(DEFERREDCONSTANT (Function) NIL NIL ("18") 6) X) [Function] Similar to CONSTANT, except that the evaluation of X is always deferred until the compiled function is first run. This is useful when the storage for the constant is excessive so that it shouldn't be allocated until (unless) the function is actually invoked. If DEFERREDCONSTANT is interpreted, it merely returns the value of X. (CONSTANTS(CONSTANTS (Function) NIL NIL ("18") 6) VAR1 VAR2 ... VARN) [NLambda NoSpread Function] Defines VAR1, ... VARN (unevaluated) to be compile-time constants. Whenever the compiler encounters a (free) reference to one of these constants, it will compile the form (CONSTANT VARi) instead. If VARi is a list of the form (VAR FORM), a free reference to the variable will compile as (CONSTANT FORM). The compiler prints a warning if user code attempts to bind a variable previously declared as a constant. Constants can be saved using the CONSTANTS file package command. Compiling(COMPILING NIL Compiling NIL ("18") 6 SUBNAME FUNCTION% CALLS SUBTEXT function% calls) Function Calls 1 When compiling the call to a function, the compiler must know the type of the function, to determine how the arguments should be prepared (evaluated/unevaluated, spread/nospread). There are three seperate cases: lambda, nlambda spread, and nlambda nospread functions. To determine which of these three cases is appropriate, the compiler will first look for a definition among the functions in the file that is being compiled. The function can be defined anywhere in any of the files given as arguments to BCOMPL, TCOMPL, BRECOMPILE or RECOMPILE. If the function is not contained in the file, the compiler will look for other information in the variables NLAMA, NLAML, and LAMS, which can be set by you: NLAMA (NLAMA% (Variable) NIL NIL ("18") 7) [Variable] (for NLAMbda Atoms) A list of functions to be treated as nlambda nospread functions by the compiler. NLAML(NLAML (Variable) NIL NIL ("18") 7) [Variable] (for NLAMbda List) A list of functions to be treated as nlambda spread functions by the compiler. LAMS(LAMS (Variable) NIL NIL ("18") 7) [Variable] A list of functions to be treated as lambda functions by the compiler. Note that including functions on LAMS is only necessary to override in-core nlambda definitions, since in the absence of other information, the compiler assumes the function is a lambda. If the function is not contained in a file, or on the lists NLAMA, NLAML, or LAMS, the compiler will look for a current definition in the Interlisp system, and use its type. If there is no current definition, next COMPILEUSERFN is called: COMPILEUSERFN(COMPILEUSERFN (Variable) NIL NIL ("18") 7) [Variable] When compiling a function call, if the function type cannot be found by looking in files, the variables NLAMA, NLAML, or LAMS, or at a current definition, then if the value of COMPILEUSERFN is not NIL, the compiler calls (the value of) COMPILEUSERFN giving it as arguments CDR of the form and the form itself, i.e., the compiler does (APPLY* COMPILEUSERFN (CDR FORM) FORM). If a non-NIL value is returned, it is compiled instead of FORM. If NIL is returned, the compiler compiles the original expression as a call to a lambda spread that is not yet defined. COMPILEUSERFN is only called when the compiler encounters a list CAR of which is not the name of a defined function. You can instruct the compiler about how to compile other data types via COMPILETYPELST. CLISP uses COMPILEUSERFN to tell the compiler how to compile iterative statements, IF-THEN-ELSE statements, and pattern match constructs. If the compiler cannot determine the function type by any of the means above, it assumes that the function is a lambda function, and its arguments are to be evaluated. If there are nlambda functions called from the functions being compiled, and they are only defined in a separate file, they must be included on NLAMA or NLAML, or the compiler will incorrectly assume that their arguments are to be evaluated, and compile the calling function correspondingly. Note that this is only necessary if the compiler does not "know" about the function. If the function is defined at compile time, or is handled via a macro, or is contained in the same group of files as the functions that call it, the compiler will automatically handle calls to that function correctly. FUNCTION and Functional Arguments 1 Compiling(COMPILING NIL Compiling NIL ("18") 7 SUBNAME FUNCTION% FUNCTION SUBTEXT FUNCTION% function)(COMPILING NIL Compiling NIL ("18") 7 SUBNAME FUNCTIONAL% ARGUMENTS SUBTEXT functional% arguments) the function FUNCTION may involve creating and compiling a seperate "auxiliary function", which will be called at run time. An auxiliary function is named by attaching a GENSYM to the end of the name of the function in which they appear, e.g., FOOA0003. For example, suppose FOO is defined as (LAMBDA (X) ... (FOO1 X (FUNCTION ...)) ...) and compiled. When FOO is run, FOO1 will be called with two arguments, X, and FOOA000N and FOO1 will call FOOA000N each time it uses its functional argument. Compiling FUNCTION will not create an auxiliary function if it is a functional argument to a function that compiles open, such as most of the mapping functions (MAPCAR, MAPLIST, etc.). Note that a considerable savings in time could be achieved by making FOO1 compile open via a computed macro, e.g. (PUTPROP 'FOO1 'MACRO '(Z (LIST (SUBST (CADADR Z) (QUOTE FN) DEF) (CAR Z))) DEF is the definition of FOO1 as a function of just its first argument, and FN is the name used for its functional argument in its definition. In this case, (FOO1 X (FUNCTION ...)) would compile as an expression, containing the argument to FUNCTION as an open LAMBDA expression. Thus you save not only the function call to FOO1, but also each of the function calls to its functional argument. For example, if FOO1 operates on a list of length ten, eleven function calls will be saved. Of course, this savings in time costs space, and you must decide which is more important. Open Functions 1 When a function is called from a compiled function, a system routine is invoked that sets up the parameter and control push lists as necessary for variable bindings and return information. If the amount of time spent inside the function is small, this function calling time will be a significant percentage of the total time required to use the function. Therefore, many "small" functions, e.g., CAR, CDR, EQ, NOT, CONS are always compiled "open", i.e., they do not result in a function call. Other larger functions such as PROG, SELECTQ, MAPC, etc. are compiled open because they are frequently used. You can make other functions compile open via MACRO definitions. You can also affect the compiled code via COMPILEUSERFN and COMPILETYPELST. COMPILETYPELST 1 Most of the compiler's mechanism deals with how to handle forms (lists) and variables (literal atoms). You can affect the compiler's behaviour with respect to lists and literal atoms in a number of ways, e.g. macros, declarations, COMPILEUSERFN, etc. COMPILETYPELST allows you to tell the compiler what to do when it encounters a data type other than a list or an atom. It is the facility in the compiler that corresponds to DEFEVAL for the interpreter. COMPILETYPELST(COMPILETYPELST (Variable) NIL NIL ("18") 8) [Variable] A list of elements of the form (TYPENAME . FUNCTION). Whenever the compiler encounters a datum that is not a list and not an atom (or a number) in a context where the datum is being evaluated, the type name of the datum is looked up on COMPILETYPELST. If an entry appears CAR of which is equal to the type name, CDR of that entry is applied to the datum. If the value returned by this application is not EQ to the datum, then that value is compiled instead. If the value is EQ to the datum, or if there is no entry on COMPILETYPELST for this type name, the compiler simply compiles the datum as (QUOTE DATUM). Compiling(COMPILING NIL Compiling NIL ("18") 9 SUBNAME CLISP SUBTEXT CLISP) CLISP(CLISP NIL NIL NIL ("18") 9 SUBNAME COMPILING SUBTEXT compiling) 1 Since the compiler does not know about CLISP, in order to compile functions containing CLISP constructs, the definitions must first be DWIMIFYed. You can automate this process in several ways: 1. If the variable DWIMIFYCOMPFLG is T, the compiler will always DWIMIFY expressions before compiling them. DWIMIFYCOMPFLG is initially NIL. 2. If a file has the property FILETYPE with value CLISP on its property list, TCOMPL, BCOMPL, RECOMPILE, and BRECOMPILE will operate as though DWIMIFYCOMPFLG is T and DWIMIFY all expressions before compiling. 3. If the function definition has a local CLISP declaration, including a null declaration, i.e., just (CLISP:), the definition will be automatically DWIMIFYed before compiling. Note: COMPILEUSERFN is defined to call DWIMIFY on iterative statements, IF-THEN statements, and fetch, replace, and match expressions, i.e., any CLISP construct which can be recognized by its CAR of form. Thus, if the only CLISP constructs in a function appear inside of iterative statements, IF statements, etc., the function does not have to be dwimified before compiling. If DWIMIFY is ever unsuccessful in processing a CLISP expression, it will print the error message UNABLE TO DWIMIFY followed by the expression, and go into a break unless DWIMESSGAG=T. In this case, the expression is just compiled as is, i.e. as though CLISP had not been enabled. You can exit the break in one of these ways: 1. Type OK to the break, which will cause the compiler to try again, e.g. you could define some missing records while in the break, and then continue 2. Type , which will cause the compiler to simply compile the expression as is, i.e. as though CLISP had not been enabled in the first place 3. Return an expression to be compiled in its place by using the RETURN break command. Note: TCOMPL, BCOMPL, RECOMPILE, and BRECOMPILE all scan the entire file before doing any compiling, and take note of the names of all functions that are defined in the file as well as the names of all variables that are set by adding them to NOFIXFNSLST and NOFIXVARSLST, respectively. Thus, if a function is not currently defined, but is defined in the file being compiled, when DWIMIFY is called before compiling, it will not attempt to interpret the function name as CLISP when it appears as CAR of a form. DWIMIFY also takes into account variables that have been declared to be LOCALVARS, or SPECVARS, either via block declarations or DECLARE expressions in the function being compiled, and does not attempt spelling correction on these variables. The declaration USEDFREE may also be used to declare variables simply used freely in a function. These variables will also be left alone by DWIMIFY. Finally, NOSPELLFLG is reset to T when compiling functions from a file (as opposed to from their in-core definition) so as to suppress spelling correction. Compiler(COMPILER NIL Compiler NIL ("18") 9 SUBNAME FUNCTIONS SUBTEXT functions) Functions 1 Normally, the compiler is envoked through file package commands that keep track of the state of functions, and manage a set of files, such as MAKEFILE. However, it is also possible to explicitly call the compiler using one of a number of functions. Functions may be compiled from in-core definitions (via COMPILE), or from definitions in files (TCOMPL), or from a combination of in-core and file definitions (RECOMPILE). TCOMPL and RECOMPILE produce "compiled" files. Compiled files usually have the same name as the symbolic file they were made from, suffixed with DCOM (the compiled file extension is stored as the value of the variable COMPILE.EXT). The file name is constructed from the name field only, e.g., (TCOMPL 'FOO.TEM;3) produces FOO.DCOM on the connected directory. The version number will be the standard default. A "compiled file" contains the same expressions as the original symbolic file, except for the following: 1. A special FILECREATED expression appears at the front of the file which contains information used by the file package, and which causes the message COMPILED ON DATE to be printed when the file is loaded (the actual string printed is the value of COMPILEHEADER). 2. Every DEFINEQ in the symbolic file is replaced by the corresponding compiled definitions in the compiled file. 3. Expressions following a DONTCOPY tag inside of a DECLARE: that appears in the symbolic file are not copied to the compiled file. The compiled definitions appear at the front of the compiled file, i.e., before the other expressions in the symbolic file, regardless of where they appear in the symbolic file. The only exceptions are expressions that follow a FIRST tag inside of a DECLARE:. This "compiled" file can be loaded into any Interlisp system with LOAD. Note: When a function is compiled from its in-core definition (as opposed to being compiled from a definition in a file), and the function has been modified by BREAK, TRACE, BREAKIN, or ADVISE, it is first restored to its original state, and a message is printed out, e.g., FOO UNBROKEN. If the function is not defined by an expr definition, the value of the function's EXPR property is used for the compilation, if there is one. If there is no EXPR property, and the compilation is being performed by RECOMPILE, the definition of the function is obtained from the file (using LOADFNS). Otherwise, the compiler prints (FN NOT COMPILEABLE), and goes on to the next function. (COMPILE(COMPILE (Function) NIL NIL ("18") 10) X FLG) [Function] X is a list of functions (if atomic, (LIST X) is used). COMPILE first asks the standard compiler questions, and then compiles each function on X, using its in-core definition. Returns X. If compiled definitions are being written to a file, the file is closed unless FLG=T. (COMPILE1(COMPILE1 (Function) NIL NIL ("18") 10) FN DEF %) [Function] Compiles DEF, redefining FN if STRF=T (STRF is one of the variables set by COMPSET). COMPILE1 is used by COMPILE, TCOMPL, and RECOMPILE. If DWIMIFYCOMPFLG is T, or DEF contains a CLISP declaration, DEF is dwimified before compiling. (TCOMPL(TCOMPL (Function) NIL NIL ("18") 10) FILES) [Function] TCOMPL is used to "compile files"; given a symbolic LOAD file (e.g., one created by MAKEFILE), it produces a "compiled file". FILES is a list of symbolic files to be compiled (if atomic, (LIST FILES) is used). TCOMPL asks the standard compiler questions, except for "OUTPUT FILE:". The output from the compilation of each symbolic file is written on a file of the same name suffixed with DCOM, e.g., (TCOMPL '(SYM1 SYM2)) produces two files, SYM1.DCOM and SYM2.DCOM. TCOMPL processes the files one at a time, reading in the entire file. For each FILECREATED expression, the list of functions that were marked as changed by the file package is noted, and the FILECREATED expression is written onto the output file. For each DEFINEQ expression, TCOMPL adds any nlambda functions defined in the DEFINEQ to NLAMA or NLAML, and adds lambda functions to LAMS, so that calls to these functions will be compiled correctly. NLAMA, NLAML, and LAMS are rebound to their top level values (using RESETVAR) by all of the compiling functions, so that any additions to these lists while inside of these functions will not propagate outside. Expressions beginning with DECLARE: are processed specially. All other expressions are collected to be subsequently written onto the output file. After processing the file in this fashion, TCOMPL compiles each function, except for those functions which appear on the list DONTCOMPILEFNS (initially NIL), and writes the compiled definition onto the output file. TCOMPL then writes onto the output file the other expressions found in the symbolic file. DONTCOMPILEFNS might be used for functions that compile open, since their definitions would be superfluous when operating with the compiled file. Note that DONTCOMPILEFNS can be set via block declarations. Note: If the rootname of a file has the property FILETYPE with value CLISP, or value a list containing CLISP, TCOMPL rebinds DWIMIFYCOMPFLG to T while compiling the functions on FILE, so the compiler will DWIMIFY all expressions before compiling them. TCOMPL returns a list of the names of the output files. All files are properly terminated and closed. If the compilation of any file is aborted via an error or Control-D, all files are properly closed, and the (partially complete) compiled file is deleted. (RECOMPILE(RECOMPILE (Function) NIL NIL ("18") 11) PFILE CFILE FNS) [Function] The purpose of RECOMPILE is to allow you to update a compiled file without recompiling every function in the file. RECOMPILE does this by using the results of a previous compilation. It produces a compiled file similar to one that would have been produced by TCOMPL, but at a considerable savings in time by only compiling selected functions, and copying the compiled definitions for the remainder of the functions in the file from an earlier TCOMPL or RECOMPILE file. PFILE is the name of the Pretty file (source file) to be compiled; CFILE is the name of the Compiled file containing compiled definitions that may be copied. FNS indicates which functions in PFILE are to be recompiled, e.g., have been changed or defined for the first time since CFILE was made. Note that PFILE, not FNS, drives RECOMPILE. RECOMPILE asks the standard compiler questions, except for "OUTPUT FILE:". As with TCOMPL, the output automatically goes to PFILE.DCOM. RECOMPILE processes PFILE the same as does TCOMPL except that DEFINEQ expressions are not actually read into core. Instead, RECOMPILE uses the filemap to obtain a list of the functions contained in PFILE. The filemap enables RECOMPILE to skip over the DEFINEQs in the file by simply resetting the file pointer, so that in most cases the scan of the symbolic file is very fast (the only processing required is the reading of the non-DEFINEQs and the processing of the DECLARE: expressions as with TCOMPL). A map is built if the symbolic file does not already contain one, for example if it was written in an earlier system, or with BUILDMAPFLG=NIL. After this initial scan of PFILE, RECOMPILE then processes the functions defined in the file. For each function in PFILE, RECOMPILE determines whether or not the function is to be (re)compiled. Functions that are members of DONTCOMPILEFNS are simply ignored. Otherwise, a function is recompiled if : 1. FNS is a list and the function is a member of that list 2. FNS=T or EXPRS and the function is defined by an expr definition 3. FNS=CHANGES and the function is marked as having been changed in the FILECREATED expression in PFILE 4. FNS=ALL If a function is not to be recompiled, RECOMPILE obtains its compiled definition from CFILE, and copies it (and all generated subfunctions) to the output file, PFILE.DCOM. If the function does not appear on CFILE, RECOMPILE simply recompiles it. Finally, after processing all functions, RECOMPILE writes out all other expressions that were collected in the prescan of PFILE. Note: If FNS=ALL, CFILE is superfluous, and does not have to be specified. This option may be used to compile a symbolic file that has never been compiled before, but which has already been loaded (since using TCOMPL would require reading the file in a second time). If CFILE=NIL, PFILE.DCOM (the old version of the output file) is used for copying from. If both FNS and CFILE are NIL, FNS is set to the value of RECOMPILEDEFAULT, which is initially CHANGES. Thus you can perform his edits, dump the file, and then simply (RECOMPILE 'FILE) to update the compiled file. The value of RECOMPILE is the file name of the new compiled file, PFILE.DCOM. If RECOMPILE is aborted due to an error or Control-D, the new (partially complete) compiled file will be closed and deleted. RECOMPILE is designed to allow you to conveniently and efficiently update a compiled file, even when the corresponding symbolic file has not been (completely) loaded. For example, you can perform a LOADFROM to "notice" a symbolic file, edit the functions he wants to change (the editor will automatically load those functions not already loaded), call MAKEFILE to update the symbolic file (MAKEFILE will copy the unchanged functions from the old symbolic file), and then perform (RECOMPILE PFILE). Note: Since PRETTYDEF automatically outputs a suitable DECLARE: expression to indicate which functions in the file (if any) are defined as NLAMBDAs, calls to these functions will be handled correctly, even though the NLAMBDA functions themselves may never be loaded, or even looked at, by RECOMPILE. Block(BLOCK NIL Block NIL ("18") 12 SUBNAME COMPILING SUBTEXT compiling) Compiling(COMPILING NIL Compiling NIL ("18") 12 SUBNAME BLOCK SUBTEXT block) 1 In Interlisp-10, block compiling provides a way of compiling several functions into a single block. Function calls between the component functions of the block are very fast. Thus, compiling a block consisting of just a single recursive function may be yield great savings if the function calls itself many times. The output of a block compilation is a single, usually large, function. Calls from within the block to functions outside of the block look like regular function calls. A block can be entered via several different functions, called entries. These must be specified when the block is compiled. In Interlisp-D, block compiling is handled somewhat differently; block compiling provides a mechanism for hiding function names internal to a block, but it does not provide a performance improvement. Block compiling in Interlisp-D works by automatically renaming the block functions with special names, and calling these functions with the normal function-calling mechanisms. Specifically, a function FN is renamed to \BLOCK-NAME/FN. For example, function FOO in block BAR is renamed to \BAR/FOO. Note that it is possible with this scheme to break functions internal to a block. Block(BLOCK NIL Block NIL ("18") 13 SUBNAME DECLARATIONS SUBTEXT declarations) Declarations Block compiling a file frequently involves giving the compiler a lot of information about the nature and structure of the compilation, e.g., block functions, entries, specvars, etc. To help with this, there is the BLOCKS file package command, which has the form: (BLOCKS BLOCK1 BLOCK2 ... BLOCKN) where each BLOCKi is a block declaration. The BLOCKS command outputs a DECLARE: expression, which is noticed by BCOMPL and BRECOMPILE. BCOMPL and BRECOMPILE are sensitive to these declarations and take the appropriate action. Note: Masterscope includes a facility for checking the block declarations of a file or files for various anomalous conditions, e.g. functions in block declarations which aren't on the file(s), functions in ENTRIES not in the block, variables that may not need to be SPECVARS because they are not used freely below the places they are bound, etc. A block declaration is a list of the form: (BLKNAME BLKFN1 ... BLKFNM (VAR1 . VALUE1) ... (VARN . VALUEN)) BLKNAME is the name of a block. BLKFN1 ... BLKFNM are the functions in the block and correspond to BLKFNS in the call to BLOCKCOMPILE. The (VARi . VALUEi) expressions indicate the settings for variables affecting the compilation of that block. If VALUEi is atomic, then VARi is set to VALUEi, otherwise VARi is set to the UNION of VALUEi and the current value of the variable VARi. Also, expressions of the form (VAR * FORM) will cause FORM to be evaluated and the resulting list used as described above (e.g. (GLOBALVARS * MYGLOBALVARS)). For example, consider the block declaration below. The block name is EDITBLOCK, it includes a number of functions (EDITL0, EDITL1, ... EDITH), and it sets the variables ENTRIES, SPECVARS, RETFNS, and GLOBALVARS. (EDITBLOCK EDITL0 EDITL1 UNDOEDITL EDITCOM EDITCOMA EDITMAC EDITCOMS EDIT]UNDO UNDOEDITCOM EDITH (ENTRIES EDITL0 ## UNDOEDITL) (SPECVARS L COM LCFLG #1 #2 #3 LISPXBUFS) (RETFNS EDITL0) (GLOBALVARS EDITCOMSA EDITCOMSL EDITOPS)) Whenever BCOMPL or BRECOMPILE encounter a block declaration, they rebind RETFNS, SPECVARS, GLOBALVARS, BLKLIBRARY, and DONTCOMPILEFNS to their top level values, bind BLKAPPLYFNS and ENTRIES to NIL, and bind BLKNAME to the first element of the declaration. They then scan the rest of the declaration, setting these variables as described above. When the declaration is exhausted, the block compiler is called and given BLKNAME, the list of block functions, and ENTRIES. If a function appears in a block declaration, but is not defined in one of the files, then if it has an in-core definition, this definition is used and a message printed NOT ON FILE, COMPILING IN CORE DEFINITION. Otherwise, the message NOT COMPILEABLE, is printed and the block declaration processed as though the function were not on it, i.e. calls to the function will be compiled as external function calls. Since all compiler variables are rebound for each block declaration, the declaration only has to set those variables it wants changed. Furthermore, setting a variable in one declaration has no effect on the variable's value for another declaration. After finishing all blocks, BCOMPL and BRECOMPILE treat any functions in the file that did not appear in a block declaration in the same way as do TCOMPL and RECOMPILE. If you wish a function compiled separately as well as in a block, or if you wish to compile some functions (not blockcompile), with some compiler variables changed, you can use a special pseudo-block declaration of the form (NIL BLKFN1 ... BLKFNM (VAR1 . VALUE1) ... (VARN . VALUEN)) which means that BLKFN1 ... BLKFNM should be compiled after first setting VAR1 ... VARN as described above. The following variables control other aspects of compiling a block: RETFNS(RETFNS (Variable) NIL NIL ("18") 14) [Variable] Value is a list of internal block functions whose names must appear on the stack, e.g., if the function is to be returned from RETFROM, RETTO, RETEVAL, etc. Usually, internal calls between functions in a block are not put on the stack. BLKAPPLYFNS(BLKAPPLYFNS (Variable) NIL NIL ("18") 14) [Variable] Value is a list of internal block functions called by other functions in the same block using BLKAPPLY or BLKAPPLY* for efficiency reasons. Normally, a call to APPLY from inside a block would be the same as a call to any other function outside of the block. If the first argument to APPLY turned out to be one of the entries to the block, the block would have to be reentered. BLKAPPLYFNS enables a program to compute the name of a function in the block to be called next, without the overhead of leaving the block and reentering it. This is done by including on the list BLKAPPLYFNS those functions which will be called in this fashion, and by using BLKAPPLY in place of APPLY, and BLKAPPLY* in place of APPLY*. If BLKAPPLY or BLKAPPLY* is given a function not on BLKAPPLYFNS, the effect is the same as a call to APPLY or APPLY* and no error is generated. Note however, that BLKAPPLYFNS must be set at compile time, not run time, and furthermore, that all functions on BLKAPPLYFNS must be in the block, or an error is generated (at compile time), NOT ON BLKFNS. BLKAPPLYFNS(BLKAPPLYFNS (Variable) NIL NIL ("18") 14) [Variable] Value is a list of functions that are considered to be in the "block library" of functions that should automatically be included in the block if they are called within the block. Compiling a function open via a macro provides a way of eliminating a function call. For block compiling, the same effect can be achieved by including the function in the block. A further advantage is that the code for this function will appear only once in the block, whereas when a function is compiled open, its code appears at each place where it is called. The block library feature provides a convenient way of including functions in a block. It is just a convenience since you can always achieve the same effect by specifying the function(s) in question as one of the block functions, provided it has an expr definition at compile time. The block library feature simply eliminates the burden of supplying this definition. To use the block library feature, place the names of the functions of interest on the list BLKLIBRARY, and their expr definitions on the property list of the functions under the property BLKLIBRARYDEF. When the block compiler compiles a form, it first checks to see if the function being called is one of the block functions. If not, and the function is on BLKLIBRARY, its definition is obtained from the property value of BLKLIBRARYDEF, and it is automatically included as part of the block. Block(BLOCK NIL Block NIL ("18") 15 SUBNAME COMPILING% FUNCTIONS SUBTEXT compiling% functions) Compiling Functions There are three user level functions for block compiling, BLOCKCOMPILE, BCOMPL, and BRECOMPILE, corresponding to COMPILE, TCOMPL, and RECOMPILE. Note that all of the remarks on macros, globalvars, compiler messages, etc., all apply equally for block compiling. Using block declarations, you can intermix in a single file functions compiled normally and block compiled functions. (BLOCKCOMPILE(BLOCKCOMPILE (Function) NIL NIL ("18") 15) BLKNAME BLKFNS ENTRIES FLG) [Function] BLKNAME is the name of a block, BLKFNS is a list of the functions comprising the block, and ENTRIES a list of entries to the block. Each of the entries must also be on BLKFNS or an error is generated, NOT ON BLKFNS. If only one entry is specified, the block name can also be one of the BLKFNS, e.g., (BLOCKCOMPILE 'FOO '(FOO FIE FUM) '(FOO)). However, if more than one entry is specified, an error will be generated, CAN'T BE BOTH AN ENTRY AND THE BLOCK NAME. If ENTRIES is NIL, (LIST BLKNAME) is used, e.g., (BLOCKCOMPILE 'COUNT '(COUNT COUNT1)) If BLKFNS is NIL, (LIST BLKNAME) is used, e.g., (BLOCKCOMPILE 'EQUAL) BLOCKCOMPILE asks the standard compiler questions, and then begins compiling. As with COMPILE, if the compiled code is being written to a file, the file is closed unless FLG=T. The value of BLOCKCOMPILE is a list of the entries, or if ENTRIES=NIL, the value is BLKNAME. The output of a call to BLOCKCOMPILE is one function definition for BLKNAME, plus definitions for each of the functions on ENTRIES if any. These entry functions are very short functions which immediately call BLKNAME. (BCOMPL(BCOMPL (Function) NIL NIL ("18") 15) FILES CFILE % %) [Function] FILES is a list of symbolic files (if atomic, (LIST FILES) is used). BCOMPL differs from TCOMPL in that it compiles all of the files at once, instead of one at a time, in order to permit one block to contain functions in several files. (If you have several files to be BCOMPLed separately, you must make several calls to BCOMPL.) Output is to CFILE if given, otherwise to a file whose name is (CAR FILES) suffixed with DCOM. For example, (BCOMPL '(EDIT WEDIT)) produces one file, EDIT.DCOM. BCOMPL asks the standard compiler questions, except for "OUTPUT FILE:", then processes each file exactly the same as TCOMPL. BCOMPL next processes the block declarations as described above. Finally, it compiles those functions not mentioned in one of the block declarations, and then writes out all other expressions. If any of the files have property FILETYPE with value CLISP, or a list containing CLISP, then DWIMIFYCOMPFLG is rebound to T for all of the files. The value of BCOMPL is the output file (the new compiled file). If the compilation is aborted due to an error or Control-D, all files are closed and the (partially complete) output file is deleted. It is permissible to TCOMPL files set up for BCOMPL; the block declarations will simply have no effect. Similarly, you can BCOMPL a file that does not contain any block declarations and the result will be the same as having TCOMPLed it. (BRECOMPILE(BRECOMPILE (Function) NIL NIL ("18") 16) FILES CFILE FNS %) [Function] BRECOMPILE plays the same role for BCOMPL that RECOMPILE plays for TCOMPL. Its purpose is to allow you to update a compiled file without requiring an entire BCOMPL. FILES is a list of symbolic files (if atomic, (LIST FILES) is used). CFILE is the compiled file produced by BCOMPL or a previous BRECOMPILE that contains compiled definitions that may be copied. The interpretation of FNS is the same as with RECOMPILE. BRECOMPILE asks the standard compiler questions, except for "OUTPUT FILE:". As with BCOMPL, output automatically goes to FILE.DCOM, where FILE is the first file in FILES. BRECOMPILE processes each file the same as RECOMPILE, then processes each block declaration. If any of the functions in the block are to be recompiled, the entire block must be (is) recompiled. Otherwise, the block is copied from CFILE as with RECOMPILE. For pseudo-block declarations of the form (NIL FN1 ...), all variable assignments are made, but only those functions indicated by FNS are recompiled. After completing the block declarations, BRECOMPILE processes all functions that do not appear in a block declaration, recompiling those dictated by FNS, and copying the compiled definitions of the remaining from CFILE. Finally, BRECOMPILE writes onto the output file the "other expressions" collected in the initial scan of FILES. The value of BRECOMPILE is the output file (the new compiled file). If the compilation is aborted due to an error or Control-D, all files are closed and the (partially complete) output file is deleted. If CFILE=NIL, the old version of FILE.DCOM is used, as with RECOMPILE. In addition, if FNS and CFILE are both NIL, FNS is set to the value of RECOMPILEDEFAULT, initially CHANGES. Compiler(COMPILER NIL Compiler NIL ("18") 16 SUBNAME ERROR% MESSAGES SUBTEXT error% messages) Error Messages(ERROR% MESSAGES NIL Error% messages NIL ("18") 16 SUBNAME COMPILER SUBTEXT compiler) 1 Messages describing errors in the function being compiled are also printed on the terminal. These messages are always preceded by *****. Unless otherwise indicated below, the compilation will continue. (FN NOT ON FILE, COMPILING IN CORE DEFINITION(FN% NOT% ON% FILE,% COMPILING% IN% CORE% DEFINITION (Error Message) NIL NIL ("18") 16)) From calls to BCOMPL and BRECOMPILE. (FN NOT COMPILEABLE(FN% NOT% COMPILEABLE (Error Message) NIL NIL ("18") 16)) An EXPR definition for FN could not be found. In this case, no code is produced for FN, and the compiler proceeds to the next function to be compiled, if any. (FN NOT FOUND(FN% NOT% FOUND (Error Message) NIL NIL ("18") 17)) Occurs when RECOMPILE or BRECOMPILE try to copy the compiled definition of FN from CFILE, and cannot find it. In this case, no code is copied and the compiler proceeds to the next function to be compiled, if any. (FN NOT ON BLKFNS(FN% NOT% ON% BLKFNS (Error Message) NIL NIL ("18") 17)) FN was specified as an entry to a block, or else was on BLKAPPLYFNS, but did not appear on the BLKFNS. In this case, no code is produced for the entire block and the compiler proceeds to the next function to be compiled, if any. (FN CAN'T BE BOTH AN ENTRY AND THE BLOCK NAME(FN% CAN'T% BE% BOTH% AN% ENTRY% AND% THE% BLOCK% NAME (Error Message) NIL NIL ("18") 17)) In this case, no code is produced for the entire block and the compiler proceeds to the next function to be compiled, if any. (BLKNAME - USED BLKAPPLY WHEN NOT APPLICABLE(BLKNAME% -% USED% BLKAPPLY% WHEN% NOT% APPLICABLE (Error Message) NIL NIL ("18") 17)) BLKAPPLY is used in the block BLKNAME, but there are no BLKAPPLYFNS or ENTRIES declared for the block. (VAR SHOULD BE A SPECVAR - USED FREELY BY FN(VAR% SHOULD% BE% A% SPECVAR% -% USED% FREELY% BY% FN (Error Message) NIL NIL ("18") 17)) While compiling a block, the compiler has already generated code to bind VAR as a LOCALVAR, but now discovers that FN uses VAR freely. VAR should be declared a SPECVAR and the block recompiled. ((* --) COMMENT USED FOR VALUE(%(*% --%)% COMMENT% USED% FOR% VALUE (Error Message) NIL NIL ("18") 17)) A comment appears in a context where its value is being used, e.g. (LIST X (* --) Y). The compiled function will run, but the value at the point where the comment was used is undefined. ((FORM) - NON-ATOMIC CAR OF FORM(%(FORM%)% -% NON-ATOMIC% CAR% OF% FORM (Error Message) NIL NIL ("18") 17)) If you intended to treat the value of FORM as a function, you should use APPLY* (Chapter 10). FORM is compiled as if APPLY* had been used. ((SETQ VAR EXPR --) BAD SETQ(%(SETQ% VAR% EXPR% --%)% BAD% SETQ (Error Message) NIL NIL ("18") 17)) SETQ of more than two arguments. (FN - USED AS ARG TO NUMBER FN?(FN% -% USED% AS% ARG% TO% NUMBER% FN? (Error Message) NIL NIL ("18") 17)) The value of a predicate, such as GREATERP or EQ, is used as an argument to a function that expects numbers, such as IPLUS. (FN - NO LONGER INTERPRETED AS FUNCTIONAL ARGUMENT(FN% -% NO% LONGER% INTERPRETED% AS% FUNCTIONAL% ARGUMENT (Error Message) NIL NIL ("18") 17)) The compiler has assumed FN is the name of a function. If you intended to treat the value of FN as a function, APPLY* (Chapter 10) should be used. This message is printed when FN is not defined, and is also a local variable of the function being compiled. (FN - ILLEGAL RETURN(FN% -% ILLEGAL% RETURN (Error Message) NIL NIL ("18") 17)) RETURN encountered when not in PROG. (TG - ILLEGAL GO(TG% -% ILLEGAL% GO (Error Message) NIL NIL ("18") 17)) GO encountered when not in a PROG. (TG - MULTIPLY DEFINED TAG(TG% -% MULTIPLY% DEFINED% TAG (Error Message) NIL NIL ("18") 18)) TG is a PROG label that is defined more than once in a single PROG. The second definition is ignored. (TG - UNDEFINED TAG(TG% -% UNDEFINED% TAG (Error Message) NIL NIL ("18") 18)) TG is a PROG label that is referenced but not defined in a PROG. (VAR - NOT A BINDABLE VARIABLE(VAR% -% NOT% A% BINDABLE% VARIABLE (Error Message) NIL NIL ("18") 18)) VAR is NIL, T, or else not a literal atom. (VAR VAL -- BAD PROG BINDING(VAR% VAL% --% BAD% PROG% BINDING (Error Message) NIL NIL ("18") 18)) Occurs when there is a prog binding of the form (VAR VAL1 ... VALN). (TG - MULTIPLY DEFINED TAG, LAP(TG% -% MULTIPLY% DEFINED% TAG,% LAP (Error Message) NIL NIL ("18") 18)) TG is a label that was encountered twice during the second pass of the compilation. If this error occurs with no indication of a multiply defined tag during pass one, the tag is in a LAP macro. (TG - UNDEFINED TAG, LAP(TG% -% UNDEFINED% TAG,% LAP (Error Message) NIL NIL ("18") 18)) TG is a label that is referenced during the second pass of compilation and is not defined. LAP treats TG as though it were a COREVAL, and continues the compilation. (TG - MULTIPLY DEFINED TAG, ASSEMBLE(TG% -% MULTIPLY% DEFINED% TAG,% ASSEMBLE (Error Message) NIL NIL ("18") 18)) TG is a label that is defined more than once in an assemble form. (TG - UNDEFINED TAG, ASSEMBLE(TG% -% UNDEFINED% TAG,% ASSEMBLE (Error Message) NIL NIL ("18") 18)) TG is a label that is referenced but not defined in an assemble form. (OP - OPCODE? - ASSEMBLE(OP% -% OPCODE?% -% ASSEMBLE (Error Message) NIL NIL ("18") 18)) OP appears as CAR of an assemble statement, and is illegal. (NO BINARY CODE GENERATED OR LOADED FOR FN(NO% BINARY% CODE% GENERATED% OR% LOADED% FOR% FN (Error Message) NIL NIL ("18") 18)) A previous error condition was sufficiently serious that binary code for FN cannot be loaded without causing an error. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "18-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "18-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "18-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "18-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "18-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "18-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))))),1HH +TT,ll3H +T,ll,ll,ll,ll,ll2l2l~~3HH +T5;ll8ll8ll2HZZ2Hll2Hll2Hll2Hll/HH3HH +T/ll +2Hll2HH +2HH5,HH,2F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR3$$(T,HH/ll/HH +,HH +-T3(T3KT,,TITAN +TITAN +TITAN + HELVETICA + HELVETICA  HELVETICACLASSIC +TITAN +CLASSIC + HELVETICACLASSIC +CLASSIC +MODERN +MODERN +MODERNMODERN +" HRULE.GETFNCLASSIC + " ! HRULE.GETFNCLASSIC + !    HRULE.GETFNCLASSIC +    HRULE.GETFNCLASSIC + IM.CHAP.GETFN HELVETICA + HRULE.GETFNMODERN +O $]  6 J      m                    3 V 8   z x   `  T         1 - D `           z  U     +  ! W 1  d   "         _   2 . , + ++++++++++ "   R  )FIM.INDEX.GETFN +( HRULE.GETFNMODERN +"                    +      Q        >  2 +        1         ( !  &   &  + g * >  4   i b   )XIM.INDEX.GETFN +DIM.INDEX.GETFN( HRULE.GETFNMODERN +" +   + c           \  z               <  k  p  )BIM.INDEX.GETFNVIM.INDEX.GETFNFIM.INDEX.GETFNZIM.INDEX.GETFN( HRULE.GETFNMODERN +  Q   R 1   E    t       5       L  O (^ ;D   N    O  8  9   %        <  +         S X             1  J   )       2IM.INDEX.GETFN  + %V  ,     2    %   #T P ) HIM.INDEX.GETFN( HRULE.GETFNMODERN +& G  &IM.INDEX.GETFN  %8  0   2 H %      +  %  :  %!   g %K  t ;  * %   .IM.INDEX.GETFN  %   ] / 0    .IM.INDEX.GETFN  %   0    +'IM.INDEX.GETFN    %        + %      3    %j %!  ) VIM.INDEX.GETFN( HRULE.GETFNMODERN +&    o     %IM.INDEX.GETFN   W  #IM.INDEX.GETFN   T  "IM.INDEX.GETFN %i  %<      +IM.INDEX.GETFN %h   3  $  :      .   r % /   y  % ; + % %   )"( HRULE.GETFNMODERN +& \IM.INDEX.GETFNbIM.INDEX.GETFN  D   ,  $      ,  +    O )         / P <  : S  )( HRULE.GETFNMODERN +&      j  j 9   )( HRULE.GETFNMODERN +&  K  Q  *,IM.INDEX.GETFN %       $ V   B   ) ?    ) BIM.INDEX.GETFN@IM.INDEX.GETFN( HRULE.GETFNMODERN +& 4    %         +   # * 7 '        G c P  & - 8     B      +  B  * l  A  # { u  + { )HIM.INDEX.GETFN ( HRULE.GETFNMODERN +&  ! :   ~ E A  + O i     R   b    K | 5 4   E      R U G 5 B #   $  &IM.INDEX.GETFN   % $    P  )   %O      'IM.INDEX.GETFNTITAN +    %           %       %IM.INDEX.GETFN  %.  #  8    3 n     %J e 7  +    @   .  p %+ M  = U  $  2      "   ( %  +(IM.INDEX.GETFN  % \     %   )    B    S       % 3  #       8 A          %   I   ^ ?  +  6  +    4  +   T    +    %' &  E  &   A H    +     3 %     :          B   % ,   q % .   Q +    " L G A  )CIM.INDEX.GETFN +CIM.INDEX.GETFN( HRULE.GETFNMODERN +'f $   +    +  U #IIM.INDEX.GETFN ' +         $    !  +  +F  5 H $+                $     2       _           '  !        G +  $F %     +  ,0!--$  +,   + +  #  $    $~  u $  +b                    $    (     $D *%IM.INDEX.GETFN    V * *IM.INDEX.GETFN ^    w Z  D       &  0   < B  * *IM.INDEX.GETFN  l q [ +V  +8 8 #YIM.INDEX.GETFN':   +    * +IM.INDEX.GETFN  +    6  ! $    I   )M                      K M    !         0  P    %IM.INDEX.GETFNTITAN +    (       + !   -        3 0                 I _  )IM.INDEX.GETFNTITAN +    +   U   )     "  +O     +3           +! -   -    K   ) +b  =    +V    +           +      )UIM.INDEX.GETFNUIM.INDEX.GETFN( HRULE.GETFNMODERN +' D & *WIM.INDEX.GETFN%  + & 8IM.INDEX.GETFN%   <  I   2IM.INDEX.GETFN%  +(    ~ &  7IM.INDEX.GETFN % 6   & )YIM.INDEX.GETFN%Q * &  !UIM.INDEX.GETFN%     & % XIM.INDEX.GETFN%I       +    &HIM.INDEX.GETFN%C g & JIM.INDEX.GETFN%&       &   FIM.INDEX.GETFN% & IIM.INDEX.GETFN%"  E  & /\IM.INDEX.GETFN%  :     <  N & :IM.INDEX.GETFN%  & 6IM.INDEX.GETFN%  & AIM.INDEX.GETFN%  2 % & 9IM.INDEX.GETFN%  /  & FIM.INDEX.GETFN%    &   DIM.INDEX.GETFN%0       & GIM.INDEX.GETFN% & ?IM.INDEX.GETFN% e   ! & !LIM.INDEX.GETFN% B & DIM.INDEX.GETFN% D & ?IM.INDEX.GETFN% + &( TIM.INDEX.GETFN%I  , %ن +z \ No newline at end of file diff --git a/docs/porter-irm/vol2/19-MASTERSCOPE.INTERPRESS b/docs/porter-irm/vol2/19-MASTERSCOPE.INTERPRESS new file mode 100644 index 00000000..6d5d79c3 Binary files /dev/null and b/docs/porter-irm/vol2/19-MASTERSCOPE.INTERPRESS differ diff --git a/docs/porter-irm/vol2/19-MASTERSCOPE.TEDIT b/docs/porter-irm/vol2/19-MASTERSCOPE.TEDIT new file mode 100644 index 00000000..4ba3aaad --- /dev/null +++ b/docs/porter-irm/vol2/19-MASTERSCOPE.TEDIT @@ -0,0 +1,320 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 19. MASTERSCOPE 1 19. MASTERSCOPE 1 "19"19. MASTERSCOPE 6 Masterscope is an interactive program for analyzing and cross-referencing user programs. It contains facilities for analyzing user functions to determine what other functions are called, how and where variables are bound, set, or referenced, as well as which functions use particular record declarations. Masterscope is able to analyze definitions directly from a file as well as in-core definitions. Masterscope maintains a database of the results of the analyses it performs. Using a simple command language, you may interrogate the database, call the editor on those expressions in functions that were analyzed which use variables or functions in a particular way, or display the tree structure of function calls among any set of functions. Masterscope is interfaced with the editor and file package so that when a function is edited or a new definition loaded in, Masterscope knows that it must re-analyze that function. The following sample session illustrates some of these facilities. 50. ANALYZE FUNCTIONS ON RECORD ............................... NIL 51. WHO CALLS RECFIELDLOOK (RECFIELDLOOK ACCESSDEF ACCESSDEF2 EDITREC) 52_. EDIT WHERE ANY CALL RECFIELDLOOK RECFIELDLOOK : (RECFIELDLOOK (CDR Y) FIELD) tty: 5*OK ACCESSDEF : (RECFIELDLOOK DECLST FIELD VAR1) 6*OK (RECFIELDLOOK USERRECLST FIELD) 7*N VAR1 8*OK ACCESSDEF2 : (RECFIELDLOOK (RECORD.SUBDECS TRAN) FIELD) tty: (RECFIELDLOOK (RECORD.SUBDECS TRAN) FIELD) 9*N (CAR TAIL] 10*OK EDITREC : (RECFIELDLOOK USERRECLST (CAR EDITRECX)) 11*OK NIL 53. WHO CALLS ERROR .. (EDITREC) 54. SHOW PATHS TO RECFIELDLOOK FROM ACCESSDEF (inverted tree) 1. RECFIELDLOOK RECFIELDLOOK 2. ACCESSDEF 3. ACCESSDEF2 ACCESSDEF2 4. ACCESSDEF 5. RECORDCHAIN ACCESSDEF NIL 55. WHO CALLS WHO IN /FNS RECORDSTATEMENT -- /RPLNODE RECORDECL1 -- /NCONC, /RPLACD, /RPLNODE RECREDECLARE1 -- /PUTHASH UNCLISPTRAN -- /PUTHASH, /RPLNODE2 RECORDWORD -- /RPLACA RECORD1 -- /RPLACA, /SETTOPVAL EDITREC -- /SETTOPVAL Statement 50 The user directs that the functions on file RECORD be analyzed. The leading period and space specify that this line is a Masterscope command. The user may also call Masterscope directly by typing (MASTERSCOPE). Masterscope prints a greeting and prompts with ". ". Within the top-level executive of Masterscope, the user may issue Masterscope commands, programmer's assistant commands, (e.g., REDO, FIX), or run programs. The user can exit from the Masterscope executive by typing OK. The function . is defined as a nlambda nospread function which interprets its argument as a Masterscope command, executes the command and returns. Masterscope prints a . whenever it (re)analyzes a function, to let you know what it is happening. The feedback when Masterscope analyzes a function is controlled by the flag MSPRINTFLG: if MSPRINTFLG is the atom ".", Masterscope will print out a period. (If an error in the function is detected, "?" is printed instead.) If MSPRINTFLG is a number N, Masterscope will print the name of the function it is analyzing every Nth function. If MSPRINTFLG is NIL, Masterscope won't print anything. Initial setting is ".". Note that the function name is printed when Masterscope starts analyzing, and the comma is printed when it finishes. Statement 51 You ask which functions call RECFIELDLOOK. Masterscope responds with the list. Statement 52 You ask to edit the expressions where the function RECFIELDLOOK is called. Masterscope calls EDITF on the functions it had analyzed that call RECFIELDLOOK, directing the editor to the appropriate expressions. The user then edits some of those expressions. In this example, the teletype editor is used. If Dedit is enabled as the primary editor, it would be called to edit the appropriate functions (see Chapter 16). Statement 53 Next you ask which functions call ERROR. Since some of the functions in the database have been changed, Masterscope re-analyzes the changed definitions (and prints out .'s for each function it analyzes). Masterscope responds that EDITREC is the only analyzed function that calls ERROR. Statement 54 You ask to see a map of the ways in which RECFIELDLOOK is called from ACCESSDEF. A tree structure of the calls is displayed. Statement 55 You then ask to see which functions call which functions in the list /FNS. Masterscope responds with a structured printout of these relations. Command Language 1 You communicate with Masterscope using an English-like command language, e.g., WHO CALLS PRINT. With these commands, you can direct that functions be analyzed, interrogate Masterscope's database, and perform other operations. The commands deal with sets of functions, variables, etc., and relations between them (e.g., call, bind). Sets correspond to English nouns, relations to verbs. A set of atoms can be specified in a variety of ways, either explicitly, e.g., FUNCTIONS ON FIE specifies the atoms in (FILEFNSLST 'FIE), or implicitly, e.g., NOT CALLING Y, where the meaning must be determined in the context of the rest of the command. Such sets of atoms are the basic building blocks which the command language deals with. Masterscope also deals with relations between sets. For example, the relation CALL relates functions and other functions; the relations BIND and USE FREELY relate functions and variables. These relations are what get stored in the Masterscope database when functions are analyzed. In addition, Masterscope "knows" about file package conventions; CONTAIN relates files and various types of objects (functions, variables). Sets and relations are used (along with a few additional words) to form sentence-like commands. For example, the command WHO ON 'FOO USE 'X FREELY will print out the list of functions contained in the file FOO which use the variable X freely. The command EDIT WHERE ANY CALLS 'ERROR will call EDITF on those functions which have previously been analyzed that directly call ERROR, pointing at each successive expression where the call to ERROR actually occurs. Commands The normal mode of communication with Masterscope is via "commands". These are sentences in the Masterscope command language which direct Masterscope to answer questions or perform various operations. Any command may be followed by OUTPUT FILENAME to send output to the given file rather than the terminal, e.g. WHO CALLS WHO OUTPUT CROSSREF. ANALYZE SET [Masterscope Command] Analyze the functions in SET (and any functions called by them) and include the information gathered in the database. Masterscope will not re-analyzing a function if it thinks it already has valid information about that function in its database. You may use the command REANALYZE (below) to force re-analysis. Whenever a function is referred to in a command as a "subject" of one of the relations, it is automatically analyzed; the user need not give an explicit ANALYZE command. Thus, WHO IN MYFNS CALLS FIE will automatically analyze the functions in MYFNS if they have not already been analyzed. Only expr definitions will be analyzed; that is, Masterscope will not analyze compiled code. If necessary, the definition will be DWIMIFYed before analysis. If there is no in-core definition for a function (either in the function definition cell or an EXPR property), Masterscope will attempt to read in the definition from a file. Files which have been explicitly mentioned previously in some command are searched first. If the definition cannot be found on any of those files, Masterscope looks among the files on FILELST for a definition. If a function is found in this manner, Masterscope will print a message "(reading from FILENAME)". If no definition can be found at all, Masterscope will print a message "FN can't be analyzed". If the function previously was known, the message "FN disappeared!" is printed. REANALYZE SET [Masterscope Command] Causes Masterscope to reanalyze the functions in SET (and any functions called by them) even if it thinks it already has valid information in its database. For example, this would be necessary if you had disabled or subverted the file package, e.g. performed PUTD's to change the definition of functions. ERASE SET [Masterscope Command] Erase all information about the functions in SET from the database. ERASE by itself clears the entire database. SHOW PATHS PATHOPTIONS [Masterscope Command] Displays a tree of function calls. This is described in the SHOW PATHS section below. SET RELATION SET [Masterscope Command] SET IS SET [Masterscope Command] SET ARE SET [Masterscope Command] This command has the same format as an English sentence with a subject (the first SET), a verb (the RELATION or IS or ARE), and an object (the second SET). Any of the SETs within the command may be preceded by the question determiners WHICH or WHO (or just WHO alone). For example, WHICH FUNCTIONS CALL X prints the list of functions that call the function X. RELATION may be one of the relation words in present tense (CALL, BIND, TEST, SMASH, etc.) or used as a passive (e.g., WHO IS CALLED BY WHO). Other variants are allowed, e.g. WHO DOES X CALL, IS FOO CALLED BY FIE, etc. The interpretation of the command depends on the number of question elements present: 1. If there is no question element, the command is treated as an assertion and Masterscope returns either T or NIL, depending on whether that assertion is true. Thus, ANY IN MYFNS CALL HELP will print T if any function in MYFNS call the function HELP, and NIL otherwise. 2. If there is one question element, Masterscope returns the list of items for which the assertion would be true. For example, MYFN BINDS WHO USED FREELY BY YOURFN prints the list of variables bound by MYFN which are also used freely by YOURFN. 3. If there are two question elements, Masterscope will print a doubly indexed list: . WHO CALLS WHO IN /FNScr RECORDSTATEMENT -- /RPLNODE RECORDECL1 -- /NCONC, /RPLACD, /RPLNODE RECREDECLARE1 -- /PUTHASH UNCLISPTRAN -- /PUTHASH, /RPLNODE2 RECORDWORD -- /RPLACA RECORD1 -- /RPLACA, /SETTOPVAL EDITREC -- /SETTOPVAL EDIT WHERE SET RELATION SET [- EDITCOMS] [Masterscope Command] (WHERE may be omitted.) The first SET refers to a set of functions. The EDIT command calls the editor on each expression where the RELATION actually occurs. For example, EDIT WHERE ANY CALL ERROR will call EDITF on each (analyzed) function which calls ERROR stopping within a TTY: at each call to ERROR. Currently one cannot EDIT WHERE a file which CONTAINS a datum, nor where one function CALLS another SOMEHOW. EDITCOMS, if given, are a list of commands passed to EDITF to be performed at each expression. For example, EDIT WHERE ANY CALLS MYFN DIRECTLY - (SW 2 3) P will switch the first and second arguments to MYFN in every call to MYFN and print the result. EDIT WHERE ANY ON MYFILE CALL ANY NOT @ GETD will call the editor on any expression involving a call to an undefined function. Note that EDIT WHERE X SETS Y will point only at those expressions where Y is actually set, and will skip over places where Y is otherwise mentioned. SHOW WHERE SET RELATION SET [Masterscope Command] Like the EDIT command except merely prints out the expressions without calling the editor. EDIT SET [- EDITCOMS] [Masterscope Command] Calls EDITF on each function in SET. EDITCOMS, if given, will be passed as a list of editor commands to be executed. For example EDIT ANY CALLING FN1 - (R FN1 FN2) will replace FN1 by FN2 in those functions that call FN1. DESCRIBE SET [Masterscope Command] Prints out the BIND, USE FREELY and CALL information about the functions in SET. For example, the command DESCRIBE PRINTARGS might print out: PRINTARGS[N,FLG] binds: TEM,LST,X calls: MSRECORDFILE,SPACES,PRIN1 called by: PRINTSENTENCE,MSHELP,CHECKER This shows that PRINTARGS has two arguments, N and FLG, binds internally the variables TEM, LST and X, calls MSRECORDFILE, SPACES and PRIN1 and is called by PRINTSENTENCE, MSHELP, and CHECKER. You can specify additional information to be included in the description. DESCRIBELST is a list each of whose elements is a list containing a descriptive string and a form. The form is evaluated (it can refer to the name of the funtion being described by the free variable FN); if it returns a non-NIL value, the description string is printed followed by the value. If the value is a list, its elements are printed with commas between them. For example, the entry ("types: " (GETRELATION FN '(USE TYPE) T) would include a listing of the types used by each function. CHECK SET [Masterscope Command] Checks for various anomolous conditions (mainly in the compiler declarations) for the files in SET (if SET is not given, FILELST is used). For example, this command will warn about variables which are bound but never referenced, functions in BLOCKS delarations which aren't on the file containing the declaration, functions declared as ENTRIES but not in the block, variables which may not need to be declared SPECVARS because they are not used freely below the places where they are bound, etc. FOR VARIABLE SET I.S.TAIL [Masterscope Command] This command provides a way of combining CLISP iterative statements with Masterscope. An iterative statement will be constructed in which VARIABLE is iteratively assigned to each element of SET, and then the iterative statement tail I.S.TAIL is executed. For example, FOR X CALLED BY FOO WHEN CCODEP DO (PRINTOUT T X ,,, (ARGLIST X) T) will print out the name and argument list of all of the compiled functions which are called by FOO. Relations A relation is specified by one of the keywords below. Some of these "verbs" accept modifiers. For example, USE, SET, SMASH and REFERENCE all may be modified by FREELY. The modifier may occur anywhere within the command. If there is more than one verb, any modifier between two verbs is assumed to modify the first one. For example, in USING ANY FREELY OR SETTING X, the FREELY modifies USING but not SETTING; the entire phrase is interpreted as the set of all functions which either use any variable freely or set the variable X, whether or not X is set freely. Verbs can occur in the present tense (e.g., USE, CALLS, BINDS, USES) or as present or past participles (e.g., CALLING, BOUND, TESTED). The relations (with their modifiers) recognized by Masterscope are: CALL [Masterscope Relation] Function F1 calls F2 if the definition of F1 contains a form (F2 --). The CALL relation also includes any instance where a function uses a name as a function, as in (APPLY (QUOTE F2) --), (FUNCTION F2), etc. CALL SOMEHOW [Masterscope Relation] One function calls another SOMEHOW if there is some path from the first to the other. That is, if F1 calls F2, and F2 calls F3, then F1 CALLS F3 SOMEHOW. This information is not stored directly in the database; instead, Masterscope stores only information about direct function calls, and (re)computes the CALL SOMEHOW relation as necessary. USE [Masterscope Relation] If unmodified, the relation USE denotes variable usage in any way; it is the union of the relations SET, SMASH, TEST, and REFERENCE. SET [Masterscope Relation] A function SETs a variable if the function contains a form (SETQ var --), (SETQQ var --), etc. SMASH [Masterscope Relation] A function SMASHes a variable if the function calls a destructive list operation (RPLACA, RPLACD, DREMOVE, SORT, etc.) on the value of that variable. Masterscope will also find instances where the operation is performed on a "part" of the value of the variable; for example, if a function contains a form (RPLACA (NTH X 3) T) it will be noted as SMASHING X. Note that if the function contains a sequence (SETQ Y X), (RPLACA Y T) then Y is noted as being smashed, but not X. TEST [Masterscope Relation] A variable is TESTed by a function if its value is only distinguished between NIL and non-NIL. For example, the form (COND ((AND X --) --)) tests the value of X. REFERENCE [Masterscope Relation] This relation includes all variable usage except for SET. The verbs USE, SET, SMASH, TEST and REFERENCE may be modified by the words FREELY or LOCALLY. A variable is used FREELY if it is not bound in the function at the place of its use; alternatively, it is used LOCALLY if the use occurs within a PROG or LAMBDA that binds the variable. Masterscope also distinguishes between CALL DIRECTLY and CALL INDIRECTLY. A function is called DIRECTLY if it occurs as CAR-of-form in a normal evaluation context. A function is called INDIRECTLY if its name appears in a context which does not imply its immediate evaluation, for example (SETQ Y (LIST (FUNCTION FOO) 3)). The distinction is whether or not the compiled code of the caller would contain a direct call to the callee. Note that an occurrence of (FUNCTION FOO) as the functional argument to one of the built-in mapping functions which compile open is considered to be a direct call. In addition, CALL FOR EFFECT (where the value of the function is not used) is distinguished from CALL FOR VALUE. BIND [Masterscope Relation] The BIND relation between functions and variables includes both variables bound as function arguments and those bound in an internal PROG or LAMBDA expression. USE AS A FIELD [Masterscope Relation] Masterscope notes all uses of record field names within FETCH, REPLACE or CREATE expressions. FETCH [Masterscope Relation] Use of a field within a FETCH expression. REPLACE [Masterscope Relation] Use of a record field name within a REPLACE or CREATE expression. USE AS A RECORD [Masterscope Relation] Masterscope notes all uses of record names within CREATE or TYPE? expressions. Additionally, in (fetch (FOO FIE) of X), FOO is used as a record name. CREATE [Masterscope Relation] Use of a record name within a CREATE expression. USE AS A PROPERTY NAME [Masterscope Relation] Masterscope notes the property names used in GETPROP, PUTPROP, GETLIS, etc. expressions if the name is quoted. For example, if a function contains a form (GETPROP X (QUOTE INTERP)), then that function USEs INTERP as a property name. USE AS A CLISP WORD [Masterscope Relation] Masterscope notes all iterative statement operators and user-defined CLISP words as being used as a CLISP word. CONTAIN [Masterscope Relation] Files contain functions, records, and variables. This relation is not stored in the database but is computed using the file package. DECLARE AS LOCALVAR [Masterscope Relation] DECLARE AS SPECVAR [Masterscope Relation] Masterscope notes internal "calls" to DECLARE from within functions. The following abbreviations are recognized: FREE=FREELY, LOCAL=LOCALLY, PROP=PROPERTY, REF=REFERENCE. Also, the words A, AN and NAME (after AS) are "noise" words and may be omitted. Note: Masterscope uses "templates" (see the Affecting Masterscope Analysis section below) to decide which relations hold between functions and their arguments. For example, the information that SORT SMASHes its first argument is contained in the template for SORT. Masterscope initially contains templates for most system functions which set variables, test their arguments, or perform destructive operations. You may change existing templates or insert new ones in Masterscope's tables via the SETTEMPLATE function (see below). Set Specifications A "set" is a collection of things (functions, variables, etc.). A set is specified by a set phrase, consisting of a determiner (e.g., ANY, WHICH, WHO) followed by a type (e.g., FUNCTIONS, VARIABLES) followed by a specification (e.g., IN MYFNS, @ SUBRP). The determiner, type and specification may be used alone or in combination. For example, ANY FUNCTIONS IN MYFNS, ANY @ SUBRP, VARIABLES IN GLOBALVARS, and WHO are all acceptable set phrases. Set specifications are explained below: 'ATOM [Masterscope Set Specification] The simplest way to specify a set consisting of a single thing is by the name of that thing. For example, in the command WHO CALLS 'ERROR, the function ERROR is referred to by its name. Although the ' can be left out, to resolve possible ambiguities names should usually be quoted; e.g., WHO CALLS 'CALLS will return the list of functions which call the function CALLS. 'LIST [Masterscope Set Specification] Sets consisting of several atoms may be specified by naming the atoms. For example, the command WHO USES '(A B) returns the list of functions that use the variables A or B. IN EXPRESSION [Masterscope Set Specification] The form EXPRESSION is evaluated, and its value is treated as a list of the elements of a set. For example, IN GLOBALVARS specifies the list of variables in the value of the variable GLOBALVARS. @ PREDICATE [Masterscope Set Specification] A set may also be specified by giving a predicate which the elements of that set must satisfy. PREDICATE is either a function name, a LAMBDA expression, or an expression in terms of the variable X. The specification @ PREDICATE represents all atom for which the value of PREDICATE is non-NIL. For example, @ EXPRP specifies all those atoms which have expr definitions; @ (STRPOSL X CLISPCHARRAY) specifies those atoms which contain CLISP characters. The universe to be searched is either determined by the context within the command (e.g., in WHO IN FOOFNS CALLS ANY NOT @ GETD, the predicate is only applied to functions which are called by any functions in the list FOOFNS), or in the extreme case, the universe defaults to the entire set of things which have been noticed by Masterscope, as in the command WHO IS @ EXPRP. LIKE ATOM [Masterscope Set Specification] ATOM may contain ESCs; it is used as a pattern to be matched (as in the editor). For example, WHO LIKE /R$ IS CALLED BY ANY would find both /RPLACA and /RPLNODE. A set may also be specified by giving a relation its members must have with the members of another set: RELATIONING SET [Masterscope Set Specification] RELATIONING is used here generically to mean any of the relation words in the present participle form (possibly with a modifier), e.g., USING, SETTING, CALLING, BINDING. RELATIONING SET specifies the set of all objects which have that relation with some element of SET. For example, CALLING X specifies the set of functions which call the function X; USING ANY IN FOOVARS FREELY specifies the set of functions which uses freely any variable in the value of FOOVARS. RELATIONED BY SET [Masterscope Set Specification] RELATIONED IN SET [Masterscope Set Specification] This is similar to the RELATIONING construction. For example, CALLED BY ANY IN FOOFNS represents the set of functions which are called by any element of FOOFNS; USED FREELY BY ANY CALLING ERROR is the set of variables which are used freely by any function which also calls the function ERROR. BLOCKTYPE OF FUNCTIONS [Masterscope Set Specification] BLOCKTYPE ON FILES [Masterscope Set Specification] These phrases allow you to ask about BLOCKS declarations on files (see Chapter 18). BLOCKTYPE is one of LOCALVARS, SPECVARS, GLOBALVARS, ENTRIES, BLKFNS, BLKAPPLYFNS, or RETFNS. BLOCKTYPE OF FUNCTIONS specifies the names which are declared to be BLOCKTYPE in any blocks declaration which contain any of FUNCTIONS (a "set" of functions). The "functions" in FUNCTIONS can either be block names or just functions in a block. For example, WHICH ENTRIES OF ANY CALLING 'Y BIND ANY GLOBALVARS ON 'FOO. BLOCKTYPE ON FILES specifies all names which are declared to be BLOCKTYPE on any of the given FILES (a "set" of files). FIELDS OF SET [Masterscope Set Specification] SET is a set of records. This denotes the field names of those records. For example, the command WHO USES ANY FIELDS OF BRECORD returns the list of all functions which do a fetch or replace with any of the field names declared in the record declaration of BRECORD. KNOWN [Masterscope Set Specification] The set of all functions which have been analyzed. For example, the command WHO IS KNOWN will print out the list of functions which have been analyzed. THOSE [Masterscope Set Specification] The set of things printed out by the last Masterscope question. For example, following the command WHO IS USED FREELY BY PARSE, you could ask WHO BINDS THOSE to find out where those variables are bound. ON PATH PATHOPTIONS [Masterscope Set Specification] Refers to the set of functions which would be printed by the command SHOW PATHS PATHOPTIONS. For example, IS FOO BOUND BY ANY ON PATH TO 'PARSE tests if FOO might be bound "above" the function PARSE. SHOW PATHS is explained in detail in the SHOW PATHS section below. Sets may also be specified with "relative clauses" introduced by the word THAT, e.g. THE FUNCTIONS THAT BIND 'X. Set Determiners(MASTERSCOPE NIL Masterscope NIL ("19") 10 SUBNAME SET% DETERMINERS SUBTEXT set% determiners) Set phrases may be preceded by a determiner. A determiner is one of the words THE, ANY, WHO or WHICH. The "question" determiners (WHO and WHICH) are only meaningful in some of the commands, namely those that take the form of questions. ANY and WHO (or WHOM) can be used alone; they are "wild-card" elements, e.g., the command WHO USES ANY FREELY, will print out the names of all (known) functions which use any variable freely. If the determiner is omitted, ANY is assumed; e.g. the command WHO CALLS '(PRINT PRIN1 PRIN2) will print the list of functions which call any of PRINT, PRIN1, PRIN2. THE is also allowed, e.g. WHO USES THE RECORD FIELD FIELDX. Set Types(MASTERSCOPE NIL Masterscope NIL ("19") 10 SUBNAME SET% TYPES SUBTEXT set% types) Any set phrase has a type; that is, a set may specify either functions, variables, files, record names, record field names or property names. The type may be determined by the context within the command (e.g., in CALLED BY ANY ON FOO, the set ANY ON FOO is interpreted as meaning the functions on FOO since only functions can be CALLED), or the type may be given explicitly by you (e.g., FUNCTIONS ON FIE). The following types are recognized: FUNCTIONS, VARIABLES, FILES, PROPERTY NAMES, RECORDS, FIELDS, I.S.OPRS. Also, the abbreviations FNS, VARS, PROPNAMES or the singular forms FUNCTION, FN, VARIABLE, VAR, FILE, PROPNAME, RECORD, FIELD are recognized. Note that most of these types correspond to built-in "file package types" (see Chapter 17). The type is used by Masterscope in a variety of ways when interpreting the set phrase: 1. Set types are used to disambiguate possible parsings. For example, both commands WHO SETS ANY BOUND IN X OR USED BY Y and WHO SETS ANY BOUND IN X OR CALLED BY Y have the same general form. However, the first case is parsed as WHO SETS ANY (BOUND BY X OR USED BY Y) since both BOUND BY X and USED BY Y refer to variables; while the second case as WHO SETS ANY BOUND IN (X OR CALLED BY Y), since CALLED BY Y and X must refer to functions. Note that parentheses may be used to group phrases. 2. The type is used to determine the modifier for USE: FOO USES WHICH RECORDS is equivalent to FOO USES WHO AS A RECORD FIELD. 3. The interpretation of CONTAIN depends on the type of its object: the command WHAT FUNCTIONS ARE CONTAINED IN MYFILE prints the list of functions in MYFILE; WHAT RECORDS ARE ON MYFILE prints the list of records. 4. The implicit "universe" in which a set expression is interpreted depends on the type: ANY VARIABLES @ GETD is interpreted as the set of all variables which have been noticed by Masterscope (i.e., bound or used in any function which has been analyzed) that also have a definition. ANY FUNCTIONS @ (NEQ (GETTOPVAL X) 'NOBIND) is interpreted as the set of all functions which have been noticed (either analyzed or called by a function which has been analyzed) that also have a top-level value. Conjunctions of Sets Sets may be joined by the conjunctions AND and OR or preceded by NOT to form new sets. AND is always interpreted as meaning "intersection"; OR as "union", while NOT means "complement". For example, the set CALLING X AND NOT CALLED BY Y specifies the set of all functions which call the function X but are not called by Y. Masterscope's interpretation of AND and OR follow LISP conventions rather than the conventional English interpretation. For example "calling X and Y" would, in English, be interpreted as the intersection of (CALLING X) and (CALLING Y); but Masterscope interprets CALLING X AND Y as CALLING ('X AND 'Y); which is the null set. Only sets may be joined with conjunctions: joining modifiers, as in USING X AS A RECORD FIELD OR PROPERTY NAME, is not allowed; in this case, you must say USING X AS A RECORD FIELD OR USING X AS A PROPERTY NAME. As described above, the type of sets is used to disambiguate parsings. The algorithm used is to first try to match the type of the phrases being joined and then try to join with the longest preceding phrase. In any case, you may group phrases with parentheses to specify the manner in which conjunctions should be parsed. SHOW PATHS 1 In trying to work with large programs, you can lose track of the hierarchy of functions. The Masterscope SHOW PATHS command aids you by providing a map showing the calling structure of a set of functions. SHOW PATHS prints out a tree structure showing which functions call which other functions. For example, the command SHOW PATHS FROM MSPARSE will print out the structure of Masterscope's parser: 1.MSPARSE MSINIT MSMARKINVALID 2. | MSINITH MSINITH 3. MSINTERPRET MSRECORDFILE 4. | MSPRINTWORDS 5. | PARSECOMMAND GETNEXTWORD CHECKADV 6. | | PARSERELATION {a} 7. | | PARSESET {b} 8. | | PARSEOPTIONS {c} 9. | | MERGECONJ GETNEXTWORD {5} 10. | GETNEXTWORD {5} 11. | FIXUPTYPES SUBJTYPE 12. | | OBJTYPE 13. | FIXUPCONJUNCTIONS MERGECONJ {9} 14. | MATCHSCORE 15. MSPRINTSENTENCE ----------------------------------------------------- overflow - a 16.PARSERELATION GETNEXTWORD {5} 17. CHECKADV ----------------------------------------------------- overflow - b 19.PARSESET PARSESET 20. GETNEXTWORD {5} 21. PARSERELATION {6} 22. SUBPARSE GETNEXTWORD {5} ----------------------------------------------------- overflow - c 23.PARSEOPTIONS GETNEXTWORD {5} 24. PARSESET {19} The above printout displays that the function MSPARSE calls MSINIT, MSINTERPRET, and MSPRINTSENTENCE. MSINTERPRET in turn calls MSRECORDFILE, MSPRINTWORDS, PARSECOMMAND, GETNEXTWORD, FIXUPTYPES, and FIXUPCONJUNCTIONS. The numbers in braces {} after a function name are backward references: they indicate that the tree for that function was expanded on a previous line. The lowercase letters in braces are forward references: they indicate that the tree for that function will be expanded below, since there is no more room on the line. The vertical bar is used to keep the output aligned. Note: Loading the Browser library package modifies the SHOW PATHS command so the command's output is displayed as an undirected graph. The SHOW PATHS command takes the form: SHOW PATHS followed by some combination of the following path options: FROM SET [Masterscope Path Option] Display the function calls from the elements of SET. TO SET [Masterscope Path Option] Display the function calls leading to elements of SET. If TO is given before FROM (or no FROM is given), the tree is "inverted" and a message, (inverted tree) is printed to warn you that if FN1 appears after FN2 it is because FN1 is called by FN2. When both FROM and TO are given, the first one indicates a set of functions which are to be displayed while the second restricts the paths that will be traced; i.e., the command SHOW PATHS FROM X TO Y will trace the elements of the set CALLED SOMEHOW BY X AND CALLING Y SOMEHOW. If TO is not given, TO KNOWN OR NOT @ GETD is assumed; that is, only functions which have been analyzed or which are undefined will be included. Note that Masterscope will analyze a function while printing out the tree if that function has not previously been seen and it currently has an expr definition; thus, any function which can be analyzed will be displayed. AVOIDING SET [Masterscope Path Option] Do not display any function in SET. AMONG is recognized as a synonym for AVOIDING NOT. For example, SHOW PATHS TO ERROR AVOIDING ON FILE2 will not display (or trace) any function on FILE2. NOTRACE SET [Masterscope Path Option] Do not trace from any element of SET. NOTRACE differs from AVOIDING in that a function which is marked NOTRACE will be printed, but the tree beyond it will not be expanded; the functions in an AVOIDING set will not be printed at all. For example, SHOW PATHS FROM ANY ON FILE1 NOTRACE ON FILE2 will display the tree of calls eminating from FILE1, but will not expand any function on FILE2. SEPARATE SET [Masterscope Path Option] Give each element of SET a separate tree. Note that FROM and TO only insure that the designated functions will be displayed. SEPARATE can be used to guarantee that certain functions will begin new tree structures. SEPARATE functions are displayed in the same manner as overflow lines; i.e., when one of the functions indicated by SEPARATE is found, it is printed followed by a forward reference (a lower-case letter in braces) and the tree for that function is then expanded below. LINELENGTH N [Masterscope Path Option] Resets LINELENGTH to N before displaying the tree. The linelength is used to determine when a part of the tree should "overflow" and be expanded lower. Error Messages 1 When you give Masterscope a command, the command is first parsed, i.e. translated to an internal representation, and then the internal representation is interpreted. If a command cannot be parsed, e.g. if you typed SHOW WHERE CALLED BY X, the message "Sorry, I can't parse that!" is printed and an error is generated. If the command is of the correct form but cannot be interpreted (e.g., the command EDIT WHERE ANY CONTAINS ANY) Masterscope will print the message "Sorry, that isn't implemented!" and generate an error. If the command requires that some functions having been analyzed (e.g., the command WHO CALLS X) and the database is empty, Masterscope will print the message "Sorry, no functions have been analyzed!" and generate an error. Macro Expansion 1 As part of analysis, Masterscope will expand the macro definition of called functions, if they are not otherwise defined (see Chapter 10). Masterscope macro expansion is controlled by the variable MSMACROPROPS: MSMACROPROPS [Variable] Value is an ordered list of macro-property names that Masterscope will search to find a macro definition. Only the kinds of macros that appear on MSMACROPROPS will be expanded. All others will be treated as function calls and left unexpanded. Initially (MACRO). MSMACROPROPS initially contains only MACRO (and not 10MACRO, DMACRO, etc.) in the theory that the machine-dependent macro definitions are more likely "optimizers". If you edit a macro, Masterscope will know to reanalyze the functions which call that macro. However, if your macro is of the "computed-macro" style, and it calls functions which you edit, Masterscope will not notice. You must be careful to tell masterscope to REANALYZE the appropriate functions (e.g., if you edit FOOEXPANDER which is used to expand FOO macros, you have to . REANALYZE ANY CALLING FOO. Affecting Masterscope Analysis 1 Masterscope analyzes the expr definitions of functions and notes in its database the relations that function has with other functions and with variables. To perform this analysis, Masterscope uses templates which describe the behavior of functions. For example, the information that SORT destructively modifies its first argument is contained in the template for SORT. Masterscope initially contains templates for most system functions which set variables, test their arguments, or perform destructive operations. A template is a list structure containing any of the following atoms: PPE [in Masterscope template] If an expression appears in this location, there is most likely a parenthesis error. Masterscope notes this as a "call" to the function "ppe" (lowercase). Therefore, SHOW WHERE ANY CALLS ppe will print out all possible parenthesis errors. When Masterscope finds a possible parenthesis error in the course of analyzing a function definition, rather than printing the usual ".", it prints out a "?" instead. NIL [in Masterscope template] The expression occuring at this location is not evaluated. SET [in Masterscope template] A variable appearing at this place is set. SMASH [in Masterscope template] The value of this expression is smashed. TEST [in Masterscope template] This expression is used as a predicate (that is, the only use of the value of the expression is whether it is NIL or non-NIL). PROP [in Masterscope template] The value of this expression is used as a property name. If the expression is of the form (QUOTE ATOM), Masterscope will note that ATOM is USED AS A PROPERTY NAME. For example, the template for GETPROP is (EVAL PROP . PPE). FUNCTION [in Masterscope template] The expression at this point is used as a functional argument. For example, the template for MAPC is (SMASH FUNCTION FUNCTION . PPE). FUNCTIONAL [in Masterscope template] The expression at this point is used as a functional argument. This is like FUNCTION, except that Masterscope distinguishes between functional arguments to functions which "compile open" from those that do not. For the latter (e.g. SORT and APPLY), FUNCTIONAL should be used rather than FUNCTION. EVAL [in Masterscope template] The expression at this location is evaluated (but not set, smashed, tested, used as a functional argument, etc.). RETURN [in Masterscope template] The value of the function (of which this is the template) is the value of this expression. TESTRETURN [in Masterscope template] A combination of TEST and RETURN: If the value of the function is non-NIL, then it is returned. For instance, a one-element COND clause is this way. EFFECT [in Masterscope template] The expression at this location is evaluated, but the value is not used. FETCH [in Masterscope template] An atom at this location is a field which is fetched. REPLACE [in Masterscope template] An atom at this location is a field which is replaced. RECORD [in Masterscope template] An atom at this location is used as a record name. CREATE [in Masterscope template] An atom at this location is a record which is created. BIND [in Masterscope template] An atom at this location is a variable which is bound. CALL [in Masterscope template] An atom at this location is a function which is called. CLISP [in Masterscope template] An atom at this location is used as a CLISP word. ! [in Masterscope template] This atom, which can only occur as the first element of a template, allows one to specify a template for the CAR of the function form. If ! doesn't appear, the CAR of the form is treated as if it had a CALL specified for it. In other words, the templates (.. EVAL) and (! CALL .. EVAL) are equivalent. If the next atom after a ! is NIL, this specifies that the function name should not be remembered. For example, the template for AND is (! NIL .. TEST RETURN), which means that if you see an "AND", don't remember it as being called. This keeps the Masterscope database from being cluttered by too many uninteresting relations; Masterscope also throws away relations for COND, CAR, CDR, and a couple of others. In addition to the above atoms which occur in templates, there are some "special forms" which are lists keyed by their CAR. .. TEMPLATE [in Masterscope template] Any part of a template may be preceded by the atom .. (two periods) which specifies that the template should be repeated an indefinite number (N>=0) of times to fill out the expression. For example, the template for COND might be (.. (TEST .. EFFECT RETURN)) while the template for SELECTQ is (EVAL .. (NIL .. EFFECT RETURN) RETURN). (BOTH TEMPLATE1 TEMPLATE2) [in Masterscope template] Analyze the current expression twice, using the each of the templates in turn. (IF EXPRESSION TEMPLATE1 TEMPLATE2) [in Masterscope template] Evaluate EXPRESSION at analysis time (the variable EXPR will be bound to the expression which corresponds to the IF), and if the result is non-NIL, use TEMPLATE1, otherwise TEMPLATE2. If EXPRESSION is a literal atom, it is APPLY'd to EXPR. For example, (IF LISTP (RECORD FETCH) FETCH) specifies that if the current expression is a list, then the first element is a record name and the second element a field name, otherwise it is a field name. (@ EXPRFORM TEMPLATEFORM) [in Masterscope template] Evaluate EXPRFORM giving EXPR, evaluate TEMPLATEFORM giving TEMPLATE. Then analyze EXPR with TEMPLATE. @ lets you compute on the fly both a template and an expression to analyze with it. The forms can use the variable EXPR, which is bound to the current expression. (MACRO . MACRO) [in Masterscope template] MACRO is interpreted in the same way as a macro (see Chapter 10) and the resulting form is analyzed. If the template is the atom MACRO alone, Masterscope will use the MACRO property of the function itself. This is useful when analyzing code which contains calls to user-defined macros. If you change a macro property (e.g., by editing it) of an atom which has template of MACRO, Masterscope will mark any function which used that macro as needing to be reanalyzed. Some examples of templates: function: template: DREVERSE (SMASH . PPE) AND (! NIL TEST .. RETURN) MAPCAR (EVAL FUNCTION FUNCTION) COND (! NIL .. (IF CDR (TEST .. EFFECT RETURN) (TESTRETURN . PPE))) Templates may be changed and new templates defined using the functions: (GETTEMPLATE FN) [Function] Returns the current template of FN. (SETTEMPLATE FN TEMPLATE) [Function] Changes the template for the function FN and returns the old value. If any functions in the database are marked as calling FN, they will be marked as needing re-analysis. Database Updating 1 Masterscope is interfaced to the editor and file package so that it notes whenever a function has been changed, either through editing or loading in a new definition. Whenever a command is given which requires knowing the information about a specific function, if that function has been noted as being changed, the function is automatically re-analyzed before the command is interpreted. If the command requires that all the information in the database be consistent (e.g., you ask WHO CALLS X) then all functions which have been marked as changed are re-analyzed. Masterscope Entries 1 (CALLS FN USEDATABASE %) [Function] FN can be a function name, a definition, or a form. CALLS will also work on compiled code. CALLS returns a list of four elements: a list of all the functions called by FN, a list of all the variables bound in FN, a list of all the variables used freely in FN, and a list of the variables used globally in FN. For the purpose of CALLS, variables used freely which are on GLOBALVARS or have a property GLOBALVAR value T are considered to be used globally. If USEDATABASE is NIL (or FN is not a litatom), CALLS will perform a one-time analysis of FN. Otherwise (i.e. if USEDATABASE is non-NIL and FN a function name), CALLS will use the information in Masterscope's database (FN will be analyzed first if necessary). (CALLSCCODE FN % %) [Function] The sub-function of CALLS which analyzes compiled code. CALLSCCODE returns a list of five elements: a list of all the functions called via "linked" function calls (not implemented in Interlisp-D), a list of all functions called regularly, a list of variables bound in FN, a list of variables used freely, and a list of variables used globally. (FREEVARS FN USEDATABASE) [Function] Equivalent to (CADDR (CALLS FN USEDATABASE)). Returns the list of variables used freely within FN. (MASTERSCOPE COMMAND %) [Function] Top level entry to Masterscope. If COMMAND is NIL, will enter into an executive in which you may enter commands. If COMMAND is not NIL, the command is interpreted and MASTERSCOPE will return the value that would be printed by the command. Note that only the question commands return meaningful values. (SETSYNONYM PHRASE MEANING %) [Function] Defines a new synonym for Masterscope's parser. Both OLDPHRASE and NEWPHRASE are words or lists of words; anywhere OLDPHRASE is seen in a command, NEWPHRASE will be substituted. For example, (SETSYNONYM 'GLOBALS '(VARS IN GLOBALVARS OR @(GETPROP X 'GLOBALVAR))) would allow you to refer with the single word GLOBALS to the set of variables which are either in GLOBALVARS or have a GLOBALVAR property. The following functions allow you to write your own routines using Masterscope's database: (PARSERELATION RELATION) [Function] RELATION is a relation phrase; e.g., (PARSERELATION '(USE FREELY)). PARSERELATION returns an internal representation for RELATION. For use in conjunction with GETRELATION. (GETRELATION ITEM RELATION INVERTED) [Function] RELATION is an internal representation as returned by PARSERELATION (if not, GETRELATION will first perform (PARSERELATION RELATION)); ITEM is an atom. GETRELATION returns the list of all atoms which have the given relation to ITEM. For example, (GETRELATION 'X '(USE FREELY)) returns the list of variables that X uses freely. If INVERTED is T, the inverse relation is used; e.g. (GETRELATION 'X '(USE FREELY) T) returns the list of functions which use X freely. If ITEM is NIL, GETRELATION will return the list of atoms which have RELATION with any other item; i.e., answers the question WHO RELATIONS ANY. GETRELATION does not check to see if ITEM has been analyzed, or that other functions that have been changed have been re-analyzed. (TESTRELATION ITEM RELATION ITEM2 INVERTED) [Function] Equivalent to (MEMB ITEM2 (GETRELATION ITEM RELATION INVERTED)), that is, tests if ITEM and ITEM2 are related via RELATION. If ITEM2 is NIL, the call is equivalent to (NOT (NULL (GETRELATION ITEM RELATION INVERTED))), i.e., TESTRELATION tests if ITEM has the given RELATION with any other item. (MAPRELATION RELATION MAPFN) [Function] Calls the function MAPFN on every pair of items related via RELATION. If (NARGS MAPFN) is 1, then MAPFN is called on every item which has the given RELATION to any other item. (MSNEEDUNSAVE FNS MSG MARKCHANGEFLG) [Function] Used to mark functions which depend on a changed record declaration (or macro, etc.), and which must be LOADed or UNSAVEd (see below). FNS is a list of functions to be marked, and MSG is a string describing the records, macros, etc. on which they depend. If MARKCHANGEFLG is non-NIL, each function in the list is marked as needing re-analysis. (UPDATEFN FN EVENIFVALID %) [Function] Equivalent to the command ANALYZE 'FN; that is, UPDATEFN will analyze FN if FN has not been analyzed before or if it has been changed since the time it was analyzed. If EVENIFVALID is non-NIL, UPDATEFN will re-analyze FN even if Masterscope thinks it has a valid analysis in the database. (UPDATECHANGED) [Function] Performs (UPDATEFN FN) on every function which has been marked as changed. (MSMARKCHANGED NAME TYPE REASON) [Function] Mark that NAME has been changed and needs to be reanalyzed. See MARKASCHANGED in Chapter 17. (DUMPDATABASE FNLST) [Function] Dumps the current Masterscope database on the current output file in a LOADable form. If FNLST is not NIL, DUMPDATABASE will only dump the information for the list of functions in FNLST. The variable DATABASECOMS is initialized to ((E (DUMPDATABASE))); thus, you may merely perform (MAKEFILE 'DATABASE.EXTENSION) to save the current Masterscope database. If a Masterscope database already exists when a DATABASE file is loaded, the database on the file will be merged with the one in core. Note that functions whose definitions are different from their definition when the database was made must be REANALYZEd if their new definitions are to be noticed. The Databasefns library package provides a more convenient way of saving databases along with the source files which they correspond to. Noticing Changes that Require Recompiling 1 When a record declaration, iterative statement operator or macro is changed, and Masterscope has "noticed" a use of that declaration or macro (i.e. it is used by some function known about in the database), Masterscope alerts you about those functions which might need to be re-compiled (e.g. they do not currently have expr definitions). Extra functions may be noticed. For example, if FOO contains (fetch (REC X) --), and some declaration other than REC which contains X is changed, Masterscope will still think that FOO needs to be loaded/unsaved. The functions which need recompiling are added to the list MSNEEDUNSAVE and a message is printed out: The functions FN1, FN2,... use macros which have changed. Call UNSAVEFNS() to load and/or unsave them. In this situation, the following function is useful: (UNSAVEFNS %) [Function] Uses LOADFNS or UNSAVEDEF to make sure that all functions in the list MSNEEDUNSAVE have expr definitions, and then sets MSNEEDUNSAVE to NIL. Note: If RECOMPILEDEFAULT (see Chapter 18) is set to CHANGES, UNSAVEFNS prints out "WARNING: you must set RECOMPILEDEFAULT to EXPRS in order to have these functions recompiled automatically". Implementation Notes 1 Masterscope keeps a database of the relations noticed when functions are analyzed. The relations are intersected to form "primitive relationships" such that there is little or no overlap of any of the primitives. For example, the relation SET is stored as the union of SET LOCAL and SET FREE. The BIND relation is divided into BIND AS ARG, BIND AND NOT USE, and SET LOCAL, SMASH LOCAL, etc. Splitting the relations in this manner reduces the size of the database considerably, to the point where it is reasonable to maintain a Masterscope database for a large system of functions during a normal debugging session. Each primitive relationship is stored in a pair of hash-tables, one for the "forward" direction and one for the "reverse". For example, there are two hash tables, USE AS PROPERTY and USED AS PROPERTY. To retrieve the information from the database, Masterscope performs unions of the hash-values. For example, to answer FOO BINDS WHO Masterscope will look in all of the tables which make up the BIND relation. The "internal representation" returned by PARSERELATION is just a list of dotted pairs of hash-tables. To perform GETRELATION requires only mapping down that list, doing GETHASH's on the appropriate hash-tables and UNIONing the result. Hash tables are used for a variety of reasons: storage space is smaller; it is not necessary to maintain separate lists of which functions have been analyzed (a special table, DOESN'T DO ANYTHING is maintained for functions which neither call other functions nor bind or use any variables); and accessing is relatively fast. Within any of the tables, if the hash-value would be a list of one atom, then the atom itself, rather than the list, is stored as the hash-value. This also reduces the size of the database significantly. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "19-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "19-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "19-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "19-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "19-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "19-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))!5,,2Hll-llT4(TT2l2l~~F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR,ll5,ll,ll2HZZ3HZT3$$(T0$$T3HZ +T,ll0$$T-T3(T,2,HH +,HH,HH5,,TITAN +TITAN +TITAN +CLASSIC + HELVETICA + HELVETICA  HELVETICA HELVETICATITAN +CLASSIC +CLASSIC +CLASSIC +MODERNMODERN +MODERN +MODERN +  HRULE.GETFNCLASSIC +     HRULE.GETFNCLASSIC +   +  + HRULE.GETFNCLASSIC +     HRULE.GETFNCLASSIC +  IM.CHAP.GETFN HELVETICA HRULE.GETFNMODERN + X  C         , &      !      +  +    + )        +   ,     # ! -      .  (  (   :  4     P     +  +   T   +  +H  + +   9  y + ' A   ,  0   >  *   8  . S  G  HRULE.GETFNMODERN +O  ' = + +     + + &  +"  6   +  D V  +  <       K  ;         +A     +   +      -  )   t    ]   + L  + 7  +     + 1  +  *    + -  +   '  +  + = +   +  +  +  +    +  +    + R  +  +      +  +A    +    5    +4 +     $  & %  V   +Y    6           +n $ '     U !      .    (    (    +   + + +      +#  6  +   )       +   !     +-  2 / /      , ]  ,  2    +   +   M    + + +     + +T "           +    +   $  +   *-     !                 J    * <    + _  +  +  s  X  C  M    + )  ]  +,  +(  + F _    l         e  +@    +   x    =        +      H             W        A                    E            -       B          +  .    $       <       +     *  +    +               W       '       ? + ; +  }   E       }      8            $      2              -      V         o     +w     &   +                        (  + 7   u + +        +   +    ]        J   +! z    +  X  ;     +! a  6       + +!  + +Z > +    +! ` +  7     +, +    8  " [      +!  +  K       h  +  +!  + }          +   +P  + 8      O    +  +  +  +!   +   D   ]    +   +  +    +  %  * +    +          +   +. +0 +- +G $   +    +. +  +    +  +_  .    C     L ?  ! c    -    %  +     + % 2   + 9 J     ]IM.INDEX.GETFN! + +$            ^      F  r    -  +            QIM.INDEX.GETFN  +  + +  +    5  (                                 n W U $  & B & +  . (    O 2         0   ! !     Z  + '        2    +  <         c    ;      ^    - 7  D   HRULE.GETFNMODERN +j + [ + k +  7 !     $      -                &             +   (    C  !    C        %  C     .             +      + 8 + F  +  + / +    + 0  +    +  #  +  +      2        +    +     $ )      "  +    +   +   % -      + !  +    $  S  / - /  &      +   +    ?  R  l   +   +  +   +  HRULE.GETFNMODERN +  |    &  n A '   HRULE.GETFNMODERN +     a      +    a  .       HRULE.GETFNMODERN + +N  L  F   U 4       +   :   *   (   n       [    +   +  !        ]      +  M      +      q   Z  +      '  3     H   5   6   2   6   6   8   1   m      '  2        a    "        w      + 3  Z  +I  +              + +  N    + +   + +  :      +  + + +         +    +  + +  +  +  +  s  +   +   +}  !   W     ! E H    +    +   +  &  +T  +-  HRULE.GETFNMODERN +   +>  HRULE.GETFNMODERN +    +    +3  #  H  +'  +-  +/  +  % +    ) +    +  %  + +    +  5  +&    +     +   +  +I    +     + + 4  +    +   $  +  D  +  ! |    +   6 + +' + +$  ; .  - + [    +  +   (  +     +   +. +     +   + @  +  $    +  % )    +   *  +  +(    +     +  +Z    +      +   +  +  +   +  +  +  +      +  +  +  +  +  +  +    +    +$  +    +  +-  +  +    +    +*  +L +H    +     +    +  +\ +      +D      + 4    +   +  +3     +  G    +   =  +      + \  .  * HRULE.GETFNMODERN +  +  "    /  Y    +  + #, 5         - &     +     j  HRULE.GETFNMODERN +                    z   >  6 < -  &      N  z \ No newline at end of file diff --git a/docs/porter-irm/vol2/20-DWIM.INTERPRESS b/docs/porter-irm/vol2/20-DWIM.INTERPRESS new file mode 100644 index 00000000..983df4f7 Binary files /dev/null and b/docs/porter-irm/vol2/20-DWIM.INTERPRESS differ diff --git a/docs/porter-irm/vol2/20-DWIM.TEDIT b/docs/porter-irm/vol2/20-DWIM.TEDIT new file mode 100644 index 00000000..f1b83c41 --- /dev/null +++ b/docs/porter-irm/vol2/20-DWIM.TEDIT @@ -0,0 +1,137 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 20. DWIM 1 20. DWIM 1 "20"20. DWIM 6 A surprisingly large percentage of the errors made by Interlisp users are of the type that could be corrected by another Lisp programmer without any information about the purpose of the program or expression in question, e.g., misspellings, certain kinds of parentheses errors, etc. To correct these types of errors we have implemented in Interlisp a DWIM facility, short for Do-What-I-Mean. DWIM is called automatically whenever an error occurs in the evaluation of an Interlisp expression. (Currently, DWIM only operates on unbound atoms and undefined function errors.) DWIM then proceeds to try to correct the mistake using the current context of computation plus information about what you had previously been doing (and what mistakes he had been making) as guides to the remedy of the error. If DWIM is able to make the correction, the computation continues as though no error had occurred. Otherwise, the procedure is the same as though DWIM had not intervened: a break occurs, or an unwind to the last ERRORSET (see Chapter 14). The following protocol illustrates the operation of DWIM. For example, suppose you define the factorial function (FACT N) as follows: DEFINEQ((FACT (LAMBDA (N) (COND ((ZEROP N0 1) ((T (ITIMS N (FACCT 9SUB1 N] (FACT) Note that the definition of FACT contains several mistakes: ITIMES and FACT have been misspelled; the 0 in N0 was intended to be a right parenthesis, but the Shift key was not pressed; similarly, the 9 in 9SUB1 was intended to be a left parenthesis; and finally, there is an extra left parenthesis in front of the T that begins the final clause in the conditional. PRETTYPRNT((FACCT] =PRETTYPRINT =FACT (FACT [LAMBDA (N) (COND ((ZEROP N0 1) ((T (ITIMS N (FACCT 9SUB1 N]) (FACT) After defining FACT, you want to look at its definition using PRETTYPRINT, which you unfortunately misspell. Since there is no function PRETTYPRNT in the system, an undefined function error occurs, and DWIM is called. DWIM invokes its spelling corrector, which searches a list of functions frequently used (by this user) for the best possible match. Finding one that is extremely close, DWIM proceeds on the assumption that PRETTYPRNT meant PRETTYPRINT, notifies you of this, and calls PRETTYPRINT. At this point, PRETTYPRINT would normally print (FACCT NOT PRINTABLE) and exit, since FACCT has no definition. Note that this is not an Interlisp error condition, so that DWIM would not be called as described above. However, it is obviously not what you meant. This sort of mistake is corrected by having PRETTYPRINT itself explicitly invoke the spelling corrector portion of DWIM whenever given a function with no EXPR definition. Thus, with the aid of DWIM PRETTYPRINT is able to determine that you want to see the definition of the function FACT, and proceeds accordingly. FACT(3] N0 [IN FACT] -> N ) ? YES [IN FACT] (COND -- ((T --))) -> (COND -- (T --)) ITIMS [IN FACT] -> ITIMES FACCT [IN FACT] -> FACT 9SUB1 [IN FACT] -> ( SUB1 ? YES 6 PP FACT (FACT [LAMBDA (N) (COND ((ZEROP N) 1) (T (ITIMES N (FACT (SUB1 N]) FACT You now call FACT. During its execution, five errors occur, and DWIM is called five times. At each point, the error is corrected, a message is printed describing the action taken, and the computation is allowed to continue as if no error had occurred. Following the last correction, 6 is printed, the value of (FACT 3). Finally, you prettyprints the new, now correct, definition of FACT. In this particular example, you were operating in TRUSTING mode, which gives DWIM carte blanche for most corrections. You can also operate in CAUTIOUS mode, in which case DWIM will inform you of intended corrections before they are made, and allow you to approve or disapprove of them. If DWIM was operating in CAUTIOUS mode in the example above, it would proceed as follows: FACT(3) N0 [IN FACT] -> N ) ? YES U.D.F. T [IN FACT] FIX? YES [IN FACT] (COND -- ((T --))) -> (COND -- (T --)) ITIMS [IN FACT] -> ITIMES ? ...YES FACCT [IN FACT] -> FACT ? ...YES 9SUB1 [IN FACT] -> ( SUB1 ? NO U.B.A. (9SUB1 BROKEN) : For most corrections, if you do not respond in a specified interval of time, DWIM automatically proceeds with the correction, so that you need intervene only when you do not approve. In the example, you responded to the first, second, and fifth questions; DWIM responded for you on the third and fourth. DWIM uses ASKUSER for its interactions with you (see Chapter 26). Whenever an interaction is about to take place and you have typed ahead, ASKUSER types several bells to warn you to stop typing, then clears and saves the input buffers, restoring them after the interaction is complete. Thus if you typed ahead before a DWIM interaction, DWIM will not confuse your type-ahead with the answer to its question, nor will your type-ahead be lost. The bells are printed by the function PRINTBELLS, which can be advised or redefined for specialized applications, e.g. to flash the screen for a display terminal. A great deal of effort has gone into making DWIM "smart", and experience with a large number of users indicates that DWIM works very well; DWIM seldom fails to correct an error you feel it should have, and almost never mistakenly corrects an error. However, it is important to note that even when DWIM is wrong, no harm is done: since an error had occurred, you would have had to intervene anyway if DWIM took no action. Thus, if DWIM mistakenly corrects an error, you simply interrupt or abort the computation, UNDO the DWIM change using UNDO (see Chapter 13), and make the correction you would have had to make without DWIM. An exception is if DWIM's correction mistakenly caused a destructive computation to be initiated, and information was lost before you could interrupt. We have not yet had such an incident occur. (DWIM(DWIM (Function) NIL NIL ("20") 3) X) [Function] Used to enable/disable DWIM. If X is the litatom C, DWIM is enabled in CAUTIOUS mode, so that DWIM will ask you before making corrections. If X is T, DWIM is enabled in TRUSTING mode, so DWIM will make most corrections automatically. If X is NIL, DWIM is disabled. Interlisp initially has DWIM enabled in CAUTIOUS mode. DWIM returns CAUTIOUS, TRUSTING or NIL, depending to what mode it has just been put into. For corrections to expressions typed in for immediate execution (typed into LISPX, Chapter 13), DWIM always acts as though it were in TRUSTING mode, i.e., no approval necessary. For certain types of corrections, e.g., run-on spelling corrections, 9-0 errors, etc., DWIM always acts like it was in CAUTIOUS mode, and asks for approval. In either case, DWIM always informs you of its action as described below. Spelling Correction Protocol 1 One type of error that DWIM can correct is the misspelling of a function or a variable name. When an unbound litatom or undefined function error occurs, DWIM tries to correct the spelling of the bad litatom. If a litatom is found whose spelling is "close" to the offender, DWIM proceeds as follows: If the correction occurs in the typed-in expression, DWIM prints =CORRECT-SPELLINGcr and continues evaluating the expression. For example: (SETQ FOO (IPLUSS 1 2)) =IPLUS 3 If the correction does not occur in type-in, DWIM prints BAD-SPELLING [IN FUNCTION-NAME] -> CORRECT-SPELLING The appearance of -> is to call attention to the fact that the user's function will be or has been changed. Then, if DWIM is in TRUSTING mode, it prints a carriage return, makes the correction, and continues the computation. If DWIM is in CAUTIOUS mode, it prints a few spaces and ? and then wait for approval. The user then has six options: 1. Type Y. DWIM types es, and proceeds with the correction. 2. Type N. DWIM types o, and does not make the correction. 3. Type . DWIM does not make the correction, and furthermore guarantees that the error will not cause a break. 4. Type Control-E. For error correction, this has the same effect as typing N. 5. Do nothing. In this case DWIM waits for DWIMWAIT seconds, and if you have not responded, DWIM will type ... followed by the default answer. The default on spelling corrections is determined by the value of the variable FIXSPELLDEFAULT, whose top level value is initially Y. 6. Type space or carriage-return. In this case DWIM will wait indefinitely. This option is intended for those cases where the user wants to think about his answer, and wants to insure that DWIM does not get "impatient" and answer for him. The procedure for spelling correction on other than Interlisp errors is analogous. If the correction is being handled as type-in, DWIM prints = followed by the correct spelling, and returns it to the function that called DWIM. Otherwise, DWIM prints the incorrect spelling, followed by the correct spelling. Then, if DWIM if in TRUSTING mode, DWIM prints a carriage-return and returns the correct spelling. Otherwise, DWIM prints a few spaces and a ? and waits for approval. You can then respond with Y, N, Control-E, space, carriage return, or do nothing as described above. The spelling corrector itself is not ERRORSET protected like the DWIM error correction routines. Therefore, typing N and typing Control-E may have different effects when the spelling corrector is called directly. The former simply instructs the spelling corrector to return NIL, and lets the calling function decide what to do next; the latter causes an error which unwinds to the last ERRORSET, however far back that may be. Parentheses Errors Protocol 1 When an unbound litatom or undefined error occurs, and the offending litatom contains 9 or 0, DWIM tries to correct errors caused by typing 9 for left parenthesis and 0 for right parenthesis. In these cases, the interaction with you is similar to that for spelling correction. If the error occurs in type-in, DWIM types =CORRECTIONcr, and continues evaluating the expression. For example: (SETQ FOO 9IPLUS 1 2] = ( IPLUS 3 If the correction does not occur in type-in, DWIM prints BAD-ATOM [IN FUNCTION-NAME] -> CORRECTION ? and then waits for approval. You then have the same six options as for spelling correction, except the waiting time is 3*DWIMWAIT seconds. If you type Y, DWIM operates as if it were in TRUSTING mode, i.e., it makes the correction and prints its message. Actually, DWIM uses the value of the variables LPARKEY and RPARKEY to determine the corresponding lower case character for left and right parentheses. LPARKEY and RPARKEY are initially 9 and 0 respectively, but they can be reset for other keyboard layouts, e.g., on some terminals left parenthesis is over 8, and right parenthesis is over 9. Undefined Function T Errors 1 When an undefined function error occurs, and the offending function is T, DWIM tries to correct certain types of parentheses errors involving a T clause in a conditional. DWIM recognizes errors of the following forms: (COND --) (T --) The T clause appears outside and immediately following the COND. (COND -- (-- & (T --))) The T clause appears inside a previous clause. (COND -- ((T --))) The T clause has an extra pair of parentheses around it. For undefined function errors that are not one of these three types, DWIM takes no corrective action at all, and the error will occur. If the error occurs in type-in, DWIM simply types T FIXED and makes the correction. Otherwise if DWIM is in TRUSTING mode, DWIM makes the correction and prints the message: [IN FUNCTION-NAME] {BAD-COND} -> {CORRECTED-COND} If DWIM is in CAUTIOUS mode, DWIM prints UNDEFINED FUNCTION T [IN FUNCTION-NAME] FIX? and waits for approval. You then have the same options as for spelling corrections and parenthesis errors. If you type Y or default, DWIM makes the correction and prints its message. Having made the correction, DWIM must then decide how to proceed with the computation. In the first case, (COND --) (T --), DWIM cannot know whether the T clause would have been executed if it had been inside of the COND. Therefore DWIM asks you CONTINUE WITH T CLAUSE (with a default of YES). If you type N, DWIM continues with the form after the COND, i.e., the form that originally followed the T clause. In the second case, (COND -- (-- & (T --))), DWIM has a different problem. After moving the T clause to its proper place, DWIM must return as the value of & as the value of the COND. Since this value is no longer around, DWIM asks you OK TO REEVALUATE and then prints the expression corresponding to &. If you type Y, or default, DWIM continues by reevaluating &, otherwise DWIM aborts, and a U.D.F. T error will then occur (even though the COND has in fact been fixed). If DWIM can determine for itself that the form can safely be reevaluated, it does not consult you before reevaluating. DWIM can do this if the form is atomic, or CAR of the form is a member of the list OKREEVALST, and each of the arguments can safely be reevaluated. For example, (SETQ X (CONS (IPLUS Y Z) W)) is safe to reevaluate because SETQ, CONS, and IPLUS are all on OKREEVALST. In the third case, (COND -- ((T --))), there is no problem with continuation, so no further interaction is necessary. DWIM(DWIM NIL NIL NIL ("20") 5 SUBNAME OPERATION SUBTEXT operation) Operation 1 Whenever the interpreter encounters an atomic form with no binding, or a non-atomic form CAR of which is not a function or function object, it calls the function FAULTEVAL. Similarly, when APPLY is given an undefined function, FAULTAPPLY is called. When DWIM is enabled, FAULTEVAL and FAULTAPPLY are redefined to first call the DWIM package, which tries to correct the error. If DWIM cannot decide how to fix the error, or you disapprove of DWIM's correction (by typing N), or you type Control-E, then FAULTEVAL and FAULTAPPLY cause an error or break. If you type to DWIM, DWIM exits by performing (RETEVAL 'FAULTEVAL '(ERROR!)), so that an error will be generated at the position of the call to FAULTEVAL. If DWIM can (and is allowed to) correct the error, it exits by performing RETEVAL of the corrected form, as of the position of the call to FAULTEVAL or FAULTAPPLY. Thus in the example at the beginning of the chapter, when DWIM determined that ITIMS was ITIMES misspelled, DWIM called RETEVAL with (ITIMES N (FACCT 9SUB1 N)). Since the interpreter uses the value returned by FAULTEVAL exactly as though it were the value of the erroneous form, the computation will thus proceed exactly as though no error had occurred. In addition to continuing the computation, DWIM also repairs the cause of the error whenever possible; in the above example, DWIM also changed (with RPLACA) the expression (ITIMS N (FACCT 9SUB1 N)) that caused the error. Note that if your program had computed the form and called EVAL, it would not be possible to repair the cause of the error, although DWIM could correct the misspelling each time it occurred. Error correction in DWIM is divided into three categories: unbound atoms, undefined CAR of form, and undefined function in APPLY. Assuming that the user approves DWIM's corrections, the action taken by DWIM for the various types of errors in each of these categories is summarized below. DWIM Correction: Unbound Atoms If DWIM is called as the result of an unbound atom error, it proceeds as follows: 1. If the first character of the unbound atom is ', DWIM assumes that you (intentionally) typed 'ATOM for (QUOTE ATOM) and makes the appropriate change. No message is typed, and no approval is requested. If the unbound atom is just ' itself, DWIM assumes you want the next expression quoted, e.g., (CONS X '(A B C)) will be changed to (CONS X (QUOTE (A B C))). Again no message will be printed or approval asked. If no expression follows the ', DWIM gives up. Note: ' is normally defined as a read-macro character which converts 'FOO to (QUOTE FOO) on input, so DWIM will not see the ' in the case of expressions that are typed-in. 2. If CLISP (see Chapter 21) is enabled, and the atom is part of a CLISP construct, the CLISP transformation is performed and the result returned. For example, N-1 is transformed to (SUB1 N), and (... FOO_3 ...) is transformed into (... (SETQ FOO 3) ...). 3. If the atom contains an 9 (actually LPARKEY, see the DWIM Functions and Variables section below), DWIM assumes the 9 was intended to be a left parenthesis, and calls the editor to make appropriate repairs on the expression containing the atom. DWIM assumes that you did not notice the mistake, i.e., that the entire expression was affected by the missing left parenthesis. For example, if you type (SETQ X (LIST (CONS 9CAR Y) (CDR Z)) Y), the expression will be changed to (SETQ X (LIST (CONS (CAR Y) (CDR Z)) Y)). The 9 does not have to be the first character of the atom: DWIM will handle (CONS X9CAR Y) correctly. 4. If the atom contains a 0 (actually RPARKEY, see the DWIM Functions and Variables section below), DWIM assumes the 0 was intended to be a right parenthesis and operates as in the case above. 5. If the atom begins with a 7, the 7 is treated as a '. For example, 7FOO becomes 'FOO, and then (QUOTE FOO). 6. The expressions on DWIMUSERFORMS (see the DWIMUSERFORMS section below) are evaluated in the order that they appear. If any of these expressions returns a non-NIL value, this value is treated as the form to be used to continue the computation, it is evaluated and its value is returned by DWIM. 7. If the unbound atom occurs in a function, DWIM attempts spelling correction using the LAMBDA and PROG variables of the function as the spelling list. 8. If the unbound atom occurred in a type-in to a break, DWIM attempts spelling correction using the LAMBDA and PROG variables of the broken function as the spelling list. 9. Otherwise, DWIM attempts spelling correction using SPELLINGS3 (see the Spelling Lists section below). 10. If all of the above fail, DWIM gives up. Undefined CAR of Form If DWIM is called as the result of an undefined CAR of form error, it proceeds as follows: 1. If CAR of the form is T, DWIM assumes a misplaced T clause and operates as described in the Undefined Function T Errors section above. 2. If CAR of the form is F/L, DWIM changes the "F/L" to "FUNCTION(LAMBDA". For example, (F/L (Y) (PRINT (CAR Y))) is changed to (FUNCTION (LAMBDA (Y) (PRINT (CAR Y))). No message is printed and no approval requested. If you omit the variable list, DWIM supplies (X), e.g., (F/L (PRINT (CAR X))) is changed to (FUNCTION (LAMBDA (X) (PRINT (CAR X)))). DWIM determines that you have supplied the variable list when more than one expression follows F/L, CAR of the first expression is not the name of a function, and every element in the first expression is atomic. For example, DWIM will supply (X) when correcting (F/L (PRINT (CDR X)) (PRINT (CAR X))). 3. If CAR of the form is a CLISP word (IF, FOR, DO, FETCH, etc.), the indicated CLISP transformation is performed, and the result is returned as the corrected form. See Chapter 21. 4. If CAR of the form has a function definition, DWIM attempts spelling correction on CAR of the definition using as spelling list the value of LAMBDASPLST, initially (LAMBDA NLAMBDA). 5. If CAR of the form has an EXPR or CODE property, DWIM prints CAR-OF-FORM UNSAVED, performs an UNSAVEDEF, and continues. No approval is requested. 6. If CAR of the form has a FILEDEF property, the definition is loaded from a file (except when DWIMIFYing). If the value of the property is atomic, the entire file is to be loaded. If the value is a list, CAR is the name of the file and CDR the relevant functions, and LOADFNS will be used. For both cases, LDFLG will be SYSLOAD (see Chapter 17). DWIM uses FINDFILE (Chapter 24), so that the file can be on any of the directories on DIRECTORIES, initially (NIL NEWLISP LISP LISPUSERS). If the file is found, DWIM types SHALL I LOAD followed by the file name or list of functions. If you approve, DWIM loads the function(s) or file, and continues the computation. 7. If CLISP is enabled, and CAR of the form is part of a CLISP construct, the indicated transformation is performed, e.g., (NN-1) becomes (SETQ N (SUB1 N)). 8. If CAR of the form contains an 9, DWIM assumes a left parenthesis was intended e.g., (CONS9CAR X). 9. If CAR of the form contains a 0, DWIM assumes a right parenthesis was intended. 10. If CAR of the form is a list, DWIM attempts spelling correction on CAAR of the form using LAMBDASPLST as spelling list. If successful, DWIM returns the corrected expression itself. 11. The expressions on DWIMUSERFORMS are evaluated in the order they appear. If any returns a non-NIL value, this value is treated as the corrected form, it is evaluated, and DWIM returns its value. 12. Otherwise, DWIM attempts spelling correction using SPELLINGS2 as the spelling list (see the Spelling Lists section below). When DWIMIFYing, DWIM also attemps spelling correction on function names not defined but previously encountered, using NOFIXFNSLST as a spelling list (see Chapter 21). 13. If all of the above fail, DWIM gives up. Undefined Function in APPLY If DWIM is called as the result of an undefined function in APPLY error, it proceeds as follows: 1. If the function has a definition, DWIM attempts spelling correction on CAR of the definition using LAMBDASPLST as spelling list. 2. If the function has an EXPR or CODE property, DWIM prints FN UNSAVED, performs an UNSAVEDEF and continues. No approval is requested. 3. If the function has a property FILEDEF, DWIM proceeds as in case 6 of undefined CAR of form. 4. If the error resulted from type-in, and CLISP is enabled, and the function name contains a CLISP operator, DWIM performs the indicated transformation, e.g., type FOO(APPEND FIE FUM). 5. If the function name contains an 9, DWIM assumes a left parenthesis was intended, e.g., EDIT9FOO]. 6. If the "function" is a list, DWIM attempts spelling correction on CAR of the list using LAMBDASPLST as spelling list. 7. The expressions on DWIMUSERFORMS are evaluated in the order they appear, and if any returns a non-NIL value, this value is treated as the function used to continue the computation, i.e., it will be applied to its arguments. 8. DWIM attempts spelling correction using SPELLINGS1 as the spelling list. 9. DWIM attempts spelling correction using SPELLINGS2 as the spelling list. 10. If all fail, DWIM gives up. DWIMUSERFORMS 1 The variable DWIMUSERFORMS(DWIMUSERFORMS (Variable) NIL NIL ("20") 8) provides a convenient way of adding to the transformations that DWIM performs. For example, you might want to change atoms of the form $X to (QA4LOOKUP X). Before attempting spelling correction, but after performing other transformations (F/L, 9, 0, CLISP, etc.), DWIM evaluates the expressions on DWIMUSERFORMS in the order they appear. If any expression returns a non-NIL value, this value is treated as the transformed form to be used. If DWIM was called from FAULTEVAL, this form is evaluated and the resulting value is returned as the value of FAULTEVAL. If DWIM is called from FAULTAPPLY, this form is treated as a function to be applied to FAULTARGS, and the resulting value is returned as the value of FAULTAPPLY. If all of the expressions on DWIMUSERFORMS return NIL, DWIM proceeds as though DWIMUSERFORMS=NIL, and attempts spelling correction. Note that DWIM simply takes the value and returns it; the expressions on DWIMUSERFORMS are responsible for making any modifications to the original expression. The expressions on DWIMUSERFORMS should make the transformation permanent, either by associating it with FAULTX via CLISPTRAN, or by destructively changing FAULTX. In order for an expression on DWIMUSERFORMS to be able to be effective, it needs to know various things about the context of the error. Therefore, several of DWIM's internal variables have been made SPECVARS (see Chapter 18) and are therefore "visible" to DWIMUSERFORMS. Below are a list of those variables that may be useful. FAULTX(FAULTX (Variable) NIL NIL ("20") 9) [Variable] For unbound atom and undefined car of form errors, FAULTX is the atom or form. For undefined function in APPLY errors, FAULTX is the name of the function. FAULTARGS(FAULTARGS (Variable) NIL NIL ("20") 9) [Variable] For undefined function in APPLY errors, FAULTARGS is the list of arguments. FAULTARGS may be modified or reset by expressions on DWIMUSERFORMS. FAULTAPPLYFLG(FAULTAPPLYFLG (Variable) NIL NIL ("20") 9) [Variable] Value is T for undefined function in APPLY errors; NIL otherwise. The value of FAULTAPPLYFLG after an expression on DWIMUSERFORMS returns a non-NIL value determines how the latter value is to be treated. Following an undefined function in APPLY error, if an expression on DWIMUSERFORMS sets FAULTAPPLYFLG to NIL, the value returned is treated as a form to be evaluated, rather than a function to be applied. FAULTAPPLYFLG is necessary to distinguish between unbound atom and undefined function in APPLY errors, since FAULTARGS may be NIL and FAULTX atomic in both cases. TAIL(TAIL (Variable) NIL NIL ("20") 9) [Variable] For unbound atom errors, TAIL is the tail of the expression CAR of which is the unbound atom. DWIMUSERFORMS expression can replace the atom by another expression by performing (/RPLACA TAIL EXPR) PARENT(PARENT (Variable) NIL NIL ("20") 9) [Variable] For unbound atom errors, PARENT is the form in which the unbound atom appears. TAIL is a tail of PARENT. TYPE-IN?(TYPE-IN? (Variable) NIL NIL ("20") 9) [Variable] True if the error occurred in type-in. FAULTFN(FAULTFN (Variable) NIL NIL ("20") 9) [Variable] Name of the function in which error occurred. FAULTFN is TYPE-IN when the error occurred in type-in, and EVAL or APPLY when the error occurred under an explicit call to EVAL or APPLY. DWIMIFYFLG(DWIMIFYFLG (Variable) NIL NIL ("20") 9) [Variable] True if the error was encountered while DWIMIFYing (as opposed to happening while running a program). EXPR(EXPR (Variable) NIL NIL ("20") 9) [Variable] Definition of FAULTFN, or argument to EVAL, i.e., the superform in which the error occurs. The initial value of DWIMUSERFORMS is ((DWIMLOADFNS?)). DWIMLOADFNS? is a function for automatically loading functions from files. If DWIMLOADFNSFLG is T (its initial value), and CAR of the form is the name of a function, and the function is contained on a file that has been noticed by the file package, the function is loaded, and the computation continues. DWIM(DWIM NIL NIL NIL ("20") 10 SUBNAME FUNCTIONS SUBTEXT functions) Functions and Variables(DWIM NIL NIL NIL ("20") 10 SUBNAME VARIABLES SUBTEXT variables) 1 DWIMWAIT(DWIMWAIT (Variable) NIL NIL ("20") 10) [Variable] Value is the number of seconds that DWIM will wait before it assumes that you are not going to respond to a question and uses the default response FIXSPELLDEFAULT. DWIM operates by dismissing for 250 milliseconds, then checking to see if anything has been typed. If not, it dismisses again, etc. until DWIMWAIT seconds have elapsed. Thus, there will be a delay of at most 1/4 second before DWIM responds to your answer. FIXSPELLDEFAULT(FIXSPELLDEFAULT (Variable) NIL NIL ("20") 10) [Variable] If approval is requested for a spelling correction, and you do not respond, defaults to value of FIXSPELLDEFAULT, initially Y. FIXSPELLDEFAULT is rebound to N when DWIMIFYing. ADDSPELLFLG(ADDSPELLFLG (Variable) NIL NIL ("20") 10) [Variable] If NIL, suppresses calls to ADDSPELL. Initially T. NOSPELLFLG(NOSPELLFLG (Variable) NIL NIL ("20") 10) [Variable] If T, suppresses all spelling correction. If some other non-NIL value, suppresses spelling correction in programs but not type-in. NOSPELLFLG is initially NIL. It is rebound to T when compiling from a file. RUNONFLG(RUNONFLG (Variable) NIL NIL ("20") 10) [Variable] If NIL, suppresses run-on spelling corrections. Initially NIL. DWIMLOADFNSFLG(DWIMLOADFNSFLG (Variable) NIL NIL ("20") 10) [Variable] If T, tells DWIM that when it encounters a call to an undefined function contained on a file that has been noticed by the file package, to simply load the function. DWIMLOADFNSFLG is initially T (see above). LPARKEY(LPARKEY (Variable) NIL NIL ("20") 10) [Variable] RPARKEY(RPARKEY (Variable) NIL NIL ("20") 10) [Variable] DWIM uses the value of the variables LPARKEY and RPARKEY (initially 9 and 0 respectively) to determine the corresponding lower case character for left and right parentheses. LPARKEY and RPARKEY can be reset for other keyboard layouts. For example, on some terminals left parenthesis is over 8, and right parenthesis is over 9. OKREEVALST(OKREEVALST (Variable) NIL NIL ("20") 10) [Variable] The value of OKREEVALST is a list of functions that DWIM can safely reevaluate. If a form is atomic, or CAR of the form is a member of OKREEVALST, and each of the arguments can safely be reevaluated, then the form can be safely reevaluated. For example, (SETQ X (CONS (IPLUS Y Z) W)) is safe to reevaluate because SETQ, CONS, and IPLUS are all on OKREEVALST. DWIMFLG(DWIMFLG (Variable) NIL NIL ("20") 10) [Variable] DWIMFLG=NIL, all DWIM operations are disabled. (DWIM 'C) and (DWIM T) set DWIMFLG to T; (DWIM NIL) sets DWIMFLG to NIL. APPROVEFLG (APPROVEFLG% (Variable) NIL NIL ("20") 11) [Variable] APPROVEFLG=T if DWIM should ask the user for approval before making a correction that will modify the definition of one of his functions; NIL otherwise. When DWIM is put into CAUTIOUS mode with (DWIM 'C), APPROVEFLG is set to T; for TRUSTING mode, APPROVEFLG is set to NIL. LAMBDASPLST(LAMBDASPLST (Variable) NIL NIL ("20") 11) [Variable] DWIM uses the value of LAMBDASPLST as the spelling list when correcting "bad" function definitions. Initially (LAMBDA NLAMBDA). You may wish to add to LAMBDASPLST if you elect to define new "function types" via an appropriate DWIMUSERFORMS entry. For example, the QLAMBDAs of SRI's QLISP are handled in this way. Spelling Correction(SPELLING% CORRECTION NIL Spelling% correction NIL ("20") 11) 1 The spelling corrector is given as arguments a misspelled word (word means literal atom), a spelling list (a list of words), and a number: XWORD, SPLST, and REL respectively. Its task is to find that word on SPLST which is closest to XWORD, in the sense described below. This word is called a respelling of XWORD. REL specifies the minimum "closeness" between XWORD and a respelling. If the spelling corrector cannot find a word on SPLST closer to XWORD than REL, or if it finds two or more words equally close, its value is NIL, otherwise its value is the respelling. The spelling corrector can also be given an optional functional argument, FN, to be used for selecting out a subset of SPLST, i.e., only those members of SPLST that satisfy FN will be considered as possible respellings. The exact algorithm for computing the spelling metric is described later, but briefly "closeness" is inversely proportional to the number of disagreements between the two words, and directly proportional to the length of the longer word. For example, PRTTYPRNT is "closer" to PRETTYPRINT than CS is to CONS even though both pairs of words have the same number of disagreements. The spelling corrector operates by proceeding down SPLST, and computing the closeness between each word and XWORD, and keeping a list of those that are closest. Certain differences between words are not counted as disagreements, for example a single transposition, e.g., CONS to CNOS, or a doubled letter, e.g., CONS to CONSS, etc. In the event that the spelling corrector finds a word on SPLST with no disagreements, it will stop searching and return this word as the respelling. Otherwise, the spelling corrector continues through the entire spelling list. Then if it has found one and only one "closest" word, it returns this word as the respelling. For example, if XWORD is VONS, the spelling corrector will probably return CONS as the respelling. However, if XWORD is CONZ, the spelling corrector will not be able to return a respelling, since CONZ is equally close to both CONS and COND. If the spelling corrector finds an acceptable respelling, it interacts with you as described earlier. In the special case that the misspelled word contains one or more $s (escape), the spelling corrector searches for those words on SPLST that match XWORD, where a $ can match any number of characters (including 0), e.g., FOO$ matches FOO1 and FOO, but not NEWFOO. $FOO$ matches all three. Both completion and correction may be involved, e.g. RPETTY$ will match PRETTYPRINT, with one mistake. The entire spelling list is always searched, and if more than one respelling is found, the spelling corrector prints AMBIGUOUS, and returns NIL. For example, CON$ would be ambiguous if both CONS and COND were on the spelling list. If the spelling corrector finds one and only one respelling, it interacts with you as described earlier. For both spelling correction and spelling completion, regardless of whether or not you approve of the spelling corrector's choice, the respelling is moved to the front of SPLST. Since many respellings are of the type with no disagreements, this procedure has the effect of considerably reducing the time required to correct the spelling of frequently misspelled words. Synonyms(SPELLING% CORRECTION NIL Spelling% correction NIL ("20") 12 SUBNAME SYNONYMS SUBTEXT synonyms) Spelling lists also provide a way of defining synonyms for a particular context. If a dotted pair appears on a spelling list (instead of just an atom), CAR is interpreted as the correct spelling of the misspelled word, and CDR as the antecedent for that word. If CAR is identical with the misspelled word, the antecedent is returned without any interaction or approval being necessary. If the misspelled word corrects to CAR of the dotted pair, the usual interaction and approval will take place, and then the antecedent, i.e., CDR of the dotted pair, is returned. For example,you could make IFLG synonymous with CLISPIFTRANFLG by adding (IFLG . CLISPIFTRANFLG) to SPELLINGS3, the spelling list for unbound atoms. Similarly, you could make OTHERWISE mean the same as ELSEIF by adding (OTHERWISE . ELSEIF) to CLISPIFWORDSPLST, or make L be synonymous with LAMBDA by adding (L . LAMBDA) to LAMBDASPLST. You can also use L as a variable without confusion, since the association of L with LAMBDA occurs only in the appropriate context. Spelling Lists(SPELLING% CORRECTION NIL Spelling% correction NIL ("20") 12 SUBNAME SPELLING% LISTS SUBTEXT spelling% lists) Any list of atoms can be used as a spelling list, e.g., BROKENFNS, FILELST, etc. Various system packages have their own spellings lists, e.g., LISPXCOMS, CLISPFORWORDSPLST, EDITCOMSA, etc. These are documented under their corresponding sections, and are also indexed under "spelling lists." In addition to these spelling lists, the system maintains, i.e., automatically adds to, and occasionally prunes, four lists used solely for spelling correction: SPELLINGS1, SPELLINGS2, SPELLINGS3, and USERWORDS. These spelling lists are maintained only when ADDSPELLFLG is non-NIL. ADDSPELLFLG is initially T. SPELLINGS1(SPELLINGS1 (Variable) NIL NIL ("20") 12) [Variable] SPELLINGS1 is a list of functions used for spelling correction when an input is typed in apply format, and the function is undefined, e.g., EDTIF(FOO). SPELLINGS1 is initialized to contain DEFINEQ, BREAK, MAKEFILE, EDITF, TCOMPL, LOAD, etc. Whenever LISPX is given an input in apply format, i.e., a function and arguments, the name of the function is added to SPELLINGS1 if the function has a definition. For example, typing CALLS(EDITF) will cause CALLS to be added to SPELLINGS1. Thus if you typed CALLS(EDITF) and later typed CALLLS(EDITV), since SPELLINGS1 would then contain CALLS, DWIM would be successful in correcting CALLLS to CALLS. SPELLINGS2(SPELLINGS2 (Variable) NIL NIL ("20") 12) [Variable] SPELLINGS2 is a list of functions used for spelling correction for all other undefined functions. It is initialized to contain functions such as ADD1, APPEND, COND, CONS, GO, LIST, NCONC, PRINT, PROG, RETURN, SETQ, etc. Whenever LISPX is given a non-atomic form, the name of the function is added to SPELLINGS2. For example, typing (RETFROM (STKPOS (QUOTE FOO) 2)) to a break would add RETFROM to SPELLINGS2. Function names are also added to SPELLINGS2 by DEFINE, DEFINEQ, LOAD (when loading compiled code), UNSAVEDEF, EDITF, and PRETTYPRINT. SPELLINGS3(SPELLINGS3 (Variable) NIL NIL ("20") 13) [Variable] SPELLINGS3 is a list of words used for spelling correction on all unbound atoms. SPELLINGS3 is initialized to EDITMACROS, BREAKMACROS, BROKENFNS, and ADVISEDFNS. Whenever LISPX is given an atom to evaluate, the name of the atom is added to SPELLINGS3 if the atom has a value. Atoms are also added to SPELLINGS3 whenever they are edited by EDITV, and whenever they are set via RPAQ or RPAQQ. For example, when a file is loaded, all of the variables set in the file are added to SPELLINGS3. Atoms are also added to SPELLINGS3 when they are set by a LISPX input, e.g., typing (SETQ FOO (REVERSE (SETQ FIE ...))) will add both FOO and FIE to SPELLINGS3. USERWORDS(USERWORDS (Variable) NIL NIL ("20") 13) [Variable] USERWORDS is a list containing both functions and variables that you have referred to, e.g., by breaking or editing. USERWORDS is used for spelling correction by ARGLIST, UNSAVEDEF, PRETTYPRINT, BREAK, EDITF, ADVISE, etc. USERWORDS is initially NIL. Function names are added to it by DEFINE, DEFINEQ, LOAD, (when loading compiled code, or loading exprs to property lists) UNSAVEDEF, EDITF, EDITV, EDITP, PRETTYPRINT, etc. Variable names are added to USERWORDS at the same time as they are added to SPELLINGS3. In addition, the variable LASTWORD is always set to the last word added to USERWORDS, i.e., the last function or variable referred to by the user, and the respelling of NIL is defined to be the value of LASTWORD. Thus, if you had just defined a function, you can then prettyprint it by typing PP(). Each of the above four spelling lists are divided into two sections separated by a special marker (the value of the variable SPELLSTR1). The first section contains the "permanent" words; the second section contains the temporary words. New words are added to the corresponding spelling list at the front of its temporary section (except that functions added to SPELLINGS1 or SPELLINGS2 by LISPX are always added to the end of the permanent section. If the word is already in the temporary section, it is moved to the front of that section; if the word is in the permanent section, no action is taken. If the length of the temporary section then exceeds a specified number, the last (oldest) word in the temporary section is forgotten, i.e., deleted. This procedure prevents the spelling lists from becoming cluttered with unimportant words that are no longer being used, and thereby slowing down spelling correction time. Since the spelling corrector usually moves each word selected as a respelling to the front of its spelling list, the word is thereby moved into the permanent section. Thus once a word is misspelled and corrected, it is considered important and will never be forgotten. The spelling correction algorithm will not alter a spelling list unless it contains the special marker (the value of SPELLSTR1). This provides a way to ensure that a spelling list will not be altered. #SPELLINGS1(#SPELLINGS1 (Variable) NIL NIL ("20") 13) [Variable] #SPELLINGS2(#SPELLINGS2 (Variable) NIL NIL ("20") 13) [Variable] #SPELLINGS3(#SPELLINGS3 (Variable) NIL NIL ("20") 13) [Variable] #USERWORDS(#USERWORDS (Variable) NIL NIL ("20") 13) [Variable] The maximum length of the temporary section for SPELLINGS1, SPELLINGS2, SPELLINGS3 and USERWORDS is given by the value of #SPELLINGS1, #SPELLINGS2, #SPELLINGS3, and #USERWORDS, initialized to 30, 30, 30, and 60 respectively. You can alter these values to modify the performance behavior of spelling correction. Generators for Spelling Correction(SPELLING% CORRECTION NIL Spelling% correction NIL ("20") 14 SUBNAME GENERATORS% FOR SUBTEXT generators% for) For some applications, it is more convenient to generate candidates for a respelling one by one, rather than construct a complete list of all possible candidates, e.g., spelling correction involving a large directory of files, or a natural language data base. For these purposes, SPLST can be an array (of any size). The first element of this array is the generator function, which is called with the array itself as its argument. Thus the function can use the remainder of the array to store "state" information, e.g., the last position on a file, a pointer into a data structure, etc. The value returned by the function is the next candidate for respelling. If NIL is returned, the spelling "list" is considered to be exhausted, and the closest match is returned. If a candidate is found with no disagreements, it is returned immediately without waiting for the "list" to exhaust. SPLST can also be a generator, i.e. the value of the function GENERATOR (Chapter 11). The generator SPLST will be started up whenever the spelling corrector needs the next candidate, and it should return candidates via the function PRODUCE. For example, the following could be used as a "spelling list" which effectively contains all functions in the system: [GENERATOR (MAPATOMS (FUNCTION (LAMBDA (X) (if (GETD X) then (PRODUCE X] Spelling Corrector Algorithm(SPELLING% CORRECTION NIL Spelling% correction NIL ("20") 14 SUBNAME ALGORITHM SUBTEXT algorithm) The basic philosophy of DWIM spelling correction is to count the number of disagreements between two words, and use this number divided by the length of the longer of the two words as a measure of their relative disagreement. One minus this number is then the relative agreement or closeness. For example, CONS and CONX differ only in their last character. Such substitution errors count as one disagreement, so that the two words are in 75% agreement. Most calls to the spelling corrector specify a relative agreement of 70, so that a single substitution error is permitted in words of four characters or longer. However, spelling correction on shorter words is possible since certain types of differences such as single transpositions are not counted as disagreements. For example, AND and NAD have a relative agreement of 100. Calls to the spelling corrector from DWIM use the value of FIXSPELLREL, which is initially 70. Note that by setting FIXSPELLREL to 100, only spelling corrections with "zero" mistakes, will be considered, e.g., transpositions, double characters, etc. The central function of the spelling corrector is CHOOZ. CHOOZ takes as arguments: a word, a minimum relative agreement, a spelling list, and an optional functional argument, XWORD, REL, SPLST, and FN respectively. CHOOZ proceeds down SPLST examining each word. Words not satisfying FN (if FN is non-NIL), or those obviously too long or too short to be sufficiently close to XWORD are immediately rejected. For example, if REL=70, and XWORD is 5 characters long, words longer than 7 characters will be rejected. Special treatment is necessary for words shorter than XWORD, since doubled letters are not counted as disagreements. For example, CONNSSS and CONS have a relative agreement of 100. CHOOZ handles this by counting the number of doubled characters in XWORD before it begins scanning SPLST, and taking this into account when deciding whether to reject shorter words. If TWORD, the current word on SPLST, is not rejected, CHOOZ computes the number of disagreements between it and XWORD by calling a subfunction, SKOR. SKOR operates by scanning both words from left to right one character at a time. SKOR operates on the list of character codes for each word. This list is computed by CHOOZ before calling SKOR. Characters are considered to agree if they are the same characters or appear on the same key (i.e., a shift mistake). The variable SPELLCASEARRAY is a CASEARRAY which is used to determine equivalence classes for this purpose. It is initialized to equivalence lowercase and upper case letters, as well as the standard key transitions: for example, 1 with !, 3 with #, etc. If the first character in XWORD and TWORD do not agree, SKOR checks to see if either character is the same as one previously encountered, and not accounted-for at that time. (In other words, transpositions are not handled by lookahead, but by lookback.) A displacement of two or fewer positions is counted as a tranposition; a displacement by more than two positions is counted as a disagreement.In either case, both characters are now considered as accounted for and are discarded, and SKORing continues. If the first character in XWORD and TWORD do not agree, and neither agree with previously unaccounted-for characters, and TWORD has more characters remaining than XWORD, SKOR removes and saves the first character of TWORD, and continues by comparing the rest of TWORD with XWORD as described above. If TWORD has the same or fewer characters remaining than XWORD, the procedure is the same except that the character is removed from XWORD. In this case, a special check is first made to see if that character is equal to the previous character in XWORD, or to the next character in XWORD, i.e., a double character typo, and if so, the character is considered accounted-for, and not counted as a disagreement. In this case, the "length" of XWORD is also decremented. Otherwise making XWORD sufficiently long by adding double characters would make it be arbitrarily close to TWORD, e.g., XXXXXX would correct to PP. When SKOR has finished processing both XWORD and TWORD in this fashion, the value of SKOR is the number of unaccounted-for characters, plus the number of disagreements, plus the number of tranpositions, with two qualifications: 1. If both XWORD and TWORD have a character unaccounted-for in the same position, the two characters are counted only once, i.e., substitution errors count as only one disagreement, not two 2. If there are no unaccounted-for characters and no disagreements, transpositions are not counted. This permits spelling correction on very short words, such as edit commands, e.g., XRT->XTR. Transpositions are also not counted when FASTYPEFLG=T, for example, IPULX and IPLUS will be in 80% agreement with FASTYPEFLG=T, only 60% with FASTYPEFLG=NIL. The rationale behind this is that transpositions are much more common for fast typists, and should not be counted as disagreements, whereas more deliberate typists are not as likely to combine tranpositions and other mistakes in a single word, and therefore can use more conservative metric. FASTYPEFLG is initially NIL. Spelling Corrector(SPELLING% CORRECTION NIL Spelling% correction NIL ("20") 15 SUBNAME FUNCTIONS SUBTEXT functions) Functions and Variables(SPELLING% CORRECTION NIL Spelling% correction NIL ("20") 15 SUBNAME VARIABLES SUBTEXT variables) (ADDSPELL(ADDSPELL (Function) NIL NIL ("20") 15) X SPLST N) [Function] Adds X to one of the spelling lists as determined by the value of SPLST: NIL Adds X to USERWORDS and to SPELLINGS2. Used by DEFINEQ. 0 Adds X to USERWORDS. Used by LOAD when loading EXPRs to property lists. 1 Adds X to SPELLINGS1 (at end of permanent section). Used by LISPX. 2 Adds X to SPELLINGS2 (at end of permanent section). Used by LISPX. 3 Adds X to USERWORDS and SPELLINGS3. a spelling list If SPLST is a spelling list, X is added to it. In this case, N is the (optional) length of the temporary section. If X is already on the spelling list, and in its temporary section, ADDSPELL moves X to the front of that section. ADDSPELL sets LASTWORD to X when SPLST=NIL, 0 or 3. If X is not a literal atom, ADDSPELL takes no action. Note that the various systems calls to ADDSPELL, e.g., from DEFINE, EDITF, LOAD, etc., can all be suppressed by setting or binding ADDSPELLFLG to NIL (see the DWIM Functions and Variables section above). (MISSPELLED?(MISSPELLED? (Function) NIL NIL ("20") 16) XWORD REL SPLST FLG TAIL FN) [Function] If XWORD=NIL or $ (), MISSPELLED? prints = followed by the value of LASTWORD, and returns this as the respelling, without asking for approval. Otherwise, MISSPELLED? checks to see if XWORD is really misspelled, i.e., if FN applied to XWORD is true, or XWORD is already contained on SPLST. In this case, MISSPELLED? simply returns XWORD. Otherwise MISSPELLED? computes and returns (FIXSPELL XWORD REL SPLST FLG TAIL FN). (FIXSPELL(FIXSPELL (Function) NIL NIL ("20") 16) XWORD REL SPLST FLG TAIL FN TIEFLG DONTMOVETOPFLG % %) [Function] The value of FIXSPELL is either the respelling of XWORD or NIL. If for some reason XWORD itself is on SPLST, then FIXSPELL aborts and calls ERROR!. If there is a possibility that XWORD is spelled correctly, MISSPELLED? should be used instead of FIXSPELL. FIXSPELL performs all of the interactions described earlier, including requesting your approval if necessary. If XWORD=NIL or $ (escape), the respelling is the value of LASTWORD, and no approval is requested. If XWORD contains lowercase characters, and the corresponding uppercase word is correct, i.e. on SPLST or satisfies FN, the uppercase word is returned and no interaction is performed. If FIXSPELL.UPPERCASE.QUIET is NIL (the default), a warning "=XX" is printed when coercing from "xx" to "XX". If FIXSPELL.UPPERCASE.QUIET is non-NIL, no warning is given. If REL=NIL, defaults to the value of FIXSPELLREL (initially 70). If FLG=NIL, the correction is handled in type-in mode, i.e., approval is never requested, and XWORD is not typed. If FLG=T, XWORD is typed (before the =) and approval is requested if APPROVEFLG=T. If FLG=NO-MESSAGE, the correction is returned with no further processing. In this case, a run-on correction will be returned as a dotted pair of the two parts of the word, and a synonym correction as a list of the form (WORD1 WORD2), where WORD1 is (the corrected version of) XWORD, and WORD2 is the synonym. The effect of the function CHOOZ can be obtained by calling FIXSPELL with FLG=NO-MESSAGE. If TAIL is not NIL, and the correction is successful, CAR of TAIL is replaced by the respelling (using /RPLACA). FIXSPELL will attempt to correct misspellings caused by running two words together, if the global variable RUNONFLG is non-NIL (default is NIL). In this case, approval is always requested. When a run-on error is corrected, CAR of TAIL is replaced by the two words, and the value of FIXSPELL is the first one. For example, if FIXSPELL is called to correct the edit command (MOVE TO AFTERCOND 3 2) with TAIL=(AFTERCOND 3 2), TAIL would be changed to (AFTER COND 2 3), and FIXSPELL would return AFTER (subject to yourapproval where necessary). If TAIL=T, FIXSPELL will also perform run-on corrections, returning a dotted pair of the two words in the event the correction is of this type. If TIEFLG=NIL and a tie occurs, i.e., more than one word on SPLST is found with the same degree of "closeness", FIXSPELL returns NIL, i.e., no correction. If TIEFLG=PICKONE and a tie occurs, the first word is taken as the correct spelling. If TIEFLG=LIST, the value of FIXSPELL is a list of the respellings (even if there is only one), and FIXSPELL will not perform any interaction with you, nor modify TAIL, the idea being that the calling program will handle those tasks. Similarly, if TIEFLG=EVERYTHING, a list of all candidates whose degree of closeness is above REL will be returned, regardless of whether some are better than others. No interaction will be performed. If DONTMOVETOPFLG=T and a correction occurs, it will not be moved to the front of the spelling list. Also, the spelling list will not be altered unless it contains the special marker used to separate the temporary and perminant parts of the system spelling lists (the value of SPELLSTR1). (FNCHECK(FNCHECK (Function) NIL NIL ("20") 17) FN NOERRORFLG SPELLFLG PROPFLG TAIL) [Function] The task of FNCHECK is to check whether FN is the name of a function and if not, to correct its spelling. If FN is the name of a function or spelling correction is successful, FNCHECK adds the (corrected) name of the function to USERWORDS using ADDSPELL, and returns it as its value. Since FNCHECK is called by many low level functions such as ARGLIST, UNSAVEDEF, etc., spelling correction only takes place when DWIMFLG=T, so that these functions can operate in a small Interlisp system which does not contain DWIM. NOERRORFLG informs FNCHECK whether or not the calling function wants to handle the unsuccessful case: if NOERRORFLG is T, FNCHECK simply returns NIL, otherwise it prints fn NOT A FUNCTION and generates a non-breaking error. If FN does not have a definition, but does have an EXPR property, then spelling correction is not attempted. Instead, if PROPFLG=T, FN is considered to be the name of a function, and is returned. If PROPFLG=NIL, FN is not considered to be the name of a function, and NIL is returned or an error generated, depending on the value of NOERRORFLG. FNCHECK calls MISSPELLED? to perform spelling correction, so that if FN=NIL, the value of LASTWORD will be returned. SPELLFLG corresponds to MISSPELLED?'s fourth argument, FLG. If SPELLFLG=T, approval will be asked if DWIM was enabled in CAUTIOUS mode, i.e., if APPROVEFLG=T. TAIL corresponds to the fifth argument to MISSPELLED?. FNCHECK is currently used by ARGLIST, UNSAVEDEF, PRETTYPRINT, BREAK0, BREAKIN, ADVISE, and CALLS. For example, BREAK0 calls FNCHECK with NOERRORFLG=T since if FNCHECK cannot produce a function, BREAK0 wants to define a dummy one. CALLS however calls FNCHECK with NOERRORFLG=NIL, since it cannot operate without a function. Many other system functions call MISSPELLED? or FIXSPELL directly. For example, BREAK1 calls FIXSPELL on unrecognized atomic inputs before attempting to evaluate them, using as a spelling list a list of all break commands. Similarly, LISPX calls FIXSPELL on atomic inputs using a list of all LISPX commands. When UNBREAK is given the name of a function that is not broken, it calls FIXSPELL with two different spelling lists, first with BROKENFNS, and if that fails, with USERWORDS. MAKEFILE calls MISSPELLED? using FILELST as a spelling list. Finally, LOAD, BCOMPL, BRECOMPILE, TCOMPL, and RECOMPILE all call MISSPELLED? if their input file(s) won't open. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "20-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "20-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "20-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "20-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "20-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "20-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))"2HZZ-T3HZ +ZT2HZ +Z5.llTT,ll,ll,ll5/HH +/ll3llT3llT2HHZ2HHZ2HZZ,ll,ll3HZT3HZ +T3$$(T,HH +3(T,ll,HH,HH,2F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR-TTITAN +TITAN +CLASSIC +TITAN + HELVETICA  HELVETICATITAN +CLASSIC +CLASSIC + HELVETICA HELVETICA +CLASSIC +MODERN +MODERNMODERN +! HRULE.GETFNCLASSIC + !    HRULE.GETFNCLASSIC +      HRULE.GETFNCLASSIC +    HRULE.GETFNCLASSIC +  IM.CHAP.GETFN HELVETICA + HRULE.GETFNMODERN +` & m A   > H  7    +      [  h 2    +&  + @ +9  X J ! + "     ' ' P  , < # ) J    "  + #  0  A  2  >  s  9    %#!M  ,  {    +s , E    `  N   N   "IM.INDEX.GETFN        -     + /   '      4 M  " |  / 5  HRULE.GETFNMODERN +  u  5  8  9        X   ]  " =    $    $  a M    )   ! P %   .    N  K   H  4  G % G  m   HRULE.GETFNMODERN +V    0      +9   +-     +  y    =  + !  V    r   " HRULE.GETFNMODERN +G H J   6   *   4  2  4  9         y  .  K   >   +    # .    ,    )  + 1      (  q ' % +E     +  Q ?IM.INDEX.GETFN " HRULE.GETFNMODERN +Y F  ! +   +U :    +,   D   C :  +=      4  + N   7  F 6  < $ # $ R  K 1  (   W      U    >     /        7    '$ ( 7     7  K            - (  1 9 (  8  $ +)   0 (    T       &S  +   ' [  z   %      }  ( ! 7           ,   = i       D  $ B ?  \        /     +  =  # *  ? J  7 +D  b & . <  J         + " *  +    $ 6  E    B { + + + +   HRULE.GETFNMODERN + +IM.INDEX.GETFNA D  V     < F  M   +6 6 +     / ; ^ I      1 ; $IM.INDEX.GETFN  3 1    'IM.INDEX.GETFN     ,   +IM.INDEX.GETFN         ]    a  L     "IM.INDEX.GETFN     E  $IM.INDEX.GETFN   1   &IM.INDEX.GETFN  & %IM.INDEX.GETFN  /  )  3    +(IM.INDEX.GETFN  ( 6 "IM.INDEX.GETFN    0    C    @IM.INDEX.GETFN@IM.INDEX.GETFN HRULE.GETFNMODERN +'IM.INDEX.GETFN    n .IM.INDEX.GETFN  a       *IM.INDEX.GETFN       +)IM.INDEX.GETFN    ) E +   'IM.INDEX.GETFN   5  -IM.INDEX.GETFN       &IM.INDEX.GETFN + &IM.INDEX.GETFN  !    d  c    +)IM.INDEX.GETFN  + 1  +n     + &IM.INDEX.GETFN         +    ++IM.INDEX.GETFN  +  v       +   +   *IM.INDEX.GETFN   M  @  ) =IM.INDEX.GETFN HRULE.GETFNMODERN +   1  7 +  + D   ? s +   -     | 4     A    . !  H   i B ?   +  9    +  J          _IM.INDEX.GETFN HELVETICA D &    h >    +A     +     ;  ) mIM.INDEX.GETFN HELVETICA8  F    + + + &       +)IM.INDEX.GETFN  + + +       i +#    +   + )    +)IM.INDEX.GETFN  +            B +   +# +        +)IM.INDEX.GETFN  +G + +   + @ +2 +   X + +  #   +  (IM.INDEX.GETFN  A # $        $   C     # ' + ) U  R  }  + +  u M  *IM.INDEX.GETFN  *IM.INDEX.GETFN  *IM.INDEX.GETFN  +)IM.INDEX.GETFN 0 + + +     +2 V "mIM.INDEX.GETFN0  ~  9   x  AaIM.INDEX.GETFN      H  . { 2  q      +   H +  H 6 G  # >  N    5   M Q                  Q $  * )   1 F X     ' U            g S + +    +  + ( +  aIM.INDEX.GETFNaIM.INDEX.GETFN 'IM.INDEX.GETFN    <   +    +   +       +   +)   +   +)   +    +  +   4  + @                '    4  7  *IM.INDEX.GETFN       +   N                'IM.INDEX.GETFNTITAN + F'         "    f     *    Y  F  P           T      +   +     -    +    #   '   c   S  0 $ '       /   }    / /    I   ? 7 R  +> i    "   &IM.INDEX.GETFN$   D A .     /  2  Y   + O +    %   . C   B    . > +   ,        0  +  &             +  +     + .  !      &  ? /        +   + $   fz \ No newline at end of file diff --git a/docs/porter-irm/vol2/21-CLISP.TEDIT b/docs/porter-irm/vol2/21-CLISP.TEDIT new file mode 100644 index 00000000..32a7c806 --- /dev/null +++ b/docs/porter-irm/vol2/21-CLISP.TEDIT @@ -0,0 +1,129 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 21. CLISP 1 21. CLISP 1 "21"21. CLISP 6 The syntax of Lisp is very simple, in the sense that it can be described concisely, but not in the sense that Lisp programs are easy to read or write! This simplicity of syntax is achieved by, and at the expense of, extensive use of explicit structuring, namely grouping through parenthesization. Unlike many languages, there are no reserved words in Lisp such as IF, THEN, FOR, DO, etc., nor reserved characters like +, -, =, , etc. The only special characters are left and right parentheses and period, which are used for indicating structure, and space and end-of-line, which are used for delimiting identifiers. This eliminates entirely the need for parsers and precedence rules in the Lisp interpreter and compiler, and thereby makes program manipulation of Lisp programs straightforward. In other words, a program that "looks at" other Lisp programs does not need to incorporate a lot of syntactic information. For example, a Lisp interpreter can be written in one or two pages of Lisp code. It is for this reason that Lisp is by far the most suitable, and frequently used, programming language for writing programs that deal with other programs as data, e.g., programs that analyze, modify, or construct other programs. However, it is precisely this same simplicity of syntax that makes Lisp programs difficult to read and write (especially for beginners). 'Pushing down' is something programs do very well, and people do poorly. As an example, consider the following two "equivalent" sentences: "The rat that the cat that the dog that I owned chased caught ate the cheese." versus "I own the dog that chased the cat that caught the rat that ate the cheese." Natural language contains many linguistic devices such as that illustrated in the second sentence above for minimizing embedding, because embedded sentences are more difficult to grasp and understand than equivalent non-embedded ones (even if the latter sentences are somewhat longer). Similarly, most high level programming languages offer syntactic devices for reducing apparent depth and complexity of a program: the reserved words and infix operators used in ALGOL-like languages simultaneously delimit operands and operations, and also convey meaning to the programmer. They are far more intuitive than parentheses. In fact, since Lisp uses parentheses (i.e., lists) for almost all syntactic forms, there is very little information contained in the parentheses for the person reading a Lisp program, and so the parentheses tend mostly to be ignored: the meaning of a particular Lisp expression for people is found almost entirely in the words, not in the structure. For example, the expression (COND (EQ N 0) 1) (T TIMES N FACTORIAL ((SUB1 N))) is recognizable as factorial even though there are five misplaced or missing parentheses. Grouping words together in parentheses is done more for Lisp's benefit, than for the programmer's. CLISP(CLISP NIL NIL NIL ("21") 1) is designed to make Interlisp programs easier to read and write by permitting you to employ various infix operators, IF statements (Chapter 9), and iterative statements (Chapter 9), which are automatically converted to equivalent Interlisp expressions when they are first interpreted. For example, factorial could be written in CLISP: (IF N=0 THEN 1 ELSE N*(FACTORIAL N-1)) Note that this expression would become an equivalent COND after it had been interpreted once, so that programs that might have to analyze or otherwise process this expression could take advantage of the simple syntax. There have been similar efforts in other Lisp systems. CLISP differs from these in that it does not attempt to replace the Lisp syntax so much as to augment it. In fact, one of the principal criteria in the design of CLISP was that users be able to freely intermix Lisp and CLISP without having to identify which is which. You can write programs, or type in expressions for evaluation, in Lisp, CLISP, or a mixture of both. In this way, you do not have to learn a whole new language and syntax to be able to use selected facilities of CLISP when and where they find them useful. CLISP is implemented via the error correction machinery in Interlisp (see Chapter 20). Thus, any expression that is well-formed from Interlisp's standpoint will never be seen by CLISP (i.e., if you defined a function IF, you would effectively turn off that part of CLISP). This means that interpreted programs that do not use CLISP constructs do not pay for its availability by slower execution time. In fact, the Interlisp interpreter does not "know" about CLISP at all. It operates as before, and when an erroneous form is encountered, the interpreter calls an error routine which in turn invokes the Do-What-I-Mean (DWIM) analyzer which contains CLISP. If the expression in question turns out to be a CLISP construct, the equivalent Interlisp form is returned to the interpreter. In addition, the original CLISP expression, is modified so that it becomes the correctly translated Interlisp form. In this way, the analysis and translation are done only once. Integrating CLISP into the Interlisp system (instead of, for example, implementing it as a separate preprocessor) makes possible Do-What-I-Mean features for CLISP constructs as well as for pure Lisp expressions. For example, if you have defined a function named GET-PARENT, CLISP would know not to attempt to interpret the form (GET-PARENT) as an arithmetic infix operation. (Actually, CLISP would never get to see this form, since it does not contain any errors.) If you mistakenly write (GET-PRAENT), CLISP would know he meant (GET-PARENT), and not (DIFFERENCE GET PRAENT), by using the information that PARENT is not the name of a variable, and that GET-PARENT is the name of a user function whose spelling is "very close" to that of GET-PRAENT. Similarly, by using information about the program's environment not readily available to a preprocessor, CLISP can successfully resolve the following sorts of ambiguities: 1. (LIST X*FACT N), where FACT is the name of a variable, means (LIST (X*FACT) N). 2. (LIST X*FACT N), where FACT is not the name of a variable but instead is the name of a function, means (LIST X*(FACT N)), i.e., N is FACT's argument. 3. (LIST X*FACT(N)), FACT the name of a function (and not the name of a variable), means (LIST X*(FACT N)). 4. Cases 1, 2 and 3 with FACT misspelled! The first expression is correct both from the standpoint of CLISP syntax and semantics and the change would be made without the user being notified. In the other cases, you would be informed or consulted about what was taking place. For example, to take an extreme case, suppose the expression (LIST X*FCCT N) were encountered, where there was both a function named FACT and a variable named FCT. 1. You would first be asked if FCCT were a misspelling of FCT. If you said YES, the expression would be interpreted as (LIST (X*FCT) N). If you said NO, you would be asked if FCCT were a misspelling of FACT, i.e., if you intended X*FCCT N to mean X*(FACT N). 2. If you said YES to this question, the indicated transformation would be performed. If you said NO, the system would then ask if X*FCCT should be treated as CLISP, since FCCT is not the name of a (bound) variable. 3. If you said YES, the expression would be transformed, if NO, it would be left alone, i.e., as (LIST X*FCCT N). Note that we have not even considered the case where X*FCCT is itself a misspelling of a variable name, e.g., a variable named XFCT (as with GET-PRAENT). This sort of transformation would be considered after the user said NO to X*FCCT N -> X*(FACT N). The question of whether X*FCCT should be treated as CLISP is important because Interlisp users may have programs that employ identifiers containing CLISP operators. Thus, if CLISP encounters the expression A/B in a context where either A or B are not the names of variables, it will ask you if A/B is intended to be CLISP, in case you really do have a free variable named A/B. Note: Through the discussion above, we speak of CLISP or DWIM asking the user. Actually, if the expression in question was typed in by the user for immediate execution, the user is simply informed of the transformation, on the grounds that the user would prefer an occasional misinterpretation rather than being continuously bothered, especially since he can always retype what he intended if a mistake occurs, and ask the programmer's assistant to UNDO the effects of the mistaken operations if necessary. For transformations on expressions in user programs, the user can inform CLISP whether he wishes to operate in CAUTIOUS or TRUSTING mode. In the former case (most typical) the user will be asked to approve transformations, in the latter, CLISP will operate as it does on type-in, i.e., perform the transformation after informing the user. CLISP can also handle parentheses errors caused by typing 8 or 9 for ( or ). (On most terminals, 8 and 9 are the lowercase characters for ( and ), i.e., ( and 8 appear on the same key, as do ) and 9.) For example, if you write N*8FACTORIAL N-1, the parentheses error can be detected and fixed before the infix operator * is converted to the Interlisp function TIMES. CLISP is able to distinguish this situation from cases like N*8*X meaning (TIMES N 8 X), or N*8X, where 8X is the name of a variable, again by using information about the programming environment. In fact, by integrating CLISP with DWIM, CLISP has been made sufficiently tolerant of errors that almost everything can be misspelled! For example, CLISP can successfully translate the definition of FACTORIAL: (IFF N=0 THENN1 ESLE N*8FACTTORIALNN-1) to the corresponding COND, while making five spelling corrections and fixing the parenthesis error. CLISP also contains a facility for converting from Interlisp back to CLISP, so that after running the above incorrect definition of FACTORIAL, the user could "clispify" the now correct version to obtain (IF N=0 THEN 1 ELSE N*(FACTORIAL N-1)). This sort of robustness prevails throughout CLISP. For example, the iterative statement permits the user to say things like: (FOR OLD X FROM M TO N DO (PRINT X) WHILE (PRIMEP X)) However, you can also write OLD (XM), (OLD XM), (OLD (XM)), permute the order of the operators, e.g., (DO PRINT X TO N FOR OLD XM WHILE PRIMEP X), omit either or both sets of parentheses, misspell any or all of the operators FOR, OLD, FROM, TO, DO, or WHILE, or leave out the word DO entirely! And, of course, you can also misspell PRINT, PRIMEP, M or N! In this example, the only thing you could not misspell is the first X, since it specifies the name of the variable of iteration. The other two instances of X could be misspelled. CLISP is well integrated into the Interlisp system. For example, the above iterative statement translates into an following equivalent Interlisp form using PROG, COND, GO, etc. When the interpreter subsequently encounters this CLISP expression, it automatically obtains and evaluates the translation. Similarly, the compiler "knows" to compile the translated form. However, if the user PRETTYPRINTs his program, PRETTYPRINT "knows" to print the original CLISP at the corresponding point in his function. Similarly, when the user edits his program, the editor keeps the translation invisible to the user. If the user modifies the CLISP, the translation is automatically discarded and recomputed the next time the expression is evaluated. In short, CLISP is not a language at all, but rather a system. It plays a role analagous to that of the programmer's assistant (Chapter 13). Whereas the programmer's assistant is an invisible intermediary agent between the user's console requests and the Interlisp executive, CLISP sits between the user's programs and the Interlisp interpreter. Only a small effort has been devoted to defining the core syntax of CLISP. Instead, most of the effort has been concentrated on providing a facility which "makes sense" out of the input expressions using context information as well as built-in and acquired information about user and system programs. It has been said that communication is based on the intention of the speaker to produce an effect in the recipient. CLISP operates under the assumption that what the user said was intended to represent a meaningful operation, and therefore tries very hard to make sense out of it. The motivation behind CLISP is not to provide the user with many different ways of saying the same thing, but to enable him to worry less about the syntactic aspects of his communication with the system. In other words, it gives the user a new degree of freedom by permitting him to concentrate more on the problem at hand, rather than on translation into a formal and unambiguous language. DWIM and CLISP are invoked on iterative statements because CAR of the iterative statement is not the name of a function, and hence generates an error. If the user defines a function by the same name as an i.s. operator, e.g., WHILE, TO, etc., the operator will no longer have the CLISP interpretation when it appears as CAR of a form, although it will continue to be treated as an i.s. operator if it appears in the interior of an i.s. To alert the user, a warning message is printed, e.g., (WHILE DEFINED, THEREFORE DISABLED IN CLISP). CLISP(CLISP NIL NIL NIL ("21") 4 SUBNAME INTERACTION% WITH% USER SUBTEXT interaction% with% user) Interaction with User 1 Syntactically and semantically well formed CLISP transformations are always performed without informing the user. Other CLISP transformations described in the previous section, e.g., misspellings of operands, infix operators, parentheses errors, unary minus - binary minus errors, all follow the same protocol as other DWIM transformations (Chapter 20). That is, if DWIM has been enabled in TRUSTING mode, or the transformation is in an expression you typed in for immediate execution, your approval is not requested, but you are informed. However, if the transformation involves a user program, and DWIM was enabled in CAUTIOUS mode, you will be asked to approve. If you say NO, the transformation is not performed. Thus, in the previous section, phrases such as "one of these (transformations) succeeds" and "the transformation LAST-ELL -> LAST-EL would be found" etc., all mean if you are in CAUTIOUS mode and the error is in a program, the corresponding transformation will be performed only if you approve (or defaults by not responding). If you say NO, the procedure followed is the same as though the transformation had not been found. For example, if A*B appears in the function FOO, and B is not bound (and no other transformations are found) the user would be asked A*B [IN FOO] TREAT AS CLISP ? (The waiting time on such interactions is three times as long as for simple corrections, i.e., 3*DWIMWAIT). In certain situations, DWIM asks for approval even if DWIM is enabled in TRUSTING mode. For example, you are always asked to approve a spelling correction that might also be interpreted as a CLISP transformation, as in LAST-ELL -> LAST-EL. If you approved, A*B would be transformed to (ITIMES A B), which would then cause a U.B.A.B. error in the event that the program was being run (remember the entire discussion also applies to DWIMifying). If you said NO, A*B would be left alone. If the value of CLISPHELPFLG=NIL (initally T), the user will not be asked to approve any CLISP transformation. Instead, in those situations where approval would be required, the effect is the same as though the user had been asked and said NO. CLISP(CLISP NIL NIL NIL ("21") 5 SUBNAME CHARACTER% OPERATORS SUBTEXT character% operators) Character Operators 1 CLISP recognizes a number of special characters operators, both prefix and infix, which are translated into common expressions. For example, the character + is recognized to represent addition, so CLISP translates the litatom A+B to the form (IPLUS A B). Note that CLISP is envoked, and this translation is made, only if an error occurs, such as an unbound atom error or an undefined function error for the perfectly legitamate litatom A+B. Therefore the user may choose not to use these facilities with no penalty, similar to other CLISP facilities. The user has a lot of flexability in using CLISP character operators. A list, can always be substituted for a litatom, and vice versa, without changing the interpretation of a phrase. For example, if the value of (FOO X) is A, and the value of (FIE Y) is B, then (LIST (FOO X)+(FIE Y)) has the same value as (LIST A+B). Note that the first expression is a list of four elements: the atom "LIST", the list "(FOO X)", the atom "+", and the list "(FIE X)", whereas the second expression, (LIST A+B), is a list of only two elements: the litatom "LIST" and the litatom "A+B". Since (LIST (FOO X)+(FIE Y)) is indistinguishable from (LIST (FOO X) + (FIE Y)) because spaces before or after parentheses have no effect on the Interlisp READ program, to be consistent, extra spaces have no effect on atomic operands either. In other words, CLISP will treat (LIST A+ B), (LIST A +B), and (LIST A + B) the same as (LIST A+B). Note: CLISP does not use its own special READ program because this would require the user to explicitly identify CLISP expressions, instead of being able to intermix Interlisp and CLISP. +(+ (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] -(- (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] *(* (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] /(/ (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] ( (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] CLISP recognizes +, -, *, /, and as the normal arithmetic infix operators. The - is also recognized as the prefix operator, unary minus. These are converted to PLUS, DIFFERENCE (or in the case of unary minus, MINUS), TIMES, QUOTIENT, and EXPT. Normally, CLISP uses the "generic" arithmetic functions PLUS, TIMES, etc. CLISP contains a facility for declaring which type of arithmetic is to be used, either by making a global declaration, or by separate declarations about individual functions or variables. The usual precedence rules apply (although these can be easily changed by the user), i.e., * has higher precedence than + so that A+B*C is the same as A+(B*C), and both * and / are lower than so that 2*X2 is the same as 2*(X2). Operators of the same precedence group from left to right, e.g., A/B/C is equivalent to (A/B)/C. Minus is binary whenever possible, i.e., except when it is the first operator in a list, as in (-A) or (-A), or when it immediately follows another operator, as in A*-B. Note that grouping with parentheses can always be used to override the normal precedence grouping, or when the user is not sure how a particular expression will parse. The complete order of precedence for CLISP operators is given below. Note that + in front of a number will disappear when the number is read, e.g., (FOO X +2) is indistinguishable from (FOO X 2). This means that (FOO X +2) will not be interpreted as CLISP, or be converted to (FOO (IPLUS X 2)). Similarly, (FOO X -2) will not be interpreted the same as (FOO X-2). To circumvent this, always type a space between the + or - and a number if an infix operator is intended, e.g., write (FOO X + 2). =(= (CLISP Operator) NIL NIL ("21") 6) [CLISP Operator] GT(GT (CLISP Operator) NIL NIL ("21") 6) [CLISP Operator] LT(LT (CLISP Operator) NIL NIL ("21") 6) [CLISP Operator] GE(GE (CLISP Operator) NIL NIL ("21") 6) [CLISP Operator] LE(LE (CLISP Operator) NIL NIL ("21") 6) [CLISP Operator] These are infix operators for "Equal", "Greater Than", "Less Than", "Greater Than or Equal", and "Less Than or Equal". GT, LT, GE, and LE are all affected by the same declarations as + and *, with the initial default to use GREATERP and LESSP. Note that only single character operators, e.g., +, , =, etc., can appear in the interior of an atom. All other operators must be set off from identifiers with spaces. For example, XLTY will not be recognized as CLISP. In some cases, DWIM will be able to diagnose this situation as a run-on spelling error, in which case after the atom is split apart, CLISP will be able to perform the indicated transformation. A number of Lisp functions, such as EQUAL, MEMBER, AND, OR, etc., can also be treated as CLISP infix operators. New infix operators can be easily added (see the CLISP Internal Convetions section below). Spelling correction on misspelled infix operators is peformed using CLISPINFIXSPLST as a spelling list. AND is higher than OR, and both AND and OR are lower than the other infix operators, so (X OR Y AND Z) is the same as (X OR (Y AND Z)), and (X AND Y EQUAL Z) is the same as (X AND (Y EQUAL Z)). All of the infix predicates have lower precedence than Interlisp forms, since it is far more common to apply a predicate to two forms, than to use a Boolean as an argument to a function. Therefore, (FOO X GT FIE Y) is translated as ((FOO X) GT (FIE Y)), rather than as (FOO (X GT (FIE Y))). However, the user can easily change this. :(: (CLISP Operator) NIL NIL ("21") 6) [CLISP Operator] X:N extracts the Nth element of the list X. FOO:3 specifies the third element of FOO, or (CADDR FOO). If N is less than zero, this indicates elements counting from the end of the list; i.e. FOO:-1 is the last element of FOO. : operators can be nested, so FOO:1:2 means the second element of the first element of FOO, or (CADAR FOO). The : operator can also be used for extracting substructures of records (see Chapter 8). Record operations are implemented by replacing expressions of the form X:FOO by (fetch FOO of X). Both lower- and uppercase are acceptable. : is also used to indicate operations in the pattern match facility (see Chapter 12). X:(& 'A -- 'B) translates to (match X with (& 'A -- 'B)) .(%. (CLISP Operator) NIL NIL ("21") 6) [CLISP Operator] In combination with :, a period can be used to specify the "data path" for record operations. For example, if FOO is a field of the BAR record, X:BAR.FOO is translated into (fetch (BAR FOO) of X). Subrecord fields can be specified with multiple periods: X:BAR.FOO.BAZ translates into (fetch (BAR FOO BAZ) of X). Note: If a record contains fields with periods in them, CLISPIFY will not translate a record operation into a form using periods to specify the data path. For example, CLISPIFY will NOT translate (fetch A.B of X) into X:A.B. ::(:: (CLISP Operator) NIL NIL ("21") 7) [CLISP Operator] X:N, returns the Nth tail of the list X. For example, FOO::3 is (CDDDR FOO), and FOO::-1 is (LAST FOO). [CLISP Operator] is used to indicate assignment. For example, XY translates to (SETQ X Y). If X does not have a value, and is not the name of one of the bound variables of the function in which it appears, spelling correction is attempted. However, since this may simply be a case of assigning an initial value to a new free variable, DWIM will always ask for approval before making the correction. In conjunction with : and ::, can also be used to perform a more general type of assignment, involving structure modification. For example, X:2Y means "make the second element of X be Y", in Interlisp terms (RPLACA (CDR X) Y). Note that the value of this operation is the value of RPLACA, which is (CDR X), rather than Y. Negative numbers can also be used, e.g., X:-2_Y, which translates to (RPLACA (NLEFT X 2) Y). The user can indicate he wants /RPLACA and /RPLACD used (undoable version of RPLACA and RPLACD, see Chapter 13), or FRPLACA and FRPLACD (fast versions of RPLACA and RPLACD, see Chapter 3), by means of CLISP declarations. The initial default is to use RPLACA and RPLACD. is also used to indicate assignment in record operations (X:FOOY translates to (replace FOO of X with Y).), and pattern match operations (Chapter 12). has different precedence on the left from on the right. On the left, is a "tight" operator, i.e., high precedence, so that A+BC is the same as A+(BC). On the right, has broader scope so that AB+C is the same as A(B+C). On typein, $FORM (where $ is the escape key) is equivalent to set the "last thing mentioned", i.e., is equivalent to (SET LASTWORD FORM) (see Chapter 20). For example, immediately after examining the value of LONGVARIABLENAME, the user could set it by typing $ followed by a form. Note that an atom of the form XY, appearing at the top level of a PROG, will not be recognized as an assignment statement because it will be interpreted as a PROG label by the Interlisp interpreter, and therefore will not cause an error, so DWIM and CLISP will never get to see it. Instead, one must write (XY). < [CLISP Operator] > [CLISP Operator] Angle brackets are used in CLISP to indicate list construction. The appearance of a "<" corresponds to a "(" and indicates that a list is to be constructed containing all the elements up to the corresponding ">". For example, > translates to (LIST A B (LIST C)). ! can be used to indicate that the next expression is to be inserted in the list as a segment, e.g., translates to (CONS A (CONS B C)) and to (APPEND A B (LIST C)). !! is used to indicate that the next expression is to be inserted as a segment, and furthermore, all list structure to its right in the angle brackets is to be physically attached to it, e.g., translates to (NCONC1 A B), and to (NCONC A (APPEND B C)). Not (NCONC (APPEND A B) C), which would have the same value, but would attach C to B, and not attach either to A. Note that <, !, !!, and > need not be separate atoms, for example, may be written equally well as < A B !C >. Also, arbitrary Interlisp or CLISP forms may be used within angle brackets. For example, one can write which translates to (CONS (SETQ FOO (FIE X)) Y). CLISPIFY converts expressions in CONS, LIST, APPEND, NCONC, NCONC1, /NCONC, and /NCONC1 into equivalent CLISP expressions using <, >, !, and !!. Note: brackets differ from other CLISP operators. For example, translates to (LIST A B (QUOTE C)) even though following ', all operators are ignored for the rest of the identifier. (This is true only if a previous unmatched < has been seen, e.g., (PRINT 'A>B) will print the atom A>B.) Note however that D> is equivalent to (LIST A B (QUOTE C>) D). ' [CLISP Operator] CLISP recognizes ' as a prefix operator. ' means QUOTE when it is the first character in an identifier, and is ignored when it is used in the interior of an identifier. Thus, X='Y means (EQ X (QUOTE Y)), but X=CAN'T means (EQ X CAN'T), not (EQ X CAN) followed by (QUOTE T). This enables users to have variable and function names with ' in them (so long as the ' is not the first character). Following ', all operators are ignored for the rest of the identifier, e.g., '*A means (QUOTE *A), and 'X=Y means (QUOTE X=Y), not (EQ (QUOTE X) Y). To write (EQ (QUOTE X) Y), one writes Y='X, or 'X =Y. This is one place where an extra space does make a difference. On typein, '$ (escape) is equivalent to (QUOTE VALUE-OF-LASTWORD) (see Chapter 20). For example, after calling PRETTYPRINT on LONGFUNCTION, the user could move its definition to FOO by typing (MOVD '$ 'FOO). Note that this is not (MOVD $ 'FOO), which would be equivalent to (MOVD LONGFUNCTION 'FOO), and would (probably) cause a U.B.A. LONGFUNCTION error, nor MOVD($ FOO), which would actually move the definition of $ to FOO, since DWIM and the spelling corrector would never be invoked. ~ [CLISP Operator] CLISP recognizes ~ as a prefix operator meaning NOT. ~ can negate a form, as in ~(ASSOC X Y), or ~X, or negate an infix operator, e.g., (A ~GT B) is the same as (A LEQ B). Note that ~A=B means (EQ (NOT A) B). When ~ negates an operator, e.g., ~=, ~LT, the two operators are treated as a single operator whose precedence is that of the second operator. When ~ negates a function, e.g., (~FOO X Y), it negates the whole form, i.e., (~(FOO X Y)). Order of Precedence of CLISP Operators: ' : (left precedence) - (unary), ~ *, / +, - (binary) (right precedence) = Interlisp forms LT, GT, EQUAL, MEMBER, etc. AND OR IF, THEN, ELSEIF, ELSE iterative statement operators Declarations 1 CLISP declarations are used to affect the choice of Interlisp function used as the translation of a particular operator. For example, A+B can be translated as either (PLUS A B), (FPLUS A B), or (IPLUS A B), depending on the declaration in effect. Similarly X:1Y can mean (RPLACA X Y), (FRPLACA X Y), or (/RPLACA X Y), and either (NCONC1 A B) or (/NCONC1 A B). Note that the choice of function on all CLISP transformations are affected by the CLISP declaration in effect, i.e., iterative statements, pattern matches, record operations, as well as infix and prefix operators. (CLISPDEC(CLISPDEC (Function) NIL NIL ("21") 9) DECLST) [Function] Puts into effect the declarations in DECLST. CLISPDEC performs spelling corrections on words not recognized as declarations. CLISPDEC is undoable. The user can makes (changes) a global declaration by calling CLISPDEC with DECLST a list of declarations, e.g., (CLISPDEC '(FLOATING UNDOABLE)). Changing a global declaration does not affect the speed of subsequent CLISP transformations, since all CLISP transformation are table driven (i.e., property list), and global declarations are accomplished by making the appropriate internal changes to CLISP at the time of the declaration. If a function employs local declarations (described below), there will be a slight loss in efficiency owing to the fact that for each CLISP transformation, the declaration list must be searched for possibly relevant declarations. Declarations are implemented in the order that they are given, so that later declarations override earlier ones. For example, the declaration FAST specifies that FRPLACA, FRPLACD, FMEMB, and FLAST be used in place of RPLACA, RPLACD, MEMB, and LAST; the declaration RPLACA specifies that RPLACA be used. Therefore, the declarations (FAST RPLACA RPLACD) will cause FMEMB, FLAST, RPLACA, and RPLACD to be used. The initial global declaration is MIXED and STANDARD. The table below gives the declarations available in CLISP, and the Interlisp functions they indicate: Declaration: Interlisp Functions to be used: MIXED PLUS, MINUS, DIFFERENCE, TIMES, QUOTIENT, LESSP, GREATERP INTEGER or FIXED IPLUS, IMINUS, IDIFFERENCE, ITIMES, IQUOTIENT, ILESSP, IGREATERP FLOATING FPLUS, FMINUS, FDIFFERENCE, FTIMES, FQUOTIENT, LESSP, FGREATERP FAST FRPLACA, FRPLACD, FMEMB, FLAST, FASSOC UNDOABLE /RPLACA, /RPLACD, /NCONC, /NCONC1, /MAPCONC, /MAPCON STANDARD RPLACA, RPLACD, MEMB, LAST, ASSOC, NCONC, NCONC1, MAPCONC, MAPCON RPLACA, RPLACD, /RPLACA, etc. corresponding function The user can also make local declarations affecting a selected function or functions by inserting an expression of the form (CLISP: . DECLARATIONS) immediately following the argument list, i.e., as CADDR of the definition. Such local declarations take precedence over global declarations. Declarations affecting selected variables can be indicated by lists, where the first element is the name of a variable, and the rest of the list the declarations for that variable. For example, (CLISP: FLOATING (X INTEGER)) specifies that in this function integer arithmetic be used for computations involving X, and floating arithmetic for all other computations, where "involving" means where the variable itself is an operand. For example, with the declaration (FLOATING (X INTEGER)) in effect, (FOO X)+(FIE X) would translate to FPLUS, i.e., use floating arithmetic, even though X appears somewhere inside of the operands, whereas X+(FIE X) would translate to IPLUS. If there are declarations involving both operands, e.g., X+Y, with (X FLOATING) (Y INTEGER), whichever appears first in the declaration list will be used. The user can also make local record declarations by inserting a record declaration, e.g., (RECORD --), (ARRAYRECORD --), etc., in the local declaration list. In addition, a local declaration of the form (RECORDS A B C) is equivalent to having copies of the global declarations A, B, and C in the local declaration. Local record declarations override global record declarations for the function in which they appear. Local declarations can also be used to override the global setting of certain DWIM/CLISP parameters effective only for transformations within that function, by including in the local declaration an expression of the form (VARIABLE = VALUE), e.g., (PATVARDEFAULT = QUOTE). The CLISP: expression is converted to a comment of a special form recognized by CLISP. Whenever a CLISP transformation that is affected by declarations is about to be performed in a function, this comment will be searched for a relevant declaration, and if one is found, the corresponding function will be used. Otherwise, if none are found, the global declaration(s) currently in effect will be used. Local declarations are effective in the order that they are given, so that later declarations can be used to override earlier ones, e.g., (CLISP: FAST RPLACA RPLACD) specifies that FMEMB, FLAST, RPLACA, and RPLACD be used. An exception to this is that declarations for specific variables take precedence of general, function-wide declarations, regardless of the order of appearance, as in (CLISP: (X INTEGER) FLOATING). CLISPIFY also checks the declarations in effect before selecting an infix operator to ensure that the corresponding CLISP construct would in fact translate back to this form. For example, if a FLOATING declaration is in effect, CLISPIFY will convert (FPLUS X Y) to X+Y, but leave (IPLUS X Y) as is. If (FPLUS X Y) is CLISPIFYed while a FLOATING declaration is under effect, and then the declaration is changed to INTEGER, when X+Y is translated back to Interlisp, it will become (IPLUS X Y). CLISP Operation 1 CLISP is a part of the basic Interlisp system. Without any special preparations, the user can include CLISP constructs in programs, or type them in directly for evaluation (in EVAL or APPLY format), then, when the "error" occurrs, and DWIM is called, it will destructively transform the CLISP to the equivalent Interlisp expression and evaluate the Interlisp expression. CLISP transformations, like all DWIM corrections, are undoable. User approval is not requested, and no message is printed. This entire discussion also applies to CLISP transformation initiated by calls to DWIM from DWIMIFY. However, if a CLISP construct contains an error, an appropriate diagnostic is generated, and the form is left unchanged. For example, if the user writes (LIST X+Y*), the error diagnostic MISSING OPERAND AT X+Y* IN (LIST X+Y*) would be generated. Similarly, if the user writes (LAST+EL X), CLISP knows that ((IPLUS LAST EL) X) is not a valid Interlisp expression, so the error diagnostic MISSING OPERATOR IN (LAST+EL X) is generated. (For example, the user might have meant to say (LAST+EL*X).) If LAST+EL were the name of a defined function, CLISP would never see this form. Since the bad CLISP transformation might not be CLISP at all, for example, it might be a misspelling of a user function or variable, DWIM holds all CLISP error messages until after trying other corrections. If one of these succeeds, the CLISP message is discarded. Otherwise, if all fail, the message is printed (but no change is made). For example, suppose the user types (R/PLACA X Y). CLISP generates a diagnostic, since ((IQUOTIENT R PLACA) X Y) is obviously not right. However, since R/PLACA spelling corrects to /RPLACA, this diagnostic is never printed. Note: CLISP error messages are not printed on type-in. For example, typing X+*Y will just produce a U.B.A. X+*Y message. If a CLISP infix construct is well formed from a syntactic standpoint, but one or both of its operands are atomic and not bound, it is possible that either the operand is misspelled, e.g., the user wrote X+YY for X+Y, or that a CLISP transformation operation was not intended at all, but that the entire expression is a misspelling. For the purpose of DWIMIFYing, "not bound" means no top level value, not on list of bound variables built up by DWIMIFY during its analysis of the expression, and not on NOFIXVARSLST, i.e., not previously seen. For example, if the user has a variable named LAST-EL, and writes (LIST LAST-ELL). Therefore, CLISP computes, but does not actually perform, the indicated infix transformation. DWIM then continues, and if it is able to make another correction, does so, and ignores the CLISP interpretation. For example, with LAST-ELL, the transformation LAST-ELL -> LAST-EL would be found. If no other transformation is found, and DWIM is about to interpret a construct as CLISP for which one of the operands is not bound, DWIM will ask the user whether CLISP was intended, in this case by printing LAST-ELL TREAT AS CLISP ?. Note: If more than one infix operator was involved in the CLISP construct, e.g., X+Y+Z, or the operation was an assignment to a variable already noticed, or TREATASCLISPFLG is T (initially NIL), the user will simply be informed of the correction, e.g., X+Y+Z TREATED AS CLISP. Otherwise, even if DWIM was enabled in TRUSTING mode, the user will be asked to approve the correction. The same sort of procedure is followed with 8 and 9 errors. For example, suppose the user writes FOO8*X where FOO8 is not bound. The CLISP transformation is noted, and DWIM proceeds. It next asks the user to approve FOO8*X -> FOO ( *X. For example, this would make sense if the user has (or plans to define) a function named *X. If he refuses, the user is asked whether FOO8*X is to be treated as CLISP. Similarly, if FOO8 were the name of a variable, and the user writes FOOO8*X, he will first be asked to approve FOOO8*X -> FOOO ( XX, and if he refuses, then be offered the FOOO8 -> FOO8 correction. The 8-9 transformation is tried before spelling correction since it is empirically more likely that an unbound atom or undefined function containing an 8 or a 9 is a parenthesis error, rather than a spelling error. CLISP also contains provision for correcting misspellings of infix operators (other than single characters), IF words, and i.s. operators. This is implemented in such a way that the user who does not misspell them is not penalized. For example, if the user writes IF N=0 THEN 1 ELSSE N*(FACT N-1) CLISP does not operate by checking each word to see if it is a misspelling of IF, THEN, ELSE, or ELSEIF, since this would seriously degrade CLISP's performance on all IF statements. Instead, CLISP assumes that all of the IF words are spelled correctly, and transforms the expression to (COND ((ZEROP N) 1 ELSSE N*(FACT N-1))). Later, after DWIM cannot find any other interpretation for ELSSE, and using the fact that this atom originally appeared in an IF statement, DWIM attempts spelling correction, using (IF THEN ELSE ELSEIF) for a spelling list. When this is successful, DWIM "fails" all the way back to the original IF statement, changes ELSSE to ELSE, and starts over. Misspellings of AND, OR, LT, GT, etc. are handled similarly. CLISP also contains many Do-What-I-Mean features besides spelling corrections. For example, the form (LIST +X Y) would generate a MISSING OPERATOR error. However, (LIST -X Y) makes sense, if the minus is unary, so DWIM offers this interpretation to the user. Another common error, especially for new users, is to write (LIST X*FOO(Y)) or (LIST X*FOO Y), where FOO is the name of a function, instead of (LIST X*(FOO Y)). Therefore, whenever an operand that is not bound is also the name of a function (or corrects to one), the above interpretations are offered. CLISP Translations 1 The translation of CLISP character operators and the CLISP word IF are handled by replacing the CLISP expression with the corresponding Interlisp expression, and discarding the original CLISP. This is done because (1) the CLISP expression is easily recomputable (by CLISPIFY) and (2) the Interlisp expressions are simple and straightforward. Another reason for discarding the original CLISP is that it may contain errors that were corrected in the course of translation (e.g., FOOFOOO:1, N*8FOO X), etc.). If the original CLISP were retained, either the user would have to go back and fix these errors by hand, thereby negating the advantage of having DWIM perform these corrections, or else DWIM would have to keep correcting these errors over and over. Note that CLISPIFY is sufficiently fast that it is practical for the user to configure his Interlisp system so that all expressions are automatically CLISPIFYed immediately before they are presented to him. For example, he can define an edit macro to use in place of P which calls CLISPIFY on the current expression before printing it. Similarly, he can inform PRETTYPRINT to call CLISPIFY on each expression before printing it, etc. Where (1) or (2) are not the case, e.g., with iterative statements, pattern matches, record expressions, etc. the original CLISP is retained (or a slightly modified version thereof), and the translation is stored elsewhere (by the function CLISPTRAN, in the Miscellaneous Functions and Variables), usually in the hash array CLISPARRAY. The interpreter automatically checks this array when given a form CAR of which is not a function. Similarly, the compiler performs a GETHASH when given a form it does not recognize to see if it has a translation, which is then compiled instead of the form. Whenever the user changes a CLISP expresson by editing it, the editor automatically deletes its translation (if one exists), so that the next time it is evaluated or dwimified, the expression will be retranslated (if the value of CLISPRETRANFLG is T, DWIMIFY will also (re)translate any expressions which have translations stored remotely, see the CLISPIFY section). The function PPT and the edit commands PPT and CLISP: are available for examining translations (see the Miscellaneous Functions and Variables section). The user can also indicate that he wants the original CLISP retained by embedding it in an expression of the form (CLISP . CLISP-EXPRESSION), e.g., (CLISP X:5:3) or (CLISP ). In such cases, the translation will be stored remotely as described above. Furthermore, such expressions will be treated as CLISP even if infix and prefix transformations have been disabled by setting CLISPFLG to NIL (see the Miscellaneous Functions and Variables section). In other words, the user can instruct the system to interpret as CLISP infix or prefix constructs only those expressions that are specifically flagged as such. The user can also include CLISP declarations by writing (CLISP DECLARATIONS . FORM), e.g., (CLISP (CLISP: FLOATING) ... ). These declarations will be used in place of any CLISP declarations in the function definition. This feature provides a way of including CLISP declarations in macro definitions. Note: CLISP translations can also be used to supply an interpretation for function objects, as well as forms, either for function objects that are used openly, i.e., appearing as CAR of form, function objects that are explicitly APPLYed, as with arguments to mapping functions, or function objects contained in function definition cells. In all cases, if CAR of the object is not LAMBDA or NLAMBDA, the interpreter and compiler will check CLISPARRAY. DWIMIFY 1 DWIMIFY is effectively a preprocessor for CLISP. DWIMIFY operates by scanning an expression as though it were being interpreted, and for each form that would generate an error, calling DWIM to "fix" it. DWIMIFY performs all DWIM transformations, not just CLISP transformations, so it does spelling correction, fixes 8-9 errors, handles F/L, etc. Thus the user will see the same messages, and be asked for approval in the same situations, as he would if the expression were actually run. If DWIM is unable to make a correction, no message is printed, the form is left as it was, and the analysis proceeds. DWIMIFY knows exactly how the interpreter works. It knows the syntax of PROGs, SELECTQs, LAMBDA expressions, SETQs, et al. It knows how variables are bound, and that the argument of NLAMBDAs are not evaluated (the user can inform DWIMIFY of a function or macro's nonstandard binding or evaluation by giving it a suitable INFO property, see below). In the course of its analysis of a particular expression, DWIMIFY builds a list of the bound variables from the LAMBDA expressions and PROGs that it encounters. It uses this list for spelling corrections. DWIMIFY also knows not to try to "correct" variables that are on this list since they would be bound if the expression were actually being run. However, note that DWIMIFY cannot, a priori, know about variables that are used freely but would be bound in a higher function if the expression were evaluated in its normal context. Therefore, DWIMIFY will try to "correct" these variables. Similarly, DWIMIFY will attempt to correct forms for which CAR is undefined, even when the form is not in error from the user's standpoint, but the corresponding function has simply not yet been defined. Note: DWIMIFY rebinds FIXSPELLDEFAULT to N, so that if the user is not at the terminal when dwimifying (or compiling), spelling corrections will not be performed. DWIMIFY will also inform the user when it encounters an expression with too many arguments (unless DWIMCHECK#ARGSFLG=NIL), because such an occurrence, although does not cause an error in the Interlisp interpreter, nevertheless is frequently symptomatic of a parenthesis error. For example, if the user wrote (CONS (QUOTE FOO X)) instead of (CONS (QUOTE FOO) X), DWIMIFY will print: POSSIBLE PARENTHESIS ERROR IN (QUOTE FOO X) TOO MANY ARGUMENTS (MORE THAN 1) DWIMIFY will also check to see if a PROG label contains a clisp character (unless DWIMCHECKPROGLABELSFLG=NIL, or the label is a member of NOFIXVARSLST), and if so, will alert the user by printing the message SUSPICIOUS PROG LABEL, followed by the label. The PROG label will not be treated as CLISP. Note that in most cases, an attempt to transform a form that is already as the user intended will have no effect (because there will be nothing to which that form could reasonably be transformed). However, in order to avoid needless calls to DWIM or to avoid possible confusion, the user can inform DWIMIFY not to attempt corrections or transformations on certain functions or variables by adding them to the list NOFIXFNSLST or NOFIXVARSLST respectively. Note that the user could achieve the same effect by simply setting the corresponding variables, and giving the functions dummy definitions. DWIMIFY will never attempt corrections on global variables, i.e., variables that are a member of the list GLOBALVARS, or have the property GLOBALVAR with value T, on their property list. Similarly, DWIMIFY will not attempt to correct variables declared to be SPECVARS in block declarations or via DECLARE expressions in the function body. The user can also declare variables that are simply used freely in a function by using the USEDFREE declaration. DWIMIFY and DWIMIFYFNS (used to DWIMIFY several functions) maintain two internal lists of those functions and variables for which corrections were unsuccessfully attempted. These lists are initialized to the values of NOFIXFNSLST and NOFIXVARSLST. Once an attempt is made to fix a particular function or variable, and the attempt fails, the function or variable is added to the corresponding list, so that on subsequent occurrences (within this call to DWIMIFY or DWIMIFYFNS), no attempt at correction is made. For example, if FOO calls FIE several times, and FIE is undefined at the time FOO is dwimified, DWIMIFY will not bother with FIE after the first occurrence. In other words, once DWIMIFY "notices" a function or variable, it no longer attempts to correct it. DWIMIFY and DWIMIFYFNS also "notice" free variables that are set in the expression being processed. Moreover, once DWIMIFY "notices" such functions or variables, it subsequently treats them the same as though they were actually defined or set. Note that these internal lists are local to each call to DWIMIFY and DWIMIFYFNS, so that if a function containing FOOO, a misspelled call to FOO, is DWIMIFYed before FOO is defined or mentioned, if the function is DWIMIFYed again after FOO has been defined, the correction will be made. The user can undo selected transformations performed by DWIMIFY, as described in Chapter 13. (DWIMIFY(DWIMIFY (Function) NIL NIL ("21") 14) X QUIETFLG L) [Function] Performs all DWIM and CLISP corrections and transformations on X that would be performed if X were run, and prints the result unless QUIETFLG=T. If X is an atom and L is NIL, X is treated as the name of a function, and its entire definition is dwimified. If X is a list or L is not NIL, X is the expression to be dwimified. If L is not NIL, it is the edit push-down list leading to X, and is used for determining context, i.e., what bound variables would be in effect when X was evaluated, whether X is a form or sequence of forms, e.g., a COND clause, etc. If X is an iterative statement and L is NIL, DWIMIFY will also print the translation, i.e., what is stored in the hash array. (DWIMIFYFNS(DWIMIFYFNS (Function) NIL NIL ("21") 15) FN1 ... FNN) [NLambda NoSpread Function] Dwimifies each of the functions given. If only one argument is given, it is evalued. If its value is a list, the functions on this list are dwimified. If only one argument is given, it is atomic, its value is not a list, and it is the name of a known file, DWIMIFYFNS will operate on (FILEFNSLST FN1), e.g. (DWIMIFYFNS FOO.LSP) will dwimify every function in the file FOO.LSP. Every 30 seconds, DWIMIFYFNS prints the name of the function it is processing, a la PRETTYPRINT. Value is a list of the functions dwimified. DWIMINMACROSFLG(DWIMINMACROSFLG (Variable) NIL NIL ("21") 15) [Variable] Controls how DWIMIFY treats the arguments in a "call" to a macro, i.e., where the CAR of the form is undefined, but has a macro definition. If DWIMINMACROSFLG is T, then macros are treated as LAMBDA functions, i.e., the arguments are assumed to be evaluated, which means that DWIMIFY will descend into the argument list. If DWIMINMACROSFLG is NIL, macros are treated as NLAMBDA functions. DWIMINMACROSFLG is initially T. INFO(INFO (Property) NIL NIL ("21") 15) [Property Name] Used to inform DWIMIFY of nonstandard behavior of particular forms with respect to evaluation, binding of arguments, etc. The INFO property of a litatom is a single atom or list of atoms chosen from among the following: EVAL Informs DWIMIFY (and CLISP and Masterscope) that an nlambda function does evaluate its arguments. Can also be placed on a macro name to override the behavior of DWIMINMACROSFLG = NIL. NOEVAL Informs DWIMIFY that a macro does not evaluate all of its arguments, even when DWIMINMACROSFLG = T. BINDS Placed on the INFO property of a function or the CAR of a special form to inform DWIMIFY that the function or form binds variables. In this case, DWIMIFY assumes that CADR of the form is the variable list, i.e., a list of litatoms, or lists of the form (VAL VALUE). LAMBDA, NLAMBDA, PROG, and RESETVARS are handled in this fashion. LABELS Informs CLISPIFY that the form interprets top-level litatoms as labels, so that CLISPIFY will never introduce an atom (by packing) at the top level of the expression. PROG is handled in this fashion. NOFIXFNSLST (NOFIXFNSLST% (Variable) NIL NIL ("21") 15) [Variable] List of functions that DWIMIFY will not try to correct. NOFIXVARSLST(NOFIXVARSLST (Variable) NIL NIL ("21") 15) [Variable] List of variables that DWIMIFY will not try to correct. NOSPELLFLG(NOSPELLFLG (Variable) NIL NIL ("21") 15) [Variable] If T, DWIMIFY will not perform any spelling corrections. Initially NIL. NOSPELLFLG is reset to T when compiling functions whose definitions are obtained from a file, as opposed to being in core. CLISPHELPFLG (CLISPHELPFLG% (Variable) NIL NIL ("21") 16) [Variable] If NIL, DWIMIFY will not ask the user for approval of any CLISP transformations. Instead, in those situations where approval would be required, the effect is the same as though the user had been asked and said NO. Initially T. DWIMIFYCOMPFLG (DWIMIFYCOMPFLG% (Variable) NIL NIL ("21") 16) [Variable] If T, DWIMIFY is called before compiling an expression. Initially NIL. DWIMCHECK#ARGSFLG(DWIMCHECK#ARGSFLG (Variable) NIL NIL ("21") 16) [Variable] If T, causes DWIMIFY to check for too many arguments in a form. Initially T. DWIMCHECKPROGLABELSFLG(DWIMCHECKPROGLABELSFLG (Variable) NIL NIL ("21") 16) [Variable] If T, causes DWIMIFY to check whether a PROG label contains a CLISP character. Initially T. DWIMESSGAG(DWIMESSGAG (Variable) NIL NIL ("21") 16) [Variable] If T, suppresses all DWIMIFY error messages. Initially NIL. CLISPRETRANFLG(CLISPRETRANFLG (Variable) NIL NIL ("21") 16) [Variable] If T, informs DWIMIFY to (re)translate all expressions which have remote translations in the CLISP hash array. Initially NIL. CLISPIFY 1 CLISPIFY converts Interlisp expressions to CLISP. Note that the expression given to CLISPIFY need not have originally been input as CLISP, i.e., CLISPIFY can be used on functions that were written before CLISP was even implemented. CLISPIFY is cognizant of declaration rules as well as all of the precedence rules. For example, CLISPIFY will convert (IPLUS A (ITIMES B C)) into A+B*C, but (ITIMES A (IPLUS B C)) into A*(B+C). CLISPIFY handles such cases by first DWIMIFYing the expression. CLISPIFY also knows how to handle expressions consisting of a mixture of Interlisp and CLISP, e.g., (IPLUS A B*C) is converted to A+B*C, but (ITIMES A B+C) to (A*(B+C)). CLISPIFY converts calls to the six basic mapping functions, MAP, MAPC, MAPCAR, MAPLIST, MAPCONC, and MAPCON, into equivalent iterative statements. It also converts certain easily recognizable internal PROG loops to the corresponding iterative statements. CLISPIFY can convert all iterative statements input in CLISP back to CLISP, regardless of how complicated the translation was, because the original CLISP is saved. CLISPIFY is not destructive to the original Interlisp expression, i.e., CLISPIFY produces a new expression without changing the original. The new expression may however contain some "pieces" of the original, since CLISPIFY attempts to minimize the number of CONSes by not copying structure whenever possible. CLISPIFY will not convert expressions appearing as arguments to NLAMBDA functions, except for those functions whose INFO property is or contains the atom EVAL. CLISPIFY also contains built in information enabling it to process special forms such as PROG, SELECTQ, etc. If the INFO property is or contains the atom LABELS, CLISPIFY will never create an atom (by packing) at the top level of the expression. PROG is handled in this fashion. Note: Disabling a CLISP operator with CLDISABLE (see the Miscellaneous Functions and Variables section) will also disable the corresponding CLISPIFY transformation. Thus, if is "turned off", AB will not transform to (SETQ A B), nor vice versa. (CLISPIFY X EDITCHAIN) [Function] Clispifies X. If X is an atom and EDITCHAIN is NIL, X is treated as the name of a function, and its definition (or EXPR property) is clispified. After CLISPIFY has finished, X is redefined (using /PUTD) with its new CLISP definition. The value of CLISPIFY is X. If X is atomic and not the name of a function, spelling correction is attempted. If this fails, an error is generated. If X is a list, or EDITCHAIN is not NIL, X itself is the expression to be clispified. If EDITCHAIN is not NIL, it is the edit push-down list leading to X and is used to determine context as with DWIMIFY, as well as to obtain the local declarations, if any. The value of CLISPIFY is the clispified version of X. (CLISPIFYFNS FN1 ... FNN) [NLambda NoSpread Function] Like DWIMIFYFNS except calls CLISPIFY instead of DWIMIFY. CL:FLG(CL:FLG (Variable) NIL NIL ("21") 17) [Variable] Affects CLISPIFY's handling of forms beginning with CAR, CDR, ... CDDDDR, as well as pattern match and record expressions. If CL:FLG is NIL, these are not transformed into the equivalent : expressions. This will prevent CLISPIFY from constructing any expression employing a : infix operator, e.g., (CADR X) will not be transformed to X:2. If CL:FLG is T, CLISPIFY will convert to : notation only when the argument is atomic or a simple list (a function name and one atomic argument). If CL:FLG is ALL, CLISPIFY will convert to : expressions whenever possible. CL:FLG is initially T. CLREMPARSFLG (CLREMPARSFLG% (Variable) NIL NIL ("21") 17) [Variable] If T, CLISPIFY will remove parentheses in certain cases from simple forms, where "simple" means a function name and one or two atomic arguments. For example, (COND ((ATOM X) --)) will CLISPIFY to (IF ATOM X THEN --). However, if CLREMPARSFLG is set to NIL, CLISPIFY will produce (IF (ATOM X) THEN --). Regardless of the flag setting, the expression can be input in either form. CLREMPARSFLG is initially NIL. CLISPIFYPACKFLG (CLISPIFYPACKFLG% (Variable) NIL NIL ("21") 17) [Variable] CLISPIFYPACKFLG affects the treatment of infix operators with atomic operands. If CLISPIFYPACKFLG is T, CLISPIFY will pack these into single atoms, e.g., (IPLUS A (ITIMES B C)) becomes A+B*C. If CLISPIFYPACKFLG is NIL, no packing is done, e.g., the above becomes A + B * C. CLISPIFYPACKFLG is initially T. CLISPIFYUSERFN(CLISPIFYUSERFN (Variable) NIL NIL ("21") 17) [Variable] If T, causes the function CLISPIFYUSERFN, which should be a function of one argument, to be called on each form (list) not otherwise recognized by CLISPIFY. If a non-NIL value is returned, it is treated as the clispified form. Initially NIL Note that CLISPIFYUSERFN must be both set and defined to use this feature. FUNNYATOMLST(FUNNYATOMLST (Variable) NIL NIL ("21") 18) [Variable] Suppose the user has variables named A, B, and A*B. If CLISPIFY were to convert (ITIMES A B) to A*B, A*B would not translate back correctly to (ITIMES A B), since it would be the name of a variable, and therefore would not cause an error. The user can prevent this from happening by adding A*B to the list FUNNYATOMLST. Then, (ITIMES A B) would CLISPIFY to A * B. Note that A*B's appearance on FUNNYATOMLST would not enable DWIM and CLISP to decode A*B+C as (IPLUS A*B C); FUNNYATOMLST is used only by CLISPIFY. Thus, if an identifier contains a CLISP character, it should always be separated (with spaces) from other operators. For example, if X* is a variable, the user should write (SETQ X* FORM) in CLISP as X* FORM, not X*FORM. In general, it is best to avoid use of identifiers containing CLISP character operators as much as possible. Miscellaneous Functions and Variables 1 CLISPFLG(CLISPFLG (Variable) NIL NIL ("21") 18) [Variable] If CLISPFLG=NIL, disables all CLISP infix or prefix transformations (but does not affect IF/THEN/ELSE statements, or iterative statements). If CLISPFLG=TYPE-IN, CLISP transformations are performed only on expressions that are typed in for evaluation, i.e., not on user programs. If CLISPFLG=T, CLISP transformations are performed on all expressions. The initial value for CLISPFLG is T. CLISPIFYing anything will cause CLISPFLG to be set to T. CLISPCHARS (CLISPCHARS% (Variable) NIL NIL ("21") 18) [Variable] A list of the operators that can appear in the interior of an atom. Currently (+ - * / ~ ' = : < > +- ~= @ !). CLISPCHARRAY(CLISPCHARRAY (Variable) NIL NIL ("21") 18) [Variable] A bit table of the characters on CLISPCHARS used for calls to STRPOSL (Chapter 4). CLISPCHARRAY is initialized by performing (SETQ CLISPCHARRAY (MAKEBITTABLE CLISPCHARS)). (CLISPINFIXSPLST% (Variable) NIL NIL ("21") 18)CLISPINFIXSPLST [Variable] A list of infix operators used for spelling correction. CLISPARRAY (CLISPARRAY (Variable) NIL NIL ("21") 18) [Variable] Hash array used for storing CLISP translations. CLISPARRAY is checked by FAULTEVAL and FAULTAPPLY on erroneous forms before calling DWIM, and by the compiler. (CLISPTRAN(CLISPTRAN (Function) NIL NIL ("21") 18) X TRAN) [Function] Gives X the translation TRAN by storing (key X, value TRAN) in the hash array CLISPARRAY. CLISPTRAN is called for all CLISP translations, via a non-linked, external function call, so it can be advised. (CLISPDEC(CLISPDEC (Function) NIL NIL ("21") 18) DECLST) [Function] Puts into effect the declarations in DECLST. CLISPDEC performs spelling corrections on words not recognized as declarations. CLISPDEC is undoable. (CLDISABLE(CLDISABLE (Function) NIL NIL ("21") 19) OP) [Function] Disables the CLISP operator OP. For example, (CLDISABLE '-) makes - be just another character. CLDISABLE can be used on all CLISP operators, e.g., infix operators, prefix operators, iterative statement operators, etc. CLDISABLE is undoable. Note: Simply removing a character operator from CLISPCHARS will prevent it from being treated as a CLISP operator when it appears as part of an atom, but it will continue to be an operator when it appears as a separate atom, e.g. (FOO + X) vs FOO+X. CLISPIFTRANFLG(CLISPIFTRANFLG (Variable) NIL NIL ("21") 19) [Variable] Affects handling of translations of IF-THEN-ELSE statements (see Chapter 9). If T, the translations are stored elsewhere, and the (modified) CLISP retained. If NIL, the corresponding COND expression replaces the CLISP. Initially T. CLISPIFYPRETTYFLG(CLISPIFYPRETTYFLG (Variable) NIL NIL ("21") 19) [Variable] If non-NIL, causes PRETTYPRINT (and therefore PP and MAKEFILE) to CLISPIFY selected function definitions before printing them according to the following interpretations of CLISPIFYPRETTYFLG: ALL Clispify all functions. T or EXPRS Clispify all functions currently defined as EXPRs. CHANGES Clispify all functions marked as having been changed. a list Clispify all functions in that list. CLISPIFYPRETTYFLG is (temporarily) reset to T when MAKEFILE is called with the option CLISPIFY, and reset to CHANGES when the file being dumped has the property FILETYPE value CLISP. CLISPIFYPRETTYFLG is initially NIL. Note: If CLISPIFYPRETTYFLG is non-NIL, and the only transformation performed by DWIM are well formed CLISP transformations, i.e., no spelling corrections, the function will not be marked as changed, since it would only have to be re-clispified and re-prettyprinted when the file was written out. (PPT X) [NLambda NoSpread Function] Both a function and an edit macro for prettyprinting translations. It performs a PP after first resetting PRETTYTRANFLG to T, thereby causing any translations to be printed instead of the corresponding CLISP. CLISP:(CL (Editor Command) NIL NIL ("21") 19) [Editor Command] Edit macro that obtains the translation of the correct expression, if any, from CLISPARRAY, and calls EDITE on it. CL(CL (Editor Command) NIL NIL ("21") 19) [Editor Command] Edit macro. Replaces current expression with CLISPIFYed current expression. Current expression can be an element or tail. DW(DW (Editor Command) NIL NIL ("21") 19) [Editor Command] Edit macro. DWIMIFYs current expression, which can be an element (atom or list) or tail. Both CL and DW can be called when the current expression is either an element or a tail and will work properly. Both consult the declarations in the function being edited, if any, and both are undoable. (LOWERCASE(LOWERCASE (Function) NIL NIL ("21") 20) FLG) [Function] If FLG=T, LOWERCASE makes the necessary internal modifications so that CLISPIFY will use lower case versions of AND, OR, IF, THEN, ELSE, ELSEIF, and all i.s. operators. This produces more readable output. Note that the user can always type in either upper or lower case (or a combination), regardless of the action of LOWERCASE. If FLG=NIL, CLISPIFY will use uppercase versions of AND, OR, et al. The value of LOWERCASE is its previous "setting". LOWERCASE is undoable. The initial setting for LOWERCASE is T. CLISP Internal Conventions 1 CLISP is almost entirely table driven by the property lists of the corresponding infix or prefix operators. For example, much of the information used for translating the + infix operator is stored on the property list of the litatom "+". Thus it is relatively easy to add new infix or prefix operators or change old ones, simply by adding or changing selected property values. (There is some built in information for handling minus, :, ', and ~, i.e., the user could not himself add such "special" operators, although he can disable or redefine them.) Global declarations operate by changing the LISPFN and CLISPINFIX properties of the appropriate operators. CLISPTYPE(CLISPTYPE (Property) NIL NIL ("21") 20) [Property Name] The property value of the property CLISPTYPE is the precedence number of the operator: higher values have higher precedence, i.e., are tighter. Note that the actual value is unimportant, only the value relative to other operators. For example, CLISPTYPE for :, , and * are 14, 6, and 4 respectively. Operators with the same precedence group left to right, e.g., / also has precedence 4, so A/B*C is (A/B)*C. An operator can have a different left and right precedence by making the value of CLISPTYPE be a dotted pair of two numbers, e.g., CLISPTYPE of is (8 . -12). In this case, CAR is the left precedence, and CDR the right, i.e., CAR is used when comparing with operators on the left, and CDR with operators on the right. For example, A*BC+D is parsed as A*(B(C+D)) because the left precedence of is 8, which is higher than that of *, which is 4. The right precedence of is -12, which is lower than that of +, which is 2. If the CLISPTYPE property for any operator is removed, the corresponding CLISP transformation is disabled, as well as the inverse CLISPIFY transformation. UNARYOP(UNARYOP (Property) NIL NIL ("21") 20) [Property Name] The value of property UNARYOP must be T for unary operators or brackets. The operand is always on the right, i.e., unary operators or brackets are always prefix operators. BROADSCOPE(BROADSCOPE (Property) NIL NIL ("21") 20) [Property Name] The value of property BROADSCOPE is T if the operator has lower precedence than Interlisp forms, e.g., LT, EQUAL, AND, etc. For example, (FOO X AND Y) parses as ((FOO X) AND Y). If the BROADSCOPE property were removed from the property list of AND, (FOO X AND Y) would parse as (FOO (X AND Y)). LISPFN(LISPFN (Property) NIL NIL ("21") 21) [Property Name] The value of the property LISPFN is the name of the function to which the infix operator translates. For example, the value of LISPFN for is EXPT, for ' QUOTE, etc. If the value of the property LISPFN is NIL, the infix operator itself is also the function, e.g., AND, OR, EQUAL. SETFN(SETFN (Property) NIL NIL ("21") 21) [Property Name] If FOO has a SETFN property FIE, then (FOO --)X translates to (FIE -- X). For example, if the user makes ELT be an infix operator, e.g. #, by putting appropriate CLISPTYPE and LISPFN properties on the property list of # then he can also make # followed by translate to SETA, e.g., X#NY to (SETA X N Y), by putting SETA on the property list of ELT under the property SETFN. Putting the list (ELT) on the property list of SETA under property SETFN will enable SETA forms to CLISPIFY back to ELT's. CLISPINFIX(CLISPINFIX (Property) NIL NIL ("21") 21) [Property Name] The value of this property is the CLISP infix to be used in CLISPIFYing. This property is stored on the property list of the corresponding Interlisp function, e.g., the value of property CLISPINFIX for EXPT is , for QUOTE is ' etc. CLISPWORD(CLISPWORD (Property) NIL NIL ("21") 21) [Property Name] Appears on the property list of clisp operators which can appear as CAR of a form, such as FETCH, REPLACE, IF, iterative statement operators, etc. Value of property is of the form (KEYWORD . NAME), where NAME is the lowercase version of the operator, and KEYWORD is its type, e.g. FORWORD, IFWORD, RECORDWORD, etc. KEYWORD can also be the name of a function. When the atom appears as CAR of a form, the function is applied to the form and the result taken as the correct form. In this case, the function should either physically change the form, or call CLISPTRAN to store the translation. As an example, to make & be an infix character operator meaning OR, you could do the following: (PUTPROP '& 'CLISPTYPE (GETPROP 'OR 'CLISPTYPE)) (PUTPROP '& 'LISPFN 'OR) (PUTPROP '& 'BROADSCOPE T) (PUTPROP 'OR 'CLISPINFIX '&) (SETQ CLISPCHARS (CONS '& CLISPCHARS)) (SETQ CLISPCHARRAY (MAKEBITTABLE CLISPCHARS)) (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "21-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "21-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "21-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "21-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "21-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "21-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))))) 53HZ +T,ll,,,ll53HHT3(T2l3HH +T2HH2HH +-T,lH,HH +3(T3(T,/2Hll/HH,ll2HZZ,HH,HHF PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR,5 CLASSIC +TITAN + HELVETICACLASSIC +CLASSIC +TITAN + HELVETICA HELVETICA +CLASSIC +MODERNMODERN +MODERNMODERN + HRULE.GETFNCLASSIC +   HRULE.GETFNCLASSIC +  +  HRULE.GETFNCLASSIC +  +  HRULE.GETFNCLASSIC +  IM.CHAP.GETFN HELVETICA  HRULE.GETFNMODERN + n    T  O  M  3  IM.INDEX.GETFNQ '5  p   W i  +8    +  ) +J +   !     E      @    ' 9     ; )      " ) b 8 D  + M      4 K      :      "       L ( >    !  (  > & ~ 5  +   ,  P       1    G  ;       < \    ;   U     +\IM.INDEX.GETFN + HRULE.GETFNMODERN +     . w p I      }       VIM.INDEX.GETFN HRULE.GETFNMODERN +  F   q       +/    ! +         +   %IM.INDEX.GETFN    %IM.INDEX.GETFN   %IM.INDEX.GETFN    %IM.INDEX.GETFN    %IM.INDEX.GETFN      4 Q  +!     8   [          D  b  9   + D +  +6  +% 7  <    %IM.INDEX.GETFN    &IM.INDEX.GETFN   &IM.INDEX.GETFN   &IM.INDEX.GETFN   &IM.INDEX.GETFN  w    .  "   1   F $        5       -  %IM.INDEX.GETFN         T   V      . V     &IM.INDEX.GETFN  o     >    +: i       &IM.INDEX.GETFN         +    /   + 1   r  #     #   +           Q   <   / ~    -        \ J "      " X  N  =          U    e    +    ' .            4       *  +k           )      +A    Y             z      + ?    + B  +      B   /  (        .  @          %        k  +#  (                             HRULE.GETFNMODERN +   +  5   +        &IM.INDEX.GETFNMODERN + +  %  H  =   :             '      "   f  .              +     +                    |  3  W    - 3  '   @ Z  U ;   _                    E  1    HRULE.GETFNMODERN +       &4  > ?  G z ' )  $ M   +    V 3  .       R G   = * 9 b  h \ , + 2 $ )  m   @    <  5 ? '> u ]   $     f      '    HRULE.GETFNMODERN + @      +  n  I  -  m K +E A     {   c r    +      / z   ) +  HRULE.GETFNMODERN + +   + q  B    F ) T R /  D   4 )     y E     ! *   :    ,  h   c +  & 6     + +     +6      3 I  +^ z 9  +#    + -  0 8   &IM.INDEX.GETFNMODERN +  +  ?  (       S    (  + Z  )      I   )IM.INDEX.GETFNCLASSIC +  +  )   +8  ,  .IM.INDEX.GETFNCLASSIC +  > ;   N *       #IM.INDEX.GETFNCLASSIC +  i Z   6 Y      *       ;  R        @ P    ,IM.INDEX.GETFN    +IM.INDEX.GETFNCLASSIC +    +)IM.INDEX.GETFNCLASSIC +   7  + c   -IM.INDEX.GETFN        /IM.INDEX.GETFN   6   0IM.INDEX.GETFNCLASSIC +   7   5IM.INDEX.GETFNCLASSIC +    .   +)IM.INDEX.GETFNCLASSIC +      -IM.INDEX.GETFNCLASSIC +   + e    HRULE.GETFNMODERN + M  , P Y        \     4      _ 3  @  $ / 8 - "  Q   "  M  ' ] .   +         > !   /   t     0  + * E       +    %IM.INDEX.GETFN  $   7  0 ! .       k         -IM.INDEX.GETFN          O      0IM.INDEX.GETFN D   *    .     -IM.INDEX.GETFN   k  E   + 3  +IM.INDEX.GETFN %       '        +   !     %     o   & HRULE.GETFNMODERN +  'IM.INDEX.GETFN   J '   x   :         + +IM.INDEX.GETFN O      +IM.INDEX.GETFNMODERN + ! +   -   0IM.INDEX.GETFNTITAN + 7   + )IM.INDEX.GETFNTITAN + 1 +  +=   +(IM.INDEX.GETFN      + f   'IM.INDEX.GETFN  +  %  I    +(IM.INDEX.GETFN      s  1 +     -IM.INDEX.GETFN $ ! P  +    0IM.INDEX.GETFNMODERN +      b        -    7  -     ,      +  +  C w      R   T   'IM.INDEX.GETFNMODERN +  P +    'IM.INDEX.GETFNMODERN +  . E   'IM.INDEX.GETFNMODERN +    F     +(IM.INDEX.GETFN      4 !      f E        '     HRULE.GETFNMODERN +  ?  /   l ,  +*  (IM.INDEX.GETFN   #    _    R (  +    .       E M   r   &IM.INDEX.GETFN       +)IM.INDEX.GETFN    + B      + +1      %IM.INDEX.GETFN   `  +  %  8      $IM.INDEX.GETFN     +    +"    $              +    +)IM.INDEX.GETFN  < x +      (IM.INDEX.GETFN   D    H  /    + ?    (   1    ' .: z \ No newline at end of file diff --git a/docs/porter-irm/vol2/22-PERFORMANCE.TEDIT b/docs/porter-irm/vol2/22-PERFORMANCE.TEDIT new file mode 100644 index 00000000..09ced884 --- /dev/null +++ b/docs/porter-irm/vol2/22-PERFORMANCE.TEDIT @@ -0,0 +1,64 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 22. PERFORMANCE ISSUES 1 22. PERFORMANCE ISSUES 1 "22"22. PERFORMANCE ISSUES 6 This chapter describes a number of areas that often contribute to performance problems in Interlisp-D programs. Many performance problems can be improved by optimizing the use of storage, since allocating and reclaiming large amounts of storage is expensive. Another tactic that can sometimes yield performance improvements is to change the use of variable bindings on the stack to reduce variable lookup time. There are a number of tools that can be used to determine which parts of a computation cause performance bottlenecks. Storage Allocation and Garbage Collection 1 As an Interlisp-D applications program runs, it creates data structures (allocated out of free storage space), manipulates them, and then discards them. If there were no way of reclaiming this space, over time the Interlisp-D memory (both the physical memory in the machine and the virtual memory stored on the disk) would fill up, and the computation would come to a halt. Actually, long before this could happen the system would probably become intolerably slow, due to "data fragmentation," which occurs when the data currently in use are spread over many virtual memory pages, so that most of the computer time must be spent swapping disk pages into physical memory. The problem of fragmentation will occur in any situation where the virtual memory is significantly larger than the real physical memory. To reduce swapping, it is desirable to keep the "working set" (the set of pages containing actively referenced data) as small as possible. It is possible to write programs that don't generate much "garbage" data, or which recycle data, but such programs tend to be overly complicated and difficult to debug. Spending effort writing such programs defeats the whole point of using a system with automatic storage allocation. An important part of any Lisp implementation is the "garbage collector" which identifies discarded data and reclaims its space. There are several well-known approaches to garbage collection. One method is the traditional mark-and-sweep garbage collection algorithm, which identifies "garbage" data by marking all accessible data structures, and then sweeping through the data spaces to find all unmarked objects (i.e., not referenced by any other object). Although this method is guaranteed to reclaim all garbage, it takes time proportional to the number of allocated objects, which may be very large. (Some allocated objects will have been marked during the "mark" phase, and the remainder will be collected during the "sweep" phase; so all will have to be touched in some way.) Also, the time that a mark-and-sweep garbage collection takes is independent of the amount of garbage collected; it is possible to sweep through the whole virtual memory, and only recover a small amount of garbage. For interactive applications, it is not acceptable to have long interruptions in a computation for the purpose of garbage collection. Interlisp-D solves this problem by using a reference-counting garbage collector. With this scheme, there is a table containing counts of how many times each object is referenced. This table is incrementally updated as pointers are created and discarded, incurring a small overhead distributed over the computation as a whole. (Note: References from the stack are not counted, but are handled separately at "sweep" time; thus the vast majority of data manipulations do not cause updates to this table.) At opportune moments, the garbage collector scans this table, and reclaims all objects that are no longer accessible (have a reference count of zero). The pause while objects are reclaimed is only the time for scanning the reference count tables (small) plus time proportional to the amount of garbage that has to be collected (typically less than a second). "Opportune" times occur when a certain number of cells have been allocated or when the system has been waiting for the user to type something for long enough. The frequency of garbage collection is controlled by the functions and variables described below. For the best system performance, it is desirable to adjust these parameters for frequent, short garbage collections, which will not interrupt interactive applications for very long, and which will have the added benefit of reducing data fragmentation, keeping the working set small. One problem with the Interlisp-D garbage collector is that not all garbage is guaranteed to be collected. Circular data structures, which point to themselves directly or indirectly, are never reclaimed, since their reference counts are always at least one. With time, this unreclaimable garbage may increase the working set to unacceptable levels. Some users have worked with the same Interlisp-D virtual memory for a very long time, but it is a good idea to occasionally save all of your functions in files, reinitialize Interlisp-D, and rebuild your system. Many users end their working day by issuing a command to rebuild their system and then leaving the machine to perform this task in their absence. If the system seems to be spending too much time swapping (an indication of fragmented working set), this procedure is definitely recommended. Garbage collection in Interlisp-D is controlled by the following functions and variables: (RECLAIM(RECLAIM (Function) NIL NIL ("22") 2)) [Function] Initiates a garbage collection. Returns 0. (RECLAIMMIN(RECLAIMMIN (Function) NIL NIL ("22") 2) N) [Function] Sets the frequency of garbage collection. Interlisp keeps track of the number of cells of any type that have been allocated; when it reaches a given number, a garbage collection occurs. If N is non-NIL, this number is set to N. Returns the current setting of the number. RECLAIMWAIT(RECLAIMWAIT (Variable) NIL NIL ("22") 2) [Variable] Interlisp-D will invoke a RECLAIM if the system is idle and waiting for your input for RECLAIMWAIT seconds (currently set for 4 seconds). (GCGAG(GCGAG (Function) NIL NIL ("22") 2) MESSAGE) [Function] Sets the behavior that occurs while a garbage collection is taking place. If MESSAGE is non-NIL, the cursor is complemented during a RECLAIM; if MESSAGE=NIL, nothing happens. The value of GCGAG is its previous setting. (GCTRP(GCGAG (Function) NIL NIL ("22") 2)) [Function] Returns the number of cells until the next garbage collection, according to the RECLAIMMIN number. The amount of storage allocated to different data types, how much of that storage is in use, and the amount of data fragmentation can be determined using the following function: (STORAGE(STORAGE (Function) NIL NIL ("22") 2) TYPES PAGETHRESHOLD) [Function] STORAGE prints out a summary, for each data type, of the amount of space allocated to the data type, and how much of that space is currently in use. If TYPES is non-NIL, STORAGE only lists statistics for the specified types. TYPES can be a litatom or a list of types. If PAGETHRESHOLD is non-NIL, then STORAGE only lists statistics for types that have at least PAGETHRESHOLD pages allocated to them. STORAGE prints out a table with the column headings Type, Assigned, Free Items, In use, and Total alloc. Type is the name of the data type. Assigned is how much of your virtual memory is set aside for items of this type. Currently, memory is allocated in quanta of two pages (1024 bytes). The numbers under Assigned show the number of pages and the total number of items that fit on those pages. Free Items shows how many items are available to be allocated (using the create construct, Chapter 8); these constitute the "free list" for that data type. In use shows how many items of this type are currently in use, i.e., have pointers to them and hence have not been garbage collected. If this number is higher than your program seems to warrant, you may want to look for storage leaks. The sum of Free Items and In use is always the same as the total Assigned items. Total alloc is the total number of items of this type that have ever been allocated (see BOXCOUNT, in the Performance Measuring section below). Note: The information about the number of items of type LISTP is only approximate, because list cells are allocated in a special way that precludes easy computation of the number of items per page. Note: When a data type is redeclared, the data type name is reassigned. Pages which were assigned to instances of the old data type are labeled **DEALLOC**. At the end of the table printout, STORAGE prints a "Data Spaces Summary" listing the number of pages allocated to the major data areas in the virtual address space: the space for fixed-length items (including datatypes), the space for variable-length items, and the space for litatoms. Variable-length data types such as arrays have fixed-length "headers," which is why they also appear in the printout of fixed-length data types. Thus, the line printed for the BITMAP data type says how many bitmaps have been allocated, but the "assigned pages" column counts only the headers, not the space used by the variable-length part of the bitmap. This summary also lists "Remaining Pages" in relation to the largest possible virtual memory, not the size of the virtual memory backing file in use. This file may fill up, causing a STORAGE FULL error, long before the "Remaining Pages" numbers reach zero. STORAGE also prints out information about the sizes of the entries on the variable-length data free list. The block sizes are broken down by the value of the variable STORAGE.ARRAYSIZES, initially (4 16 64 256 1024 4096 16384 NIL), which yields a printout of the form: variable-datum free list: le 4 26 items; 104 cells. le 16 72 items; 783 cells. le 64 36 items; 964 cells. le 256 28 items; 3155 cells. le 1024 3 items; 1175 cells. le 4096 5 items; 8303 cells. le 16384 3 items; 17067 cells. others 1 items; 17559 cells. This information can be useful in determining if the variable-length data space is fragmented. If most of the free space is composed of small items, then the allocator may not be able to find room for large items, and will extend the variable datum space. If this is extended too much, this could cause an ARRAYS FULL error, even if there is a lot of space left in little chunks. (STORAGE.LEFT(STORAGE.LEFT (Function) NIL NIL ("22") 4)) [Function] Provides a programmatic way of determining how much storage is left in the major data areas in the virtual address space. Returns a list of the form (MDSFREE MDSFRAC 8MBFRAC ATOMFREE ATOMFRAC), where the elements are interpreted as follows: MDSFREE The number of free pages left in the main data space (which includes both fixed-length and variable-length data types). MDSFRAC The fraction of the total possible main data space that is free. 8MBFRAC The fraction of the total main data space that is free, relative to eight megabytes. This number is useful when using Interlisp-D on some early computers where the hardware limits the address space to eight megabytes. The function 32MBADDRESSABLE returns non-NIL if the currently running Interlisp-D system can use the full 32 megabyte address space. ATOMFREE The number of free pages left in the litatom space. ATOMFRAC The fraction of the total litatom space that is free. Note: Another important space resource is the amount of the virtual memory backing file in use (see VMEMSIZE, Chapter 12). The system will crash if the virtual memory file is full, even if the address space is not exhausted. Variable Bindings 1 Different implementations of lisp use different methods of accessing free variables. The binding of variables occurs when a function or a PROG is entered. For example, if the function FOO has the definition (LAMBDA (A B) BODY), the variables A and B are bound so that any reference to A or B from BODY or any function called from BODY will refer to the arguments to the function FOO and not to the value of A or B from a higher level function. All variable names (litatoms) have a top level value cell which is used if the variable has not been bound in any function. In discussions of variable access, it is useful to distinquish between three types of variable access: local, special and global. Local variable access is the use of a variable that is bound within the function from which it is used. Special variable access is the use of a variable that is bound by another function. Global variable access is the use of a variable that has not been bound in any function. We will often refer to a variable all of whose accesses are local as a "local variable." Similarly, a variable all of whose accesses are global we call a "global variable." In a "deep" bound system, a variable is bound by saving on the stack the variable's name together with a value cell which contains that variable's new value. When a variable is accessed, its value is found by searching the stack for the most recent binding (occurrence) and retrieving the value stored there. If the variable is not found on the stack, the variable's top level value cell is used. In a "shallow" bound system, a variable is bound by saving on the stack the variable name and the variable's old value and putting the new value in the variable's top level value cell. When a variable is accessed, its value is always found in its top level value cell. The deep binding scheme has one disadvantage: the amount of cpu time required to fetch the value of a variable depends on the stack distance between its use and its binding. The compiler can determine local variable accesses and compiles them as fetches directly from the stack. Thus this computation cost only arises in the use of variable not bound in the local frame ("free" variables). The process of finding the value of a free variable is called free variable lookup. In a shallow bound system, the amount of cpu time required to fetch the value of a variable is constant regardless of whether the variable is local, special or global. The disadvantages of this scheme are that the actual binding of a variable takes longer (thus slowing down function call), the cells that contain the current in use values are spread throughout the space of all litatom value cells (thus increasing the working set size of functions) and context switching between processes requires unwinding and rewinding the stack (thus effectively prohibiting the use of context switching for many applications). Interlisp-D uses deep binding, because of the working set considerations and the speed of context switching. The free variable lookup routine is microcoded, thus greatly reducing the search time. In benchmarks, the largest percentage of free variable lookup time was 20 percent of the total ellapsed time; the normal time was between 5 and 10 percent. One consequence of Interlisp-D's deep binding scheme is that users may significantly improve performance by declaring global variables in certain situations. If a variable is declared global, the compiler will compile an access to that variable as a retrieval of its top level value, completely bypassing a stack search. This should be done only for variables that are never bound in functions, such as global databases and flags. Global variable declarations should be done using the GLOBALVARS file package command (Chapter 17). Its form is (GLOBALVARS VAR1 ... VARN). Another way of improving performance is to declare variables as local within a function. Normally, all variables bound within a function have their names put on the stack, and these names are scanned during free variable lookup. If a variable is declared to be local within a function, its name is not put on the stack, so it is not scanned during free variable lookup, which may increase the speed of lookups. The compiler can also make some other optimizations if a variable is known to be local to a function. A variable may be declared as local within a function by including the form (DECLARE (LOCALVARS VAR1 ... VARN)) following the argument list in the definition of the function. Local variable declarations only effect the compilation of a function. Interpreted functions put all of their variable names on the stack, regardless of any declarations. Performance Measuring 1 This section describes functions that gather and display statistics about a computation, such as as the elapsed time, and the number of data objects of different types allocated. TIMEALL and TIME gather statistics on the evaluation of a specified form. BREAKDOWN gathers statistics on individual functions called during a computation. These functions can be used to determine which parts of a computation are consuming the most resources (time, storage, etc.), and could most profitably be improved. (TIMEALL(TIMEALL (Function) NIL NIL ("22") 5) TIMEFORM NUMBEROFTIMES TIMEWHAT INTERPFLG %) [NLambda Function] Evaluates the form TIMEFORM and prints statistics on time spent in various categories (elapsed, keyboard wait, swapping time, gc) and data type allocation. For more accurate measurement on small computations, NUMBEROFTIMES may be specified (its default is 1) to cause TIMEFORM to be executed NUMBEROFTIMES times. To improve the accuracy of timing open-coded operations in this case, TIMEALL compiles a form to execute TIMEFORM NUMBEROFTIMES times (unless INTERPFLG is non-NIL), and then times the execution of the compiled form. Note: If TIMEALL is called with NUMBEROFTIMES>1, the dummy form is compiled with compiler optimizations on. This means that it is not meaningful to use TIMEALL with very simple forms that are optimized out by the compiler. For example, (TIMEALL '(IPLUS 2 3) 1000) will time a compiled function which simply returns the number 5, since (IPLUS 2 3) is optimized to the integer 5. TIMEWHAT restricts the statistics to specific categories. It can be an atom or list of datatypes to monitor, and/or the atom TIME to monitor time spent. Note that ordinarily, TIMEALL monitors all time and datatype usage, so this argument is rarely needed. TIMEALL returns the value of the last evaluation of TIMEFORM. (TIME(TIME (Function) NIL NIL ("22") 6) TIMEX TIMEN TIMETYP) [NLambda Function] TIME evaluates the form TIMEX, and prints out the number of CONS cells allocated and computation time. Garbage collection time is subtracted out. This function has been largely replaced by TIMEALL. If TIMEN is greater than 1, TIMEX is executed TIMEN times, and TIME prints out (number of conses)/TIMEN, and (computation time)/TIMEN. If TIMEN=NIL, it defaults to 1. This is useful for more accurate measurement on small computations. If TIMETYP is 0, TIME measures and prints total real time as well as computation time. If TIMETYP = 3, TIME measures and prints garbage collection time as well as computation time. If TIMETYP=T, TIME measures and prints the number of pagefaults. TIME returns the value of the last evaluation of TIMEX. (BOXCOUNT(BOXCOUNT (Function) NIL NIL ("22") 6) TYPE N) [Function] Returns the number of data objects of type TYPE allocated since this Interlisp system was created. TYPE can be any data type name (see TYPENAME, Chapter 8). If TYPE is NIL, it defaults to FIXP. If N is non-NIL, the corresponding counter is reset to N. (CONSCOUNT(CONSCOUNT (Function) NIL NIL ("22") 6) N) [Function] Returns the number of CONS cells allocated since this Interlisp system was created. If N is non-NIL, resets the counter to N. Equivalent to (BOXCOUNT 'LISTP N). (PAGEFAULTS(PAGEFAULTS (Function) NIL NIL ("22") 6)) [Function] Returns the number of page faults since this Interlisp system was created. BREAKDOWN 1 TIMEALL collects statistics for whole computations. BREAKDOWN is available to analyze the breakdown of computation time (or any other measureable quantity) function by function. (BREAKDOWN(BREAKDOWN (Function) NIL NIL ("22") 7) FN1 ... FNN) [NLambda NoSpread Function] The user calls BREAKDOWN giving it a list of function names (unevaluated). These functions are modified so that they keep track of various statistics. To remove functions from those being monitored, simply UNBREAK (Chapter 15) the functions, thereby restoring them to their original state. To add functions, call BREAKDOWN on the new functions. This will not reset the counters for any functions not on the new list. However (BREAKDOWN) will zero the counters of all functions being monitored. The procedure used for measuring is such that if one function calls other and both are "broken down", then the time (or whatever quantity is being measured) spent in the inner function is not charged to the outer function as well. BREAKDOWN will not give accurate results if a function being measured is not returned from normally, e.g., a lower RETFROM (or ERROR) bypasses it. In this case, all of the time (or whatever quantity is being measured) between the time that function is entered and the time the next function being measured is entered will be charged to the first function. (BRKDWNRESULTS(BRKDWNRESULTS (Function) NIL NIL ("22") 7) RETURNVALUESFLG) [Function] BRKDWNRESULTS prints the analysis of the statistics requested as well as the number of calls to each function. If RETURNVALUESFLG is non-NIL, BRKDWNRESULTS will not to print the results, but instead return them in the form of a list of elements of the form (FNNAME #CALLS VALUE). Example: (BREAKDOWN SUPERPRINT SUBPRINT COMMENT1) (SUPERPRINT SUBPRINT COMMENT1) (PRETTYDEF '(SUPERPRINT) 'FOO) FOO.;3 (BRKDWNRESULTS) FUNCTIONS TIME #CALLS PER CALL % SUPERPRINT 8.261 365 0.023 20 SUBPRINT 31.910 141 0.226 76 COMMENT1 1.612 8 0.201 4 TOTAL 41.783 514 0.081 NIL (BRKDWNRESULTS T) ((SUPERPRINT 365 8261) (SUBPRINT 141 31910) (COMMENT1 8 1612)) BREAKDOWN can be used to measure other statistics, by setting the following variables: BRKDWNTYPE(BRKDWNTYPE (Variable) NIL NIL ("22") 7) [Variable] To use BREAKDOWN to measure other statistics, before calling BREAKDOWN, set the variable BRKDWNTYPE to the quantity of interest, e.g., TIME, CONSES, etc, or a list of such quantities. Whenever BREAKDOWN is called with BRKDWNTYPE not NIL, BREAKDOWN performs the necessary changes to its internal state to conform to the new analysis. In particular, if this is the first time an analysis is being run with a particular statistic, a measuring function will be defined, and the compiler will be called to compile it. The functions being broken down will be redefined to call this measuring function. When BREAKDOWN is through initializing, it sets BRKDWNTYPE back to NIL. Subsequent calls to BREAKDOWN will measure the new statistic until BRKDWNTYPE is again set and a new BREAKDOWN performed. BRKDWNTYPES(BRKDWNTYPES (Variable) NIL NIL ("22") 8) [Variable] The list BRKDWNTYPES contains the information used to analyze new statistics. Each entry on BRKDWNTYPES should be of the form (TYPE FORM FUNCTION), where TYPE is a statistic name (as would appear in BRKDWNTYPE), FORM computes the statistic, and FUNCTION (optional) converts the value of form to some more interesting quantity. For example, (TIME (CLOCK 2) (LAMBDA (X) (FQUOTIENT X 1000))) measures computation time and reports the result in seconds instead of milliseconds. BRKDWNTYPES currently contains entries for TIME, CONSES, PAGEFAULTS, BOXES, and FBOXES. Example: (SETQ BRKDWNTYPE '(TIME CONSES)) (TIME CONSES) (BREAKDOWN MATCH CONSTRUCT) (MATCH CONSTRUCT) (FLIP '(A B C D E F G H C Z) '(.. $1 .. #2 ..) '(.. #3 ..)) (A B D E F G H Z) (BRKDWNRESULTS) FUNCTIONS TIME #CALLS PER CALL % MATCH 0.036 1 0.036 54 CONSTRUCT 0.031 1 0.031 46 TOTAL 0.067 2 0.033 FUNCTIONS CONSES #CALLS PER CALL % MATCH 32 1 32.000 40 CONSTRUCT 49 1 49.000 60 TOTAL 81 2 40.500 NIL Occasionally, a function being analyzed is sufficiently fast that the overhead involved in measuring it obscures the actual time spent in the function. If you were using TIME, you would specify a value for TIMEN greater than 1 to give greater accuracy. A similar option is available for BREAKDOWN. You can specify that a function(s) be executed a multiple number of times for each measurement, and the average value reported, by including a number in the list of functions given to BREAKDOWN. For example, BREAKDOWN(EDITCOM EDIT4F 10 EDIT4E EQP) means normal breakdown for EDITCOM and EDIT4F but executes (the body of) EDIT4E and EQP 10 times each time they are called. Of course, the functions so measured must not cause any harmful side effects, since they are executed more than once for each call. The printout from BRKDWNRESULTS will look the same as though each function were run only once, except that the measurement will be more accurate. Another way of obtaining more accurate measurement is to expand the call to the measuring function in-line. If the value of BRKDWNCOMPFLG is non-NIL (initially NIL), then whenever a function is broken-down, it will be redefined to call the measuring function, and then recompiled. The measuring function is expanded in-line via an appropriate macro. In addition, whenever BRKDWNTYPE is reset, the compiler is called for all functions for which BRKDWNCOMPFLG was set at the time they were originally broken-down, i.e. the setting of the flag at the time a function is broken-down determines whether the call to the measuring code is compiled in-line. GAINSPACE 1 If you have large programs and databases, you may sometimes find yourself in a situation where you need to obtain more space, and are willing to pay the price of eliminating some or all of the context information that the various user-assistance facilities such as the programmer's assistant, file package, CLISP, etc., have accumulated during the course of his session. The function GAINSPACE provides an easy way to selectively throw away accumulated data: (GAINSPACE(GAINSPACE (Function) NIL NIL ("22") 9)) [Function] Prints a list of deletable objects, allowing you to specify at each point what should be discarded and what should be retained. For example: (GAINSPACE) purge history lists ? Yes purge everything, or just the properties, e.g., SIDE, LISPXPRINT, etc. ? just the properties discard definitions on property lists ? Yes discard old values of variables ? Yes erase properties ? No erase CLISP translations? Yes GAINSPACE is driven by the list GAINSPACEFORMS. Each element on GAINSPACEFORMS is of the form (PRECHECK MESSAGE FORM KEYLST). If PRECHECK, when evaluated, returns NIL, GAINSPACE skips to the next entry. For example, you will not be asked whether or not to purge the history list if it is not enabled. Otherwise, ASKUSER (Chapter 26) is called with the indicated MESSAGE and the (optional) KEYLST. If you respond No, i.e., ASKUSER returns N, GAINSPACE skips to the next entry. Otherwise, FORM is evaluated with the variable RESPONSE bound to the value of ASKUSER. In the above example, the FORM for the "purge history lists" question calls ASKUSER to ask "purge everything, ..." only if you had responded Yes. If you had responded with Everything, the second question would not have been asked. The "erase properties" question is driven by a list SMASHPROPSMENU. Each element on this list is of the form (MESSAGE . PROPS). You are prompted with MESSAGE (by ASKUSER), and if your response is Yes, PROPS is added to the list SMASHPROPS. The "discard definitions on property lists" and "discard old values of variables" questions also add to SMASHPROPS. You will not be prompted for any entry on SMASHPROPSMENU for which all of the corresponding properties are already on SMASHPROPS. SMASHPROPS is initially set to the value of SMASHPROPSLST. This permits you to specify in advance those properties which you always want discarded, and not be asked about them subsequently. After finishing all the entries on GAINSPACEFORMS, GAINSPACE checks to see if the value of SMASHPROPS is non-NIL, and if so, does a MAPATOMS, i.e., looks at every atom in the system, and erases the indicated properties. You can change or add new entries to GAINSPACEFORMS or SMASHPROPSMENU, so that GAINSPACE can also be used to purge structures that your programs have accumulated. Using Data Types Instead of Records 1 If a program uses large numbers of large data structures, there are several advantages to representing them as user data types rather than as list structures. The primary advantage is increased speed: accessing and setting the fields of a data type can be significantly faster than walking through a list with repeated CARs and CDRs. Also, compiled code for referencing data types is usually smaller. Finally, by reducing the number of objects created (one object against many list cells), this can reduce the expense of garbage collection. User data types are declared by using the DATATYPE record type (Chapter 8). If a list structure has been defined using the RECORD record type (Chapter 8), and all accessing operations are written using the record package's fetch, replace, and create operations, changing from RECORDs to DATATYPEs only requires editing the record declaration (using EDITREC, Chapter 8) to replace declaration type RECORD by DATATYPE, and recompiling. Note: There are some minor disadvantages with allocating new data types: First, there is an upper limit on the number of data types which can exist. Also, space for data types is allocated a page at a time, so each data type has at least one page assigned to it, which may be wasteful of space if there are only a few examples of a given data type. These problems should not effect most applications programs. Using Incomplete File Names 1 Currently, Interlisp allows you to specify an open file by giving the file name. If the file name is incomplete (it doesn't have the device/host, directory, name, extension, and version number all supplied), the system converts it to a complete file name, by supplying defaults and searching through directories (which may be on remote file servers), and then searches the open streams for one corresponding to that file name. This file name-completion process happens whenever any I/O function is given an incomplete file name, which can cause a serious performance problem if I/O operations are done repeatedly. In general, it is much faster to convert an incomplete file name to a stream once, and use the stream from then on. For example, suppose a file is opened with (SETQ STRM (OPENSTREAM 'MYNAME 'INPUT)). After doing this, (READC 'MYNAME) and (READC STRM) would both work, but (READC 'MYNAME) would take longer (sometimes orders of magnitude longer). This could seriously effect the performance if a program which is doing many I/O operations. At some point in the future, when multiple streams are supported to a single file, the feature of mapping file names to streams will be removed. This is yet another reason why programs should use streams as handles to open files, instead of file names. For more information on efficiency considerations when using files, see Chapter 24. Using "Fast" and "Destructive" Functions 1 Among the functions used for manipulating objects of various data types, there are a number of functions which have "fast" and "destructive" versions. You should be aware of what these functions do, and when they should be used. "Fast" functions: By convention, a function named by prefixing an existing function name with F indicates that the new function is a "fast" version of the old. These usually have the same definitions as the slower versions, but they compile open and run without any "safety" error checks. For example, FNTH runs faster than NTH, however, it does not make as many checks (for lists ending with anything but NIL, etc). If these functions are given arguments that are not in the form that they expect, their behavior is unpredictable; they may run forever, or cause a system error. In general, you should only use "fast" functions in code that has already been completely debugged, to speed it up. "Destructive" functions: By convention, a function named by prefixing an existing function with D indicates the new function is a "destructive" version of the old one, which does not make any new structure but cannibalizes its argument(s). For example, REMOVE returns a copy of a list with a particular element removed, but DREMOVE actually changes the list structure of the list. (Unfortunately, not all destructive functions follow this naming convention: the destructive version of APPEND is NCONC.) You should be careful when using destructive functions that they do not inadvertantly change data structures. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "22-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "22-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "22-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "22-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "22-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "22-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))-HH +T-HHT5,,3HZ +T-llT2l,,3HZ +T,ll3(T,HH +,HH,HH-T3(T,/F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR2 TITAN +TITAN +CLASSIC +CLASSIC +CLASSIC + HELVETICA HELVETICA HELVETICA +CLASSIC +MODERN +MODERNMODERN + HRULE.GETFNCLASSIC +   HRULE.GETFNCLASSIC +   HRULE.GETFNCLASSIC +   HRULE.GETFNCLASSIC +   IM.CHAP.GETFN HELVETICA HRULE.GETFNMODERN +  * HRULE.GETFNMODERN +    V Z  %IM.INDEX.GETFN  +   (IM.INDEX.GETFNMODERN +     -   )IM.INDEX.GETFNMODERN +   6 '   #IM.INDEX.GETFN   N  &   !    #IM.INDEX.GETFN  P +    %IM.INDEX.GETFN      1 *   4  -   +     R +? N  + !  N / 9     "  f >    !' ######## 4 >   *IM.INDEX.GETFNMODERN +  1  y  B  V   Y  5  7 e v   HRULE.GETFN HELVETICA +    $    -       j b  6 +1   L  +   HRULE.GETFNMODERN + F   + v   %IM.INDEX.GETFNTITAN + V &      5 .  O     6   l N H   v / L  -    "IM.INDEX.GETFN +                Y     '  N   /  -    &IM.INDEX.GETFNMODERN +   + 5       (    +'IM.INDEX.GETFN   >       (IM.INDEX.GETFN  J   + HRULE.GETFNMODERN + . u  +'IM.INDEX.GETFN       7 e i :  *   a     +IM.INDEX.GETFN  f   f    + )       ) ) ) ) "    ?  M  +(IM.INDEX.GETFN   -  +$  /  +  e " +  & +    )IM.INDEX.GETFNMODERN +  I   ) +  X 0W    +     + !      <    ( ( ( # ( ( ( #   M   '     s  }    +&    + HRULE.GETFNMODERN +  B  +'IM.INDEX.GETFN   +   I  , &          +      &     +     +1   ,      + %  +- > + +"    +  P %   + K  $ HRULE.GETFNMODERN + @    * J ^     6 )      HRULE.GETFNMODERN +  (     T  ) HRULE.GETFNMODERN +  _   O ! a  A   r z \ No newline at end of file diff --git a/docs/porter-irm/vol2/23-PROCESSES.TEDIT b/docs/porter-irm/vol2/23-PROCESSES.TEDIT new file mode 100644 index 00000000..c5b7e7cc Binary files /dev/null and b/docs/porter-irm/vol2/23-PROCESSES.TEDIT differ diff --git a/docs/porter-irm/vol3/24-STREAMS.INTERPRESS b/docs/porter-irm/vol3/24-STREAMS.INTERPRESS new file mode 100644 index 00000000..f8d14bd5 Binary files /dev/null and b/docs/porter-irm/vol3/24-STREAMS.INTERPRESS differ diff --git a/docs/porter-irm/vol3/24-STREAMS.TEDIT b/docs/porter-irm/vol3/24-STREAMS.TEDIT new file mode 100644 index 00000000..19545434 Binary files /dev/null and b/docs/porter-irm/vol3/24-STREAMS.TEDIT differ diff --git a/docs/porter-irm/vol3/25-IO.INTERPRESS b/docs/porter-irm/vol3/25-IO.INTERPRESS new file mode 100644 index 00000000..e012f784 Binary files /dev/null and b/docs/porter-irm/vol3/25-IO.INTERPRESS differ diff --git a/docs/porter-irm/vol3/25-IO.TEDIT b/docs/porter-irm/vol3/25-IO.TEDIT new file mode 100644 index 00000000..9302247d --- /dev/null +++ b/docs/porter-irm/vol3/25-IO.TEDIT @@ -0,0 +1,2188 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 25. INPUT/OUTPUT FUNCTIONS 1 25. INPUT/OUTPUT FUNCTIONS 1 "25"25. INPUT/OUTPUT FUNCTIONS 6 This chapter describes the standard I/O functions used for reading and printing characters and Interlisp expressions on files and other streams. First, the primitive input functions are presented, then the output functions, then functions for random-access operations (such as searching a file for a given stream, or changing the "next-character" pointer to a position in a file). Next, the PRINTOUT statement is documented (see below), which provides an easy way to write complex output operations. Finally, read tables, used to parse characters as Interlisp expressions, are documented. Specifying Streams for Input/Output Functions(INPUT/OUTPUT% FUNCTIONS NIL Input/Output% Functions NIL NIL 1 SUBNAME SPECIFYING% STREAMS% FOR SUBTEXT specifying% streams% for) 1 Most of the input/output functions in Interlisp-D have an argument named STREAM or FILE, specifying on which open stream the function's action should occur (the name FILE is used in older functions that predate the concept of stream; the two should, however, be treated synonymously). The value of this argument should be one of the following: a stream An object of type STREAM, as returned by OPENSTREAM (Chapter 24) or other stream-producing functions, is always the most precise and efficient way to designate a stream argument. T The litatom T designates the terminal input or output stream of the currently running process, controlling input from the keyboard and output to the display screen. For functions where the direction (input or output) is ambiguous, T is taken to designate the terminal output stream. The T streams are always open; they cannot be closed. The terminal output stream can be set to a given window or display stream by using TTYDISPLAYSTREAM (Chapter 28). The terminal input stream cannot be changed. For more information on terminal I/O, see Chapter 30. NIL The litatom NIL designates the "primary" input or output stream. These streams are initially the same as the terminal input/output streams, but they can be changed by using the functions INPUT and OUTPUT. For functions where the direction (input or output) is ambiguous, e.g., GETFILEPTR, the argument NIL is taken to mean the primary input stream, if that stream is not identical to the terminal input stream, else the primary output stream. a window Uses the display stream of the window . Valid for output only. a file name As of this writing, the name of an open file (as a litatom) can be used as a stream argument. However, there are inefficiencies and possible future incompatibilities associated with doing so. See Chapter 24 for details. (GETSTREAM(GETSTREAM (Function) NIL NIL NIL 1) FILE ACCESS) [Function] Coerces the argument FILE to a stream by the above rules. If ACCESS is INPUT, OUTPUT, or BOTH, produces the stream designated by FILE that is open for ACCESS. If ACCESS=NIL, returns a stream for FILE open for any kind of input/output (see the list above for the ambiguous cases). If FILE does not designate a stream open in the specified mode, causes an error, FILE NOT OPEN. (STREAMP(STREAMP (Function) NIL NIL NIL 2) X) [Function] Returns X if X is a STREAM, otherwise NIL. Input Functions 1 While the functions described below can take input from any stream, some special actions occur when the input is from the terminal (the T input stream, see above). When reading from the terminal, the input is buffered a line at a time, unless buffering has been inhibited by CONTROL (Chapter 30) or the input is being read by READC or PEEKC. Using specified editing characters, the user can erase a character at a time, a word at a time, or the whole line. The keys that perform these editing functions are assignable via SETSYNTAX, with the initial settings chosen to be those most natural for the given operating system. In Interlisp-D, the initial settings are as follows: characters are deleted one at a time by Backspace; words are erased by control-W; the whole line is erased by Control-Q. On the Interlisp-D display, deleting a character or a line causes the characters to be physically erased from the screen. In Interlisp-10, the deleting action can be modified for various types of display terminals by using DELETECONTROL (Chapter 30). Unless otherwise indicated, when the end of file is encountered while reading from a file, all input functions generate an error, END OF FILE. Note that this does not close the input file. The ENDOFSTREAMOP stream attribute (Chapter 24) is useful for changing the behavior at end of file. Most input functions have a RDTBL argument, which specifies the read table to be used for input. Unless otherwise specified, if RDTBL is NIL, the primary read table is used. If the FILE or STREAM argument to an input function is NIL, the primary input stream is used. (INPUT(INPUT (Function) NIL NIL NIL 2) FILE) [Function] Sets FILE as the primary input stream; returns the old primary input stream. FILE must be open for input. (INPUT) returns the current primary input stream, which is not changed. Note: If the primary input stream is set to a file, the file's full name, rather than the stream itself, is returned. See discussion in Chapter 24. (READ(READ (Function) NIL NIL NIL 2) FILE RDTBL FLG) [Function] Reads one expression from FILE. Atoms are delimited by the break and separator characters as defined in RDTBL. To include a break or separator character in an atom, the character must be preceded by the character %, e.g., AB%(C is the atom AB(C, %% is the atom %, %control-K is the atom Control-K. For input from the terminal, an atom containing an interrupt character can be input by typing instead the corresponding alphabetic character preceded by Control-V, e.g., ^VD for Control-D. Strings are delimited by double quotes. To input a string containing a double quote or a %, precede it by %, e.g., "AB%"C" is the string AB"C. Note that % can always be typed even if next character is not "special", e.g., %A%B%C is read as ABC. If an atom is interpretable as a number, READ creates a number, e.g., 1E3 reads as a floating point number, 1D3 as a literal atom, 1.0 as a number, 1,0 as a literal atom, etc. An integer can be input in a non-decimal radix by using syntax such as 123Q, |b10101, |5r1234 (see Chapter 7). The function RADIX, sets the radix used to print integers. When reading from the terminal, all input is line-buffered to enable the action of the backspacing control characters, unless inhibited by CONTROL (Chapter 30). Thus no characters are actually seen by the program until a carriage-return (actually the character with terminal syntax class EOL, see Chapter 30), is typed. However, for reading by READ, when a matching right parenthesis is encountered, the effect is the same as though a carriage-return were typed, i.e., the characters are transmitted. To indicate this, Interlisp also prints a carriage-return line-feed on the terminal. The line buffer is also transmitted to READ whenever an IMMEDIATE read macro character is typed (see below). FLG=T suppresses the carriage-return normally typed by READ following a matching right parenthesis. (However, the characters are still given to READ; i.e., the user does not have to type the carriage-return.) (RATOM FILE RDTBL) [Function] Reads in one atom from FILE. Separation of atoms is defined by RDTBL. % is also defined for RATOM, and the remarks concerning line-buffering and editing control characters also apply. If the characters comprising the atom would normally be interpreted as a number by READ, that number is returned by RATOM. Note however that RATOM takes no special action for " whether or not it is a break character, i.e., RATOM never makes a string. (RSTRING(RSTRING (Function) NIL NIL NIL 3) FILE RDTBL) [Function] Reads characters from FILE up to, but not including, the next break or separator character, and returns them as a string. Backspace, Control-W, Control-Q, Control-V, and % have the same effect as with READ. Note that the break or separator character that terminates a call to RATOM or RSTRING is not read by that call, but remains in the buffer to become the first character seen by the next reading function that is called. If that function is RSTRING, it will return the null string. This is a common source of program bugs. (RATOMS(RATOMS (Function) NIL NIL NIL 3) A FILE RDTBL) [Function] Calls RATOM repeatedly until the atom A is read. Returns a list of the atoms read, not including A. (RATEST(RATEST (Function) NIL NIL NIL 3) FLG) [Function] If FLG = T, RATEST returns T if a separator was encountered immediately prior to the atom returned by the last RATOM or READ, NIL otherwise. If FLG = NIL, RATEST returns T if last atom read by RATOM or READ was a break character, NIL otherwise. If FLG = 1, RATEST returns T if last atom read (by READ or RATOM) contained a % used to quote the next character (as in %[ or %A%B%C), NIL otherwise. (READC (READC% (Function) NIL NIL NIL 3)FILE RDTBL) [Function] Reads and returns the next character, including %, ", etc, i.e., is not affected by break or separator characters. The action of READC is subject to line-buffering, i.e., READC does not return a value until the line has been terminated even if a character has been typed. Thus, the editing control characters have their usual effect. RDTBL does not directly affect the value returned, but is used as usual in line-buffering, e.g., determining when input has been terminated. If (CONTROL T) has been executed (Chapter 30), defeating line-buffering, the RDTBL argument is irrelevant, and READC returns a value as soon as a character is typed (even if the character typed is one of the editing characters, which ordinarily would never be seen in the input buffer). (PEEKC (PEEKC% (Function) NIL NIL NIL 4)FILE %) [Function] Returns the next character, but does not actually read it and remove it from the buffer. If reading from the terminal, the character is echoed as soon as PEEKC reads it, even though it is then "put back" into the system buffer, where Backspace, Control-W, etc. could change it. Thus it is possible for the value returned by PEEKC to "disagree" in the first character with a subsequent READ. (LASTC(LASTC (Function) NIL NIL NIL 4) FILE) [Function] Returns the last character read from FILE. (READCCODE(READCCODE (Function) NIL NIL NIL 4) FILE RDTBL) [Function] Returns the next character code from STREAM; thus, this operation is equivalent to, but more efficient than, (CHCON1 (READC FILE RDTBL)). (PEEKCCODE(PEEKCCODE (Function) NIL NIL NIL 4) FILE %) [Function] Returns, without consuming, the next character code from STREAM; thus, this operation is equivalent to, but more efficient than, (CHCON1 (PEEKC FILE)). (BIN(BIN (Function) NIL NIL NIL 4) STREAM) [Function] Returns the next byte from STREAM. This operation is useful for reading streams of binary, rather than character, data. Note: BIN is similar to READCCODE, except that BIN always reads a single byte, whereas READCCODE reads a "character" that can consist of more than one byte, depending on the character and its encoding. READ, RATOM, RATOMS, PEEKC, READC all wait for input if there is none. The only way to test whether or not there is input is to use READP: (READP(READP (Function) NIL NIL NIL 4) FILE FLG) [Function] Returns T if there is anything in the input buffer of FILE, NIL otherwise. This operation is only interesting for streams whose source of data is dynamic, e.g., the terminal or a byte stream over a network; for other streams, such as to files, (READP FILE) is equivalent to (NOT (EOFP FILE)). Note that because of line-buffering, READP may return T, indicating there is input in the buffer, but READ may still have to wait. Frequently, the terminal's input buffer contains a single EOL character left over from a previous input. For most applications, this situation wants to be treated as though the buffer were empty, and so READP returns NIL in this case. However, if FLG=T, READP returns T if there is any character in the input buffer, including a single EOL. FLG is ignored for streams other than the terminal. (EOFP(EOFP (Function) NIL NIL NIL 5) FILE) [Function] Returns true if FILE is at "end of file", i.e., the next call to an input function would cause an END OF FILE error; NIL otherwise. For randomly accessible files, this can also be thought of as the file pointer pointing beyond the last byte of the file. FILE must be open for (at least) input, or an error is generated, FILE NOT OPEN. Note that EOFP can return NIL and yet the next call to READ might still cause an END OF FILE error, because the only characters remaining in the input were separators or otherwise constituted an incomplete expression. The function SKIPSEPRS is sometimes more useful as a way of detecting end of file when it is known that all the expressions in the file are well formed. (WAITFORINPUT (WAITFORINPUT% (Function) NIL NIL NIL 5)FILE) [Function] Waits until input is available from FILE or from the terminal, i.e. from T. WAITFORINPUT is functionally equivalent to (until (OR (READP T) (READP FILE)) do NIL), except that it does not use up machine cycles while waiting. Returns the device for which input is now available, i.e. FILE or T. FILE can also be an integer, in which case WAITFORINPUT waits until there is input available from the terminal, or until FILE milliseconds have elapsed. Value is T if input is now available, NIL in the case that WAITFORINPUT timed out. (SKREAD(SKREAD (Function) NIL NIL NIL 5) FILE REREADSTRING RDTBL) [Function] "Skip Read". SKREAD consumes characters from FILE as if one call to READ had been performed, without paying the storage and compute cost to really read in the structure. REREADSTRING is for the case where the caller has already performed some READC's and RATOM's before deciding to skip this expression. In this case, REREADSTRING should be the material already read (as a string), and SKREAD operates as though it had seen that material first, thus setting up its parenthesis count, double-quote count, etc. The read table RDTBL is used for reading from FILE. If RDTBL is NIL, it defaults to the value of FILERDTBL. SKREAD may have difficulties if unusual read macros are defined in RDTBL. SKREAD does not recognize read macro characters in REREADSTRING, nor SPLICE or INFIX read macros. This is only a problem if the read macros are defined to parse subsequent input in the stream that does not follow the normal parenthesis and string-quote conventions. SKREAD returns %) if the read terminated on an unbalanced closing parenthesis; %] if the read terminated on an unbalanced %], i.e., one which also would have closed any extant open left parentheses; otherwise NIL. (SKIPSEPRS(SKIPSEPRS (Function) NIL NIL NIL 5) FILE RDTBL) [Function] Consumes characters from FILE until it encounters a non-separator character (as defined by RDTBL). SKIPSEPRS returns, but does not consume, the terminating character, so that the next call to READC would return the same character. If no non-separator character is found before the end of file is reached, SKIPSEPRS returns NIL and leaves the stream at end of file. This function is useful for skipping over "white space" when scanning a stream character by character, or for detecting end of file when reading expressions from a stream with no pre-arranged terminating expression. Output Functions(OUTPUT% FUNCTIONS NIL Output% Functions NIL NIL 6) 1 Unless otherwise specified by DEFPRINT, pointers other than lists, strings, atoms, or numbers, are printed in the form {DATATYPE} followed by the octal representation of the address of the pointer (regardless of radix). For example, an array pointer might print as {ARRAYP}#43,2760. This printed representation is for compactness of display on the user's terminal, and will not read back in correctly; if the form above is read, it will produce the litatom {ARRAYP}#43,2760. Note: The term "end-of-line" appearing in the description of an output function means the character or characters used to terminate a line in the file system being used by the given implementation of Interlisp. For example, in Interlisp-D end-of-line is indicated by the character carriage-return. Some of the functions described below have a RDTBL argument, which specifies the read table to be used for output. If RDTBL is NIL, the primary read table is used. Most of the functions described below have an argument FILE, which specifies the stream on which the operation is to take place. If FILE is NIL, the primary output stream is used . (OUTPUT(OUTPUT (Function) NIL NIL NIL 6) FILE) [Function] Sets FILE as the primary output stream; returns the old primary output stream. FILE must be open for output. (OUTPUT) returns the current primary output stream, which is not changed. Note: If the primary output stream is set to a file, the file's full name, rather than the stream itself, is returned. See the discussion in Chapter 24. (PRIN1(PRIN1 (Function) NIL NIL NIL 6) X FILE) [Function] Prints X on FILE. (PRIN2(PRIN2 (Function) NIL NIL NIL 6) X FILE RDTBL) [Function] Prints X on FILE with %'s and "'s inserted where required for it to read back in properly by READ, using RDTBL. Both PRIN1 and PRIN2 print any kind of Lisp expression, including lists, atoms, numbers, and strings. PRIN1 is generally used for printing expressions where human readability, rather than machine readability, is important, e.g., when printing text rather than program fragments. PRIN1 does not print double quotes around strings, or % in front of special characters. PRIN2 is used for printing Interlisp expressions which can then be read back into Interlisp with READ; i.e., break and separator characters in atoms will be preceded by %'s. For example, the atom "()" is printed as %(%) by PRIN2. If the integer output radix (as set by RADIX) is not 10, PRIN2 prints the integer using the input syntax for non-decimal integers (see Chapter 7) but PRIN1 does not (but both print the integer in the output radix). (PRIN3(PRIN3 (Function) NIL NIL NIL 6) X FILE) [Function] (PRIN4(PRIN4 (Function) NIL NIL NIL 6) X FILE RDTBL) [Function] PRIN3 and PRIN4 are the same as PRIN1 and PRIN2 respectively, except that they do not increment the horizontal position counter nor perform any linelength checks. They are useful primarily for printing control characters. (PRINT(PRINT (Function) NIL NIL NIL 6) X FILE RDTBL) [Function] Prints the expression X using PRIN2 followed by an end-of-line. Returns X. (PRINTCCODE(PRINT (Function) NIL NIL NIL 7) CHARCODE FILE) [Function] Outputs a single character whose code is CHARCODE to FILE. This is similar to (PRIN1 (CHARACTER CHARCODE)), except that numeric characters are guaranteed to print "correctly"; e.g., (PRINTCCODE (CHARCODE 9)) always prints "9", independent of the setting of RADIX. PRINTCCODE may actually print more than one byte on FILE, due to character encoding and end of line conventions; thus, no assumptions should be made about the relative motion of the file pointer (see GETFILEPTR) during this operation. (BOUT(BOUT (Function) NIL NIL NIL 7) STREAM BYTE) [Function] Outputs a single 8-bit byte to STREAM. This is similar to PRINTCCODE, but for binary streams the character position in STREAM is not updated (as with PRIN3), and end of line conventions are ignored. Note: BOUT is similar to PRINTCCODE, except that BOUT always writes a single byte, whereas PRINTCCODE writes a "character" that can consist of more than one byte, depending on the character and its encoding. (SPACES(SPACES (Function) NIL NIL NIL 7) N FILE) [Function] Prints N spaces. Returns NIL. (TERPRI (TERPRI% (Function) NIL NIL NIL 7)FILE) [Function] Prints an end-of-line character. Returns NIL. (FRESHLINE(FRESHLINE (Function) NIL NIL NIL 7) STREAM) [Function] Equivalent to TERPRI, except it does nothing if it is already at the beginning of the line. Returns T if it prints an end-of-line, NIL otherwise. (TAB(TAB (Function) NIL NIL NIL 7) POS MINSPACES FILE) [Function] Prints the appropriate number of spaces to move to position POS. MINSPACES indicates how many spaces must be printed (if NIL, 1 is used). If the current position plus MINSPACES is greater than POS, TAB does a TERPRI and then (SPACES POS). If MINSPACES is T, and the current position is greater than POS, then TAB does nothing. Note: A sequence of PRINT, PRIN2, SPACES, and TERPRI expressions can often be more conveniently coded with a single PRINTOUT statement. (SHOWPRIN2(SHOWPRIN2 (Function) NIL NIL NIL 7) X FILE RDTBL) [Function] Like PRIN2 except if SYSPRETTYFLG=T, prettyprints X instead. Returns X. (SHOWPRINT(SHOWPRINT (Function) NIL NIL NIL 7) X FILE RDTBL) [Function] Like PRINT except if SYSPRETTYFLG=T, prettyprints X instead, followed by an end-of-line. Returns X. SHOWPRINT and SHOWPRIN2 are used by the programmer's assistant (Chapter 13) for printing the values of expressions and for printing the history list, by various commands of the break package (Chapter 14), e.g. ?= and BT commands, and various other system packages. The idea is that by simply settting or binding SYSPRETTYFLG to T (initially NIL), the user instructs the system when interacting with the user to PRETTYPRINT expressions (Chapter 26) instead of printing them. (PRINTBELLS(PRINTBELLS (Function) NIL NIL NIL 8) %) [Function] Used by DWIM (Chapter 20) to print a sequence of bells to alert the user to stop typing. Can be advised or redefined for special applications, e.g., to flash the screen on a display terminal. (FORCEOUTPUT(FORCEOUTPUT (Function) NIL NIL NIL 8) STREAM WAITFORFINISH) [Function] Forces any buffered output data in STREAM to be transmitted. If WAITFORFINISH is non-NIL, this doesn't return until the data has been forced out. (POSITION(POSITION (Function) NIL NIL NIL 8) FILE N) [Function] Returns the column number at which the next character will be read or printed. After a end of line, the column number is 0. If N is non-NIL, resets the column number to be N. Note that resetting POSITION only changes Lisp's belief about the current column number; it does not cause any horizontal motion. Also note that (POSITION FILE) is not the same as (GETFILEPTR FILE) which gives the position in the file, not on the line. (LINELENGTH(LINELENGTH (Function) NIL NIL NIL 8) N FILE) [Function] Sets the length of the print line for the output file FILE to N; returns the former setting of the line length. FILE defaults to the primary output stream. (LINELENGTH NIL FILE) returns the current setting for FILE. When a file is first opened, its line length is set to the value of the variable FILELINELENGTH. Whenever printing an atom or string would increase a file's position beyond the line length of the file, an end of line is automatically inserted first. This action can be defeated by using PRIN3 and PRIN4. (SETLINELENGTH(SETLINELENGTH (Function) NIL NIL NIL 8) N) [Function] Sets the line length for the terminal by doing (LINELENGTH N T). If N is NIL, it determines N by consulting the operating system's belief about the terminal's characteristics. In Interlisp-D, this is a no-op. PRINTLEVEL When using Interlisp one often has to handle large, complicated lists, which are difficult to understand when printed out. PRINTLEVEL allows the user to specify in how much detail lists should be printed. The print functions PRINT, PRIN1, and PRIN2 are all affected by level parameters set by: (PRINTLEVEL(PRINTLEVEL (Function) NIL NIL NIL 8) CARVAL CDRVAL) [Function] Sets the CAR print level to CARVAL, and the CDR print level to CDRVAL. Returns a list cell whose CAR and CDR are the old settings. PRINTLEVEL is initialized with the value (1000 . -1). In order that PRINTLEVEL can be used with RESETFORM or RESETSAVE, if CARVAL is a list cell it is equivalent to (PRINTLEVEL (CAR CARVAL) (CDR CARVAL)). (PRINTLEVEL N NIL) changes the CAR printlevel without affecting the CDR printlevel. (PRINTLEVEL NIL N) changes the CDR printlevel with affecting the CAR printlevel. (PRINTLEVEL) gives the current setting without changing either. Note: Control-P (Chapter 30) can be used to change the PRINTLEVEL setting dynamically, even while Interlisp is printing. The CAR printlevel specifies how "deep" to print a list. Specifically, it is the number of unpaired left parentheses which will be printed. Below that level, all lists will be printed as &. If the CAR printlevel is negative, the action is similar except that an end-of-line is inserted after each right parentheses that would be immediately followed by a left parenthesis. The CDR printlevel specifies how "long" to print a list. It is the number of top level list elements that will be printed before the printing is terminated with --. For example, if CDRVAL=2, (A B C D E) will print as (A B --). For sublists, the number of list elements printed is also affected by the depth of printing in the CAR direction: Whenever the sum of the depth of the sublist (i.e. the number of unmatched left parentheses) and the number of elements is greater than the CDR printlevel, -- is printed. This gives a "triangular" effect in that less is printed the farther one goes in either CAR or CDR direction. If the CDR printlevel is negative, then it is the same as if the CDR printlevel were infinite. Examples: After: (A (B C (D (E F) G) H) K L) prints as: (PRINTLEVEL 3 -1) (A (B C (D & G) H) K L) (PRINTLEVEL 2 -1) (A (B C & H) K L) (PRINTLEVEL 1 -1) (A & K L) (PRINTLEVEL 0 -1) & (PRINTLEVEL 1000 2) (A (B --) --) (PRINTLEVEL 1000 3) (A (B C --) K --) (PRINTLEVEL 1 3) (A & K --) PLVLFILEFLG (PLVLFILEFLG% (Variable) NIL NIL NIL 9) [Variable] Normally, PRINTLEVEL only affects terminal output. Output to all other files acts as though the print level is infinite. However, if PLVLFILEFLG is T (initially NIL), then PRINTLEVEL affects output to files as well. The following three functions are useful for printing isolated expressions at a specified print level without going to the overhead of resetting the global print level. (LVLPRINT (LVLPRINT% (Function) NIL NIL NIL 9)X FILE CARLVL CDRLVL TAIL) [Function] Performs PRINT of X to FILE, using as CAR and CDR print levels the values CARLVL and CDRLVL, respectively. Uses the T read table. If TAIL is specified, and X is a tail of it, then begins its printing with "...", rather than on open parenthesis. (LVLPRIN2(LVLPRIN2 (Function) NIL NIL NIL 9) X FILE CARLVL CDRLVL TAIL) [Function] Similar to LVLPRIN2, but performs a PRIN2. (LVLPRIN1(LVLPRIN1 (Function) NIL NIL NIL 10) X FILE CARLVL CDRLVL TAIL) [Function] Similar to LVLPRIN1, but performs a PRIN1. Printing Numbers(PRINTING% NUMBERS NIL Printing% Numbers NIL NIL 10) How the ordinary printing functions (PRIN1, PRIN2, etc.) print numbers can be affected in several ways. RADIX influences the printing of integers, and FLTFMT influences the printing of floating point numbers. The setting of the variable PRXFLG determines how the symbol-manipulation functions handle numbers. The PRINTNUM package permits greater controls on the printed appearance of numbers, allowing such things as left-justification, suppression of trailing decimals, etc. (RADIX(RADIX (Function) NIL NIL NIL 10) N) [Function] Resets the output radix for integers to the absolute value of N. The value of RADIX is its previous setting. (RADIX) gives the current setting without changing it. The initial setting is 10. Note that RADIX affects output only. There is no input radix; on input, numbers are interpreted as decimal unless they are entered in a non-decimal radix with syntax such as 123Q, |b10101, |5r1234 (see Chapter 7). RADIX does not affect the behavior of UNPACK, etc., unless the value of PRXFLG (below) is T. For example, if PRXFLG is NIL and the radix is set to 8 with (RADIX 8), the value of (UNPACK 9) is (9), not (1 1). Using PRINTNUM (below) or the PRINTOUT command .I (below) is often a more convenient and appropriate way to print a single number in a specified radix than to globally change RADIX. (FLTFMT(FLTFMT (Function) NIL NIL NIL 10) FORMAT) [Function] Resets the output format for floating point numbers to the FLOAT format FORMAT (see PRINTNUM below for a description of FLOAT formats). FORMAT=T specifies the default "free" formatting: some number of significant digits (a function of the implementation) are printed, with trailing zeros suppressed; numbers with sufficiently large or small exponents are instead printed in exponent notation. FLTFMT returns its current setting. (FLTFMT) returns the current setting without changing it. The initial setting is T. Note: In Interlisp-D, FLTFMT ignores the WIDTH and PAD fields of the format (they are implemented only by PRINTNUM). Whether print name manipulation functions (UNPACK, NCHARS, etc.) use the values of RADIX and FLTFMT is determined by the variable PRXFLG: PRXFLG(PRXFLG (Variable) NIL NIL NIL 10) [Variable] If PRXFLG=NIL (the initial setting), then the "PRIN1" name used by PACK, UNPACK, MKSTRING, etc., is computed using base 10 for integers and the system default floating format for floating point numbers, independent of the current setting of RADIX or FLTFMT. If PRXFLG=T, then RADIX and FLTFMT do dictate the "PRIN1" name of numbers. Note that in this case, PACK and UNPACK are not inverses. Examples with (RADIX 8), (FLTFMT '(FLOAT 4 2)): With PRXFLG=NIL, (UNPACK 13) => (1 3) (PACK '(A 9)) => A9 (UNPACK 1.2345) => (1 %. 2 3 4 5) With PRXFLG=T, (UNPACK 13) => (1 5) (PACK '(A 9)) => A11 (UNPACK 1.2345) => (1 %. 2 3) Note that PRXFLG does not effect the radix of "PRIN2" names, so with (RADIX 8), (NCHARS 9 T), which uses PRIN2 names, would return 3, (since 9 would print as 11Q) for either setting of PRXFLG. Warning: Some system functions will not work correctly if PRXFLG is not NIL. Therefore, resetting the global value of PRXFLG is not recommended. It is much better to rebind PRXFLG as a SPECVAR for that part of a program where it needs to be non-NIL. The basic function for printing numbers under format control is PRINTNUM. Its utility is considerably enhanced when used in conjunction with the PRINTOUT package, which implements a compact language for specifying complicated sequences of elementary printing operations, and makes fancy output formats easy to design and simple to program. (PRINTNUM FORMAT NUMBER FILE) [Function] Prints NUMBER on FILE according to the format FORMAT. FORMAT is a list structure with one of the forms described below. If FORMAT is a list of the form (FIX WIDTH RADIX PAD0 LEFTFLUSH), this specifies a FIX format. NUMBER is rounded to the nearest integer, and then printed in a field WIDTH characters long with radix set to RADIX (or 10 if RADIX=NIL; note that the setting from the function RADIX is not used as the default). If PAD0 and LEFTFLUSH are both NIL, the number is right-justified in the field, and the padding characters to the left of the leading digit are spaces. If PAD0 is T, the character "0" is used for padding. If LEFTFLUSH is T, then the number is left-justified in the field, with trailing spaces to fill out WIDTH characters. The following examples illustrate the effects of the FIX format options on the number 9 (the vertical bars indicate the field width): FORMAT: (PRINTNUM FORMAT 9) prints: (FIX 2) | 9| (FIX 2 NIL T) |09| (FIX 12 8 T) |000000000011| (FIX 5 NIL NIL T) |9 | If FORMAT is a list of the form (FLOAT WIDTH DECPART EXPPART PAD0 ROUND), this specifies a FLOAT format. NUMBER is printed as a decimal number in a field WIDTH characters wide, with DECPART digits to the right of the decimal point. If EXPPART is not 0 (or NIL), the number is printed in exponent notation, with the exponent occupying EXPPART characters in the field. EXPPART should allow for the character E and an optional sign to be printed before the exponent digits. As with FIX format, padding on the left is with spaces, unless PAD0 is T. If ROUND is given, it indicates the digit position at which rounding is to take place, counting from the leading digit of the number. Interlisp-D interprets WIDTH=NIL to mean no padding, i.e., to use however much space the number needs, and interprets DECPART=NIL to mean as many decimal places as needed. The following examples illustrate the effects of the FLOAT format options on the number 27.689 (the vertical bars indicate the field width): FORMAT: (PRINTNUM FORMAT 27.689) prints: (FLOAT 7 2) | 27.69| (FLOAT 7 2 NIL T) |0027.69| (FLOAT 7 2 2) | 2.77E1| (FLOAT 11 2 4) | 2.77E+01| (FLOAT 7 2 NIL NIL 1) | 30.00| (FLOAT 7 2 NIL NIL 2) | 28.00| NILNUMPRINTFLG(NILNUMPRINTFLG (Variable) NIL NIL NIL 12) [Variable] If PRINTNUM's NUMBER argument is not a number and not NIL, a NON-NUMERIC ARG error is generated. If NUMBER is NIL, the effect depends on the setting of the variable NILNUMPRINTFLG. If NILNUMPRINTFLG is NIL, then the error occurs as usual. If it is non-NIL, then no error occurs, and the value of NILNUMPRINTFLG is printed right-justified in the field described by FORMAT. This option facilitates the printing of numbers in aggregates with missing values coded as NIL. User Defined Printing Initially, Interlisp only knows how to print in an interesting way objects of type litatom, number, string, list and stackp. All other types of objects are printed in the form {datatype} followed by the octal representation of the address of the pointer, a format that cannot be read back in to produce an equivalent object. When defining user data types (using the DATATYPE record type, Chapter 8), it is often desirable to specify as well how objects of that type should be printed, so as to make their contents readable, or at least more informative to the viewer. The function DEFPRINT is used to specify the printing format of a data type. (DEFPRINT(DEFPRINT (Function) NIL NIL NIL 12) TYPE FN) [Function] TYPE is a type name. Whenever a printing function (PRINT, PRIN1, PRIN2, etc.) or a function requiring a print name (CHCON, NCHARS, etc.) encounters an object of the indicated type, FN is called with two arguments: the item to be printed and the name of the stream, if any, to which the object is to be printed. The second argument is NIL on calls that request the print name of an object without actually printing it. If FN returns a list of the form (ITEM1 . ITEM2), ITEM1 is printed using PRIN1 (unless it is NIL), and then ITEM2 is printed using PRIN2 (unless it is NIL). No spaces are printed between the two items. Typically, ITEM1 is a read macro character. If FN returns NIL, the datum is printed in the system default manner. If FN returns T, nothing further is printed; FN is assumed to have printed the object to the stream itself. Note that this case if permitted only when the second argument passed to FN is non-NIL; otherwise, there is no destination for FN to do its printing, so it must return as in one of the other two cases. Printing Unusual Data Structures HPRINT (for "Horrible Print") and HREAD provide a mechanism for printing and reading back in general data structures that cannot normally be dumped and loaded easily, such as (possibly re-entrant or circular) structures containing user datatypes, arrays, hash tables, as well as list structures. HPRINT will correctly print and read back in any structure containing any or all of the above, chasing all pointers down to the level of literal atoms, numbers or strings. HPRINT currently cannot handle compiled code arrays, stack positions, or arbitrary unboxed numbers. HPRINT operates by simulating the Interlisp PRINT routine for normal list structures. When it encounters a user datatype (see Chapter 8), or an array or hash array, it prints the data contained therein, surrounded by special characters defined as read macro characters. While chasing the pointers of a structure, it also keeps a hash table of those items it encounters, and if any item is encountered a second time, another read macro character is inserted before the first occurrence (by resetting the file pointer with SETFILEPTR) and all subsequent occurrences are printed as a back reference using an appropriate macro character. Thus the inverse function, HREAD merely calls the Interlisp READ routine with the appropriate read table. (HPRINT(HPRINT (Function) NIL NIL NIL 13) EXPR FILE UNCIRCULAR DATATYPESEEN) [Function] Prints EXPR on FILE. If UNCIRCULAR is non-NIL, HPRINT does no checking for any circularities in EXPR (but is still useful for dumping arbitrary structures of arrays, hash arrays, lists, user data types, etc., that do not contain circularities). Specifying UNCIRCULAR as non-NIL results in a large speed and internal-storage advantage. Normally, when HPRINT encounters a user data type for the first time, it outputs a summary of the data type's declaration. When this is read in, the data type is redeclared. If DATATYPESEEN is non-NIL, HPRINT assumes that the same data type declarations will be in force at read time as were at HPRINT time, and not output declarations. HPRINT is intended primarily for output to random access files, since the algorithm depends on being able to reset the file pointer. If FILE is not a random access file (and UNCIRCULAR = NIL), a temporary file, HPRINT.SCRATCH, is opened, EXPR is HPRINTed on it, and then that file is copied to the final output file and the temporary file is deleted. (HREAD(HREAD (Function) NIL NIL NIL 13) FILE) [Function] Reads and returns an HPRINT-ed expression from FILE. (HCOPYALL(HCOPYALL (Function) NIL NIL NIL 13) X) [Function] Copies data structure X. X may contain circular pointers as well as arbitrary structures. Note: HORRIBLEVARS and UGLYVARS (Chapter 17) are two file package commands for dumping and reloading circular and re-entrant data structures. They provide a convenient interface to HPRINT and HREAD. When HPRINT is dumping a data structure that contains an instance of an Interlisp datatype, the datatype declaration is also printed onto the file. Reading such a data structure with HREAD can cause problems if it redefines a system datatype. Redefining a system datatype will almost definitely cause serious errors. The Interlisp system datatypes do not change very often, but there is always a possibility when loading in old files created under an old Interlisp release. To prevent accidental system crashes, HREAD will not redefine datatypes. Instead, it will cause an error "attempt to read DATATYPE with different field specification than currently defined". Continuing from this error will redefine the datatype. Random Access File Operations 1 For most applications, files are read starting at their beginning and proceeding sequentially, i.e., the next character read is the one immediately following the last character read. Similarly, files are written sequentially. However, for files on some devices, it is also possible to read/write characters at arbitrary positions in a file, essentially treating the file as a large block of auxiliary storage. For example, one application might involve writing an expression at the beginning of the file, and then reading an expression from a specified point in its middle. This particular example requires the file be open for both input and output. However, random file input or output can also be performed on files that have been opened for only input or only output. Associated with each file is a "file pointer" that points to the location where the next character is to be read from or written to. The file position of a byte is the number of bytes that precede it in the file, i.e., 0 is the position of the beginning of the file. The file pointer to a file is automatically advanced after each input or output operation. This section describes functions which can be used to reposition the file pointer on those files that can be randomly accessed. A file used in this fashion is much like an array in that it has a certain number of addressable locations that characters can be put into or taken from. However, unlike arrays, files can be enlarged. For example, if the file pointer is positioned at the end of a file and anything is written, the file "grows." It is also possible to position the file pointer beyond the end of file and then to write. (If the program attempts to read beyond the end of file, an END OF FILE error occurs.) In this case, the file is enlarged, and a "hole" is created, which can later be written into. Note that this enlargement only takes place at the end of a file; it is not possible to make more room in the middle of a file. In other words, if expression A begins at position 1000, and expression B at 1100, and the program attempts to overwrite A with expression C, whose printed representation is 200 bytes long, part of B will be altered. Warning: File positions are always in terms of bytes, not characters. The user should thus be very careful about computing the space needed for an expression. In particular, NS characters may take multiple bytes (see below). Also, the end-of-line character (see Chapter 24) may be represented by a different number of characters in different implementations. Output functions may also introduce end-of-line's as a result of LINELENGTH considerations. Therefore NCHARS (see Chapter 2) does not specify how many bytes an expression takes to print, even ignoring line length considerations. (GETFILEPTR(GETFILEPTR (Function) NIL NIL NIL 14) FILE) [Function] Returns the current position of the file pointer for FILE, i.e., the byte address at which the next input/output operation will commence. (SETFILEPTR(SETFILEPTR (Function) NIL NIL NIL 14) FILE ADR) [Function] Sets the file pointer for FILE to the position ADR; returns ADR. The special value ADR=-1 is interpreted to mean the address of the end of file. Note: If a file is opened for output only, the end of file is initially zero, even if an old file by the same name had existed (see OPENSTREAM, Chapter 24). If a file is opened for both input and output, the initial file pointer is the beginning of the file, but (SETFILEPTR FILE -1) sets it to the end of the file. If the file had been opened in append mode by (OPENSTREAM FILE 'APPEND), the file pointer right after opening would be set to the end of the existing file, in which case a SETFILEPTR to position the file at the end would be unnecessary. (GETEOFPTR(GETEOFPTR (Function) NIL NIL NIL 15) FILE) [Function] Returns the byte address of the end of file, i.e., the number of bytes in the file. Equivalent to performing (SETFILEPTR FILE -1) and returning (GETFILEPTR FILE) except that it does not change the current file pointer. (RANDACCESSP(RANDACCESSP (Function) NIL NIL NIL 15) FILE) [Function] Returns FILE if FILE is randomly accessible, NIL otherwise. The file T is not randomly accessible, nor are certain network file connections in Interlisp-D. FILE must be open or an error is generated, FILE NOT OPEN. (COPYBYTES(COPYBYTES (Function) NIL NIL NIL 15) SRCFIL DSTFIL START END) [Function] Copies bytes from SRCFIL to DSTFIL, starting from position START and up to but not including position END. Both SRCFIL and DSTFIL must be open. Returns T. If END=NIL, START is interpreted as the number of bytes to copy (starting at the current position). If START is also NIL, bytes are copied until the end of the file is reached. Warning: COPYBYTES does not take any account of multi-byte NS characters (see Chapter 2). COPYCHARS (below) should be used whenever copying information that might include NS characters. (COPYCHARS(COPYCHARS (Function) NIL NIL NIL 15) SRCFIL DSTFIL START END) [Function] Like COPYBYTES except that it copies NS characters (see Chapter 2), and performs the proper conversion if the end-of-line conventions of SRCFIL and DSTFIL are not the same (see Chapter 24). START and END are interpreted the same as with COPYBYTES, i.e., as byte (not character) specifications in SRCFIL. The number of bytes actually output to DSTFIL might be more or less than the number of bytes specified by START and END, depending on what the end-of-line conventions are. In the case where the end-of-line conventions happen to be the same, COPYCHARS simply calls COPYBYTES. (FILEPOS(FILEPOS (Function) NIL NIL NIL 15) PATTERN FILE START END SKIP TAIL CASEARRAY) [Function] Analogous to STRPOS (see Chapter 4), but searches a file rather than a string. FILEPOS searches FILE for the string PATTERN. Search begins at START (or the current position of the file pointer, if START=NIL), and goes to END (or the end of FILE, if END=NIL). Returns the address of the start of the match, or NIL if not found. SKIP can be used to specify a character which matches any character in the file. If TAIL is T, and the search is successful, the value is the address of the first character after the sequence of characters corresponding to PATTERN, instead of the starting address of the sequence. In either case, the file is left so that the next i/o operation begins at the address returned as the value of FILEPOS. CASEARRAY should be a "case array" that specifies that certain characters should be transformed to other characters before matching. Case arrays are returned by CASEARRAY or SEPRCASE below. CASEARRAY=NIL means no transformation will be performed. A case array is an implementation-dependent object that is logically an array of character codes with one entry for each possible character. FILEPOS maps each character in the file "through" CASEARRAY in the sense that each character code is transformed into the corresponding character code from CASEARRAY before matching. Thus if two characters map into the same value, they are treated as equivalent by FILEPOS. CASEARRAY and SETCASEARRAY provide an implementation-independent interface to case arrays. For example, to search without regard to upper and lower case differences, CASEARRAY would be a case array where all characters map to themselves, except for lower case characters, whose corresponding elements would be the upper case characters. To search for a delimited atom, one could use " ATOM " as the pattern, and specify a case array in which all of the break and separator characters mapped into the same code as space. For applications calling for extensive file searches, the function FFILEPOS is often faster than FILEPOS. (FFILEPOS(FFILEPOS (Function) NIL NIL NIL 16) PATTERN FILE START END SKIP TAIL CASEARRAY) [Function] Like FILEPOS, except much faster in most applications. FFILEPOS is an implementation of the Boyer-Moore fast string searching algorithm. This algorithm preprocesses the string being searched for and then scans through the file in steps usually equal to the length of the string. Thus, FFILEPOS speeds up roughly in proportion to the length of the string, e.g., a string of length 10 will be found twice as fast as a string of length 5 in the same position. Because of certain fixed overheads, it is generally better to use FILEPOS for short searches or short strings. (CASEARRAY(CASEARRAY (Function) NIL NIL NIL 16) OLDARRAY) [Function] Creates and returns a new case array, with all elements set to themselves, to indicate the identity mapping. If OLDARRAY is given, it is reused. (SETCASEARRAY(SETCASEARRAY (Function) NIL NIL NIL 16) CASEARRAY FROMCODE TOCODE) [Function] Modifies the case array CASEARRAY so that character code FROMCODE is mapped to character code TOCODE. (GETCASEARRAY(GETCASEARRAY (Function) NIL NIL NIL 16) CASEARRAY FROMCODE) [Function] Returns the character code that FROMCODE is mapped to in CASEARRAY. (SEPRCASE(SEPRCASE (Function) NIL NIL NIL 16) CLFLG) [Function] Returns a new case array suitable for use by FILEPOS or FFILEPOS in which all of the break/separators of FILERDTBL are mapped into character code zero. If CLFLG is non-NIL, then all CLISP characters are mapped into this character as well. This is useful for finding a delimited atom in a file. For example, if PATTERN is " FOO ", and (SEPRCASE T) is used for CASEARRAY, then FILEPOS will find "(FOO_". UPPERCASEARRAY(UPPERCASEARRAY (Variable) NIL NIL NIL 17) [Variable] Value is a case array in which every lowercase character is mapped into the corresponding uppercase character. Useful for searching text files. Input/Output Operations with Characters and Bytes 1 Interlisp-D supports the 16-bit NS character set (see Chapter 2). All of the standard string and print name functions accept litatoms and strings containing NS characters. In almost all cases, a program does not have to distinguish between NS characters or 8-bit characters. The exception to this rule is the handling of input/output operations. Interlisp-D uses two ways of writing 16-bit NS characters on files. One way is to write the full 16-bits (two bytes) every time a character is output. The other way is to use "run-encoding." Each 16 NS character can be decoded into a character set (an integer from 0 to 254 inclusive) and a character number (also an integer from 0 to 254 inclusive). In run-encoding, the byte 255 (illegal as either a character set number or a character number) is used to signal a change to a given character set, and the following bytes are all assumed to come from the same character set (until the next change-character set sequence). Run-encoding can reduce the number of bytes required to encode a string of NS characters, as long as there are long sequences of characters from the same character set (usually the case). Note that characters are not the same as bytes. A single character can take anywhere from one to four bytes bytes, depending on whether it is in the same character set as the preceeding character, and whether run-encoding is enabled. Programs which assume that characters are equal to bytes must be changed to work with NS characters. The functions BIN and BOUT (see above) should only be used to read and write single eight-bit bytes. The functions READCCODE and PRINTCCODE (see above) should be used to read and write single character codes, interpreting run-encoded NS characters. COPYBYTES should only be used to copy blocks of 8-bit data; COPYCHARS should be used to copy characters. Most I/O functions (READC, PRIN1, etc.) read or write 16-bit NS characters. The use of NS characters has serious consequences for any program that uses file pointers to access a file in a random access manner. At any point when a file is being read or written, it has a "current character set." If the file pointer is changed with SETFILEPTR to a part of the file with a different character set, any characters read or written may have the wrong character set. The current character set can be accessed with the following function: (CHARSET(CHARSET (Function) NIL NIL NIL 17) STREAM CHARACTERSET) [Function] Returns the current character set of the stream STREAM. If CHARACTERSET is non-NIL, the current character set for STREAM is set. Note that for output streams this may cause bytes to be written to the stream. If CHARACTERSET is T, run encoding for STREAM is disabled: both the character set and the character number (two bytes total) will be written to the stream for each character printed. PRINTOUT 1 Interlisp provides many facilities for controlling the format of printed output. By executing various sequences of PRIN1, PRIN2, TAB, TERPRI, SPACES, PRINTNUM, and PRINTDEF, almost any effect can be achieved. PRINTOUT implements a compact language for specifying complicated sequences of these elementary printing functions. It makes fancy output formats easy to design and simple to program. PRINTOUT is a CLISP word (like FOR and IF) for interpreting a special printing language in which the user can describe the kinds of printing desired. The description is translated by DWIMIFY to the appropriate sequence of PRIN1, TAB, etc., before it is evaluated or compiled. PRINTOUT printing descriptions have the following general form: (PRINTOUT STREAM PRINTCOM1 ... PRINTCOMN) STREAM is evaluated to obtain the stream to which the output from this specification is directed. The PRINTOUT commands are strung together, one after the other without punctuation, after STREAM. Some commands occupy a single position in this list, but many commands expect to find arguments following the command name in the list. The commands fall into several logical groups: one set deals with horizontal and vertical spacing, another group provides controls for certain formatting capabilities (font changes and subscripting), while a third set is concerned with various ways of actually printing items. Finally, there is a command that permits escaping to a simple Lisp evaluation in the middle of a PRINTOUT form. The various commands are described below. The following examples give a general flavor of how PRINTOUT is used: Example 1: Suppose the user wanted to print out on the terminal the values of three variables, X, Y, and Z, separated by spaces and followed by a carriage return. This could be done by: (PRIN1 X T) (SPACES 1 T) (PRIN1 Y T) (SPACES 1 T) (PRIN1 Z T) (TERPRI T) or by the more concise PRINTOUT form: (PRINTOUT T X , Y , Z T) Here the first T specifies output to the terminal, the commas cause single spaces to be printed, and the final T specifies a TERPRI. The variable names are not recognized as special PRINTOUT commands, so they are printed using PRIN1 by default. Example 2: Suppose the values of X and Y are to be pretty-printed lined up at position 10, preceded by identifying strings. If the output is to go to the primary output stream, the user could write either: (PRIN1 "X =") (PRINTDEF X 10 T) (TERPRI ) (PRIN1 "Y =") (PRINTDEF Y 10 T) (TERPRI) or the equivalent: (PRINTOUT NIL "X =" 10 .PPV X T "Y =" 10 .PPV Y T) Since strings are not recognized as special commands, "X =" is also printed with PRIN1 by default. The positive integer means TAB to position 10, where the .PPV command causes the value of X to be prettyprinted as a variable. By convention, special atoms used as PRINTOUT commands are prefixed with a period. The T causes a carriage return, so the Y information is printed on the next line. Example 3. As a final example, suppose that the value of X is an integer and the value of Y is a floating-point number. X is to be printed right-flushed in a field of width 5 beginning at position 15, and Y is to be printed in a field of width 10 also starting at position 15 with 2 places to the right of the decimal point. Furthermore, suppose that the variable names are to appear in the font class named BOLDFONT and the values in font class SMALLFONT. The program in ordinary Interlisp that would accomplish these effects is too complicated to include here. With PRINTOUT, one could write: (PRINTOUT NIL .FONT BOLDFONT "X =" 15 .FONT SMALLFONT .I5 X T .FONT BOLDFONT "Y =" 15 .FONT SMALLFONT .F10.2 Y T .FONT BOLDFONT) The .FONT commands do whatever is necessary to change the font on a multi-font output device. The .I5 command sets up a FIX format for a call to the function PRINTNUM (see above) to print X in the desired format. The .F10.2 specifies a FLOAT format for PRINTNUM. Horizontal Spacing Commands The horizontal spacing commands provide convenient ways of calling TAB and SPACES. In the following descriptions, N stands for a literal positive integer (not for a variable or expression whose value is an integer). N (N a number) [PRINTOUT Command] Used for absolute spacing. It results in a TAB to position N (literally, a (TAB N)). If the line is currently at position N or beyond, the file will be positioned at position N on the next line. .TAB(TAB (Command) .TAB NIL NIL 19)(.TAB (Command) NIL NIL NIL 19) POS [PRINTOUT Command] Specifies TAB to position (the value of) POS. This is one of several commands whose effect could be achieved by simply escaping to Lisp, and executing the corresponding form. It is provided as a separate command so that the PRINTOUT form is more concise and is prettyprinted more compactly. Note that .TAB N and N, where N is an integer, are equivalent. .TAB0(TAB0 (Command) .TAB0 NIL NIL 19)(.TAB0 (Command) NIL NIL NIL 19) POS [PRINTOUT Command] Like .TAB except that it can result in zero spaces (i.e. the call to TAB specifies MINSPACES=0). -N (N a number) [PRINTOUT Command] Negative integers indicate relative (as opposed to absolute) spacing. Translates as (SPACES |N|). ,(, (Command) NIL NIL NIL 19) [PRINTOUT Command] ,,(,, (Command) NIL NIL NIL 19) [PRINTOUT Command] ,,,(,,, (Command) NIL NIL NIL 19) [PRINTOUT Command] (1, 2 or 3 commas) Provides a short-hand way of specifying 1, 2 or 3 spaces, i.e., these commands are equivalent to -1, -2, and -3, respectively. .SP(SP (Command) .SP NIL NIL 19)(.SP (Command) NIL NIL NIL 19) DISTANCE [PRINTOUT Command] Translates as (SPACES DISTANCE). Note that .SP N and -N, where N is an integer, are equivalent. Vertical Spacing Commands Vertical spacing is obtained by calling TERPRI or printing form-feeds. The relevant commands are: T(T (Command) NIL NIL NIL 20) [PRINTOUT Command] Translates as (TERPRI), i.e., move to position 0 (the first column) of the next line. To print the letter T, use the string "T". .SKIP(SKIP (Command) .SKIP NIL NIL 20)(.SKIP (Command) NIL NIL NIL 20) LINES [PRINTOUT Command] Equivalent to a sequence of LINES (TERPRI)'s. The .SKIP command allows for skipping large constant distances and for computing the distance to be skipped. .PAGE(PAGE (Command) .PAGE NIL NIL 20)(.PAGE (Command) NIL NIL NIL 20) [PRINTOUT Command] Puts a form-feed (Control-L) out on the file. Care is taken to make sure that Interlisp's view of the current line position is correctly updated. Special Formatting Controls There are a small number of commands for invoking some of the formatting capabilities of multi-font output devices. The available commands are: .FONT(FONT (Command) .FONT NIL NIL 20)(.FONT (Command) NIL NIL NIL 20) FONTSPEC [PRINTOUT Command] Changes printing to the font FONTSPEC, which can be a font descriptor, a "font list" such as '(MODERN 10), an image stream (coerced to its current font), or a windows (coerced to the current font of its display stream). See fonts (Chapter 27) for more information. FONTSPEC may also be a positive integer N, which is taken as an abbreviated reference to the font class named FONTN (e.g. 1 => FONT1). .SUP(SUP (Command) .SUP NIL NIL 20)(.SUP (Command) NIL NIL NIL 20) [PRINTOUT Command] Specifies superscripting. All subsequent characters are printed above the base of the current line. Note that this is absolute, not relative: a .SUP following a .SUP is a no-op. .SUB(SUB (Command) .SUB NIL NIL 20)(.SUB (Command) NIL NIL NIL 20) [PRINTOUT Command] Specifies subscripting. Subsequent printing is below the base of the current line. As with superscripting, the effect is absolute. .BASE(BASE (Command) .BASE NIL NIL 20)(.BASE (Command) NIL NIL NIL 20) [PRINTOUT Command] Moves printing back to the base of the current line. Un-does a previous .SUP or .SUB; a no-op, if printing is currently at the base. Printing Specifications The value of any expression in a PRINTOUT form that is not recognized as a command itself or as a command argument is printed using PRIN1 by default. For example, title strings can be printed by simply including the string as a separate PRINTOUT command, and the values of variables and forms can be printed in much the same way. Note that a literal integer, say 51, cannot be printed by including it as a command, since it would be interpreted as a TAB; the desired effect can be obtained by using instead the string specification "51", or the form (QUOTE 51). For those instances when PRIN1 is not appropriate, e.g., PRIN2 is required, or a list structures must be prettyprinted, the following commands are available: .P2(P2 (Command) .P2 NIL NIL 21)(.P2 (Command) NIL NIL NIL 21) THING [PRINTOUT Command] Causes THING to be printed using PRIN2; translates as (PRIN2 THING). .PPF(PPF (Command) .PPF NIL NIL 21)(.PPF (Command) NIL NIL NIL 21) THING [PRINTOUT Command] Causes THING to be prettyprinted at the current line position via PRINTDEF (see Chapter 26). The call to PRINTDEF specifies that THING is to be printed as if it were part of a function definition. That is, SELECTQ, PROG, etc., receive special treatment. .PPV(PPV (Command) .PPV NIL NIL 21)(.PPV (Command) NIL NIL NIL 21) THING [PRINTOUT Command] Prettyprints THING as a variable; no special interpretation is given to SELECTQ, PROG, etc. .PPFTL(PPFTL (Command) .PPFTL NIL NIL 21)(.PPFTL (Command) NIL NIL NIL 21) THING [PRINTOUT Command] Like .PPF, but prettyprints THING as a tail, that is, without the initial and final parentheses if it is a list. Useful for prettyprinting sub-lists of a list whose other elements are formatted with other commands. .PPVTL(PPVTL (Command) .PPVTL NIL NIL 21)(.PPVTL (Command) NIL NIL NIL 21) THING [PRINTOUT Command] Like .PPV, but prettyprints THING as a tail. Paragraph Format Interlisp's prettyprint routines are designed to display the structure of expressions, but they are not really suitable for formatting unstructured text. If a list is to be printed as a textual paragraph, its internal structure is less important than controlling its left and right margins, and the indentation of its first line. The .PARA and .PARA2 commands allow these parameters to be conveniently specified. .PARA(PARA (Command) .PARA NIL NIL 21)(.PARA (Command) NIL NIL NIL 21) LMARG RMARG LIST [PRINTOUT Command] Prints LIST in paragraph format, using PRIN1. Translates as (PRINTPARA LMARG RMARG LIST) (see below). Example: (PRINTOUT T 10 .PARA 5 -5 LST) will print the elements of LST as a paragraph with left margin at 5, right margin at (LINELENGTH)-5, and the first line indented to 10. .PARA2(PARA2 (Command) .PARA2 NIL NIL 21)(.PARA2 (Command) NIL NIL NIL 21) LMARG RMARG LIST [PRINTOUT Command] Print as paragraph using PRIN2 instead of PRIN1. Translates as (PRINTPARA LMARG RMARG LIST T). Right-Flushing Two commands are provided for printing simple expressions flushed-right against a specified line position, using the function FLUSHRIGHT (see below). They take into account the current position, the number of characters in the print-name of the expression, and the position the expression is to be flush against, and then print the appropriate number of spaces to achieve the desired effect. Note that this might entail going to a new line before printing. Note also that right-flushing of expressions longer than a line (e.g. a large list) makes little sense, and the appearance of the output is not guaranteed. .FR(FR (Command) .FR NIL NIL 22)(.FR (Command) NIL NIL NIL 22) POS EXPR [PRINTOUT Command] Flush-right using PRIN1. The value of POS determines the position that the right end of EXPR will line up at. As with the horizontal spacing commands, a negative position number means |POS| columns from the current position, a positive number specifies the position absolutely. POS=0 specifies the right-margin, i.e. is interpreted as (LINELENGTH). .FR2(FR2 (Command) .FR2 NIL NIL 22)(.FR2 (Command) NIL NIL NIL 22) POS EXPR [PRINTOUT Command] Flush-right using PRIN2 instead of PRIN1. Centering Commands for centering simple expressions between the current line position and another specified position are also available. As with right flushing, centering of large expressions is not guaranteed. .CENTER(CENTER (Command) .CENTER NIL NIL 22)(.CENTER (Command) NIL NIL NIL 22) POS EXPR [PRINTOUT Command] Centers EXPR between the current line position and the position specified by the value of POS. A positive POS is an absolute position number, a negative POS specifies a position relative to the current position, and 0 indicates the right-margin. Uses PRIN1 for printing. .CENTER2(CENTER2 (Command) .CENTER2 NIL NIL 22)(.CENTER2 (Command) NIL NIL NIL 22) POS EXPR [PRINTOUT Command] Centers using PRIN2 instead of PRIN1. Numbering The following commands provide FORTRAN-like formatting capabilities for integer and floating-point numbers. Each command specifies a printing format and a number to be printed. The format specification translates into a format-list for the function PRINTNUM. .I(I (Command) .I NIL NIL 22)(.I (Command) NIL NIL NIL 22)FORMAT NUMBER [PRINTOUT Command] Specifies integer printing. Translates as a call to the function PRINTNUM with a FIX format-list constructed from FORMAT. The atomic format is broken apart at internal periods to form the format-list. For example, .I5.8.T yields the format-list (FIX 5 8 T), and the command sequence (PRINTOUT T .I5.8.T FOO) translates as (PRINTNUM '(FIX 5 8 T) FOO). This expression causes the value of FOO to be printed in radix 8 right-flushed in a field of width 5, with 0's used for padding on the left. Internal NIL's in the format specification may be omitted, e.g., the commands .I5..T and .I5.NIL.T are equivalent. The format specification .I1 is often useful for forcing a number to be printed in radix 10 (but not otherwise specially formatted), independent of the current setting of RADIX. .F(F (Command) .F NIL NIL 22)(.F (Command) NIL NIL NIL 22) FORMAT NUMBER [PRINTOUT Command] Specifies floating-number printing. Like the .I format command, except translates with a FLOAT format-list. .N(N (Command) .N NIL NIL 23)(.N (Command) NIL NIL NIL 23) FORMAT NUMBER [PRINTOUT Command] The .I and .F commands specify calls to PRINTNUM with quoted format specifications. The .N command translates as (PRINTNUM FORMAT NUMBER), i.e., it permits the format to be the value of some expression. Note that, unlike the .I and .F commands, FORMAT is a separate element in the command list, not part of an atom beginning with .N. Escaping to Lisp There are many reasons for taking control away from PRINTOUT in the middle of a long printing expression. Common situations involve temporary changes to system printing parameters (e.g. LINELENGTH), conditional printing (e.g. print FOO only if FIE is T), or lower-level iterative printing within a higher-level print specification. #(# (Command) NIL NIL NIL 23) FORM [PRINTOUT Command] The escape command. FORM is an arbitrary Lisp expression that is evaluated within the context established by the PRINTOUT form, i.e., FORM can assume that the primary output stream has been set to be the FILE argument to PRINTOUT. Note that nothing is done with the value of FORM; any printing desired is accomplished by FORM itself, and the value is discarded. Note: Although PRINTOUT logically encloses its translation in a RESETFORM (Chapter 14) to change the primary output file to the FILE argument (if non-NIL), in most cases it can actually pass FILE (or a locally bound variable if FILE is a non-trivial expression) to each printing function. Thus, the RESETFORM is only generated when the # command is used, or user-defined commands (below) are used. If many such occur in repeated PRINTOUT forms, it may be more efficient to embed them all in a single RESETFORM which changes the primary output file, and then specify FILE=NIL in the PRINTOUT expressions themselves. User-Defined Commands The collection of commands and options outlined above is aimed at fulfilling all common printing needs. However, certain applications might have other, more specialized printing idioms, so a facility is provided whereby the user can define new commands. This is done by adding entries to the global list PRINTOUTMACROS to define how the new commands are to be translated. PRINTOUTMACROS(PRINTOUTMACROS (Variable) NIL NIL NIL 23) [Variable] PRINTOUTMACROS is an association-list whose elements are of the form (COMM FN). Whenever COMM appears in command position in the sequence of PRINTOUT commands (as opposed to an argument position of another command), FN is applied to the tail of the command-list (including the command). After inspecting as much of the tail as necessary, the function must return a list whose CAR is the translation of the user-defined command and its arguments, and whose CDR is the list of commands still remaining to be translated in the normal way. For example, suppose the user wanted to define a command "?", which will cause its single argument to be printed with PRIN1 only if it is not NIL. This can be done by entering (? ?TRAN) on PRINTOUTMACROS, and defining the function ?TRAN as follows: (DEFINEQ (?TRAN (COMS) (CONS (SUBST (CADR COMS) 'ARG '(PROG ((TEMP ARG)) (COND (TEMP (PRIN1 TEMP))))) (CDDR COMS))] Note that ?TRAN does not do any printing itself; it returns a form which, when evaluated in the proper context, will perform the desired action. This form should direct all printing to the primary output file. Special Printing Functions The paragraph printing commands are translated into calls on the function PRINTPARA, which may also be called directly: (PRINTPARA(PRINTPARA (Function) NIL NIL NIL 24) LMARG RMARG LIST P2FLAG PARENFLAG FILE) [Function] Prints LIST on FILE in line-filled paragraph format with its first element beginning at the current line position and ending at or before RMARG, and with subsequent lines appearing between LMARG and RMARG. If P2FLAG is non-NIL, prints elements using PRIN2, otherwise PRIN1. If PARENFLAG is non-NIL, then parentheses will be printed around the elements of LIST. If LMARG is zero or positive, it is interpreted as an absolute column position. If it is negative, then the left margin will be at |LMARG|+(POSITION). If LMARG=NIL, the left margin will be at (POSITION), and the paragraph will appear in block format. If RMARG is positive, it also is an absolute column position (which may be greater than the current (LINELENGTH)). Otherwise, it is interpreted as relative to (LINELENGTH), i.e., the right margin will be at (LINELENGTH)+|RMARG|. Example: (TAB 10) (PRINTPARA 5 -5 LST T) will PRIN2 the elements of LST in a paragraph with the first line beginning at column 10, subsequent lines beginning at column 5, and all lines ending at or before (LINELENGTH)-5. The current (LINELENGTH) is unaffected by PRINTPARA, and upon completion, FILE will be positioned immediately after the last character of the last item of LIST. PRINTPARA is a no-op if LIST is not a list. The right-flushing and centering commands translate as calls to the function FLUSHRIGHT: (FLUSHRIGHT(FLUSHRIGHT (Function) NIL NIL NIL 24) POS X MIN P2FLAG CENTERFLAG FILE) [Function] If CENTERFLAG=NIL, prints X right-flushed against position POS on FILE; otherwise, centers X between the current line position and POS. Makes sure that it spaces over at least MIN spaces before printing by doing a TERPRI if necessary; MIN=NIL is equivalent to MIN=1. A positive POS indicates an absolute position, while a negative POS signifies the position which is |POS| to the right of the current line position. POS=0 is interpreted as (LINELENGTH), the right margin. READFILE and WRITEFILE 1 For those applications where the user simply wants to simply read all of the expressions on a file, and not evaluate them, the function READFILE is available: (READFILE(READFILE (Function) NIL NIL NIL 25) FILE RDTBL ENDTOKEN) [NoSpread Function] Reads successive expressions from file using READ (with read table RDTBL) until the single litatom ENDTOKEN is read, or an end of file encountered. Returns a list of these expressions. If RDTBL is not specified, it defaults to FILERDTBL. If ENDTOKEN is not specified, it defaults to the litatom STOP. (WRITEFILE(WRITEFILE (Function) NIL NIL NIL 25) X FILE) [Function] Writes a date expression onto FILE, followed by successive expressions from X, using FILERDTBL as a read table. If X is atomic, its value is used. If FILE is not open, it is opened. If FILE is a list, (CAR FILE) is used and the file is left opened. Otherwise, when X is finished, the litatom STOP is printed on FILE and it is closed. Returns FILE. (ENDFILE(ENDFILE (Function) NIL NIL NIL 25) FILE) [Function] Prints STOP on FILE and closes it. Read Tables 1 Many Interlisp input functions treat certain characters in special ways. For example, READ recognizes that the right and left parenthesis characters are used to specify list structures, and that the quote character is used to delimit text strings. The Interlisp input and (to a certain extent) output routines are table driven by read tables. Read tables are objects that specify the syntactic properties of characters for input routines. Since the input routines parse character sequences into objects, the read table in use determines which sequences are recognized as literal atoms, strings, list structures, etc. Most Interlisp input functions take an optional read table argument, which specifies the read table to use when reading an expression. If NIL is given as the read table, the "primary read table" is used. If T is specified, the system terminal read table is used. Some functions will also accept the atom ORIG (not the value of ORIG) as indicating the "original" system read table. Some output functions also take a read table argument. For example, PRIN2 prints an expression so that it would be read in correctly using a given read table. The Interlisp-D system uses the following read tables: T for input/output from terminals, the value of FILERDTBL for input/output from files, the value of EDITRDTBL for input from terminals while in the tty-based editor, the value of DEDITRDTBL for input from terminals while in the display-based editor, and the value of CODERDTBL for input/output from compiled files. These five read tables are initially copies of the ORIG read table, with changes made to some of them to provide read macros that are specific to terminal input or file input. Using the functions described below, the user may further change, reset, or copy these tables. However, in the case of FILERDTBL and CODERDTBL, the user is cautioned that changing these tables may prevent the system from being able to read files made with the original tables, or prevent users possessing only the standard tables from reading files made using the modified tables. The user can also create new read tables, and either explicitly pass them to input/output functions as arguments, or install them as the primary read table, via SETREADTABLE, and then not specify a RDTBL argument, i.e., use NIL. Read Table Functions (READTABLEP(READTABLEP (Function) NIL NIL NIL 26) RDTBL) [Function] Returns RDTBL if RDTBL is a real read table (not T or ORIG), otherwise NIL. (GETREADTABLE(GETREADTABLE (Function) NIL NIL NIL 26) RDTBL) [Function] If RDTBL=NIL, returns the primary read table. If RDTBL=T, returns the system terminal read table. If RDTBL is a real read table, returns RDTBL. Otherwise, generates an ILLEGAL READTABLE error. (SETREADTABLE(SETREADTABLE (Function) NIL NIL NIL 26) RDTBL FLG) [Function] Sets the primary read table to RDTBL. If FLG=T, SETREADTABLE sets the system terminal read table, T. Note that the user can reset the other system read tables with SETQ, e.g., (SETQ FILERDTBL (GETREADTABLE)). Generates an ILLEGAL READTABLE error if RDTBL is not NIL, T, or a real read table. Returns the previous setting of the primary read table, so SETREADTABLE is suitable for use with RESETFORM (Chapter 14). (COPYREADTABLE(COPYREADTABLE (Function) NIL NIL NIL 26) RDTBL) [Function] Returns a copy of RDTBL. RDTBL can be a real read table, NIL, T, or ORIG (in which case COPYREADTABLE returns a copy of the original system read table), otherwise COPYREADTABLE generates an ILLEGAL READTABLE error. Note that COPYREADTABLE is the only function that creates a read table. (RESETREADTABLE(RESETREADTABLE (Function) NIL NIL NIL 26) RDTBL FROM) [Function] Copies (smashes) FROM into RDTBL. FROM and RDTBL can be NIL, T, or a real read table. In addition, FROM can be ORIG, meaning use the system's original read table. Syntax Classes A read table is an object that contains information about the "syntax class" of each character. There are nine basic syntax classes: LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, STRINGDELIM, ESCAPE, BREAKCHAR, SEPRCHAR, and OTHER, each associated with a primitive syntactic property. In addition, there is an unlimited assortment of user-defined syntax classes, known as "read macros". The basic syntax classes are interpreted as follows: LEFTPAREN (normally left parenthesis) Begins list structure. RIGHTPAREN (normally right parenthesis) Ends list structure. LEFTBRACKET (normally left bracket) Begins list structure. Also matches RIGHTBRACKET characters. RIGHTBRACKET (normally left bracket) Ends list structure. Can close an arbitrary numbers of LEFTPAREN lists, back to the last LEFTBRACKET. STRINGDELIM (normally double quote) Begins and ends text strings. Within the string, all characters except for the one(s) with class ESCAPE are treated as ordinary, i.e., interpreted as if they were of syntax class OTHER. To include the string delimiter inside a string, prefix it with the ESCAPE character. ESCAPE (normally percent sign) Inhibits any special interpretation of the next character, i.e., the next character is interpreted to be of class OTHER, independent of its normal syntax class. BREAKCHAR (None initially) Is a break character, i.e., delimits atoms, but is otherwise an ordinary character. SEPRCHAR (space, carriage return, etc.) Delimits atoms, and is otherwise ignored. OTHER Characters that are not otherwise special belong to the class OTHER. Characters of syntax class LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, and STRINGDELIM are all break characters. That is, in addition to their interpretation as delimiting list or string structures, they also terminate the reading of an atom. Characters of class BREAKCHAR serve only to terminate atoms, with no other special meaning. In addition, if a break character is the first non-separator encountered by RATOM, it is read as a one-character atom. In order for a break character to be included in an atom, it must be preceded by the ESCAPE character. Characters of class SEPRCHAR also terminate atoms, but are otherwise completely ignored; they can be thought of as logically spaces. As with break characters, they must be preceded by the ESCAPE character in order to appear in an atom. For example, if $ were a break character and * a separator character, the input stream ABC**DEF$GH*$$ would be read by six calls to RATOM returning respectively ABC, DEF, $, GH, $, $. Although normally there is only one character in a read table having each of the list- and string-delimiting syntax classes (such as LEFTPAREN), it is perfectly acceptable for any character to have any syntax class, and for more than one to have the same class. Note that a "syntax class" is an abstraction: there is no object referencing a collection of characters called a syntax class. Instead, a read table provides the association between a character and its syntax class, and the input/output routines enforce the abstraction by using read tables to drive the parsing. The functions below are used to obtain and set the syntax class of a character in a read table. CH can either be a character code (a integer), or a character (a single-character atom). Single-digit integers are interpreted as character codes, rather than as characters. For example, 1 indicates Control-A, and 49 indicates the character 1. Note that CH can be a full sixteen-bit NS character (see Chapter 2). Note: Terminal tables, described in Chapter 30, also associate characters with syntax classes, and they can also be manipulated with the functions below. The set of read table and terminal table syntax classes are disjoint, so there is never any ambiguity about which type of table is being referred to. (GETSYNTAX CH TABLE) [Function] Returns the syntax class of CH, a character or a character code, with respect to TABLE. TABLE can be NIL, T, ORIG, or a real read table or terminal table. CH can also be a syntax class, in which case GETSYNTAX returns a list of the character codes in TABLE that have that syntax class. (SETSYNTAX CHAR CLASS TABLE) [Function] Sets the syntax class of CHAR, a character or character code, in TABLE. TABLE can be either NIL, T, or a real read table or terminal table. SETSYNTAX returns the previous syntax class of CHAR. CLASS can be any one of the following: f The name of one of the basic syntax classes. f A list, which is interpreted as a read macro (see below). f NIL, T, ORIG, or a real read table or terminal table, which means to give CHAR the syntax class it has in the table indicated by CLASS. For example, (SETSYNTAX '%( 'ORIG TABLE) gives the left parenthesis character in TABLE the same syntax class that it has in the original system read table. f A character code or character, which means to give CHAR the same syntax class as the character CHAR in TABLE. For example, (SETSYNTAX '{ '%[ TABLE) gives the left brace character the same syntax class as the left bracket. (SYNTAXP(SYNTAXP (Function) NIL NIL NIL 28) CODE CLASS TABLE) [Function] CODE is a character code; TABLE is NIL, T, or a real read table or terminal table. Returns T if CODE has the syntax class CLASS in TABLE; NIL otherwise. CLASS can also be a read macro type (MACRO, SPLICE, INFIX), or a read macro option (FIRST, IMMEDIATE, etc.), in which case SYNTAXP returns T if the syntax class is a read macro with the specified property. SYNTAXP will not accept a character as an argument, only a character code. For convenience in use with SYNTAXP, the atom BREAK may be used to refer to all break characters, i.e., it is the union of LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, STRINGDELIM, and BREAKCHAR. For purely symmetrical reasons, the atom SEPR corresponds to all separator characters. However, since the only separator characters are those that also appear in SEPRCHAR, SEPR and SEPRCHAR are equivalent. Note that GETSYNTAX never returns BREAK or SEPR as a value although SETSYNTAX and SYNTAXP accept them as arguments. Instead, GETSYNTAX returns one of the disjoint basic syntax classes that comprise BREAK. BREAK as an argument to SETSYNTAX is interpreted to mean BREAKCHAR if the character is not already of one of the BREAK classes. Thus, if %( is of class LEFTPAREN, then (SETSYNTAX '%( 'BREAK) doesn't do anything, since %( is already a break character, but (SETSYNTAX '%( 'BREAKCHAR) means make %( be just a break character, and therefore disables the LEFTPAREN function of %(. Similarly, if one of the format characters is disabled completely, e.g., by (SETSYNTAX '%( 'OTHER), then (SETSYNTAX '%( 'BREAK) would make %( be only a break character; it would not restore %( as LEFTPAREN. The following functions provide a way of collectively accessing and setting the separator and break characters in a read table: (GETSEPR(GETSEPR (Function) NIL NIL NIL 28) RDTBL) [Function] Returns a list of separator character codes in RDTBL. Equivalent to (GETSYNTAX 'SEPR RDTBL). (GETBRK(GETBRK (Function) NIL NIL NIL 28) RDTBL) [Function] Returns a list of break character codes in RDTBL. Equivalent to (GETSYNTAX 'BREAK RDTBL). (SETSEPR(SETSEPR (Function) NIL NIL NIL 29) LST FLG RDTBL) [Function] Sets or removes the separator characters for RDTBL. LST is a list of charactors or character codes. FLG determines the action of SETSEPR as follows: If FLG=NIL, makes RDTBL have exactly the elements of LST as separators, discarding from RDTBL any old separator characters not in LST. If FLG=0, removes from RDTBL as separator characters all elements of LST. This provides an "UNSETSEPR". If FLG=1, makes each of the characters in LST be a separator in RDTBL. If LST=T, the separator characters are reset to be those in the system's read table for terminals, regardless of the value of FLG, i.e., (SETSEPR T) is equivalent to (SETSEPR (GETSEPR T)). If RDTBL is T, then the characters are reset to those in the original system table. Returns NIL. (SETBRK(SETBRK (Function) NIL NIL NIL 29) LST FLG RDTBL) [Function] Sets the break characters for RDTBL. Similar to SETSEPR. As with SETSYNTAX to the BREAK class, if any of the list- or string-delimiting break characters are disabled by an appropriate SETBRK (or by making it be a separator character), its special action for READ will not be restored by simply making it be a break character again with SETBRK. However, making these characters be break characters when they already are will have no effect. The action of the ESCAPE character (normally %) is not affected by SETSEPR or SETBRK. It can be disabled by setting its syntax to the class OTHER, and other characters can be used for escape on input by assigning them the class ESCAPE. As of this writing, however, there is no way to change the output escape character; it is "hardwired" as %. That is, on output, characters of special syntax that need to be preceded by the ESCAPE character will always be preceded by %, independent of the syntax of % or which, if any characters, have syntax ESCAPE. The following function can be used for defeating the action of the ESCAPE character or characters: (ESCAPE(ESCAPE (Function) NIL NIL NIL 29) FLG RDTBL) [Function] If FLG=NIL, makes characters of class ESCAPE behave like characters of class OTHER on input. Normal setting is (ESCAPE T). ESCAPE returns the previous setting. Read Macros Read macros are user-defined syntax classes that can cause complex operations when certain characters are read. Read macro characters are defined by specifying as a syntax class an expression of the form: (TYPE OPTION1 ... OPTIONN FN) where TYPE is one of MACRO, SPLICE, or INFIX, and FN is the name of a function or a lambda expression. Whenever READ encounters a read macro character, it calls the associated function, giving it as arguments the input stream and read table being used for that call to READ. The interpretation of the value returned depends on the type of read macro: MACRO This is the simplest type of read macro. The result returned from the macro is treated as the expression to be read, instead of the read macro character. Often the macro reads more input itself. For example, in order to cause ~EXPR to be read as (NOT EXPR), one could define ~ as the read macro: [MACRO (LAMBDA (FL RDTBL) (LIST 'NOT (READ FL RDTBL] SPLICE The result (which should be a list or NIL) is spliced into the input using NCONC. For example, if $ is defined by the read macro: (SPLICE (LAMBDA NIL (APPEND FOO))) and the value of FOO is (A B C), then when the user inputs (X $ Y), the result will be (X A B C Y). INFIX The associated function is called with a third argument, which is a list, in TCONC format (Chapter 3), of what has been read at the current level of list nesting. The function's value is taken as a new TCONC list which replaces the old one. For example, the infix operator + could be defined by the read macro: (INFIX (LAMBDA (FL RDTBL Z) (RPLACA (CDR Z) (LIST (QUOTE IPLUS) (CADR Z) (READ FL RDTBL))) Z)) If an INFIX read macro character is encountered not in a list, the third argument to its associated function is NIL. If the function returns NIL, the read macro character is essentially ignored and reading continues. Otherwise, if the function returns a TCONC list of one element, that element is the value of the READ. If it returns a TCONC list of more than one element, the list is the value of the READ. The specification for a read macro character can be augmented to specify various options OPTION1 ... OPTIONN, e.g., (MACRO FIRST IMMEDIATE FN). The following three disjoint options specify when the read macro character is to be effective: ALWAYS The default. The read macro character is always effective (except when preceded by the % character), and is a break character, i.e., a member of (GETSYNTAX 'BREAK RDTBL). FIRST The character is interpreted as a read macro character only when it is the first character seen after a break or separator character; in all other situations, the character is treated as having class OTHER. The read macro character is not a break character. For example, the quote character is a FIRST read macro character, so that DON'T is read as the single atom DON'T, rather than as DON followed by (QUOTE T). ALONE The read macro character is not a break character, and is interpreted as a read macro character only when the character would have been read as a separate atom if it were not a read macro character, i.e., when its immediate neighbors are both break or separator characters. Making a FIRST or ALONE read macro character be a break character (with SETBRK) disables the read macro interpretation, i.e., converts it to syntax class BREAKCHAR. Making an ALWAYS read macro character be a break character is a no-op. The following two disjoint options control whether the read macro character is to be protected by the ESCAPE character on output when a litatom containing the character is printed: ESCQUOTE or ESC The default. When printed with PRIN2, the read macro character will be preceded by the output escape character (%) as needed to permit the atom containing it to be read correctly. Note that for FIRST macros, this means that the character need be quoted only when it is the first character of the atom. NOESCQUOTE or NOESC The read macro character will always be printed without an escape. For example, the ? read macro in the T read table is a NOESCQUOTE character. Unless you are very careful what you are doing, read macro characters in FILERDTBL should never be NOESCQUOTE, since symbols that happen to contain the read macro character will not read back in correctly. The following two disjoint options control when the macro's function is actually executed: IMMEDIATE or IMMED The read macro character is immediately activated, i.e., the current line is terminated, as if an EOL had been typed, a carriage-return line-feed is printed, and the entire line (including the macro character) is passed to the input function. IMMEDIATE read macro characters enable the user to specify a character that will take effect immediately, as soon as it is encountered in the input, rather than waiting for the line to be terminated. Note that this is not necessarily as soon as the character is typed. Characters that cause action as soon as they are typed are interrupt characters (see Chapter 30). Note that since an IMMEDIATE macro causes any input before it to be sent to the reader, characters typed before an IMMEDIATE read macro character cannot be erased by Control-A or Control-Q once the IMMEDIATE character has been typed, since they have already passed through the line buffer. However, an INFIX read macro can still alter some of what has been typed earlier, via its third argument. NONIMMEDIATE or NONIMMED The default. The read macro character is a normal character with respect to the line buffering, and so will not be activated until a carriage-return or matching right parenthesis or bracket is seen. Making a read macro character be both ALONE and IMMEDIATE is a contradiction, since ALONE requires that the next character be input in order to see if it is a break or separator character. Thus, ALONE read macros are always NONIMMEDIATE, regardless of whether or not IMMEDIATE is specified. Read macro characters can be "nested". For example, if = is defined by (MACRO (LAMBDA (FL RDTBL) (EVAL (READ FL RDTBL)))) and ! is defined by (SPLICE (LAMBDA (FL RDTBL) (READ FL RDTBL))) then if the value of FOO is (A B C), and (X =FOO Y) is input, (X (A B C) Y) will be returned. If (X !=FOO Y) is input, (X A B C Y) will be returned. Note: If a read macro's function calls READ, and the READ returns NIL, the function cannot distinguish the case where a RIGHTPAREN or RIGHTBRACKET followed the read macro character, (e.g. "(A B ')"), from the case where the atom NIL (or "()") actually appeared. In Interlisp-D, a READ inside of a read macro when the next input character is a RIGHTPAREN or RIGHTBRACKET reads the character and returns NIL, just as if the READ had not occurred inside a read macro. If a call to READ from within a read macro encounters an unmatched RIGHTBRACKET within a list, the bracket is simply put back into the buffer to be read (again) at the higher level. Thus, inputting an expression such as (A B '(C D] works correctly. (INREADMACROP(INREADMACROP (Function) NIL NIL NIL 32)) [Function] Returns NIL if currently not under a read macro function, otherwise the number of unmatched left parentheses or brackets. (READMACROS(READMACROS (Function) NIL NIL NIL 32) FLG RDTBL) [Function] If FLG=NIL, turns off action of read macros in read table RDTBL. If FLG=T, turns them on. Returns previous setting. The following read macros are standardly defined in Interlisp in the T and EDITRDTBL read tables: ' (single-quote) Returns the next expression, wrapped in a call to QUOTE; e.g., 'FOO reads as (QUOTE FOO). The macro is defined as a FIRST read macro, so that the quote character has no effect in the middle of a symbol. The macro is also ignored if the quote character is immediately followed by a separator character. Control-Y Defined in T and EDITRDTBL. Returns the result of evaluating the next expression. For example, if the value of FOO is (A B), then (LIST 1 control-YFOO 2) is read as (LIST 1 (A B) 2). Note that no structure is copied; the third element of that input expression is still EQ to the value of FOO. Control-Y can thus be used to read structures that ordinarily have no read syntax. For example, the value returned from reading (KEY1 Control-Y(ARRAY 10)) has an array as its second element. Control-Y can be thought of as an "un-quote" character. The choice of character to perform this function is changeable with SETTERMCHARS (see Chapter 16). ` (backquote) Backquote makes it easier to write programs to construct complex data structures. Backquote is like quote, except that within the backquoted expression, forms can be evaluated. The general idea is that the backquoted expression is a "template" containing some constant parts (as with a quoted form) and some parts to be filled in by evaluating something. Unlike with control-Y, however, the evaluation occurs not at the time the form is read, but at the time the backquoted expression is evaluated. That is, the backquote macro returns an expression which, when evaluated, produces the desired structure. Within the backquoted expression, the character "," (comma) introduces a form to be evaluated. The value of a form preceded by ",@" is to be spliced in, using APPEND. If it is permissible to destroy the list being spliced in (i.e., NCONC may be used in the translation), then ",." can be used instead of ",@". For example, if the value of FOO is (1 2 3 4), then the form `(A ,(CAR FOO) ,@(CDDR FOO) D E) evaluates to (A 1 3 4 D E); it is logically equivalent to writing (CONS 'A (CONS (CAR FOO) (APPEND (CDDR FOO) '(D E)))) . Backquote is particularly useful for writing macros. For example, the body of a macro that refers to X as the macro's argument list might be `(COND ((FIXP ,(CAR X)) ,(CADR X)) (T .,(CDDR X))) which is equivalent to writing (LIST 'COND (LIST (LIST 'FIXP (CAR X)) (CADR X)) (CONS 'T (CDDR X))) Note that comma does not have any special meaning outside of a backquote context. For users without a backquote character on their keyboards, backquote can also be written as |' (vertical-bar, quote). ? Implements the ?= command for on-line help regarding the function currently being "called" in the typein (see Chapter 26). | (vertical bar) When followed by an end of line, tab or space, | is ignored, i.e., treated as a separator character, enabling the editor's CHANGECHAR feature (see Chapter 26). Otherwise it is a "dispatching" read macro whose meaning depends on the character(s) following it. The following are currently defined: ' (quote) -- A synonym for backquote. . (period) -- Returns the evaluation of the next expression, i.e., this is a synonym for Control-Y. , (comma) -- Returns the evaluation of the next expression at load time, i.e., the following expression is quoted in such a manner that the compiler treats it as a literal whose value is not determined until the compiled expression is loaded. O or o (the letter O) -- Treats the next number as octal, i.e., reads it in radix 8. For example, |o12 = 10 (decimal). B or b -- Treats the next number as binary, i.e., reads it in radix 2. For example, |b101 = 5 (decimal). X or x -- Treats the next number as hexadecimal, i.e., reads it in radix 16. The uppercase letters A though F are used as the digits after 9. For example, |x1A = 26 (decimal). R or r -- Reads the next number in the radix specified by the (decimal) number that appears between the | and the R. When inputting a number in a radix above ten, the upper-case letters A through Z can be used as the digits after 9 (but there is no digit above Z, so it is not possible to type all base-99 digits). For example, |3r120 reads 120 in radix 3, returning 15. (, {, ^ -- Used internally by HPRINT and HREAD (see above) to print and read unusual expressions. The dispatching characters that are letters can appear in either upper- or lowercase. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "25-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "25-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "25-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "25-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "25-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "25-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))9/HH +52HHl2HHl5/HH3(T-T3HZ +T-llT3lT,-ll +T4HZ +TT2lxx2Hll/HH +5,2lll2lll2HHl2HH +2l0HHT/ll3$$lT2HH +l3HH +T5,,53$$(T2l +2Hll3(T2Hl +l,ll +3HZT,HH3HZ +T,ll5,HH +-T3(T,HH,2F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR,,,CLASSIC + HELVETICA TITAN +TITAN +CLASSIC +CLASSIC + HELVETICA HELVETICA HELVETICA +CLASSIC +MODERN +MODERN +MODERNMODERN +6 HRULE.GETFNCLASSIC + +6  +5 HRULE.GETFNCLASSIC + +5 4  +4 HRULE.GETFNCLASSIC + +3  +3 HRULE.GETFNCLASSIC + + 22 IM.CHAP.GETFN HELVETICA1 HRULE.GETFNMODERN +0 + +/-IM.INDEX.GETFN. HRULE.GETFNMODERN +-I + +O + +, + + + +, + + +8 +1 +,U +t +, + + + + +,J + + + +,J +, +* +$IM.INDEX.GETFN +  ++ +% + + + +$ + + + + +U +J +  +*"IM.INDEX.GETFN ++ + + + + +/. HRULE.GETFNMODERN +- + +, + + +  +) +  +) + 6 + S +) +a + +" +) + +" +$ +* IM.INDEX.GETFN + ++ +E + ++A ++ +*IM.INDEX.GETFN + ++ +K +i + + + + + +  + ++t + +R + + ++) + +# + + +a + + + +) ++ + +6 + + + , ++ +2 +V += +* + + ++ +% + + +W ++S + + + +/ + +*"IM.INDEX.GETFN + + ++ + + + +-E + + + +L +*!IM.INDEX.GETFN + + + +; + +*!IM.INDEX.GETFN + ++ + + + +S + + + ++ + + + + + + + ++ + + + + + + +) + + + +*"IM.INDEX.GETFN + ++0 + +N +% + + + ? + + + +*"IM.INDEX.GETFNTITAN + + ++ + +8 + +* IM.INDEX.GETFN + ++% + +* +$IM.INDEX.GETFN + + ++ + +B + + +*$IM.INDEX.GETFNTITAN + +  + ++/ + +B + +*IM.INDEX.GETFNMODERN + + ++ +X +& + +  +% + j +- + + + +c + +* IM.INDEX.GETFN + ++ +- + + + + + ++% + +/ + ++: + + + + + + + +3 + +1 +IM.INDEX.GETFN + ++ +N +  + +> +  ++ + + + + +  +  +*)IM.INDEX.GETFNMODERN + ++$ +! + +  +z + + ++' + B +% + + +  +*!IM.INDEX.GETFNMODERN + + ++ + + +c + = + +; + 8 +u ++ + + + + +  += + +- +  + + ++ +> +) +U + +* +$IM.INDEX.GETFNMODERN + + + ++ +> + + T +m +  + +%3IM.INDEX.GETFN. HRULE.GETFNMODERN +- +R + +^ +P + +$, +)- +E + +" +)7 +J + +& +*!IM.INDEX.GETFN + ++ +G + ++B + +* IM.INDEX.GETFN + ++ + + +* IM.INDEX.GETFN +  ++ + + + +> + + +- + +S + +1 +" +\ +D + + + +* + +X +< +* IM.INDEX.GETFN + + +( IM.INDEX.GETFN +  +' + + + +* IM.INDEX.GETFN +  +' + +& + +*  IM.INDEX.GETFN +  +') + + +L + +! + +' +* + + + +*IM.INDEX.GETFN +  +' + + +3 + +. +# + + + +& + +j +*!IM.INDEX.GETFN + +' + + +*#IM.INDEX.GETFN +'* + +* +$IM.INDEX.GETFN + +' +Q + + +*IM.INDEX.GETFN + +'< + + / +, +  + + + + + +  ++ + + +& + + + +@ + +* +$IM.INDEX.GETFN +  +' + +  + + + +* +$IM.INDEX.GETFN +  +' + +  + +/ + +- + +  +I + + + + + + + + + + +; +*%IM.INDEX.GETFNMODERN +  + +' +* &IM.INDEX.GETFNMODERN + + +'# + +' +  +: +* #IM.INDEX.GETFNMODERN +  +' + + + + +' +v +  + + ! + + +* %IM.INDEX.GETFNCLASSIC + + +'6 + +2 +) +! +T + +'E +t + + +*(IM.INDEX.GETFNCLASSIC + + +'/ +  + + +u +" -| + +] + + +. +* %IM.INDEX.GETFNMODERN + +  +' + + + + + + + + + +  +' + + +  +  +$ + +  +" + + + + + 4 +#8 + +7 +' + + + + + +' + + + +  +f + +| + +f + + +7 + +' + +! + +!+!%!!!#!'!* (IM.INDEX.GETFN +' + + +s +  + + + +" +- +*  %IM.INDEX.GETFN +' + + + + + + + + + +0 +" +* #IM.INDEX.GETFNMODERN + + +' + + +* $IM.INDEX.GETFNMODERN +  +' + + +"4IM.INDEX.GETFN-% + +8 +* +Q +G + +*!IM.INDEX.GETFNMODERN + + +'> + + +L +' + + + + + + +! + + + + + +  + + + + +' + + +~ + +*"IM.INDEX.GETFN  +'; + + + + + + +' +J + +' + + +4 + +-+ + + + + + +*"IM.INDEX.GETFNMODERN + +' + +" + + + + + + + + + + +, + + + + +! +  +   $  + +  ' + + + +  +  +K + +'; + +, +2 + +5 + +-@ +J + +*   + ++ + + + +; ++ + +  + + +@ +# + + +* + + + +  + +z + + + +  +S + ++5 +N + +   + + + + + + +  + + ++ + + + + ++ + +/ + + +K + + +K +4 + + +~ ++ + +V + ++ ++5 +S +  +  + + +  + + + + + + +  + + + + +**IM.INDEX.GETFNMODERN + ++ + +" + + + +4 + + +0 +) +6 +^ + +"- + + + +8 +* $IM.INDEX.GETFNMODERN + ++0 + + +. + +4 + +Q ++ + + + + + + + += + ++ + +5 ++ + + + + +) +H +" - + + +^ +)& + + + + +* +*"IM.INDEX.GETFNMODERN + +! ++ + + + + + ++ + + + +: ++ + +  + +W +$ ++ +" + + + + + +b +*!IM.INDEX.GETFNMODERN + + ++ + + +* $IM.INDEX.GETFNMODERN + + ++ + +? ++ +  + + + ++ + + ++& + +7 +R; +/. HRULE.GETFNMODERN +) + K +9 + +) + + +A + +  +i +) +0 + +: + +) + + + +` +* &IM.INDEX.GETFN + ++5 +Q +* &IM.INDEX.GETFN + ++ + + + + + +8 + + +z + P + e + +7 +* +%IM.INDEX.GETFN + ++n +  + : +* 'IM.INDEX.GETFNCLASSIC + ++ + + + +W +( +  +* +%IM.INDEX.GETFNMODERN + + ++ + + +& + + + + ++ + + +W + +9 ++ + + I + W +* +%IM.INDEX.GETFNMODERN +  ++ + { + +% + +" + 2 +* += + +{ +  +  +*#IM.INDEX.GETFNMODERN +* ++ += + + + + +2 + + + + + +6 + ++Q + +P +- + + + +  +  + +  +, ++ ++ + a + e + +  + C ++K +  + +C + + +* $IM.INDEX.GETFNMODERN + +* ++ +, + + ++B +& +* +%IM.INDEX.GETFNMODERN + + ++q + +* (IM.INDEX.GETFNMODERN + + ++ +  + + +* (IM.INDEX.GETFNMODERN + + ++ + +  +* $IM.INDEX.GETFNMODERN + + ++- + +) + * + + + + +  +  + + + *IM.INDEX.GETFN ++ +/2. HRULE.GETFNMODERN +-] +)0 +)/" +) + +Z +  + +o + 3 + 9 + +, +) + + +*#IM.INDEX.GETFNMODERN + + ++0 + +  + +Y ++ +  + + +% . HRULE.GETFNMODERN +-t + + + + + + +& + +) + + + + +- +8 + +)a +N + +g + + +) W + + +Q +      ) + +) +_ + +4 +% + +)  + + + + ) + )6 + +) + + +J ++ +" +* +) 1 + + +T + + + s + +) +Z + +# + + + + + +"-C + +" +( +: +*  + ++, + + +) +4 + +*IM.INDEX.GETFNMODERN +IM.INDEX.GETFN ++ + + + +F + + + +*!IM.INDEX.GETFNMODERN + IM.INDEX.GETFN ++ +< + +  +*    ++U + + + +* IM.INDEX.GETFN +( IM.INDEX.GETFN +( IM.INDEX.GETFN ++t + + + +*IM.INDEX.GETFNIM.INDEX.GETFNCLASSIC + ++ + + + + +"-( +5 +*IM.INDEX.GETFNCLASSIC + ++ +U + + +*!IM.INDEX.GETFN IM.INDEX.GETFNCLASSIC + ++ + + +d +*!IM.INDEX.GETFN IM.INDEX.GETFNCLASSIC + ++ +"- +*!IM.INDEX.GETFN IM.INDEX.GETFN ++ +8 +  ++ +E + + +*IM.INDEX.GETFNIM.INDEX.GETFNCLASSIC + ++ + + +*IM.INDEX.GETFNIM.INDEX.GETFNCLASSIC + ++ +*!IM.INDEX.GETFNCLASSIC + IM.INDEX.GETFN ++I + +1 +"-! +[ +e + +a + + +) + +a +*IM.INDEX.GETFNIM.INDEX.GETFNCLASSIC + ++ + + + +*IM.INDEX.GETFNIM.INDEX.GETFNCLASSIC + ++ +6 + + +I + +# +*IM.INDEX.GETFNIM.INDEX.GETFNCLASSIC + ++ +6 + + +*#IM.INDEX.GETFN!IM.INDEX.GETFNCLASSIC + ++ + + + +*#IM.INDEX.GETFNCLASSIC +!IM.INDEX.GETFN ++ + + +-P + +A +*!IM.INDEX.GETFN IM.INDEX.GETFNCLASSIC + ++ + + + + ++ + + +7 + ( +*#IM.INDEX.GETFN!IM.INDEX.GETFNCLASSIC + ++ + + + + +-~ + + +*IM.INDEX.GETFNIM.INDEX.GETFNCLASSIC + ++ + +/ +^ +[ +7 +  +*IM.INDEX.GETFNCLASSIC +IM.INDEX.GETFN ++ + + + +- +*%IM.INDEX.GETFN"IM.INDEX.GETFNCLASSIC + ++ +N + +, +` + +*'IM.INDEX.GETFNCLASSIC +#IM.INDEX.GETFN ++ + + +  - + +*IM.INDEX.GETFNIM.INDEX.GETFN ++B + + +` + +  + +' +p +B + +  ++ + + +*IM.INDEX.GETFNIM.INDEX.GETFN ++. +* + + IM.INDEX.GETFNIM.INDEX.GETFN ++ + + +) + + Y + + +O + +"-4 + + +$ + + +P +* IM.INDEX.GETFN   ++ +Y + +B + +& + +* +% +& +) + 7 + +& +! +D +  +] +? + 9 + + + +"-2 +8 +**IM.INDEX.GETFN ++6 + +0 +C +E ++Y +M +M ++: +; + + +  + + + ++ + + +"-J + % +* +%IM.INDEX.GETFN& ++ + +w +. + + + + + + +  +: + ++ +} + + + + + + +1 ++ +\ + 0 + % +  + + + + + ++ +  +  +M + +  + +-M + + +* &IM.INDEX.GETFN  ++ + + + + + + +' ++ +# + + + + +2 +" +. + +  +. HRULE.GETFNMODERN +- +* $IM.INDEX.GETFNMODERN + + ++- + + +O ++ +" +  +. + +* +%IM.INDEX.GETFNMODERN + + ++ +* + +  +# + + +7 + + + + +*#IM.INDEX.GETFNMODERN +  ++ + + +% . HRULE.GETFNMODERN +-W + +) +C +a + + + +x +V +)8 +/ + + + F + +N + [ + +  +  +) +  + + +"* &IM.INDEX.GETFN + + + + + + + +* (IM.INDEX.GETFN + + +& + +. + + + +* (IM.INDEX.GETFN  + + + + + & +B + + + + + + + +T +  +  +*)IM.INDEX.GETFN + + + + + + +  + +  + + + +  + +**IM.INDEX.GETFN + + + + + + + +& + +/ +" - +  + + +  +  +  + +  + + + + 5 + + +4 + + ? +  + + R +  +  + + | +L +G + + +* + +g + +K +? + + +  + + +  +  +  + +  + +| + + + +* + + +) + + + + + + + + + + z +r + & +  +b + +  +9 +2 +* + ++ +3 + + + + +* +++ + * + + + + +$ + + + ++ + & + +" + +0 + += + + + + +> +3 + +) +F + +7 +( + + +K +*#IM.INDEX.GETFNCLASSIC + + + + +3 + + + + + + + + + + +  + +B + +5 + + + + +, +  + + +  +  +  + , +v + + + + + +  + + +  +% + @ + + +  + / + + +  + +# + + +/ +  +O + + + + + + +  + +*#IM.INDEX.GETFNCLASSIC + +/ + + +*"IM.INDEX.GETFNCLASSIC + ++ + + + +*#IM.INDEX.GETFNCLASSIC +  +- + +. + + + + + + +% + + +) + +  +$ + + + + +v + +  + + +G + + +*"IM.INDEX.GETFNCLASSIC +  + + + + +  +a +D + +A +J + + + + + +9 +S +l +T +& + +* + +C + +*"IM.INDEX.GETFNCLASSIC +  + + + +! + + + + +   + + + + + += + +O +, + + + + +87$, +' +" + + +%, + + + +  +, +N +y +C +% +877 77$7 , +% += + +o +7 + += + +Y + + +b +, +Y +9 + +, +8 + + +; + + + + +  +, + + + + +1 +L +  +7 +h +I + + +! +L +R +g + + + +V + + + +V +  + +a +] + +  +c + +  +e + + W + J + ` +Y + +  + +( + +  +k + +  +  +8 + + + + + + +  +  +  +  +( + + + +3 + + + + +! + +) +; + + + ! + +' + +2 +  + +  + (IM.INDEX.GETFN + + +] + &IM.INDEX.GETFNMODERN +  + + +0 + + +, +E + +  + +A + + +  + + + + W + + +  +Y + + +   +  + +n +3 +O + +D +( + + + + +  +# + ( + %h +' +87779 +8 7 77 +: +` + + +j + + + +% +c +: +  + +^ + + + +P + + + +^ + + + + + + +l +H + +! + +D +% + + + +5 +X + z \ No newline at end of file diff --git a/docs/porter-irm/vol3/26-USERIO-PACKAGES.INTERPRESS b/docs/porter-irm/vol3/26-USERIO-PACKAGES.INTERPRESS new file mode 100644 index 00000000..a5a1e890 Binary files /dev/null and b/docs/porter-irm/vol3/26-USERIO-PACKAGES.INTERPRESS differ diff --git a/docs/porter-irm/vol3/26-USERIO-PACKAGES.TEDIT b/docs/porter-irm/vol3/26-USERIO-PACKAGES.TEDIT new file mode 100644 index 00000000..1efe85e4 Binary files /dev/null and b/docs/porter-irm/vol3/26-USERIO-PACKAGES.TEDIT differ diff --git a/docs/porter-irm/vol3/27-GRAPHICS.INTERPRESS b/docs/porter-irm/vol3/27-GRAPHICS.INTERPRESS new file mode 100644 index 00000000..17ad8ab8 Binary files /dev/null and b/docs/porter-irm/vol3/27-GRAPHICS.INTERPRESS differ diff --git a/docs/porter-irm/vol3/27-GRAPHICS.TEDIT b/docs/porter-irm/vol3/27-GRAPHICS.TEDIT new file mode 100644 index 00000000..d97f49fc --- /dev/null +++ b/docs/porter-irm/vol3/27-GRAPHICS.TEDIT @@ -0,0 +1,212 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 27. GRAPHICS OUTPUT OPERATIONS 1 27. GRAPHICS OUTPUT OPERATIONS 1 "27"27. GRAPHICS OUTPUT OPERATIONS 6 Streams are used as the basis for all I/O operations. Files are implemented as streams that can support character printing and reading operations, and file pointer manipulation. An image stream is a type of stream that also provides an interface for graphical operations. All of the operations that can applied to streams can be applied to image streams. For example, an image stream can be passed as the argument to PRINT, to print something on an image stream. In addition, special functions are provided to draw lines and curves and perform other graphical operations. Calling these functions on a stream that is not an image stream will generate an error. Primitive Graphics Concepts 1 The Interlisp-D graphics system is based on manipulating bitmaps (rectangular arrays of pixels), positions, regions, and textures. These objects are used by all of the graphics functions. Positions A position denotes a point in an X,Y coordinate system. A POSITION is an instance of a record with fields XCOORD and YCOORD and is manipulated with the standard record package facilities. For example, (create POSITION XCOORD 10 YCOORD 20) creates a position representing the point (10,20). (POSITIONP X) [Function] Returns X if X is a position; NIL otherwise. Regions A Region denotes a rectangular area in a coordinate system. Regions are characterized by the coordinates of their bottom left corner and their width and height. A REGION is a record with fields LEFT, BOTTOM, WIDTH, and HEIGHT. It can be manipulated with the standard record package facilities. There are access functions for the REGION record that return the TOP and RIGHT of the region. The following functions are provided for manipulating regions: (CREATEREGION LEFT BOTTOM WIDTH HEIGHT) [Function] Returns an instance of the REGION record which has LEFT, BOTTOM, WIDTH and HEIGHT as respectively its LEFT, BOTTOM, WIDTH, and HEIGHT fields. Example: (CREATEREGION 10 -20 100 200) will create a region that denotes a rectangle whose width is 100, whose height is 200, and whose lower left corner is at the position (10,-20). (REGIONP X) [Function] Returns X if X is a region, NIL otherwise. (INTERSECTREGIONS(INTERSECTREGIONS (Function) NIL NIL ("27") 1) REGION1 REGION2 ... REGIONn) [NoSpread Function] Returns a region which is the intersection of a number of regions. Returns NIL if the intersection is empty. (UNIONREGIONS(UNIONREGIONS (Function) NIL NIL ("27") 2) REGION1 REGION2 ... REGIONn) [NoSpread Function] Returns a region which is the union of a number of regions, i.e. the smallest region that contains all of them. Returns NIL if there are no regions given. (REGIONSINTERSECTP(REGIONSINTERSECTP (Function) NIL NIL ("27") 2) REGION1 REGION2) [Function] Returns T if REGION1 intersects REGION2. Returns NIL if they do not intersect. (SUBREGIONP(SUBREGIONP (Function) NIL NIL ("27") 2) LARGEREGION SMALLREGION) [Function] Returns T if SMALLREGION is a subregion (is equal to or entirely contained in) LARGEREGION; otherwise returns NIL. (EXTENDREGION(EXTENDREGION (Function) NIL NIL ("27") 2) REGION INCLUDEREGION) [Function] Changes (destructively modifies) the region REGION so that it includes the region INCLUDEREGION. It returns REGION. (MAKEWITHINREGION(MAKEWITHINREGION (Function) NIL NIL ("27") 2) REGION LIMITREGION) [Function] Changes (destructively modifies) the left and bottom of the region REGION so that it is within the region LIMITREGION, if possible. If the dimension of REGION are larger than LIMITREGION, REGION is moved to the lower left of LIMITREGION. If LIMITREGION is NIL, the value of the variable WHOLEDISPLAY (the screen region) is used. MAKEWITHINREGION returns the modified REGION. (INSIDEP(INSIDEP (Function) NIL NIL ("27") 2) REGION POSORX Y) [Function] If POSORX and Y are numbers, it returns T if the point (POSORX,Y) is inside of REGION. If POSORX is a POSITION, it returns T if POSORX is inside of REGION. If REGION is a WINDOW, the window's interior region in window coordinates is used. Otherwise, it returns NIL. Bitmaps The display primitives manipulate graphical images in the form of bitmaps. A bitmap is a rectangular array of "pixels," each of which is an integer representing the color of one point in the bitmap image. A bitmap is created with a specific number of bits allocated for each pixel. Most bitmaps used for the display screen use one bit per pixel, so that at most two colors can be represented. If a pixel is 0, the corresponding location on the image is white. If a pixel is 1, its location is black. This interpretation can be changed for the display screen with the function VIDEOCOLOR. Bitmaps with more than one bit per pixel are used to represent color or grey scale images. Bitmaps use a positive integer coordinate system with the lower left corner pixel at coordinate (0,0). Bitmaps are represented as instances of the datatype BITMAP. Bitmaps can be saved on files with the VARS file package command. (BITMAPCREATE WIDTH HEIGHT BITSPERPIXEL) [Function] Creates and returns a new bitmap which is WIDTH pixels wide by HEIGHT pixels high, with BITSPERPIXEL bits per pixel. If BITSPERPIXEL is NIL, it defaults to 1. (BITMAPP(BITMAPP (Function) NIL NIL ("27") 2) X) [Function] Returns X if X is a bitmap, NIL otherwise. (BITMAPWIDTH(BITMAPWIDTH (Function) NIL NIL ("27") 2) BITMAP) [Function] Returns the width of BITMAP in pixels. (BITMAPHEIGHT(BITMAPHEIGHT (Function) NIL NIL ("27") 3) BITMAP) [Function] Returns the height of BITMAP in pixels. (BITSPERPIXEL BITMAP) [Function] Returns the number of bits per pixel of BITMAP. (BITMAPBIT(BITMAPBIT (Function) NIL NIL ("27") 3) BITMAP X Y NEWVALUE) [Function] If NEWVALUE is between 0 and the maximum value for a pixel in BITMAP, the pixel (X,Y) is changed to NEWVALUE and the old value is returned. If NEWVALUE is NIL, BITMAP is not changed but the value of the pixel is returned. If NEWVALUE is anything else, an error is generated. If (X,Y) is outside the limits of BITMAP, 0 is returned and no pixels are changed. BITMAP can also be a window or display stream. Note: non-window image streams are "write-only"; the NEWVALUE argument must be non-NIL. (BITMAPCOPY(BITMAPCOPY (Function) NIL NIL ("27") 3) BITMAP) [Function] Returns a new bitmap which is a copy of BITMAP (same dimensions, bits per pixel, and contents). (EXPANDBITMAP(EXPANDBITMAP (Function) NIL NIL ("27") 3) BITMAP WIDTHFACTOR HEIGHTFACTOR) [Function] Returns a new bitmap that is WIDTHFACTOR times as wide as BITMAP and HEIGHTFACTOR times as high. Each pixel of BITMAP is copied into a WIDTHFACTOR times HEIGHTFACTOR block of pixels. If NIL, WIDTHFACTOR defaults to 4, HEIGHTFACTOR to 1. (SHRINKBITMAP(SHRINKBITMAP (Function) NIL NIL ("27") 3) BITMAP WIDTHFACTOR HEIGHTFACTOR DESTINATIONBITMAP) [Function] Returns a copy of BITMAP that has been shrunken by WIDTHFACTOR and HEIGHTFACTOR in the width and height, respectively. If NIL, WIDTHFACTOR defaults to 4, HEIGHTFACTOR to 1. If DESTINATIONBITMAP is not provided, a bitmap that is 1/WIDTHFACTOR by 1/HEIGHTFACTOR the size of BITMAP is created and returned. WIDTHFACTOR and HEIGHTFACTOR must be positive integers. (PRINTBITMAP(PRINTBITMAP (Function) NIL NIL ("27") 3) BITMAP FILE) [Function] Prints the bitmap BITMAP on the file FILE in a format that can be read back in by READBITMAP. (READBITMAP(READBITMAP (Function) NIL NIL ("27") 3) FILE) [Function] Creates a bitmap by reading an expression (written by PRINTBITMAP) from the file FILE. (EDITBM(EDITBM (Function) NIL NIL ("27") 3) BMSPEC) [Function] EDITBM provides an easy-to-use interactive editing facility for various types of bitmaps. If BMSPEC is a bitmap, it is edited. If BMSPEC is an atom whose value is a bitmap, its value is edited. If BMSPEC is NIL, EDITBM asks for dimensions and creates a bitmap. If BMSPEC is a region, that portion of the screen bitmap is used. If BMSPEC is a window, it is brought to the top and its contents edited. EDITBM sets up the bitmap being edited in an editing window. The editing window has two major areas: a gridded edit area in the lower part of the window and a display area in the upper left part. In the edit area, the left button will add points, the middle button will erase points. The right button provides access to the normal window commands to reposition and reshape the window. The actual size bitmap is shown in the display area. For example, the following is a picture of the bitmap editing window editing a eight-high by eighteen-wide bitmap: *~RsFuvmmqmmmv[bfwUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTxDUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTDxUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTHDUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTDUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPƪUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPƪ If the bitmap is too large to fit in the edit area, only a portion will be editable. This portion can be changed by scrolling both up and down in the left margin and left and right in the bottom margin. Pressing the middle button while in the display area will bring up a menu that allows global placement of the portion of the bitmap being edited. To allow more of the bitmap to be editing at once, the window can be reshaped to make it larger or the GridSize command described below can be used to reduce the size of a bit in the edit area. The bitmap editing window can be reshaped to provide more or less room for editing. When this happens, the space allocated to the editing area will be changed to fit in the new region. Whenever the left or middle button is down and the cursor is not in the edit area, the section of the display of the bitmap that is currently in the edit area is complemented. Pressing the left button while not in the edit region will put the lower left 16 x 16 section of the bitmap into the cursor for as long as the left button is held down. Pressing the middle button while not in either the edit area or the display area (i.e., while in the grey area in the upper right or in the title) will bring up a command menu. There are commands to stop editing, to restore the bitmap to its initial state and to clear the bitmap. Holding the middle button down over a command will result in an explanatory message being printed in the prompt window. The commands are described below: Paint Puts the current bitmap into a window and call the window PAINT command on it. The PAINT command implements drawing with various brush sizes and shapes but only on an actual sized bitmap. The PAINT mode is left by pressing the RIGHT button and selecting the QUIT command from the menu. At this point, you will be given a choice of whether or not the changes you made while in PAINT mode should be made to the current bitmap. ShowAsTile Tesselates the current bitmap in the upper part of the window. This is useful for determining how a bitmap will look if it were made the display background (using the function CHANGEBACKGROUND). Note: The tiled display will not automatically change as the bitmap changes; to update it, use the ShowAsTile command again. Grid,On/Off Turns the editing grid display on or off. GridSize Allows specification of the size of the editing grid. Another menu will appear giving a choice of several sizes. If one is selected, the editing portion of the bitmap editor will be redrawn using the selected grid size, allowing more or less of the bitmap to be edited without scrolling. The original size is chosen hueristically and is typically about 8. It is particularly useful when editing large bitmaps to set the edit grid size smaller than the original. Reset Sets all or part of the bitmap to the contents it had when EDITBM was called. Another menu will appear giving a choice between resetting the entire bitmap or just the portion that is in the edit area. The second menu also acts as a confirmation, since not selecting one of the choices on this menu results in no action being taken. Clear Sets all or part of the bitmap to 0. As with the Reset command, another menu gives a choice between clearing the entire bitmap or just the portion that is in the edit area. Cursor Sets the cursor to the lower left part of the bitmap. This prompts the user to specify the cursor "hot spot" by clicking in the lower left corner of the grid. OK Copies the changed image into the original bitmap, stops the bitmap editor and closes the edit windows. The changes the bitmap editor makes during the interaction occur on a copy of the original bitmap. Unless the bitmap editor is exited via OK, no changes are made in the original. Stop Stops the bitmap editor without making any changes to the original bitmap. Textures A Texture denotes a pattern of gray which can be used to (conceptually) tessellate the plane to form an infinite sheet of gray. It is currently either a 4 by 4 pattern or a 16 by N (N <= 16) pattern. Textures are created from bitmaps using the following function: (CREATETEXTUREFROMBITMAP(CREATETEXTUREFROMBITMAP (Function) NIL NIL ("27") 5) BITMAP) [Function] Returns a texture object that will produce the texture of BITMAP. If BITMAP is too large, its lower left portion is used. If BITMAP is too small, it is repeated to fill out the texture. (TEXTUREP(TEXTUREP (Function) NIL NIL ("27") 5) OBJECT) [Function] Returns OBJECT if it is a texture; NIL otherwise. The functions which accept textures (TEXTUREP, BITBLT, DSPTEXTURE, etc.) also accept bitmaps up to 16 bits wide by 16 bits high as textures. When a region is being filled with a bitmap texture, the texture is treated as if it were 16 bits wide (if less, the rest is filled with white space). The common textures white and black are available as system constants WHITESHADE and BLACKSHADE. The global variable GRAYSHADE is used by many system facilities as a background gray shade and can be set by the user. (EDITSHADE(EDITSHADE (Function) NIL NIL ("27") 5) SHADE) [Function] Opens a window that allows the user to edit textures. Textures can be either small (4 by 4) patterns or large (16 by 16). In the edit area, the left button adds bits to the shade and the middle button erases bits from the shade. The top part of the window is painted with the current texture whenever all mouse keys are released. Thus it is possible to directly compare two textures that differ by more than one pixel by holding a mouse key down until all changes are made. When the "quit" button is selected, the texture being edited is returned. If SHADE is a texture object, EDITSHADE starts with it. If SHADE is T, it starts with a large (16 by 16) white texture. Otherwise, it starts with WHITESHADE. The following is a picture of the texture editor, editing a large (16 by 16) pattern: ,,000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000``0``0``0`>`0`H`0`H`0`H`0`H`0`H`0`H`0``0``0``0``0``0``000000 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00UUUuU]UWUUUuU]UW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW0ꪺꪺ0 TUU@UTUU@W00 TUU@UTUU@W00 TUU@UTUU@W00 TUU@UTUU@W00UUU`WUUU`W00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW0ꪪꪪ0 TUPUTUPW0**0 TUPUTUPW0**0 TUPUTUPW0**0 TUPUTUPW0**0U@5UXWU@5UXW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW0ꪪꪪ0 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00UUUuUXWUUUuUXW0 0 0 0 0 0 0 0 0@ @ 0 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00UUUuUXWUUUuUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW0ꪪꪪ0 TUPUTUPW0**0 TUPUTUPW0**0 TUPUTUPW0**0 TUPUTUPW0**0U@5UPWU@5UXW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW0ꪪꪪ0 TUU@UTUU@W00 TUU@UTUU@W00 TUU@UTUU@W00 TUU@UTUU@W00TUU`WTUU`W00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW0ꪺꪺ0 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00UUUuU]UWTUUuU]UW0 0 0 0 0 0 0 0 0@ @ 000000 Opening Image Streams 1 An image stream is an output stream which "knows" how to process graphic commands to a graphics output device. Besides accepting the normal character-output functions (PRINT, etc.), an image stream can also be passed as an argument to functions to draw curves, to print characters in multiple fonts, and other graphics operations. Each image stream has an "image stream type," a litatom that specifies the type of graphic output device that the image stream is processing graphics commands for. Currently, the built-in image stream types are DISPLAY (for the display screen), INTERPRESS (for Interpress format printers), and PRESS (for Press format printers). There are also library packages available that define image stream types for the IRIS display, 4045 printer, FX-80 printer, C150 printer, etc. Image streams to the display (display streams) interpret graphics commands by immediately executing the appropriate operations to cause the desired image to appear on the display screen. Image streams for hardcopy devices such as Interpress printers interpret the graphic commands by saving information in a file, which can later be sent to the printer. Note: Not all graphics operations can be properly executed for all image stream types. For example, BITBLT may not be supported to all printers. This functionality is still being developed, but even in the long run some operations may be beyond the physical or logical capabilities of some devices or image file formats. In these cases, the stream will approximate the specified image as best it can. (OPENIMAGESTREAM(OPENIMAGESTREAM (Function) NIL NIL ("27") 7) FILE IMAGETYPE OPTIONS) [Function] Opens and returns an image stream of type IMAGETYPE on a destination specified by FILE. If FILE is a file name on a normal file storage device, the image stream will store graphics commands on the specified file, which can be transmitted to a printer by explicit calls to LISTFILES and SEND.FILE.TO.PRINTER. If IMAGETYPE is DISPLAY, then the user is prompted for a window to open. FILE in this case will be used as the title of the window. If FILE is a file name on the LPT device, this indicates that the graphics commands should be stored in a temporary file, and automatically sent to the printer when the image stream is closed by CLOSEF. FILE = NIL is equivalent to FILE = {LPT}. File names on the LPT device are of the form {LPT}PRINTERNAME.TYPE, where PRINTERNAME, TYPE, or both may be omitted. PRINTERNAME is the name of the particular printer to which the file will be transmitted on closing; it defaults to the first printer on DEFAULTPRINTINGHOST that can print IMAGETYPE files. The TYPE extension supplies the value of IMAGETYPE when it is defaulted (see below). OPENIMAGESTREAM will generate an error if the specified printer does not accept the kind of file specified by IMAGETYPE. If IMAGETYPE is NIL, the image type is inferred from the extension field of FILE and the EXTENSIONS properties in the list PRINTFILETYPES. Thus, the extensions IP, IPR, and INTERPRESS indicate Interpress format, and the extension PRESS indicates Press format. If FILE is a printer file with no extension (of the form {LPT}PRINTERNAME), then IMAGETYPE will be the type that the indicated printer can print. If FILE has no extension but is not on the printer device {LPT}, then IMAGETYPE will default to the type accepted by the first printer on DEFAULTPRINTINGHOST. OPTIONS is a list in property list format, (PROP1 VAL1 PROP2 VAL2 %), used to specify certain attributes of the image stream; not all attributes are meaningful or interpreted by all types of image streams. Acceptable properties are: REGION Value is the region on the page (in stream scale units, 0,0 being the lower-left corner of the page) that text will fill up. It establishes the initial values for DSPLEFTMARGIN, DSPRIGHTMARGIN, DSPBOTTOMMARGIN (the point at which carriage returns cause page advancement) and DSPTOPMARGIN (where the stream is positioned at the beginning of a new page). If this property is not given, the value of the variable DEFAULTPAGEREGION, is used. FONTS Value is a list of fonts that are expected to be used in the image stream. Some image streams (e.g. Interpress) are more efficient if the expected fonts are specified in advance, but this is not necessary. The first font in this list will be the initial font of the stream, otherwise the default font for that image stream type will be used. HEADING Value is the heading to be placed automatically on each page. NIL means no heading. Examples: Suppose that Tremor: is an Interpress printer, Quake is a Press printer, and DEFAULTPRINTINGHOST is (Tremor: Quake): (OPENIMAGESTREAM) returns an Interpress image stream on printer Tremor:. (OPENIMAGESTREAM NIL 'PRESS) returns a Press stream on Quake. (OPENIMAGESTREAM '{LPT}.INTERPRESS) returns an Interpress stream on Tremor:. (OPENIMAGESTREAM '{CORE}FOO.PRESS) returns a Press stream on the file {CORE}FOO.PRESS. (IMAGESTREAMP(IMAGESTREAMP (Function) NIL NIL ("27") 8) X IMAGETYPE) [NoSpread Function] Returns X (possibly coerced to a stream) if it is an output image stream of type IMAGETYPE (or of any type if IMAGETYPE=NIL), otherwise NIL. (IMAGESTREAMTYPE(IMAGESTREAMTYPE (Function) NIL NIL ("27") 8) STREAM) [Function] Returns the image stream type of STREAM. (IMAGESTREAMTYPEP(IMAGESTREAMTYPEP (Function) NIL NIL ("27") 8) STREAM TYPE) [Function] Returns T if STREAM is an image stream of type TYPE. Accessing Image Stream Fields 1 The following functions manipulate the fields of an image stream. These functions return the old value (the one being replaced). A value of NIL for the new value will return the current setting without changing it. These functions do not change any of the bits drawn on the image stream; they just affect future operations done on the image stream. (DSPCLIPPINGREGION(DSPCLIPPINGREGION (Function) NIL NIL ("27") 8) REGION STREAM) [Function] The clipping region is a region that limits the extent of characters printed and lines drawn (in the image stream's coordinate system). Initially set so that no clipping occurs. Warning: For display streams, the window system maintains the clipping region during window operations. Users should be very careful about changing this field. (DSPFONT(DSPFONT (Function) NIL NIL ("27") 8) FONT STREAM) [Function] The font field specifies the font used when printing characters to the image stream. Note: DSPFONT determines its new font descriptor from FONT by the same coercion rules that FONTPROP and FONTCREATE use , with one additional possibility: If FONT is a list of the form (PROP1 VAL1 PROP2 VAL2 ...) where PROP1 is acceptable as a font-property to FONTCOPY, then the new font is obtained by (FONTCOPY (DSPFONT NIL STREAM) PROP1 VAL1 PROP2 VAL2 ...). For example, (DSPFONT '(SIZE 12) STREAM) would change the font to the 12 point version of the current font, leaving all other font properties the same. (DSPTOPMARGIN(DSPTOPMARGIN (Function) NIL NIL ("27") 9) YPOSITION STREAM) [Function] The top margin is an integer that is the Y position after a new page (in the image stream's coordinate system). This function has no effect on windows. (DSPBOTTOMMARGIN(DSPBOTTOMMARGIN (Function) NIL NIL ("27") 9) YPOSITION STREAM) [Function] The bottom margin is an integer that is the minimum Y position that characters will be printed by PRIN1 (in the image stream's coordinate system). This function has no effect on windows. (DSPLEFTMARGIN(DSPLEFTMARGIN (Function) NIL NIL ("27") 9) XPOSITION STREAM) [Function] The left margin is an integer that is the X position after an end-of-line (in the image stream's coordinate system). Initially the left edge of the clipping region. (DSPRIGHTMARGIN(DSPRIGHTMARGIN (Function) NIL NIL ("27") 9) XPOSITION STREAM) [Function] The right margin is an integer that is the maximum X position that characters will be printed by PRIN1 (in the image stream's coordinate system). This is initially the position of the right edge of the window or page. The line length of a window or image stream (as returned by LINELENGTH) is computed by dividing the distance between the left and right margins by the width of an uppercase "A" in the current font. The line length is changed whenever the font, left margin, or right margin are changed or whenever the window is reshaped. (DSPOPERATION(DSPOPERATION (Function) NIL NIL ("27") 9) OPERATION STREAM) [Function] The operation is the default BITBLT operation used when printing or drawing on the image stream. One of REPLACE, PAINT, INVERT, or ERASE. Initially REPLACE. This is a meaningless operation for most printers which support the model that once dots are deposited on a page they cannot be removed. (DSPLINEFEED(DSPLINEFEED (Function) NIL NIL ("27") 9) DELTAY STREAM) [Function] The linefeed is an integer that specifies the Y increment for each linefeed, normally negative. Initially minus the height of the initial font. (DSPSCALE(DSPSCALE (Function) NIL NIL ("27") 9) SCALE STREAM) [Function] Returns the scale of the image stream STREAM, a number indicating how many units in the streams coordinate system correspond to one printer's point (1/72 of an inch). For example, DSPSCALE returns 1 for display streams, and 35.27778 for Interpress and Press streams (the number of micas per printer's point). In order to be device-independent, user graphics programs must either not specify position values absolutely, or must multiply absolute point quantities by the DSPSCALE of the destination stream. For example, to set the left margin of the Interpress stream XX to one inch, do (DSPLEFTMARGIN (TIMES 72 (DSPSCALE NIL XX)) XX) The SCALE argument to DSPSCALE is currently ignored. In a future release it will enable the scale of the stream to be changed under user control, so that the necessary multiplication will be done internal to the image stream interface. In this case, it would be possible to set the left margin of the Interpress stream XX to one inch by doing (DSPSCALE 1 XX) (DSPLEFTMARGIN 72 XX) (DSPSPACEFACTOR FACTOR STREAM) [Function] The space factor is the amount by which to multiply the natural width of all following space characters on STREAM; this can be used for the justification of text. The default value is 1. For example, if the natural width of a space in STREAM's current font is 12 units, and the space factor is set to two, spaces appear 24 units wide. The values returned by STRINGWIDTH and CHARWIDTH are also affected. The following two functions only have meaning for image streams that can display color: (DSPCOLOR COLOR STREAM) [Function] Sets the default foreground color of STREAM. Returns the previous foreground color. If COLOR is NIL, it returns the current foreground color without changing anything. The default color is white (DSPBACKCOLOR COLOR STREAM) [Function] Sets the background color of STREAM. Returns the previous background color. If COLOR is NIL, it returns the current background color without changing anything. The default background color is black. Current Position of an Image Stream 1 Each image stream has a "current position," which is a position (in the image stream's coordinate system) where the next printing operation will start from. The functions which print characters or draw on an image stream update these values appropriately. The following functions are used to explicitly access the current position of an image stream: (DSPXPOSITION XPOSITION STREAM) [Function] Returns the X coordinate of the current position of STREAM. If XPOSITION is non-NIL, the X coordinate is set to it (without changing the Y coordinate). (DSPYPOSITION YPOSITION STREAM) [Function] Returns the Y coordinate of the current position of STREAM. If YPOSITION is non-NIL, the Y coordinate is set to it (without changing the X coordinate). (MOVETO X Y STREAM) [Function] Changes the current position of STREAM to the point (X,Y). (RELMOVETO DX DY STREAM) [Function] Changes the current position to the point (DX,DY) coordinates away from current position of STREAM. (MOVETOUPPERLEFT STREAM REGION) [Function] Moves the current position to the beginning position of the top line of text. If REGION is non-NIL, it must be a REGION and the X position is changed to the left edge of REGION and the Y position changed to the top of REGION less the font ascent of STREAM. If REGION is NIL, the X coordinate is changed to the left margin of STREAM and the Y coordinate is changed to the top of the clipping region of STREAM less the font ascent of STREAM. Moving Bits Between Bitmaps With BITBLT 1 BITBLT is the primitive function for moving bits from one bitmap to another, or from a bitmap to an image stream. (BITBLT SOURCE SOURCELEFT SOURCEBOTTOM DESTINATION DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT SOURCETYPE OPERATION TEXTURE CLIPPINGREGION) [Function] Transfers a rectangular array of bits from SOURCE to DESTINATION. SOURCE can be a bitmap, or a display stream or window, in which case its associated bitmap is used. DESTINATION can be a bitmap or an arbitrary image stream. WIDTH and HEIGHT define a pair of rectangles, one in each of the SOURCE and DESTINATION whose left, bottom corners are at, respectively, (SOURCELEFT, SOURCEBOTTOM) and (DESTINATIONLEFT, DESTINATIONBOTTOM). If these rectangles overlap the boundaries of either source or destination they are both reduced in size (without translation) so that they fit within their respective boundaries. If CLIPPINGREGION is non-NIL it should be a REGION and is interpreted as a clipping region within DESTINATION; clipping to this region may further reduce the defining rectangles. These (possibly reduced) rectangles define the source and destination rectangles for BITBLT. The mode of transferring bits is defined by SOURCETYPE and OPERATION. SOURCETYPE and OPERATION specify whether the source bits should come from SOURCE or TEXTURE, and how these bits are combined with those of DESTINATION. SOURCETYPE and OPERATION are described further below. TEXTURE is a texture. BITBLT aligns the texture so that the upper-left pixel of the texture coincides with the upper-left pixel of the destination bitmap. SOURCELEFT, SOURCEBOTTOM, DESTINATIONLEFT, and DESTINATIONBOTTOM default to 0. WIDTH and HEIGHT default to the width and height of the SOURCE. TEXTURE defaults to white. SOURCETYPE defaults to INPUT. OPERATION defaults to REPLACE. If CLIPPINGREGION is not provided, no additional clipping is done. BITBLT returns T if any bits were moved; NIL otherwise. Note: If SOURCE or DESTINATION is a window or image stream, the remaining arguments are interpreted as values in the coordinate system of the window or image stream and the operation of BITBLT is translated and clipped accordingly. Also, if a window or image stream is used as the destination to BITBLT, its clipping region further limits the region involved. SOURCETYPE specifies whether the source bits should come from the bitmap SOURCE, or from the texture TEXTURE. SOURCETYPE is interpreted as follows: INPUT The source bits come from SOURCE. TEXTURE is ignored. INVERT The source bits are the inverse of the bits from SOURCE. TEXTURE is ignored. TEXTURE The source bits come from TEXTURE. SOURCE, SOURCELEFT, and SOURCEBOTTOM are ignored. OPERATION specifies how the source bits (as specified by SOURCETYPE) are combined with the bits in DESTINATION and stored back into DESTINATION. DESTINATION is one of the following: REPLACE All source bits (on or off) replace destination bits. PAINT Any source bits that are on replace the corresponding destination bits. Source bits that are off have no effect. Does a logical OR between the source bits and the destination bits. INVERT Any source bits that are on invert the corresponding destination bits. Does a logical XOR between the source bits and the destination bits. ERASE Any source bits that are on erase the corresponding destination bits. Does a logical AND operation between the inverse of the source bits and the destination bits. Different combinations of SOURCETYPE and OPERATION can be specified to achieve many different effects. Given the following bitmaps as the values of SOURCE, TEXTURE, and DESTINATION: <F??~~??<Ǿal0ql09>l0 l0' ;FDDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" 3 f3f3f3f3 f3< PZxbgl6gllfÃ7glnf7mlnf6lmlc6lmx6xg BITBLT would produce the results given below for the difference combinations of SOURCETYPE and OPERATION (assuming CLIPPINGREGION, SOURCELEFT, etc. are set correctly, of course): Pf???~~~~~~?~~?~~?~~??????????????????????????????7`6ٌ8ٌflٌg|wc`v3<6 y mq9mqmyهmmmm9e Pf~~~?~?~?~??????????????????????????????7`6ٌ8ٌflٌg|wc`v3<6 xCflf09lv0mmv0myn0mcn09cf0 Pf~~~?~?~?~???????????????????????????????7`6ٌ8ٌflٌg|wc`v3<6 ll9n0mn>mm0mm09l> Pf?7`6ٌ8ٌflٌg|wc`v3<6 }a09a0m}>maðma9}> Pf??mm0mm09l> Pf???~~~~~~maðma9}> PfDDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#}|`a`8``fl|`|g|``c`a`3<}Ǚ|y mq9mqmyهmmmm9e PfDDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""}|`a`8``fl|`|g|``c`a`3<}Ǚ|xCflf09lv0mmv0myn0mcn09cf0 PfDDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""}|`a`8``fl|`|g|``c`a`3<}Ǚ|ll9n0mn>mm0mm09l> Pfٙٙٙٙٙٙٙٙ}|`a`8``fl|`|g|``c`a`3<}Ǚ|}a09a0m}>maðma9}> (BLTSHADE TEXTURE DESTINATION DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT OPERATION CLIPPINGREGION) [Function] BLTSHADE is the SOURCETYPE = TEXTURE case of BITBLT. It fills the specified region of the destination bitmap DESTINATION with the texture TEXTURE. DESTINATION can be a bitmap or image stream. (BITMAPIMAGESIZE BITMAP DIMENSION STREAM) [Function] Returns the size that BITMAP will be when BITBLTed to STREAM, in STREAM's units. DIMENSION can be one of WIDTH, HEIGHT, or NIL, in which case the dotted pair (WIDTH . HEIGHT) will be returned. Drawing Lines 1 Interlisp-D provides several functions for drawing lines and curves on image streams. The line drawing functions are intended for interactive applications where efficiency is important. They do not allow the use of "brush" patterns, like the curve drawing functions, but (for display streams) they support drawing a line in INVERT mode, so redrawing the line will erase it. DRAWCURVE can be used to draw lines using a brush. (DRAWLINE X1 Y1 X2 Y2 WIDTH OPERATION STREAM COLOR DASHING) [Function] Draws a straight line from the point (X1,Y1) to the point (X2,Y2) on the image stream STREAM. The position of STREAM is set to (X2,Y2). If X1 equals X2 and Y1 equals Y2, a point is drawn at (X1,Y1). WIDTH is the width of the line, in the units of the device. If WIDTH is NIL, the default is 1. OPERATION is the BITBLT operation used to draw the line. If OPERATION is NIL, the value of DSPOPERATION for the image stream is used. COLOR is a color specification that determines the color used to draw the line for image streams that support color. If COLOR is NIL, the DSPCOLOR of STREAM is used. DASHING is a list of positive integers that determines the dashing characteristics of the line. The line is drawn for the number of points indicated by the first element of the dashing list, is not drawn for the number of points indicated by the second element. The third element indicates how long it will be on again, and so forth. The dashing sequence is repeated from the beginning when the list is exhausted. If DASHING is NIL, the line is not dashed. (DRAWBETWEEN POSITION1 POSITION2 WIDTH OPERATION STREAM COLOR DASHING) [Function] Draws a line from the point POSITION1 to the point POSITION2 onto the destination bitmap of STREAM. The position of STREAM is set to POSITION2. (DRAWTO X Y WIDTH OPERATION STREAM COLOR DASHING) [Function] Draws a line from the current position to the point (X,Y) onto the destination bitmap of STREAM. The position of STREAM is set to (X,Y). (RELDRAWTO DX DY WIDTH OPERATION STREAM COLOR DASHING) [Function] Draws a line from the current position to the point (DX,DY) coordinates away onto the destination bitmap of STREAM. The position of STREAM is set to the end of the line. If DX and DY are both 0, nothing is drawn. Drawing Curves 1 A curve is drawn by placing a brush pattern centered at each point along the curve's trajectory. A brush pattern is defined by its shape, size, and color. The predefined brush shapes are ROUND, SQUARE, HORIZONTAL, VERTICAL, and DIAGONAL; new brush shapes can be created using the INSTALLBRUSH function, described below. A brush size is an integer specifying the width of the brush in the units of the device. The color is a color specification, which is only used if the curve is drawn to an image stream that supports colors. A brush is specified to the various drawing functions as a list of the form (SHAPE WIDTH COLOR), for example (SQUARE 2) or (VERTICAL 4 RED). A brush can also be specified as a positive integer, which is interpreted as a ROUND brush of that width. If a brush is a litatom, it is assumed to be a function which is called at each point of the curve's trajectory (with three arguments: the X-coordinate of the point, the Y-coordinate, and the image stream), and should do whatever image stream operations are necessary to draw each point. Finally, if a brush is specified as NIL, a (ROUND 1) brush is used as default. The appearance of a curve is also determined by its dashing characteristics. Dashing is specified by a list of positive integers. If a curve is dashed, the brush is placed along the trajectory for the number of units indicated by the first element of the dashing list. The brush is off, not placed in the bitmap, for a number of units indicated by the second element. The third element indicates how long it will be on again, and so forth. The dashing sequence is repeated from the beginning when the list is exhausted. The units used to measure dashing are the units of the brush. For example, specifying the dashing as (1 1) with a brush of (ROUND 16) would put the brush on the trajectory, skip 16 points, and put down another brush. A curve is not dashed if the dashing argument to the drawing function is NIL. The curve functions use the image stream's clipping region and operation. Most types of image streams only support the PAINT operation when drawing curves. When drawing to a display stream, the curve-drawing functions accept the operation INVERT if the brush argument is 1. For brushes larger than 1, these functions will use the ERASE operation instead of INVERT. For display streams, the curve-drawing functions treat the REPLACE operation the same as PAINT. (DRAWCURVE KNOTS CLOSED BRUSH DASHING STREAM) [Function] Draws a "parametric cubic spline curve" on the image stream STREAM. KNOTS is a list of positions to which the curve will be fitted. If CLOSED is non-NIL, the curve will be closed; otherwise it ends at the first and last positions in KNOTS. BRUSH and DASHING are interpreted as described above. For example, (DRAWCURVE '((10 . 10)(50 . 50)(100 . 10)(150 . 50)) NIL '(ROUND 5) '(1 1 1 2) XX) would draw a curve like the following on the display stream XX: ;  > > >                 p p p p       > > > q  p   (DRAWCIRCLE CENTERX CENTERY RADIUS BRUSH DASHING STREAM) [Function] Draws a circle of radius RADIUS about the point (CENTERX,CENTERY) onto the image stream STREAM. STREAM's position is left at (CENTERX,CENTERY). The other arguments are interpreted as described above. (DRAWELLIPSE CENTERX CENTERY SEMIMINORRADIUS SEMIMAJORRADIUS ORIENTATION BRUSH DASHING STREAM) [Function] Draws an ellipse with a minor radius of SEMIMINORRADIUS and a major radius of SEMIMAJORRADIUS about the point (CENTERX,CENTERY) onto the image stream STREAM. ORIENTATION is the angle of the major axis in degrees, positive in the counterclockwise direction. STREAM's position is left at (CENTERX,CENTERY). The other arguments are interpreted as described above. New brush shapes can be defined using the following function: (INSTALLBRUSH BRUSHNAME BRUSHFN BRUSHARRAY) [Function] Installs a new brush called BRUSHNAME with creation-function BRUSHFN and optional array BRUSHARRAY. BRUSHFN should be a function of one argument (a width), which returns a bitmap of the brush for that width. BRUSHFN will be called to create new instances of BRUSHNAME-type brushes; the sixteen smallest instances will be pre-computed and cached. "Hand-crafted" brushes can be supplied as the BRUSHARRAY argument. Changing an existing brush can be done by calling INSTALLBRUSH with new BRUSHFN and/or BRUSHARRAY. (DRAWPOINT X Y BRUSH STREAM OPERATION) [Function] Draws BRUSH centered around point (X, Y) on STREAM, using the operation OPERATION. BRUSH may be a bitmap or a brush. Miscellaneous Drawing and Printing Operations 1 (DSPFILL REGION TEXTURE OPERATION STREAM) [Function] Fills REGION of the image stream STREAM (within the clipping region) with the texture TEXTURE. If REGION is NIL, the whole clipping region of STREAM is used. If TEXTURE or OPERATION is NIL, the values for STREAM are used. (FILLPOLYGON POINTS TEXTURE STREAM) [Function] Fills in the polygon outlined by POINTS on the image stream STREAM, using the texture TEXTURE. POINTS is a list of positions determining the vertices of a closed polygon. FILLPOLYGON fills in this polygon with the texture TEXTURE. POINTS can also be a list whose elements are lists of positions, in which case each sublist describes a separate polygon to be filled. Note: When filling a polygon, there is more than one way of dealing with the situation where two polygon sides intersect, or one polygon is fully inside the other. Currently, FILLPOLYGON to a display stream uses the "odd" fill rule, which means that intersecting polygon sides define areas that are filled or not filled somewhat like a checkerboard. For example, (FILLPOLYGON '((125 . 125)(150 . 200)(175 . 125)(125 . 175)(175 . 175)) GRAYSHADE WINDOW) would produce a display something like this: /E@@@ PP**PUT**UTUT*UTUTTUP * *P@P @((*PUT** UPTUPT*UTUUUPU  U@UUU@TU@P@ @PP**PP  @ This fill convention also takes into account all polygons in POINTS, if it specifies multiple polygons. This can be used to put "holes" in filled polygons. For example, (FILLPOLYGON '( ((110 . 110)(150 . 200)(190 . 110)) ((135 . 125)(160 . 125)(160 . 150)(135 . 150)) ) GRAYSHADE WINDOW) will put a square hole in a triangular region: OX@@ PP**UTUTUTUUUUUU@ UU@UU@ UUPUUP**UUUTUUUTUUUUUUUUUUUU@UUUU@ UUUUPUUUUP***UTUT**UTUU**UUUU@* *UU@UU@ * *UUPUUP****UUUTUUUT**UUUUUUUUUUUUUUUUUUUU@UUUUUUUU@ UUUUUUUUPUUUUUUUUP**UUUUUUUUUTUUUUUUUUUT Currently, FILLPOLYGON uses the "Replace" BITBLT operation to fill areas with the texture. However, any areas that are not filled are not changed. If there are "holes" in the filled polygon, this can be used to produce a "window" effect. For example, the following is the display produced by filling the star polygon (above) over a window full of text: `P @ 'D!B"(@E!>@! @!B@*(HE !D0* @ '*D!B*(@E!U@!U@!B@HE !0U@U@U@ U@@ '!B@E!UT>UP!UD UP!B"*E FU@UU@U* P P 'D!B"(E!>@! @!B@"(E !D@@P@  U@ UU@ '!BE!U]UP!UUUP!BJE !UUTUUT ** WT TT 'F!B*"(E!U>U!U U!B"(jE !D*P@P@C @@ #D !B"(@E!>@! @!B@"(HE !D0 (FILLCIRCLE CENTERX CENTERY RADIUS TEXTURE STREAM) [Function] Fills in a circular area of radius RADIUS about the point (CENTERX,CENTERY) in STREAM with TEXTURE. STREAM's position is left at (CENTERX,CENTERY). (DSPRESET STREAM) [Function] Sets the X coordinate of STREAM to its left margin, sets its Y coordinate to the top of the clipping region minus the font ascent. For a display stream, this also fills its destination bitmap with its background texture. (DSPNEWPAGE STREAM) [Function] Starts a new page. The X coordinate is set to the left margin, and the Y coordinate is set to the top margin plus the linefeed. (CENTERPRINTINREGION EXP REGION STREAM) [Function] Prints EXP so that is it centered within REGION of the STREAM. If REGION is NIL, EXP will be centered in the clipping region of STREAM. Drawing and Shading Grids 1 A grid is a partitioning of an arbitrary coordinate system (hereafter referred to as the "source system") into rectangles. This section describes functions that operate on grids. It includes functions to draw the outline of a grid, to translate between positions in a source system and grid coordinates (the coordinates of the rectangle which contains a given position), and to shade grid rectangles. A grid is defined by its "unit grid," a region (called a grid specification) which is the origin rectangle of the grid in terms of the source system. Its LEFT field is interpreted as the X-coordinate of the left edge of the origin rectangle, its BOTTOM field is the Y-coordinate of the bottom edge of the origin rectangle, its WIDTH is the width of the grid rectangles, and its HEIGHT is the height of the grid rectangles. (GRID GRIDSPEC WIDTH HEIGHT BORDER STREAM GRIDSHADE) [Function] Outlines the grid defined by GRIDSPEC which is WIDTH rectangles wide and HEIGHT rectangles high on STREAM. Each box in the grid has a border within it that is BORDER points on each side; so the resulting lines in the grid are 2*BORDER thick. If BORDER is the atom POINT, instead of a border the lower left point of each grid rectangle will be turned on. If GRIDSHADE is non-NIL, it should be a texture and the border lines will be drawn using that texture. (SHADEGRIDBOX X Y SHADE OPERATION GRIDSPEC GRIDBORDER STREAM) [Function] Shades the grid rectangle (X,Y) of GRIDSPEC with texture SHADE using OPERATION on STREAM. GRIDBORDER is interpreted the same as for GRID. The following two functions map from the X,Y coordinates of the source system into the grid X,Y coordinates: (GRIDXCOORD XCOORD GRIDSPEC) [Function] Returns the grid X-coordinate (in the grid specified by GRIDSPEC) that contains the source system X-coordinate XCOORD. (GRIDYCOORD YCOORD GRIDSPEC) [Function] Returns the grid Y-coordinate (in the grid specified by GRIDSPEC) that contains the source system Y-coordinate YCOORD. The following two functions map from the grid X,Y coordinates into the X,Y coordinates of the source system: (LEFTOFGRIDCOORD GRIDX GRIDSPEC) [Function] Returns the source system X-coordinate of the left edge of a grid rectangle at grid X-coordinate GRIDX (in the grid specified by GRIDSPEC). (BOTTOMOFGRIDCOORD GRIDY GRIDSPEC) [Function] Returns the source system Y-coordinate of the bottom edge of a grid rectangle at grid Y-coordinate GRIDY (in the grid specified by GRIDSPEC). Display Streams 1 Display streams (image streams of type DISPLAY) are used to control graphic output operations to a bitmap, known as the "destination" bitmap of the display stream. For each window on the screen, there is an associated display stream which controls graphics operations to a specific part of the screen bitmap. Any of the functions that take a display stream will also take a window, and use the associated display stream. Display streams can also have a destination bitmap that is not connected to any window or display device. (DSPCREATE DESTINATION) [Function] Creates and returns a display stream. If DESTINATION is specified, it is used as the destination bitmap, otherwise the screen bitmap is used. (DSPDESTINATION DESTINATION DISPLAYSTREAM) [Function] Returns the current destination bitmap for DISPLAYSTREAM, setting it to DESTINATION if non-NIL. DESTINATION can be either the screen bitmap, or an auxilliary bitmap in order to construct figures, possibly save them, and then display them in a single operation. Warning: The window system maintains the destination of a window's display stream. Users should be very careful about changing this field. (DSPXOFFSET XOFFSET DISPLAYSTREAM) [Function] (DSPYOFFSET YOFFSET DISPLAYSTREAM) [Function] Each display stream has its own coordinate system, separate from the coordinate system of its destination bitmap. Having the coordinate system local to the display stream allows objects to be displayed at different places by translating the display stream's coordinate system relative to its destination bitmap. This local coordinate system is defined by the X offset and Y offset. DSPXOFFSET returns the current X offset for DISPLAYSTREAM, the X origin of the display stream's coordinate system in the destination bitmap's coordinate system. It is set to XOFFSET if non-NIL. DSPYOFFSET returns the current Y offset for DISPLAYSTREAM, the Y origin of the display stream's coordinate system in the destination bitmap's coordinate system. It is set to YOFFSET if non-NIL. The X offset and Y offset for a display stream are both initially 0 (no X or Y-coordinate translation). Warning: The window system maintains the X and Y offset of a window's display stream. Users should be very careful about changing these fields. (DSPTEXTURE TEXTURE DISPLAYSTREAM) [Function] Returns the current texture used as the background pattern for DISPLAYSTREAM. It is set to TEXTURE if non-NIL. Initially the value of WHITESHADE. (DSPSOURCETYPE SOURCETYPE DISPLAYSTREAM) [Function] Returns the current BITBLT sourcetype used when printing characters to the display stream. It is set to SOURCETYPE, if non-NIL. Must be either INPUT or INVERT. Initially INPUT. (DSPSCROLL SWITCHSETTING DISPLAYSTREAM) [Function] Returns the current value of the "scroll flag," a flag that determines the scrolling behavior of the display stream; either ON or OFF. If ON, the bits in the display streams's destination bitmap are moved after any linefeed that moves the current position out of the destination bitmap. Any bits moved out of the current clipping region are lost. Does not adjust the X offset, Y offset, or clipping region of the display stream. Initially OFF. Sets the scroll flag to SWITCHSETTING, if non-NIL. Note: The word "scrolling" also describes the use of "scroll bars" on the left and bottom of a window to move an object displayed in a window. Each window has an associated display stream. To get the window of a particular display stream, use WFROMDS: (WFROMDS DISPLAYSTREAM DONTCREATE) [Function] Returns the window associated with DISPLAYSTREAM, creating a window if one does not exist (and DONTCREATE is NIL). Returns NIL if the destination of DISPLAYSTREAM is not a screen bitmap that supports a window system. If DONTCREATE is non-NIL, WFROMDS will never create a window, and returns NIL if DISPLAYSTREAM does not have an associated window. TTYDISPLAYSTREAM calls WFROMDS with DONTCREATE = T, so it will not create a window unnecessarily. Also, if WFROMDS does create a window, it calls CREATEW with NOOPENFLG = T. (DSPBACKUP WIDTH DISPLAYSTREAM) [Function] Backs up DISPLAYSTREAM over a character which is WIDTH screen points wide. DSPBACKUP fills the backed over area with the display stream's background texture and decreases the X position by WIDTH. If this would put the X position less than DISPLAYSTREAM's left margin, its operation is stopped at the left margin. It returns T if any bits were written, NIL otherwise. Fonts 1 A font is the collection of images that are printed or displayed when characters are output to a graphic output device. Some simple displays and printers can only print characters using one font. Bitmap displays and graphic printers can print characters using a large number of fonts. Fonts are identified by a distinctive style or family (such as Modern or Classic), a size (such as 10 points), and a face (such as bold or italic). Fonts also have a rotation that indicates the orientation of characters on the screen or page. A normal horizontal font (also called a portrait font) has a rotation of 0; the rotation of a vertical (landscape) font is 90 degrees. While any combination can be specified, in practice the user will find that only certain combinations of families, sizes, faces, and rotations are available for any graphic output device. To specify a font to the functions described below, a FAMILY is represented by a literal atom, a SIZE by a positive integer, and a FACE by a three-element list of the form (WEIGHT SLOPE EXPANSION). WEIGHT, which indicates the thickness of the characters, can be BOLD, MEDIUM, or LIGHT; SLOPE can be ITALIC or REGULAR; and EXPANSION can be REGULAR, COMPRESSED, or EXPANDED, indicating how spread out the characters are. For convenience, faces may also be specified by three-character atoms, where each character is the first letter of the corresponding field. Thus, MRR is a synonym for (MEDIUM REGULAR REGULAR). In addition, certain common face combinations may be indicated by special literal atoms: STANDARD = (MEDIUM REGULAR REGULAR) = MRR ITALIC = (MEDIUM ITALIC REGULAR) = MIR BOLD = (BOLD REGULAR REGULAR) = BRR BOLDITALIC = (BOLD ITALIC REGULAR) = BIR Interlisp represents all the information related to a font in an object called a font descriptor. Font descriptors contain the family, size, etc. properties used to represent the font. In addition, for each character in the font, the font descriptor contains width information for the character and (for display fonts) a bitmap containing the picture of the character. The font functions can take fonts specified in a variety of different ways. DSPFONT, FONTCREATE, FONTCOPY, etc. can be applied to font descriptors, "font lists" such as '(MODERN 10), image streams (coerced to its current font), or windows (coerced to the current font of its display stream). The printout command ".FONT" will also accept fonts specified in any of these forms. (FONTCREATE FAMILY SIZE FACE ROTATION DEVICE NOERRORFLG CHARSET) [Function] Returns a font descriptor for the specified font. FAMILY is a litatom specifying the font family. SIZE is an integer indicating the size of the font in points. FACE specifies the face characteristics in one of the formats listed above; if FACE is NIL, STANDARD is used. ROTATION, which specifies the orientation of the font, is 0 (or NIL) for a portrait font and 90 for a landscape font. DEVICE indicates the output device for the font, and can be any image stream type , such as DISPLAY, INTERPRESS, etc. DEVICE may also be an image stream, in which case the type of the stream determines the font device. DEVICE defaults to DISPLAY. The FAMILY argument to FONTCREATE may also be a list, in which case it is interpreted as a font-specification quintuple, a list of the form (FAMILY SIZE FACE ROTATION DEVICE). Thus, (FONTCREATE '(GACHA 10 BOLD)) is equivalent to (FONTCREATE 'GACHA 10 'BOLD). FAMILY may also be a font descriptor, in which case that descriptor is simply returned. If a font descriptor has already been created for the specified font, FONTCREATE simply returns it. If it has not been created, FONTCREATE has to read the font information from a font file that contains the information for that font. The name of an appropriate font file, and the algorithm for searching depends on the device that the font is for, and is described in more detail below. If an appropriate font file is found, it is read into a font descriptor. If no file is found, for DISPLAY fonts FONTCREATE looks for fonts with less face information and fakes the remaining faces (such as by doubling the bit pattern of each character or slanting it). For hardcopy printer fonts, there is no acceptable faking algorithm. If no acceptable font is found, the action of FONTCREATE is determined by NOERRORFLG. If NOERRORFLG is NIL, it generates a FONT NOT FOUND error with the offending font specification; otherwise, FONTCREATE returns NIL. CHARSET is the character set which will be read to create the font. Defaults to 0. For more information on character sets, see NS Characters. (FONTP X) [Function] Returns X if X is a font descriptor; NIL otherwise. (FONTPROP FONT PROP) [Function] Returns the value of the PROP property of font FONT. The following font properties are recognized: FAMILY The style of the font, represented as a literal atom, such as CLASSIC or MODERN. SIZE A positive integer giving the size of the font, in printer's points (1/72 of an inch). WEIGHT The thickness of the characters; one of BOLD, MEDIUM, or LIGHT. SLOPE The "slope" of the characters in the font; one of ITALIC or REGULAR. EXPANSION The extent to which the characters in the font are spread out; one of REGULAR, COMPRESSED, or EXPANDED. Most available fonts have EXPANSION=REGULAR. FACE A three-element list of the form (WEIGHT SLOPE EXPANSION), giving all of the typeface parameters. ROTATION An integer that gives the orientation of the font characters on the screen or page, in degrees. A normal horizontal font (also called a portrait font) has a rotation of 0; the rotation of a vertical (landscape) font is 90. DEVICE The device that the font can be printed on; one of DISPLAY, INTERPRESS, etc. ASCENT An integer giving the maximum height of any character in the font from its base line (the printing position). The top line will be at BASELINE+ASCENT-1. DESCENT An integer giving the maximum extent of any character below the base line, such as the lower part of a "p". The bottom line of a character will be at BASELINE-DESCENT. HEIGHT Equal to ASCENT + DESCENT. SPEC The (FAMILY SIZE FACE ROTATION DEVICE) quintuple by which the font is known to Lisp. DEVICESPEC The (FAMILY SIZE FACE ROTATION DEVICE) quintuple that identifies what will be used to represent the font on the display or printer. It will differ from the SPEC property only if an implicit coercion is done to approximate the specified font with one that actually exists on the device. SCALE The units per printer's point (1/72 of an inch) in which the font is measured. For example, this is 35.27778 (the number of micas per printer's point) for Interpress fonts, which are measured in terms of micas. (FONTCOPY OLDFONT PROP1 VAL1 PROP2 VAL2 ...) [NoSpread Function] Returns a font descriptor that is a copy of the font OLDFONT, but which differs from OLDFONT in that OLDFONT's properties are replaced by the specified properties and values. Thus, (FONTCOPY FONT 'WEIGHT 'BOLD 'DEVICE 'INTERPRESS) will return a bold Interpress font with all other properties the same as those of FONT. FONTCOPY accepts the properties FAMILY, SIZE, WEIGHT, SLOPE, EXPANSION, FACE, ROTATION, and DEVICE. If the first property is a list, it is taken to be the PROP1 VAL1 PROP2 VAL2 ... sequence. Thus, (FONTCOPY FONT '(WEIGHT BOLD DEVICE INTERPRESS)) is equivalent to the example above. If the property NOERROR is specified with value non-NIL, FONTCOPY will return NIL rather than causing an error if the specified font cannot be created. (FONTSAVAILABLE FAMILY SIZE FACE ROTATION DEVICE CHECKFILESTOO?) [Function] Returns a list of available fonts that match the given specification. FAMILY, SIZE, FACE, ROTATION, and DEVICE are the same as for FONTCREATE. Additionally, any of them can be the atom *, in which case all values of that field are matched. If CHECKFILESTOO? is NIL, only fonts already loaded into virtual memory will be considered. If CHECKFILESTOO? is non-NIL, the font directories for the specified device will be searched. When checking font files, the ROTATION is ignored. Note: The search is conditional on the status of the server which holds the font. Thus a file server crash may prevent FONTCREATE from finding a file that an earlier FONTSAVAILABLE returned. Each element of the list returned will be of the form (FAMILY SIZE FACE ROTATION DEVICE). Examples: (FONTSAVAILABLE 'MODERN 10 'MRR 0 'DISPLAY) will return ((MODERN 10 (MEDIUM REGULAR REGULAR) 0 DISPLAY)) if the regular Modern 10 font for the display is in virtual memory; NIL otherwise. (FONTSAVAILABLE '* 14 '* '* 'INTERPRESS T) will return a list of all the size 14 Interpress fonts, whether they are in virtual memory or in font files. Warning: One must be careful when using the function FONTSAVAILABLE to determine what Press font files are available. For Press font families/faces, the font widths for different sizes are consistently scaled versions of the smallest font in the family/face. Therefore, instead of storing data about all of the sizes in the FONTS.WIDTHS file, only the widths for the font of SIZE=1 are stored, and the other widths are calculated by scaling these widths up. This is signified in the FONTS.WIDTHS file by a font with SIZE=0. Therefore, if FONTSAVAILABLE is called with CHECKFILESTOO?=T, and it finds such a "relative" font, it returns a font spec list with size of 0. For example, _(FONTSAVAILABLE 'GACHA '* '* 0 'PRESS T) ((GACHA 0 (BOLD ITALIC REGULAR) 0 PRESS) (GACHA 0 (BOLD REGULAR REGULAR) 0 PRESS) (GACHA 0 (MEDIUM ITALIC REGULAR) 0 PRESS) (GACHA 0 (MEDIUM REGULAR REGULAR) 0 PRESS)) This indicates that Press files can be created with GACHA files of any size with faces BIR, BRR, MIR, and MRR. Of course, this doesn't guarantee that these fonts are available in all sizes on your printer. (SETFONTDESCRIPTOR FAMILY SIZE FACE ROTATION DEVICE FONT) [Function] Indicates to the system that FONT is the font that should be associated with the FAMILY SIZE FACE ROTATION DEVICE characteristics. If FONT is NIL, the font associated with these characteristics is cleared and will be recreated the next time it is needed. As with FONTPROP and FONTCOPY, FONT is coerced to a font descriptor if it is not one already. This functions is useful when it is desirable to simulate an unavailable font or to use a font with characteristics different from the interpretations provided by the system. (DEFAULTFONT DEVICE FONT %) [Function] Returns the font that would be used as the default (if NIL were specified as a font argument) for image stream type DEVICE. If FONT is a font descriptor, it is set to be the default font for DEVICE. (CHARWIDTH CHARCODE FONT) [Function] CHARCODE is an integer that represents a valid character (as returned by CHCON1). Returns the amount by which an image stream's X-position will be incremented when the character is printed. (CHARWIDTHY CHARCODE FONT) [Function] Like CHARWIDTH, but returns the Y component of the character's width, the amount by which an image stream's Y-position will be incremented when the character is printed. This will be zero for most characters in normal portrait fonts, but may be non-zero for landscape fonts or for vector-drawing fonts. (STRINGWIDTH STR FONT FLG RDTBL) [Function] Returns the amount by which a stream's X-position will be incremented if the printname for the Interlisp-D object STR is printed in font FONT. If FONT is an image stream, its font is used. If FLG is non-NIL, the PRIN2-pname of STR with respect to the readtable RDTBL is used. (STRINGREGION STR STREAM PRIN2FLG RDTBL) [Function] Returns the region occupied by STR if it were printed at the current location in the image stream STREAM. This is useful, for example, for determining where text is in a window to allow the user to select it. The arguments PRIN2FLG and RDTBL are passed to STRINGWIDTH. Note: STRINGREGION does not take into account any carriage returns in the string, or carriage returns that may be automatically printed if STR is printed to STREAM. Therefore, the value returned is meaningless for multi-line strings. The following functions allow the user to access and change the bitmaps for individual characters in a display font. Note: Character code 256 can be used to access the "dummy" character, used for characters in the font with no bitmap defined. (GETCHARBITMAP CHARCODE FONT) [Function] Returns a bitmap containing a copy of the image of the character CHARCODE in the font FONT. (PUTCHARBITMAP CHARCODE FONT NEWCHARBITMAP NEWCHARDESCENT) [Function] Changes the bitmap image of the character CHARCODE in the font FONT to the bitmap NEWCHARBITMAP. If NEWCHARDESCENT is non-NIL, the descent of the character is changed to the value of NEWCHARDESCENT. (EDITCHAR CHARCODE FONT) [Function] Calls the bitmap editor (EDITBM) on the bitmap image of the character CHARCODE in the font FONT. CHARCODE can be a character code (as returned by CHCON1) or an atom or string, in which case the first character of CHARCODE is used. Font Files and Font Directories 1 If FONTCREATE is called to create a font that has not been loaded into Interlisp, FONTCREATE has to read the font information from a font file that contains the information for that font. For printer devices, the font files have to contain width information for each character in the font. For display fonts, the font files have to contain, in addition, bitmap images for each character in the fonts. The font file names, formats, and searching algorithms are different for each device. There are a set of variables for each device, that determine the directories that are searched for font files. All of these variables must be set before Interlisp can auto-load font files. These variables should be initialized in the site-specific INIT file. DISPLAYFONTDIRECTORIES [Variable] Value is a list of directories searched to find font bitmap files for display fonts. DISPLAYFONTEXTENSIONS [Variable] Value is a list of file extensions used when searching DISPLAYFONTDIRECTORIES for display fonts. Initially set to (DISPLAYFONT), but when using older font files it may be necessary to add STRIKE and AC to this list. INTERPRESSFONTDIRECTORIES [Variable] Value is a list of directories searched to find font widths files for Interpress fonts. PRESSFONTWIDTHSFILES [Variable] Value is a list of files (not directories) searched to find font widths files for Press fonts. Press font widths are packed into large files (usually named FONTS.WIDTHS). Font Profiles 1 PRETTYPRINT contains a facility for printing different elements (user functions, system functions, clisp words, comments, etc.) in different fonts to emphasize (or deemphasize) their importance, and in general to provide for a more pleasing appearance. Of course, in order to be useful, this facility requires that the user is printing on a device (such as a bitmapped display or a laser printer) which supports multiple fonts. PRETTYPRINT signals font changes by inserting into the file a user-defined escape sequence (the value of the variable FONTESCAPECHAR) followed by the character code which specifies, by number, which font to use, i.e. A for font number 1, etc. Thus, if FONTESCAPECHAR were the character F, FC would be output to change to font 3, FA to change to font 1, etc. If FONTESCAPECHAR consists of characters which are separator charactors in FILERDTBL, then a file with font changes in it can also be loaded back in. Currently, PRETTYPRINT uses the following font classes. The user can specify separate fonts for each of these classes, or use the same font for several different classes. LAMBDAFONT The font for printing the name of the function being prettyprinted, before the actual definition (usually a large font). CLISPFONT If CLISPFLG is on, the font for printing any clisp words, i.e. atoms with property CLISPWORD. COMMENTFONT The font used for comments. USERFONT The font for the name of any function in the file, or any member of the list FONTFNS. SYSTEMFONT The font for any other (defined) function. CHANGEFONT The font for an expression marked by the editor as having been changed. PRETTYCOMFONT The font for the operand of a file package command. DEFAULTFONT The font for everything else. Note that not all combinations of fonts will be aesthetically pleasing (or even readable!) and the user may have to experiment to find a compatible set. Although in some implementations LAMBDAFONT et al. may be defined as variables, one should not set them directly, but should indicate what font is to be used for each class by calling the function FONTPROFILE: (FONTPROFILE PROFILE) [Function] Sets up the font classes as determined by PROFILE, a list of elements which defines the correspondence between font classes and specific fonts. Each element of PROFILE is a list of the form: (FONTCLASS FONT# DISPLAYFONT PRESSFONT INTERPRESSFONT) FONTCLASS is the font class name and FONT# is the font number for that class. For each font class name, the escape sequence will consist of FONTESCAPECHAR followed by the character code for the font number, e.g. A for font number 1, etc. If FONT# is NIL for any font class, the font class named DEFAULTFONT (which must always be specified) is used. Alternatively, if FONT# is the name of a previously defined font class, this font class will be equivalenced to the previously defined one. DISPLAYFONT, PRESSFONT, and INTERPRESSFONT are font specifications (of the form accepted by FONTCREATE) for the fonts to use when printing to the display and to Press and Interpress printers respectively. FONTPROFILE [Variable] This is the variable used to store the current font profile, in the form accepted by the function FONTPROFILE. Note that simply editing this value will not change the fonts used for the various font classes; it is necessary to execute (FONTPROFILE FONTPROFILE) to install the value of this variable. The process of printing with multiple fonts is affected by a large number of variables: FONTPROFILE, FILELINELENGTH, PRETTYLCOM, etc. To facilitate switching back and forth between various sets of values for the font variables, Interlisp supports the idea of named "font configurations" encapsulating the values of all relevant variables. To create a new font configuration, set all "relevant" variables to the values you want, and then call FONTNAME to save them (on the variable FONTDEFS) under a given name. To install a particular font configuration, call FONTSET giving it your name. To change the values in a saved font configuration, edit the value of the variable FONTDEFS. Note: The list of variables saved by FONTNAME is stored in the variable FONTDEFSVARS. This can be changed by the user. (FONTNAME NAME) [Function] Collects the names and values of the variables on FONTDEFSVARS, and saves them on FONTDEFS. (FONTSET NAME) [Function] Installs font configuration for NAME. Also evaluates (FONTPROFILE FONTPROFILE) to install the font classes as specified in the new value of the variable FONTPROFILE. Generates an error if NAME not previously defined. FONTDEFSVARS [Variable] The list of variables to be packaged by a FONTNAME. Initially FONTCHANGEFLG, FILELINELENGTH, COMMENTLINELENGTH, FIRSTCOL, PRETTYLCOM, LISTFILESTR, and FONTPROFILE. FONTDEFS [Variable] An association list of font configurations. FONTDEFS is a list of elements of form (NAME . PARAMETER-PAIRS). To save a configuration on a file after performing a FONTNAME to define it, the user could either save the entire value of FONTDEFS, or use the ALISTS file package command to dump out just the one configuration. FONTESCAPECHAR [Variable] The character or string used to signal the start of a font escape sequence. FONTCHANGEFLG [Variable] If T, enables fonts when prettyprinting. If NIL, disables fonts. LISTFILESTR [Variable] In Interlisp-10, passed to the operating system by LISTFILES . Can be used to specify subcommands to the LIST command, e.g. to establish correspondance between font number and font name. COMMENTLINELENGTH [Variable] Since comments are usually printed in a smaller font, COMMENTLINELENGTH is provided to offset the fact that Interlisp does not know about font widths. When FONTCHANGEFLG=T, CAR of COMMENTLINELENGTH is the linelength used to print short comments, i.e. those printed in the right margin, and CDR is the linelength used when printing full width comments. (CHANGEFONT FONT STREAM) [Function] Executes the operations on STREAM to change to the font FONT. For use in PRETTYPRINTMACROS. Image Objects 1 An Image Object is an object that includes information about an image, such as how to display it, how to print it, and how to manipulate it when it is included in a collection of images (such as a document). More generally, it enables you to include one kind of image, with its own semantics, layout rules, and editing paradigms, inside another kind of image. Image Objects provide a general-purpose interface between image users who want to manipulate arbitrary images, and image producers, who create images for use, say, in documents. Images are encapsulated inside a uniform barrier%the IMAGEOBJ data type. From the outside, you communicate to the image by calling a standard set of functions. For example, calling one function tells you how big the image is; calling another causes the image object to be displayed where you tell it, and so on. Anyone who wants to create images for general use can implement his own brand of IMAGEOBJ. IMAGEOBJs have been implemented (in library packages) for bitmaps, menus, annotations, graphs, and sketches. Image Objects were originally implemented to support inserting images into TEdit text files, but the facility is available for use by any tools that manipulate images. The Image Object interface allows objects to exist in TEdit documents and be edited with their own editor. It also provides a facility in which objects can be shift-selected (or "copy-selected") between TEdit and non-TEdit windows. For example, the Image Objects interface allows you to copy-select graphs from a Grapher window into a TEdit window. The source window (where the object comes from) does not have to know what sort of window the destination window (where the object is inserted) is, and the destination does not have to know where the insertion comes from. A new data type, IMAGEOBJ, contains the data and the procedures necessary to manipulate an object that is to be manipulated in this way. IMAGEOBJs are created with the function IMAGEOBJCREATE (below). Another new data type, IMAGEFNS, is a vector of the procedures necessary to define the behavior of a type of IMAGEOBJ. Grouping the operations in a separate data type allows multiple instances of the same type of image object to share procedure vectors. The data and procedure fields of an IMAGEOBJ have a uniform interface through the function IMAGEOBJPROP. IMAGEFNS are created with the function IMAGEFNSCREATE: (IMAGEFNSCREATE(IMAGEFNSCREATE (Function) NIL NIL ("27") 27) DISPLAYFN IMAGEBOXFN PUTFN GETFN COPYFN BUTTONEVENTINFN COPYBUTTONEVENTINFN WHENMOVEDFN WHENINSERTEDFN WHENDELETEDFN WHENCOPIEDFN WHENOPERATEDONFN PREPRINTFN %) [Function] Returns an IMAGEFNS object that contains the functions necessary to define the behavior of an IMAGEOBJ. The arguments DISPLAYFN through PREPRINTFN should all be function names to be stored as the "methods" of the IMAGEFNS. The purpose of each IMAGEFNS method is described below. Note: Image objects must be "registered" before they can be read by TEdit or HREAD. IMAGEFNSCREATE implicitly registers its GETFN argument. (IMAGEOBJCREATE(IMAGEOBJCREATE (Function) NIL NIL ("27") 27) OBJECTDATUM IMAGEFNS) [Function] Returns an IMAGEOBJ that contains the object datum OBJECTDATUM and the operations vector IMAGEFNS. OBJECTDATUM can be arbitrary data. (IMAGEOBJPROP(IMAGEOBJPROP (Function) NIL NIL ("27") 28) IMAGEOBJECT PROPERTY NEWVALUE) [NoSpread Function] Accesses and sets the properties of an IMAGEOBJ. Returns the current value of the PROPERTY property of the image object IMAGEOBJECT. If NEWVALUE is given, the property is set to it. IMAGEOBJPROP can be used on the system properties OBJECTDATUM, DISPLAYFN, IMAGEBOXFN, PUTFN, GETFN, COPYFN, BUTTONEVENTINFN, COPYBUTTONEVENTINFN, WHENOPERATEDONFN, and PREPRINTFN. Additionally, it can be used to save arbitrary properties on an IMAGEOBJ. (IMAGEFNSP(IMAGEFNSP (Function) NIL NIL ("27") 28) X) [Function] Returns X if X is an IMAGEFNS object, NIL otherwise. (IMAGEOBJP(IMAGEOBJP (Function) NIL NIL ("27") 28) X) [Function] Returns X if X is an IMAGEOBJ object, NIL otherwise. IMAGEFNS Methods Note: Many of the IMAGEFNS methods below are passed "host stream" arguments. The TEdit text editor passes the "text stream" (an object contain all of the information in the document being edited) as the "host stream" argument. Other editing programs that want to use image objects may want to pass the data structure being edited to the IMAGEFNS methods as the "host stream" argument. (DISPLAYFN IMAGEOBJ IMAGESTREAM IMAGESTREAMTYPE HOSTSTREAM) [IMAGEFNS Method] The DISPLAYFN method is called to display the object IMAGEOBJ at the current position on IMAGESTREAM. The type of IMAGESTREAM indicates whether the device is the display or some other image stream. Note: When the DISPLAYFN method is called, the offset and clipping regions for the stream are set so the object's image is at (0,0), and only that image area can be modified. (IMAGEBOXFN IMAGEOBJ IMAGESTREAM CURRENTX RIGHTMARGIN) [IMAGEFNS Method] The IMAGEBOXFN method should return the size of the object as an IMAGEBOX, which is a data structure that describes the image laid down when an IMAGEOBJ is displayed in terms of width, height, and descender height. An IMAGEBOX has four fields: XSIZE, YSIZE, YDESC, and XKERN. XSIZE and YSIZE are the width and height of the object image. YDESC and XKERN give the position of the baseline and the left edge of the image relative to where you want to position it. For characters, the YDESC is the descent (height of the descender) and the XKERN is the amount of left kerning (note: TEdit doesn't support left kerning). The IMAGEBOXFN looks at the type of the stream to determine the output device if the object's size changes from device to device. (For example, a bit-map object may specify a scale factor that is ignored when the bit map is displayed on the screen.) CURRENTX and RIGHTMARGIN allow an object to take account of its environment when deciding how big it is. If these fields are not available, they are NIL. Note: TEdit calls the IMAGEBOXFN only during line formatting, then caches the IMAGEBOX as the BOUNDBOX property of the IMAGEOBJ. This avoids the need to call the IMAGEBOXFN when incomplete position and margin information is available. (PUTFN IMAGEOBJ FILESTREAM) [IMAGEFNS Method] The PUTFN method is called to save the object on a file. It prints a description on FILESTREAM that, when read by the corresponding GETFN method (see below), regenerates the image object. (TEdit and HPRINT take care of writing out the name of the GETFN.) (GETFN FILESTREAM) [IMAGEFNS Method] The GETFN method is called when the object is encountered on the file during input. It reads the description that was written by the PUTFN method and returns an IMAGEOBJ. (COPYFN IMAGEOBJ SOURCEHOSTSTREAM TARGETHOSTSTREAM) [IMAGEFNS Method] The COPYFN method is called during a copy-select operation. It should return a copy of IMAGEOBJ. If it returns the litatom DON'T, copying is suppressed. (BUTTONEVENTINFN IMAGEOBJ WINDOWSTREAM SELECTION RELX RELY WINDOW HOSTSTREAM BUTTON) [IMAGEFNS Method] The BUTTONEVENTINFN method is called when you press a mouse button inside the object. The BUTTONEVENTINFN decides whether or not to handle the button, to track the cursor in parallel with mouse movement, and to invoke selections or edits supported by the object (but see the COPYBUTTONEVENTINFN method below). If the BUTTONEVENTINFN returns NIL, TEdit treats the button press as a selection at its level. Note that when this function is first called, a button is down. The BUTTONEVENTINFN should also support the button-down protocol to descend inside of any composite objects with in it. In most cases, the BUTTONEVENTINFN relinquishes control (i.e., returns) when the cursor leaves its object's region. When the BUTTONEVENTINFN is called, the window's clipping region and offsets have been changed so that the lower-left corner of the object's image is at (0,0), and only the object's image can be changed. The selection is available for changing to fit your needs; the mouse button went down at (RELX,RELY) within the object's image. You can affect how TEdit treats the selection by returning one of several values. If you return NIL, TEdit forgets that you selected an object; if you return the atom DON'T, TEdit doesn't permit the selection; if you return the atom CHANGED, TEdit updates the screen. Use CHANGED to signal TEdit that the object has changed size or will have side effects on other parts of the screen image. (COPYBUTTONEVENTINFN IMAGEOBJ WINDOWSTREAM) [IMAGEFNS Method] The COPYBUTTONEVENTINFN method is called when you button inside an object while holding down a copy key. Many of the comments about BUTTONEVENTINFN apply here too. Also, see the discussion below about copying image objects between windows. (WHENMOVEDFN IMAGEOBJ TARGETWINDOWSTREAM SOURCEHOSTSTREAM TARGETHOSTSTREAM) [IMAGEFNS Method] The WHENMOVEDFN method provides hooks by which the object is notified when TEdit performs an operation (MOVEing) on the whole object. It allows objects to have side effects. (WHENINSERTEDFN IMAGEOBJ TARGETWINDOWSTREAM SOURCEHOSTSTREAM TARGETHOSTSTREAM) [IMAGEFNS Method] The WHENINSERTEDFN method provides hooks by which the object is notified when TEdit performs an operation (INSERTing) on the whole object. It allows objects to have side effects. (WHENDELETEDFN IMAGEOBJ TARGETWINDOWSTREAM) [IMAGEFNS Method] The WHENDELETEDFN method provides hooks by which the object is notified when TEdit performs an operation (DELETEing) on the whole object. It allows objects to have side effects. (WHENCOPIEDFN IMAGEOBJ TARGETWINDOWSTREAM SOURCEHOSTSTREAM TARGETHOSTSTREAM) [IMAGEFNS Method] The WHENCOPIEDFN method provides hooks by which the object is notified when TEdit performs an operation (COPYing) on the whole object. The WHENCOPIEDFN method is called in addition to (and after) the COPYFN method above. It allows objects to have side effects. (WHENOPERATEDONFN IMAGEOBJ WINDOWSTREAM HOWOPERATEDON SELECTION HOSTSTREAM) [IMAGEFNS Method] The WHENOPERATEDONFN method provides a hook for edit operations. HOWOPERATEDON should be one of SELECTED, DESELECTED, HIGHLIGHTED, and UNHILIGHTED. The WHENOPERATEDONFN differs from the BUTTONEVENTINFN because it is called when you extend a selection through the object. That is, the object is treated in toto as a TEdit character. HIGHLIGHTED refers to the selection being highlighted on the screen, and UNHIGHLIGHTED means that the highlighting is being turned off. (PREPRINTFN IMAGEOBJ) [IMAGEFNS Method] The PREPRINTFN method is called to convert the object into something that can be printed for inclusion in documents. It returns an object that the receiving window can print (using either PRIN1 or PRIN2,its choice) to obtain a character representation of the object. If the PREPRINTFN method is NIL, the OBJECTDATUM field of IMAGEOBJ itself is used. TEdit uses this function when you indicate that you want to print the characters from an object rather than the object itself (presumably using PRIN1 case). Registering Image Objects Each legitimate GETFN needs to be known to the system, to prevent various Trojan-horse problems and to allow the automatic loading of the supporting code for infrequently used IMAGEOBJs. To this end, there is a global list, IMAGEOBJGETFNS, that contains an entry for each GETFN. The existence of the entry marks the GETFN as legitimate; the entry itself is a property list, which can hold information about the GETFN. No action needs to be taken for GETFNs that are currently in use: the function IMAGEFNSCREATE automatically adds its GETFN argument to the list. However, packages that support obsolete versions of objects may need to explicitly add the obsolete GETFNs. For example, TEdit supports bit-map IMAGEOBJs. Recently, a change was made in the format in which objects are stored; to retain compatibility with the old object format, there are now two GETFNs. The current GETFN is automatically on the list, courtesy of IMAGEFNSCREATE. However, the code file that supports the old bit-map objects contains the clause: (ADDVARS (IMAGEOBJGETFNS (OLDGETFNNAME))), which adds the old GETFN to IMAGEOBJGETFNS. For a given GETFN, the entry on IMAGEOBJGETFNS may be a property list of information. Currently the only recognized property is FILE. FILE is the name of the file that can be loaded if the GETFN isn't defined. This file should define the GETFN, along with all the other functions needed to support that kind of IMAGEOBJ. For example, the bit-map IMAGEOBJ implemented by TEdit use the GETFN BMOBJ.GETFN2. Its entry on IMAGEOBJGETFNS is (BMOBJ.GETFN2 FILE IMAGEOBJ), indicating that the support code for bit-map image objects resides on the file IMAGEOBJ, and that the GETFN for them is BMOBJ.GETFN2. This makes it possible to have entries for GETFNs whose supporting code isn't loaded%you might, for instance, have your init file add entries to IMAGEOBJGETFNS for the kinds of image objects you commonly use. The system's default reading method will automatically load the code when necessary. Reading and Writing Image Objects on Files Image Objects can be written out to files using HPRINT and read back using HREAD. The following functions can also be used: (WRITEIMAGEOBJ(WRITEIMAGEOBJ (Function) NIL NIL ("27") 31) IMAGEOBJ STREAM) [Function] Prints (using PRIN2) a call to READIMAGEOBJ, then calls the PUTFN for IMAGEOBJ to write it onto STREAM. During input, then, the call to READIMAGEOBJ is read and evaluated; it in turn reads back the object's description, using the appropriate GETFN. (READIMAGEOBJ(READIMAGEOBJ (Function) NIL NIL ("27") 31) STREAM GETFN NOERROR) [Function] Reads an IMAGEOBJ from STREAM, starting at the current file position. Uses the function GETFN after validating it (and loading support code, if necessary). If the GETFN can't be validated or isn't defined, READIMAGEOBJ returns an "encapsulated image object", an IMAGEOBJ that safely encapsulates all of the information in the image object. An encapsulated image object displays as a rectangle that says, "Unknown IMAGEOBJ Type" and lists the GETFN's name. Selecting an encapsulated image object with the mouse causes another attempt to read the object from the file; this is so you can load any necessary support code and then get to the object. Warning: You cannot save an encapsulated image object on a file because there isn't enough information to allow copying the description to the new file from the old one. If NOERROR is non-NIL, READIMAGEOBJ returns NIL if it can't successfully read the object. Copying Image Objects Between Windows Copying between windows is implemented as follows: If a button event occurs in a window when a copy key is down, the window's COPYBUTTONEVENTFN window property is called. If this window supports copy-selection, it should track the mouse, indicating the item to be copied. When the button is released, the COPYBUTTONEVENTFN should create an image object out of the selected information, and call COPYINSERT to insert it in the current TTY window. COPYINSERT calls the COPYINSERTFN window property of the TTY window to insert this image object. Therefore, both the source and destination windows can determine how they handle copying image objects. If the COPYBUTTONEVENTFN of a window is NIL, the BUTTONEVENTFN is called instead when a button event occurs in the window when a copy key is down, and copying from that window is not supported. If the COPYINSERTFN of the TTY window is NIL, COPYINSERT will turn the image object into a string (by calling the PREPRINTFN method of the image object) and insert it by calling BKSYSBUF. COPYBUTTONEVENTFN [Window Property] The COPYBUTTONEVENTFN of a window is called (if it exists) when a button event occurs in the window and a copy key is down. If no COPYBUTTONEVENTFN exists, the BUTTONEVENTFN is called. COPYINSERTFN [Window Property] The COPYINSERTFN of the "destination" window is called by COPYINSERT to insert something into the destination window. It is called with two arguments: the object to be inserted and the destination window. The object to be inserted can be a character string, an IMAGEOBJ, or a list of IMAGEOBJs and character strings. As a convention, the COPYINSERTFN should call BKSYSBUF if the object to be inserted insert is a character string. (COPYINSERT IMAGEOBJ) [Function] COPYINSERT inserts IMAGEOBJ into the window that currently has the TTY. If the current TTY window has a COPYINSERTFN, it is called, passing it IMAGEOBJ and the window as arguments. If no COPYINSERTFN exists and if IMAGEOBJ is an image object, BKSYSBUF is called on the result of calling its PREPRINTFN on it. If IMAGEOBJ is not an image object, it is simply passed to BKSYSBUF . In this case, BKSYSBUF will call PRIN2 with a read table taken from the process associated with the TTY window. A window that wishes to use PRIN1 or a different read table must provide its own COPYINSERTFN to do this. Implementation of Image Streams 1 Interlisp does all image creation through a set of functions and data structures for device-independent graphics, known popularly as DIG. DIG is implemented through the use of a special type of stream, known as an image stream. An image stream, by convention, is any stream that has its IMAGEOPS field (described in detail below) set to a vector of meaningful graphical operations. Using image streams, you can write programs that draw and print on an output stream without regard to the underlying device, be it a window, a disk, or a printer. To define a new image stream type, it is necessary to put information on the variable IMAGESTREAMTYPES: IMAGESTREAMTYPES [Variable] This variable describes how to create a stream for a given image stream type. The value of IMAGESTREAMTYPES is an association list, indexed by the image stream type (e.g., DISPLAY, INTERPRESS, etc.). The format of a single association list item is: (IMAGETYPE (OPENSTREAM OPENSTREAMFN) (FONTCREATE FONTCREATEFN) (FONTSAVAILABLE FONTSAVAILABLEFN)) OPENSTREAMFN, FONTCREATEFN, and FONTSAVAILABLEFN are "image stream methods," device-dependent functions used to implement generic image stream operations. For Interpress image streams, the association list entry is: (INTERPRESS (OPENSTREAM OPENIPSTREAM) (FONTCREATE \CREATEINTERPRESSFONT) (FONTSAVAILABLE \SEARCHINTERPRESSFONTS)) (OPENSTREAMFN FILE OPTIONS) [Image Stream Method] FILE is the file name as it was passed to OPENIMAGESTREAM, and OPTIONS is the OPTIONS property list passed to OPENIMAGESTREAM. The result must be a stream of the appropriate image type. (FONTCREATEFN FAMILY SIZE FACE ROTATION DEVICE) [Image Stream Method] FAMILY is the family name for the font, e.g., MODERN. SIZE is the body size of the font, in printer's points. FACE is a three-element list describing the weight, slope, and expansion of the face desired, e.g., (MEDIUM ITALIC EXPANDED). ROTATION is how much the font is to be rotated from the normal orientation, in minutes of arc. For example, to print a landscape page, fonts have the rotation 5400 (90 degrees). The function's result must be a FONTDESCRIPTOR with the fields filled in appropriately. (FONTSAVAILABLEFN FAMILY SIZE FACE ROTATION DEVICE) [Image Stream Method] This function returns a list of all fonts agreeing with the FAMILY, SIZE, FACE, and ROTATION arguments; any of them may be wild-carded (i.e., equal to *, which means any value is acceptable). Each element of the list should be a quintuple of the form (FAMILY SIZE FACE ROTATION DEVICE). Where the function looks is an implementation decision: the FONTSAVAILABLEFN for the display device looks at DISPLAYFONTDIRECTORIES, the Interpress code looks on INTERPRESSFONTDIRECTORIES, and implementors of new devices should feel free to introduce new search path variables. As indicated above, image streams use a field that no other stream uses: IMAGEOPS. IMAGEOPS is an instance of the IMAGEOPS data type and contains a vector of the stream's graphical methods. The methods contained in the IMAGEOPS object can make arbitrary use of the stream's IMAGEDATA field, which is provided for their use, and may contain any data needed. IMAGETYPE [IMAGEOPS Field] Value is the name of an image type. Monochrome display streams have an IMAGETYPE of DISPLAY; color display streams are identified as (COLOR DISPLAY). The IMAGETYPE field is informational and can be set to anything you choose. IMFONTCREATE [IMAGEOPS Field] Value is the device name to pass to FONTCREATE when fonts are created for the stream. The remaining fields are all image stream methods, whose value should be a device-dependent function that implements the generic operation. Most methods are called by a similarly-named function, e.g. the function DRAWLINE calls the IMDRAWLINE method. All coordinates that refer to points in a display device's space are measured in the device's units. (The IMSCALE method provides access to a device's scale.) For arguments that have defaults (such as the BRUSH argument of DRAWCURVE), the default is substituted for the NIL argument before it is passed to the image stream method. Therefore, image stream methods do not have to handle defaults. (IMCLOSEFN STREAM) [Image Stream Method] Called before a stream is closed with CLOSEF. This method should flush buffers, write header or trailer information, etc. (IMDRAWLINE STREAM X1 Y1 X2 Y2 WIDTH OPERATION COLOR DASHING) [Image Stream Method] Draws a line of width WIDTH from (X1, Y1) to (X2, Y2). See DRAWLINE. (IMDRAWCURVE STREAM KNOTS CLOSED BRUSH DASHING) [Image Stream Method] Draws a curve through KNOTS. See DRAWCURVE. (IMDRAWCIRCLE STREAM CENTERX CENTERY RADIUS BRUSH DASHING) [Image Stream Method] Draws a circle of radius RADIUS around (CENTERX, CENTERY). See DRAWCIRCLE. (IMDRAWELLIPSE STREAM CENTERX CENTERY SEMIMINORRADIUS SEMIMAJORRADIUS ORIENTATION BRUSH DASHING) [Image Stream Method] Draws an ellipse around (CENTERX, CENTERY). See DRAWELLIPSE. (IMFILLPOLYGON STREAM POINTS TEXTURE) [Image Stream Method] Fills in the polygon outlined by POINTS on the image stream STREAM, using the texture TEXTURE. See FILLPOLYGON. (IMFILLCIRCLE STREAM CENTERX CENTERY RADIUS TEXTURE) [Image Stream Method] Draws a circle filled with texture TEXTURE around (CENTERX, CENTERY). See FILLCIRCLE. (IMBLTSHADE TEXTURE STREAM DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT OPERATION CLIPPINGREGION) [Image Stream Method] The texture-source case of BITBLT. DESTINATIONLEFT, DESTINATIONBOTTOM, WIDTH, HEIGHT, and CLIPPINGREGION are measured in STREAM's units. This method is invoked by the functions BITBLT and BLTSHADE. (IMBITBLT SOURCEBITMAP SOURCELEFT SOURCEBOTTOM STREAM DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT SOURCETYPE OPERATION TEXTURE CLIPPINGREGION CLIPPEDSOURCELEFT CLIPPEDSOURCEBOTTOM SCALE) [Image Stream Method] Contains the bit-map-source cases of BITBLT. SOURCELEFT, SOURCEBOTTOM, CLIPPEDSOURCELEFT, CLIPPEDSOURCEBOTTOM, WIDTH, and HEIGHT are measured in pixels; DESTINATIONLEFT, DESTINATIONBOTTOM, and CLIPPINGREGION are in the units of the destination stream. (IMSCALEDBITBLT SOURCEBITMAP SOURCELEFT SOURCEBOTTOM STREAM DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT SOURCETYPE OPERATION TEXTURE CLIPPINGREGION CLIPPEDSOURCELEFT CLIPPEDSOURCEBOTTOM SCALE) [Image Stream Method] A scaled version of IMBITBLT. Each pixel in SOURCEBITMAP is replicated SCALE times in the X and Y directions; currently, SCALE must be an integer. (IMMOVETO STREAM X Y) [Image Stream Method] Moves to (X,Y). This method is invoked by the function MOVETO. If IMMOVETO is not supplied, a default method composed of calls to the IMXPOSITION and IMYPOSITION methods is used. (IMSTRINGWIDTH STREAM STR RDTBL) [Image Stream Method] Returns the width of string STR in STREAM's units, using STREAM's current font. This is envoked when STRINGWIDTH is passed a stream as its FONT argument. If IMSTRINGWIDTH is not supplied, it defaults to calling STRINGWIDTH on the default font of STREAM. (IMCHARWIDTH STREAM CHARCODE) [Image Stream Method] Returns the width of character CHARCODE in STREAM's units, using STREAM's current font. This is invoked when CHARWIDTH is passed a stream as its FONT argument. If IMCHARWIDTH is not supplied, it defaults to calling CHARWIDTH on the default font of STREAM. (IMCHARWIDTHY STREAM CHARCODE) [Image Stream Method] Returns the Y componant of the width of character CHARCODE in STREAM's units, using STREAM's current font. This is envoked when CHARWIDTHY is passed a stream as its FONT argument. If IMCHARWIDTHY is not supplied, it defaults to calling CHARWIDTHY on the default font of STREAM. (IMBITMAPSIZE STREAM BITMAP DIMENSION) [Image Stream Method] Returns the size that BITMAP will be when BITBLTed to STREAM, in STREAM's units. DIMENSION can be one of WIDTH, HEIGHT, or NIL, in which case the dotted pair (WIDTH . HEIGHT) will be returned. This is envoked by BITMAPIMAGESIZE. If IMBITMAPSIZE is not supplied, it defaults to a method that multiplies the bitmap height and width by the scale of STREAM. (IMNEWPAGE STREAM) [Image Stream Method] Causes a new page to be started. The X position is set to the left margin, and the Y position is set to the top margin plus the linefeed. If not supplied, defaults to (\OUTCHAR STREAM (CHARCODE ^L)). Envoked by DSPNEWPAGE. (IMTERPRI STREAM) [Image Stream Method] Causes a new line to be started. The X position is set to the left margin, and the Y position is set to the current Y position plus the linefeed. If not supplied, defaults to (\OUTCHAR STREAM (CHARCODE EOL)). Envoked by TERPRI. (IMRESET(IMRESET (Image Stream Method) NIL NIL ("27") 35) STREAM) [Image Stream Method] Resets the X and Y position of STREAM. The X coordinate is set to its left margin; the Y coordinate is set to the top of the clipping region minus the font ascent. Envoked by DSPRESET. The following methods all have corresponding DSPxx functions (e.g., IMYPOSITION corresponds to DSPYPOSITION) that invoke them. They also have the property of returning their previous value; when called with NIL they return the old value without changing it. (IMCLIPPINGREGION(IMCLIPPINGREGION (Image Stream Method) NIL NIL ("27") 35) STREAM REGION) [Image Stream Method] Sets a new clipping region on STREAM. (IMXPOSITION(IMXPOSITION (Image Stream Method) NIL NIL ("27") 36) STREAM XPOSITION) [Image Stream Method] Sets the X-position on STREAM. (IMYPOSITION(IMYPOSITION (Image Stream Method) NIL NIL ("27") 36) STREAM YPOSITION) [Image Stream Method] Sets a new Y-position on STREAM. (IMFONT(IMFONT (Image Stream Method) NIL NIL ("27") 36) STREAM FONT) [Image Stream Method] Sets STREAM's font to be FONT. (IMLEFTMARGIN(IMLEFTMARGIN (Image Stream Method) NIL NIL ("27") 36) STREAM LEFTMARGIN) [Image Stream Method] Sets STREAM's left margin to be LEFTMARGIN. The left margin is defined as the X-position set after the new line. (IMRIGHTMARGIN(IMRIGHTMARGIN (Image Stream Method) NIL NIL ("27") 36) STREAM RIGHTMARGIN) [Image Stream Method] Sets STREAM's right margin to be RIGHTMARGIN. The right margin is defined as the maximum X-position at which characters are printed; printing beyond it causes a new line. (IMTOPMARGIN(IMTOPMARGIN (Image Stream Method) NIL NIL ("27") 36) STREAM YPOSITION) [Image Stream Method] Sets STREAM's top margin (the Y-position of the tops of characters that is set after a new page) to be YPOSITION. (IMBOTTOMMARGIN(IMBOTTOMMARGIN (Image Stream Method) NIL NIL ("27") 36) STREAM YPOSITION) [Image Stream Method] Sets STREAM's bottom margin (the Y-position beyond which any printing causes a new page) to be YPOSITION. (IMLINEFEED(IMLINEFEED (Image Stream Method) NIL NIL ("27") 36) STREAM DELTA) [Image Stream Method] Sets STREAM's line feed distance (distance to move vertically after a new line) to be DELTA. (IMSCALE(IMSCALE (Image Stream Method) NIL NIL ("27") 36) STREAM SCALE) [Image Stream Method] Returns the number of device points per screen point (a screen point being ~1/72 inch). SCALE is ignored. (IMSPACEFACTOR(IMSPACEFACTOR (Image Stream Method) NIL NIL ("27") 36) STREAM FACTOR) [Image Stream Method] Sets the amount by which to multiply the natural width of all following space characters on STREAM; this can be used for the justification of text. The default value is 1. For example, if the natural width of a space in STREAM's current font is 12 units, and the space factor is set to two, spaces appear 24 units wide. The values returned by STRINGWIDTH and CHARWIDTH are also affected. (IMOPERATION(IMOPERATION (Image Stream Method) NIL NIL ("27") 36) STREAM OPERATION) [Image Stream Method] Sets the default BITBLT OPERATION argument. (IMBACKCOLOR(IMBACKCOLOR (Image Stream Method) NIL NIL ("27") 36) STREAM COLOR) [Image Stream Method] Sets the background color of STREAM. (IMCOLOR (IMCOLOR% (Image Stream Method) NIL NIL ("27") 36)STREAM COLOR) [Image Stream Method] Sets the default color of STREAM. In addition to the IMAGEOPS methods described above, there are two other important methods, which are contained in the stream itself. These fields can be installed using a form like (replace (STREAM OUTCHARFN) of STREAM with (FUNCTION MYOUTCHARFN)). Note: You need to have loaded the Interlisp-D system declarations to manipulate the fields of STREAMs. The declarations can be loaded by loading the Lisp Library package SYSEDIT. (STRMBOUTFN(STRMBOUTFN (Stream Method) NIL NIL ("27") 37) STREAM CHARCODE) [Stream Method] The function called by BOUT. (OUTCHARFN(OUTCHARFN (Stream Method) NIL NIL ("27") 37) STREAM CHARCODE) [Stream Method] The function that is called to output a single byte. This is like STRMBOUTFN, except for being one level higher: it is intended for text output. Hence, this function should convert (CHARCODE EOL) into the stream's actual end-of-line sequence and should adjust the stream's CHARPOSITION appropriately before invoking the stream's STRMBOUTFN (by calling BOUT) to actually put the character. Defaults to \FILEOUTCHARFN, which is probably incorrect for an image stream. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "27-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "27-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "27-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "27-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "27-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "27-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))'3HH +T3HZT,ll,0T,00/HH5,,,3$$(T,,/HH +/ll3HZ +T,ll +,ll3(T5,HH +,HH3$$(T-T3(T,ll,HH +,HH,2F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR,,-T, TITAN + HELVETICA TITAN +CLASSIC + HELVETICACLASSIC +CLASSIC + HELVETICA +CLASSIC +MODERN +MODERN +MODERNMODERN +# HRULE.GETFNCLASSIC + # " HRULE.GETFNCLASSIC + "! ! HRULE.GETFNCLASSIC +     HRULE.GETFNCLASSIC +   IM.CHAP.GETFNMODERN!  HRULE.GETFNMODERN +    HRULE.GETFNMODERN +   ; (  O   4  +           j    ?                     .IM.INDEX.GETFN    L   *IM.INDEX.GETFN    y  /IM.INDEX.GETFN        (IM.INDEX.GETFN    7    *IM.INDEX.GETFN  ,     .IM.INDEX.GETFN  C ! $          %IM.INDEX.GETFN               U  F + *    *      %IM.INDEX.GETFN      )IM.INDEX.GETFN    *IM.INDEX.GETFN     (   +'IM.INDEX.GETFNMODERN +   3   $   < /  , _    (IM.INDEX.GETFN  ( 1   *IM.INDEX.GETFNMODERN +             *IM.INDEX.GETFN 1    ,    %       )IM.INDEX.GETFN    ) +  (IM.INDEX.GETFN  6    $IM.INDEX.GETFNMODERN +  X  >   / = @ (  BMOBJ.GETFN3MODERN +  T  Z   ;  i   s ,  + g +  +    <   3 w     '  L   R 5IM.INDEX.GETFNCLASSIC + :  3 7  &IM.INDEX.GETFNCLASSIC +    %   + F + + Z  +'IM.INDEX.GETFN )     N + V , BMOBJ.GETFN3MODERN +  HRULE.GETFNMODERN +     +'  c f ) -IM.INDEX.GETFNCLASSIC +  *       3 7              }   ! $ _    9  +    +/  2   < +  + v & H  + +L  @    B B '9   Y  @       /    #!  "$   *IM.INDEX.GETFNCLASSIC +   H     -IM.INDEX.GETFNCLASSIC + !  .IM.INDEX.GETFNCLASSIC +       & HRULE.GETFNMODERN +   /IM.INDEX.GETFN    %IM.INDEX.GETFN  U  ) !  ++   % #  p  *IM.INDEX.GETFN  -IM.INDEX.GETFN b T  +IM.INDEX.GETFN  ,IM.INDEX.GETFNCLASSIC + a u < +  *IM.INDEX.GETFNCLASSIC +  F        )IM.INDEX.GETFN    &IM.INDEX.GETFN  &   Z   0  #    +  k | v   X    % .  a     .  l  $& HRULE.GETFNMODERN + a   4   E   4   E  +     +  * +    R   3 *    4 F   ( HRULE.GETFNMODERN + l  +   ^ /  1  3 +      0   , +  + 2  0  +     +     (   +    3     +   i 9   +?   +      2       +   0 +     7        + c    %< BMOBJ.GETFN3MODERN + < BMOBJ.GETFN3  BMOBJ.GETFN3 'J +   +&  BMOBJ.GETFN3MODERN +  BMOBJ.GETFN3  BMOBJ.GETFN3  BMOBJ.GETFN3  BMOBJ.GETFN3MODERN +  BMOBJ.GETFN3  BMOBJ.GETFN3  BMOBJ.GETFN3  BMOBJ.GETFN3MODERN +  BMOBJ.GETFN3  BMOBJ.GETFN3  BMOBJ.GETFN3  [  +  ;   "             HRULE.GETFNMODERN + F - +  % %           ;     &    t     +      %       ( 4      +* 4 1  $    HRULE.GETFNMODERN +    +  ,  L  + R \    T  +  x t V  >    +! <  ?  Q   %  5!<   BMOBJ.GETFN3MODERN +  ,      ;  P (     Y  ; >      + f + ~ +>  +  +            . HRULE.GETFNMODERN +    /           !    G (     Q.  BMOBJ.GETFN3MODERN + = h  )6/  | BMOBJ.GETFN3MODERN +   4   BMOBJ.GETFN3MODERN +  % #                       ,  & HRULE.GETFNMODERN + / X K . ' -   +   7 ?   Y  P  /        +  m   8 /    8 /  m  a     c    HRULE.GETFNMODERN + '   +  * Z  +            +" v    +" v   i    ?    +   O +      + |   .      e    # / +   7  +  )  %   + :        +     i . I     HRULE.GETFNMODERN +  9 6 %  %  9         +   \ *'$)s M  + ?   ;  3 3 + ; K    8 4 V  + `     +k    R F +1 +^  + . + + +  9 +      +        +   1  ?    W  )     3    G  +     "  )    4  +           +     /  + w ~       5   I + S          :     %     F  0 G      +- 6   H  `  x +%  6   + , 0E  +n 6 6 i     2 0%*$)$*$+$-4     b %  0       w   ;     7 :  <   +   A o      !    r   +    +      @ y     y  G      A   * *     :       '   ) =  +  & HRULE.GETFNMODERN +  +E +  U  7 & =    X     HRULE.GETFNMODERN +    k  6 "    '   : B    +z   H     N   +,  +I  4    ! +    * p       c      * > u    2 +g    E   ) X   + g  H j  &  $   2      K      *      +    -  8 >  >  L    )    3 . M   6 V   ] ;          HRULE.GETFNMODERN +   j    _   q    N  /    -IM.INDEX.GETFN?   K    +C   N    -IM.INDEX.GETFNCLASSIC +       +IM.INDEX.GETFNCLASSIC + ' $   &  &   +       +C   +(IM.INDEX.GETFN       +(IM.INDEX.GETFNCLASSIC +        9 (  +/  (   I  f ,  )  +3 G C       0   2 K  +  ~   +.   # +?   L +& ? *   +  }   *  N   B  H     y R    ~ D = ! p   n `  =   =       =   | 1 8 8  .   +     > 4    +  I +   +       ) " ( Z   *  | (   + W       T  3 - D       M   +  V +   T 0  - ,IM.INDEX.GETFNCLASSIC +      # ^   +IM.INDEX.GETFNCLASSIC +   < ?  & ,         +  %   I +* +        +: +6    n      * +  /  <    + N      ( + 0   g 0    HRULE.GETFNMODERN +  ;  V   \ A  +;                       &    >    (  5 `   *   <    ; d  < !  [ I   b / J    H  *  ?    $ +(   +u ]  & {  +  & N           !      +    +  P         !      % #   +  V        3       %  +        -       -    +   *  <          '   )           '   )       2   ' +  ) +                   f    +      +          1IM.INDEX.GETFNMODERN +     -   e 0 :IM.INDEX.GETFNMODERN +       5IM.INDEX.GETFNMODERN +      5IM.INDEX.GETFNMODERN +      0IM.INDEX.GETFNMODERN +        6IM.INDEX.GETFNMODERN +    +F  7IM.INDEX.GETFNMODERN +       5IM.INDEX.GETFNMODERN +   \   8IM.INDEX.GETFNMODERN +   T    4IM.INDEX.GETFNMODERN +    K   1IM.INDEX.GETFNMODERN +   Y   7IM.INDEX.GETFNMODERN +  \ | v    5IM.INDEX.GETFNMODERN +      5IM.INDEX.GETFNMODERN +      3IM.INDEX.GETFN       Bb G   .IM.INDEX.GETFNMODERN +     +-IM.INDEX.GETFNMODERN +  C +j N , + / 3 z \ No newline at end of file diff --git a/docs/porter-irm/vol3/28-WINDOWS.INTERPRESS b/docs/porter-irm/vol3/28-WINDOWS.INTERPRESS new file mode 100644 index 00000000..e78a183b Binary files /dev/null and b/docs/porter-irm/vol3/28-WINDOWS.INTERPRESS differ diff --git a/docs/porter-irm/vol3/28-WINDOWS.TEDIT b/docs/porter-irm/vol3/28-WINDOWS.TEDIT new file mode 100644 index 00000000..7721fe7e Binary files /dev/null and b/docs/porter-irm/vol3/28-WINDOWS.TEDIT differ diff --git a/docs/porter-irm/vol3/29-HARDCOPY.INTERPRESS b/docs/porter-irm/vol3/29-HARDCOPY.INTERPRESS new file mode 100644 index 00000000..4d57f1e5 Binary files /dev/null and b/docs/porter-irm/vol3/29-HARDCOPY.INTERPRESS differ diff --git a/docs/porter-irm/vol3/29-HARDCOPY.TEDIT b/docs/porter-irm/vol3/29-HARDCOPY.TEDIT new file mode 100644 index 00000000..b417bade --- /dev/null +++ b/docs/porter-irm/vol3/29-HARDCOPY.TEDIT @@ -0,0 +1,59 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 29. HARDCOPY FACILITIES 1 29. HARDCOPY FACILITIES 1 "29"29. HARDCOPY FACILITIES 6 Interlisp-D includes facilities for generating hardcopy in "Interpress" format and "Press" format. Interpress is a file format used for communicating documents to Xerox Network System printers such as the Xerox 8044 and Xerox 5700. Press is a file format used for communicating documents to Xerox laser Xerographic printers known by the names "Dover", "Spruce", "Penguin", and "Raven". There are also library packages available for supporting other types of printer formats (4045, FX-80, C150, etc.). The hardcopy facilities are designed to allow the user to support new types of printers with minimal changes to the user interface. Files can be in a number of formats, including Interpress files, plain text files, and formatted Tedit files. In order to print a file on a given printer, it is necessary to identify the format of the file, convert the file to a format that the printer can accept, and transmit it. Rather than require that the user explicitly determine file types and do the conversion, the Interlisp-D hardcopy functions generate Interpress or other format output depending on the appropriate choice for the designated printer. The hardcopy functions use the variables PRINTERTYPES and PRINTFILETYPES (described below) to determine the type of a file, how to convert it for a given printer, and how to send it. By changing these variables, the user can define other kinds of printers and print to them using the normal hardcopy functions. (SEND.FILE.TO.PRINTER(SEND.FILE.TO.PRINTER (Function) NIL NIL ("29") 1) FILE HOST PRINTOPTIONS) [Function] The function SEND.FILE.TO.PRINTER causes the file FILE to be sent to the printer HOST. If HOST is NIL, the first host in the list DEFAULTPRINTINGHOST which can print FILE is used. PRINTOPTIONS is a property list of the form (PROP1 VALUE1 PROP2 VALUE2 ...). The properties accepted depends on the type of printer. For Interpress printers, the following properties are accepted: DOCUMENT.NAME The document name to appear on the header page (a string). Default is the full name of the file. DOCUMENT.CREATION.DATE The creation date to appear on the header page (a Lisp integer date, such as returned by IDATE). The default value is the creation date of the file. SENDER.NAME The name of the sender to appear on the header page (a string). The default value is the name of the user. RECIPIENT.NAME The name of the recipient to appear on the header page (a string). The default is none. MESSAGE An additional message to appear on the header page (a string). The default is none. #COPIES The number of copies to be printed. The default value is 1. PAGES.TO.PRINT The pages of the document that should be printed, represented as a list (FIRSTPAGE# LASTPAGE#). For example, if this option is (3 5), this specifies that pages 3 through 5, inclusive, should be printed. Note that the page numbering used for this purpose has no connection to any page numbers that may be printed on the document. The default is to print all of the pages in the document. MEDIUM The medium on which the master is to be printed. If omitted, this defaults to the value of NSPRINT.DEFAULT.MEDIUM, as follows: NIL means to use the printer's default; T means to use the first medium reported available by the printer; any other value must be a Courier value of type MEDIUM. The format of this type is a list (PAPER (KNOWN.SIZE TYPE)) or (PAPER (OTHER.SIZE (WIDTH LENGTH))). The paper TYPE is one of US.LETTER, US.LEGAL, A0 through A10, ISO.B0 through ISO.B10, and JIS.B0 through JIS.B10. For users who use A4 paper exclusively, it should be sufficient to set NSPRINT.DEFAULT.MEDIUM to (PAPER (KNOWN.SIZE "A4")). When using different paper sizes, it may be necessary to reset the variable DEFAULTPAGEREGION, the region on the page used for printing (measured in micas from the lower-left corner). STAPLE? True if the document should be stapled. #SIDES 1 or 2 to indicate that the document should be printed on one or two sides, respectively. The default is the value of EMPRESS#SIDES. PRIORITY The priority of this print request, one of LOW, NORMAL, or HIGH. The default is the printer's default. Note: Press printers only recognize the options #COPIES, #SIDES, DOCUMENT.CREATION.DATE, and DOCUMENT.NAME. For example, (SEND.FILE.TO.PRINTER 'FOO NIL '(#COPIES 3 #SIDES 2 DOCUMENT.NAME "For John")) SEND.FILE.TO.PRINTER calls PRINTERTYPE and PRINTFILETYPE to determine the printer type of HOST and the file format of FILE. If FILE is a formatted file already in a form that the printer can print, it is transmitted directly. Otherwise, CONVERT.FILE.TO.TYPE.FOR.PRINTER is called to do the conversion. [Note: If the file is converted, PRINTOPTIONS is passed to the formatting function, so it can include properties such as HEADING, REGION, and FONTS.] All of these functions use the lists PRINTERTYPES and PRINTFILETYPES to actually determine how to do the conversion. LISTFILES (Chapter 17) calls the function LISTFILES1 to send a single file to a hardcopy printing device. Interlisp-D is initialized with LISTFILES1 defined to call SEND.FILE.TO.PRINTER. (HARDCOPYW(HARDCOPYW (Function) NIL NIL ("29") 2) WINDOW/BITMAP/REGION FILE HOST SCALEFACTOR ROTATION PRINTERTYPE) [Function] Creates a hardcopy file from a bitmap and optionally sends it to a printer. Note that some printers may have limitations concerning how big or how "complicated" the bitmap may be printed. WINDOW/BITMAP/REGION can either be a WINDOW (open or closed), a BITMAP, or a REGION (interpreted as a region of the screen). If WINDOW/BITMAP/REGION is NIL, the user is prompted for a screen region using GETREGION. If FILE is non-NIL, it is used as the name of the file for output. If HOST=NIL, this file is not printed. If FILE is NIL, a temporary file is created, and sent to HOST. To save an image on a file without printing it, perform (HARDCOPYW IMAGE FILE). To print an image to the printer PRINTER without saving the file, perform (HARDCOPYW IMAGE NIL PRINTER). If both FILE and HOST are NIL, the default action is to print the image, without saving the file. The printer used is determined by the argument PRINTERTYPE and the value of the variable DEFAULTPRINTINGHOST. If PRINTERTYPE is non-NIL, the first host on DEFAULTPRINTINGHOST of the type PRINTERTYPE is used. If PRINTERTYPE is NIL, the first printer on DEFAULTPRINTINGHOST that implements the BITMAPSCALE (as determined by PRINTERTYPES) operation is used, if any. Otherwise, the first printer on DEFAULTPRINTINGHOST is used. The type of hardcopy file produced is determined by HOST if non-NIL, else by PRINTERTYPE if non-NIL, else by the value of DEFAULTPRINTINGHOST, as described above. SCALEFACTOR is a reduction factor. If not given, it is computed automatically based on the size of the bitmap and the capabilities of the printer type. This may not be supported for some printers. ROTATION specifies how the bitmap image should be rotated on the printed page. Most printers (including Interpress printers) only support a ROTATION of multiples of 90. PRINTERTYPE specifies what type of printer to use when HOST is NIL. HARDCOPYW uses this information to select which printer to use or what print file format to convert the output into, as described above. The background menu contains a "Hardcopy" command (Chapter 28) that prompts the user for a region on the screen, and sends the image to the default printer. Hardcopy output may also be obtained by writing a file on the printer device LPT, e.g. (COPYFILE 'FOO '{LPT}). When a file on this device is closed, it is converted to Interpress or some other format (if necessary) and sent to the default printer (the first host on DEFAULTPRINTINGHOST). One can include the printer name directly in the file name, e.g. (COPYFILE 'FOO {LPT}TREMOR:) will send the file to the printer TREMOR:. (PRINTERSTATUS(PRINTERSTATUS (Function) NIL NIL ("29") 3) PRINTER) [Function] Returns a list describing the current status of the printer named PRINTER. The exact form of the value returned depends on the type of printer. For InterPress printers, the status describes whether the printer is available or busy or needs attention, and what type of paper is loaded in the printer. Returns NIL if the printer does not respond in a reasonable time, which can occur if the printer is very busy, or does not implement the printer status service. DEFAULTPRINTINGHOST (DEFAULTPRINTINGHOST% (Variable) NIL NIL ("29") 3) [Variable] The variable DEFAULTPRINTINGHOST is used to designate the default printer to be used as the output of printing operations. It should be a list of the known printer host names, for example, (QUAKE LISPPRINT:). If an element of DEFAULTPRINTINGHOST is a list, is interpreted as (PRINTERTYPE HOST), specifying both the host type and the host name. The type of the printer, which determines the protocol used to send to it and the file format it requires, is determined by the function PRINTERTYPE. If DEFAULTPRINTINGHOST is a single printer name, it is treated as if it were a list of one element. (PRINTFILETYPE(PRINTFILETYPE (Function) NIL NIL ("29") 4) FILE %) [Function] Returns the format of the file FILE. Possible values include INTERPRESS, TEDIT, etc. If it cannot determine the file type, it returns NIL. Uses the global variable PRINTFILETYPES. (PRINTERTYPE(PRINTERTYPE (Function) NIL NIL ("29") 4) HOST) [Function] Returns the type of the printer HOST. Currently uses the following heuristic: 1. If HOST is a list, the CAR is assumed to be the printer type and CADR the name of the printer 2. If HOST is a litatom with a non-NIL PRINTERTYPE property, the property value is returned as the printer type 3. If HOST contains a colon (e.g., PRINTER:PARC:XEROX) it is assumed to be an INTERPRESS printer 4. If HOST is the CADR of a list on DEFAULTPRINTINGHOST, the CAR is returned as the printer type 5. Otherwise, the value of DEFAULTPRINTERTYPE is returned as the printer type. Low-level Hardcopy Variables 1 The following variables are used to define how Interlisp should generate hardcopy of different types. The user should only need to change these variables when it is necessary to access a new type of printer, or define a new hardcopy document type (not often). PRINTERTYPES(PRINTERTYPES (Variable) NIL NIL ("29") 4) [Variable] The characteristics of a given printer are determined by the value of the list PRINTERTYPES. Each element is a list of the form (TYPES (PROPERTY1 VALUE1) (PROPERTY2 VALUE2) ...) TYPES is a list of the printer types that this entry addresses. The (PROPERTYn VALUEn) pairs define properties associated with each printer type. The printer properties include the following: CANPRINT Value is a list of the file types that the printer can print directly. STATUS Value is a function that knows how to find out the status of the printer, used by PRINTERSTATUS. PROPERTIES Value is a function which returns a list of known printer properties. SEND Value is a function which invokes the appropriate protocol to send a file to the printer. BITMAPSCALE Value is a function of arguments WIDTH and HEIGHT in bits which returns a scale factor for scaling a bitmap. BITMAPFILE Value is a form which, when evaluated, converts a bitmap to a file format that the printer will accept. Note: The name 8044 is defined on PRINTERTYPES as a synonym for the INTERPRESS printer type. The names SPRUCE, PENGUIN, and DOVER are defined on PRINTERTYPES as synonyms for the PRESS printer type. The printer types FULLPRESS and RAVEN are also defined the same as PRESS, except that these printer types indicate that the printer is a "Full Press" printer that is able to scale bitmap images, in addition to the normal Press printer facilities. PRINTFILETYPES(PRINTFILETYPES (Variable) NIL NIL ("29") 5) [Variable] The variable PRINTFILETYPES contains information about various file formats, such as Tedit files and Interpress files. The format is similar to PRINTERTYPES. The properties that can be specified include: TEST Value is a function which tests a file if it is of the given type. Note that this function is passed an open stream. CONVERSION Value is a property list of other file types and funcitons that convert from the specified type to the file format. EXTENSION Value is a list of possible file extensions for files of this type. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "29-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "29-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "29-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "29-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "29-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "29-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))2l2l~~,-T3(T552ll,HH +/ll0llT3H` +T2l5,ll3HH +T,HH,HH,2F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR TITAN + HELVETICACLASSIC +CLASSIC +TITAN + HELVETICA +CLASSIC +MODERN +MODERN +MODERNMODERN + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + HRULE.GETFNCLASSIC + + IM.CHAP.GETFNMODERN + HRULE.GETFNMODERN + }- 2IM.INDEX.GETFNMODERN +    +  {   b Z8  l  Y  U  =  H + "  \%r%        JN[  '   w   +) 1  4  "k D L) 1 ! +W + +'IM.INDEX.GETFNMODERN +?  + +.1  +5$2 +B%. +u       > + +4 +  +  + + ,  + +ME# +IM.INDEX.GETFNMODERN + +  B +  3IM.INDEX.GETFN  +    +M +IM.INDEX.GETFNMODERN +  + +9 )IM.INDEX.GETFN  + +' > + !" HRULE.GETFNMODERN +   *IM.INDEX.GETFNMODERN +  +O &   +@ < +.HS  +G[ "< +i  + "  ,IM.INDEX.GETFNMODERN +  v 1 w u E12bz \ No newline at end of file diff --git a/docs/porter-irm/vol3/30-TERMINAL.INTERPRESS b/docs/porter-irm/vol3/30-TERMINAL.INTERPRESS new file mode 100644 index 00000000..64aee6de Binary files /dev/null and b/docs/porter-irm/vol3/30-TERMINAL.INTERPRESS differ diff --git a/docs/porter-irm/vol3/30-TERMINAL.TEDIT b/docs/porter-irm/vol3/30-TERMINAL.TEDIT new file mode 100644 index 00000000..913949bc Binary files /dev/null and b/docs/porter-irm/vol3/30-TERMINAL.TEDIT differ diff --git a/docs/porter-irm/vol3/31-ETHERNET.INTERPRESS b/docs/porter-irm/vol3/31-ETHERNET.INTERPRESS new file mode 100644 index 00000000..89e5a6d6 Binary files /dev/null and b/docs/porter-irm/vol3/31-ETHERNET.INTERPRESS differ diff --git a/docs/porter-irm/vol3/31-ETHERNET.TEDIT b/docs/porter-irm/vol3/31-ETHERNET.TEDIT new file mode 100644 index 00000000..37929906 --- /dev/null +++ b/docs/porter-irm/vol3/31-ETHERNET.TEDIT @@ -0,0 +1,117 @@ +1 Interlisp-D Reference Manual 1 Interlisp-D Reference Manual 31. ETHERNET 1 31. ETHERNET 1 "31"31. ETHERNET 6 Interlisp was first developed on large timesharing machines which provided each user with access to large amounts of disk storage, printers, mail systems, etc. Interlisp-D, however, was designed to run on smaller, single-user machines without these facilities. In order to provide Interlisp-D users with access to all of these services, Interlisp-D supports the Ethernet communications network, which allows multiple Interlisp-D machines to share common printers, file servers, etc. Interlisp-D supports the Experimental Ethernet (3 Megabits per second) and the Ethernet (10 Megabits per second) local communications networks. These networks may be used for accessing file servers, remote printers, mail servers, or other machines. This chapter is divided into three sections: First, an overview of the various Ethernet and Experimental Ethernet protocols is presented. Then follow sections documenting the functions used for implementing PUP and NS protocols at various levels. Ethernet(ETHERNET NIL Ethernet NIL NIL 1 SUBNAME PROTOCOLS SUBTEXT protocols) Protocols 1 The members of the Xerox 1100 family (1108, 1132), Xerox file servers and laser xerographic printers, along with machines made by other manufacturers (most notably DEC) have the capability of communicating over 3 Megabit per second Experimental Ethernets, 10 Megabit per second Ethernets and telephone lines. Xerox pioneered its work with Ethernet using a set of protocols known as PARC Universal Packet (PUP) computer communication protocols. The architecture has evolved into the newer Network Systems (NS) protocols developed for use in Xerox office products. All of the members of the Xerox 1100 family can use both NS and PUP protocols. Protocol Layering(ETHERNET NIL Ethernet NIL NIL 1 SUBNAME PROTOCOLS SUBTEXT protocols SUBSUBNAME LAYERING SUBSUBTEXT layering) The communication protocols used by the members of the Xerox 1100 family are implemented in a "layered" fashion, which means that different levels of communication are implemented as different protocol layers. Protocol Layering allows implementations of specific layers to be changed without requiring changes to any other layers. The layering also allows use of the same higher level software with different lower levels of protocols. Protocol designers can implement new types of protocols at the correct protocol level for their specific application in a layered system. At the bottom level, level zero, there is a need to physically transmit data from one point to another. This level is highly dependent on the particular transmission medium involved. There are many different level zero protocols, and some of them may contain several internal levels. At level one, there is a need to decide where the data should go. This level is concerned with how to address a source and destination, and how to choose the correct transmission medium to use in order to route the packet towards its destination. A level one packet is transmitted by encapsulating it in the level zero packet appropriate for the transmission medium selected. For each independent communication protocol system, a single level one protocol is defined. The rule for delivery of a level one packet is that the communication system must only make a best effort to deliver the packet. There is no guarantee that the packet is delivered, that the packet is not duplicated and delivered twice, or that the packets will be delivered in the same order as they were sent. The addresses used in level zero and level one packets are not necessarily the same. Level zero packets are specific to a particular transmission medium. For example, the destination address of a level zero packet transmitted on one of the two kinds of Ethernet is the Ethernet address (host number) of a machine on the particular network. Level one packets specify addresses meaningful to the particular class of protocols being implemented. For the PUP and NS protocols, the destination address comprises a network number, host number (not necessarily the same as the level zero host number), and a socket number. The socket number is a higher-level protocol concept, used to multiplex packets arriving at a single machine destined for separate logical processes on the machine. Protocols in level two add order and reliability to the level one facilities. They suppress duplicate packets, and are responsible for retransmission of packets for which acknowledgement has not been received. The protocol layers above level two add conventions for data structuring, and implement application specific protocols. Level Zero Protocols(ETHERNET NIL Ethernet NIL NIL 2 SUBNAME PROTOCOLS SUBTEXT protocols SUBSUBNAME LEVEL% ZERO SUBSUBTEXT level% zero) Level zero protocols are used to physically connect computers. The addresses used in level zero protocols are protocol specific. The Ethernet and Experimental Ethernet level zero protocols use host numbers, but level zero phone line protocols contain less addressing information since there are only two hosts connected to the telephone line, one at each end. As noted above, a level zero protocol does not include network numbers. The 3MB Experimental Ethernet [1] was developed at PARC. Each Experimental Ethernet packet includes a source and destination host address of eight bits. The Experimental Ethernet standard is used by any machine attached to an Experimental Ethernet. The 10MB Ethernet [2] was jointly developed and standardized by Digital, Intel, and Xerox. Each Ethernet level zero packet includes a source and destination host address that is 48 bits long. The Ethernet standard is used by any machine attached to an Ethernet. Both of the level one protocols described later (PUP and NS) can be transported on any of the level zero protocols described above. The Ethernet and Experimental Ethernet protocols are broadcast mediums. Data packets can be sent on these networks to every host attached to the net. A packet directed at every host on a network is a broadcast packet. Other Level 0 protocols in use in industry include X.25, broadband networks, and Chaosnet. In addition, by using the notion of "mutual encapsulation", it is possible to treat a higher-level protocol (e.g. ARPANET) as if it were a Level Zero Protocol. Level One Protocols(ETHERNET NIL Ethernet NIL NIL 2 SUBNAME PROTOCOLS SUBTEXT protocols SUBSUBNAME LEVEL% ONE SUBSUBTEXT level% one) Two Level One Protocols are used in the Xerox 1100 Family, the PUP and the NS protocols. With the proper software, computers attached to Ethernets or Experimental Ethernets can send PUPs and NS packets to other computers on the same network, and to computers attached to other Ethernets or Experimental Ethernets. The PUP protocols [3] were designed by Xerox computer scientists at the Palo Alto Research Center. The destination and source addresses in a PUP packet are specified using an 8-bit network number, an 8-bit host number, and a 32-bit socket number. The 8-bit network number allows an absolute maximum of 256 PUP networks in an internet. The 8-bit host number is network relative. That is, there may be many host number "1"s, but only one per network. 8 bits for the host number limits the number of hosts per network to 256. The socket number is used for further levels of addressing within a specific machine. The Network Systems (NS) protocols [4, 5] were developed by the Xerox Office Products Division. Each NS packet address includes a 32-bit network number, a 48-bit host number, and a 16-bit socket number. The NS host and network numbers are unique through all space and time. A specific NS host number is generally assigned to a machine when it is manufactured, and is never changed. In the same fashion, all networks (including those sold by Xerox and those used within Xerox) use the same network numbering space---there is only one network "74". Higher Level Protocols(ETHERNET NIL Ethernet NIL NIL 3 SUBNAME PROTOCOLS SUBTEXT protocols SUBSUBNAME HIGHER% LEVEL SUBSUBTEXT higher% level) The higher level PUP protocols include the File Transfer Protocol (FTP) and the Leaf Protocol used to send and retrieve files from Interim File Servers (IFSs) and DEC File Servers, the Telnet protocol implemented by "Chat" windows and servers, and the EFTP protocol used to communicate with the laser xerographic printers developed by PARC ("Dovers" and "Penguins"). The higher level NS protocols include the Filing Protocol which allows workstations to access the product File Services sold by Xerox, the Clearinghouse Protocol used to access product Clearinghouse Services, and the TelePress Protocol used to communicate with the Xerox model 8044 Print Server. Connecting Networks: Routers and Gateways When a level one packet is sent from one machine to another, and the two machines are not on the same network, the packet must be passed between networks. Computers that are connected to two or more level zero mediums are used for this function. In the PUP world, these machines have been historically called "Gateways." In the NS world these machines are called Internetwork Routers (Routers), and the function is packaged and sold by Xerox as the Internetwork Routing Service (IRS). Every host that uses the PUP protocols requires a PUP address; NS Hosts require NS addresses. An address consists of two parts: the host number and the network number. A computer learns its network number by communicating with a Router or Gateway that is attached to the same network. Host number determination is dependent on the hardware and the type of host number, PUP or NS. Note that there is absolutely no relationship between a host's NS host and net numbers and the same host's PUP host and net numbers. Addressing Conflicts with Level Zero Mediums For convenience in the respective protocols, a level one PUP (8-bit) host number is the same as a level zero Experimental Ethernet host number; i.e., when a PUP level one packet is transported by an Experimental Ethernet to another host on the same network, the level zero packet specifies the same host number as the level one packet. Similarly, a level one NS (48-bit) host number is the same as a level zero Ethernet host number. When a PUP level one packet is transported by an Ethernet, or an NS level one packet is sent on Experimental Ethernet, the level one host number cannot be used as the level zero address, but rather some means must be provided to determine the correct level zero address. Xerox solved this problem by specifying another level-one protocol called translation to allow hosts on an Experimental Ethernet to announce their NS host numbers, or hosts on an Ethernet to announce their PUP host numbers. Thus, both the Ethernet and Experimental Ethernet Level Zero Protocols totally support both families of higher level protocols. References [1] Robert M. Metcalfe and David R. Boggs, Ethernet: Distributed Packet Switching for Local Computer Networks, Communications of the ACM, vol. 19 no. 7, July 1976. [2] Digital Equipment Corporation, Intel Corporation, Xerox Corporation. The Ethernet, A Local Area Network: Data Link Layer and Physical Layer Specifications. September 30, 1980, Version 1.0 [3] D. R. Boggs, J. F. Shoch, E. A. Taft, and R. M. Metcalfe, PUP: An Internetwork Architecture, IEEE Transactions on Communications, com-28:4, April 1980. [4] Xerox Corporation. Courier: The Remote Procedure Call Protocol. Xerox System Integration Standard. Stamford, Connecticut, December, 1981, XSIS 038112. [5] Xerox Corporation. Internet Transport Protocols. Xerox System Integration Standard. Stamford, Connecticut, December, 1981, XSIS 028112. Higher Level PUP Protocol Functions(ETHERNET NIL Ethernet NIL NIL 4 SUBNAME PUP% PROTOCOL% FUNCTIONS SUBTEXT Pup% protocol% functions) 1 This section describes some of the functions provided in Interlisp-D to perform protocols above Level One. Level One functions are described in a later section, for the benefit of those users who wish to program new protocols. (ETHERHOSTNUMBER(ETHERHOSTNUMBER (Function) NIL NIL NIL 4) NAME) [Function] Returns the number of the named host. The number is 16-bit quantity, the high 8 bits designating the net and the low 8 bits the host. If NAME is NIL, returns the number of the local host. (ETHERPORT(ETHERPORT (Function) NIL NIL NIL 4) NAME ERRORFLG MULTFLG) [Function] Returns a port corresponding to NAME. A "port" is a network address that represents (potentially) one end of a network connection, and includes a socket number in addition to the network and host numbers. Most network functions that take a port as argument allow the socket to be zero, in which case a well-known socket is supplied. A port is currently represented as a dotted pair (NETHOST . SOCKET). NAME may be a litatom, in which case its address is looked up, or a port, which is just returned directly. If ERRORFLG is true, generates an error "host not found" if the address lookup fails, else it returns NIL. If MULTFLG is true, returns a list of alternative port specifications for NAME, rather than a single port (this is provided because it is possible for a single name in the name database to have multiple addresses). If MULTFLG is NIL and NAME has more than one address, the currently nearest one is returned. ETHERPORT caches its results. The SOCKET of a port is usually zero, unless the name explicitly contains a socket designation, a number or symbolic name following a + in NAME, e.g., PHYLUM+LEAF. A port can also be specified in the form "NET#HOST#SOCKET", where each of NET, HOST and SOCKET is a sequence of octal digits; the socket, but not the terminating #, can be omitted, in which case the socket is zero. (ETHERHOSTNAME(ETHERHOSTNAME (Function) NIL NIL NIL 5) PORT USE.OCTAL.DEFAULT) [Function] Looks up the name of the host at address PORT. PORT may be a numeric address, a (NETHOST . SOCKET) pair returned from ETHERPORT, or a numeric designation in string form, "NET#HOST#SOCKET", as described above. In the first case, the net defaults to the local net. If PORT is NIL, returns the name of the local host. If there is no name for the given port, but USE.OCTAL.DEFAULT is true, the function returns a string specifying the port in octal digits, in the form "NET#HOST#SOCKET", with SOCKET omitted if it is zero. Most functions that take a port argument will also accept ports in this octal format. (EFTP(EFTP (Function) NIL NIL NIL 5) HOST FILE PRINTOPTIONS) [Function] Transmits FILE to HOST using the EFTP protocol. The FILE need not be open on entry, but in any case is closed on exit. EFTP returns only on success; if HOST does not respond, it keeps trying. The principal use of the EFTP protocol is for transmitting Press files to a printer. If PRINTOPTIONS is non-NIL, EFTP assumes that HOST is a printer and FILE is a Press file, and takes additional action: it calls PRINTERSTATUS (see Chapter 29) for HOST and prints this information to the prompt window; and it fills in the "printed-by" field on the last page of the press file with the value of USERNAME (see Chapter 24). Also, PRINTOPTIONS is interpreted as a list in property list format that controls details of the printing. Possible properties are as follows: #COPIES Value is the number copies of the file to print. Default is one. #SIDES If the value is 2, select two-sided printing (if the printer can print two-sided copies). DOCUMENT.CREATION.DATE Value is the document creation date to appear on the header page (an integer date as returned by IDATE). DOCUMENT.NAME Value is the document name to appear on the header page (as a string). Default is the full name of the file. Higher Level NS Protocol Functions(ETHERNET NIL Ethernet NIL NIL 5 SUBNAME NS% PROTOCOL% FUNCTIONS SUBTEXT NS% protocol% functions) 1 The following is a description of the Interlisp-D facilities for using Xerox SPP and Courier protocols and the services based on them. The sections on naming conventions, Printing, and Filing are of general interest to users of Network Systems servers. The remaining sections describe interfaces of interest to those who wish to program other applications on top of either Courier or SPP. Name and Address Conventions Addresses of hosts in the NS world consist of three parts, a network number, a machine number, and a socket number. These three parts are embodied in the Interlisp-D data type NSADDRESS. Objects of type NSADDRESS print as "net#h1.h2.h3#socket", where all the numbers are printed in octal radix, and the 48-bit host number is broken into three 16-bit fields. Most functions that accept an address argument will accept either an NSADDRESS object or a string that is the printed representation of the address. Higher-level functions accept host arguments in the form of a symbolic name for the host. The NS world has a hierarchical name space. Each object name is in three parts: the Organization, the Domain, and the Object parts. There can be many domains in a single organization, and many objects in a single domain. The name space is maintained by the Clearinghouse, a distributed network database service. A Clearinghouse name is standardly notated as object:domain:organization. The parts organization or domain:organization may be omitted if they are the default (see below). Alphabetic case is not significant. Internally, names are represented as objects of data type NSNAME, but most functions accept the textual representation as well, either as a litatom or a string. Objects of type NSNAME print as object:domain:organization, with fields omitted when they are equal to the default. A Domain is standardly represented as an NSNAME in which the object part is null. If frequent use is to be made of an NS name, it is generally preferable to convert it to an NSNAME once, by calling PARSE.NSNAME, then passing the resultant object to all functions desiring it. CH.DEFAULT.ORGANIZATION(CH.DEFAULT.ORGANIZATION (Variable) NIL NIL NIL 6) [Variable] This is a string specifying the default Clearinghouse organization. CH.DEFAULT.DOMAIN(CH.DEFAULT.DOMAIN (Variable) NIL NIL NIL 6) [Variable] This is a string specifying the default Clearinghouse domain. If it or the variable CH.DEFAULT.ORGANIZATION is NIL, they are set by Lisp system code (when they are needed) to be the first domain served by the nearest Clearinghouse server. In small organizations with just one domain, it is reasonable to just leave these variables NIL and have the system set them appropriately. In organizations with more than one domain, it is wise to set them in the site initialization file, so as not to be dependent on exactly which Clearinghouse servers are up at any time. (PARSE.NSNAME(PARSE.NSNAME (Function) NIL NIL NIL 6) NAME #PARTS DEFAULTDOMAIN) [Function] When #PARTS is 3 (or NIL), parses NAME, a litatom or string, into its three parts, returning an object of type NSNAME. If the domain or organization is omitted, defaults are supplied, either from DEFAULTDOMAIN (an NSNAME whose domain and organization fields only are used) or from the variables CH.DEFAULT.DOMAIN and CH.DEFAULT.ORGANIZATION. If #PARTS is 2, NAME is interpreted as a domain name, and an with null object is returned. In this case, if NAME is a full 3-part name, the object part is stripped off. If #PARTS is 1, NAME is interpreted as an organization name, and a simple string is returned. In this case, if NAME is a 2- or 3-part name, the organization is extracted from it. If NAME is already an object of type , then it is returned as is (if #PARTS is 3), or its domain and/or organization parts are extracted (if #PARTS is 1 or 2). (NSNAME.TO.STRING(NSNAME.TO.STRING (Function) NIL NIL NIL 6) NSNAME FULLNAMEFLG) [Function] Converts NSNAME, an object of type , to its string representation. If FULLNAMEFLG is true, the full printed name is returned; otherwise, fields that are equal to the default are omitted. Programmers who wish to manipulate NSADDRESS and NSNAME objects directly should load the Library package ETHERRECORDS. Clearinghouse Functions This section describes functions that may be used to access information in the Clearinghouse. (START.CLEARINGHOUSE(START.CLEARINGHOUSE (Function) NIL NIL NIL 7) RESTARTFLG) [Function] Performs an expanding ring broadcast in order to find the nearest Clearinghouse server, whose address it returns. If a Clearinghouse has already been located, this function simply returns its address immediately, unless RESTARTFLG is true, in which case the cache of Clearinghouse information is invalidated and a new broadcast is performed. START.CLEARINGHOUSE is normally performed automatically by the system the first time it needs Clearinghouse information; however, it may be necessary to call it explicitly (with RESTARTFLG set) if the local Clearinghouse server goes down. CH.NET.HINT(CH.NET.HINT (Variable) NIL NIL NIL 7) [Variable] A number or list of numbers, giving a hint as to which network the nearest Clearinghouse server is on. When START.CLEARINGHOUSE looks for a Clearinghouse server, it probes the network(s) given by CH.NET.HINT first, performing the expanding ring broadcast only if it fails there. If the nearest Clearinghouse server is not on the directly connected network, setting CH.NET.HINT to the proper network number in the local site init file (see Chapter 12) can speed up START.CLEARINGHOUSE considerably. (SHOW.CLEARINGHOUSE(SHOW.CLEARINGHOUSE (Function) NIL NIL NIL 7) ENTIRE.CLEARINGHOUSE? DONT.GRAPH) [Function] This function displays the structure of the cached Clearinghouse information in a window. Once created, it will be redisplayed whenever the cache is updated, until the window is closed. The structure is shown using the Library package GRAPHER. If ENTIRE.CLEARINGHOUSE? is true, then this function probes the Clearinghouse to discover the entire domain:organization structure of the Internet, and graphs the result. If DONT.GRAPH is true, the structure is not graphed, but rather the results are returned as a nested list indicating the structure. (LOOKUP.NS.SERVER(LOOKUP.NS.SERVER (Function) NIL NIL NIL 7) NAME TYPE FULLFLG) [Function] Returns the address, as an NSADDRESS, for the object NAME. TYPE is the property under which the address is stored, which defaults to ADDRESS.LIST. The information is cached so that it need not be recomputed on each call; the cache is cleared by restarting the Clearinghouse. If FULLFLG is true, returns a list whose first element is the canonical name of NAME and whose tail is the address list. The following functions perform various sorts of retrieval operations on database entries in the Clearinghouse. Here, "The Clearinghouse" refers to the collective service offered by all the Clearinghouse servers on an internet; Lisp internally deals with which actual server(s) it needs to contact to obtain the desried information. The argument(s) describing the objects under consideration can be strings or NSNAME's, and in most cases can contain the wild card "*", which matches a subsequence of zero or more characters. Wildcards are permitted only in the most specific field of a name (e.g., in the object part of a full three-part name). When an operation intended for a single object is instead given a pattern, the operation is usually performed on the first matching object in the database, which may or may not be interesting. (CH.LOOKUP.OBJECT(CH.LOOKUP.OBJECT (Function) NIL NIL NIL 8) OBJECTPATTERN) [Function] Looks up OBJECTPATTERN in the Clearinghouse database, returning its canonical name (as an NSNAME) if found, NIL otherwise. If OBJECTPATTERN contains a "*", returns the first matching name. (CH.LIST.ORGANIZATIONS(CH.LIST.ORGANIZATIONS (Function) NIL NIL NIL 8) ORGANIZATIONPATTERN) [Function] Returns a list of organization names in the Clearinghouse database matching ORGANIZATIONPATTERN. The default pattern is "*", which matches anything. (CH.LIST.DOMAINS(CH.LIST.DOMAINS (Function) NIL NIL NIL 8) DOMAINPATTERN) [Function] Returns a list of domain names (two-part NSNAME's) in the Clearinghouse database matching DOMAINPATTERN. The default pattern is "*", which matches anything in the default organization. (CH.LIST.OBJECTS(CH.LIST.OBJECTS (Function) NIL NIL NIL 8) OBJECTPATTERN PROPERTY) [Function] Returns a list of object names matching OBJECTPATTERN and having the property PROPERTY. PROPERTY is a number or a symbolic name for a Clearinghouse property; the latter include USER, PRINT.SERVICE, FILE.SERVICE, MEMBERS, ADDRESS.LIST and ALL. For example, (CH.LIST.OBJECTS "*:PARC:Xerox" (QUOTE USER)) returns a list of the names of users in the domain PARC:Xerox. (CH.LIST.OBJECTS "*lisp*:PARC:Xerox" (QUOTE MEMBERS)) returns a list of all group names in PARC:Xerox containing the substring "lisp". (CH.LIST.ALIASES(CH.LIST.ALIASES (Function) NIL NIL NIL 8) OBJECTNAMEPATTERN) [Function] Returns a list of all objects in the Clearinghouse database that are aliases and match OBJECTNAMEPATTERN. (CH.LIST.ALIASES.OF(CH.LIST.ALIASES.OF (Function) NIL NIL NIL 8) OBJECTPATTERN) [Function] Returns a list of all objects in the Clearinghouse database that are aliases of OBJECTPATTERN. (CH.RETRIEVE.ITEM(CH.RETRIEVE.ITEM (Function) NIL NIL NIL 8) OBJECTPATTERN PROPERTY INTERPRETATION) [Function] Retrieves the value of the PROPERTY property of OBJECTPATTERN. Returns a list of two elements, the canonical name of the object and the value. If INTERPRETATION is given, it is a Clearinghouse type (see the Constructed Types section below) with which to interpret the bits that come back; otherwise, the value is simply of the form (SEQUENCE UNSPECIFIED), a list of 16-bit integers representing the value. (CH.RETRIEVE.MEMBERS(CH.RETRIEVE.MEMBERS (Function) NIL NIL NIL 8) OBJECTPATTERN PROPERTY %) [Function] Retrieves the members of the group OBJECTPATTERN, as a list of NSNAMEs. PROPERTY is the Clearinghouse Group property under which the members are stored; the usual property used for this purpose is MEMBERS. (CH.ISMEMBER(CH.ISMEMBER NIL NIL NIL NIL 9) GROUPNAME PROPERTY SECONDARYPROPERTY NAME) [Function] Tests whether NAME is a member of GROUPNAME's PROPERTY property. This is a potentially complex operation; see the description of procedure IsMember in the Clearinghouse Protocol documentation for details. NS Printing This section describes the facilities that are available for printing Interpress masters on NS Print servers. (NSPRINT(NSPRINT (Function) NIL NIL NIL 9) PRINTER FILE OPTIONS) [Function] This function prints an Interpress master on PRINTER, which is a Clearinghouse name represented as a string or NSNAME. If PRINTER is NIL, NSPRINT uses the first print server registered in the default domain. FILE is the name of an Interpress file to be printed. OPTIONS is a list in property list format that controls details of the printing (see SEND.FILE.TO.PRINTER, Chapter 29). (NSPRINTER.STATUS(NSPRINTER.STATUS (Function) NIL NIL NIL 9) PRINTER) [Function] This function returns a list describing the printer's current status; whether it is available or busy, and what kind of paper is loaded. (NSPRINTER.PROPERTIES(NSPRINTER.PROPERTIES (Function) NIL NIL NIL 9) PRINTER) [Function] This function returns a list describing the printer's capabilities at the moment; the type of paper loaded, whether it can print two-sided, etc. SPP Stream Interface This section describes the stream interface to the Sequenced Packet Protocol. SPP is the transport protocol for Courier, which in turn is the transport layer for Filing and Printing. (SPP.OPEN(SPP.OPEN (Function) NIL NIL NIL 9) HOST SOCKET PROBEP NAME PROPS) [Function] This function is used to open a bidirectional SPP stream. There are two cases: user and server. User: If HOST is specified, an SPP connection is initiated to HOST, an NSADDRESS or string representing an NS address. If the socket part of the address is null (zero), it is defaulted to SOCKET. If both HOST and PROBEP are specified, then the connection is probed for a response before returning the stream; NIL is returned if HOST doesn't respond. Server: If HOST is NIL, a passive connection is created which listens for an incoming connection to local socket SOCKET. SPP.OPEN returns the input side of the bidirectional stream; the function SPPOUTPUTSTREAM is used to obtain the output side. The standard stream operations BIN, READP, EOFP (on the input side), and BOUT, FORCEOUTPUT (on the output side), are defined on these streams, as is CLOSEF, which can be applied to either stream to close the connection. NAME is a mnemonic name for the connection process, mainly useful for debugging. PROPS is an optional property list, used to set the properties that determine the behavior of the SPP stream when certain events occur. The following properties can be specified: CLOSEFN A function or list of functions called (with the stream as argument) when an SPP connection is closed. ATTENTIONFN A function called (with the stream as argument) when an ATTENTION packet is received on the SPP connection. ERRORHANDLER A function called (with the stream as argument) when an error (such as end-of-stream) occurs on the SPP connection. OTHERXIPHANDLER A function called (with the stream as argument) when a non-SPP, non-error packet is received on the socket associated with the SPP connection. EOM.ON.FORCEOUTPUT The value of this property should be either T or NIL (the default). If T, then the end-of-message bit is set when the current collection of bytes buffered for transmission is forcibly sent (e.g. by FORCEOUTPUT, Chapter 25). SERVER.FUNCTION This property can be used for creating SPP servers. Normally, when a connection is opened with the HOST argument set to NIL, a passive "listener" connection is created. SPP.OPEN will not return until some other host attempts to connect to socket specified in the SPP.OPEN call. If the SERVER.FUNCTION property is specified, a new listener (and listener process) is created. SPP.OPEN will return immediately. Whenever another host attempts to connect to the specified socket, a new process and unique SPP connection are created. The function specified by the SERVER.FUNCTION property is run in the top level of the new process. The server function should be a function of two arguments: the first argument is the SPP input stream associated with the connection; the second argument is the SPP output stream associated with the connection. (SPPOUTPUTSTREAM(SPPOUTPUTSTREAM (Function) NIL NIL NIL 10) STREAM) [Function] Applied to the input stream of an SPP connection, this function returns the corresponding output stream. SPP.USER.TIMEOUT(SPP.USER.TIMEOUT (Variable) NIL NIL NIL 10) [Variable] Specifies the time, in milliseconds, to wait before deciding that a host isn't responding. (SPP.DSTYPE(SPP.DSTYPE (Function) NIL NIL NIL 10) STREAM DSTYPE) [Function] Accesses the current datastream type of the connection. If DSTYPE is NIL, returns the datastream type of the current packet being read. If DSTYPE is non-NIL, sets the datastream type of all subsequent packets sent on this connection, until the next call to SPP.DSTYPE. Since this affects the current partially-filled packet, the stream should probably be flushed (via FORCEOUTPUT) before this function is called. (SPP.SENDEOM(SPP.SENDEOM (Function) NIL NIL NIL 10) STREAM) [Function] Transmits the data buffered so far on the output stream STREAM, if any, with the End of Message bit set. If there is nothing buffered, sends a zero-length packet with the End of Message bit set. (SPP.SENDATTENTION(SPP.SENDATTENTION (Function) NIL NIL NIL 11) STREAM ATTENTIONBYTE %) [Function] Sends an SPP "attention" packet on the output stream STREAM, with the Attention bit set and containing the single byte of data ATTENTIONBYTE. The appropriate way to determine whether an SPP stream is open, or whether an End of Message or Attention indication has been reached (for input streams) is to use the EOFP function (Chapter 25). When EOFP is applied to an SPP stream, it returns one of the following values: NIL The connection is open and readable or writable. T The connection is closed. EOM (Input streams only) The End of Message bit was set in the last packet received, and all bytes from the packet have been read. The function SPP.CLEAREOM (below) must be called to clear this condition. ATTENTION (Input streams only) An attention packet is waiting. SPP.CLEARATTENTION (below) must be called before the single byte of data associated with the attention packet can be read. (SPP.CLEAREOM(SPP.CLEAREOM (Function) NIL NIL NIL 11) STREAM NOERRORFLG) [Function] Clears the End of Message indication on STREAM. This is necessary in order to read beyond the EOM. Causes an error if the stream is not currently at the End of Message, unless NOERRORFLG is non-NIL. (SPP.CLEARATTENTION(SPP.CLEARATTENTION (Function) NIL NIL NIL 11) STREAM NOERRORFLG) [Function] Clears the Attention packet indication on STREAM. This must be called before the single byte of data associated with the attention packet can be read. Causes an error if the stream does not have an attention packet waiting, unless NOERRORFLG is non-NIL. Courier Remote Procedure Call Protocol Courier is the Xerox Network Systems Remote Procedure Call protocol. It uses the Sequenced Packet Protocol for reliable transport. Courier uses procedure call as a metaphor for the exchange of a request from a user process and its positive reply from a server process; exceptions or error conditions are the metaphor for a negative reply. A family of remote procedures and the errors they can raise constitute a remote program. A remote program generally represents a complete service, such as the Filing or Printing programs described earlier in this chapter. For more detail about Courier, the reader is referred to the published specification of the Courier protocol. The following documentation assumes some familiarity with the protocol. It describes how to define a Courier program and use it to communicate with a remote system element that implements a server for that program. This section does not discuss how to construct such a server. Defining Courier Programs A Courier program definition is accessed using the file package type COURIERPROGRAMS, so GETDEF, PUTDEF, and EDITDEF can be used to manipulate Courier programs. The file package command COURIERPROGRAMS (Chapter 17) can be used to save Courier programs on files. Courier program are initially defined using the following function: (COURIERPROGRAM(COURIERPROGRAM (Function) NIL NIL NIL 12) NAME ...) [NLambda NoSpread Function] This function is used to define Courier programs. The syntax is (COURIERPROGRAM NAME (PROGRAMNUMBER VERSIONNUMBER) . DEFINITIONS) The tail DEFINITIONS is a property list where the properties are selected from TYPES, PROCEDURES, ERRORS and INHERITS; the values are lists of pairs of the form (LABEL . DEFINITION). These are described in more detail as follows: The TYPES section lists the symbolically-defined types used to represent the arguments and results of procedures and errors in this Courier program. Each element in this section is of the form (TYPENAME TYPEDEFINITION), e.g., (PRIORITY INTEGER). The TYPEDEFINITION can be a predefined type (see next section), another type defined in this TYPES section, or a qualified typename taken from another Courier program; these latter are written as a dotted pair (PROGRAMNAME . TYPENAME). The PROCEDURES section lists the remote procedures defined by this Courier program. A procedure definition is a stylized reduction of the Courier definition syntax defined in the Courier Protocol specification: (PROCEDURENAME NUMBER ARGUMENTS RETURNS RESULTTYPES REPORTS ERRORNAMES) ARGUMENTS is a list of type names, one per argument to the remote procedure, or NIL if the procedure takes no arguments. RESULTTYPES is a list of type names, one for each value to be returned. ERRORNAMES is a list of names of errors that can be raised by this procedure; each such error must be listed in the program's ERRORS section. The atoms RETURNS and REPORTS are noise words to aid readability. The ERRORS section lists the errors that can be raised by procedures in this program. An error definition is of the form (ERRORNAME NUMBER ARGUMENTS) where ARGUMENTS is a list of type names, one for each argument, if any, reported by the error. The INHERITS section is an optional list of other Courier programs, some of whose definitions are "inherited" by this program. More specifically, if a type, procedure or error referenced in the current program definition is not defined in this program, the system searches for a definition of it in each of the inherited programs in turn, and uses the first such definition found. The INHERITS section is useful when defining variants of a given Courier program. For example, if one wanted to try out version 4 of Courier program BAR, and version 4 differed from version 3 of program BAR only in a small number of procedure or type definitions, one could define a program NEWBAR with an INHERITS section of (BAR) and only need to list the few changed definitions inside NEWBAR. Courier Type Definitions This section describes how the Courier types described in the Courier Protocol document are expressed in a Lisp Courier program definition, and how values of each type are represented. Each type in a Courier program's TYPES section must ultimately be defined in terms of one of the following "base" types, although the definition can be indirect through arbitrarily many levels. That is, a type can be defined in terms of any other type known by an extant Courier definition. The names of the base types are "global"; they need no qualification, nor do type names mentioned in the same Courier program. To refer to a type not defined in the same Courier program (or to any non-base type when there is no program context), one writes a Qualified name, in the form (PROGRAM . TYPE). In general, a Qualified name is legal in any place that calls for a Courier type. Pre-defined Types Pre-defined (atomic) types are expressed as uppercase litatoms from the following set: BOOLEAN Values are represented by T and NIL. INTEGER Values are represented as small integers in the range [-32768..32767]. CARDINAL Values are represented as small integers in the range [0..65535]. UNSPECIFIED Same as CARDINAL. LONGINTEGER Values are represented as FIXP's. LONGCARDINAL Same as LONGINTEGER. Note that Interlisp-D does not (currently) have a datatype that truly represents a 32-bit unsigned integer. STRING Values are represented as Lisp strings. In addition, the following types not in the document have been added for convenience: TIME Represents a date and time in accordance with the Network Time Standard. The value is a FIXP such as returned by the function IDATE, and is encoded as a LONGCARDINAL. NSADDRESS Represents a network address. The value is an object of type NSADDRESS (see the Higher-Level NS Protocol Functions section above), and is encoded as six items of type UNSPECIFIED. NSNAME Represents a three-part Clearinghouse name. The value is an object of type NSNAME (see the Higher-Level NS Protocol Functions section above), and is encoded as three items of type STRING. NSNAME2 Represents a two-part Clearinghouse name, i.e., a domain. The value is an object of type NSNAME (see the Higher-Level NS Protocol Functions section above), and is encoded as two items of type STRING. Constructed Types Constructed Types are composite objects made up of elements of other types. They are all expressed as a list whose CAR names the type and whose remaining elements give details. The following are available: (ENUMERATION (NAME INDEX) ... (NAME INDEX)) Each NAME is an arbitrary litatom or string; the corresponding INDEX is its Courier encoding (a CARDINAL). Values of type ENUMERATION are represented as a NAME from the list of choices. For example, a value of type (ENUMERATION (UNKNOWN 0) (RED 1) (BLUE 2)) might be the litatom RED. (SEQUENCE TYPE) A SEQUENCE value is represented as a list, each element being of type TYPE. A SEQUENCE of length zero is represented as NIL. Note that there is no maximum length for a SEQUENCE in the Lisp implementation of Courier. (ARRAY LENGTH TYPE) An ARRAY value is represented as a list of LENGTH elements, each of type TYPE. (CHOICE (NAME INDEX TYPE) ... (NAME INDEX TYPE)) The CHOICE type allows one to select among several different types at runtime; the INDEX is used in the encoding to distinguish the value types. A value of type CHOICE is represented in Lisp as a list of two elements, (NAME VALUE). For example, a value of type (CHOICE (STATUS 0 (ENUMERATION (BUSY 0) (COMPLETE 1))) (MESSAGE 1 STRING)) could be (STATUS COMPLETE) or (MESSAGE "Out of paper."). (RECORD (FIELDNAME TYPE) ... (FIELDNAME TYPE)) Values of type RECORD are represented as lists, with one element for each field of the record. The field names are not part of the value, but are included for documentation purposes. For programmer convenience, there are two macros that allow Courier records to be constructed and dissected in a manner similar to Lisp records. These compile into the appropriate composites of CONS, CAR and CDR. (COURIER.CREATE(COURIER.CREATE (Macro) NIL NIL NIL 14) TYPE FIELDNAME VALUE ... FIELDNAME VALUE) [Macro] Creates a value of type TYPE, which should be a fully-qualified type name that designates a RECORD type, e.g., (MAILTRANSPORT . POSTMARK). Each FIELDNAME should correspond to a field of the record, and all fields must be included. Each VALUE is evaluated; all other arguments are not. The assignment arrows are for readability, and are optional. (COURIER.FETCH(COURIER.FETCH (Macro) NIL NIL NIL 14) TYPE FIELD OBJECT) [Macro] Analogous to the Record Package operator fetch. Argument TYPE is as with COURIER.CREATE; FIELD is the name of one of its fields. COURIER.FETCH extracts the indicated field from OBJECT. For readability, the noiseword "of" may be inserted between FIELD and OBJECT. Only the argument OBJECT is evaluated. For example, if the program CLEARINGHOUSE has a type declaration (USERDATA.VALUE (RECORD (LAST.NAME.INDEX CARDINAL) (FILE.SERVICE STRING))), then the expression (SETQ INFO (COURIER.CREATE (CLEARINGHOUSE . USERDATA.VALUE) LAST.NAME.INDEX 12 FILE.SERVICE "Phylex:PARC:Xerox") would set the variable INFO to the list (12 "Phylex:PARC:Xerox"). The expression (COURIER.FETCH (CLEARINGHOUSE . USERDATA.VALUE) FILE.SERVICE of INFO) would produce "Phylex:PARC:Xerox". User Extensions to the Type Language The programmer can add new base types to the Courier language by telling the system how to read and write values of that type. The programmer chooses a name for the type, and gives the name a COURIERDEF property. The new name can then be used anywhere that the type names listed in the previous sections, such as CARDINAL, can be used. Such extensions are useful for user-defined objects, such as datatypes, that are not naturally represented by any predefined or constructed type. The NSADDRESS and NSNAME Courier types are defined by this mechanism. COURIERDEF(COURIERDEF (Property Name) NIL NIL NIL 15) [Property Name] The format of the COURIERDEF property is a list of up to four elements, (READFN WRITEFN LENGTHFN WRITEREPFN). The first two elements are required; if the latter two are omitted, the system will simulate them as needed. The elements are as follows: READFN This is a function of three arguments, (STREAM PROGRAM TYPE). The function is called by Courier when it needs to read a value of this type from STREAM as part of a Courier transaction. The function reads and returns the value from STREAM, possibly using functions such as COURIER.READ (see the Courier Subfunctions for Data Transfer section below). PROGRAM and TYPE are the name of the Courier program and the type. In the case of atomic types, TYPE is a litatom, and is provided for type discrimination in case the programmer has supplied a single reading function for several different types. In the case of constructed types, TYPE is a list, CAR of which is the type name. WRITEFN This is a function of four arguments, (STREAM VALUE PROGRAM TYPE). The function is called by Courier when it needs to write VALUE to STREAM. PROGRAM and TYPE are as with the reading function. The function should write VALUE on STREAM. The result returned from this function is ignored. LENGTHFN This function is called when Courier wants to write a value of this type in the form (SEQUENCE UNSPECIFIED), and then only if the WRITEREPFN is omitted. The function is of three arguments, (VALUE PROGRAM TYPE). It should return, as an integer, the number of 16-bit words that the WRITEFN would require to write out this value. If values of this type are all the same length, the LENGTHFN can be a simple integer instead of a function. See discussion of COURIER.WRITE.SEQUENCE.UNSPECIFIED (see the Courier Subfunctions for Data Transfer section below). WRITEREPFN This function is called when Courier wants to write a value of this type in the form (SEQUENCE UNSPECIFIED). The function takes the same arguments as the WRITEFN, but must write the value to the stream preceded by its length. If this function is omitted, Courier invokes the LENGTHFN to find out how long the value is, and then invokes the WRITEFN. If the LENGTHFN is omitted, Courier invokes the WRITEFN on a scratch stream to find out how long the value is. Performing Courier Transactions The normal use of Courier is to open a connection with a remote system element using COURIER.OPEN, perform one or more remote procedure calls using COURIER.CALL, then close the connection with CLOSEF. (COURIER.OPEN(COURIER.OPEN (Function) NIL NIL NIL 16) HOSTNAME SERVERTYPE NOERRORFLG NAME WHENCLOSEDFN OTHERPROPS) [Function] Opens a Courier connection to the Courier socket on HOST, and returns an SPP stream that can be passed to COURIER.CALL. HOSTNAME can be an NS address, or a symbolic Clearinghouse name in the form of a string, litatom or NSNAME. In the case of a symbolic name, SERVERTYPE specifies the Clearinghouse property under which the server's address may be found; normally, this is NIL, in which case the ADDRESS.LIST property is used. Normally, if a connection cannot be made, or the server supports the wrong version of Courier, an error occurs. If NOERRORFLG is non-NIL, COURIER.OPEN returns NIL in these cases. If NAME is non-NIL, it is used as the name of the Courier connection process. WHENCLOSEDFN is a function (or list of functions) of one argument, the Courier stream, that will be called when the connection is closed, either by user or server. If OTHERPROPS is non-NIL, it should be a property list of SPP stream properties, as accepted by SPP.OPEN (see the SPP Stream Interface section above). Any CLOSEFN property on this list is overridden by the value of WHENCLOSEDFN. (COURIER.CALL(COURIER.CALL (Function) NIL NIL NIL 16) STREAM PROGRAM PROCEDURE ARG1 ... ARGN NOERRORFLG) [NoSpread Function] This function calls the remote procedure PROCEDURE of the Courier program PROGRAM. STREAM is the stream returned by COURIER.OPEN. The arguments should be Lisp values appropriate for the Courier types of the corresponding formal parameters of the procedure. There must be the same number of actual and formal arguments. If the procedure call is successful, Courier returns the result(s) of the call as specified in the RETURNS section of the procedure definition. If there is only a single result, it is returned directly, otherwise a list of results is returned. Procedures that take a Bulk Data argument (source or sink) are treated specially; see the Using Bulk Data Transfer section below. If the procedure call results in an error, one of three possible courses is available. The default behavior is to cause a Lisp error. To suppress the error, an optional keyword can be appended to the argument list, as if an extra argument. This NOERRORFLG argument can be the atom NOERROR, in which case NIL is returned as the result of the call. If NOERRORFLG is RETURNERRORS, the result of the call is a list (ERROR ERRORNAME . ERRORARGS). If the failure was a Courier Reject, rather than Error, then ERRORNAME is the atom REJECT. Examples: (COURIERPROGRAM PERSONNEL (17 1) TYPES ((PERSON.NAME (RECORD (FIRST.NAME STRING) MIDDLE MIDDLE.PART) LAST.NAME STRING))) (MIDDLE.PART (CHOICE (NAME 0 STRING) (INITIAL 1 STRING))) (BIRTHDAY (RECORD (YEAR CARDINAL) (MONTH STRING) (DAY CARDINAL)))) PROCEDURES ((GETBIRTHDAY 3 (PERSON.NAME) RETURNS (BIRTHDAY) REPORTS (NO.SUCH.PERSON))) ERRORS ((NO.SUCH.PERSON 1)) ) This expression defines PERSONNEL to be Courier program number 17, version number 1. The example defines three types, PERSON.NAME, MIDDLE.PART and BIRTHDAY, and one procedure, GETBIRTHDAY, whose procedure number is 3. The following code could be used to call the remote GETBIRTHDAY procedure on the host with address HOSTADDRESS. (SETQ STREAM (COURIER.OPEN HOSTADDRESS)) (PROG1 (COURIER.CALL STREAM 'PERSONNEL 'GETBIRTHDAY (COURIER.CREATE (PERSONNEL . PERSON.NAME) FIRST.NAME "Eric" MIDDLE '(INITIAL "C") LAST.NAME "Cooper")) (CLOSEF STREAM)) COURIER.CALL in this example might return a value such as (1959 "January" 10). Expedited Procedure Call Some Courier servers support "Expedited Procedure Call", which is a way of performing a single Courier transaction by a Packet Exchange protocol, rather than going to the expense of setting up a full Courier connection. Expedited calls must have no bulk data arguments, and their arguments and results must each fit into a single packet. (COURIER.EXPEDITED.CALL(COURIER.EXPEDITED.CALL (Function) NIL NIL NIL 17) ADDRESS SOCKET# PROGRAM PROCEDURE ARG1 ... ARGN NOERRORFLG) [NoSpread Function] Attempts to perform a Courier call using the Expedited Procedure Call. ADDRESS is the NS address of the remote host and SOCKET# is the socket on which it is known to listen for expedited calls. The remaining arguments are exactly as with COURIER.CALL. If the arguments to the procedure do not fit in one packet, or if there is no response to the call, or if the call returns the error USE.COURIER (which must be defined by exactly that name in PROGRAM), then the call is attempted instead by the normal, non-expedited method%a Courier connection is opened with ADDRESS, and COURIER.CALL is invoked on the arguments given. Expanding Ring Broadcast "Expanding Ring Broadcast" is a method of locating a server of a particular type whose address is not known in advance. The system broadcasts some sort of request packet on the directly-connected network, then on networks one hop away, then on networks two hops away, etc., until a positive response is received. For use in locating a server for a particular Courier program, a stylized form of Expanding Ring Broadcast is defined. The request packet is essentially the call portion of an Expedited Procedure Call for some procedure defined in the program. The response packet is a Courier response, and typically contains at least the server's address as the result of the call. The designer of the protocol must, of course, specify which procedure to use in the broadcast (usually it is procedure number zero) and on what socket the server should listen for broadcasts. START.CLEARINGHOUSE uses this procedure to locate the nearest Clearinghouse server. (COURIER.BROADCAST.CALL(COURIER.BROADCAST.CALL (Function) NIL NIL NIL 18) DESTSOCKET# PROGRAM PROCEDURE ARGS RESULTFN NETHINT MESSAGE) [Function] Performs an expanding ring broadcast for servers willing to implement PROCEDURE in Courier program PROGRAM. DESTSOCKET# is the socket on which such servers of this type are known to listen for broadcasts, typically the same socket on which they listen for expedited calls. ARGS is the argument list, if any, to the procedure (note that it is not spread, unlike with COURIER.CALL). If a host responds positively, then the function RESULTFN is called with one argument, the Courier results of the procedure call. If RESULTFN returns a non-null value, the value is returned as the value of COURIER.BROADCAST.CALL and the search stops there; otherwise, the search for a responsive host continues. If RESULTFN is not supplied (or is NIL), then the results of the procedure call are returned directly from COURIER.BROADCAST.CALL; i.e., RESULTFN defaults to the identity function. NETHINT, if supplied, is a net number or list of net numbers as a hint concerning which net(s) to try first before performing a pure expanding-ring broadcast. If MESSAGE is non-NIL, it is a description (string) of what the broadcast is looking for, to be printed in the prompt window to inform the user of what is happening. For example, START.CLEARINGHOUSE passes in the message Clearinghouse servers and the hint CH.NET.HINT. Using Bulk Data Transfer When a Courier program needs to transfer an arbitrary amount of information as an argument or result of a Courier procedure, the procedure is usually defined to have one argument of type "Bulk Data". The argument is a "source" if it is information transferred from caller to server (as though a procedure argument), a "sink" if it is information transferred from server to caller (as though a procedure result). These two "types" are indicated in a Courier procedure's formal argument list as BULK.DATA.SOURCE and BULK.DATA.SINK, respectively. A Courier procedure may have at most one such argument. In a Courier call, the bulk data is transmitted in a special way, between the arguments and the results. There are two basic ways to handle this in the call. The caller can specify how the bulk data is to be interpreted (how to read or write it), or the caller can request to be given a bulk data stream as the result of the Courier call. The former is the preferred way; both are described below. In the first method, the caller passes as the actual argument to the Courier call (i.e., in the position in the argument list occupied by BULK.DATA.SOURCE or BULK.DATA.SINK) a function to perform the transfer. Courier sets up the transaction, then calls the supplied function with one argument, a stream on which to write (if a source argument) or read (if a sink) the bulk data. If the function returns normally, the Courier transaction proceeds as usual; if it errors out, Courier sends a Bulk Data Abort to abort the transaction. In the case of a sink argument, if the value returned from the sink function is non-NIL, it is returned as the result of COURIER.CALL; otherwise, the result of COURIER.CALL is the usual procedure result, as declared in the Courier program. For convenience, a Bulk Data sink argument to a Courier call can be specified as a fully qualified Courier type, e.g., (CLEARINGHOUSE . NAME), in which case the Bulk Data stream is read as a "stream of" that type (see COURIER.READ.BULKDATA, below). The second method for handling bulk data is to pass NIL as the bulk data "argument" to COURIER.CALL. In this case, Courier sets up the call, then returns a stream that is open for OUTPUT (if a source argument) or INPUT (if a sink). The caller is responsible for transferring the bulk data on the stream, then closing the stream to complete the transaction. The value returned from CLOSEF is the Courier result. This method is required if the caller's control structure is open-ended in a way such that the bulk data cannot be transferred within the scope of the call to COURIER.CALL. In either method, the stream on which the bulk data is transferred is a standard Interlisp stream, so BIN, BOUT, COPYBYTES are all appropriate. Many Courier programs define a "Stream of " as a means of transferring an arbitrary number of objects, all of the same type. Although this is typically specified formally in the printed Courier documentation as a recursive definition, the recursion is in practice unnecessary and unwieldy; instead, the following function should be used. (COURIER.READ.BULKDATA(COURIER.READ.BULKDATA (Function) NIL NIL NIL 19) STREAM PROGRAM TYPE DONTCLOSE) [Function] Reads from STREAM a "Stream of TYPE" for Courier program PROGRAM, and returns a list of the objects read. STREAM is closed on exit, unless DONTCLOSE is non-NIL. Passing (X . Y) as the bulk argument to a Courier call is thus equivalent to passing the function (LAMBDA (STREAM) (COURIER.READ.BULKDATA STREAM X Y)). Courier Subfunctions for Data Transfer The following functions are of interest to those who transfer data in Courier representations, e.g., as part of a function to implement a user-defined Courier type. (COURIER.READ(COURIER.READ (Function) NIL NIL NIL 19) STREAM PROGRAM TYPE) [Function] Reads from the stream STREAM a Courier value of type TYPE for program PROGRAM. If TYPE is a predefined type, then PROGRAM is irrelevant; otherwise, it is required in order to qualify TYPE. (COURIER.WRITE(COURIER.WRITE (Function) NIL NIL NIL 19) STREAM ITEM PROGRAM TYPE) [Function] Writes ITEM to the stream STREAM as a Courier value of type TYPE for program PROGRAM. (COURIER.READ.SEQUENCE(COURIER.READ.SEQUENCE (Function) NIL NIL NIL 20) STREAM PROGRAM TYPE) [Function] Reads from the stream STREAM a Courier value SEQUENCE of values of type TYPE for program PROGRAM. Equivalent to (COURIER.READ STREAM PROGRAM (SEQUENCE TYPE)). (COURIER.WRITE.SEQUENCE(COURIER.WRITE.SEQUENCE (Function) NIL NIL NIL 20) STREAM ITEM PROGRAM TYPE) [Function] Equivalent to (COURIER.WRITE STREAM ITEM PROGRAM (SEQUENCE TYPE)). Some Courier programs traffic in values whose interpretation is left up to the clients of the program; the values are transferred in Courier transactions as values of type (SEQUENCE UNSPECIFIED). For example, the Clearinghouse program transfers the value of a database property as an uninterpreted sequence, leaving it up to the caller, who knows what type of value the particular property takes, to interpret the sequence of raw bits as some other Courier representation. The following functions are useful when dealing with such values. (COURIER.WRITE.REP VALUE PROGRAM TYPE) [Function] Produces a list of 16-bit integers, i.e., a value of type (SEQUENCE UNSPECIFIED), that represents VALUE when interpreted as a Courier value of type TYPE in PROGRAM. Examples: (COURIER.WRITE.REP T NIL 'BOOLEAN) => (1) (COURIER.WRITE.REP "Thing" NIL 'STRING) => (5 52150Q 64556Q 63400Q) (COURIER.WRITE.REP '(10 25) NIL '(SEQUENCE INTEGER)) => (2 10 25) (COURIER.READ.REP(COURIER.READ.REP (Function) NIL NIL NIL 20) LIST.OF.WORDS PROGRAM TYPE) [Function] Interprets LIST.OF.WORDS, a list of 16-bit integers, as a Courier object of type TYPE in the Courier program PROGRAM. (COURIER.WRITE.SEQUENCE.UNSPECIFIED(COURIER.READ.REP (Function) NIL NIL NIL 20) STREAM ITEM PROGRAM TYPE) [Function] Writes to the stream STREAM in the form (SEQUENCE UNSPECIFIED) the object ITEM, whose value is really a Courier value of type TYPE for program PROGRAM. Equivalent to, but usually much more efficient than, (COURIER.WRITE STREAM (COURIER.WRITE.REP ITEM PROGRAM TYPE) NIL '(SEQUENCE UNSPECIFIED)). Level One Ether Packet Format 1 The data type ETHERPACKET is the vehicle for all kinds of packets transmitted on an Ethernet or Experimental Ethernet. An ETHERPACKET contains several fields for use by the Ethernet drivers and a large, contiguous data area making up the data of the level zero packet. The first several words of the area are reserved for the level one to zero encapsulation, and the remainder (starting at field EPBODY) make up the level one packet. Typically, each level one protocol defines a BLOCKRECORD (see Chapter 8) that overlays the ETHERPACKET starting at the EPBODY field, describing the format of a packet for that particular protocol. For example, the records PUP and XIP define the format of level one packets in the PUP and NS protocols. The extra fields in the beginning of an ETHERPACKET have mostly a fixed interpretation over all protocols. Among the interesting ones are: EPLINK A pointer used to link packets, used by the SYSQUEUE mechanism (see the SYSQUEUE Mechanism section below). Since this field is used by the system for maintaining the free packet queue and ether transmission queues, do not use this field unless you understand it. EPFLAGS A byte field that can be used for any purpose by the user. EPUSERFIELD A pointer field that can be used for any purpose by the user. It is set to NIL when a packet is released. EPTRANSMITTING A flag that is true while the packet is "being transmitted", i.e., from the time that the user instructs the system to transmit the packet until the packet is gathered up from the transmitter's finished queue. While this flag is true, the user must not modify the packet. EPREQUEUE A pointer field that specifies the desired disposition of the packet after transmission. The possible values are: NIL means no special treatment; FREE means the packet is to be released after transmission; an instance of a SYSQUEUE means the packet is to be enqueued on the specified queue (see the SYSQUEUE Mechanism section below). The normal life of an outgoing Ether packet is that a program obtains a blank packet, fills it in according to protocol, then sends the packet over the Ethernet. If the packet needs to be retained for possible retransmission, the EPREQUEUE field is used to specify a queue to place the packet on after its transmission, or the caller hangs on to the packet explicitly. There are redefinitions, or "overlays" of the ETHERPACKET record specifically for use with the PUP and NS protocols. The following sections describe those records and the handling of the PUP and NS level one protocols, how to add new level one protocols, and the queueing mechanism associated with the EPREQUEUE field. PUP Level One Functions The functions in this section are used to implement level two and higher PUP protocols. That is, they deal with sending and receiving PUP packets. It is assumed the reader is familiar with the format and use of pups, e.g., from reading reference [3] in the References section above. Creating and Managing Pups There is a record PUP that overlays the data portion of an ETHERPACKET and describes the format of a pup. This record defines the following numeric fields: PUPLENGTH (16 bits), TCONTROL (transmit control, 8 bits, cleared when a PUP is transmitted), PUPTYPE (8 bits), PUPID (32 bits), PUPIDHI and PUPIDLO (16 bits each overlaying PUPID), PUPDEST (16 bits overlayed by 8-bit fields PUPDESTNET and PUPDESTHOST), PUPDESTSOCKET (32 bits, overlayed by 16-bit fields PUPDESTSOCKETHI and PUPDESTSOCKETLO), and PUPSOURCE, PUPSOURCENET, PUPSOURCEHOST, PUPSOURCESOCKET, PUPSOURCESOCKETHI, and PUPSOURCESOCKETLO, analagously. The field PUPCONTENTS is a pointer to the start of the data portion of the pup. (ALLOCATE.PUP(ALLOCATE.PUP (Function) NIL NIL NIL 22)) [Function] Returns a (possibly used) pup. Keeps a free pool, creating new pups only when necessary. The pup header fields of the pup returned are guaranteed to be zero, but there may be garbage in the data portion if the pup had been recycled, so the caller should clear the data if desired. (CLEARPUP(CLEARPUP (Function) NIL NIL NIL 22) PUP) [Function] Clears all information from PUP, including the pointer fields of the ETHERPACKET and the pup data portion. (RELEASE.PUP(RELEASE.PUP (Function) NIL NIL NIL 22) PUP) [Function] Releases PUP to the free pool. Sockets Pups are sent and received on a socket. Generally, for each "conversation" between one machine and another, there is a distinct socket. When a pup arrives at a machine, the low-level pup software examines the pup's destination socket number. If there is a socket on the machine with that number, the incoming pup is handed over to the socket; otherwise the incoming pup is discarded. When a user process initiates a conversation, it generally selects a large, random socket number different from any other in use on the machine. A server process, on the other hand, provides a specific service at a "well-known" socket, usually a fairly small number. In the PUP world, advertised sockets are in the range 0 to 100Q. (OPENPUPSOCKET(OPENPUPSOCKET (Function) NIL NIL NIL 22) SKT# IFCLASH) [Function] Opens a new pup socket. If SKT# is NIL (the normal case), a socket number is chosen automatically, guaranteed to be unique, and probably different from any socket opened this way in the last 18 hours (the low half of the time of day clock is sampled). If a specific local socket is desired, as is typically the case when implementing a server, SKT# is given, and must be a (up to 32-bit) number. IFCLASH indicates what to do in the case that the designated socket is already in use: if NIL, an error is generated; if ACCEPT, the socket is quietly returned; if FAIL, then OPENPUPSOCKET returns NIL without causing an error. Note that "well-known" socket numbers should be avoided unless the caller is actually implementing one of the services advertised as provided at the socket. (CLOSEPUPSOCKET(CLOSEPUPSOCKET (Function) NIL NIL NIL 22) PUPSOC NOERRORFLG) [Function] Closes and releases socket PUPSOC. If PUPSOC is T, closes all pup sockets (this must be used with caution, since it will also close system sockets!). If PUPSOC is already closed, an error is generated unless NOERRORFLG is true. (PUPSOCKETNUMBER(PUPSOCKETNUMBER (Function) NIL NIL NIL 22) PUPSOC) [Function] Returns the socket number (a 32-bit integer) of PUPSOC. (PUPSOCKETEVENT(PUPSOCKETEVENT (Function) NIL NIL NIL 22) PUPSOC) [Function] Returns the EVENT of PUPSOC (see Chapter 23). This event is notified whenever a pup arrives on PUPSOC, so pup clients can perform an AWAIT.EVENT on this event if they have nothing else to do at the moment. Sending and Receiving Pups(PUPS NIL Pups NIL NIL 23 SUBNAME SENDING% AND% RECEIVING SUBTEXT sending% and% receiving) (SENDPUP(SENDPUP (Function) NIL NIL NIL 23) PUPSOC PUP) [Function] Sends PUP on socket PUPSOC. If any of the PUPSOURCESHOST, PUPSOURCENET, or PUPSOURCESOCKET fields is zero, SENDPUP fills them in using the pup address of this machine and/or the socket number of PUPSOC, as needed. (GETPUP(GETPUP (Function) NIL NIL NIL 23) PUPSOC WAIT) [Function] Returns the next pup that has arrived addressed to socket PUPSOC. If there are no pups waiting on PUPSOC, then GETPUP returns NIL, or waits for a pup to arrive if WAIT is T. If WAIT is an integer, GETPUP interprets it as a number of milliseconds to wait, finally returning NIL if a pup does not arrive within that time. (DISCARDPUPS(DISCARDPUPS (Function) NIL NIL NIL 23) SOC) [Function] Discards without examination any pups that have arrived on SOC and not yet been read by a GETPUP. (EXCHANGEPUPS(EXCHANGEPUPS (Function) NIL NIL NIL 23) SOC OUTPUP DUMMY IDFILTER TIMEOUT) [Function] Sends OUTPUP on SOC, then waits for a responding pup, which it returns. If IDFILTER is true, ignores pups whose PUPID is different from that of OUTPUP. TIMEOUT is the length of time (msecs) to wait for a response before giving up and returning NIL. TIMEOUT defaults to \ETHERTIMEOUT. EXCHANGEPUPS discards without examination any pups that are currently waiting on SOC before OUTPUP gets sent. (DUMMY is ignored; it exists for compatibility with an earlier implementation). Pup Routing Information Ordinarily, a program calls SENDPUP and does not worry at all about the route taken to get the pup to its destination. There is an internet routing process in Lisp whose job it is to maintain information about the best routes to networks of interest. However, there are some algorithms for which routing information and/or the topology of the net are explicitly desired. To this end, the following functions are supplied: (PUPNET.DISTANCE(PUPNET.DISTANCE (Function) NIL NIL NIL 23) NET#) [Function] Returns the "hop count" to network NET#, i.e., the number of gateways through which a pup must pass to reach NET#, according to the best routing information known at this point. The local (directly-connected) network is considered to be zero hops away. Current convention is that an inaccessible network is 16 hops away. PUPNET.DISTANCE may need to wait to obtain routing information from an Internetwork Router if NET# is not currently in its routing cache. (SORT.PUPHOSTS.BY.DISTANCE(SORT.PUPHOSTS.BY.DISTANCE (Function) NIL NIL NIL 23) HOSTLIST) [Function] Sorts HOSTLIST by increasing distance, in the sense of PUPNET.DISTANCE. HOSTLIST is a list of lists, the CAR of each list being a 16-bit Net/Host address, such as returned by ETHERHOSTNUMBER. In particular, a list of ports ((nethost . socket) pairs) is in this format. (PRINTROUTINGTABLE(PRINTROUTINGTABLE (Function) NIL NIL NIL 23) TABLE SORT FILE) [Function] Prints to FILE the current routing cache. The table is sorted by network number if SORT is true. TABLE = PUP (the default) prints the PUP routing table; TABLE = NS prints the NS routing table. Miscellaneous PUP Utilities (SETUPPUP(SETUPPUP (Function) NIL NIL NIL 24) PUP DESTHOST DESTSOCKET TYPE ID SOC REQUEUE) [Function] Fills in various fields in PUP's header: its length (the header overhead length; assumes data length of zero), TYPE, ID (if ID is NIL, generates a new one itself from an internal 16-bit counter), destination host and socket (DESTHOST may be anything that ETHERPORT accepts; an explicit nonzero socket in DESTHOST overrides DESTSOCKET). If SOC is not supplied, a new socket is opened. REQUEUE fills the packets EPREQUEUE field (see above). Value of SETUPPUP is the socket. (SWAPPUPPORTS (SWAPPUPPORTS% (Function) NIL NIL NIL 24)PUP) [Function] Swaps the source and destination addresses in PUP. This is useful in simple packet exchange protocols, where you want to respond to an input packet by diddling the data portion and then sending the pup back whence it came. (GETPUPWORD(GETPUPWORD (Function) NIL NIL NIL 24) PUP WORD#) [Function] Returns as a 16-bit integer the contents of the WORD#th word of PUP's data portion, counting the first word as word zero. (PUTPUPWORD(PUTPUPWORD (Function) NIL NIL NIL 24) PUP WORD# VALUE) [Function] Stores 16-bit integer VALUE in the WORD#th word of PUP's data portion. (GETPUPBYTE(GETPUPBYTE (Function) NIL NIL NIL 24) PUP BYTE#) [Function] Returns as an integer the contents of the BYTE#th 8-bit byte of PUP's data portion, counting the first byte as byte zero. (PUTPUPBYTE(PUTPUPBYTE (Function) NIL NIL NIL 24) PUP BYTE# VALUE) [Function] Stores VALUE in the BYTE#th 8-bit byte of PUP's data portion. (GETPUPSTRING(GETPUPSTRING (Function) NIL NIL NIL 24) PUP OFFSET) [Function] Returns a string consisting of the characters in PUP's data portion starting at byte OFFSET (default zero) through the end of PUP. (PUTPUPSTRING (PUTPUPSTRING% (Function) NIL NIL NIL 24)PUP STR) [Function] Appends STR to the data portion of PUP, incrementing PUP's length appropriately. PUP Debugging Aids Tracing facilities are provided to allow the user to see the pup traffic that passes through SENDPUP and GETPUP. The tracing can be verbose, displaying much information about each packet, or terse, which shows a concise "picture" of the traffic. PUPTRACEFLG(PUPTRACEFLG (Variable) NIL NIL NIL 24) [Variable] Controls tracing information provided by SENDPUP and GETPUP. Legal values: NIL No tracing. T Every SENDPUP and every successful GETPUP call PRINTPUP of the pup at hand (see below). PEEK Allows a concise "picture" of the traffic. For normal, non-broadcast packets, SENDPUP prints "!", GETPUP prints "+". For broadcast packets, SENDPUP prints "^", GETPUP prints "*". In addition, for packets that arrive not addressed to any socket on this machine (e.g., broadcast packets for a service not implemented on this machine), a "&" is printed. PUPIGNORETYPES(PUPIGNORETYPES (Variable) NIL NIL NIL 25) [Variable] A list of pup types (small integers). If the type of a pup is on this list, then GETPUP and SENDPUP will not print the pup verbosely, but treat it as though PUPTRACEFLG were PEEK. This allows the user to filter out "uninteresting" pups, e.g., routine routing information pups (type 201Q). PUPONLYTYPES(PUPONLYTYPES (Variable) NIL NIL NIL 25) [Variable] A list of pup types. If this variable is non-NIL, then GETPUP and SENDPUP print verbosely only pups whose types appear on the list, treating others as though PUPTRACEFLG were PEEK. This lets the tracing be confined to only a certain class of pup traffic. PUPTRACEFILE(PUPTRACEFILE (Variable) NIL NIL NIL 25) [Variable] The file to which pup tracing output is sent by default. The file must be open. PUPTRACEFILE is initially T. PUPTRACETIME(PUPTRACETIME (Variable) NIL NIL NIL 25) [Variable] If this variable is true, then each printout of a pup is accompanied by a relative timestamp (in seconds, with 2 decimal places) of the current time (i.e., when the SENDPUP or GETPUP was called; for incoming pups, this is not the same as when the pup actually arrived). (PUPTRACE(PUPTRACE (Function) NIL NIL NIL 25) FLG REGION) [Function] Creates a window for puptracing, and sets PUPTRACEFILE to it. If PUPTRACEFILE is currently a window and FLG is NIL, closes the window. Sets PUPTRACEFLG to be FLG. If REGION is supplied, the window is created with that region. The window's BUTTONEVENTFN is set to cycle PUPTRACEFLG through the values NIL, T, and PEEK when the mouse is clicked in the window. (PRINTPUP(PRINTPUP (Function) NIL NIL NIL 25) PACKET CALLER FILE PRE.NOTE DOFILTER) [Function] Prints the information in the header and possibly data portions of pup PACKET to FILE. If CALLER is supplied, it identifies the direction of the pup (GET or PUT), and is printed in front of the header. FILE defaults to PUPTRACEFILE. If PRE.NOTE is non-NIL, it is PRIN1'ed first. If DOFILTER is true, then if PUP's type fails the filtering criteria of PUPIGNORETYPES or PUPONLYTYPES, then PUP is printed "tersely", i.e., as a !, +, , or *, as described above. GETPUP and SENDPUP, when PUPTRACEFLG is non-NIL, call (PRINTPUP PUP {'GET or 'PUT} NIL NIL T). The form of printing provided by PRINTPUP can be influenced by adding elements to PUPPRINTMACROS. PUPPRINTMACROS(PUPPRINTMACROS (Variable) NIL NIL NIL 25) [Variable] An association list of elements (PUPTYPE . MACRO) for printing pups. The MACRO (CDR of each element) tells how to print the information in a pup of type PUPTYPE (CAR of the element). If MACRO is a litatom, then it is a function of two arguments (PUP FILE) that is applied to the pup to do the printing. Otherwise, MACRO is a list describing how to print the data portion of the pup (the header is printed in a standard way). The list form of MACRO consists of "commands" that specify a "datatype" to interpret the data, and an indication of how far that datatype extends in the packet. Each element of MACRO is one of the following: (a) a byte offset (positive integer), indicating the byte at which the next element, if any, takes effect; (b) a negative integer, the absolute value of which is the number of bytes until the next element, if any, takes effect; or (c) an atom giving the format in which to print the data, one of the following: BYTES Print the data as 8-bit bytes, enclosed in brackets. This is the default format to start with. CHARS Print the data as (8-bit) characters. Non-printing characters are printed as if the format were BYTES, except that the sequence 15Q, 12Q is printed specially as [crlf]. WORDS Print the data as 16-bit integers, separated by commas (or the current SEPR). INTEGERS Print the data as 32-bit integers, separated by commas (or the current SEPR). The singular BYTE, CHAR, WORD, INTEGER are accepted as synonyms for these four commands. SEPR Set the separator for WORDS and INTEGERS to be the next element of the macro. The separator is initially the two characters, comma, space. IFSSTRING Interprets the data as a 16-bit length followed by that many 8-bit bytes or characters. If the current datatype is BYTES, leaves it alone; otherwise, sets it to be CHARS. ... If there is still data left in the packet by the time processing reaches this command, prints "..." and stops. FINALLY The next element of the macro is printed when the end of the packet is reached (or printing stops because of a ...). This command does not alter the datatype, and can appear anywhere in the macro as long as it is encountered before the actual end of the packet. T Perform a TERPRI. REPEAT The remainder of the macro is itself treated as a macro to be applied over and over until the packet is exhausted. The offsets specified in the macro must be in the relative form, i.e., negative integers. For example, the macro (INTEGERS 4 REPEAT BYTES -2 WORDS -4) says to print the first 4 bytes of the data as one 32-bit integer, then print the rest of the data as sets of two 8-bit bytes and two 16-bit words. Only as much of the macro is processed as is needed to print the data in the given packet. The default macro for printing a pup is (BYTES 12 ...), meaning to print the first up to 12 bytes as bytes, and then print "..." if there is anything left. (PUP.ECHOUSER(PUP.ECHOUSER (Function) NIL NIL NIL 26) HOST ECHOSTREAM INTERVAL NTIMES) [Function] Sends dummy packets to be echoed by the host HOST. Can be used as a simple test of the functioning of the Ethernet and the host. HOST is the pup host to send the packets to. ECHOSTREAM is the stream for printing status information. INTERVAL is the interval (in milliseconds) to wait for the packet to be echoed (default 1000). NTIMES is the number of packets to send (default 1000). As each packet is sent and received, characters are printed to ECHOSTREAM as follows: ! Printed when a packet is sent. + Printed when an echo packet is sucessfully received. . Printed when an echo packet has not been received after INTERVAL milliseconds. ? Printed when a packet is received, but it isn't an echo packet or an error packet. (late) Printed when an error packet is received, after the echo request timed out. The trace can be used to test the functioning of the ethernet and host. For example, if the trace is !+!+!+!+!+, the host is listening and echoing correctly. !.!.!.!.!. indicates that for some reason the host is not responding. !+!.!.!(late).!(late)(late)+ indicates that the packets are being echoed, but not immediately. The following functions are used by PRINTPUP and similar functions, and may be of interest in special cases. (PORTSTRING(PORTSTRING (Function) NIL NIL NIL 27) NETHOST SOCKET) [Function] Converts the pup address NETHOST, SOCKET into octal string format as follows: NET#HOST#SOCKET. NETHOST may be a port (dotted pair of nethost and socket), in which case SOCKET is ignored, and the socket portion of NETHOST is omitted from the string if it is zero. (PRINTPUPROUTE(PRINTPUPROUTE (Function) NIL NIL NIL 27) PACKET CALLER FILE) [Function] Prints the source and destination addresses of pup PACKET to FILE in the PORTSTRING format, preceded by CALLER (interpreted as with PRINTPUP). (PRINTPACKETDATA(PRINTPACKETDATA (Function) NIL NIL NIL 27) BASE OFFSET MACRO LENGTH FILE) [Function] Prints data according to MACRO, which is a list interpreted as described under PUPPRINTMACROS, to FILE. The data starts at BASE and extends for LENGTH bytes. The actual printing starts at the OFFSETth byte, which defaults to zero. For example, PRINTPUP ordinarily calls (PRINTPACKETDATA (fetch PUPCONTENTS of PUP) 0 MACRO (IDIFFERENCE (fetch PUPLENGTH of PUP) 20) FILE). (PRINTCONSTANT(PRINTCONSTANT (Function) NIL NIL NIL 27) VAR CONSTANTLIST FILE PREFIX) [Function] CONSTANTLIST is a list of pairs (VARNAME VALUE), of the form given to the CONSTANTS File Package Command. PRINTCONSTANT prints VAR to FILE, followed in parentheses by the VARNAME out of CONSTANTLIST whose VALUE is EQ to VAR, or ? if it finds no such element. If PREFIX is non-NIL and is an initial substring of the selected VARNAME, then VARNAME is printed without the prefix. For example, if FOOCONSTANTS is ((FOO.REQUEST 1) (FOO.ANSWER 2) (FOO.ERROR 3)), then (PRINTCONSTANT 2 FOOCONSTANTS T "FOO.") produces "2 (ANSWER)". (OCTALSTRING(OCTALSTRING (Function) NIL NIL NIL 27) N) [Function] Returns a string of octal digits representing N in radix 8. NS Level One Functions 1 The functions in this section are used to implement level two and higher NS protocols. The packets used in the NS protocol are termed Xerox Internet Packets (XIPs). The functions for manipulating XIPs are similar to those for managing PUPs, so will be described in less detail here. The major difference is that NS host addresses are 48-bit numbers. Since Interlisp-D cannot currently represent 48-bit numbers directly as integers, there is an interim form called NSHOSTNUMBER, which is defined as a TYPERECORD of three fields, each of them being a 16-bit portion of the 48-bit number. Creating and Managing XIPs There is a record XIP that overlays the data portion of an ETHERPACKET and describes the format of a XIP. This record defines the following fields: XIPLENGTH (16 bits), XIPTCONTROL (transmit control, 8 bits, cleared when a XIP is transmitted), XIPTYPE (8 bits), XIPDESTNET (32 bits), XIPDESTHOST (an NSHOSTNUMBER), XIPDESTSOCKET (16 bits), and XIPSOURCENET, XIPSOURCEHOST, and XIPSOURCESOCKET, analagously. The field XIPCONTENTS is a pointer to the start of the data portion of the XIP. (ALLOCATE.XIP(ALLOCATE.XIP (Function) NIL NIL NIL 28)) [Function] Returns a (possibly used) XIP. As with ALLOCATE.PUP, the header fields are guaranteed to be zero, but there may be garbage in the data portion if the pup had been recycled. (RELEASE.XIP(RELEASE.XIP (Function) NIL NIL NIL 28) XIP) [Function] Releases XIP to the free pool. NS Sockets As with pups, XIPs are sent and received on a socket. The same comments apply as with pup sockets (see the Sockets section above), except that NS socket numbers are only 16 bits. (OPENNSOCKET(OPENNSOCKET (Function) NIL NIL NIL 28) SKT# IFCLASH) [Function] Opens a new NS socket. If SKT# is NIL (the normal case), a socket number is chosen automatically, guaranteed to be unique, and probably different from any socket opened this way in the last 18 hours. If a specific local socket is desired, as is typically the case when implementing a server, SKT# is given, and must be a (up to 16-bit) number. IFCLASH governs what to do if SKT# is already in use: if IFCLASH is NIL, an error is generated; if IFCLASH is ACCEPT, the socket is quietly returned; if IFCLASH is FAIL, then OPENNSOCKET returns NIL without causing an error. (CLOSENSOCKET(CLOSENSOCKET (Function) NIL NIL NIL 28) NSOC NOERRORFLG) [Function] Closes and releases socket NSOC. If NSOC is T, closes all NS sockets (this must be used with caution, since it will also close system sockets!). If NSOC is already closed, an error is generated unless NOERRORFLG is true. (NSOCKETNUMBER(NSOCKETNUMBER (Function) NIL NIL NIL 28) NSOC) [Function] Returns the socket number (a 16-bit integer) of NSOC. (NSOCKETEVENT(NSOCKETEVENT (Function) NIL NIL NIL 28) NSOC) [Function] Returns the EVENT of NSOC. This event is notified whenever a XIP arrives on NSOC. Sending and Receiving XIPs (SENDXIP(SENDXIP (Function) NIL NIL NIL 29) NSOC XIP) [Function] Sends XIP on socket NSOC. If any of the XIPSOURCESHOST, XIPSOURCENET, or XIPSOURCESOCKET fields is zero, SENDXIP fills them in using the NS address of this machine and/or the socket number of NSOC, as needed. (GETXIP(GETXIP (Function) NIL NIL NIL 29) NSOC WAIT) [Function] Returns the next XIP that has arrived addressed to socket NSOC. If there are no XIPs waiting on NSOC, then GETXIP returns NIL, or waits for a XIP to arrive if WAIT is T. If WAIT is an integer, GETXIP interprets it as a number of milliseconds to wait, finally returning NIL if a XIP does not arrive within that time. (DISCARDXIPS(DISCARDXIPS (Function) NIL NIL NIL 29) NSOC) [Function] Discards without examination any XIPs that have arrived on NSOC and not yet been read by a GETXIP. (EXCHANGEXIPS(EXCHANGEXIPS (Function) NIL NIL NIL 29) SOC OUTXIP IDFILTER TIMEOUT) [Function] Useful for simple NS packet exchange protocls. Sends OUTXIP on SOC, then waits for a responding XIP, which it returns. If IDFILTER is true, ignores XIPs whose packet exchange ID (the first 32 bits of the data portion) is different from that of OUTXIP. TIMEOUT is the length of time (msecs) to wait for a response before giving up and returning NIL. TIMEOUT defaults to \ETHERTIMEOUT. EXCHANGEXIPS discards without examination any XIPs that are currently waiting on SOC before OUTXIP gets sent. NS Debugging Aids XIPs can be printed automatically by SENDXIP and GETXIP analogously to the way pups are. The following variables behave with respect to XIPs the same way that the corresponding PUP-named variables behave with respect to PUPs: XIPTRACEFLG, XIPTRACEFILE, XIPIGNORETYPES, XIPONLYTYPES, XIPPRINTMACROS. In addition, the functions PRINTXIP, PRINTXIPROUTE, XIPTRACE, and NS.ECHOUSER are directly analogous to PRINTPUP, PRINTPUPROUTE, PUPTRACE, and PUP.ECHOUSER. See the PUP Debugging Aids section above. Support for Other Level One Protocols 1 Raw packets other than of type PUP or NS can also be sent and received. This section describes facilities to support such protocols. Many of these functions have a \ in their names to designate that they are system internal, not to be dealt with as casually as user-level functions. (RESTART.ETHER(RESTART.ETHER (Function) NIL NIL NIL 29)) [Function] This function is intended to be invoked from the executive on those rare occasions when the Ethernet appears completely unresponsive, due to Lisp having gotten into a bad state. RESTART.ETHER reinitializes Lisp's Ethernet driver(s), just as when the Lisp system is started up following a LOGOUT, SYSOUT, etc. This aborts any Ethernet activity and clears several internal caches, including the routing table. (\ALLOCATE.ETHERPACKET(\ALLOCATE.ETHERPACKET (Function) NIL NIL NIL 30)) [Function] Returns an ETHERPACKET datum. Enough of the packet is cleared so that if the packet represents a PUP or NS packet, that its header is all zeros; no guarantee is made about the remainder of the packet. (\RELEASE.ETHERPACKET(\RELEASE.ETHERPACKET (Function) NIL NIL NIL 30) EPKT) [Function] Returns EPKT to the pool of free packets. This operation is dangerous if the caller actually is still holding on to EPKT, e.g., in some queue, since this packet could be returned to someone else (via \ALLOCATE.ETHERPACKET) and suffer the resulting contention. From a logical standpoint, programs need never call \RELEASE.ETHERPACKET, since the packets are eventually garbage-collected after all pointers to them drop. However, since the packets are so large, normal garbage collections tend not to occur frequently enough. Thus, for best performance, a well-disciplined program should explicitly release packets when it knows it is finished with them. A locally-connected network for the transmission and receipt of Ether packets is specified by a network descriptor block, an object of type NDB. There is one NDB for each directly-connected network; ordinarily there is only one. The NDB contains information specific to the network, e.g., its PUP and NS network numbers, and information about how to send and receive packets on it. \LOCALNDBS(\LOCALNDBS (Variable) NIL NIL NIL 30) [Variable] The first NDB connected to this machine, or NIL if there is no network. Any other NDBs are linked to this first one via the NDBNEXT field of the NDB. To transmit an Ether packet, a program must specify the packet's type and its immediate destination. The type is a 16-bit integer identifying the packet's protocol. There are preassigned types for PUP and NS. The destination is a host address on the local network, in whatever form the local network uses for addressing; it is not necessarily related to the logical ultimate destination of the packet. Determining the immediate destination of a packet is the task of routing. The functions SENDPUP and SENDXIP take care of this for the PUP and NS protocols, routing a packet directly to its destination if that host is on the local network, or routing it to a gateway if the host is on some other network accessible via the gateway. Of course, a gateway must know about the type (protocol) of a packet in order to be able to forward it. (ENCAPSULATE.ETHERPACKET(ENCAPSULATE.ETHERPACKET (Function) NIL NIL NIL 30) NDB PACKET PDH NBYTES ETYPE) [Function] Encapsulates PACKET for transmission on network NDB. PDH is the physical destination host (e.g., an 8-bit pup host number or a 48-bit NS host number); NBYTES is the length of the packet in bytes; ETYPE is the packet's encapsulation type (an integer). (TRANSMIT.ETHERPACKET(TRANSMIT.ETHERPACKET (Function) NIL NIL NIL 30) NDB PACKET) [Function] Transmits PACKET, which must already have been encapsulated, on network NDB. Disposition of the packet after transmission is complete is determined by the value of PACKET's EPREQUEUE field. To receive Ether packets of type other than PUP or NS, the programmer must specify what to do with incoming packets. Lisp maintains a set of packet filters, functions whose job it is to appropriately dispose of incoming packets of the kind they want. When a packet arrives, the Ethernet driver calls each filter function in turn until it finds one that accepts the packet. The filter function is called with two arguments: (PACKET TYPE), where PACKET is the actual packet, and TYPE is its Ethernet encapsulation type (a number). If a filter function accepts the packet, it should do what it wants to with it, and return T; else it should return NIL, allowing other packet filters to see the packet. Since the filter function is run at interrupt level, it should keep its computation to a minimum. For example, if there is a lot to be done with the packet, the filter function can place it on a queue and notify another process of its arrival. The system already supplies packet filters for packets of type PUP and NS; these filters enqueue the incoming packet on the input queue of the socket to which the packet is addressed, after checking that the packet is well-formed and indeed addressed to an existing socket on this machine. Incoming packets have their EPNETWORK field filled in with the NDB of the network on which the packet arrived. (\ADD.PACKET.FILTER(\ADD.PACKET.FILTER (Function) NIL NIL NIL 31) FILTER) [Function] Adds function FILTER to the list of packet filters if it is not already there. (\DEL.PACKET.FILTER(\DEL.PACKET.FILTER (Function) NIL NIL NIL 31) FILTER) [Function] Removes FILTER from the list of packet filters. (\CHECKSUM(\CHECKSUM (Function) NIL NIL NIL 31) BASE NWORDS INITSUM) [Function] Computes the one's complement add and cycle checksum for the NWORDS words starting at address BASE. If INITSUM is supplied, it is treated as the accumulated checksum for some set of words preceding BASE; normally INITSUM is omitted (and thus treated as zero). (PRINTPACKET(PRINTPACKET (Function) NIL NIL NIL 31) PACKET CALLER FILE PRE.NOTE DOFILTER) [Function] Prints PACKET by invoking a function appropriate to PACKET's type. See PRINTPUP for the intended meaning of the other arguments. In order for PRINTPACKET to work on a non-standard packet, there must be information on the list \PACKET.PRINTERS. \PACKET.PRINTERS(\PACKET.PRINTERS (Variable) NIL NIL NIL 31) [Variable] An association list mapping packet type into the name of a function for printing that type of packet. The SYSQUEUE Mechanism 1 The SYSQUEUE facility provides a low-level queueing facility. The functions described herein are all system internal: they can cause much confusion if misused. A SYSQUEUE is a datum containing a pointer to the first element of the queue and a pointer to the last; each item in the queue points to the next via a pointer field located at offset 0 in the item (its QLINK field in the QABLEITEM record). A SYSQUEUE can be created by calling (NCREATE 'SYSQUEUE). (\ENQUEUE Q ITEM) [Function] Enqueues ITEM on Q, i.e., links it to the tail of the queue, updating Q's tail pointer appropriately. (\DEQUEUE Q) [Function] Removes the first item from Q and returns it, or returns NIL if Q is empty. (\UNQUEUE Q ITEM NOERRORFLG) [Function] Removes the ITEM from Q, wherever it is located in the queue, and returns it. If ITEM is not in Q, causes an error, unless NOERRORFLG is true, in which case it returns NIL. (\QUEUELENGTH Q) [Function] Returns the number of elements in Q. (\ONQUEUE ITEM Q) [Function] True if ITEM is an element of Q. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "31-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "31-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "31-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "31-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "31-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "31-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))(6T-llT0HHT-HHT3HZ +T,,/HH/HH +5,5fTf,5/ll3HH(T0$$T553$$(T53HZ +T,,3(T,ll-T3$$(T3(T,HH +,HH,HH,2F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR,-T CLASSIC + HELVETICA  HELVETICATITAN +CLASSIC +CLASSIC + HELVETICA +CLASSIC +MODERN +MODERNMODERN +& HRULE.GETFNCLASSIC +&% HRULE.GETFNCLASSIC +%$ $ HRULE.GETFNCLASSIC +# # HRULE.GETFNCLASSIC + +" IM.CHAP.GETFNMODERN +! HRULE.GETFNMODERN +  EIM.INDEX.GETFN  HRULE.GETFNMODERN + 5PmIM.INDEX.GETFN HELVETICAC/LsIM.INDEX.GETFN HELVETICA qIM.INDEX.GETFN HELVETICA ;g'wIM.INDEX.GETFN HELVETICA o(* , Z   +  q  c#  #cIM.INDEX.GETFN HRULE.GETFNMODERN + *IM.INDEX.GETFN  '  +$IM.INDEX.GETFN  \k[@D | ,D3 (IM.INDEX.GETFN ) +QSYn IM.INDEX.GETFN  + @$< 8  ~  B  Y   a  o"aIM.INDEX.GETFN HRULE.GETFNMODERN +     G  + *.    r + =! B2IM.INDEX.GETFN  C ,IM.INDEX.GETFN T}\ 'IM.INDEX.GETFN  +IP KZ9\@>B +IM.INDEX.GETFN  8 j# 2 ^.IM.INDEX.GETFN   +q +2  &IM.INDEX.GETFN mE  X -IM.INDEX.GETFN!  +v +IM.INDEX.GETFN  F F%1v+IM.INDEX.GETFN   D   # 0IM.INDEX.GETFN L *IM.INDEX.GETFN )+ 6 *IM.INDEX.GETFN (  Q     .? 6P *IM.INDEX.GETFN W -IM.INDEX.GETFN P  +IM.INDEX.GETFN'   W3 (.IM.INDEX.GETFNTITAN +5 # u  IM.INDEX.GETFN+  V9   n"IM.INDEX.GETFN -;@3N +IM.INDEX.GETFN  /IM.INDEX.GETFN    #IM.INDEX.GETFN a +1 m +Z [BD ;AM  g 9 *   t    ,~   d/V K ++IM.INDEX.GETFN i,IM.INDEX.GETFN [ &IM.INDEX.GETFN  <Df +f " 'IM.INDEX.GETFN 8$-IM.INDEX.GETFNTITAN +,5D F 2   1  7i (IM.INDEX.GETFN ( +.IM.INDEX.GETFN * +& 5EG*IM.INDEX.GETFN  A            ; +- +3 Kq  +         G' > +t%p    Pr3U  : V W +   + H + C +    +   +   ] + +  ( V +  Y"  +  > a   +  Lc +  Za  tY  + + 6 9*  <".(  # IJ4"8'  'IM.INDEX.GETFN- @  Ti &IM.INDEX.GETFN )  $ ## 3% 0$  +p . ++IM.INDEX.GETFN  +-")VR# BQ (=>6V +3I]C" >  +V0s9 +!7  U 3 ! (IM.INDEX.GETFN; 42 \  +g t +  <  +H45   (IM.INDEX.GETFN   )  % +, + #@   +! .  )!'!5 + V   T $ )40 ' . S2IM.INDEX.GETFNMODERN +< H*p ~` F  :2A2IM.INDEX.GETFN=  F  Y 1MAXE$   JkT"  DwM +4  R f Y1IM.INDEX.GETFN   + S4 &  (IM.INDEX.GETFN  > )IM.INDEX.GETFN   1IM.INDEX.GETFN    2IM.INDEX.GETFN  [ :-  *+: ,IM.INDEX.GETFN   9#,IM.INDEX.GETFN   0 8 HRULE.GETFNMODERN +  b N # bF( Y-< M tIg 0    & W  @  $ +  &    < (IM.INDEX.GETFN  $IM.INDEX.GETFN &  'IM.INDEX.GETFN   e)IM.INDEX.GETFN  \1r%  *IM.INDEX.GETFN n1 +  +IM.INDEX.GETFN 0 *IM.INDEX.GETFN  E  = ZIM.INDEX.GETFN #IM.INDEX.GETFN    Q "IM.INDEX.GETFN  :# "F, 'IM.INDEX.GETFN ; (IM.INDEX.GETFNMODERN +" 9U  EJ +IM.INDEX.GETFNMODERN + #FO(5IM.INDEX.GETFNMODERN +  )CP-IM.INDEX.GETFNMODERN +  +F -  $IM.INDEX.GETFNMODERN ++ Q\ (  ++  *IM.INDEX.GETFN . &IM.INDEX.GETFN  0 7 &IM.INDEX.GETFN   &IM.INDEX.GETFN  *7 &IM.INDEX.GETFN  (IM.INDEX.GETFN + 1!# *IM.INDEX.GETFN  ] 'IM.INDEX.GETFN )    !  O    *IM.INDEX.GETFN R: p (IM.INDEX.GETFN .@ M (IM.INDEX.GETFN R  (IM.INDEX.GETFN X $IM.INDEX.GETFNMODERN + + *    D  * $IM.INDEX.GETFNMODERN +$ G6+  ( "  !)*IM.INDEX.GETFN !F7=jQ a  aD  G H3  d u,`  %e  (IM.INDEX.GETFNMODERN + -Q* +1X2? +  69TMf +0 +=C$A &IM.INDEX.GETFNMODERN + &B'* )IM.INDEX.GETFNMODERN + 3 + +IM.INDEX.GETFNMODERN + 1+/&  )IM.INDEX.GETFN    ! "-  ( + +  'IM.INDEX.GETFNMODERN + .  ( HRULE.GETFNMODERN +   +L & O   @  +       < (IM.INDEX.GETFN ( z 'IM.INDEX.GETFN   + . 'IM.INDEX.GETFN  1%   (IM.INDEX.GETFN h1 + +)IM.INDEX.GETFN 0 (IM.INDEX.GETFN  4 #IM.INDEX.GETFN   P "IM.INDEX.GETFN + :# "F, 'IM.INDEX.GETFN ;  (IM.INDEX.GETFN 69rU  E  %       -& HRULE.GETFNMODERN + v)IM.INDEX.GETFN  ak1IM.INDEX.GETFN   L_ 0IM.INDEX.GETFN iQ'5B`I9O &IM.INDEX.GETFN  +$'$3IM.INDEX.GETFN  _'1 0IM.INDEX.GETFN +  +8Z ,Y 3? -.IM.INDEX.GETFNMODERN + < .IM.INDEX.GETFNMODERN + ! %IM.INDEX.GETFNMODERN + =X ( 'IM.INDEX.GETFNMODERN +$ '@ I,IM.INDEX.GETFNMODERN + f HRULE.GETFNMODERN +      4     ;  +#  " m3z \ No newline at end of file diff --git a/docs/turpin-irm/IRM-1/001-TITLEPAGE.TEDIT b/docs/turpin-irm/IRM-1/001-TITLEPAGE.TEDIT new file mode 100644 index 00000000..598f5fb0 Binary files /dev/null and b/docs/turpin-irm/IRM-1/001-TITLEPAGE.TEDIT differ diff --git a/docs/turpin-irm/IRM-1/003-TOC.TEDIT b/docs/turpin-irm/IRM-1/003-TOC.TEDIT new file mode 100644 index 00000000..407e8f83 --- /dev/null +++ b/docs/turpin-irm/IRM-1/003-TOC.TEDIT @@ -0,0 +1,6 @@ +1 MEDLEY REFERENCE 1 MEDLEY REFERENCE TABLE OF CONTENTS 1 TABLE OF CONTENTS 1 TABLE of CONTENTS 6 Volume 1 - Lanuage Reference 1. Introduction 1 2. Litatoms (Symbols) 2-1 Using Symbols as Variables 2-1 Function Definition Cells 2-3 Property Lists 2-4 Print Names 2-5 Characters and Character Codes 2-9 3. Lists 3-1 Creating Lists 3-3 Building Lists from Left to Right 3-4 Copying Lists 3-6 Extracting Tails of Lists 3-6 Counting List Cells 3-8 Logical Operations 3-9 Searching Lists 3-10 Substitution Functions 3-10 Association Lists and Property Lists 3-11 Sorting Lists 3-13 Other List Functions 3-15 4. Strings 4-1 5. Arrays 5-1 6. Hash Arrays 6-1 Hash Overflow 6-3 User-Specified Hashing Functions 6-3 7. Numbers and Arithmetic Functions 7-1 Generic Arithmetic 7-2 Integer Arithmetic 7-3 Logical Arithmetic Functions 7-6 Floating-Point Arithmetic 7-8 Other Arithmetic Functions 7-10 8. Record Package 8-1 FETCH and REPLACE 8-1 CREATE 8-2 TYPE? 8-3 WITH 8-4 Record Declarations 8-4 Record Types 8-5 Optional Record Specifications 8-10 Defining New Record Types 8-12 Record Manipulation Functions 8-12 Changetran 8-13 Built-in and User Data Types 8-15 9. Conditionals and Iterative Statements 9-1 Data Type Predicates 9-1 Equality Predicates 9-2 Logical Predicates 9-3 COND Conditional Function 9-3 The IF Statement 9-4 Selection Functions 9-5 PROG and Associated Control Functions 9-6 The Iterative Statement 9-7 I.s. Types 9-8 Iterative Variable I.s.oprs 9-9 Condition I.s.oprs 9-12 Other I.s.oprs 9-13 Miscellaneous Hints on I.s.oprs 9-13 Errors in Iterative Statements 9-15 Defining New Iterative Statement Operators 9-15 10. Function Definition, Manipulation, and Evaluation 10-1 Function Types 10-2 Lambda-Spread Functions 10-2 Nlambda-Spread Functions 10-3 Lambda-Nospread Functions 10-4 Nlambda-Nospread Functions 10-4 Compiled Functions 10-5 Function Type Functions 10-5 Defining Functions 10-7 Function Evaluation 10-1 Iterating and Mapping Functions 10-1 Function Arguments 10-1 Macros 10-1 DEFMACRO 10-15 Interpreting Macros 10-15 11. Variable Binds and the Interlisp Stack 11-1 Spaghetti Stack 11-2 Stack Functions 11-3 Searching the Stack 11-4 Variable Binds in Stack Frames 11-5 Evaluating Expressions in Stack Frames 11-6 Altering Flow of Control 11-6 Releasing and Reusing Stack Pointers 11-7 Backtrace Functions 11-8 Other Stack Functions 11-10 The Stack and the Interpreter 11-10 Generators 11-12 Coroutines 11-14 Possibilities Lists 11-15 12. Miscellaneous 12-1 Greeting and Initialization Files 12-1 Idle Mode 12-3 Saving Virtual Memory State 12-5 System Version Information 12-9 Date and Time Functions 12-11 Timers and Duration Functions 12-13 Resources 12-15 A Simple Example 12-16 Trade-offs in More Complicated Cases 12-18 Macros for Accessing Resources 12-18 Saving Resources in a File 12-19 Pattern Matching 12-19 Pattern Elements 12-20 Element Patterns 12-20 Segment Patterns 12-21 Assignments 12-23 Place-Markers 12-23 Replacements 12-24 Reconstruction 12-24 Examples 12-25 Volume 2 - Environment Reference 13. Interlisp Executive 13-1 Input Formats 13-3 Programmer's Assistant Commands 13-4 Event Specification 13-4 Commands 13-6 P.A. Commands Applied to P.A. Commands 13-15 Changing the Programmer's Assistant 13-16 Undoing 13-19 Undoing Out of Order 13-20 SAVESET 13-21 UNDONLSETQ and RESETUNDO 13-22 Format and Use of the History List 13-23 Programmer's Assistant Functions 13-26 The Editor and the Programmer's Assistant 13-32 14. Errors and Breaks 14-1 Breaks 14-1 Break Windows 14-2 Break Commands 14-3 Controlling When to Break 14-10 Break Window Variables 14-11 Creating Breaks with BREAK1 14-12 Signalling Errors 14-14 Catching Errors 14-16 Changing and Restoring System State 14-18 Error List 14-20 15. Breaking, Tracing, and Advising 15-1 Breaking Functions and Debugging 15-1 Advising 15-7 Implementation of Advising 15-7 Advise Functions 15-8 16. List Structure Editor 16-1 SEdit 16-1 Local Attention-Changing Commands 16-10 Commands That Search 16-14 Search Algorithm 16-15 Search Commands 16-16 Location Specification 16-18 Commands That Save and Restore the Edit Chain 16-21 Commands That Modify Structure 16-22 Implementation 16-23 The A, B, and : Commands 16-24 Form Oriented Editing and the Role of UP 16-26 Extract and Embed 16-26 The MOVE Command 16-28 Commands That Move Parentheses 16-30 TO and THRU 16-31 The R Command 16-34 Commands That Print 16-35 Commands for Leaving the Editor 16-37 Nested Calls to Editor 16-39 Manipulating the Characters of an Atom or String 16-39 Manipulating Predicates and Conditional Expressions 16-40 History Commands in the Editor 16-41 Miscellaneous Commands 16-41 Commands That Evaluate 16-43 Commands That Test 16-45 Edit Macros 16-46 Undo 16-48 EDITDEFAULT 16-50 Editor Functions 16-51 Time Stamps 16-57 17. File Package 17-1 Loading Files 17-3 Storing Files 17-8 Remaking a Symbolic File 17-12 Loading Files in a Distributed Environment 17-13 Marking Changes 17-13 Noticing Files 17-15 Distributing Change Information 17-16 File Package Types 17-16 Functions for Manipulating Typed Definitions 17-19 Defining New File Package Types 17-23 File Package Commands 17-25 Functions and Macros 17-26 Variables 17-27 Litatom Properties 17-29 Miscellaneous File Package Commands 17-30 DECLARE: 17-31 Exporting Definitions 17-33 FileVars 17-34 Defining New File Package Commands 17-35 Functions for Manipulating File Command Lists 17-37 Symbolic File Format 17-38 Copyright Notices 17-40 Functions Used Within Source Files 17-42 File Maps 17-42 18. Compiler 18-1 Compiler Printout 18-2 Global Variables 18-3 Local Variables and Special Variables 18-4 Constants 18-5 Compiling Function Calls 18-6 FUNCTION and Functional Arguments 18-7 Open Functions 18-8 COMPILETYPELST 18-8 Compiling CLISP 18-9 Compiler Functions 18-9 Block Compiling 18-12 Block Declarations 18-13 Block Compiling Functions 18-15 Compiler Error Messages 18-16 19. DWIM 20-1 Spelling Correction Protocol 20-3 Parentheses Errors Protocol 20-4 Undefined Function T Errors 20-4 DWIM Operation 20-5 DWIM Correction: Unbound Atoms 20-6 Undefined CAR of Form 20-7 Undefined Function in APPLY 20-8 DWIMUSERFORMS 20-8 DWIM Functions and Variables 20-10 Spelling Correction 20-11 Synonyms 20-12 Spelling Lists 20-12 Generators for Spelling Correction 20-14 Spelling Corrector Algorithm 20-14 Spelling Corrector Functions and Variables 20-15 20. CLISP 21-1 CLISP Interaction with User 21-4 CLISP Character Operators 21-5 Declarations 21-9 CLISP Operation 21-10 CLISP Translations 21-12 DWIMIFY 21-13 CLISPIFY 21-16 Miscellaneous Functions and Variables 21-18 CLISP Internal Conventions 21-20 21. Performance Issues 22-1 Storage Allocation and Garbage Collection 22-1 Variable Bindings 22-4 Performance Measuring 22-5 BREAKDOWN 22-7 GAINSPACE 22-9 Using Data Types Instead of Records 22-9 Using Incomplete File Names 22-10 Using "Fast" and "Destructive" Functions 22-10 22. Processes 23-1 Creating and Destroying Processes 23-1 Process Control Constructs 23-4 Events 23-5 Monitors 23-7 Global Resources 23-8 Typein and the TTY Process 23-9 Switing the TTY Process 23-9 Handling of Interrupts 23-11 Keeping the Mouse Alive 23-12 Process Status Window 23-12 Non-Process Compatibility 23-14 Volume 3 - I/O Reference 23. Streams and Files 24-1 Opening and Closing File Streams 24-1 File Names 24-4 Incomplete File Names 24-7 Version Recognition 24-9 Using File Names Instead of Streams 24-10 File Name Efficiency Considerations 24-11 Obsolete File Opening Functions 24-11 Converting Old Programs 24-11 Using Files with Processes 24-12 File Attributes 24-12 Closing and Reopening Files 24-15 Local Hard Disk Device 24-16 Floppy Disk Device 24-18 I/O Operations To and From Strings 24-22 Temporary Files and the CORE Device 24-23 NULL Device 24-24 Deleting, Copying, and Renaming Files 24-24 Searching File Directories 24-24 Listing File Directories 24-25 File Servers 24-28 PUP File Server Protocols 24-28 Xerox NS File Server Protocols 24-28 Operating System Designations 24-29 Logging In 24-30 Abnormal Conditions 24-31 24. Input/Output Functions 25-1 Specifying Streams for Input/Output Functions 25-1 Input Functions 25-2 Output Functions 25-6 PRINTLEVEL 25-8 Printing Numbers 25-10 User Defined Printing 25-12 Printing Unusual Data Structures 25-13 Random Access File Operations 25-14 Input/Output Operations with Characters and Bytes 25-17 PRINTOUT 25-17 Horizontal Spacing Commands 25-19 Vertical Spacing Commands 25-20 Special Formatting Controls 25-20 Printing Specifications 25-20 Paragraph Format 25-21 Right-Flushing 25-21 Centering 25-22 Numbering 25-22 Escaping to Lisp 25-23 User-Defined Commands 25-23 Special Printing Functions 25-24 READFILE and WRITEFILE 25-25 Read Tables 25-25 Read Table Functions 25-26 Syntax Classes 25-26 Read Macros 25-29 25. User Input/Output Packages 26-1 Inspector 26-1 Calling the Inspector 26-1 Multiple Ways of Inspecting 26-2 Inspect Windows 26-3 Inspect Window Commands 26-3 Interaction with Break Windows 26-4 Controlling the Amount Displayed During Inspection 26-4 Inspect Macros 26-4 INSPECTWs 26-5 PROMPTFORWORD 26-7 ASKUSER 26-9 Format of KEYLST 26-10 Options 26-12 Operation 26-13 Completing a Key 26-14 Special Keys 26-15 Startup Protocol and Typeahead 26-16 TTYIN Typein Editor 26-17 Entering Input with TTYIN 26-17 Mouse Commands (Interlisp-D Only) 26-19 Display Editing Commands 26-19 Using TTYIN for Lisp Input 26-22 Useful Macros 26-23 Programming with TTYIN 26-23 Using TTYIN as a General Editor 26-25 ?= Handler 26-26 Read Macros 26-27 Assorted Flags 26-28 Special Responses 26-29 Display Types 26-30 Prettyprint 26-31 Comment Feature 26-33 Comment Pointers 26-34 Converting Comments to Lowercase 26-35 Special Prettyprint Controls 26-36 26. Graphics Output Operations 27-1 Primitive Graphics Concepts 27-1 Positions 27-1 Regions 27-1 Bitmaps 27-2 Textures 27-5 Opening Image Streams 27-6 Accessing Image Stream Fields 27-8 Current Position of an Image Stream 27-10 Moving Bits Between Bitmaps with BITBLT 27-11 Drawing Lines 27-13 Drawing Curves 27-14 Miscellaneous Drawing and Printing Operations 27-15 Drawing and Shading Grids 27-17 Display Streams 27-18 Fonts 27-19 Font Files and Font Directories 27-24 Font Profiles 27-24 Image Objects 27-27 IMAGEFNS Methods 27-28 Registering Image Objects 27-30 Reading and Writing Image Objects on Files 27-31 Copying Image Objects Between Windows 27-31 Implementation of Image Streams 27-32 27. Windows and Menus 28-1 Using the Window System 28-1 Changing the Window System 28-6 Interactive Display Functions 28-7 Windows 28-9 Window Properties 28-10 Creating Windows 28-10 Opening and Closing Windows 28-11 Redisplaying Windows 28-12 Reshaping Windows 28-13 Moving Windows 28-14 Exposing and Burying Windows 28-16 Shrinking Windows into Icons 28-16 Coordinate Systems, Extents, and Scrolling 28-18 Mouse Activity in Windows 28-21 Terminal I/O and Page Holding 28-22 TTY Process and the Caret 28-23 Miscellaneous Window Functions 28-24 Miscellaneous Window Properties 28-25 Example: A Scrollable Window 28-26 Menus 28-28 Menu Fields 28-29 Miscellaneous Menu Functions 28-32 Examples of Menu Use 28-32 Attached Windows 28-34 Attaching Menus to Windows 28-37 Attached Prompt Windows 28-38 Window Operations and Attached Windows 28-39 Window Properties of Attached Windows 28-41 28. Hardcopy Facilities 29-1 Hardcopy Functions 29-1 Low-Level Hardcopy Variables 29-4 29. Terminal Input/Output 30-1 Interrupt Characters 30-1 Terminal Tables 30-4 Terminal Syntax Classes 30-4 Terminal Control Functions 30-5 Line-Buffering 30-7 Dribble Files 30-10 Cursor and Mouse 30-10 Changing the Cursor Image 30-11 Flashing Bars on the Cursor 30-13 Cursor Position 30-13 Mouse Button Testing 30-14 Low-Level Mouse Functions 30-15 Keyboard Interpretation 30-15 Display Screen 30-18 Miscellaneous Terminal I/O 30-19 30. Ethernet 31-1 Ethernet Protocols 31-1 Protocol Layering 31-1 Level Zero Protocols 31-2 Level One Protocols 31-2 Higher Level Protocols 31-3 Connecting Networks: Routers and Gateways 31-3 Addressing Conflicts with Level Zero Mediums 31-3 References 31-4 Higher-Level PUP Protocol Functions 31-4 Higher-Level NS Protocol Functions 31-5 Name and Address Conventions 31-5 Clearinghouse Functions 31-7 NS Printing 31-9 SPP Stream Interface 31-9 Courier Remote Procedure Call Protocol 31-11 Defining Courier Programs 31-11 Courier Type Definitions 31-12 Pre-defined Types 31-13 Constructed Types 31-13 User Extensions to the Type Language 31-15 Performing Courier Transactions 31-16 Expedited Procedure Call 31-17 Expanding Ring Broadcast 31-18 Using Bulk Data Transfer 31-18 Courier Subfunctions for Data Transfer 31-19 Level One Ether Packet Format 31-20 PUP Level One Functions 31-21 Creating and Managing Pups 31-21 Sockets 31-22 Sending and Receiving Pups 31-23 Pup Routing Information 31-23 Miscellaneous PUP Utilities 31-24 PUP Debugging Aids 31-24 NS Level One Functions 31-28 Creating and Managing XIPs 31-28 NS Sockets 31-28 Sending and Receiving XIPs 31-29 NS Debugging Aids 31-29 Support for Other Level One Protocols 31-29 The SYSQUEUE Mechanism 31-31 Glossary GLOSSARY-1 Index INDEX-1 [This page intentionally left blank](LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))))),,"-T3 T25TTf2 5F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR5F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGRPALATINO  HELVETICA + HELVETICAPALATINO MODERN +MODERN +CLASSIC  HRULE.GETFNPALATINO   HRULE.GETFNPALATINO  + HRULE.GETFNPALATINO   HRULE.GETFNPALATINO   HRULE.GETFNCLASSIC#&*%*!   + %#"/*!&%1= !% 2%-+$'! $,&"!%.* )'0  "*+&!! (4% 0&&7:% 1&4'+*4*+'!"!!&"#*$2!,!/)"/'    &*+'!")*,!!&%"3($8#!#"&"%9 &!) "'($&##*.4  &!2-& # #$$2!%!&'$ $".-"!!!#!!03)(#. +&-$""#"",$4}z \ No newline at end of file diff --git a/docs/turpin-irm/IRM-1/01-INTRO.TEDIT b/docs/turpin-irm/IRM-1/01-INTRO.TEDIT new file mode 100644 index 00000000..176f6df1 Binary files /dev/null and b/docs/turpin-irm/IRM-1/01-INTRO.TEDIT differ diff --git a/docs/turpin-irm/IRM-1/02-LITATOM.TEDIT b/docs/turpin-irm/IRM-1/02-LITATOM.TEDIT new file mode 100644 index 00000000..c6830886 Binary files /dev/null and b/docs/turpin-irm/IRM-1/02-LITATOM.TEDIT differ diff --git a/docs/turpin-irm/IRM-1/03-lists.tedit b/docs/turpin-irm/IRM-1/03-lists.tedit new file mode 100644 index 00000000..cedb00d4 --- /dev/null +++ b/docs/turpin-irm/IRM-1/03-lists.tedit @@ -0,0 +1,143 @@ +INTERLISP-D REFERENCE MANUAL LISTS (3.% % LISTS% (Chapter) NIL NIL NIL NIL)3. LISTS 3 One of the most useful datatypes in Lisp is the list cell, a data structure that contains pointers to two other objects, called the CAR and the CDR of the list cell. You can build very complicated structures out of list cells, including lattices and trees, but most often they're used to represent simple linear lists of objects. The following functions are used to manipulate individual list cells: (CONS(CONS (Function) NIL NIL ("3") 1) X Y) [Function] CONS is the primary list construction function. It creates and returns a new list cell containing pointers to X and Y. If Y is a list, this returns a list with X added at the beginning of Y. (LISTP(LISTP (Function) NIL NIL ("3") 1) X) [Function] Returns X if X is a list cell, e.g., something created by CONS; NIL otherwise. (LISTP NIL) = NIL (NLISTP(NLISTP (Function) NIL NIL ("3") 1) X) [Function] The same as (NOT (LISTP X)). Returns T if X is not a list cell, NIL otherwise. However, (NLISTP NIL) = T (CAR(CAR (Function) NIL NIL ("3") 1) X) [Function] Returns the first element of the list X. CAR of NIL is always NIL. For all other nonlists (e.g., symbols, numbers, etc.), the value returned is controlled by CAR/CDRERR (below). (CDR(CDR (Function) NIL NIL ("3") 1) X) [Function] Returns all but the first element of the list X. CDR of NIL is always NIL. The value of CDR for other nonlists is controlled by CAR/CDRERR (below). CAR/CDRERR(CAR/CDRERR (Variable) NIL NIL ("3") 1) [Variable] The variable CAR/CDRERR controls the behavior of CAR and CDR when they are passed non-lists (other than NIL). If CAR/CDRERR = NIL (the current default), then CAR or CDR of a non-list (other than NIL) return the string "{car of non-list}" or "{cdr of non-list}". If CAR/CDRERR = T, then CAR and CDR of a non-list (other than NIL) causes an error. If CAR/CDRERR = ONCE, then CAR or CDR of a string causes an error, but CAR or CDR of anything else returns the string "{car of non-list}" or "{cdr of non-list}" as above. This catches loops which repeatedly take CAR or CDR of an object, but it allows one-time errors to pass undetected. If CAR/CDRERR = CDR, then CAR of a non-list returns "{car of non-list}" as above, but CDR of a non-list causes an error. This setting is based on the observation that nearly all infinite loops involving non-lists occur from taking CDRs, but a fair amount of careless code takes CAR of something it has not tested to be a list. (CAAR(CAAR (Function) NIL NIL ("3") 2) X) (CADR(CADR (Function) NIL NIL ("3") 2) X) ((CDDR (Function) NIL NIL ("3") 2)CDDR X) etc. (CAAAR (Function) NIL NIL ("3") 2) (CAADR (Function) NIL NIL ("3") 2) (CADAR (Function) NIL NIL ("3") 2) (CADDR (Function) NIL NIL ("3") 2) (CDAAR (Function) NIL NIL ("3") 2) (CDADR (Function) NIL NIL ("3") 2) (CDDAR (Function) NIL NIL ("3") 2) (CDDDR (Function) NIL NIL ("3") 2) (CAAAAR (Function) NIL NIL ("3") 2) (CAAADR (Function) NIL NIL ("3") 2) (CAADAR (Function) NIL NIL ("3") 2) (CAADDR (Function) NIL NIL ("3") 2) (CADAAR (Function) NIL NIL ("3") 2) (CADADR (Function) NIL NIL ("3") 2) (CADDAR (Function) NIL NIL ("3") 2) (CADDDR (Function) NIL NIL ("3") 2) (CDAAAR (Function) NIL NIL ("3") 2) (CDAADR (Function) NIL NIL ("3") 2) (CDADAR (Function) NIL NIL ("3") 2) (CDADDR (Function) NIL NIL ("3") 2) (CDDAAR (Function) NIL NIL ("3") 2) (CDDADR (Function) NIL NIL ("3") 2) (CDDDAR (Function) NIL NIL ("3") 2) (CDDDDR (Function) NIL NIL ("3") 2) [Function] Often, combinations of CAR and CDR are used to extract parts of complex list structures. Functions of the form C...R may be used for some of these combinations: (CAAR X) ==> (CAR (CAR X)) (CADR X) ==> (CAR (CDR X)) (CDDDDR X) ==> (CDR (CDR (CDR (CDR X)))) All 30 combinations of nested CARs and CDRs up to 4 deep are included in the system. (RPLACD(RPLACD (Function) NIL NIL ("3") 2) X Y) [Function] Replaces the CDR of the list cell X with Y. This physically changes the internal structure of X, as opposed to CONS, which creates a new list cell. You can make a circular list by using RPLACD to place a pointer to the beginning of a list at the end of the list. The value of RPLACD is X. An attempt to RPLACD NIL will cause an error, Attempt to RPLACD NIL (except for (RPLACD NIL NIL)). An attempt to RPLACD any other non-list will cause an error, Arg not list. (RPLACA(RPLACD (Function) NIL NIL ("3") 2) X Y) [Function] Like RPLACD, but replaces the CAR of X with Y. The value of RPLACA is X. An attempt to RPLACA NIL will cause an error, Attempt to RPLACA NIL, (except for (RPLACA NIL NIL)). An attempt to RPLACA any other non-list will cause an error, Arg not list. (RPLNODE(RPLNODE (Function) NIL NIL ("3") 2) X A D) [Function] Performs (RPLACA X A), (RPLACD X D), and returns X. (RPLNODE2(RPLNODE2 (Function) NIL NIL ("3") 2) X Y) [Function] Performs (RPLACA X (CAR Y)), (RPLACD X (CDR Y)) and returns X. (FRPLACD(FRPLACD (Function) NIL NIL ("3") 2) X Y) [Function] (FRPLACA(FRPLACA (Function) NIL NIL ("3") 2) X Y) [Function] (FRPLNODE(FRPLNODE (Function) NIL NIL ("3") 2) X A D) [Function] (FRPLNODE2(FRPLNODE2 (Function) NIL NIL ("3") 2) X Y) [Function] Faster versions of RPLACD, etc. Usually, you don't use list cells alone, but in structures called lists. A list is represented by a list cell whose CAR is the first element of the list, and whose CDR is the rest of the list. That's normally another list cell (with another element of the list) or the empty list, NIL, marking the list's end. List elements may be any Lisp objects, including other lists. You type in a list as a sequence of Lisp data objects (symbols, numbers, other lists, etc.) enclosed in parentheses or brackets. Note that () is read as the symbol NIL. Sometimes, you won't want your list to end in NIL, but just with the final element. To indicate that, type a period (with spaces on both sides) in front of the final element. This makes CDR of the list's final cell be the element immediately following the period, e.g. (A . B) or (A B C . D). Note that a list needn't end in NIL. It is simply a structure composed of one or more list cells. The input sequence (A B C . NIL) is equivalent to (A B C), and (A B . (C D)) is equivalent to (A B C D). Note, however, that (A B . C D) will create a list containing the five symbols A, B, %., C, and D. Lists are printed by printing a left parenthesis, and then printing the first element of the list, a space, the second element, etc., until the final list cell is reached. The individual elements of a list are printed by PRIN1, if the list is being printed by PRIN1, and by PRIN2 if the list is being printed by PRINT or PRIN2. Lists are considered to terminate when CDR of some node is not a list. If CDR of this terminal node is NIL (the usual case), CAR of the last node is printed followed by a right parenthesis. If CDR of the terminal node is not NIL, CAR of the last node is printed, followed by a space, a period, another space, CDR of the last node, and the right parenthesis. A list input as (A B C . NIL) will print as (A B C), and a list input as (A B . (C D)) will print as (A B C D). PRINTLEVEL affects the printing of lists (see the PRINTLEVEL section of Chapter 25), and that carriage returns may be inserted where dictated by LINELENGTH (see the Output Functions section of Chapter 25). Note: Be careful when testing the equality of list structures. EQ will be true only when the two lists are the exact same list. For example, (SETQ A '(1 2)) (1 2) (SETQ B A) (1 2) (EQ A B) T (SETQ C '(1 2)) (1 2) (EQ A C) NIL (EQUAL A C) T In the example above, the values of A and B are the exact same list, so they are EQ. However, the value of C is a totally different list, although it happens to have the same elements. EQUAL should be used to compare the elements of two lists. In general, one should notice whether list manipulation functions use EQ or EQUAL for comparing lists. This is a frequent source of errors. Creating Lists 1 (LIST(MKLIST (Function) NIL NIL ("3") 3) X1 X2 ... XN) [NoSpread Function] Returns a list of its arguments, e.g. (LIST 'A 'B '(C D)) => (A B (C D)) (LIST*(LIST* (Function) NIL NIL ("3") 3) X1 X2 ... XN) [NoSpread Function] Returns a list of its arguments, using the last argument for the tail of the list. This is like an iterated CONS: (LIST* A B C) == (CONS A (CONS B C)). For example, (LIST* 'A 'B 'C) => (A B . C) (LIST* 'A 'B '(C D)) => (A B C D) (APPEND(APPEND (Function) NIL NIL ("3") 4) X1 X2 ... XN) [NoSpread Function] Copies the top level of the list X1 and appends this to a copy of the top level of the list X2 appended to ... appended to XN, e.g., (APPEND '(A B) '(C D E) '(F G)) => (A B C D E F G) Only the first N-1 lists are copied. However N = 1 is treated specially; (APPEND X) copies the top level of a single list. To copy a list to all levels, use COPY. The following examples illustrate the treatment of non-lists: (APPEND '(A B C) 'D) => (A B C . D) (APPEND 'A '(B C D)) => (B C D) (APPEND '(A B C . D) '(E F G)) => (A B C E F G) (APPEND '(A B C . D)) => (A B C . D) (NCONC(NCONC (Function) NIL NIL ("3") 4) X1 X2 ... XN) [NoSpread Function] Returns the same value as APPEND, but modifies the list structure of X1 ... Xn-1. NCONC cannot change NIL to a list: (SETQ FOO NIL) NIL (NCONC FOO '(A B C)) (A B C) FOO NIL Although the value of the NCONC is (A B C), FOO has not been changed. The problem is that while it is possible to alter list structure with RPLACA and RPLACD, there is no way to change the non-list NIL to a list. (NCONC1(NCONC1 (Function) NIL NIL ("3") 4) LST X) [Function] Adds X to the end of LST: (NCONC LST (LIST X)) (ATTACH(ATTACH (Function) NIL NIL ("3") 4) X L) [Function] Attaches X to the front of L by doing a RPLACA and RPLACD. The value is EQUAL to (CONS X L), but EQ to L, which it physically changes (except if L is NIL). (ATTACH X NIL) is the same as (CONS X NIL). Otherwise, if L is not a list, an error is generated, Arg not list. (MKLIST(MKLIST (Function) NIL NIL ("3") 4) X) [Function] Make List. If X is a list or NIL, returns X; Otherwise, returns (LIST X). Building Lists From Left to Right 1 (TCONC(TCONC (Function) NIL NIL ("3") 4) PTR X) [Function] TCONC is similar to NCONC1; it is useful for building a list by adding elements one at a time at the end. Unlike NCONC1, TCONC does not have to search to the end of the list each time it is called. Instead, it keeps a pointer to the end of the list being assembled, and updates this pointer after each call. This can be considerably faster for long lists. The cost is an extra list cell, PTR. (CAR PTR) is the list being assembled, (CDR PTR) is (LAST (CAR PTR)). TCONC returns PTR, with its CAR and CDR appropriately modified. PTR can be initialized in two ways. If PTR is NIL, TCONC will create and return a PTR. In this case, the program must set some variable to the value of the first call to TCONC. After that, it is unnecessary to reset the variable, since TCONC physically changes its value. Example: (SETQ FOO (TCONC NIL 1)) ((1) 1) (for I from 2 to 5 do (TCONC FOO I)) NIL FOO ((1 2 3 4 5) 5) If PTR is initially (NIL), the value of TCONC is the same as for PTR = NIL. but TCONC changes PTR. This method allows the program to initialize the TCONC variable before adding any elements to the list. Example: (SETQ FOO (CONS)) (NIL) (for I from 1 to 5 do (TCONC FOO I)) NIL FOO ((1 2 3 4 5) 5) (LCONC(LCONC (Function) NIL NIL ("3") 5) PTR X) [Function] Where TCONC is used to add elements at the end of a list, LCONC is used for building a list by adding lists at the end, i.e., it is similar to NCONC instead of NCONC1. Example: (SETQ FOO (CONS)) (NIL) (LCONC FOO '(1 2)) ((1 2) 2) (LCONC FOO '(3 4 5)) ((1 2 3 4 5) 5) (LCONC FOO NIL) ((1 2 3 4 5) 5) LCONC uses the same pointer conventions as TCONC for eliminating searching to the end of the list, so that the same pointer can be given to TCONC and LCONC interchangeably. Therefore, continuing from above, (TCONC FOO NIL) ((1 2 3 4 5 NIL) NIL) (TCONC FOO '(3 4 5)) ((1 2 3 4 5 NIL (3 4 5)) (3 4 5)) The functions DOCOLLECT and ENDCOLLECT also let you build lists from left-to-right like TCONC, but without the overhead of an extra list cell. The listis kept as a circular list. DOCOLLECT adds items; ENDCOLLECT replaces the tail with its second argument, and returns the full list. (DOCOLLECT(DOCOLLECT (Function) NIL NIL ("3") 6) ITEM LST) [Function] Adds ITEM to the end of LST. Returns the new circular list. Note that LST is modified, but it is not EQ to the new list. The new list should be stored and used as LST to the next call to DOCOLLECT. (ENDCOLLECT(DOCOLLECT (Function) NIL NIL ("3") 6) LST TAIL) [Function] Takes LST, a list returned by DOCOLLECT, and returns it as a non-circular list, adding TAIL as the terminating CDR. Here is an example using DOCOLLECT and ENDCOLLECT. HPRINT is used to print the results because they are circular lists. Notice that FOO has to be set to the value of DOCOLLECT as each element is added. (SETQ FOO NIL] NIL (HPRINT (SETQ FOO (DOCOLLECT 1 FOO] (1 . {1}) (HPRINT (SETQ FOO (DOCOLLECT 2 FOO] (2 1 . {1}) (HPRINT (SETQ FOO (DOCOLLECT 3 FOO] (3 1 2 . {1}) (HPRINT (SETQ FOO (DOCOLLECT 4 FOO] (4 1 2 3 . {1}) (SETQ FOO (ENDCOLLECT FOO 5] (1 2 3 4 . 5) The following two functions are useful when writing programs that reuse a scratch list to collect together some result(s) (both of these compile open): (SCRATCHLIST(SCRATCHLIST (Function) NIL NIL ("3") 6) LST X1 X2 ... XN) [NLambda NoSpread Function] SCRATCHLIST sets up a context in which the value of LST is used as a scratch list. The expressions X1, X2, ... XN are evaluated in turn. During the course of evaluation, any value passed to ADDTOSCRATCHLIST will be saved, reusing CONS cells from the value of LST. If the value of LST is not long enough, new CONS cells will be added onto its end. If the value of LST is NIL, the entire value of SCRATCHLIST will be new (i.e., no CONS cells will be reused). (ADDTOSCRATCHLIST(ADDTOSCRATCHLIST (Function) NIL NIL ("3") 6) VALUE) [Function] For use under calls to SCRATCHLIST. VALUE is added on to the end of the list of things being collected by SCRATCHLIST. When SCRATCHLIST returns, its value is a list containing all of the things added by ADDTOSCRATCHLIST. Copying Lists 1 (COPY(COPY (Function) NIL NIL ("3") 6) X) [Function] Creates and returns a copy of the list X. All levels of X are copied down to non-lists, so that if X contains arrays and strings, the copy of X will contain the same arrays and strings, not copies. COPY is recursive in the CAR direction only, so very long lists can be copied. To copy just the top level of X, do (APPEND X). (COPYALL(COPYALL (Function) NIL NIL ("3") 7) X) [Function] Like COPY, but it copies down to atoms. Arrays, hash-arrays, strings, user data types, etc., are all copied. Analagous to EQUALALL (see the Equality Predicates section of Chapter 9). This will not work if given a data structure with circular pointers; in this case, use HCOPYALL. (HCOPYALL(HCOPYALL (Function) NIL NIL ("3") 7) X) [Function] Like COPYALL, but it will work even if the data structure contains circular pointers. Extracting Tails of Lists 1 (NTH(NTH (Function) NIL NIL ("3") 7) X N) [Function] Returns the tail of X beginning with the Nth element. Returns NIL if X has fewer than N elements. This is different from Common Lisp's NTH. Examples: (NTH '(A B C D) 1) => (A B C D) (NTH '(A B C D) 3) => (C D) (NTH '(A B C D) 9) => NIL (NTH '(A . B) 2) => B For consistency, if N = 0, NTH returns (CONS NIL X): (NTH '(A B) 0) => (NIL A B) (FNTH(FNTH (Function) NIL NIL ("3") 7) X N) [Function] Faster version of NTH that terminates on a null-check. (LAST(LAST (Function) NIL NIL ("3") 7) X) [Function] Returns the last list cell in the list X. Returns NIL if X is not a list. Examples: (LAST '(A B C)) => (C) (LAST '(A B . C)) => (B . C) (LAST 'A) => NIL (FLAST(FLAST (Function) NIL NIL ("3") 7) X) [Function] Faster version of LAST that terminates on a null-check. (NLEFT(NLEFT (Function) NIL NIL ("3") 7) L N TAIL) [Function] NLEFT returns the tail of L that contains N more elements than TAIL. If L does not contain N more elements than TAIL, NLEFT returns NIL. If TAIL is NIL or not a tail of L, NLEFT returns the last N list cells in L. NLEFT can be used to work backwards through a list. Example: (SETQ FOO '(A B C D E)) (A B C D E) (NLEFT FOO 2) (D E) (NLEFT FOO 1 (CDDR FOO)) (B C D E) (NLEFT FOO 3 (CDDR FOO)) NIL (LASTN(LASTN (Function) NIL NIL ("3") 8) L N) [Function] Returns (CONS X Y), where Y is the last N elements of L, and X is the initial segment, e.g., (LASTN '(A B C D E) 2) => ((A B C) D E) (LASTN '(A B) 2) => (NIL A B) Returns NIL if L is not a list containing at least N elements. (TAILP(TAILP (Function) NIL NIL ("3") 8) X Y) [Function] Returns X, if X is a tail of the list Y; otherwise NIL. X is a tail of Y if it is EQ to 0 or more CDRs of Y. Note: If X is EQ to 1 or more CDRs of Y, X is called a proper tail. Counting List Cells 1 (LENGTH(LENGTH (Function) NIL NIL ("3") 8) X) [Function] Returns the length of the list X, where length is defined as the number of CDRs required to reach a non-list. Examples: (LENGTH '(A B C)) => 3 (LENGTH '(A B C . D)) => 3 (LENGTH 'A) => 0 (FLENGTH(FLENGTH (Function) NIL NIL ("3") 8) X) [Function] Faster version of LENGTH that terminates on a null-check. (EQLENGTH(EQLENGTH (Function) NIL NIL ("3") 8) X N) [Function] Equivalent to (EQUAL (LENGTH X) N), but more efficient, because EQLENGTH stops as soon as it knows that X is longer than N. EQLENGTH is safe to use on (possibly) circular lists, since it is bounded by N. (COUNT(COUNT (Function) NIL NIL ("3") 8) X) [Function] Returns the number of list cells in the list X. Thus, COUNT is like a LENGTH that goes to all levels. COUNT of a non-list is 0. Examples: (COUNT '(A)) => 1 (COUNT '(A . B)) => 1 (COUNT '(A (B) C)) => 4 In this last example, the value is 4 because the list (A X C) uses three list cells for any object X, and (B) uses another list cell. (COUNTDOWN(COUNT (Function) NIL NIL ("3") 8) X N) [Function] Counts the number of list cells in X, decrementing N for each one. Stops and returns N when it finishes counting, or when N reaches 0. COUNTDOWN can be used on circular structures since it is bounded by N. Examples: (COUNTDOWN '(A) 100) => 99 (COUNTDOWN '(A . B) 100) => 99 (COUNTDOWN '(A (B) C) 100) => 96 (COUNTDOWN (DOCOLLECT 1 NIL) 100) => 0 (EQUALN(EQUALN (Function) NIL NIL ("3") 9) X Y DEPTH) [Function] Like EQUAL, for use with (possibly) circular structures. Whenever the depth of CAR recursion plus the depth of CDR recursion exceeds DEPTH, EQUALN does not search further along that chain, and returns the symbol ?. If recursion never exceeds DEPTH, EQUALN returns T if the expressions X and Y are EQUAL; otherwise NIL. (EQUALN '(((A)) B) '(((Z)) B) 2) => ? (EQUALN '(((A)) B) '(((Z)) B) 3) => NIL (EQUALN '(((A)) B) '(((A)) B) 3) => T Set Operations 1 (INTERSECTION(INTERSECTION (Function) NIL NIL ("3") 9) X Y) [Function] Returns a list whose elements are members of both lists X and Y (using EQUAL to do compares). Note that (INTERSECTION X X) gives a list of all members of X without duplicates. (UNION(INTERSECTION (Function) NIL NIL ("3") 9) X Y) [Function] Returns a (new) list consisting of all elements included on either of the two original lists (using EQUAL to compare elements). It is more efficient for X to be the shorter list. The value of UNION is Y with all elements of X not in Y CONSed on the front of it. Therefore, if an element appears twice in Y, it will appear twice in (UNION X Y). Since (UNION '(A) '(A A)) = (A A), while (UNION '(A A) '(A)) = (A), UNION is non-commutative. (LDIFFERENCE(LDIFFERENCE (Function) NIL NIL ("3") 9) X Y) [Function] List Difference. Returns a list of the elements in X that are not members of Y (using EQUAL to compare elements). Note: If X and Y share no elements, LDIFFERENCE returns a copy of X. (LDIFF(INTERSECTION (Function) NIL NIL ("3") 9) LST TAIL ADD) [Function] TAIL must be a tail of LST, i.e., EQ to the result of applying some number of CDRs to LST. (LDIFF LST TAIL) returns a list of all elements in LST up to TAIL. If ADD is not NIL, the value of LDIFF is effectively (NCONC ADD (LDIFF LST TAIL)), i.e., the list difference is added at the end of ADD. If TAIL is not a tail of LST, LDIFF generates an error, LDIFF: not a tail. LDIFF terminates on a null-check, so it will go into an infinite loop if LST is a circular list and TAIL is not a tail. Example: (SETQ FOO '(A B C D E F)) (A B C D E F) (CDDR FOO) (C D E F) (LDIFF FOO (CDDR FOO)) (A B) (LDIFF FOO (CDDR FOO) '(1 2)) (1 2 A B) (LDIFF FOO '(C D E F)) LDIFF: not a tail (C D E F) Note that the value of LDIFF is always new list structure unless TAIL = NIL, in which case the value is LST itself. Searching Lists 1 (MEMB(MEMB (Function) NIL NIL ("3") 10) X Y) [Function] Determines if X is a member of the list Y. If there is an element of Y EQ to X, returns the tail of Y starting with that element. Otherwise, returns NIL. Examples: (MEMB 'A '(A (W) C D)) => (A (W) C D) (MEMB 'C '(A (W) C D)) => (C D) (MEMB 'W '(A (W) C D)) => NIL (MEMB '(W) '(A (W) C D)) => NIL (FMEMB(FMEMB (Function) NIL NIL ("3") 10) X Y) [Function] Faster version of MEMB that terminates on a null-check. (MEMBER(MEMBER (Function) NIL NIL ("3") 10) X Y) [Function] Identical to MEMB except that it uses EQUAL instead of EQ to check membership of X in Y. Examples: (MEMBER 'C '(A (W) C D)) => (C D) (MEMBER 'W '(A (W) C D)) => NIL (MEMBER '(W) '(A (W) C D)) => ((W) C D) (EQMEMB(EQMEMB (Function) NIL NIL ("3") 10) X Y) [Function] Returns T if either X is EQ to Y, or else Y is a list and X is an FMEMB of Y. Substitution Functions 1 (SUBST(SUBST (Function) NIL NIL ("3") 10) NEW OLD EXPR) [Function] Returns the result of substituting NEW for all occurrences of OLD in the expression EXPR. Substitution occurs whenever OLD is EQUAL to CAR of some subexpression of EXPR, or when OLD is atomic and EQ to a non-NIL CDR of some subexpression of EXPR. For example: (SUBST 'A 'B '(C B (X . B))) => (C A (X . A)) (SUBST 'A '(B C) '((B C) D B C)) => (A D B C) not (A D . A) SUBST returns a copy of EXPR with the appropriate changes. Furthermore, if NEW is a list, it is copied at each substitution. (DSUBST(DSUBST (Function) NIL NIL ("3") 11) NEW OLD EXPR) [Function] Like SUBST, but it does not copy EXPR, but changes the list structure EXPR itself. Like SUBST, DSUBST substitutes with a copy of NEW. More efficient than SUBST. (LSUBST(LSUBST (Function) NIL NIL ("3") 11) NEW OLD EXPR) [Function] Like SUBST, but NEW is substituted as a segment of the list EXPR rather than as an element. For instance, (LSUBST '(A B) 'Y '(X Y Z)) => (X A B Z) If NEW is not a list, LSUBST returns a copy of EXPR with all OLD's deleted: (LSUBST NIL 'Y '(X Y Z)) => (X Z) (SUBLIS(SUBLIS (Function) NIL NIL ("3") 11) ALST EXPR FLG) [Function] ALST is a list of pairs: ((OLD1 . NEW1) (OLD2 . NEW2) ... (OLDN . NEWN)) Each OLDi is an atom. SUBLIS returns the result of substituting each NEWi for the corresponding OLDi in EXPR, e.g., (SUBLIS '((A . X) (C . Y)) '(A B C D)) => (X B Y D) If FLG = NIL, new structure is created only if needed, so if there are no substitutions, the value is EQ to EXPR. If FLG = T, the value is always a copy of EXPR. (DSUBLIS(DSUBLIS (Function) NIL NIL ("3") 11) ALST EXPR FLG) [Function] Like SUBLIS, but it changes the list structure EXPR itself instead of copying it. (SUBPAIR(SUBPAIR (Function) NIL NIL ("3") 11) OLD NEW EXPR FLG) [Function] Like SUBLIS, but elements of NEW are substituted for corresponding atoms of OLD in EXPR, e.g., (SUBPAIR '(A C) '(X Y) '(A B C D)) => (X B Y D) As with SUBLIS, new structure is created only if needed, or if FLG = T, e.g., if FLG = NIL and there are no substitutions, the value is EQ to EXPR. If OLD ends in an atom other than NIL, the rest of the elements on NEW are substituted for that atom. For example, if OLD = (A B . C) and NEW = (U V X Y Z), U is substituted for A, V for B, and (X Y Z) for C. Similarly, if OLD itself is an atom (other than NIL), the entire list NEW is substituted for it. Examples: (SUBPAIR '(A B . C) '(W X Y Z) '(C A B B Y)) => ((Y Z) W X X Y) SUBST, DSUBST, and LSUBST all substitute copies of the appropriate expression, whereas SUBLIS, and DSUBLIS, and SUBPAIR substitute the identical structure (unless FLG = T). For example: (SETQ FOO '(A B)) (A B) (SETQ BAR '(X Y Z)) (X Y Z) (DSUBLIS (LIST (CONS 'X FOO)) BAR) ((A B) Y Z) (DSUBLIS (LIST (CONS 'Y FOO)) BAR T) ((A B) (A B) Z) (EQ (CAR BAR) FOO) T (EQ (CADR BAR) FOO) NIL Association Lists and Property Lists 1 It is often useful to associate a set of property names (NAME1, NAME2, etc.), with a set of property values (VALUE1, VALUE2, etc.). Two list structures commonly used to store such associations are called property lists and association lists. A list in association list format is a list where each element is a call whose CAR is a property name, and whose CDR is the value: ( (NAME1 . VALUE1) (NAME2 . VALUE2) ...) A list in property list format is a list where the first, third, etc. elements are the property names, and the second, forth, etc. elements are the associated values: ( NAME1 VALUE1 NAME2 VALUE2 ...) Another data structure that offers some of the advantages of association lists and property lists is the hash array (see the first page of Chapter 6). The functions below provide facilities for searching and changing lists in property list or association list format. Note: Property lists are used in many Medley system datatypes. There are special functions that can be used to set and retrieve values from the property lists of symbols (see the Property Lists section of Chapter 2), from properties of windows (see the Window Properties section of Chapter 28), etc. (ASSOC(ASSOC (Function) NIL NIL ("3") 12) KEY ALST) [Function] ALST is a list of lists. ASSOC returns the first sublist of ALST whose CAR is EQ to KEY. If such a list is not found, ASSOC returns NIL. Example: (ASSOC 'B '((A . 1) (B . 2) (C . 3))) => (B . 2) (FASSOC(FASSOC (Function) NIL NIL ("3") 12) KEY ALST) [Function] Faster version of ASSOC that terminates on a null-check. (SASSOC(SASSOC (Function) NIL NIL ("3") 12) KEY ALST) [Function] Same as ASSOC, but uses EQUAL instead of EQ when searching for KEY. (PUTASSOC(PUTASSOC (Function) NIL NIL ("3") 12) KEY VAL ALST) [Function] Searches ALST for a sublist CAR of which is EQ to KEY. If one is found, the CDR is replaced (using RPLACD) with VAL. If no such sublist is found, (CONS KEY VAL) is added at the end of ALST. Returns VAL. If ALST is not a list, generates an error, Arg not list. The argument order for ASSOC, PUTASSOC, etc. is different from that of LISTGET, LISTPUT, etc. (LISTGET(LISTGET (Function) NIL NIL ("3") 13) LST PROP) [Function] Searches LST two elements at a time, by CDDR, looking for an element EQ to PROP. If one is found, returns the next element of LST, otherwise NIL. Returns NIL if LST is not a list. Example: (LISTGET '(A 1 B 2 C 3) 'B) => 2 (LISTGET '(A 1 B 2 C 3) 'W) => NIL (LISTPUT(LISTGET (Function) NIL NIL ("3") 13) LST PROP VAL) [Function] Searches LST two elements at a time, by CDDR, looking for an element EQ to PROP. If PROP is found, replaces the next element of LST with VAL. Otherwise, PROP and VAL are added to the end of LST. If LST is a list with an odd number of elements, or ends in a non-list other than NIL, PROP and VAL are added at its beginning. Returns VAL. If LST is not a list, generates an error, Arg not list. (LISTGET1(LISTGET1 (Function) NIL NIL ("3") 13) LST PROP) [Function] Like LISTGET, but searches LST one CDR at a time, i.e., looks at each element. Returns the next element after PROP. Examples: (LISTGET1 '(A 1 B 2 C 3) 'B) => 2 (LISTGET1 '(A 1 B 2 C 3) '1) => B (LISTGET1 '(A 1 B 2 C 3) 'W) => NIL (LISTPUT1(LISTPUT1 (Function) NIL NIL ("3") 13) LST PROP VAL) [Function] Like LISTPUT, but searches LST one CDR at a time. Returns the modified LST. Example: (SETQ FOO '(A 1 B 2)) (A 1 B 2) (LISTPUT1 FOO 'B 3) (A 1 B 3) (LISTPUT1 FOO 'C 4) (A 1 B 3 C 4) (LISTPUT1 FOO 1 'W) (A 1 W 3 C 4) FOO (A 1 W 3 C 4) If LST is not a list, no error is generated. However, since a non-list cannot be changed into a list, LST is not modified. In this case, the value of LISTPUT1 should be saved. Example: (SETQ FOO NIL) NIL (LISTPUT1 FOO 'A 5) (A 5) FOO NIL Sorting Lists 1 (SORT(SORT (Function) NIL NIL ("3") 14) DATA COMPAREFN) [Function] DATA is a list of items to be sorted using COMPAREFN, a predicate function of two arguments which can compare any two items on DATA and return T if the first one belongs before the second. If COMPAREFN is NIL, ALPHORDER is used; thus (SORT DATA) will alphabetize a list. If COMPAREFN is T, CAR's of items that are lists are given to ALPHORDER, otherwise the items themselves; thus (SORT A-LIST T) will alphabetize an assoc list by the CAR of each item. (SORT X 'ILESSP) will sort a list of integers. The value of SORT is the sorted list. The sort is destructive and uses no extra storage. The value returned is EQ to DATA but elements have been switched around. There is no safe way to interrupt SORT. If you abort a call to SORT by any means, you may loose elements from the list beeing sorted. The algorithm used by SORT is such that the maximum number of compares is N*log2N, where N is (LENGTH DATA). Note: If (COMPAREFN A B) = (COMPAREFN B A), then the ordering of A and B may or may not be preserved. For example, if (FOO . FIE) appears before (FOO . FUM) in X, (SORT X T) may or may not reverse the order of these two elements. (MERGE(MERGE (Function) NIL NIL ("3") 14) A B COMPAREFN) [Function] A and B are lists which have previously been sorted using SORT and COMPAREFN. Value is a destructive merging of the two lists. It does not matter which list is longer. After merging both A and B are equal to the merged list. (In fact, (CDR A) is EQ to (CDR B)). (ALPHORDER(ALPHORDER (Function) NIL NIL ("3") 14) A B CASEARRAY) [Function] A predicate function of two arguments, for alphabetizing. Returns a non-NIL value if its arguments are in lexicographic order, i.e., if B does not belong before A. Numbers come before literal atoms, and are ordered by magnitude (using GREATERP). Literal atoms and strings are ordered by comparing the character codes in their print names. Thus (ALPHORDER 23 123) is T, whereas (ALPHORDER 'A23 'A123) is NIL, because the character code for the digit 2 is greater than the code for 1. Atoms and strings are ordered before all other data types. If neither A nor B are atoms or strings, the value of ALPHORDER is always T. If CASEARRAY is non-NIL, it is a casearray (see the Random Access File Operations section of Chapter 25) that the characters of A and B are translated through before being compared. Numbers are not passed through CASEARRAY. Note: If either A or B is a number, the value returned in the true case is T. Otherwise, ALPHORDER returns either EQUAL or LESSP to discriminate the cases of A and B being equal or unequal strings/atoms. Note: ALPHORDER does no UNPACKs, CHCONs, CONSes or NTHCHARs. It is several times faster for alphabetizing than anything that can be written using these other functions. (UALPHORDER(UALPHORDER (Function) NIL NIL ("3") 15) A B) [Function] Defined as (ALPHORDER A B UPPERCASEARRAY). UPPERCASEARRAY maps every lowercase character into the corresponding uppercase character. For more information on UPPERCASEARRAY see Chapter 25. (MERGEINSERT(MERGEINSERT (Function) NIL NIL ("3") 15) NEW LST ONEFLG) [Function] LST is NIL or a list of partially sorted items. MERGEINSERT tries to find the best place to (destructively) insert NEW, e.g., (MERGEINSERT 'FIE2 '(FOO FOO1 FIE FUM)) => (FOO FOO1 FIE FIE2 FUM) Returns LST. MERGEINSERT is undoable. If ONEFLG = T and NEW is already a member of LST, MERGEINSERT does nothing and returns LST. MERGEINSERT is used by ADDTOFILE (see the Functions for Manipulating File Command Lists section of Chapter 17) to insert the name of a new function into a list of functions. The algorithm is essentially to look for the item with the longest common leading sequence of characters with respect to NEW, and then merge NEW in starting at that point. Other List Functions 1 (REMOVE(REMOVE (Function) NIL NIL ("3") 15) X L) [Function] Removes all top-level occurrences of X from list L, returning a copy of L with all elements EQUAL to X removed. Example: (REMOVE 'A '(A B C (A) A)) => (B C (A)) (REMOVE '(A) '(A B C (A) A)) => (A B C A) (DREMOVE(DREMOVE (Function) NIL NIL ("3") 15) X L) [Function] Like REMOVE, but uses EQ instead of EQUAL, and actually modifies the list L when removing X, and thus does not use any additional storage. More efficient than REMOVE. DREMOVE cannot change a list to NIL: (SETQ FOO '(A)) (A) (DREMOVE 'A FOO) NIL FOO (A) The DREMOVE above returns NIL, and does not perform any CONSes, but the value of FOO is still (A), because there is no way to change a list to a non-list. See NCONC. (REVERSE(REVERSE (Function) NIL NIL ("3") 15) L) [Function] Reverses (and copies) the top level of a list, e.g., (REVERSE '(A B (C D))) => ((C D) B A) If L is not a list, REVERSE just returns L. (DREVERSE(DREVERSE (Function) NIL NIL ("3") 16) L) [Function] Value is the same as that of REVERSE, but DREVERSE destroys the original list L and thus does not use any additional storage. More efficient than REVERSE. (COMPARELISTS(COMPARELISTS (Function) NIL NIL ("3") 16) X Y) [Function] Compares the list structures X and Y and prints a description of any differences to the terminal. If X and Y are EQUAL lists, COMPARELISTS simply prints out SAME. Returns NIL. COMPARELISTS prints a terse description of the differences between the two list structures, highlighting the items that have changed. This printout is not a complete and perfect comparison. If X and Y are radically different list structures, the printout will not be very useful. COMPARELISTS is meant to be used as a tool to help users isolate differences between similar structures. When a single element has been changed for another, COMPARELISTS prints out items such as (A -> B), for example: (COMPARELISTS '(A B C D) '(X B E D)) (A -> X) (C -> E) NIL When there are more complex differences between the two lists, COMPARELISTS prints X and Y, highlighting differences and abbreviating similar elements as much as possible. & is used to signal a single element that is present in the same place in the two lists; -- signals an arbitrary number of elements in one list but not in the other; -2-, -3-, etc. signal a sequence of two, three, etc. elements that are the same in both lists. Examples: (COMPARELISTS '(A B C D) '(A D)) (A B C --) (A D) (COMPARELISTS '(A B C D E F G H) '(A B C D X)) (A -3- E F --) (A -3- X) (COMPARELISTS '(A B C (D E F (G) H) I) '(A B (G) C (D E F H) I)) (A & & (D -2- (G) &) &) (A & (G) & (D -2- &) &) (NEGATE(NEGATE (Function) NIL NIL ("3") 16) X) [Function] For a form X, returns a form which computes the negation of X . For example: (NEGATE '(MEMBER X Y)) => (NOT (MEMBER X Y)) (NEGATE '(EQ X Y)) => (NEQ X Y) (NEGATE '(AND X (NLISTP X))) => (OR (NULL X) (LISTP X)) (NEGATE NIL) => T [This page intentionally left blank] (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "3-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "3-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "3-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "3-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "3-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "3-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))5,-T0 +T306T1EVENT5`xx-T306T56ZZ,l~,l~,ll,ll,l~,l~,l~,lx206206,@ PAGEHEADING RIGHTPAGE? PAGEHEADINGLEFTBACK,ll,~~,ll,ll,HH, +TIMESROMAN +PALATINO PALATINO PALATINO PALATINO TITAN TITAN TITAN TITAN MODERN + HELVETICATITAN +CLASSIC +CLASSIC +CLASSIC +MODERNMODERN + )IM.INDEX.GETFNMODERN + + + HRULE.GETFNMODERN  F  !IM.INDEX.GETFNCLASSIC +   k%  "IM.INDEX.GETFNCLASSIC +,     #IM.INDEX.GETFNCLASSIC +           IM.INDEX.GETFNCLASSIC +  &   ^ + +   IM.INDEX.GETFNCLASSIC +  .    % + + +'IM.INDEX.GETFNCLASSIC +   +   ,              "  $  4  A     , .  !IM.INDEX.GETFN !IM.INDEX.GETFN  !IM.INDEX.GETFNTITAN   "IM.INDEX.GETFN"IM.INDEX.GETFN"IM.INDEX.GETFN"IM.INDEX.GETFN"IM.INDEX.GETFN"IM.INDEX.GETFN"IM.INDEX.GETFN"IM.INDEX.GETFN#IM.INDEX.GETFN#IM.INDEX.GETFN#IM.INDEX.GETFN#IM.INDEX.GETFN#IM.INDEX.GETFN#IM.INDEX.GETFN#IM.INDEX.GETFN#IM.INDEX.GETFN#IM.INDEX.GETFN#IM.INDEX.GETFN#IM.INDEX.GETFN#IM.INDEX.GETFN#IM.INDEX.GETFN#IM.INDEX.GETFN#IM.INDEX.GETFN#IM.INDEX.GETFN   N -    )   +  #IM.INDEX.GETFNCLASSIC +     5 H G       (   #IM.INDEX.GETFNCLASSIC +           '   $IM.INDEX.GETFNCLASSIC +         %IM.INDEX.GETFNCLASSIC +         $IM.INDEX.GETFNCLASSIC +   $IM.INDEX.GETFNCLASSIC +   %IM.INDEX.GETFNCLASSIC +   &IM.INDEX.GETFNCLASSIC +   w - u Y  .  P  # T     0      #  !  * !   A   L ?     + ( + U + 3 +@ .         +    $  &  N }  < + HRULE.GETFN   #IM.INDEX.GETFNCLASSIC +   &%   "IM.INDEX.GETFNCLASSIC +   m $   $   #IM.INDEX.GETFNCLASSIC +   !9  5    + K >&  "  2  '   "IM.INDEX.GETFNCLASSIC +    %            X  )   #IM.INDEX.GETFNCLASSIC +       #IM.INDEX.GETFNCLASSIC +          )   '   #IM.INDEX.GETFNCLASSIC +   +  ! + HRULE.GETFN  "IM.INDEX.GETFNCLASSIC +    X        +      %  V > )  &        4 <  &      "IM.INDEX.GETFNCLASSIC +    '$       +        & \  4    "   + 2 X  + H  &IM.INDEX.GETFNCLASSIC +  - =   +&IM.INDEX.GETFNCLASSIC +   0   +  L    %  %  %  %      (IM.INDEX.GETFNCLASSIC +     )/ N   4     -IM.INDEX.GETFNCLASSIC +   A  D  + HRULE.GETFN  !IM.INDEX.GETFNCLASSIC +  '**8  3     $IM.INDEX.GETFNCLASSIC +   s    %IM.INDEX.GETFNCLASSIC +  J + HRULE.GETFN   IM.INDEX.GETFNCLASSIC +  1 "           !IM.INDEX.GETFNCLASSIC +   "  !IM.INDEX.GETFNCLASSIC +  '       "IM.INDEX.GETFNCLASSIC +  "  "IM.INDEX.GETFNCLASSIC +       9      +     "IM.INDEX.GETFNCLASSIC +  +    *   "   "IM.INDEX.GETFNCLASSIC +      +   +   + HRULE.GETFN  #IM.INDEX.GETFNCLASSIC + - +     $IM.INDEX.GETFNCLASSIC +  "  %IM.INDEX.GETFNCLASSIC +      G  "IM.INDEX.GETFNCLASSIC + -       6  &   "IM.INDEX.GETFNCLASSIC + #"$  =  ! # )   #IM.INDEX.GETFNCLASSIC +   F   B     ( * (  + HRULE.GETFN  )IM.INDEX.GETFNCLASSIC + 8  +      )IM.INDEX.GETFNCLASSIC + d 1   B       (IM.INDEX.GETFNCLASSIC + 6  +   )IM.INDEX.GETFNCLASSIC +   *   #     3   D     +     +    +  %   + HRULE.GETFN  "IM.INDEX.GETFNCLASSIC +  1 ( "  "   #IM.INDEX.GETFNCLASSIC +  !  $IM.INDEX.GETFNCLASSIC +      "  (   $IM.INDEX.GETFNCLASSIC +    +  + HRULE.GETFN  #IM.INDEX.GETFNCLASSIC +  #    + +  . .   0/  $IM.INDEX.GETFNCLASSIC +   !     $IM.INDEX.GETFNCLASSIC +    )+ +   +  $   $IM.INDEX.GETFNCLASSIC +           ) 6  Z    %IM.INDEX.GETFNCLASSIC +   $  %IM.INDEX.GETFNCLASSIC +  , 2  1  .  1           # @    >   ,     %  '      $ + HRULE.GETFN9(         w)  #IM.INDEX.GETFNCLASSIC +       3   $IM.INDEX.GETFNCLASSIC +  "  $IM.INDEX.GETFNCLASSIC +  +    &IM.INDEX.GETFNCLASSIC +           $   !    %IM.INDEX.GETFNCLASSIC +    0   # %   %IM.INDEX.GETFNCLASSIC +     (L &#   &IM.INDEX.GETFNCLASSIC +    I $ $ &   &IM.INDEX.GETFNCLASSIC +    "   +   +       a.        + HRULE.GETFN  "IM.INDEX.GETFNCLASSIC + ' K  1        ( ' '    ` L  Z 0   +  +        + ;  #IM.INDEX.GETFNCLASSIC +  2  p)       'IM.INDEX.GETFNCLASSIC +  I =J g  +  MF$ +   iO  7    '     p  +(IM.INDEX.GETFNCLASSIC +   +   e   )IM.INDEX.GETFNCLASSIC +  % :C       + HRULE.GETFN  $IM.INDEX.GETFNCLASSIC + %  * ,   %IM.INDEX.GETFNCLASSIC +    !E               ?   %IM.INDEX.GETFNCLASSIC + 5(    &IM.INDEX.GETFNCLASSIC +   D   *IM.INDEX.GETFNCLASSIC + B     Q ]4    &   ? S Y L  c!   0   + B     $IM.INDEX.GETFNCLASSIC +  0-  8  % z \ No newline at end of file diff --git a/docs/turpin-irm/IRM-1/04-STRINGS.TEDIT b/docs/turpin-irm/IRM-1/04-STRINGS.TEDIT new file mode 100644 index 00000000..8bc603c2 --- /dev/null +++ b/docs/turpin-irm/IRM-1/04-STRINGS.TEDIT @@ -0,0 +1,76 @@ + INTERLISP-D REFERENCE MANUAL STRINGS 4. STRINGS(4.% % STRINGS (Chapter) NIL NIL NIL NIL) 3 A string represents a sequence of characters. Interlisp strings are a subtype of Common Lisp strings. Medley provides functions for creating strings, concatenating strings, and creating sub-strings of a string; all accepting or producing Common Lisp-acceptable strings. A string is typed as a double quote ("), followed by a sequence of any characters except double quote and %, terminated by a double quote. To include % or " in a string, type % in front of them: "A string" "A string with %" in it, and a %%." "" ; an empty string Strings are printed by PRINT and PRIN2 with initial and final double quotes, and %s inserted where necessary for it to read back in properly. Strings are printed by PRIN1 without the double quotes and extra %s. The null string is printed by PRINT and PRIN2 as "". (PRIN1 "") doesn't print anything. Internally, a string is stored in two parts: a string header and the sequence of characters. Several string headers may refer to the the same character sequence, so a substring can be made by creating a new string header, without copying any characters. Functions that refer to strings actually manipulate string headers. Some functions take an old string argument, and re-use the string pointer. (STRINGP X) [Function] Returns X if X is a string, NIL otherwise. (STREQUAL X Y) [Function] Returns T if X and Y are both strings and they contain the same sequence of characters, otherwise NIL. EQUAL uses STREQUAL. Note that strings may be STREQUAL without being EQ. For instance, (STREQUAL "ABC" "ABC") => T (EQ "ABC" "ABC") => NIL STREQUAL returns T if X and Y are the same string pointer, or two different string pointers which point to the same character sequence, or two string pointers which point to different character sequences which contain the same characters. Only in the first case would X and Y be EQ. (STRING-EQUAL X Y) [Function] Returns T if X and Y are either strings or symbols, and they contain the same sequence of characters, ignoring case. For instance, (STRING-EQUAL "FOO" "Foo") => T (STRING-EQUAL "FOO" 'Foo) => T This is useful for comparing things that might want to be considered equal even though they're not both symbols in a consistent case, such as file names and user names. (STRING.EQUAL X Y) [Function] Returns T if the print names of X and Y contain the same sequence of characters, ignoring case. For instance, (STRING-EQUAL "320" 320) => T (STRING-EQUAL "FOO" 'Foo) => T This is like STRING-EQUAL, but handles numbers, etc., where STRING-EQUAL doesn't. (ALLOCSTRING N INITCHAR OLD FATFLG) [Function] Creates a string of length N characters of INITCHAR (which can be either a character code or something coercible to a character). If INITCHAR is NIL, it defaults to character code 0. if OLD is supplied, it must be a string pointer, which is modified and returned. If FATFLG is non-NIL, the string is allocated using full 16-bit NS characters (see Chapter 2) instead of 8-bit characters. This can speed up some string operations if NS characters are later inserted into the string. This has no other effect on the operation of the string functions. (MKSTRING X FLG RDTBL) [Function] If X is a string, returns X. Otherwise, creates and returns a string containing the print name of X. Examples: (MKSTRING "ABC") => "ABC" (MKSTRING '(A B C)) => "(A B C)" (MKSTRING NIL) => "NIL" Note that the last example returns the string "NIL", not the symbol NIL. If FLG is T, then the PRIN2-name of X is used, computed with respect to the readtable RDTBL. For example, (MKSTRING "ABC" T) => "%"ABC%"" (NCHARS X FLG RDTBL) [Function] Returns the number of characters in the print name of X. If FLG=T, the PRIN2-name is used. For example, (NCHARS 'ABC) => 3 (NCHARS "ABC" T) => 5 Note: NCHARS works most efficiently on symbols and strings, but can be given any object. (SUBSTRING(SUBSTRING (Function) NIL NIL ("4") 3) X N M OLDPTR) [Function] Returns the substring of X consisting of the Nth through Mth characters of X. If M is NIL, the substring contains the Nth character thru the end of X. N and M can be negative numbers, which are interpreted as counts back from the end of the string, as with NTHCHAR (Chapter 2). SUBSTRING returns NIL if the substring is not well defined, (e.g., N or M specify character positions outside of X, or N corresponds to a character in X to the right of the character indicated by M). Examples: (SUBSTRING "ABCDEFG" 4 6) => "DEF" (SUBSTRING "ABCDEFG" 3 3) => "C" (SUBSTRING "ABCDEFG" 3 NIL) => "CDEFG" (SUBSTRING "ABCDEFG" 4 -2) => "DEF" (SUBSTRING "ABCDEFG" 6 4) => NIL (SUBSTRING "ABCDEFG" 4 9) => NIL If X is not a string, it is converted to one. For example, (SUBSTRING '(A B C) 4 6) => "B C" SUBSTRING does not actually copy any characters, but simply creates a new string pointer to the characters in X. If OLDPTR is a string pointer, it is modified and returned. (GNC(GNC (Function) NIL NIL ("4") 3) X) [Function] Get Next Character. Returns the next character of the string X (as a symbol); also removes the character from the string, by changing the string pointer. Returns NIL if X is the null string. If X isn't a string, a string is made. Used for sequential access to characters of a string. Example: (SETQ FOO "ABCDEFG") "ABCDEFG" (GNC FOO) A (GNC FOO) B FOO "CDEFG" Note that if A is a substring of B, (GNC A) does not remove the character from B. (GLC(GLC (Function) NIL NIL ("4") 3) X) [Function] Get Last Character. Returns the last character of the string X (as a symbol); also removes the character from the string. Similar to GNC. Example: (SETQ FOO "ABCDEFG") "ABCDEFG" (GLC FOO) G (GLC FOO) F FOO "ABCDE" (CONCAT(CONCAT (Function) NIL NIL ("4") 4) X1 X2 ... XN) [NoSpread Function] Returns a new string which is the concatenation of (copies of) its arguments. Any arguments which are not strings are transformed to strings. Examples: (CONCAT "ABC" 'DEF "GHI") => "ABCDEFGHI" (CONCAT '(A B C) "ABC") => "(A B C)ABC" (CONCAT) returns the null string, "" (CONCATLIST(CONCATLIST (Function) NIL NIL ("4") 4) L) [Function] L is a list of strings and/or other objects. The objects are transformed to strings if they aren't strings. Returns a new string which is the concatenation of the strings. Example: (CONCATLIST '(A B (C D) "EF")) => "AB(C D)EF" (RPLSTRING(RPLSTRING (Function) NIL NIL ("4") 4) X N Y) [Function] Replaces the characters of string X beginning at character position N with string Y. X and Y are converted to strings if they aren't already. N may be positive or negative, as with SUBSTRING. Characters are smashed into (converted) X. Returns the string X. Examples: (RPLSTRING "ABCDEF" -3 "END") => "ABCEND" (RPLSTRING "ABCDEFGHIJK" 4 '(A B C)) => "ABC(A B C)K" Generates an error if there is not enough room in X for Y, i.e., the new string would be longer than the original. If Y was not a string, X will already have been modified since RPLSTRING does not know whether Y will fit without actually attempting the transfer. Warning: In some implementations of Interlisp, if X is a substring of Z, Z will also be modified by the action of RPLSTRING or RPLCHARCODE. However, this is not guaranteed to be true in all cases, so programmers should not rely on RPLSTRING or RPLCHARCODE altering the characters of any string other than the one directly passed as argument to those functions. (RPLCHARCODE(RPLCHARCODE (Function) NIL NIL ("4") 4) X N CHAR) [Function] Replaces the Nth character of the string X with the character code CHAR. N may be positive or negative. Returns the new X. Similar to RPLSTRING. Example: (RPLCHARCODE "ABCDE" 3 (CHARCODE F)) => "ABFDE" (STRPOS(STRPOS (Function) NIL NIL ("4") 4) PAT STRING START SKIP ANCHOR TAIL CASEARRAY BACKWARDSFLG) [Function] STRPOS is a function for searching one string looking for another. PAT and STRING are both strings (or else they are converted automatically). STRPOS searches STRING beginning at character number START, (or 1 if START is NIL) and looks for a sequence of characters equal to PAT. If a match is found, the character position of the first matching character in STRING is returned, otherwise NIL. Examples: (STRPOS "ABC" "XYZABCDEF") => 4 (STRPOS "ABC" "XYZABCDEF" 5) => NIL (STRPOS "ABC" "XYZABCDEFABC" 5) => 10 SKIP can be used to specify a character in PAT that matches any character in STRING. Examples: (STRPOS "A&C&" "XYZABCDEF" NIL '&) => 4 (STRPOS "DEF&" "XYZABCDEF" NIL '&) => NIL If ANCHOR is T, STRPOS compares PAT with the characters beginning at position START (or 1 if START is NIL). If that comparison fails, STRPOS returns NIL without searching any further down STRING. Thus it can be used to compare one string with some portion of another string. Examples: (STRPOS "ABC" "XYZABCDEF" NIL NIL T) => NIL (STRPOS "ABC" "XYZABCDEF" 4 NIL T) => 4 If TAIL is T, the value returned by STRPOS if successful is not the starting position of the sequence of characters corresponding to PAT, but the position of the first character after that, i.e., the starting position plus (NCHARS PAT). Examples: (STRPOS "ABC" "XYZABCDEFABC" NIL NIL NIL T) => 7 (STRPOS "A" "A" NIL NIL NIL T) => 2 If TAIL = NIL, STRPOS returns NIL, or a character position within STRING which can be passed to SUBSTRING. In particular, (STRPOS "" "") => NIL. However, if TAIL = T, STRPOS may return a character position outside of STRING. For instance, note that the second example above returns 2, even though A has only one character. If CASEARRAY is non-NIL, this should be a casearray like that given to FILEPOS (Chapter 25). The casearray is used to map the string characters before comparing them to the search string. If BACKWARDSFLG is non-NIL, the search is done backwards from the end of the string. (STRPOSL(STRPOSL (Function) NIL NIL ("4") 5) A STRING START NEG BACKWARDSFLG) [Function] STRING is a string (or is converted automatically to a string), A is a list of characters or character codes. STRPOSL searches STRING beginning at character number START (or 1 if START = NIL) for one of the characters in A. If one is found, STRPOSL returns as its value the corresponding character position, otherwise NIL. Example: (STRPOSL '(A B C) "XYZBCD") => 4 If NEG = T, STRPOSL searches for a character not on A. Example: (STRPOSL '(A B C) "ABCDEF" NIL T) => 4 If any element of A is a number, it is assumed to be a character code. Otherwise, it is converted to a character code via CHCON1. Therefore, it is more efficient to call STRPOSL with A a list of character codes. If A is a bit table, it is used to specify the characters (see MAKEBITTABLE below) If BACKWARDSFLG is non-NIL, the search is done backwards from the end of the string. STRPOSL uses a bit table data structure to search efficiently. If A is not a bit table, it is converted to a bit table using MAKEBITTABLE. If STRPOSL is to be called frequently with the same list of characters, a considerable savings can be achieved by converting the list to a bit table once, and then passing the bit table to STRPOSL as its first argument. (MAKEBITTABLE(MAKEBITTABLE (Function) NIL NIL ("4") 6) L NEG A) [Function] Returns a bit table suitable for use by STRPOSL. L is a list of characters or character codes, NEG is the same as described for STRPOSL. If A is a bit table, MAKEBITTABLE modifies and returns it. Otherwise, it will create a new bit table. Note: If NEG = T, STRPOSL must call MAKEBITTABLE whether A is a list or a bit table. To obtain bit table efficiency with NEG=T, MAKEBITTABLE should be called with NEG=T, and the resulting inverted bit table should be given to STRPOSL with NEG=NIL. [This page intentionally left blank] (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "4-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "4-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "4-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "4-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "4-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "4-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))5,1EVENT-T,l~,~~,l~,,@ PAGEHEADING RIGHTPAGE? PAGEHEADINGLEFTBACK,ll,HH306 +T, +TIMESROMAN +PALATINO PALATINO PALATINO TITAN TITAN TITAN TITAN CLASSIC +TITAN +CLASSIC +MODERN +MODERNMODERN +   +  )IM.INDEX.GETFN  HRULE.GETFNMODERN %D,8 +T%" +  + + +    +  +  N    +  + +  p" !  +  + +  H  !  #  + +  +  + S'K  + +  + H  #  .   1" +  +   6  M + &IM.INDEX.GETFNTITAN + +  +  c  .(,% # ) & # # 8$ e3 + IM.INDEX.GETFNTITAN + + + @ec  +   $ + IM.INDEX.GETFNTITAN + + + @H  +   +#IM.INDEX.GETFNTITAN + +   + + *  + +'IM.INDEX.GETFNTITAN + + + 0 + &IM.INDEX.GETFNTITAN + + + "! 3& + , 8 2>' 6 ,(  ^  i + + (IM.INDEX.GETFNTITAN + + + /  2#IM.INDEX.GETFNMODERN +    >? + 2R " & ( ' * ,  ++ + $7. * [W 3 &  ! ,K  0o  ; +$IM.INDEX.GETFNMODERN +  + :. + +F #  ) h+ ;   ; >: $ + )IM.INDEX.GETFNMODERN +  + (- F     3 < +%.{-z \ No newline at end of file diff --git a/docs/turpin-irm/IRM-1/05-ARRAY.TEDIT b/docs/turpin-irm/IRM-1/05-ARRAY.TEDIT new file mode 100644 index 00000000..14afeefa Binary files /dev/null and b/docs/turpin-irm/IRM-1/05-ARRAY.TEDIT differ diff --git a/docs/turpin-irm/IRM-1/06-HASHARRAYS.TEDIT b/docs/turpin-irm/IRM-1/06-HASHARRAYS.TEDIT new file mode 100644 index 00000000..8529b8e8 --- /dev/null +++ b/docs/turpin-irm/IRM-1/06-HASHARRAYS.TEDIT @@ -0,0 +1,79 @@ +INTERLISP-D REFERENCE MANUAL HASHARRAYS 6. HASHARRAYS 3 Hash arrays(HASH% ARRAYS NIL Hash% arrays NIL (NIL) 1) let you associate arbitrary Lisp objects (hash keys) with other objects (hash values), so you can get from key to value quickly. There are functions for creating hash arrays, putting a hash key/value pair in a hash array, and quickly retrieving the hash value associated with a given hash key. By default, the hash array functions use EQ for comparing hash keys. This means that if non-symbols are used as hash keys, the exact same object (not a copy) must be used to retrieve the hash value. However, you can specify the function used to compare hash keys and to hash a hash key to a number. You can, for example, create hash arrays where EQUAL but non-EQ strings will hash to the same value. Specifying alternative hashing algorithms is described below. In the description of the functions below, the argument HARRAY should be a hasharray created by HASHARRAY. For convenience in interactive program development, it may also be NIL, in which case a hash array (SYSHASHARRAY) provided by the system is used; you must watch out for confusions if this form is used to associate more than one kind of value with the same key. Note: For backwards compatibility, the hash array functions will accept a list whose CAR is a hash array, and whose CDR is the overflow method for the hash array (see below). However, hash array functions are guaranteed to perform with maximum efficiency only if a direct value of HASHARRAY is given. Note: Interlisp hash arrays and Common Lisp hash tables are the same data type, so functions from both may be intermixed. The only difference between the functions may be argument order, as in MAPHASH and CL:MAPHASH (see below). (HASHARRAY(HASHARRAY (Function) NIL NIL (NIL) 1) MINKEYS OVERFLOW HASHBITSFN EQUIVFN RECLAIMABLE REHASH-THRESHOLD) [Function] Creates a hash array with space for at least MINKEYS hash keys, with overflow method OVERFLOW. See discussion of overflow behavior below. If HASHBITSFN and EQUIVFN are non-NIL, they specify the hashing function and comparison function used to interpret hash keys. This is described in the section on user-specified hashing functions below. If HASHBITSFN and EQUIVFN are NIL, the default is to hash EQ hash keys to the same value. If RECLAIMABLE is T the entries in the hash table will be removed if the key has a reference count of one and the table is about to be rehashed. This allows the system, in some cases, to reuse keys instead of expanding the table. Note: CL:MAKE-HASH-TABLE does not allow you to specify your own hashing functions but does provide three built-in types specified by Common Lisp, the Language. (HARRAY(HARRAY (Function) NIL NIL (NIL) 1) MINKEYS) [Function] Provided for backward compatibility, this is equivalent to (HASHARRAY MINKEYS 'ERROR), i.e. if the resulting hasarray gets full, an error occurs. (HARRAYP(HARRAYP (Function) NIL NIL (NIL) 2) X) [Function] Returns X if it is a hash array; otherwise NIL. HARRAYP returns NIL if X is a list whose CAR is an HARRAYP, even though this is accepted by the hash array functions (see below). (PUTHASH(PUTHASH (Function) NIL NIL (NIL) 2) KEY VAL HARRAY) [Function] Associates the hash value VAL with the hash key KEY in HARRAY. Replaces the previous hash value, if any. If VAL is NIL, any old association is removed (hence a hash value of NIL is not allowed). If HARRAY is full when PUTHASH is called with a key not already in the hash array, the function HASHOVERFLOW is called, and the PUTHASH is applied to the value returned (see below). Returns VAL. ((GETHASH (Function) NIL NIL (NIL) 2)GETHASH KEY HARRAY) [Function] Returns the hash value associated with the hash key KEY in HARRAY. Returns NIL, if KEY is not found. (CLRHASH(CLRHASH (Function) NIL NIL (NIL) 2) HARRAY) [Function] Clears all hash keys/values from HARRAY. Returns HARRAY. (HARRAYPROP(HARRAYPROP (Function) NIL NIL (NIL) 2) HARRAY PROP NEWVALUE) [NoSpread Function] Returns the property PROP of HARRAY; PROP can have the system-defined values SIZE (the maximum occupancy of HARRAY), NUMKEYS (number of occupied slots), OVERFLOW (overflow method), HASHBITSFN (hashing function) and EQUIVFN (comparison function). Except for SIZE and NUMKEYS, a new value may be specified as NEWVALUE. By using other values for PROP, the user may also set and get arbitrary property values, to associate additional information with a hash array. The HASHBITSFN or EQUIVFN properties can only be changed if the hash array is empty. (HARRAYSIZE(HARRAYSIZE (Function) NIL NIL (NIL) 2) HARRAY) [Function] Returns the number of slots in HARRAY. It's equivalent to (HARRAYPROP HARRAY 'SIZE). (REHASH(REHASH (Function) NIL NIL (NIL) 2) OLDHARRAY NEWHARRAY) [Function] Hashes all hash keys and values in OLDHARRAY into NEWHARRAY. The two hash arrays do not have to be (and usually aren't) the same size. Returns NEWHARRAY. (MAPHASH(MAPHASH (Function) NIL NIL (NIL) 2) HARRAY MAPHFN) [Function] MAPHFN is a function of two arguments. For each hash key in HARRAY, MAPHFN will be applied to the hash value, and the hash key. For example: [MAPHASH A (FUNCTION (LAMBDA (VAL KEY) (if (LISTP KEY) then (PRINT VAL)] will print the hash value for all hash keys that are lists. MAPHASH returns HARRAY. Note: the argument order for CL:MAPHASH is MAPHFN HARRAY. (DMPHASH(DMPHASH (Function) NIL NIL (NIL) 3) HARRAY1 HARRAY2 ... HARRAYN) [NLambda NoSpread Function] Prints on the primary output file LOADable forms which will restore the hash-arrays contained as the values of the atoms HARRAY1, HARRAY2, ... HARRAYN. Example: (DMPHASH SYSHASHARRAY) will dump the system hash-array. All EQ identities except symbols and small integers are lost by dumping and loading because READ will create new structure for each item. Thus if two lists contain an EQ substructure, when they are dumped and loaded back in, the corresponding substructures while EQUAL are no longer EQ. The HORRIBLEVARS file package command (Chapter 17) provides a way of dumping hash tables such that these identities are preserved. Hash Overflow 1 When a hash array(HASH% ARRAY NIL Hash% array NIL (NIL) 3 SUBNAME OVERFLOW SUBTEXT overflow) becomes full, trying to add another hash key will cause the function HASHOVERFLOW to be called. This either enlarges the hash array, or causes the error Hash table full. How hash overflow is handled is determined by the value of the OVERFLOW property of the hash array (which can be accessed by HARRAYPROP). The possibilities for the overflow method are: the symbol ERROR The error Hash array full is generated when the hash array overflows. This is the default overflow behavior for hash arrays returned by HARRAY. NIL The array is automatically enlarged by at least a factor 1.5 every time it overflows. This is the default overflow behavior for hash arrays returned by HASHARRAY. a positive integer N The array is enlarged to include at least N more slots than it currently has. a floating point number F The array is changed to include F times the number of current slots. a function or lambda expression FN Upon hash overflow, FN is called with the hash array as its argument. If FN returns a number, that will become the size of the array. Otherwise, the new size defaults to 1.5 times its previous size. FN could be used to print a message, or perform some monitor function. Note: For backwards compatibility, the hash array functions accept a list whose CAR is the hash array, and whose CDR is the overflow method. In this case, the overflow method specified in the list overrides the overflow method set in the hash array. Hash array functions perform with maximum efficiency only if a direct value of HASHARRAY is given. Specifying Your Own Hashing Functions 1 In general terms, when a key is looked up in a hash array(HASH% ARRAY NIL Hash% array NIL (NIL) 3 SUBNAME USER-SPECIFIED% FUNCTIONS SUBTEXT user-specified% functions), it is converted to an integer, which is used to index into a linear array. If the key is not the same as the one found at that index, other indices are tried until it the desired key is found. The value stored with that key is then returned (from GETHASH) or replaced (from PUTHASH). To customize hash arrays, you'll need to supply the hashing function used to convert a key to an integer and the comparison function used to compare the key found in the array with the key being looked up. For hash arrays to work correctly, any two objects which are equal according to the comparison function must hash to equal integers. By default, Medley uses a hashing function that computes an integer from the internal address of a key, and use EQ for comparing keys. This means that if non-atoms are used as hash keys, the exact same object (not a copy) must be used to retrieve the hash value. There are some applications for which the EQ constraint is too restrictive. For example, it may be useful to use strings as hash keys, without the restriction that EQUAL but not EQ strings are considered to be different hash keys. The user can override this default behavior for any hash array by specifying the functions used to compare keys and to hash a key to a number. This can be done by giving the HASHBITSFN and EQUIVFN arguments to HASHARRAY (see above). The EQUIVFN argument is a function of two arguments that returns non-NIL when its arguments are considered equal. The HASHBITSFN argument is a function of one argument that produces a positive small integer (in the range [0..216 - 1]) with the property that objects that are considered equal by the EQUIVFN produce the same hash bits. For an existing hash array, the function HARRAYPROP (see above) can be used to examine the hashing and equivalence functions as the HASHBITSFN and EQUIVFN hash array properties. These properties are read-only for non-empty hash arrays, as it makes no sense to change the equivalence relationship once some keys have been hashed. The following function is useful for creating hash arrays that take strings as hash keys: (STRINGHASHBITS(STRINGHASHBITS (Function) NIL NIL (NIL) 4) STRING) [Function] Hashes the string STRING into an integer that can be used as a HASHBITSFN for a hash array. Strings which are STREQUAL hash to the same integer. Example: (HASHARRAY MINKEYS OVERFLOW 'STRINGHASHBITS 'STREQUAL) creates a hash array where you can use strings as hash keys. [This page intentionally left blank] (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "6-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "6-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "6-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "6-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (102 612 510 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "6-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "6-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))5,,ZZ1EVENT-T@ PAGEHEADING RIGHTPAGE,xx,xx0 +T30<T,A PAGEHEADING RIGHTPAGET,HH,@ PAGEHEADINGLEFTBACKT +TIMESROMAN +PALATINO PALATINO PALATINOPALATINO TITAN TITAN TITAN TITAN TITAN +CLASSIC +CLASSIC +MODERN +MODERNMODERN + +   HRULE.GETFNMODERN +IM.INDEX.GETFN, ) 4  e 8" F   R      +  + + &IM.INDEX.GETFNMODERN +   + $ +  -!-  +   +      m + +#IM.INDEX.GETFNMODERN +  + ; ;  + +$IM.INDEX.GETFNMODERN +  + "      I + +$IM.INDEX.GETFNTITAN + +   + 1 8  B  8 + +$IM.INDEX.GETFN +  + 4   + +$IM.INDEX.GETFNTITAN + + + !  + + +'IM.INDEX.GETFNMODERN +    + $    +  $  " r  +  < + + +'IM.INDEX.GETFNTITAN + + +   + +#IM.INDEX.GETFNTITAN + +   + #  V  + +$IM.INDEX.GETFNTITAN + +  + 7D  )  =   +   + +$IM.INDEX.GETFNTITAN +   + + " S   "  V H ^   s  HRULE.GETFN KIM.INDEX.GETFNF I B 6 + 3   p   +#!$!4~E M    %  HRULE.GETFN 9mIM.INDEX.GETFN   Yp J7 * y  3  +  : / +bG ) + Q +   Z + ++IM.INDEX.GETFNMODERN +  + ' + &      =%)/ z \ No newline at end of file diff --git a/docs/turpin-irm/IRM-1/07-NUMBERS.TEDIT b/docs/turpin-irm/IRM-1/07-NUMBERS.TEDIT new file mode 100644 index 00000000..5ebcc055 --- /dev/null +++ b/docs/turpin-irm/IRM-1/07-NUMBERS.TEDIT @@ -0,0 +1,569 @@ +INTERLISP-D REFERENCE MANUAL NUMBERS AND ARITHMETIC FUNCTIONS "7"7. NUMBERS AND ARITHMETIC FUNCTIONS 3 There are four different types of numbers(NUMBERS NIL Numbers NIL ("7") 1) in Interlisp: small integers(NUMBERS NIL Numbers NIL ("7") 1 SUBNAME SMALL% INTEGERS SUBTEXT small% integers), large integers(NUMBERS NIL Numbers NIL ("7") 1 SUBNAME LARGE% INTEGERS SUBTEXT large% integers), bignums (arbitrary-size integers)(NUMBERS NIL Numbers NIL ("7") 1 SUBNAME BIGNUMS SUBTEXT bignums), and floating-point numbers(NUMBERS NIL Numbers NIL ("7") 1 SUBNAME FLOATING-POINT SUBTEXT floating-point). Small integers are in the range -65536 to 65535. Large integers and floating-point numbers are 32-bit quantities that are stored by boxing the number (see below). Bignums are boxed as a series of words. Large integers and floating-point numbers can be any full word quantity. To distinguish among the various kinds of numbers, and other Interlisp pointers, these numbers are boxed When a large integer or floating-point number is created (by an arithmetic operation or by READ), Interlisp gets a new word from number storage and puts the number into that word. Interlisp then passes around the pointer to that word, i.e., the boxed number(BOXED% NUMBERS NIL Boxed% numbers NIL ("7") 1), rather than the actual quantity itself. When a numeric function needs the actual numeric quantity, it performs the extra level of addressing to obtain the value of the number. This latter process is called unboxing. Unboxing does not use any storage, but each boxing operation uses one new word of number storage. If a computation creates many large integers or floating-point numbers, i.e., does lots of boxes, it may cause a garbage collection of large integer space, or of floating-point number space. The following functions can be used to distinguish the different types of numbers: (SMALLP(SMALLP (Function) NIL NIL ("7") 1) X) [Function] Returns X, if X is a small integer; NIL otherwise. Does not generate an error if X is not a number. (FIXP(FIXP (Function) NIL NIL ("7") 1) X) [Function] Returns X, if X is an integer; NIL otherwise. Note that FIXP is true for small integers, large integers, and bignums. Does not generate an error if X is not a number. (FLOATP(FLOATP (Function) NIL NIL ("7") 1) X) [Function] Returns X if X is a floating-point number; NIL otherwise. Does not give an error if X is not a number. (NUMBERP(NUMBERP (Function) NIL NIL ("7") 1) X) [Function] Returns X, if X is a number of any type; NIL otherwise. Does not generate an error if X is not a number. Note: In previous releases, NUMBERP was true only if (FLOATP X) or (FIXP X) were true. With the additon of Common Lisp ratios and complex numbers, NUMBERP now returns T for all number types . Code relying on the "old" behavior should be modified. Each small integer has a unique representation, so EQ may be used to check equality. EQ should not be used for large integers, bignums, or floating-point numbers, EQP, IEQP, or EQUAL must be used instead. (EQP(EQP (Function) NIL NIL ("7") 1) X Y) [Function] Returns T, if X and Y are equal numbers; NIL otherwise. EQ may be used if X and Y are known to be small integers. EQP does not convert X and Y to integers, e.g., (EQP 2000 2000.3) => NIL, but it can be used to compare an integer and a floating-point number, e.g., (EQP 2000 2000.0) => T. EQP does not generate an error if X or Y are not numbers. EQP can also be used to compare stack pointers (see Chapter 11) and compiled code objects (see Chapter 10). The action taken on division by zero and floating-point overflow is determined with the following function: (OVERFLOW(OVERFLOW (Function) NIL NIL ("7") 2) FLG) [Function] Sets a flag that determines the system response to arithmetic overflow (for floating-point arithmetic) and division by zero; returns the previous setting. For integer arithmetic: If FLG = T, an error occurs on division by zero. If FLG = NIL or 0, integer division by zero returns zero. Integer overflow cannot occur, because small integers are converted to bignums (see the beginning of this chapter). For floating-point arithmetic: If FLG = T, an error occurs on floating overflow or floating division by zero. If FLG = NIL or 0, the largest (or smallest) floating-point number is returned as the result of the overflowed computation or floating division by zero. The default value for OVERFLOW is T, meaning an error is generated on division by zero or floating overflow. Generic Arithmetic(GENERIC% ARITHMETIC NIL Generic% arithmetic NIL ("7") 2)(ARITHMETIC NIL Arithmetic NIL ("7") 2 SUBNAME GENERIC SUBTEXT generic) 1 The functions in this section are generic arithmetic functions. If any of the arguments are floating-point numbers (see the Floating-Point Arithmetic section below), they act exactly like floating-point functions, floating all arguments and returning a floating-point number as their value. Otherwise, they act like the integer functions (see the Integer Arithmetic section below). If given a non-numeric argument, they generate an error, Non-numeric arg. The results of division by zero and floating-point overflow is determined by the function OVERFLOW (see the section above). (PLUS(PLUS (Function) NIL NIL ("7") 2) X1 X2 ... XN) [NoSpread Function] X1 + X2 + ... + XN. (MINUS(MINUS (Function) NIL NIL ("7") 2) X) [Function] - X (DIFFERENCE(DIFFERENCE (Function) NIL NIL ("7") 2) X Y) [Function] X - Y ((TIMES (Function) NIL NIL ("7") 2)TIMES X1 X2 ... XN) [NoSpread Function] X1 * X2 * ... * XN (QUOTIENT(QUOTIENT (Function) NIL NIL ("7") 2) X Y) [Function] If X and Y are both integers, returns the integer division of X and Y. Otherwise, converts both X and Y to floating-point numbers, and does a floating-point division. (REMAINDER(REMAINDER (Function) NIL NIL ("7") 3) X Y) [Function] If X and Y are both integers, returns (IREMAINDER X Y), otherwise (FREMAINDER X Y). (GREATERP (GREATERP% (Function) NIL NIL ("7") 3)X Y) [Function] T, if X > Y, NIL otherwise. (LESSP(LESSP (Function) NIL NIL ("7") 3) X Y) [Function] T if X < Y, NIL otherwise. (GEQ(GEQ (Function) NIL NIL ("7") 3) X Y) [Function] T, if X >= Y, NIL otherwise. (LEQ(LEQ (Function) NIL NIL ("7") 3) X Y) [Function] T, if X <= Y, NIL otherwise. (ZEROP(ZEROP (Function) NIL NIL ("7") 3) X) [Function] The same as (EQP X 0). (MINUSP(MINUSP (Function) NIL NIL ("7") 3) X) [Function] T, if X is negative; NIL otherwise. Works for both integers and floating-point numbers. (MIN(MIN (Function) NIL NIL ("7") 3) X1 X2 ... XN) [NoSpread Function] Returns the minimum of X1, X2, ..., XN. (MIN) returns the value of MAX.INTEGER (see the Integer Arithmetic section below). (MAX(MAX (Function) NIL NIL ("7") 3) X1 X2 ... XN) [NoSpread Function] Returns the maximum of X1, X2, ..., XN. (MAX) returns the value of MIN.INTEGER (see the Integer Arithmetic section below). (ABS(ABS (Function) NIL NIL ("7") 3) X) [Function] X if X > 0, otherwise -X. ABS uses GREATERP and MINUS (not IGREATERP and IMINUS). Integer Arithmetic(ARITHMETIC NIL Arithmetic NIL ("7") 3 SUBNAME INTEGER SUBTEXT integer)(INTEGER% ARITHMETIC NIL Integer% arithmetic NIL ("7") 3) 1 The input syntax for an integer is an optional sign (+ or -) followed by a sequence of decimal digits, and terminated by a delimiting character. Integers entered with this syntax are interpreted as decimal integers. Integers in other radices can be entered as follows: 123Q #o123 If an integer is followed by the letter Q, or preceeded by a pound sign and the letter o, the digits are interpreted as an octal (base 8) integer. #b10101 If an integer is preceeded by a pound sign and the letter b, the digits are interpreted as a binary (base 2) integer. #x1A90 If an integer is preceeded by a pound sign and the letter x, the digits are interpreted as a hexadecimal (base 16) integer. #5r1243 If an integer is preceeded by a pound sign, a positive decimal integer BASE, and the letter r, the digits are interpreted as an integer in the base BASE. For example, #8r123 = 123Q, and #16r12A3 = #x12A3. When typing a number in a radix above ten, the uppercase letters A through Z can be used as the digits after 9 (but there is no digit above Z, so it is not possible to type all base-99 digits). Medley keeps no record of how you typed a number, so 77Q and 63 both correspond to the same integer, and are indistinguishable internally. The function RADIX (see Chapter 25), sets the radix used to print integers. PACK and MKATOM create numbers when given a sequence of characters observing the above syntax, e.g. (PACK '(1 2 Q)) => 10. Integers are also created as a result of arithmetic operations. The range of integers of various types is implementation-dependent. This information is accessible to you through the following variables: MIN.SMALLP(MIN.SMALLP (Variable) NIL NIL ("7") 4) [Variable] MAX.SMALLP(MAX.SMALLP (Variable) NIL NIL ("7") 4) [Variable] The smallest/largest possible small integer. MIN.FIXP(MIN.FIXP (Variable) NIL NIL ("7") 4) [Variable] MAX.FIXP(MAX.FIXP (Variable) NIL NIL ("7") 4) [Variable] The smallest/largest possible large integer. MIN.INTEGER(MIN.INTEGER (Variable) NIL NIL ("7") 4) [Variable] MAX.INTEGER(MAX.INTEGER (Variable) NIL NIL ("7") 4) [Variable] The value of MAX.INTEGER and MIN.INTEGER are two special system datatypes. For some algorithms, it is useful to have an integer that is larger than any other integer. Therefore, the values of MAX.INTEGER and MIN.INTEGER are two special data types; the value of MAX.INTEGER is GREATERP than any other integer, and the value of MIN.INTEGER is LESSP than any other integer. Trying to do arithmetic using these special bignums, other than comparison, will cause an error. All of the functions described below work on integers. Unless specified otherwise, if given a floating-point number, they first convert the number to an integer by truncating the fractional bits, e.g., (IPLUS 2.3 3.8) = 5; if given a non-numeric argument, they generate an error, Non-numeric arg. (IPLUS X1 X2 ... XN) [NoSpread Function] Returns the sum X1 + X2 + ... + XN. (IPLUS) = 0. (IMINUS X) [Function] -X (IDIFFERENCE X Y) [Function] X - Y (ADD1(ADD1 (Function) NIL NIL ("7") 5) X) [Function] X + 1 (SUB1(SUB1 (Function) NIL NIL ("7") 5) X) [Function] X - 1 (ITIMES(ITIMES (Function) NIL NIL ("7") 5) X1 X2 ... XN) [NoSpread Function] Returns the product X1 * X2 * ... * XN. (ITIMES) = 1. (IQUOTIENT(IQUOTIENT (Function) NIL NIL ("7") 5) X Y) [Function] X / Y truncated. Examples: (IQUOTIENT 3 2) => 1 (IQUOTIENT -3 2) => -1 If Y is zero, the result is determined by the function OVERFLOW . (IREMAINDER(IREMAINDER (Function) NIL NIL ("7") 5) X Y) [Function] Returns the remainder when X is divided by Y. Example: (IREMAINDER 5 2) => 1 (IMOD(IMOD (Function) NIL NIL ("7") 5) X N) [Function] Computes the integer modulus of X mod N; this differs from IREMAINDER in that the result is always a non-negative integer in the range [0,N). (IGREATERP(IGREATERP (Function) NIL NIL ("7") 5) X Y) [Function] T, if X > Y; NIL otherwise. (ILESSP(ILESSP (Function) NIL NIL ("7") 5) X Y) [Function] T, if X < Y; NIL otherwise. (IGEQ(IGEQ (Function) NIL NIL ("7") 5) X Y) [Function] T, if X >= Y; NIL otherwise. (ILEQ(ILEQ (Function) NIL NIL ("7") 5) X Y) [Function] T, if X <= Y; NIL otherwise. (IMIN(IMIN (Function) NIL NIL ("7") 5) X1 X2 ... XN) [NoSpread Function] Returns the minimum of X1, X2, ..., XN. (IMIN) returns the largest possible large integer, the value of MAX.INTEGER. (IMAX(IMAX (Function) NIL NIL ("7") 5) X1 X2 ... XN) [NoSpread Function] Returns the maximum of X1, X2, ..., XN. (IMAX) returns the smallest possible large integer, the value of MIN.INTEGER. (IEQP(EQP (Function) NIL NIL ("7") 6) X Y) [Function] Returns T if X and Y are equal integers; NIL otherwise. Note that EQ may be used if X and Y are known to be small integers. IEQP converts X and Y to integers, e.g., (IEQP 2000 2000.3) => T. (FIX(FIX (Function) NIL NIL ("7") 6) N) [Function] If N is an integer, returns N. Otherwise, converts N to an integer by truncating fractional bits For example, (FIX 2.3) => 2, (FIX -1.7) => -1. Since FIX is also a programmer's assistant command (see Chapter 13), typing FIX directly to a Medley executive will not cause the function FIX to be called. (FIXR(FIXR (Function) NIL NIL ("7") 6) N) [Function] If N is an integer, returns N. Otherwise, converts N to an integer by rounding. FIXR will round towards the even number if N is exactly half way between two integers. For example, (FIXR 2.3) => 2, (FIXR -1.7) => -2, (FIXR 3.5) => 4). (GCD(GCD (Function) NIL NIL ("7") 6) N1 N2) [Function] Returns the greatest common divisor of N1 and N2, (GCD 72 64)=8. Logical Arithmetic(ARITHMETIC NIL Arithmetic NIL ("7") 6 SUBNAME LOGICAL% FUNCTIONS SUBTEXT logical% functions) Functions(LOGICAL% ARITHMETIC% FUNCTIONS NIL Logical% arithmetic% functions NIL ("7") 6) 1 (LOGAND(LOGAND (Function) NIL NIL ("7") 6) X1 X2 ... XN) [NoSpread Function] Returns the logical AND of all its arguments, as an integer. Example: (LOGAND 7 5 6) => 4 (LOGOR(LOGOR (Function) NIL NIL ("7") 6) X1 X2 ... XN) [NoSpread Function] Returns the logical OR of all its arguments, as an integer. Example: (LOGOR 1 3 9) => 11 (LOGXOR(LOGXOR (Function) NIL NIL ("7") 6) X1 X2 ... XN) [NoSpread Function] Returns the logical exclusive OR of its arguments, as an integer. Example: (LOGXOR 11 5) => 14 (LOGXOR 11 5 9) = (LOGXOR 14 9) => 7 (LSH(LSH (Function) NIL NIL ("7") 6) X N) [Function] (Arithmetic) Left Shift. Returns X shifted left N places, with the sign bit unaffected. X can be positive or negative. If N is negative, X is shifted right -N places. (RSH(RSH (Function) NIL NIL ("7") 6) X N) [Function] (Arithmetic) Right Shift. Returns X shifted right N places, with the sign bit unaffected, and copies of the sign bit shifted into the leftmost bit. X can be positive or negative. If N is negative, X is shifted left -N places. Warning: Be careful if using RSH to simulate division; RSHing a negative number isn't the same as dividing by a power of two. (LLSH(LLSH (Function) NIL NIL ("7") 7) X N) [Function] (LRSH(LLSH (Function) NIL NIL ("7") 7) X N) [Function] Logical Left Shift and Logical Right Shift. The difference between a logical and arithmetic right shift lies in the treatment of the sign bit. Logical shifting treats it just like any other bit; arithmetic shifting will not change it, and will propagate rightward when actually shifting rightwards. Note that shifting (arithmetic) a negative number all the way to the right yields -1, not 0. Note: LLSH and LRSH always operate mod-232 arithmetic. Passing a bignum to either of these will cause an error. LRSH of negative numbers will shift 0s into the high bits. (INTEGERLENGTH(LLSH (Function) NIL NIL ("7") 7) X) [Function] Returns the number of bits needed to represent X. This is equivalent to: 1+floor[log2[abs[X]]]. (INTEGERLENGTH 0) = 0. (POWEROFTWOP(POWEROFTWOP (Function) NIL NIL ("7") 7) X) [Function] Returns non-NIL if X (coerced to an integer) is a power of two. (EVENP(EVENP (Function) NIL NIL ("7") 7) X Y) [NoSpread Function] If Y is not given, equivalent to (ZEROP (IMOD X 2)); otherwise equivalent to (ZEROP (IMOD X Y)). (ODDP(ODDP (Function) NIL NIL ("7") 7) N MODULUS) [NoSpread Function] Equivalent to (NOT (EVENP N MODULUS)). MODULUS defaults to 2. (LOGNOT (LOGNOT% (Macro) NIL NIL ("7") 7)N) [Macro] Logical negation of the bits in N. Equivalent to (LOGXOR N -1). (BITTEST(BITTEST (Macro) NIL NIL ("7") 7) N MASK) [Macro] Returns T if any of the bits in MASK are on in the number N. Equivalent to (NOT (ZEROP (LOGAND N MASK))). (BITCLEAR(BITCLEAR (Macro) NIL NIL ("7") 7) N MASK) [Macro] Turns off bits from MASK in N. Equivalent to (LOGAND N (LOGNOT MASK)). (BITSET(BITSET (Macro) NIL NIL ("7") 7) N MASK) [Macro] Turns on the bits from MASK in N. Equivalent to (LOGOR N MASK). (MASK.1'S(MASK.1'S (Macro) NIL NIL ("7") 7) POSITION SIZE) [Macro] Returns a bit-mask with SIZE one-bits starting with the bit at POSITION. Equivalent to (LLSH (SUB1 (EXPT 2 SIZE)) POSITION). (MASK.0'S(MASK.0'S (Macro) NIL NIL ("7") 8) POSITION SIZE) [Macro] Returns a bit-mask with all one bits, except for SIZE bits starting at POSITION. Equivalent to (LOGNOT (MASK.1'S POSITION SIZE)). (LOADBYTE(LOADBYTE (Function) NIL NIL ("7") 8) N POS SIZE) [Function] Extracts SIZE bits from N, starting at position POS. Equivalent to (LOGAND (RSH N POS) (MASK.1'S 0 SIZE)). (DEPOSITBYTE(DEPOSITBYTE (Function) NIL NIL ("7") 8) N POS SIZE VAL) [Function] Insert SIZE bits of VAL at position POS into N, returning the result. Equivalent to (LOGOR (BITCLEAR N (MASK.1'S POS SIZE)) (LSH (LOGAND VAL (MASK.1'S 0 SIZE)) POS)) (ROT(ROT (Function) NIL NIL ("7") 8) X N FIELDSIZE) [Function] Rotate bits in field. It performs a bitwise left-rotation of the integer X, by N places, within a field of FIELDSIZE bits wide. Bits being shifted out of the position selected by (EXPT 2 (SUB1 FIELDSIZE)) will flow into the units position. The notions of position and size can be combined to make up a byte specifier, which is constructed by the macro BYTE [note reversal of arguments as compared with the above functions]: (BYTE(BYTE (Macro) NIL NIL ("7") 8) SIZE POSITION) [Macro] Constructs and returns a byte specifier containing SIZE and POSITION. (BYTESIZE(BYTESIZE (Macro) NIL NIL ("7") 8) BYTESPEC) [Macro] Returns the SIZE componant of the byte specifier BYTESPEC. (BYTEPOSITION(BYTEPOSITION (Macro) NIL NIL ("7") 8) BYTESPEC) [Macro] Returns the POSITION componant of the byte specifier BYTESPEC. (LDB(LDB (Macro) NIL NIL ("7") 8) BYTESPEC VAL) [Macro] Equivalent to (LOADBYTE VAL (BYTEPOSITION BYTESPEC)(BYTESIZE BYTESPEC)) (DPB(DPB (Macro) NIL NIL ("7") 8) N BYTESPEC VAL) [Macro] Equivalent to (DEPOSITBYTE VAL (BYTEPOSITION BYTESPEC)(BYTESIZE BYTESPEC) N) Floating-Point Arithmetic(ARITHMETIC NIL Arithmetic NIL ("7") 8 SUBNAME FLOATING% POINT SUBTEXT floating% point)(FLOATING% POINT% ARITHMETIC NIL Floating% point% arithmetic NIL ("7") 8) 1 A floating-point number is input as a signed integer, followed by a decimal point, and another sequence of digits called the fraction, followed by an exponent (represented by E followed by a signed integer) and terminated by a delimiter. Both signs are optional, and either the fraction following the decimal point, or the integer preceding the decimal point may be omitted. One or the other of the decimal point or exponent may also be omitted, but at least one of them must be present to distinguish a floating-point number from an integer. For example, the following will be recognized as floating-point numbers: 5. 5.00 5.01 .3 5E2 5.1E2 5E-3 -5.2E+6 Floating-point numbers are printed using the format control specified by the function FLTFMT (see Chapter 25). FLTFMT is initialized to T, or free format. For example, the above floating-point numbers would be printed free format as: 5.0 5.0 5.01 .3 500.0 510.0 .005 -5.2E6 Floating-point numbers are created by the reader when a . or an E appears in a number, e.g., 1000 is an integer, 1000. a floating-point number, as are 1E3 and 1.E3. Note that 1000D, 1000F, and 1E3D are perfectly legal literal atoms. Floating-point numbers are also created by PACK and MKATOM, and as a result of arithmetic operations. PRINTNUM (see Chapter 25) permits greater control over the printed appearance of floating-point numbers, allowing such things as left-justification, suppression of trailing decimals, etc. The floating-point number range is stored in the following variables: MIN.FLOAT(MIN.FLOAT (Variable) NIL NIL ("7") 9) [Variable] The smallest possible floating-point number. MAX.FLOAT(MAX.FLOAT (Variable) NIL NIL ("7") 9) [Variable] The largest possible floating-point number. All of the functions described below work on floating-point numbers. Unless specified otherwise, if given an integer, they first convert the number to a floating-point number, e.g., (FPLUS 1 2.3) <=> (FPLUS 1.0 2.3) => 3.3; if given a non-numeric argument, they generate an error, Non-numeric arg. (FPLUS(FPLUS (Function) NIL NIL ("7") 9) X1 X2 ... XN) [NoSpread Function] X1 + X2 + ... + XN (FMINUS(FMINUS (Function) NIL NIL ("7") 9) X) [Function] - X (FDIFFERENCE(FDIFFERENCE (Function) NIL NIL ("7") 9) X Y) [Function] X - Y (FTIMES(FTIMES (Function) NIL NIL ("7") 9) X1 X2 ... XN) [NoSpread Function] X1 * X2 * ... * XN (FQUOTIENT(FQUOTIENT (Function) NIL NIL ("7") 10) X Y) [Function] X / Y. The results of division by zero and floating-point overflow is determined by the function OVERFLOW. (FREMAINDER(FREMAINDER (Function) NIL NIL ("7") 10) X Y) [Function] Returns the remainder when X is divided by Y. Equivalent to: (FDIFFERENCE X (FTIMES Y (FIX (FQUOTIENT X Y)))) Example: (FREMAINDER 7.5 2.3) => 0.6 (FGREATERP(FGREATERP (Function) NIL NIL ("7") 10) X Y) [Function] T, if X > Y, NIL otherwise. (FLESSP(FLESSP (Function) NIL NIL ("7") 10) X Y) [Function] T, if X < Y, NIL otherwise. (FEQP(FEQP (Function) NIL NIL ("7") 10) X Y) [Function] Returns T if X and Y are equal floating-point numbers; NIL otherwise. FEQP converts X and Y to floating-point numbers. (FMIN(FMIN (Function) NIL NIL ("7") 10) X1 X2 ... XN) [NoSpread Function] Returns the minimum of X1, X2, ..., XN. (FMIN) returns the largest possible floating-point number, the value of MAX.FLOAT. (FMAX(FMAX (Function) NIL NIL ("7") 10) X1 X2 ... XN) [NoSpread Function] Returns the maximum of X1, X2, ..., XN. (FMAX) returns the smallest possible floating-point number, the value of MIN.FLOAT. (FLOAT(FLOAT (Function) NIL NIL ("7") 10) X) [Function] Converts X to a floating-point number. Example: (FLOAT 0) => 0.0 Transcendental Arithmetic Functions 1 (EXPT(EXPT (Function) NIL NIL ("7") 10) A N) [Function] Returns AN. If A is an integer and N is a positive integer, returns an integer, e.g, (EXPT 3 4) => 81, otherwise returns a floating-point number. If A is negative and N fractional, generates the error, Illegal exponentiation. If N is floating and either too large or too small, generates the error, Value out of range expt. (SQRT(SQRT (Function) NIL NIL ("7") 11) N) [Function] Returns the square root of N as a floating-point number. N may be fixed or floating-point. Generates an error if N is negative. (LOG(LOG (Function) NIL NIL ("7") 11) X) [Function] Returns the natural logarithm of X as a floating-point number. X can be integer or floating-point. (ANTILOG(ANTILOG (Function) NIL NIL ("7") 11) X) [Function] Returns the floating-point number whose logarithm is X. X can be integer or floating-point. Example: (ANTILOG 1) = e => 2.71828... (SIN(SIN (Function) NIL NIL ("7") 11) X RADIANSFLG) [Function] Returns the sine of X as a floating-point number. X is in degrees unless RADIANSFLG = T. (COS(COS (Function) NIL NIL ("7") 11) X RADIANSFLG) [Function] Similar to SIN. ((TAN (Function) NIL NIL ("7") 11)TAN X RADIANSFLG) [Function] Similar to SIN. (ARCSIN (ARCSIN% (Function) NIL NIL ("7") 11)X RADIANSFLG) [Function] The value of ARCSIN is a floating-point number, and is in degrees unless RADIANSFLG = T. In other words, if (ARCSIN X RADIANSFLG) = Z then (SIN Z RADIANSFLG) = X. The range of the value of ARCSIN is -90 to +90 for degrees, -&s/2 to &s/2 for radians. X must be a number between -1 and 1. (ARCCOS(ARCCOS (Function) NIL NIL ("7") 11) X RADIANSFLG) [Function] Similar to ARCSIN. Range is 0 to 180, 0 to &s. (ARCTAN(ARCTAN (Function) NIL NIL ("7") 11) X RADIANSFLG) [Function] Similar to ARCSIN. Range is 0 to 180, 0 to &s. (ARCTAN2(ARCTAN2 (Function) NIL NIL ("7") 11) Y X RADIANSFLG) [Function] Computes (ARCTAN (FQUOTIENT Y X) RADIANSFLG), and returns a corresponding value in the range -180 to 180 (or -&s to &s), i.e. the result is in the proper quadrant as determined by the signs of X and Y. Generating Random Numbers 1 (RAND(RAND (Function) NIL NIL ("7") 11) LOWER UPPER) [Function] Returns a pseudo-random number between LOWER and UPPER inclusive, i.e., RAND can be used to generate a sequence of random numbers. If both limits are integers, the value of RAND is an integer, otherwise it is a floating-point number. The algorithm is completely deterministic, i.e., given the same initial state, RAND produces the same sequence of values. The internal state of RAND is initialized using the function RANDSET. (RANDSET(RANDSET (Function) NIL NIL ("7") 12) X) [Function] Returns the internal state of RAND. If X = NIL, just returns the current state. If X = T, RAND is initialized using the clocks, and RANDSET returns the new state. Otherwise, X is interpreted as a previous internal state, i.e., a value of RANDSET, and is used to reset RAND. For example, (SETQ OLDSTATE (RANDSET)) ... (for X from 1 to 10 do (PRIN1 (RAND 1 10))) 2847592748NIL (RANDSET OLDSTATE) ... (for X from 1 to 10 do (PRIN1 (RAND 1 10))) 2847592748NIL [This page intentionally left blank] (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "7-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "7-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "7-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "7-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "7-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "7-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))))),l~,rr30`T30`T,HH1EVENT-T@ PAGEHEADING RIGHTPAGE0 +T,l~,ll,l~60``T5HBH5HBH5HBH,/A PAGEHEADING RIGHTPAGET,@ PAGEHEADINGLEFTBACKT,HHTITANTITAN +TIMESROMAN +PALATINO PALATINO PALATINOPALATINO  HELVETICA TITAN TITAN TITAN CLASSIC +CLASSIC +CLASSIC +MODERN +MODERNMODERN +! IM.CHAP.GETFNMODERN +% HRULE.GETFNMODERN)!IM.INDEX.GETFNQIM.INDEX.GETFNQIM.INDEX.GETFN#AIM.INDEX.GETFNOIM.INDEX.GETFN +/IM.INDEX.GETFNS + #IM.INDEX.GETFNTITAN   +   ++  + !IM.INDEX.GETFNTITAN   +    + +Y  + #IM.INDEX.GETFNTITAN   +   +'  + $IM.INDEX.GETFNTITAN   +   ++  + +  + J +  +I3 +! +L + + + + IM.INDEX.GETFNTITAN     + +   +  +  " +   +N + +   +il + %IM.INDEX.GETFNTITAN   +  ++  + +#  +I  + + + +J 9IM.INDEX.GETFNGIM.INDEX.GETFN HRULE.GETFN +] + + !IM.INDEX.GETFNTITAN  +    +  +  +  + "IM.INDEX.GETFNTITAN  +  + +  + + 'IM.INDEX.GETFNTITAN  +  +   +"IM.INDEX.GETFN  +    +  +  +  +  + %IM.INDEX.GETFNTITAN  +  +  4    @ + &IM.INDEX.GETFNTITAN     +   +  +  +  +     + + 'IM.INDEX.GETFN    + +  +  +  + "IM.INDEX.GETFNMODERN +    + +  +  +  + IM.INDEX.GETFNMODERN +    + +  +  +  + IM.INDEX.GETFNCLASSIC +    + +  +  +  + "IM.INDEX.GETFNMODERN +  +  +    + + #IM.INDEX.GETFNMODERN +  + +  +? + IM.INDEX.GETFNMODERN +     +   +   +  + +- + IM.INDEX.GETFNMODERN +     +   +    + +- + IM.INDEX.GETFNCLASSIC +  +    +  +  + + + + + GIM.INDEX.GETFN9IM.INDEX.GETFN HRULE.GETFN + +) +/ +< +< +< +< +B +H + +8 + + +5 + +Z +: + +U +C + 'IM.INDEX.GETFNTITAN  + 'IM.INDEX.GETFNTITAN - %IM.INDEX.GETFNTITAN  %IM.INDEX.GETFNTITAN - (IM.INDEX.GETFNTITAN  (IM.INDEX.GETFNTITAN   + + + +* + +* + +z +; + +      +   +  +  +  + +    + +  +      +  +  + !IM.INDEX.GETFNTITAN    +   + + !IM.INDEX.GETFNTITAN    +   + + #IM.INDEX.GETFNTITAN       +   +  +  +  + + &IM.INDEX.GETFNTITAN     +  +   + + + 3 + + + 'IM.INDEX.GETFNTITAN     +    + + !IM.INDEX.GETFNTITAN     +    + +E  + &IM.INDEX.GETFNTITAN     + +  +  +  + #IM.INDEX.GETFNTITAN     + +  +  +  + !IM.INDEX.GETFNCLASSIC +    + +  +  +  + !IM.INDEX.GETFNTITAN     + +  +  +  + !IM.INDEX.GETFNTITAN      +   +   +  +: + + !IM.INDEX.GETFNTITAN      +   +   +  +; + + IM.INDEX.GETFNTITAN     + +   + +  " + +   + + + IM.INDEX.GETFNTITAN   +    ; + + +C +< + + !IM.INDEX.GETFNTITAN   +     +' 9 + + + + IM.INDEX.GETFNTITAN     +'   + ]IM.INDEX.GETFN +OIM.INDEX.GETFN HRULE.GETFN + #IM.INDEX.GETFNTITAN      +   + +0  + + "IM.INDEX.GETFNTITAN      +   + +0  + + #IM.INDEX.GETFNTITAN      +   + +,  + +' + + IM.INDEX.GETFNTITAN     +$  ( "   +  + IM.INDEX.GETFNTITAN     +%  b "    + +D + !IM.INDEX.GETFNTITAN     + + !IM.INDEX.GETFNTITAN     + + + + +G +7 + !IM.INDEX.GETFNTITAN   +/  +  + + + + (IM.INDEX.GETFNTITAN   +  + , + "IM.INDEX.GETFNTITAN     +  +  + + + !IM.INDEX.GETFNTITAN     + +  + "IM.INDEX.GETFN  +    +  + + !IM.INDEX.GETFNTITAN     +  +   +   + + "IM.INDEX.GETFNTITAN     +    +  +  + + IM.INDEX.GETFNTITAN     +    +  +  + + "IM.INDEX.GETFNTITAN     +  #  +  +  + + "IM.INDEX.GETFNTITAN     + 1   +   + + %IM.INDEX.GETFNTITAN       +     +    +   + + (IM.INDEX.GETFNTITAN         +    '  + +  +  + + + +  + +  + + IM.INDEX.GETFNTITAN      +K   @ +  +&r +D + IM.INDEX.GETFNTITAN     + 5   + "IM.INDEX.GETFNTITAN   +   +#  + &IM.INDEX.GETFNTITAN   +   +#  + IM.INDEX.GETFNTITAN     +  +  + + +  + + IM.INDEX.GETFNTITAN       +  +  + + +  +  + WIM.INDEX.GETFNIIM.INDEX.GETFN HRULE.GETFN +>|  + +V + + +b  +  +8 + + + +! + +  + + +P + +, +F &IM.INDEX.GETFNTITAN - &IM.INDEX.GETFNTITAN ,) +; + + "IM.INDEX.GETFNTITAN      +   +  +  +  + #IM.INDEX.GETFNTITAN   +  +  + (IM.INDEX.GETFNTITAN     +  +  + #IM.INDEX.GETFNTITAN      +   +  +  +  + 'IM.INDEX.GETFNTITAN     +  + Z + + + (IM.INDEX.GETFNTITAN     +   + +  +  +  +   + + 'IM.INDEX.GETFNTITAN     + +  +  +  + $IM.INDEX.GETFNTITAN     + +  +  +  + "IM.INDEX.GETFNTITAN     + +  # +  + +   + "IM.INDEX.GETFNTITAN      +   +   +  +B + + "IM.INDEX.GETFNTITAN      +   +   +  +C + + #IM.INDEX.GETFNTITAN   +  '  + # HRULE.GETFN + "IM.INDEX.GETFNTITAN     +   1 +1  " + E + + "IM.INDEX.GETFNTITAN   +   8  + !IM.INDEX.GETFNTITAN   + !  # + %IM.INDEX.GETFNTITAN   +5  -  + + !IM.INDEX.GETFNMODERN +  +  +   +  + + !IM.INDEX.GETFNMODERN +  +  +  + +!IM.INDEX.GETFN   +  +  + + &IM.INDEX.GETFN  +  +  +6 +  + +  + + +  +  + + + + & + $IM.INDEX.GETFNTITAN   +  +  +  + + + + $IM.INDEX.GETFNTITAN   +  +  +  + + + + %IM.INDEX.GETFNTITAN     +  +  +   + +  +1 + + + + + +M    HRULE.GETFN + "IM.INDEX.GETFNTITAN      +'   +b + +> +# + + %IM.INDEX.GETFNTITAN   + +  +&  + +& +$ ? + +  + +- + + + +- + +%b cz \ No newline at end of file diff --git a/docs/turpin-irm/IRM-1/08-RECORDPACKAGE.TEDIT b/docs/turpin-irm/IRM-1/08-RECORDPACKAGE.TEDIT new file mode 100644 index 00000000..cfb215b3 Binary files /dev/null and b/docs/turpin-irm/IRM-1/08-RECORDPACKAGE.TEDIT differ diff --git a/docs/turpin-irm/IRM-1/09-conditionals.TEDIT b/docs/turpin-irm/IRM-1/09-conditionals.TEDIT new file mode 100644 index 00000000..c998b695 --- /dev/null +++ b/docs/turpin-irm/IRM-1/09-conditionals.TEDIT @@ -0,0 +1,105 @@ + INTERLISP-D REFERENCE MANUAL CONDITIONALS AND ITERATIVE STATEMENTS "9"9. LISTS AND ITERATIVE STATEMENTS 3 Medley gives you a large number of predicates, conditional functions, and control functions. Also, there is a complex iterative statement facility which allows you to easily create complex loops and iterative constructs. Data Type Predicates 1 Medley provides separate functions for testing whether objects are of certain commonly-used types: (LITATOM(LITATOM (Function) NIL NIL ("9") 1) X) [Function] Returns T if X is a symbol; NIL otherwise. Note that a number is not a symbol. (SMALLP(SMALLP (Function) NIL NIL ("9") 1) X) [Function] Returns X if X is a small integer; NIL otherwise. (The range of small integers is -65536 to +65535. (FIXP(FIXP (Function) NIL NIL ("9") 1) X) [Function] Returns X if X is a small or large integer; NIL otherwise. (FLOATP(FLOATP (Function) NIL NIL ("9") 1) X) [Function] Returns X if X is a floating point number; NIL otherwise. (NUMBERP(NUMBERP (Function) NIL NIL ("9") 1) X) [Function] Returns X if X is a number of any type, NIL otherwise. (ATOM(ATOM (Function) NIL NIL ("9") 1) X) [Function] Returns T if X is an atom (i.e. a symbol or a number); NIL otherwise. (ATOM X) is NIL if X is an array, string, etc. In Common Lisp, CL:ATOM is defined equivalent to the Interlisp function NLISTP. (LISTP(LISTP (Function) NIL NIL ("9") 1) X) [Function] Returns X if X is a list cell (something created by CONS); NIL otherwise. (NLISTP(NLISTP (Function) NIL NIL ("9") 1) X) [Function] (NOT (LISTP X)). Returns T if X is not a list cell, NIL otherwise. (STRINGP(STRINGP (Function) NIL NIL ("9") 1) X) [Function] Returns X if X is a string, NIL otherwise. (ARRAYP X) [Function] Returns X if X is an array, NIL otherwise. (HARRAYP X) [Function] Returns X if it is a hash array object; otherwise NIL. HARRAYP(HARRAYP (Function) NIL NIL ("9") 2) returns NIL if X is a list whose CAR is an HARRAYP, even though this is accepted by the hash array functions. Note: The empty list, () or NIL, is considered to be a symbol, rather than a list. Therefore, (LITATOM NIL) = (ATOM NIL) = T and (LISTP NIL) = NIL. Take care when using these functions if the object may be the empty list NIL. Equality Predicates 1 Sometimes, there is more than one type of equality. For instance, given two lists, you can ask whether they are exactly the same object, or whether they are two distinct lists that contain the same elements. Confusion between these two types of equality is often the source of program errors. (EQ(EQ (Function) NIL NIL ("9") 2) X Y) [Function] Returns T if X and Y are identical pointers; NIL otherwise. EQ should not be used to compare two numbers, unless they are small integers; use EQP instead. (NEQ(NEQ (Function) NIL NIL ("9") 2) X Y) [Function] The same as (NOT (EQ X Y)) (NULL(NULL (Function) NIL NIL ("9") 2) X) [Function] (NOT(NOT (Function) NIL NIL ("9") 2) X) [Function] The same as (EQ X NIL) (EQP(EQP (Function) NIL NIL ("9") 2) X Y) [Function] Returns T if X and Y are EQ, or if X and Y are numbers and are equal in value; NIL otherwise. For more discussion of EQP and other number functions, see Chapter 7. EQP also can be used to compare stack pointers (Section 11) and compiled code (Chapter 10). (EQUAL(EQUAL (Function) NIL NIL ("9") 2) X Y) [Function] EQUAL returns T if X and Y are one of the following: 1. EQ 2. EQP, i.e., numbers with equal value 3. STREQUAL, i.e., strings containing the same sequence of characters 4. Lists and CAR of X is EQUAL to CAR of Y, and CDR of X is EQUAL to CDR of Y EQUAL returns NIL otherwise. Note that EQUAL can be significantly slower than EQ. A loose description of EQUAL might be to say that X and Y are EQUAL if they print out the same way. (EQUALALL(EQUALALL (Function) NIL NIL ("9") 2) X Y) [Function] Like EQUAL, except it descends into the contents of arrays, hash arrays, user data types, etc. Two non-EQ arrays may be EQUALALL if their respective componants are EQUALALL. Note: In general, EQUALALL descends all the way into all datatypes, both those you've defined and those built into the system. If you have a data structure with fonts and pointers to windows, EQUALALL will descend those also. If the data structures are circular, as windows are, EQUALALL can cause stack overflow. Logical Predicates 1 (AND X1 X2 ... XN)(AND% X1% X2% ...% XN%) (Function) %(AND% X1% X2% ...% XN%) NIL ("9") 3) [NLambda NoSpread Function] Takes an indefinite number of arguments (including zero), that are evaluated in order. If any argument evaluates to NIL, AND immediately returns NIL, without evaluating the remaining arguments. If all of the arguments evaluate to non-NIL, the value of the last argument is returned. (AND) => T. (OR X1 X2 ... XN)(OR% X1% X2% ...% XN%) (Function) %(OR% X1% X2% ...% XN%) NIL ("9") 3) [NLambda NoSpread Function] Takes an indefinite number of arguments (including zero), that are evaluated in order. If any argument is non-NIL, the value of that argument is returned by OR (without evaluating the remaining arguments). If all of the arguments evaluate to NIL, NIL is returned. (OR) => NIL. AND and OR can be used as simple logical connectives, but note that they may not evaluate all of their arguments. This makes a difference if some of the arguments cause side-effects. This also means you can use AND and OR as simple conditional statements. For example: (AND (LISTP X) (CDR X)) returns the value of (CDR X) if X is a list cell; otherwise it returns NIL without evaluating (CDR X). In general, you should avoid this use of AND and OR in favor of more explicit conditional statements in order to make programs more readable. COND Conditional Function 1 (COND CLAUSE1 CLAUSE2 ... CLAUSEK)(COND% CLAUSE1% CLAUSE2% ...% CLAUSEK%) (Function) %(COND% CLAUSE1% CLAUSE2% ...% CLAUSEK%) NIL ("9") 3) [NLambda NoSpread Function] COND takes an indefinite number of arguments, called clauses. Each CLAUSEi is a list of the form (Pi Ci1 ... CiN), where Pi is the predicate, and Ci1 ... CiN are the consequents. The operation of COND can be paraphrased as: IF P1 THEN C11 ... C1N ELSEIF P2 THEN C21 ... C2N ELSEIF P3 ... The clauses are considered in sequence as follows: The predicate P1 of the clause CLAUSEi is evaluated. If the value of P1 is true (non-NIL), the consequents Ci1 ... CiN are evaluated in order, and the value of the COND is the value of the last expression in the clause. If P1 is false (EQ to NIL), then the remainder of CLAUSEi is ignored, and the next clause, CLAUSEi+1, is considered. If no Pi is true for any clause, the value of the COND is NIL. If a clause has no consequents, and has the form (Pi), then if Pi evaluates to non-NIL, it is returned as the value of the COND. It is only evaluated once. Example: (DEFINEQ (DOUBLE (X) (COND ((NUMBERP X) (PLUS X X)) ((STRINGP X) (CONCAT X X)) ((ATOM X) (PACK* X X)) (T (PRINT "unknown") X) ((HORRIBLE-ERROR))] (DOUBLE) (DOUBLE 5) 10 (DOUBLE "FOO") "FOOFOO" (DOUBLE 'BAR) BARBAR (DOUBLE '(A B C)) "unknown" (A B C) A few points about this example: Notice that 5 is both a number and an atom, but it is caught by the NUMBERP clause before the ATOM clause. Also notice the predicate T, which is always true. This is the normal way to indicate a COND clause which will always be executed (if none of the preceeding clauses are true). (HORRIBLE-ERROR) will never be executed. The IF Statement 1 The IF statement(IF% STATEMENT NIL IF% statement NIL ("9") 4) lets you write conditional expressions that are easier to read than using COND directly. CLISP translates expressions using IF, THEN, ELSEIF, or ELSE (or their lowercase versions) into equivalent CONDs. In general, statements of the form: (if AAA then BBB elseif CCC then DDD else EEE) are translated to: (COND (AAA BBB) (CCC DDD) (T EEE)) The segment between IF or ELSEIF and the next THEN corresponds to the predicate of a COND clause, and the segment between THEN and the next ELSE or ELSEIF as the consequent(s). ELSE is the same as ELSEIF T THEN. These words are spelling corrected using the spelling list CLISPIFWORDSPLST. You may also use lower-case versions (if, then, elseif, else). If there is nothing following a THEN, or THEN is omitted entirely, the resulting COND clause has a predicate but no consequent. For example, (if X then elseif ...) and (if X elseif ...) both translate to (COND (X) ...)%if X is not NIL, it is returned as the value of the COND. Each predicate must be a single expression, but multiple expressions are allowed as the consequents after THEN or ELSE. Multiple consequent expressions are implicitely wrapped in a PROGN, and the value of the last one is returned as the value of the consequent. For example: (if X then (PRINT "FOO") (PRINT "BAR") elseif Y then (PRINT "BAZ")) Selection Functions 1 (SELECTQ X CLAUSE1 CLAUSE2 ... CLAUSEK DEFAULT) [NLambda NoSpread Function] Selects a form or sequence of forms based on the value of X. Each clause CLAUSEi is a list of the form (Si Ci1 ... CiN) where Si is the selection key. Think of SELECTQ as: IF X = S1 THEN C11 ... C1N ELSEIF X = S2 THEN ... ELSE DEFAULT If Si is a symbol, the value of X is tested to see if it is EQ to Si (which is not evaluated). If so, the expressions Ci1 ... CiN are evaluated in sequence, and the value of the SELECTQ is the value of the last expression. If Si is a list, the value of X is compared with each element (not evaluated) of Si, and if X is EQ to any one of them, then Ci1 ... CiN are evaluated as above. If CLAUSEi is not selected in one of the two ways described, CLAUSEi+1 is tested, etc., until all the clauses have been tested. If none is selected, DEFAULT is evaluated, and its value is returned as the value of the SELECTQ. DEFAULT must be present. An example of the form of a SELECTQ is: [SELECTQ MONTH (FEBRUARY (if (LEAPYEARP) then 29 else 28)) ((SEPTEMBER APRIL JUNE NOVEMBER) 30) 31] If the value of MONTH is the symbol FEBRUARY, the SELECTQ returns 28 or 29 (depending on (LEAPYEARP)); otherwise if MONTH is APRIL, JUNE, SEPTEMBER, or NOVEMBER, the SELECTQ returns 30; otherwise it returns 31. SELECTQ compiles open, and is therefore very fast; however, it will not work if the value of X is a list, a large integer, or floating point number, since SELECTQ uses EQ for all comparisons. SELCHARQ (Chapter 2) is a version of SELECTQ that recognizes CHARCODE symbols. (SELECTC X CLAUSE1 CLAUSE2 ... CLAUSEK DEFAULT) [NLambda NoSpread Function] SELECTQ-on-Constant. Like SELECTQ, but the selection keys are evaluated, and the result used as a SELECTQ-style selection key. SELECTC is compiled as a SELECTQ, with the selection keys evaluated at compile-time. Therefore, the selection keys act like compile-time constants (see Chapter 18). For example: [SELECTC NUM ((for X from 1 to 9 collect (TIMES X X)) "SQUARE") "HIP"] compiles as: (SELECTQ NUM ((1 4 9 16 25 36 49 64 81) "SQUARE") "HIP") PROG and Associated Control Functions 1 (PROG1 X1 X2 ... XN) [NLambda NoSpread Function] Evaluates its arguments in order, and returns the value of its first argument X1. For example, (PROG1 X (SETQ X Y)) sets X to Y, and returns X's original value. (PROG2 X1 X2 ... XN) [NoSpread Function] Like PROG1. Evaluates its arguments in order, and returns the value of its second argument X2. (PROGN X1 X2 ... XN) [NLambda NoSpread Function] PROGN evaluates each of its arguments in order, and returns the value of its last argument. PROGN is used to specify more than one computation where the syntax allows only one, e.g., (SELECTQ ... (PROGN ...)) allows evaluation of several expressions as the default condition for a SELECTQ. (PROG VARLST E1 E2 ... EN) [NLambda NoSpread Function] Lets you bind some variables while you execute a series of expressions. VARLST is a list of local variables (must be NIL if no variables are used). Each symbol in VARLST is treated as the name of a local variable and bound to NIL. VARLST can also contain lists of the form (NAME FORM). In this case, NAME is the name of the variable and is bound to the value of FORM. The evaluation takes place before any of the bindings are performed, e.g., (PROG ((X Y) (Y X)) ...) will bind local variable X to the value of Y (evaluated outside the PROG) and local variable Y to the value of X (outside the PROG). An attempt to use anything other than a symbol as a PROG variable will cause an error, Arg not symbol. An attempt to use NIL or T as a PROG variable will cause an error, Attempt to bind NIL or T. The rest of the PROG is a sequence of forms and symbols (labels). The forms are evaluated sequentially; the labels serve only as markers. The two special functions, GO and RETURN, alter this flow of control as described below. The value of the PROG is usually specified by the function RETURN. If no RETURN is executed before the PROG falls off the end, the value of the PROG is NIL. (GO L) [NLambda NoSpread Function] GO is used to cause a transfer in a PROG. (GO L) will cause the PROG to evaluate forms starting at the label L (GO does not evaluate its argument). A GO can be used at any level in a PROG. If the label is not found, GO will search higher progs within the same function, e.g., (PROG ... A ... (PROG ... (GO A))). If the label is not found in the function in which the PROG appears, an error is generated, Undefined or illegal GO. (RETURN X) [Function] A RETURN is the normal exit for a PROG. Its argument is evaluated and is immediately returned the value of the PROG in which it appears. Note: If a GO or RETURN is executed in an interpreted function which is not a PROG, the GO or RETURN will be executed in the last interpreted PROG entered if any, otherwise cause an error. GO or RETURN inside of a compiled function that is not a PROG is not allowed, and will cause an error at compile time. As a corollary, GO or RETURN in a functional argument, e.g., to SORT, will not work compiled. Also, since NLSETQ's and ERSETQ's compile as separate functions, a GO or RETURN cannot be used inside of a compiled NLSETQ or ERSETQ if the corresponding PROG is outside, i.e., above, the NLSETQ or ERSETQ. (LET VARLST E1 E2 ... EN) [Macro] LET is essentially a PROG that can't contain GO's or RETURN's, and whose last form is the returned value. (LET* VARLST E1 E2 ... EN) [Macro] (PROG* VARLST E1 E2 ... EN) [Macro] LET* and PROG* differ from LET and PROG only in that the binding of the bound variables is done sequentially. Thus (LET* ((A (LIST 5)) (B (LIST A A))) (EQ A (CADR B))) would evaluate to T; whereas the same form with LET might find A an unbound variable when evaluating (LIST A A). The Iterative Statement 1 The various forms of the iterative statement(ITERATIVE% STATEMENT NIL Iterative% statement NIL ("9") 7) (i.s.) let you write complex loops easily. Rather than writing PROG, MAPC, MAPCAR, etc., let Medley do it for you. An iterative statement is a form consisting of a number of special words (known as i.s. operators or i.s.oprs), followed by operands. Many i.s.oprs (FOR, DO, WHILE, etc.) act like loops in other programming languages; others (COLLECT, JOIN, IN, etc.) do things useful in Lisp. You can also use lower-case versions of i.s.oprs (do, collect, etc.). (for X from 1 to 5 do (PRINT 'FOO)) FOO FOO FOO FOO FOO NIL (for X from 2 to 10 by 2 collect (TIMES X X)) (4 16 36 64 100) (for X in '(A B 1 C 6.5 NIL (45)) count (NUMBERP X)) 2 Iterative statements are implemented using CLISP, which translates them into the appropriate PROGs, MAPCARs, etc. They're are translated using all CLISP declarations in effect (standard/fast/undoable/ etc.); see Chapter 21. Misspelled i.s.oprs are recognized and corrected using the spelling list CLISPFORWORDSPLST. Operators can appear in any order; CLISP scans the entire statement before it begins to translate. If you define a function with the same name as an i.s.opr (WHILE, TO, etc.), that i.s.opr will no longer cause looping when it appears as CAR of a form, although it will continue to be treated as an i.s.opr if it appears in the interior of an iterative statement. To alert you, a warning message is printed, e.g., (While defined, therefore disabled in CLISP). I.S. Types(I.S.% TYPES NIL I.S.% Types NIL ("9") 8) Every iterative statement must have exactly one of the following operators in it (its is.stype), to specify what happens on each iteration. Its operand is called the body of the iterative statement. DO FORMS(DO% FORM (I.S. Operator) NIL NIL ("9") 8) [I.S. Operator] Evaluate FORMS at each iteration. DO with no other operator specifies an infinite loop. If some explicit or implicit terminating condition is specified, the value of the loop is NIL. Translates to MAPC or MAP whenever possible. COLLECT FORM(COLLECT% FORM (I.S. Operator) NIL NIL ("9") 8) [I.S. Operator] The value of FORM at each iteration is collected in a list, which is returned as the value of the loop when it terminates. Translates to MAPCAR, MAPLIST or SUBSET whenever possible. When COLLECT translates to a PROG (if UNTIL, WHILE, etc. appear in the loop), the translation employs an open TCONC using two pointers similar to that used by the compiler for compiling MAPCAR. To disable this translation, perform (CLDISABLE 'FCOLLECT). JOIN FORM (JOIN% FORM% (I.S. Operator) NIL NIL ("9") 8) [I.S. Operator] FORM returns a list; the lists from each iteration are concatenated using NCONC, forming one long list. Translates to MAPCONC or MAPCON whenever possible. /NCONC, /MAPCONC, and /MAPCON are used when the CLISP declaration UNDOABLE is in effect. SUM FORM(SUM% FORM (I.S. Operator) NIL NIL ("9") 8) [I.S. Operator] The values of FORM from each iteration are added together and returned as the value of the loop, e.g., (for I from 1 to 5 sum (TIMES I I)) returns 1+4+9+16+25 = 55. IPLUS, FPLUS, or PLUS will be used in the translation depending on the CLISP declarations in effect. COUNT FORM(COUNT% FORM (I.S. Operator) NIL NIL ("9") 8) [I.S. Operator] Counts the number of times that FORM is true, and returns that count as the loop's value. ALWAYS FORM(ALWAYS% FORM (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] Returns T if the value of FORM is non-NIL for all iterations. Note: Returns NIL as soon as the value of FORM is NIL). NEVER FORM(NEVER% FORM (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] Like ALWAYS, but returns T if the value of FORM is never true. Note: Returns NIL as soon as the value of FORM is non-NIL. Often, you'll want to set a variable each time through the loop; that's called the iteration variable, or i.v. for short. The following i.s.types explicitly refer to the i.v. This is explained below under FOR. THEREIS FORM(THEREIS% FORM (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] Returns the first value of the i.v. for which FORM is non-NIL, e.g., (for X in Y thereis (NUMBERP X)) returns the first number in Y. Note: Returns the value of the i.v. as soon as the value of FORM is non-NIL. LARGEST FORM(LARGEST% FORM (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] SMALLEST FORM(SMALLEST% FORM (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] Returns the value of the i.v. that provides the largest/smallest value of FORM. $$EXTREME is always bound to the current greatest/smallest value, $$VAL to the value of the i.v. from which it came. Iteration Variable I.s.oprs You'll want to bind variables to use during the loop. Rather than putting the loop inside a PROG or LET, you can specify bindings like so: BIND VAR(BIND% VAR (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] BIND VARS(BIND% VARS (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] Used to specify dummy variables, which are bound locally within the i.s. Note: You can initialize a variable VAR by saying VARFORM: (bind HEIGHT 0 WEIGHT 0 for SOLDIER in ...) To specify iteration variables, use these operators: FOR VAR(FOR% VAR (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] Specifies the iteration variable (i.v.) that is used in conjunction with IN, ON, FROM, TO, and BY. The variable is rebound within the loop, so the value of the variable outside the loop is not affected. Example: (SETQ X 55) 55 (for X from 1 to 5 collect (TIMES X X)) (1 4 9 16 25) X 55 FOR OLD VAR(FOR% OLD% VAR (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] Like FOR, but VAR is not rebound, so its value outside the loop is changed. Example: (SETQ X 55) 55 (for old X from 1 to 5 collect (TIMES X X)) (1 4 9 16 25) X 6 FOR VARS(FOR% VARS (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] VARS a list of variables, e.g., (for (X Y Z) in ...). The first variable is the i.v., the rest are dummy variables. See BIND above. IN FORM(IN% FORM (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] FORM must evaluate to a list. The i.v. is set to successive elements of the list, one per iteration. For example, (for X in Y do ...) corresponds to (MAPC Y (FUNCTION (LAMBDA (X) ...))). If no i.v. has been specified, a dummy is supplied, e.g., (in Y collect CADR) is equivalent to (MAPCAR Y (FUNCTION CADR)). ON FORM(ON% FORM (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] Same as IN, but the i.v. is reset to the corresponding tail at each iteration. Thus IN corresponds to MAPC, MAPCAR, and MAPCONC, while ON corresponds to MAP, MAPLIST, and MAPCON. (for X on '(A B C) do (PRINT X)) (A B C) (B C) (C) NIL Note: For both IN and ON, FORM is evaluated before the main part of the i.s. is entered, i.e. outside of the scope of any of the bound variables of the i.s. For example, (for X bind (Y'(1 2 3)) in Y ...) will map down the list which is the value of Y evaluated outside of the i.s., not (1 2 3). IN OLD VAR(IN% OLD% VAR (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] Specifies that the i.s. is to iterate down VAR, with VAR itself being reset to the corresponding tail at each iteration, e.g., after (for X in old L do ... until ...) finishes, L will be some tail of its original value. IN OLD (VARFORM)(IN% OLD% %(VAR_FORM%) (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] Same as IN OLD VAR, except VAR is first set to value of FORM. ON OLD VAR(ON% OLD% VAR (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] Same as IN OLD VAR except the i.v. is reset to the current value of VAR at each iteration, instead of to (CAR VAR). ON OLD (VARFORM)(ON% OLD% %(VAR_FORM%) (I.S. Operator) NIL NIL ("9") 11) [I.S. Operator] Same as ON OLD VAR, except VAR is first set to value of FORM. INSIDE FORM(INSIDE% FORM (I.S. Operator) NIL NIL ("9") 11) [I.S. Operator] Like IN, but treats first non-list, non-NIL tail as the last element of the iteration, e.g., INSIDE '(A B C D . E) iterates five times with the i.v. set to E on the last iteration. INSIDE 'A is equivalent to INSIDE '(A), which will iterate once. FROM FORM(FROM% FORM (I.S. Operator) NIL NIL ("9") 11) [I.S. Operator] Specifies the initial value for a numerical i.v. The i.v. is automatically incremented by 1 after each iteration (unless BY is specified). If no i.v. has been specified, a dummy i.v. is supplied and initialized, e.g., (from 2 to 5 collect SQRT) returns (1.414 1.732 2.0 2.236). TO FORM(TO% FORM (I.S. Operator) NIL NIL ("9") 11) [I.S. Operator] Specifies the final value for a numerical i.v. If FROM is not specified, the i.v. is initialized to 1. If no i.v. has been specified, a dummy i.v. is supplied and initialized. If BY is not specified, the i.v. is automatically incremented by 1 after each iteration. When the i.v. is definitely being incremented, i.e., either BY is not specified, or its operand is a positive number, the i.s. terminates when the i.v. exceeds the value of FORM. Similarly, when the i.v. is definitely being decremented the i.s. terminates when the i.v. becomes less than the value of FORM (see description of BY). FORM is evaluated only once, when the i.s. is first entered, and its value bound to a temporary variable against which the i.v. is checked each interation. If the user wishes to specify an i.s. in which the value of the boundary condition is recomputed each iteration, he should use WHILE or UNTIL instead of TO. When both the operands to TO and FROM are numbers, and TO's operand is less than FROM's operand, the i.v. is decremented by 1 after each iteration. In this case, the i.s. terminates when the i.v. becomes less than the value of FORM. For example, (from 10 to 1 do PRINT) prints the numbers from 10 down to 1. BY FORM (without IN or ON) (BY% FORM% %(WITHOUT% IN/ON%)% (I.S. Operator) BY% FORM% %(without% IN/ON%)% NIL ("9") 11) [I.S. Operator] If you aren't using IN or ON, BY specifies how the i.v. itself is reset at each iteration. If you're using FROM or TO, the i.v. is known to be numerical, so the new i.v. is computed by adding the value of FORM (which is reevaluated each iteration) to the current value of the i.v., e.g., (for N from 1 to 10 by 2 collect N) makes a list of the first five odd numbers. If FORM is a positive number (FORM itself, not its value, which in general CLISP would have no way of knowing in advance), the loop stops when the value of the i.v. exceeds the value of TO's operand. If FORM is a negative number, the loop stops when the value of the i.v. becomes less than TO's operand, e.g., (for I from N to M by -2 until (LESSP I M) ...). Otherwise, the terminating condition for each iteration depends on the value of FORM for that iteration: if FORM<0, the test is whether the i.v. is less than TO's operand, if FORM>0 the test is whether the i.v. exceeds TO's operand; if FORM = 0, the loop terminates unconditionally. If you didn't use FROM or TO and FORM is not a number, the i.v. is simply reset to the value of FORM after each iteration, e.g., (for I from N by (FOO) ...) sets I to the value of (FOO) on each loop after the first. BY FORM (with IN or ON) (BY% FORM% %(WITH% IN/ON%)% (I.S. Operator) BY% FORM% %(with% IN/ON%)% NIL ("9") 12) [I.S. Operator] If you did use IN or ON, FORM's value determines the tail for the next iteration, which in turn determines the value for the i.v. as described earlier, i.e., the new i.v. is CAR of the tail for IN, the tail itself for ON. In conjunction with IN, you can refer to the current tail within FORM by using the i.v. or the operand for IN/ON, e.g., (for Z in L by (CDDR Z) ...) or (for Z in L by (CDDR L) ...). At translation time, the name of the internal variable which holds the value of the current tail is substituted for the i.v. throughout FORM. For example, (for X in Y by (CDR (MEMB 'FOO (CDR X))) collect X) specifies that after each iteration, CDR of the current tail is to be searched for the atom FOO, and (CDR of) this latter tail to be used for the next iteration. AS VAR(AS% VAR (I.S. Operator) NIL NIL ("9") 12) [I.S. Operator] Lets you have more than one i.v. for a single loop, e.g., (for X in Y as U in V do ...) moves through the lisps Y and V in parallel (see MAP2C). The loop ends when any of the terminating conditions is met, e.g., (for X in Y as I from 1 to 10 collect X) makes a list of the first ten elements of Y, or however many elements there are on Y if less than 10. The operand to AS, VAR, specifies the new i.v. For the remainder of the i.s., or until another AS is encountered, all operators refer to the new i.v. For example, (for I from 1 to N1 as J from 1 to N2 by 2 as K from N3 to 1 by -1 ...) terminates when I exceeds N1, or J exceeds N2, or K becomes less than 1. After each iteration, I is incremented by 1, J by 2, and K by -1. OUTOF FORM(OUTOF% FORM (I.S. Operator) NIL NIL ("9") 12) [I.S. Operator] For use with generators. On each iteration, the i.v. is set to successive values returned by the generator. The loop ends when the generator runs out. Condition I.S. Oprs What if you want to do things only on certain times through the loop? You could make the loop body a big COND, but it's much more readable to use one of these: WHEN FORM(WHEN% FORM (I.S. Operator) NIL NIL ("9") 12) [I.S. Operator] Only run the loop body when FORM's value is non-NIL. For example, (for X in Y collect X when (NUMBERP X)) collects only the elements of Y that are numbers. UNLESS FORM(UNLESS% FORM (I.S. Operator) NIL NIL ("9") 12) [I.S. Operator] Opposite of WHEN: WHEN Z is the same as UNLESS (NOT Z). WHILE FORM(WHILE% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] WHILE FORM evaluates FORM before each iteration, and if the value is NIL, exits. UNTIL FORM(UNTIL% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] Opposite of WHILE: Evaluates FORM before each iteration, and if the value is not NIL, exits. REPEATWHILE FORM(REPEATWHILE% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] Same as WHILE except the test is performed after the loop body, but before the i.v. is reset for the next iteration. REPEATUNTIL FORM(REPEATUNTIL% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] Same as UNTIL, except the test is performed after the loop body. Other I.S. Operators FIRST FORM(FIRST% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] FORM is evaluated once before the first iteration, e.g., (for X Y Z in L first (FOO Y Z) ...), and FOO could be used to initialize Y and Z. FINALLY FORM(FINALLY% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] FORM is evaluated after the loop terminates. For example, (for X in L bind Y_0 do (if (ATOM X) then (SETQ Y (PLUS Y 1))) finally (RETURN Y)) will return the number of atoms in L. EACHTIME FORM(EACHTIME% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] FORM is evaluated at the beginning of each iteration before, and regardless of, any testing. For example, consider, (for I from 1 to N do (... (FOO I) ...) unless (... (FOO I) ...) until (... (FOO I) ...)) You might want to set a temporary variable to the value of (FOO I) in order to avoid computing it three times each iteration. However, without knowing the translation, you can't know whether to put the assignment in the operand to DO, UNLESS, or UNTIL. You can avoid this problem by simply writing EACHTIME (SETQ J (FOO I)). DECLARE: DECL(DECLARE:% DECL (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] Inserts the form (DECLARE DECL) immediately following the PROG variable list in the translation, or, in the case that the translation is a mapping function rather than a PROG, immediately following the argument list of the lambda expression in the translation. This can be used to declare variables bound in the iterative statement to be compiled as local or special variables. For example (for X in Y declare: (LOCALVARS X) ...). Several DECLARE:s can apppear in the same i.s.; the declarations are inserted in the order they appear. DECLARE DECL(DECLARE% DECL (I.S. Operator) NIL NIL ("9") 14) [I.S. Operator] Same as DECLARE:. Since DECLARE is also the name of a function, DECLARE cannot be used as an i.s. operator when it appears as CAR of a form, i.e. as the first i.s. operator in an iterative statement. However, declare (lowercase version) can be the first i.s. operator. ORIGINAL I.S.OPR OPERAND(ORIGINAL% I.S.OPR% OPERAND (I.S. Operator) NIL NIL ("9") 14) [I.S. Operator] I.S.OPR will be translated using its original, built-in interpretation, independent of any user defined i.s. operators. There are also a number of i.s.oprs that make it easier to create iterative statements that use the clock, looping for a given period of time. See timers, Chapter 12. Miscellaneous Hints For Using I.S.Oprs Lowercase versions of all i.s. operators are equivalent to the uppercase, e.g., (for X in Y ...) is equivalent to (FOR X IN Y ...). Each i.s. operator is of lower precedence than all Interlisp forms, so parentheses around the operands can be omitted, and will be supplied where necessary, e.g., BIND (X Y Z) can be written BIND X Y Z, OLD (X_FORM) as OLD X_FORM, etc. RETURN or GO may be used in any operand. (In this case, the translation of the iterative statement will always be in the form of a PROG, never a mapping function.) RETURN means return from the loop (with the indicated value), not from the function in which the loop appears. GO refers to a label elsewhere in the function in which the loop. appears, except for the labels $$LP, $$ITERATE, and $$OUT which are reserved, as described below. In the case of FIRST, FINALLY, EACHTIME, DECLARE: or one of the i.s.types, e.g., DO, COLLECT, SUM, etc., the operand can consist of more than one form, e.g., COLLECT (PRINT (CAR X)) (CDR X), in which case a PROGN is supplied. Each operand can be the name of a function, in which case it is applied to the (last) i.v., e.g., (for X in Y do PRINT when NUMBERP) is the same as (for X in Y do (PRINT X) when (NUMBERP X)). Note that the i.v. need not be explicitly specified, e.g., (in Y do PRINT when NUMBERP) will work. For i.s.types, e.g., DO, COLLECT, JOIN, the function is always applied to the first i.v. in the i.s., whether explicity named or not. For example, (in Y as I from 1 to 10 do PRINT) prints elements on Y, not integers between 1 and 10. Note that this feature does not make much sense for FOR, OLD, BIND, IN, or ON, since they operate before the loop starts, when the i.v. may not even be bound. In the case of BY in conjunction with IN, the function is applied to the current tail e.g., (for X in Y by CDDR ...) is the same as (for X in Y by (CDDR X) ...). While the exact translation of a loop depends on which operators are present, a PROG will always be used whenever the loop specifies dummy variables%if BIND appears, or there is more than one variable specified by a FOR, or a GO, RETURN, or a reference to the variable $$VAL appears in any of the operands. When PROG is used, the form of the translation is: (PROG VARIABLES {initialize} $$LP {eachtime} {test} {body} $$ITERATE {aftertest} {update} (GO $$LP) $$OUT {finalize} (RETURN $$VAL)) where {test} corresponds to that part of the loop that tests for termination and also for those iterations for which {body} is not going to be executed, (as indicated by a WHEN or UNLESS); {body} corresponds to the operand of the i.s.type, e.g., DO, COLLECT, etc.; {aftertest} corresponds to those tests for termination specified by REPEATWHILE or REPEATUNTIL; and {update} corresponds to that part that resets the tail, increments the counter, etc. in preparation for the next iteration. {initialize}, {finalize}, and {eachtime} correspond to the operands of FIRST, FINALLY, and EACHTIME, if any. Since {body} always appears at the top level of the PROG, you can insert labels in {body}, and GO to them from within {body} or from other i.s. operands, e.g., (for X in Y first (GO A) do (FOO) A (FIE)). However, since {body} is dwimified as a list of forms, the label(s) should be added to the dummy variables for the iterative statement in order to prevent their being dwimified and possibly corrected, e.g., (for X in Y bind A first (GO A) do (FOO) A (FIE)). You can also GO to $$LP, $$ITERATE, or $$OUT, or explicitly set $$VAL. Errors in Iterative Statements An error will be generated and an appropriate diagnostic printed if any of the following conditions hold: 1. Operator with null operand, i.e., two adjacent operators, as in (for X in Y until do ...) 2. Operand consisting of more than one form (except as operand to FIRST, FINALLY, or one of the i.s.types), e.g., (for X in Y (PRINT X) collect ...). 3. IN, ON, FROM, TO, or BY appear twice in same i.s. 4. Both IN and ON used on same i.v. 5. FROM or TO used with IN or ON on same i.v. 6. More than one i.s.type, e.g., a DO and a SUM. In 3, 4, or 5, an error is not generated if an intervening AS occurs. If an error occurs, the i.s. is left unchanged. If no DO, COLLECT, JOIN or any of the other i.s.types are specified, CLISP will first attempt to find an operand consisting of more than one form, e.g., (for X in Y (PRINT X) when ATOM X ...), and in this case will insert a DO after the first form. (In this case, condition 2 is not considered to be met, and an error is not generated.) If CLISP cannot find such an operand, and no WHILE or UNTIL appears in the i.s., a warning message is printed: NO DO, COLLECT, OR JOIN: followed by the i.s. Similarly, if no terminating condition is detected, i.e., no IN, ON, WHILE, UNTIL, TO, or a RETURN or GO, a warning message is printed: Possible non-terminating iterative statement: followed by the iterative statement. However, since the user may be planning to terminate the i.s. via an error, Control-E, or a RETFROM from a lower function, the i.s. is still translated. Note: The error message is not printed if the value of CLISPI.S.GAG is T (initially NIL). Defining New Iterative Statement Operators The following function is available for defining new or redefining existing iterative statement operators: (I.S.OPR NAME FORM OTHERS EVALFLG)(I.S.OPR% NAME% FORM% OTHERS% EVALFLG%) (Function) %(I.S.OPR% NAME% FORM% OTHERS% EVALFLG%) NIL ("9") 16) [Function] NAME is the name of the new i.s.opr. If FORM is a list, NAME will be a new i.s.type, and FORM its body. OTHERS is an (optional) list of additional i.s. operators and operands which will be added to the i.s. at the place where NAME appears. If FORM is NIL, NAME is a new i.s.opr defined entirely by OTHERS. In both FORM and OTHERS, the atom $$VAL can be used to reference the value to be returned by the i.s., I.V. to reference the current i.v., and BODY to reference NAME's operand. In other words, the current i.v. will be substituted for all instances of I.V. and NAME's operand will be substituted for all instances of BODY throughout FORM and OTHERS. If EVALFLG is T, FORM and OTHERS are evaluated at translation time, and their values used as described above. A dummy variable for use in translation that does not clash with a dummy variable already used by some other i.s. operators can be obtained by calling (GETDUMMYVAR). (GETDUMMYVAR T) will return a dummy variable and also insure that it is bound as a PROG variable in the translation. If NAME was previously an i.s.opr and is being redefined, the message (NAME REDEFINED) will be printed (unless DFNFLG=T), and all expressions using the i.s.opr NAME that have been translated will have their translations discarded. The following are some examples of how I.S.OPR could be called to define some existing i.s.oprs, and create some new ones: COLLECT (I.S.OPR 'COLLECT '(SETQ $$VAL (NCONC1 $$VAL BODY))) SUM (I.S.OPR 'SUM '(SETQ $$VAL_(PLUS $$VAL BODY) '(FIRST (SETQ $$VAL0)) NEVER (I.S.OPR 'NEVER '(if BODY then (SETQ $$VAL NIL) (GO $$OUT)) Note: (if BODY then (RETURN NIL)) would exit from the i.s. immediately and therefore not execute the operations specified via a FINALLY (if any). THEREIS (I.S.OPR 'THEREIS '(if BODY then (SETQ $$VAL I.V.) (GO $$OUT))) RCOLLECT To define RCOLLECT, a version of COLLECT which uses CONS instead of NCONC1 and then reverses the list of values: (I.S.OPR 'RCOLLECT '(FINALLY (RETURN (DREVERSE $$VAL)))] TCOLLECT To define TCOLLECT, a version of COLLECT which uses TCONC: (I.S.OPR 'TCOLLECT '(TCONC $$VAL BODY) '(FIRST (SETQ $$VAL (CONS)) FINALLY (RETURN (CAR $$VAL)))] PRODUCT (I.S.OPR 'PRODUCT '(SETQ $$VAL $$VAL*BODY) '(FIRST ($$VAL 1))] UPTO To define UPTO, a version of TO whose operand is evaluated only once: (I.S.OPR 'UPTO NIL '(BIND $$FOOBODY TO $$FOO)] TO To redefine TO so that instead of recomputing FORM each iteration, a variable is bound to the value of FORM, and then that variable is used: (I.S.OPR 'TO NIL '(BIND $$END FIRST (SETQ $$END BODY) ORIGINALTO $$END)] Note the use of ORIGINAL to redefine TO in terms of its original definition. ORIGINAL is intended for use in redefining built-in operators, since their definitions are not accessible, and hence not directly modifiable. Thus if the operator had been defined by the user via I.S.OPR, ORIGINAL would not obtain its original definition. In this case, one presumably would simply modify the i.s.opr definition. I.S.OPR can also be used to define synonyms for already defined i.s. operators by calling I.S.OPR with FORM an atom, e.g., (I.S.OPR 'WHERE 'WHEN) makes WHERE be the same as WHEN. Similarly, following (I.S.OPR 'ISTHERE 'THEREIS), one can write (ISTHERE ATOM IN Y), and following (I.S.OPR 'FIND 'FOR) and (I.S.OPR 'SUCHTHAT 'THEREIS), one can write (find X in Y suchthat X member Z) . In the current system, WHERE is synonymous with WHEN, SUCHTHAT and ISTHERE with THEREIS, FIND with FOR, and THRU with TO. If FORM is the atom MODIFIER, then NAME is defined as an i.s.opr which can immediately follow another i.s. operator (i.e., an error will not be generated, as described previously). NAME will not terminate the scope of the previous operator, and will be stripped off when DWIMIFY is called on its operand. OLD is an example of a MODIFIER type of operator. The MODIFIER feature allows the user to define i.s. operators similar to OLD, for use in conjunction with some other user defined i.s.opr which will produce the appropriate translation. The file package command I.S.OPRS (Chapter 17) will dump the definition of i.s.oprs. (I.S.OPRS PRODUCT UPTO) as a file package command will print suitable expressions so that these iterative statement operators will be (re)defined when the file is loaded. [This page intentionally left blank] (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "9-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "9-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "9-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "9-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "9-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "9-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))),ll2$$,ll,<<,r~2HTT2HTT,555,Zl,<<,~~,l~,l~,HH,HH,~~,ZZ,l~306T,l~,~~,l~1EVENT-T@ PAGEHEADING RIGHTPAGE//HH0 +T,3 +(T/F PAGEHEADING RIGHTPAGE/HH,HH306T,,@ PAGEHEADINGLEFTBACKTTITANTITAN +TIMESROMAN +PALATINO PALATINO PALATINO TITAN TITAN TITAN CLASSIC + HELVETICACLASSIC +MODERN +MODERN +MODERNMODERN +) #%" IM.CHAP.GETFNMODERN +# HRULE.GETFNMODERN"'!( HRULE.GETFNMODERN + c$IM.INDEX.GETFNTITAN  % 2#IM.INDEX.GETFNTITAN  %  ?!IM.INDEX.GETFNTITAN  %   #IM.INDEX.GETFNTITAN  %   $IM.INDEX.GETFNTITAN  %   !IM.INDEX.GETFNTITAN  % ) %  ,1"IM.INDEX.GETFNTITAN  %  & #IM.INDEX.GETFNTITAN  %   $IM.INDEX.GETFNTITAN  %    %    % )%$IM.INDEX.GETFNPALATINO  <%@L! HRULE.GETFN 'IM.INDEX.GETFNTITAN   + %   P + IM.INDEX.GETFNTITAN   + %   !IM.INDEX.GETFNTITAN  & IM.INDEX.GETFNTITAN  %   IM.INDEX.GETFNTITAN   + %    %$,%Y"IM.INDEX.GETFNTITAN   + %   ";     % "%  !%IM.INDEX.GETFNTITAN   + %^$%O! HRULE.GETFN   HIM.INDEX.GETFN%uW.    FIM.INDEX.GETFN%o,T 1     & ,[! HRULE.GETFN  +  +  + hIM.INDEX.GETFN%@       (       %B      .8   "   %2   %%     +%.9$>U! HRULE.GETFN  +-IM.INDEX.GETFNK//(             #!>) $9% j@ZD! HRULE.GETFN  +  +  +  +  + %:      !        %    %  1&%  2     % 3 P = %,*%    %V =% +  +  +  +  +  + %A%%  <%  .!% HRULE.GETFN  +  +  + %N   +  +  + %R    +  +  + %XVI  +  +  +  + %I ', 9 %   : N 8%C& ' %" ) %"9! %I%7*+%-:%$'         %/         &         % O% % +! HRULE.GETFN ,;IM.INDEX.GETFNA" @U  & / 6 ]f ;A* +)IM.INDEX.GETFN   *IM.INDEX.GETFNCLASSIC +%    /IM.INDEX.GETFNCLASSIC +%  y%<G(   .IM.INDEX.GETFN% F(%  +IM.INDEX.GETFNCLASSIC +% U# P  -IM.INDEX.GETFNCLASSIC +%  6 + .IM.INDEX.GETFNCLASSIC +%    + -IM.INDEX.GETFNCLASSIC +%     + /IM.INDEX.GETFNCLASSIC +%.  %9   /IM.INDEX.GETFNCLASSIC +&  0IM.INDEX.GETFNCLASSIC +%J  9. ]$ +IM.INDEX.GETFNCLASSIC +& ,IM.INDEX.GETFNCLASSIC +%I%!    0 5  *IM.INDEX.GETFNCLASSIC +%Iu )  0IM.INDEX.GETFNCLASSIC +% ( -  ,IM.INDEX.GETFNCLASSIC +% F +IM.INDEX.GETFNMODERN +% o$=  +IM.INDEX.GETFNCLASSIC +%-"  @F".  /IM.INDEX.GETFNCLASSIC +%+  M! *  8IM.INDEX.GETFN%    /IM.INDEX.GETFNCLASSIC +% 2 "    8IM.INDEX.GETFN%    /IM.INDEX.GETFNCLASSIC +%!2*   -IM.INDEX.GETFNCLASSIC +%z`  +IM.INDEX.GETFNCLASSIC +%3w o f %  %x    \IM.INDEX.GETFN%LX O$,%   I(S  , & '% ;  VIM.INDEX.GETFN% u+ &   3&49 *IM.INDEX.GETFNCLASSIC +%:F(+(% IB   .IM.INDEX.GETFNCLASSIC +%  j3 -IM.INDEX.GETFNCLASSIC +%' /IM.INDEX.GETFNCLASSIC +%  .IM.INDEX.GETFNCLASSIC +%  %  .IM.INDEX.GETFNCLASSIC +%   %   4IM.INDEX.GETFNCLASSIC +%E  4IM.INDEX.GETFNCLASSIC +% .IM.INDEX.GETFNCLASSIC +% 5$ 0IM.INDEX.GETFNCLASSIC +% 6R$ 1IM.INDEX.GETFNCLASSIC +% q%<0 1IM.INDEX.GETFNCLASSIC +% l' X 0IM.INDEX.GETFNCLASSIC +%%!7Q  =IM.INDEX.GETFNCLASSIC +% r &P  +  x8/_ ) =b"*> n!!4T)P@<!&*   +   +i03 9  u  + + +($*1 jCB"" #; 0&!4= ,64  +k  +  +  + iIM.INDEX.GETFN$ %   $ t   & $  @% W 4   $    D$ ?  +) C$'M + & + "  +  ! _  +  #    ' +      +     +    ' +      5 "       'uS !   V=n5%" z \ No newline at end of file diff --git a/docs/turpin-irm/IRM-1/10-FUNC-DEF.TEDIT b/docs/turpin-irm/IRM-1/10-FUNC-DEF.TEDIT new file mode 100644 index 00000000..141f28e3 --- /dev/null +++ b/docs/turpin-irm/IRM-1/10-FUNC-DEF.TEDIT @@ -0,0 +1,1177 @@ +INTERLISP-D REFERENCE MANUAL FUNCTION DEFINITION, MANIPULATION AND EVALUATION "10"FUNCTION DEFINITION, MANIPULATION AND EVALUATION 3 Medley is designed to help you define and debug functions. Developing an applications program with Medley involves defining a number of functions in terms of the system primitives and other user-defined functions. Once defined, your functions may be used exactly like Interlisp primitive functions, so the programming process can be viewed as extending the Interlisp language to include the required functionality. A function's definition specifies if the function has a fixed or variable number of arguments, whether these arguments are evaluated or not, the function argument names, and a series of forms which define the behavior of the function. For example: (LAMBDA (X Y) (PRINT X) (PRINT Y)) This function has two evaluated arguments, X and Y, and it will execute (PRINT X) and (PRINT Y) when evaluated. Other types of function definitions are described below. A function is defined by putting an expr definition in the function definition cell of a symbol. There are a number of functions for accessing and setting function definition cells, but one usually defines a function with DEFINEQ (see the Defining Functions section below). For example: (DEFINEQ (FOO (LAMBDA (X Y) (PRINT X) (PRINT Y))))(FOO) The expression above will define the function FOO to have the expr definition (LAMBDA (X Y) (PRINT X) (PRINT Y)). After being defined, this function may be evaluated just like any system function: (FOO 3 (IPLUS 3 4)) 3 7 7 Not all function definition cells contain expr definitions. The compiler (see the first page of Chapter 18) translates expr definitions into compiled code objects, which execute much faster. Interlisp provides a number of function type functions which determine how a given function is defined, the number and names of function arguments, etc. See the Function Type Functions section below. Usually, functions are evaluated automatically when they appear within another function or when typed into Interlisp. However, sometimes it is useful to envoke the Interlisp interpreter explicitly to apply a given functional argument to some data. There are a number of functions which will apply a given function repeatedly. For example, MAPCAR will apply a function (or an expr definition) to all of the elements of a list, and return the values returned by the function: (MAPCAR '(1 2 3 4 5) '(LAMBDA (X) (ITIMES X X)) (1 4 9 16 25) When using functional arguments, there are a number of problems which can arise, related to accessing free variables from within a function argument. Many times these problems can be solved using the function FUNCTION to create a FUNARG object. The macro facility provides another way of specifying the behavior of a function (see the Macros section below). Macros are very useful when developing code which should run very quickly, which should be compiled differently than when it is interpreted, or which should run differently in different implementations of Interlisp. Function Types 1 Interlisp functions are defined using list expressions called expr definitions. An expr definition is a list of the form (LAMBDA-WORD ARG-LIST FORM1 ... FORMN). LAMBDA-WORD determines whether the arguments to this function will be evaluated or not. ARG-LIST determines the number and names of arguments. FORM1 ... FORMN are a series of forms to be evaluated after the arguments are bound to the local variables in ARG-LIST. If LAMBDA-WORD is the symbol LAMBDA, then the arguments to the function are evaluated. If LAMBDA-WORD is the symbol NLAMBDA, then the arguments to the function are not evaluated. Functions which evaluate or don't evaluate their arguments are therefore known as lambda or nlambda functions, respectively. If ARG-LIST is NIL or a list of symbols, this indicates a function with a fixed number of arguments. Each symbol is the name of an argument for the function defined by this expression. The process of binding these symbols to the individual arguments is called spreading the arguments, and the function is called a spread function. If the argument list is any symbol other than NIL, this indicates a function with a variable number of arguments, known as a nospread function. If ARG-LIST is anything other than a symbol or a list of symbols, such as (LAMBDA "FOO" ...), attempting to use this expr definition will generate an Arg not symbol error. In addition, if NIL or T is used as an argument name, the error Attempt to bind NIL or T is generated. These two parameters (lambda/nlambda and spread/nospread) may be specified independently, so there are four nain function types, known as lambda-spread, nlanbda-spread, lanbda-nospread, and nlambda-nospread functions. Each one has a different form and is used for a different purpose. These four function types are described more fully below. For lambda-spread, lanbda-nospread, or nlambda-spread functions, there is an upper limit to the number of arguments that a function can have, based on the number of arguments that can be stored on the stack on any one function call. Currently, the limit is 80 arguments. If a function is called with more than that many arguments, the error Too many arguments occurs. However, nlambda-nospread functions can be called with an arbitrary number of arguments, since the arguments are not individually saved on the stack. Lambda-Spread Functions(LAMBDA-SPREAD% FUNCTIONS NIL Lambda-Spread% Functions NIL ("10") 2) Lambda-spread functions take a fixed number of evaluated arguments. This is the most common function type. A lambda-spread expr definition has the form: (LAMBDA (ARG1 ... ARGM) FORM1 ... FORMN) The argument list (ARG1 ... ARGM) is a list of symbols that gives the number and names of the formal arguments to the function. If the argument list is ( ) or NIL, this indicates that the function takes no arguments. When a lambda-spread function is applied to some arguments, the arguments are evaluated, and bound to the local variables ARG1 ... ARGM. Then, FORM1 ... FORMN are evaluated in order, and the value of the function is the value of FORMN. (DEFINEQ (FOO (LAMBDA (X Y) (LIST X Y)))) (FOO) (FOO 99 (PLUS 3 4)) (99 7) In the above example, the function FOO defined by (LAMBDA (X Y) (LIST X Y)) is applied to the arguments 99 and (PLUS 3 4). These arguments are evaluated (giving 99 and 7), the local variable X is bound to 99 and Y to 7, (LIST X Y) is evaluated, returning (99 7), and this is returned as the value of the function. A standard feature of the Interlisp system is that no error occurs if a spread function is called with too many or too few arguments. If a function is called with too many argumnents, the extra arguments are evaluated but ignored. If a function is called with too few arguments, the unsupplied ones will be delivered as NIL. In fact, a spread function cannot distinguish between being given NIL as an argument, and not being given that argument, e.g., (FOO) and (FOO NIL) are exactly the same for spread functions. If it is necessary to distinguish between these two cases, use an nlambda function and explicitly evaluate the arguments with the EVAL function. Nlambda-Spread Functions(NLAMBDA-SPREAD% FUNCTIONS NIL Nlambda-Spread% Functions NIL ("10") 3) Nlambda-spread functions take a fixed number of unevaluated arguments. An nlambda-spread expr definition has the form: (NLAMBDA (ARG1 ... ARGM) FORM1 ... FORMN) Nlambda-spread functions are evaluated similarly to lanbda-spread functions, except that the arguments are not evaluated before being bound to the variables ARG1 ... ARGM. (DEFINEQ (FOO (NLAMBDA (X Y) (LIST X Y)))) (FOO) (FOO 99 (PLUS 3 4)) (99 (PLUS 3 4)) In the above example, the function FOO defined by (NLAMBDA (X Y) (LIST X Y)) is applied to the arguments 99 and (PLUS 3 4). These arguments are unevaluated to X and Y. (LIST X Y) is evaluated, returning (99 (PLUS 3 4)), and this is returned as the value of the function. Functions can be defined so that all of their arguments are evaluated (lambda functions) or none are evaluated (nlambda functions). If it is desirable to write a function which only evaluates some of its arguments (e.g., SETQ), the functions should be defined as an nlambda, with some arguments explicitly evaluated using the function EVAL. If this is done, the user should put the symbol EVAL on the property list of the function under the property INFO. This informs various system packages, such as DWIM, CLISP, and Masterscope, that this function in fact does evaluate its arguments, even though it is an nlambda. Warning: A frequent problem that occurs when evaluating arguments to nlambda functions with EVAL is that the form being evaluated may reference variables that are not accessible within the nlambda function. This is usually not a problem when interpreting code, but when the code is compiled, the values of local variables may not be accessible on the stack (see Chapter 18). The system nlambda functions that evaluate their arguments (such as SETQ) are expanded in-line by the compiler, so this is not a problem. Using the macro facility is recommended in cases where it is necessary to evaluate some arguments to an nlambda function. Lambda-Nospread Functions(LAMBDA-NOSPREAD% FUNCTIONS NIL Lambda-Nospread% Functions NIL ("10") 3) Lambda-nospread functions take a variable number of evaluated arguments. A lambda-nospread expr definition has the form: (LAMBDA VAR FORM1 ... FORMN) VAR may be any symbol, except NIL and T. When a lambda-nospread function is applied to some arguments, each of these arguments is evaluated and the values stored on the stack. VAR is then bound to the number of arguments which have been evaluated. For example, if FOO is defined by (LAMBDA X ...), when (FOO A B C) is evaluated, A, B, and C are evaluated and X is bound to 3. VAR should never be reset The following functions are used for accessing the arguments of lambda-nospread functions. (ARG(ARG (Function) NIL NIL ("10") 4) VAR M) [NLambda Function] Returns the Mth argument for the lambda-nospread function whose argument list is VAR. VAR is the name of the atomic argument list to a lambda-nospread function, and is not evaluated. M is the number of the desired argument, and is evaluated. The value of ARG is undefined for M less than or equal to 0 or greater than the value of VAR. (SETARG(SETARG (Function) NIL NIL ("10") 4) VAR M X) [NLambda Function] Sets the Mth argument for the lambda-nospread function whose argument list is VAR to X. VAR is not evaluated; M and X are evaluated. M should be between 1 and the value of VAR. In the example below, the function FOO is defined to collect and return a list of all of the evaluated arguments it is given (the value of the for statement). (DEFINEQ (FOO (LAMBDA X (for ARGNUM from 1 to X collect (ARG X ARGNUM)] (FOO) (FOO 99 (PLUS 3 4)) (99 7) (FOO 99 (PLUS 3 4)(TIMES 3 4))) (99 7 12) NLambda-Nospread Functions(NLAMBDA-NOSPREAD% FUNCTIONS NIL NLambda-Nospread% Functions NIL ("10") 4) Nlambda-nospread functions take a variable number of unevaluated arguments. An nlambda-nospread expr definition has the form: (NLAMBDA VAR FORM1 ... FORMN) VAR may be any symbol, except NIL and T. Though similar in form to lambda-nospread expr definitions, an nlambda-nospread is evaluated quite differently. When an nlambda-nospread function is applied to some arguments, VAR is simply bound to a list of the unevaluated arguments. The user may pick apart this list, and evaluate different arguments. In the example below, FOO is defined to return the reverse of the list of arguments it is given (unevaluated): (DEFINEQ (FOO (NLAMBDA X (REVERSE X)))) (FOO) (FOO 99 (PLUS 3 4)) ((PLUS 3 4) 99) (FOO 99 (PLUS 3 4)(TIMES 3 4)) (TIMES 3 4)(PLUS 3 4) 99) The warning about evaluating arguments to nlambda functions also applies to nlambda-nospread function. Compiled Functions(COMPILED% FUNCTIONS NIL Compiled% Functions NIL ("10") 5) Functions defined by expr definitions can be compiled by the Interlisp compiler (see Chapter 18). The compiler produces compiled code objects (of data type CCODEP) which execute more quickly than the corresponding expr definition code. Functions defined by compiled code objects may have the same four types as expr definitions (lambda/nlambda, spread/nospread). Functions created by the compiler are referred to as compiled functions. Function Type Functions There are a variety of functions used for examining the type, argument list, etc. of functions. These functions may be given either a symbol (in which case they obtain the function definition from the definition cell), or a function definition itself. (FNTYP(FNTYP (Function) NIL NIL ("10") 5) FN) [Function] Returns NIL if FN is not a function definition or the name of a defined function. Otherwise, FNTYP returns one of the following symbols, depending on the type of function definition. EXPR Lambda-spread expr definition CEXPR Lambda-spread compiled definition FEXPR Nlambda-spread expr definition CFEXPR Nlambda-spread compiled definition EXPR* Lambda-nospread expr definition CEXPR* Lambda-nospread compiled definition FEXPR* Nlambda-nospread expr definition CFEXPR* Nlambda-nospread compiled definition FUNARG FNTYP returns the symbol FUNARG if FN is a FUNARG expression. EXP, FEXPR, EXPR*, and FEXPR* indicate that FN is defined by an expr definition. CEXPR, CFEXPR, CEXPR*, and CFEXPR* indicate that FN is defined by a compiled definition, as indicated by the prefix C. The suffix * indicates that FN has an indefinite number of arguments, i.e., is a nospread function. The prefix F indicates unevaluated arguments. Thus, for example, a CFEXPR* is a compiled nospread nlambda function. (EXPRP(EXPRP (Function) NIL NIL ("10") 5) FN) [Function] Returns T if (FNTYP FN) is EXPR, FEXPR, EXPR*, or FEXPR*; NIL otherwise. However, (EXPRP FN) is also true if FN is (has) a list definition, even if it does not begin with LAMBDA or NLAMBDA. In other words, EXPRP is not quite as selective as FNTYP. (CCODEP(CCODEP (Function) NIL NIL ("10") 5) FN) [Function] Returns T if (FNTYP FN) is either CEXPR, CFEXPR, CEXPR*, or CFEXPR*; NIL otherwise. (ARGTYPE(ARGTYPE (Function) NIL NIL ("10") 6) FN) [Function] FN is the name of a function or its definition. ARGTYPE returns 0, 1, 2, or 3, or NIL if FN is not a function. ARGTYPE corresponds to the rows of FNTYPs. The interpretation of this value is as follows: 0 Lambda-spread function (EXPR, CEXPR) 1 Nlambda-spread function (FEXPR, CFEXPR) 2 Lambda-nospread function (EXPR*, CEXPR*) 3 Nlambda-nospread function (FEXPR*, CFEXPR*) (NARGS(NARGS (Function) NIL NIL ("10") 6) FN) [Function] Returns the number of arguments of FN, or NIL if FN is not a function. If FN is a nospread function, the value of NARGS is 1. (ARGLIST(ARGLIST (Function) NIL NIL ("10") 6) FN) [Function] Returns the argument list for FN. Note that the argument list is a symbol for nospread functions. Since NIL is a possible value for ARGLIST, the error Args not available is generated if FN is not a function. If FN is a compiled function, the argument list is constructed, i.e., each call to ARGLIST requires making a new list. For functions defined by expr definitions, lists beginning with LAMBDA or NLAMBDA, the argument list is simply CADR of GETD. If FN has an expr definition, and CAR of the definition is not LAMBDA or NLAMBDA, ARGLIST will check to see if CAR of the definition is a member of LAMBDASPLST (see Chapter 20). If it is, ARGLIST presumes this is a function object the user is defining via DWIMUSERFORMS, and simply returns CADR of the definition as its argument list. Otherwise ARGLIST generates an error as described above. (SMARTARGLIST(SMARTARGLIST (Function) NIL NIL ("10") 6) FN EXPLAINFLG TAIL) [Function] A smart version of ARGLIST that tries various strategies to get the arglist of FN. First SMARTARGLIST checks the property list of FN under the property ARGNAMES. For spread functions, the argument list itself is stored. For nospread functions, the form is (NIL ARGLIST1 . ARGLIST2), where ARGLIST1 is the value SMARTARGLIST should return when EXPLAINFLG = T, and ARGLIST2 the value when EXPLAINFLG = NIL. For example, (GETPROP 'DEFINEQ 'ARGNAMES) = (NIL (X1 Xl ... XN) . X). This allows the user to specify special argument lists. Second, if FN is not defined as a function, SMARTARGLIST attempts spelling correction on FN by calling FNCHECK (see Chapter 20), passing TAIL to be used for the call to FIXSPELL. If unsuccessful, the FN Not a function error will be generated. Third, if FN is known to the file package (see Chapter 17) but not loaded in, SMARTARGLIST will obtain the arglist information from the file. Otherwise, SMARTARGLIST simply returns (ARGLIST FN). SMARTARGLIST is used by BREAK (see Chapter 15) and ADVISE with EXPLAINFLG = NIL for constructing equivalent expr definitions, and by the TTYIN in-line command ?= (see Chapter 26), with EXPLAINFLG = T. Defining Functions 1 Function definitions are stored in a function definition cell associated with each symbol. This cell is directly accessible via the two functions PUTD and GETD (see below), but it is usually easier to define functions with DEFINEQ: (DEFINEQ(DEFINEQ (Function) NIL NIL ("10") 7) X1 X2 ... XN) [NLambda NoSpread Function] DEFINEQ is the function normally used for defining functions. It takes an indefinite number of arguments which are not evaluated. Each Xi must be a list defining one function, of the form (NAME DEFINITION). For example: (DEFINEQ (DOUBLE (LAMBDA (X) (IPLUS X X)))) The above expression will define the function DOUBLE with the expr definition (LAMBDA (X) (IPLUS X X)). Xi may also have the form (NAME ARGS . DEF-BODY), in which case an appropriate lambda expr definition will be constructed. Therefore, the above expression is exactly the same as: (DEFINEQ (DOUBLE (X) (IPLUS X X))) Note that this alternate form can only be used for lambda functions. The first form must be used to define an nlambda function. DEFINEQ returns a list of the names of the functions defined. (DEFINE(DEFINE (Function) NIL NIL ("10") 7) X %) [Function] Lambda-spread version of DEFINEQ. Each element of the list X is itself a list either of the form (NAME DEFINITION) or (NAME ARGS . DEF-BODY). DEFINE will generate an error, Incorrect defining form on encountering an atom where a defining list is expected. DEFINE and DEFINEQ operate correctly if the function is already defined and BROKEN, ADVISED, or BROKEN-IN. For expressions involving type-in only, if the time stamp facility is enabled (see the Time Stamps section of Chapter 16), both DEFINE and DEFINEQ stamp the definition with your initials and date. UNSAFE.TO.MODIFY.FNS(UNSAFE.TO.MODIFY.FNS (Variable) NIL NIL ("10") 7) [Variable] Value is a list of functions that should not be redefined, because doing so may cause unusual bugs (or crash the system!). If you try to modify a function on this list (using DEFINEQ, TRACE, etc), the system prints Warning: XXX may be unsafe to modify -- continue? If you type Yes, the function is modified, otherwise an error occurs. This provides a measure of safety for novices who may accidently redefine important system functions. You can add your own functions onto this list. By convention, all functions starting with the character backslash (\) are system internal functions, which you should never redefine or modify. Backslash functions are not on UNSAFE.TO.MODIFY.FNS, so trying to redefine them will not cause a warning. DFNFLG(DFNFLG (Variable) NIL NIL ("10") 8) [Variable] DFNFLG is a global variable that affects the operation of DEFINEQ and DEFINE. If DFNFLG=NIL, an attempt to redefine a function FN will cause DEFINE to print the message (FN REDEFINED) and to save the old definition of FN using SAVEDEF (see the Functions for Manipulating Typed Definitions section of Chapter 17) before redefining it (except if the old and new definitions are EQUAL, in which case the effect is simply a no-op). If DFNFLG=T, the function is simply redefined. If DFNFLG=PROP or ALLPROP, the new definition is stored on the property list under the property EXPR. ALLPROP also affects the operation of RPAQQ and RPAQ (see the Functions Used Within Source Files section of Chapter 17). DFNFLG is initially NIL. DFNFLG is reset by LOAD (see the Loading Files section of Chapter 17) to enable various ways of handling the defining of functions and setting of variables when loading a file. For most applications, the user will not reset DFNFLG directly. Note: The compiler does not respect the value of DFNFLG when it redefines functions to their compiled definitions (see the first page of Chapter 18). Therefore, if you set DFNFLG to PROP to completely avoid inadvertantly redefining something in your running system, you must use compile mode F, not ST. Note that the functions SAVEDEF and UNSAVEDEF (see the Functions for Manipulating Typed Definitions section of Chapter 17) can be useful for saving and restoring function definitions from property lists. (GETD(GETD (Function) NIL NIL ("10") 8) FN) [Function] Returns the function definition of FN. Returns NIL if FN is not a symbol, or has no definition. GETD of a compiled function constructs a pointer to the definition, with the result that two successive calls do not necessarily produce EQ results. EQP or EQUAL must be used to compare compiled definitions. (PUTD(PUTD (Function) NIL NIL ("10") 8) FN DEF %) [Function] Puts DEF into FN's function cell, and returns DEF. Generates an error, Arg not symbol, if FN is not a symbol. Generates an error, Illegal arg, if DEF is a string, number, or a symbol other than NIL. (MOVD(MOVD (Function) NIL NIL ("10") 8) FROM TO COPYFLG %) [Function] Moves the definition of FROM to TO, i.e., redefines TO. If COPYFLG = T, a COPY of the definition of FROM is used. COPYFLG =T is only meaningful for expr definitions, although MOVD works for compiled functions as well. MOVD returns TO. COPYDEF (see the Functions for Manipulating Typed Definitions section of Chapter 17) is a higher-level function that not only moves expr definitions, but works also for variables, records, etc. (MOVD?(MOVD? (Function) NIL NIL ("10") 9) FROM TO COPYFLG %) [Function] If TO is not defined, same as (MOVD FROM TO COPYFLG). Otherwise, does nothing and returns NIL. Function Evaluation 1 Usually, function application is done automatically by the Interlisp interpreter. If a form is typed into Interlisp whose CAR is a function, this function is applied to the arguments in the CDR of the form. These arguments are evaluated or not, and bound to the funcion parameters, as determined by the type of the function, and the body of the function is evaluated. This sequence is repeated as each form in the body of the function is evaluated. There are some situations where it is necessary to explicitly call the evaluator, and Interlisp supplies a number of functions that will do this. These functions take functional arguments, which may either be symbols with function definitions, or expr definition forms such as (LAMBDA (X)...), or FUNARG expressions. (APPLY(APPLY (Function) NIL NIL ("10") 9) FN ARGLIST %) [Function] Applies the function FN to the arguments in the list ARGLIST, and returns its value. APPLY is a lambda function, so its arguments are evaluated, but the individual elements of ARGLIST are not evaluated. Therefore, lambda and nlambda functions are treated the same by APPLY%lambda functions take their arguments from ARGLIST without evaluating them. For example: (APPLY 'APPEND '((PLUS 1 2 3)(4 5 6))) (PLUS 1 2 3 4 5 6) Note that FN may explicitly evaluate one or more of its arguments itself. For example, the system function SETQ is an nlambda function that explicitly evaluates its second argument. Therefore, (APPLY 'SETQ '(FOO (ADD1 3)))will set FOO to 4, instead of setting it to the expression (ADD1 3). APPLY can be used for manipulating expr definitions. For example: (APPLY '(LAMBDA (X Y)(ITIMES X Y)) '(3 4))) 12 (APPLY*(APPLY* (Function) NIL NIL ("10") 9) FN ARG1 ARG2 ... ARGN ) [NoSpread Function] Nospread version of APPLY. Applies the function FN to the arguments ARG1 ARG2 ... ARGN. For example: (APPLY 'APPEND '(PLUS 1 2 3)(4 5 6)) (PLUS 1 2 3 4 5 6) (EVAL(EVAL (Function) NIL NIL ("10") 10) X%) [Function] EVAL evaluates the expression X and returns this value, i.e., EVAL provides a way of calling the Interlisp interpreter. Note that EVAL is itself a lambda function, so its argument is first evaluated, e.g.: (SETQ FOO 'ADD1 3))) (ADD1 3) (EVAL FOO) 4 (EVAL 'FOO) (ADD1 3) (QUOTE(QUOTE (Function) NIL NIL ("10") 10) X) [Nlambda NoSpread Function] QUOTE prevents its arguments from being evaluated. Its value is X itself, e.g., (QUOTE FOO) is FOO. Interlisp functions can either evaluate or not evaluate their arguments. QUOTE can be used in those cases where it is desirable to specify arguments unevaluated. The single-quote character (') is defined with a read macro so it returns the next expression, wrapped in a call to QUOTE (see Chapter 25). For example, 'FOO reads as (QUOTE FOO). This is the form used for examples in this manual. Since giving QUOTE more than one argument is almost always a parenthese error, and one that would otherewise go undetected, QUOTE itself generates an error in this case, Parenthesis error. (KWOTE(KWOTE (Function) NIL NIL ("10") 10) X) [Function] Value is an expression which, when evaluated, yields X. If X is NIL or a number, this is X itself. Otherwise (LIST (QUOTE QUOTE) X). For example: (KWOTE 5) => 5 (KWOTE (CONS 'A 'B)) => (QUOTE (A.B)) (NLAMBDA.ARGS(NLAMBDA.ARGS (Function) NIL NIL ("10") 10) X) [Function] This function interprets its argument as a list of unevaluated nlambda arguments. If any of the elements in this list are of the form (QUOTE...), the enclosing QUOTE is stripped off. Actually, NLAMBDA.ARGS stops processing the list after the first non-quoted argument. Therefore, whereas (NLAMBDA.ARGS '((QUOTE FOO) BAR)) -> (FOO BAR), (NLAMBDA.ARGS '(FOO (QUOTE BAR))) -> (FOO (QUOTE BAR)). NLAMBDA.ARGS is alled by a number of nlambda functions in the system, to interpret their arguments. For instance, the function BREAK calls NLAMBDA.ARGS so that (BREAK 'FOO) will break the function FOO, rather than the function QUOTE. (EVALA(EVALA (Function) NIL NIL ("10") 10) X A) [Function] Simulates association list variable lookup. X is a form, A is a list of the form: ((NAME1 . VAL1) (NAME2 . VAL2)... (NAMEN . VALN)) The variable names and values in A are spread on the stack, and then X is evaluated. Therefore, any variables appearing free in X that also appears as CAR of an element of A will be given the value on the CDR of that element. (DEFEVAL(DEFEVAL (Function) NIL NIL ("10") 11) TYPE FN) [Function] Specifies how a datum of a particular type is to be evaluated. Intended primarily for user-defined data types, but works for all data types except lists, literal atoms, and numbers. TYPE is a type name. FN is a function object, i.e., name of a function or a lambda expression. Whenever the interpreter encounters a datum of the indicated type, FN is applied to the datum and its value returned as the result of the evaluation. DEFEVAL returns the previous evaling function for this type. If FN = NIL, DEFEVAL returns the current evaling function without changing it. If FN = T, the evaling functions is set back to the system default (which for all data types except lists is to return the datum itself). COMPILETYPELST (see Chapter 18) permits the user to specify how a datum of a particular type is to be compiled. (EVALHOOK(EVALHOOK (Function) NIL NIL ("10") 11) FORM EVALHOOKFN) [Function] EVALHOOK evaluates the expression FORM, and returns its value. While evaluating FORM, the function EVAL behaves in a special way. Whenever a list other than FORM itself is to be evaluated, whether implicitly or via an explicit call to EVAL, EVALHOOKFN is invoked (it should be a function), with the form to be evaluated as its argument. EVALHOOKFN is then responsible for evaluating the form. Whatever is returned is assume to be the result of evaluating the form. During the execution of EVALHOOKFN, this special evaluation is turned off. (Note that EVALHOOK does not affect the evaluations of variables, only of lists). Here is an example of a simple tracing routine that uses the EVALHOOK feature: (DEFINEQ (PRINTHOOK (FORM) (printout T "eval: "FORM T) (EVALHOOK FORM (FUNCTION PRINTHOOK (PRINTHOOK) Using PRINTHOOK, one might see the following interaction: (EVALHOOK '(LIST (CONS 1 2)(CONS 3 4)) 'PRINTHOOK) eval: (CONS 1 2) eval: (CONS 3 4) ((1.2)(3.4)) Iterating and Mapping Functions 1 The functions below are used to evaluate a form or apply a function repeatedly. RPT, RPTQ, and FRPTQ evaluate an expression a specified number of time. MAP, MAPCAR, MAPLIST, etc., apply a given function repeatedly to different elements of a list, possibly constructing another list. These functions allow efficient iterative computations, but they are difficult to use. For programming iterative computations, it is usually better to use the CLISP Iterative Statement facility (see Chapter 9), which provides a more general and complete facility for expressing iterative statements. Whenever possible, CLISP transltes iterative statements into expressions using the functions below, so there is no efficiency loss. (RPT(RPT (Function) NIL NIL ("10") 12) N FORM) [Function] Evaluates the expression FORM, N times. Returns the value of the last evaluation. If N is less than or equal to 0, FORM is not evaluated, and RPT returns NIL. Before each evaluation, the local variable RPTN is bound to the number of evaluations yet to take place. This variable can be referenced within FORM. For example, (RPT 10 '(PRINT RPTN)) will print the numbers 10, 9...1, and return 1. (RPTQ(RPTQ (Function) NIL NIL ("10") 12) N FORM1 FORM2... FORMN) [NLambda NoSpread Function] Nlambda-nospread version of RPT: N is evaluated, FORMi are not. Returns the value of the last evaluation of FORMN. (FRPTQ(FRPTQ (NLambda NoSpread Function) NIL NIL ("10") 12) N FORM1 FORM2... FORMN) [NLambda NoSpread Function] Faster version of RPTQ. Does not bind RPTN. (MAP(MAP (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] If MAPFN2 is NIL, MAP applies the function MAPFN1 to successive tails of the list MAPX. That is, first it computes (MAPFN1 MAPX), and then (MAPFN1 (CDR MAPX)), etc., until MAPX becomes a non-list. If MAPFN2 is provided, (MAPFN2 MAPX) is used instead of (CDR MAPX) for the next call for MAPFN1, e.g., if MAPFN2 were CDDR, alternate elements of the list would be skipped. MAP returns NIL. (MAPC(MAPC (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] Identical to MAP, except that (MAPFN1 (CAR MAPX)) is computed at each iteration instead of (MAPFN1 MAPX), i.e., MAPC works on elements, MAP on tails. MAPC returns NIL. (MAPLIST(MAPLIST (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] Successively computes the same values that MAP would compute, and returns a list consisting of those values. (MAPCAR(MAPCAR (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] Computes the same values that MAPC would compute, and returns a list consisting of those values, e.g., (MAPCAR X 'FNTYP) is a list of FNTYPs for each element on X. (MAPCON(MAPCON (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] Computes the same values that MAP and MAPLIST but NCONCs these values to form a list which it returns. (MAPCONC(MAPCONC (Function) NIL NIL ("10") 12) MAPX MAPFN1 MAPFN2) [Function] Computes the same values that MAPC and MAPCAR, but NCONCs the values to form a list which it returns. Note that MAPCAR creates a new list which is a mapping of the old list in that each element of the new list is the result of applying a function to the corresponding element on the original list. MAPCONC is used when there are a variable number of elements (including none) to be inserted at each iteration. Examples: (MAPCONC '(A B C NIL D NIL) '(LAMBDA (Y)(if (NULL Y) then NIL else (LIST Y)))) = > (A B C D) This MAPCONC returns a list consisting of MAPX with all NILs removed. (MAPCONC '((A B) C (D E F)(G) H I) '(LAMBDA (Y)(if (LISP Y) then Y else NIL))) = > (A B D E F G) This MAPCONC returns a linear list consisting of all the lists on MAPX. Since MAPCONC uses NCONC to string the corresponding lists together, in this example the original list will be altered to be ((A B C D E F G) C (D E F G)(G) H I). If this is an undesirable side effect, the functional argument to MAPCONC should return instead a top level copy of the lists, i.e., (LAMBDA (Y) (if (LISTP Y) then (APPERND Y) else NIL))). (MAP2C(MAP2C (Function) NIL NIL ("10") 13) MAPX MAPY MAPFN1 MAPFN2) [Function] Identical to MAPC except MAPFN1 is a function of two arguments, and (MAPFN1 (CAR MAPX)(CAR MAPY)) is computed at each iteration. Terminates when either MAPX or MAPY is a non-list. MAPFN2 is still a function of one argument, and is applied twice on each iteration; (MAPFN2 MAPX) gives the new MAPX, (MAPFN2 MAPY) the new MAPY. CDR is used if MAPFN2 is not supplied, i.e., is NIL. (MAP2CAR(MAP2CAR (Function) NIL NIL ("10") 13) MAPX MAPY MAPFN1 MAPFN2) [Function] Identical to MAPCAR except MAPFN1 is a function of two arguments, and (MAPFN1 (CAR MAPX)(CAR MAPY)) is used to assemble the new list. Terminates when either MAPX or MAPY is a non-list. (SUBSET(SUBSET (Function) NIL NIL ("10") 13) MAPX MAPFN1 MAPFN2) [Function] Applies MAPFN1 to elements of MAPX and returns a list of those elements for which this application is non-NIL, e.g.: (SUBSET '(A B 3 C 4) 'NUMBERP) = (3 4) MAPFN2 plays the same role as with MAP, MAPC, et al. (EVERY(EVERY (Function) NIL NIL ("10") 13) EVERYX EVERYFN1 EVERYFN2) [Function] Returns T if the result of applying EVERYFN1 to each element in EVERYX is true, otherwise NIL. For example, (EVERY '(X Y Z) 'ATOM) => T. EVERY operates by evaluating (EVERYFN1 (CAR EVERYX) EVERYX). The second argument is passed to EVERYFN1 so that it can look at the next element on EVERYX if necessary. If EVERYFN1 yields NIL, EVERY immediately returns NIL. Otherwise, EVERY computes (EVERYFN2 EVERYX), or (CDR EVERYX) if EVERYFN2 = NIL, and uses this as the new EVERYX, and the process continues. For example (EVERY X 'ATOM 'CDDR) is true if every other element of X is atomic. (SOME(SOME (Function) NIL NIL ("10") 14) SOMEX SOMEFN1 SOMEFN2) [Function] Returns the tail of SOMEX beginning with the first element that satisfies SOMEFN1, i.e., for which SOMEFN1 applied to that element is true. Value is NIL if no such element exists. (SOME X '(LAMBDA (Z) (EQUAL Z Y))) is equivalent to (MEMBER Y X). SOME operates analogously to EVERY. At each stage, (SOMEFN1 (CAR SOMEX) SOMEX) is computed, and if this not NIL, SOMEX is returned as the value of SOME. Otherwise, (SOMEFN2 SOMEX) is computed, or (CDR SOMEX) if SOMEFN2 = NIL, and used for the next SOMEX. (NOTANY(NOTANY (Function) NIL NIL ("10") 14) SOMEX SOMEFN1 SOMEFN2) [Function] (NOT (SOME SOMEX SOMEFN1 SOMEFN2)). (NOTEVERY(NOTEVERY (Function) NIL NIL ("10") 14) EVERYX EVERYFN1 EVERYFN2) [Function] (NOT (EVERY EVERYX EVERYFN1 EVERYFN2)). (MAPRINT(MAPRINT (Function) NIL NIL ("10") 14) LST FILE LEFT RIGHT SEP PFN LISPXPRINTFLG) [Function] A general printing function. For each element of the list LST, applies PFN to the element, and FILE. If PFN is NIL, PRIN1 is used. Between each application MAPRINT performs PRIN1 of SEP (or "" if SEP = NIL). If LEFT is given, it is printed (using PRIN1) initially; if RIGHT is given, it is printed (using PRIN1) at the end. For example, (MAPRINT X NIL '%( '%)) is equivalent to PRIN1 for lists. To print a list with commas between each element and a final . one could use (MAPRINT X T NIL '%. '%,). If LISPXPRINTFLG = T, LISPXPRIN1 (see Chapter 13) is used instead of PRIN1. Functional Arguments 1 The functions that call the Interlisp-D evaluator take functional arguments, which may be symbols with function definitions, or expr definition forms such as (LAMBDA (X) ...). The following functions are useful when one wants to supply a functional argument which will always return NIL, T, or 0. Note that the arguments X1 ... XN to these functions are evaluated, though they are not used. (NILL(NILL (NoSpread Function) NIL NIL ("10") 14) X1 ... XN ) [NoSpread Function] Returns NIL. (TRUE(TRUE (NoSpread Function) NIL NIL ("10") 14) X1 ... XN ) [NoSpread Function] Returns T. (ZERO(ZERO (NoSpread Function) NIL NIL ("10") 15) X1 ... XN ) [NoSpread Function] Returns 0. When using expr definitions as function arguments, they should be enclosed within the function FUNCTION rather than QUOTE, so that they will be compiled as separate functions. (FUNCTION(FUNCTION (NLambda Function) NIL NIL ("10") 15) FN ENV ) [NLambda Function] If ENV = NIL, FUNCTION is the same as QUOTE, except that it is treated differently when compiled. Consider the function definition: (DEFINEQ (FOO (LST)(FIE LST (FUNCTION (LAMBDA (Z)(ITIMES Z Z))] FOO calls the function FIE with the value of LST and the expr definition (LAMBDA (Z)(LIST (CAR Z))). If FOO is run interpreted, it does not make any difference whether FUNCTION or QUOTE is used. However, when FOO is compiled, if FUNCTION is used the compiler will define and compile the expr definition as an auxiliary function (see Chapter 18). The compiled expr definition will run considerably faster, which can make a big difference if it is applied repeatedly. Compiling FUNCTION will not create an auxiliary function if it is a functional argument to a function that compiles open, such as most of the mapping functions (MAPCAR, MAPLIST, etc.). If ENV is not NIL, it can be a list of variables that are (presumably) used freely by FN. ENV can also be an atom, in which case it is evaluated, and the value interpreted as described above. Macros 1 Macros provide an alternative way of specifying the action of a function. Whereas function definitions are evaluated with a function call, which involves binding variables and other housekeeping tasks, macros are evaluated by translating one Interlisp form into another, which is then evaluated. A symbol may have both a function definition and a macro definition. When a form is evaluated by the interpreter, if the CAR has a function definition, it is used (with a function call), otherwise if it has a macro definition, then that is used. However, when a form is compiled, the CAR is checked for a macro definition first, and only if there isn't one is the function definition compiled. This allows functions that behave differently when compiled and interpreted. For example, it is possible to define a function that, when interpreted, has a function definition that is slow and has a lot of error checks, for use when debugging a system. This function could also have a macro definition that defines a fast version of the function, which is used when the debugged system is compiled. Macro definitions are represented by lists that are stored on the property list of a symbol. Macros are often used for functions that should be compiled differently in different Interlisp implementations, and the exact property name a macro definition is stored under determines whether it should be used in a particular implementation. The global variable MACROPROPS contains a list of all possible macro property names which should be saved by the MACROS file package command. Typical macro property names are DMACRO for Interlisp-D, 10MACRO for Interlisp-10, VAXMACRO for Interlisp-VAX, JMACRO for Interlisp-Jerico, and MACRO for implementation independent macros. The global variable COMPILERMACROPROPS is a list of macro property names. Interlisp determines whether a symbol has a macro definition by checking these property names, in order, and using the first non-NIL property value as the macro definition. In Interlisp-D this list contains DMACRO and MACRO in that order so that DMACROs will override the implementation-independent MACRO properties. In general, use a DMACRO property for macros that are to be used only in Interlisp-D, use 10MACRO for macros that are to be used only in Interlisp-10, and use MACRO for macros that are to affect both systems. Macro definitions can take the following forms: (LAMBDA ...) (NLAMBDA ...) A function can be made to compile open by giving it a macro definition of the form (LAMBDA ...) or (NLAMBDA ...), e.g., (LAMBDA (X) (COND ((GREATERP X 0) X) (T (MINUS X)))) for ABS. The effect is as if the macro definition were written in place of the function wherever it appears in a function being compiled, i.e., it compiles as a lambda or nlambda expression. This saves the time necessary to call the function at the price of more compiled code generated in-line. (NIL EXPRESSION) (LIST EXPRESSION) Substitution macro. Each argument in the form being evaluated or compiled is substituted for the corresponding atom in LIST, and the result of the substitution is used instead of the form. For example, if the macro definition of ADD1 is ((X) (IPLUS X 1)), then, (ADD1 (CAR Y)) is compiled as (IPLUS (CAR Y) 1). Note that ABS could be defined by the substitution macro ((X) (COND ((GREATERP X 0) X) (T (MINUS X)))). In this case, however, (ABS (FOO X)) would compile as (COND ((GREATERP (FOO X) 0) (FOO X)) (T (MINUS (FOO X)))) and (FOO X) would be evaluated two times. (Code to evaluate (FOO X) would be generated three times.) (OPENLAMBDA ARGS BODY) This is a cross between substitution and LAMBDA macros. When the compiler processes an OPENLAMBDA, it attempts to substitute the actual arguments for the formals wherever this preserves the frequency and order of evaluation that would have resulted from a LAMBDA expression, and produces a LAMBDA binding only for those that require it. Note: OPENLAMBDA assumes that it can substitute literally the actual arguments for the formal arguments in the body of the macro if the actual is side-effect free or a constant. Thus, you should be careful to use names in ARGS which don't occur in BODY (except as variable references). For example, if FOO has a macro definition of (OPENLAMBDA (ENV) (FETCH (MY-RECORD-TYPE ENV) OF BAR)) then (FOO NIL) will expand to (FETCH (MY-RECORD-TYPE NIL) OF BAR) T When a macro definition is the atom T, it means that the compiler should ignore the macro, and compile the function definition; this is a simple way of turning off other macros. For example, the user may have a function that runs in both Interlisp-D and Interlisp-10, but has a macro definition that should only be used when compiling in Interlisp-10. If the MACRO property has the macro specification, a DMACRO of T will cause it to be ignored by the Interlisp-D compiler. This DMACRO would not be necessary if the macro were specified by a 10MACRO instead of a MACRO. (= . OTHER-FUNCTION) A simple way to tell the compiler to compile one function exactly as it would compile another. For example, when compiling in Interlisp-D, FRPLACAs are treated as RPLACAs. This is achieved by having FRPLACA have a DMACRO of (= . RPLACA). (LITATOM EXPRESSION) If a macro definition begins with a symbol other than those given above, this allows computation of the Interlisp expression to be evaluated or compiled in place of the form. LITATOM is bound to the CDR of the calling form, EXPRESSION is evaluated, and the result of this evaluation is evaluated or compiled in place of the form. For example, LIST could be compiled using the computed macro: [X (LIST 'CONS (CAR X)(AND (CDR X)(CONS 'LIST (CDR X] This would cause (LIST X Y Z) to compile as (CONS X (CONS Y (CONS Z NIL))). Note the recursion in the macro expansion. If the result of the evaluation is the symbol IGNOREMACRO, the macro is ignored and the compilation of the expression proceeds as if there were no macro definition. If the symbol in question is normally treated specially by the compiler (CAR, CDR, COND, AND, etc.), and also has a macro, if the macro expansion returns IGNOREMACRO, the symbol will still be treated specially. In Interlisp-10, if the result of the evaluation is the atom INSTRUCTIONS, no code will be generated by the compiler. It is then assumed the evaluation was done for effect and the necessary code, if any, has been added. This is a way of giving direct instructions to the compiler if you understand it. It is often useful, when constructing complex macro expressions, to use the BQUOTE facility (see the Read Macros section of Chapter 25). The following function is quite useful for debugging macro definitions: (EXPANDMACRO(EXPANDMACRO (Function) NIL NIL ("10") 18) EXP QUIETFLG % %) [Function] Takes a form whose CAR has a macro definition and expands the form as it would be compiled. The result is prettyprinted, unless QUIETFLG=T, in which case the result is simply returned. Note: EXPANDMACRO only works on Interlisp macros. Use CL:MACROEXPAND-1 to expand Interlisp macros visible to the Common Lisp interpreter and compliler. DEFMACRO Macros defined with the function DEFMACRO are much like computed macros (see the above section), in that they are defined with a form that is evaluated, and the result of the evaluation is used (evaluated or compiled) in place of the macro call. However, DEFMACRO macros support complex argument lists with optional arguments, default values, and keyword arguments as well as argument list destructuring. (DEFMACRO(DEFMACRO (Function) NIL NIL ("10") 18) NAME ARGS FORM) [NLambda NoSpread Function] Defines NAME as a macro with the arguments ARGS and the definition form FORM (NAME, ARGS, and FORM are unevaluated). If an expression starting with NAME is evaluated or compiled, arguments are bound according to ARGS, FORM is evaluated, and the value of FORM is evaluated or compiled instead. The interpretation of ARGS is described below. Note: Like the function DEFMACRO in Common Lisp, this function currently removes any function definition for NAME. ARGS is a list that defines how the argument list passed to the macro NAME is interpreted. Specifically, ARGS defines a set of variables that are set to various arguments in the macro call (unevaluated), that FORM can reference to construct the macro form. In the simplest case, ARGS is a simple list of variable names that are set to the corresponding elements of the macro call (unevaluated). For example, given: (DEFMACRO FOO (A B) (LIST 'PLUS A B B)) The macro call (FOO X (BAR Y Z)) will expand to (PLUS X (BAR Y Z) (BAR Y Z)). &-keywords (beginning with the character &) that are used to set variables to particular items from the macro call form, as follows: &OPTIONAL Used to define optional arguments, possibly with default values. Each element on ARGS after &OPTIONAL until the next &-keyword or the end of the list defines an optional argument, which can either be a symbol or a list, interpreted as follows: VAR If an optional argument is specified as a symbol, that variable is set to the corresponding element of the macro call (unevaluated). (VAR DEFAULT) If an optional argument is specified as a two element list, VAR is the variable to be set, and DEFAULT is a form that is evaluated and used as the default if there is no corresponding element in the macro call. (VAR DEFAULT VARSETP) If an optional argument is specified as a three element list, VAR and DEFAULT are the variable to be set and the default form, and VARSETP is a variable that is set to T if the optional argument is given in the macro call, NIL otherwise. This can be used to determine whether the argument was not given, or whether it was specified with the default value. For example, after (DEFMACRO FOO (&OPTIONAL A (B 5) (C 6 CSET)) FORM) expanding the macro call (FOO) would cause FORM to be evaluated with A set to NIL, B set to 5, C set to 6, and CSET set to NIL. (FOO 4 5 6) would be the same, except that A would be set to 4 and CSET would be set to T. &REST &BODY Used to get a list of all additional arguments from the macro call. Either &REST or &BODY should be followed by a single symbol, which is set to a list of all arguments to the macro after the position of the &-keyword. For example, given (DEFMACRO FOO (A B &REST C) FORM) expanding the macro call (FOO 1 2 3 4 5) would cause FORM to be evaluated with A set to 1, B set to 2, and C set to (3 4 5). If the macro calling form contains keyword arguments (see &KEY below), these are included in the &REST list. &KEY Used to define keyword arguments, that are specified in the macro call by including a keyword (a symbol starting with the character :) followed by a value. Each element on ARGS after &KEY until the next &-keyword or the end of the list defines a keyword argument, which can either be a symbol or a list, interpreted as follows: VAR (VAR) ((KEYWORD VAR)) If a keyword argument is specified by a single symbol VAR, or a one-element list containing VAR, it is set to the value of a keyword argument, where the keyword used is created by adding the character : to the front of VAR. If a keyword argument is specified by a single-element list containing a two-element list, KEYWORD is interpreted as the keyword (which should start with the letter :), and VAR is the variable to set. (VAR DEFAULT) ((KEYWORD VAR) DEFAULT) (VAR DEFAULT VARSETP) ((KEYWORD VAR) DEFAULT VARSETP) If a keyword argument is specified by a two- or three-element list, the first element of the list specifies the keyword and variable to set as above. Similar to &OPTIONAL (above), the second element DEFAULT is a form that is evaluated and used as the default if there is no corresponding element in the macro call, and the third element VARSETP is a variable that is set to T if the optional argument is given in the macro call, NIL otherwise. For example, the form (DEFMACRO FOO (&KEY A (B 5 BSET) ((:BAR C) 6 CSET)) FORM) Defines a macro with keys :A, :B (defaulting to 5), and :BAR. Expanding the macro call (FOO :BAR 2 :A 1) would cause FORM to be evaluated with A set to 1, B set to 5, BSET set to NIL, C set to 2, and CSET set to T. &ALLOW-OTHER-KEYS It is an error for any keywords to be supplied in a macro call that are not defined as keywords in the macro argument list, unless either the &-keyword &ALLOW-OTHER-KEYS appears in ARGS, or the keyword :ALLOW-OTHER-KEYS (with a non-NIL value) appears in the macro call. &AUX Used to bind and initialize auxiliary varables, using a syntax similar to PROG (see the PROG and Associated Control Functions section of Chapter 9). Any elements after &AUX should be either symbols or lists, interpreted as follows: VAR Single symbols are interpreted as auxiliary variables that are initially bound to NIL. (VAR EXP) If an auxiliary variable is specified as a two element list, VAR is a variable initially bound to the result of evaluating the form EXP. For example, given (DEFMACRO FOO (A B &AUX C (D 5)) FORM) C will be bound to NIL and D to 5 when FORM is evaluated. &WHOLE Used to get the whole macro calling form. Should be the first element of ARGS, and should be followed by a single symbol, which is set to the entire macro calling form. Other &-keywords or arguments can follow. For example, given (DEFMACRO FOO (&WHOLE X A B) FORM) Expanding the macro call (FOO 1 2) would cause FORM to be evaluated with X set to (FOO 1 2), A set to 1, and B set to 2. DEFMACRO macros also support argument list destructuring, a facility for accessing the structure of individual arguments to a macro. Any place in an argument list where a symbol is expected, an argument list (in the form described above) can appear instead. Such an embedded argument list is used to match the corresponding parts of that particular argument, which should be a list structure in the same form. In the simplest case, where the embedded argument list does not include &-keywords, this provides a simple way of picking apart list structures passed as arguments to a macro. For example, given (DEFMACRO FOO (A (B (C . D)) E) FORM) Expanding the macro call (FOO 1 (2 (3 4 5)) 6) would cause FORM to be evaluated with with A set to 1, B set to 2, C set to 3, D set to (4 5), and E set to 6. Note that the embedded argument list (B (C . D)) has an embedded argument list (C . D). Also notice that if an argument list ends in a dotted pair, that the final symbol matches the rest of the arguments in the macro call. An embedded argument list can also include &-keywords, for interpreting parts of embedded list structures as if they appeared in a top-level macro call. For example, given (DEFMACRO FOO (A (B &OPTIONAL (C 6)) D) FORM) Expanding the macro call (FOO 1 (2) 3) would cause FORM to be evaluated with with A set to 1, B set to 2, C set to 6 (because of the default value), and D set to 3. Warning: Embedded argument lists can only appear in positions in an argument list where a list is otherwise not accepted. In the above example, it would not be possible to specify an embedded argument list after the &OPTIONAL keyword, because it would be interpreted as an optional argument specification (with variable name, default value, set variable). However, it would be possible to specify an embedded argument list as the first element of an optional argument specification list, as so: (DEFMACRO FOO (A (B &OPTIONAL ((X (Y) Z) '(1 (2) 3))) D) FORM) In this case, X, Y, and Z default to 1, 2, and 3, respectively. Note that the default value has to be an appropriate list structure. Also, in this case either the whole structure (X (Y) Z) can be supplied, or it can be defaulted (i.e., is not possible to specify X while letting Y default). Interpreting Macros When the interpreter encounters a form CAR of which is an undefined function, it tries interpreting it as a macro. If CAR of the form has a macro definition, the macro is expanded, and the result of this expansion is evaluated in place of the original form. CLISPTRAN (see the Miscellaneous Functions and Variables section of Chapter 21) is used to save the result of this expansion so that the expansion only has to be done once. On subsequent occasions, the translation (expansion) is retrieved from CLISPARRAY the same as for other CLISP constructs. Note: Because of the way that the evaluator processes macros, if you have a macro on FOO, then typing (FOO 'A 'B) will work, but FOO(A B) will not work. [This page intentionally left blank] (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "10-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "10-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "10-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "10-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (102 612 510 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "10-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "10-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))(5~5/HH306T,-l~T0<NT0<<T55555,HH,,xx,Zl,l~,~~,l~/<N/NN/<<1EVENT-T@ PAGEHEADING RIGHTPAGE0 +T0 +T,NN,<</,-TA PAGEHEADING RIGHTPAGET,<</HH,5@ PAGEHEADINGLEFTBACKT/HHTITAN TITANTITANTITAN +TIMESROMAN +MODERNPALATINO PALATINO PALATINO PALATINO PALATINO TITAN CLASSIC +MODERN +CLASSIC +CLASSIC +CLASSIC + HELVETICACLASSIC +MODERN +'"1! IM.CHAP.GETFNPALATINO 1 HRULE.GETFNMODERN! +% + +# + + + +  + +J + +; +: . + +"V +  + X + +2 + + + H + + HRULE.GETFN + | +  + M + 0 + _ + +  +  +8 +  + +  + +P +  +a +  +? +: +  + +( + + Y + W + + DIM.INDEX.GETFN +  +w + + + + F + +,# + + + + +) + + + + + + + + +5 +B +E +: + +  + +FIM.INDEX.GETFNx + + +-# + + + + +& + + +  +5 + +n +3 +9 + + V +_ + +HIM.INDEX.GETFNz +  + + +V + + +  + + + + + + +[ + !IM.INDEX.GETFNTITAN +$ +D + +_ +H + +6 + + $IM.INDEX.GETFNTITAN +$ +D + + + + + +& + +# +y +#<" +JIM.INDEX.GETFN +   + + + + +V ++!i +:IM.INDEX.GETFN + + + #IM.INDEX.GETFNTITAN +$ + +M +U +   +  " +   + $ +   +  $ + +   $ +     +   +   +  +$ + + + +$ + + + + +A + + +Q +8 +* + #IM.INDEX.GETFNTITAN$ + + +  + + + + + +  +< + + + + + $IM.INDEX.GETFNTITAN$ + + + + + + + + %IM.INDEX.GETFNTITAN +$/ + + + + + + + + +5 + +  + + + + + #IM.INDEX.GETFNTITAN +$# + + + +& + + + %IM.INDEX.GETFNTITAN +$ +L + + + + +$ +N +^ + + + + + + + + + +" +  += +  +4 +( + *IM.INDEX.GETFNTITAN +$ +5 + +$ +  + +c +  + +  + + + + + + + +7; +$ + + ! + + + + + +$ + +B + 4 +$ +  +  +$  + + + + +: + + + + + + + HRULE.GETFN + + +@ + + %IM.INDEX.GETFNCLASSIC + +$ +3 + +,$. + + + + +#$ +$6 + +  $IM.INDEX.GETFNCLASSIC + +$ + +% + + + +< +  +: + + +  + + +3 + 2IM.INDEX.GETFNCLASSIC + +$ + + +2 + +$ +7 + $IM.INDEX.GETFNCLASSIC + +$4 + + + + + + + +# + + +3 +( +  +G + + + +F + + +$ + + +$  + +v + +T + + + +$ + +  + "IM.INDEX.GETFNCLASSIC + +$# + + +( +$ + + +. + + "IM.INDEX.GETFNCLASSIC +  +$ + + + +  +' +  +- + + + "IM.INDEX.GETFNCLASSIC +  +$ + + + + + + + + +3 +( + + +$ + + + #IM.INDEX.GETFNCLASSIC +  + +$ + +& + + + HRULE.GETFN + { +A + +  + + + #IM.INDEX.GETFNTITAN + +$ + + +V +U +[ +P +,$ + +` +S + + +* + +$> +/ $IM.INDEX.GETFNTITAN + + + +$ + + + +( #IM.INDEX.GETFNTITAN +$ + +A +H +     $IM.INDEX.GETFNTITAN + + + +$< + +  + +$J +T +$ +5 +! + + 6 +$ +j +) + + $IM.INDEX.GETFNTITAN + +$5 + + + + + +& +IM.INDEX.GETFNTITAN +$ + + + + S +/ +8$ t + +  +  + + + $IM.INDEX.GETFNTITAN +$- + + +$! +% +; + + + + + &IM.INDEX.GETFNTITAN +$ + + +R +: + + +? + + +$b + + 'IM.INDEX.GETFNTITAN + +$ ++ + +7 +J + + +W + + + +5 +? +$= + + +# $ + + +4  + HRULE.GETFN + Q + + +5 + + +o +  + "IM.INDEX.GETFNTITAN +$ + +7 + + + + +$+ +b + + + + + + + #IM.INDEX.GETFNTITAN + + +$ + + +6 + + 5IM.INDEX.GETFNTITAN + + + +$ + + + "IM.INDEX.GETFNTITAN +$ + + + +! + + + + + + + + + +4 + + + #IM.INDEX.GETFNTITAN  +$ + + +* + + + + + + &IM.INDEX.GETFNTITAN + +$+ +@ + %IM.INDEX.GETFNTITAN  + +$ +D + + + + %IM.INDEX.GETFNTITAN +  +$ + + +/ + &IM.INDEX.GETFNTITAN +  + +$ + + +- + + + +t +>   + + + + + +C  +6 + +  + +d +%E +< +6 + $IM.INDEX.GETFNTITAN  + +$ + +% + + +8 + + +(O + + + + + + + + &IM.INDEX.GETFNTITAN +  + +$ + +% +; + + + %IM.INDEX.GETFNTITAN +  + +$ + +H + +'$ + + + $IM.INDEX.GETFNTITAN + +$ + + + + + +$ +$ +, + + + + + + + + + + + +* +# + + #IM.INDEX.GETFNTITAN +  +$ +0 + +, + +# +  + + + + + + + + + + %IM.INDEX.GETFNTITAN + +$  + + 'IM.INDEX.GETFNTITAN + + +$  + &IM.INDEX.GETFNTITAN* +  +$; + + + + + + +$ + + + + + + + + + + + + +$ + +J + + +$ +  + + +% + + + HRULE.GETFN + + + k + + + +< + ,IM.INDEX.GETFNTITAN + + + + +( ,IM.INDEX.GETFNTITAN + + + +( ,IM.INDEX.GETFNTITAN + + + + +( _ + +7 + + /IM.INDEX.GETFNTITAN + + + + + +$ + + + +Z +@$ + + + +$ += + + + + +$ + + + + + +$ + +E + +c + + HRULE.GETFN + + ; + z + + + g + +S +9 + + + + +? + +L + + +/ + +B +> +- + 0 +  +  + S +  +  +5 +# +   + +  +{ +k + + + + ++ +. + +     +1 +" +  +  * +) + + + +) +  +  + + + +3 + + 9  + + + &  $ +D +) + +@ +9 + + +   + + + + +  +   +V + P + + + +n +, + 8  +  +. + 0 +  + + + +> + . + ? +  + +  + +  +' +  +  + + *IM.INDEX.GETFNTITAN + +$ +k + +/ +$  + & +R + ! + + + 'IM.INDEX.GETFNTITAN +$ + + + + + +3 +< + + +: + +$  +M + +$B + +d +, +$ + +($ + + +$) +\ +  S + +  +~ +  + +   > + +m +  @ + +6 + +6 + +  +3 + + + + + + + + + + +  + + + + +   + L + +v + + $  + + + + + + + + < +# + +   +  + + +| +    7 +# +~ +^ +M + +         +  + + +6 + +  + ;   + + + + + + + + + + + + + + + +  + + + + +! +  J + + +M +< +  + T + +  ? +D + +  + )  + + + + + +  J +c +6 + %  +  + + +  + + + + +  +[ + (  + + + + + + + + + + + +( +  + + - + + 0  + + + + + + + +% + + +  + +  + +&  + + + + + + + +K + + + ' +M + +  + + + + R + +  +  + + +% + +۩ z \ No newline at end of file diff --git a/docs/turpin-irm/IRM-1/11-VAR-BINDINGS.TEDIT b/docs/turpin-irm/IRM-1/11-VAR-BINDINGS.TEDIT new file mode 100644 index 00000000..f3d913eb --- /dev/null +++ b/docs/turpin-irm/IRM-1/11-VAR-BINDINGS.TEDIT @@ -0,0 +1,142 @@ +INTERLISP-D REFERENCE MANUAL VARIABLE BINDINGS AND THE STACK "11"11. VARIABLE BINDINGS AND THE STACK 3 Medley uses deep binding. Every time a function is entered, a basic frame containing the new variables is put on top of the stack. Therefore, any variable reference requires searching the stack for the first instance of that variable, which makes free variable use somewhat more expensive than in a shallow binding scheme. On the other hand, spaghetti stack operations are considerably faster. Some other tricks involving copying freely-referenced variables to higher frames on the stack are also used to speed up the search. The basic frames are allocated on a stack; for most user purposes, these frames should be thought of as containing the variable names associated with the function call, and the current values for that frame. The descriptions of the stack functions in below are presented from this viewpoint. Both interpreted and compiled functions store both the names and values of variables so that interpreted and compiled functions are compatible and can be freely intermixed, i.e., free variables can be used with no SPECVAR declarations necessary. However, it is possible to suppress storing of names in compiled functions, either for efficiency or to avoid a clash, via a LOCALVAR declaration (see the Local Variables and Special Variables section of Chapter 18). The names are also very useful in debugging, for they make possible a complete symbolic backtrace in case of error. In addition to the binding information, additional information is associated with each function call: access information indicating the path to search the basic frames for variable bindings, control information, and temporary results are also stored on the stack in a block called the frame extension. The interpreter also stores information about partially evaluated expressions as described in the Stack and Interpreter section of Chapter 11. Spaghetti Stack 1 The Bobrow/Wegbreit paper, A Model and Stack Implementation for Multiple Environments (Communications of the ACM, Vol. 16, 10, October 1973.), describes an access and control mechanism more general than a simple linear stack. The access and control mechanism used by Interlisp is a slightly modified version of the one proposed by Bobrow and Wegbreit. This mechanism is called the spaghetti stack. The spaghetti system presents the access and control stack as a data structure composed of frames. The functions described below operate on this structure. These primitives allow user functions to manipulate the stack in a machine independent way. Backtracking, coroutines, and more sophisticated control schemes can be easily implemented with these primitives. The evaluation of a function requires the allocation of storage to hold the values of its local variables during the computation. In addition to variable bindings, an activation of a function requires a return link (indicating where control is to go after the completion of the computation) and room for temporaries needed during the computation. In the spaghetti system, one stack is used for storing all this information, but it is best to view this stack as a tree of linked objects called frame extensions (or simply frames). A frame extension is a variable sized block of storage containing a frame name, a pointer to some variable bindings (the BLINK), and two pointers to other frame extensions (the ALINK and CLINK). In addition to these components, a frame extension contains other information (such as temporaries and reference counts) that does not interest us here. The block of storage holding the variable bindings is called a basic frame. A basic frame is essentially an array of pairs, each of which contains a variable name and its value. The reason frame extensions point to basic frames (rather than just having them built in) is so that two frame extensions can share a common basic frame. This allows two processes to communicate via shared variable bindings. The chain of frame extensions which can be reached via the successive ALINKs from a given frame is called the access chain of the frame. The first frame in the access chain is the starting frame. The chain through successive CLINKs is called the control chain. A frame extension completely specifies the variable bindings and control information necessary for the evaluation of a function. Whenever a function (or in fact, any form which generally binds local variables) is evaluated, it is associated with some frame extension. In the beginning there is precisely one frame extension in existence. This is the frame in which the top-level call to the interpreter is being run. This frame is called the top-level frame. Since precisely one function is being executed at any instant, exactly one frame is distinguished as having the control bubble in it. This frame is called the active frame. Initially, the top-level frame is the active frame. If the computation in the active frame invokes another function, a new basic frame and frame extension are built. The frame name of this basic frame will be the name of the function being called. The ALINK, BLINK, and CLINK of the new frame all depend on precisely how the function is invoked. The new function is then run in this new frame by passing control to that frame, i.e., it is made the active frame. Once the active computation has been completed, control normally returns to the frame pointed to by the CLINK of the active frame. That is, the frame in the CLINK becomes the active frame. In most cases, the storage associated with the basic frame and frame extension just abandoned can be reclaimed. However, it is possible to obtain a pointer to a frame extension and to hold on to this frame even after it has been exited. This pointer can be used later to run another computation in that environment, or even continue the exited computation. A separate data type, called a stack pointer, is used for this purpose. A stack pointer is just a cell that literally points to a frame extension. Stack pointers print as #ADR/FRAMENAME, e.g., #1,13636/COND. Stack pointers are returned by many of the stack manipulating functions described below. Except for certain abbreviations (such as the frame with such-and-such a name), stack pointers are the only way you can reference a frame extension. As long as you have a stack pointer which references a frame extension, that frame extension (and all those that can be reached from it) will not be garbage collected. Two stack pointers referencing the same frame extension are not necessarily EQ, i.e., (EQ (STKPOS 'FOO) (STKPOS 'FOO)) = NIL. However, EQP can be used to test if two different stack pointers reference the same frame extension (see the Equality Predicates section of Chapter 9). It is possible to evaluate a form with respect to an access chain other than the current one by using a stack pointer to refer to the head of the access chain desired. Note, however, that this can be very expensive when using a shallow binding scheme such as that in Interlisp-10. When evaluating the form, since all references to variables under the shallow binding scheme go through the variable's value cell, the values in the value cells must be adjusted to reflect the values appropriate to the desired access chain. This is done by changing all the bindings on the current access chain (all the name-value pairs) so that they contain the value current at the time of the call. Then along the new access path, all bindings are made to contain the previous value of the variable, and the current value is placed in the value cell. For that part of the access path which is shared by the old and new chain, no work has to be done. The context switching time, i.e. the overhead in switching from the current, active, access chain to another one, is directly proportional to the size of the two branches that are not shared between the access contexts. This cost should be remembered in using generators and coroutines (see the Generators section below). Stack Functions 1 In the descriptions of the stack functions below, when we refer to an argument as a stack descriptor, we mean that it is one of the following: A stack pointer An object that points to a frame on the stack. Stack pointers are returned by many of the stack manipulating functions described below. NIL Specifies the active frame; that is, the frame of the stack function itself. T Specifies the top-level frame. A symbol Specifies the first frame (along the control chain from the active frame) that has the frame name LITATOM. Equivalent to (STKPOS LITATOM -1). A list of symbols Specifies the first frame (along the control chain from the active frame) whose frame name is included in the list. A number N Specifies the Nth frame back from the active frame. If N is negative, the control chain is followed, otherwise the access chain is followed. Equivalent to (STKNTH N). In the stack functions described below, the following errors can occur: The error Illegal stack arg occurs when a stack descriptor is expected and the supplied argument is either not a legal stack descriptor (i.e., not a stack pointer, symbol, or number), or is a symbol or number for which there is no corresponding stack frame, e.g., (STKNTH -1 'FOO) where there is no frame named FOO in the active control chain or (STKNTH -10 'EVALQT). The error Stack pointer has been released occurs whenever a released stack pointer is supplied as a stack descriptor argument for any purpose other than as a stack pointer to re-use. Note: The creation of a single stack pointer can result in the retention of a large amount of stack space. Therefore, one should try to release stack pointers when they are no longer needed (see the Releasing and Reusing Stack Pointers section below). In Lisp there is a fixed ammount of space allocated for the stack. When most of this space is exhausted, the STACK OVERFLOW error occurs and the debugger will be invoked. You will still have a little room on the stack to use inside the debugger. If you use up this last little bit of stack you will encounter a hard stack overflow. A hard stack overflow will put you into URaid (see the documentation on URaid). Searching the Stack 1 (STKPOS(STKPOS (Function) NIL NIL ("11") 4) FRAMENAME N POS OLDPOS) [Function] Returns a stack pointer to the Nth frame with frame name FRAMENAME. The search begins with (and includes) the frame specified by the stack descriptor POS. The search proceeds along the control chain from POS if N is negative, or along the access chain if N is positive. If N is NIL, -1 is used. Returns a stack pointer to the frame if such a frame exists, otherwise returns NIL. If OLDPOS is supplied and is a stack pointer, it is reused. If OLDPOS is supplied and is a stack pointer and STKPOS returns NIL, OLDPOS is released. If OLDPOS is not a stack pointer it is ignored. (STKNTH(STKNTH (Function) NIL NIL ("11") 4) N POS OLDPOS) [Function] Returns a stack pointer to the Nth frame back from the frame specified by the stack descriptor POS. If N is negative, the control chain from POS is followed. If N is positive the access chain is followed. If N equals 0, STKNTH returns a stack pointer to POS (this provides a way to copy a stack pointer). Returns NIL if there are fewer than N frames in the appropriate chain. If OLDPOS is supplied and is a stack pointer, it is reused. If OLDPOS is not a stack pointer it is ignored. Note: (STKNTH 0) causes an error, Illegal stack arg; it is not possible to create a stack pointer to the active frame. (STKNAME(STKNAME (Function) NIL NIL ("11") 4) POS) [Function] Returns the frame name of the frame specified by the stack descriptor POS. (SETSTKNAME(SETSTKNAME (Function) NIL NIL ("11") 4) POS NAME) [Function] Changes the frame name of the frame specified by POS to be NAME. Returns NAME. (STKNTHNAME(STKNTHNAME (Function) NIL NIL ("11") 4) N POS) [Function] Returns the frame name of the Nth frame back from POS. Equivalent to (STKNAME (STKNTH N POS)) but avoids creation of a stack pointer. In summary, STKPOS converts function names to stack pointers, STKNTH converts numbers to stack pointers, STKNAME converts stack pointers to function names, and STKNTHNAME converts numbers to function names. Variable Bindings in Stack Frames 1 The following functions are used for accessing and changing bindings. Some of functions take an argument, N, which specifies a particular binding in the basic frame. If N is a literal atom, it is assumed to be the name of a variable bound in the basic frame. If N is a number, it is assumed to reference the Nth binding in the basic frame. The first binding is 1. If the basic frame contains no binding with the given name or if the number is too large or too small, the error Illegal arg occurs. (STKSCAN(STKSCAN (Function) NIL NIL ("11") 5) VAR IPOS OPOS) [Function] Searches beginning at IPOS for a frame in which a variable named VAR is bound. The search follows the access chain. Returns a stack pointer to the frame if found, otherwise returns NIL. If OPOS is a stack pointer it is reused, otherwise it is ignored. (FRAMESCAN(FRAMESCAN (Function) NIL NIL ("11") 5) ATOM POS) [Function] Returns the relative position of the binding of ATOM in the basic frame of POS. Returns NIL if ATOM is not found. (STKARG(STKARG (Function) NIL NIL ("11") 5) N POS %) [Function] Returns the value of the binding specified by N in the basic frame of the frame specified by the stack descriptor POS. N can be a literal atom or number. (STKARGNAME(STKARGNAME (Function) NIL NIL ("11") 5) N POS) [Function] Returns the name of the binding specified by N, in the basic frame of the frame specified by the stack descriptor POS. N can be a literal atom or number. (SETSTKARG(SETSTKARG (Function) NIL NIL ("11") 5) N POS VAL) [Function] Sets the value of the binding specified by N in the basic frame of the frame specified by the stack descriptor POS. N can be a literal atom or a number. Returns VAL. (SETSTKARGNAME(SETSTKARGNAME (Function) NIL NIL ("11") 5) N POS NAME) [Function] Sets the variable name to NAME of the binding specified by N in the basic frame of the frame specified by the stack descriptor POS. N can be a literal atom or a number. Returns NAME. This function does not work for interpreted frames. (STKNARGS(STKNARGS (Function) NIL NIL ("11") 5) POS %) [Function] Returns the number of arguments bound in the basic frame of the frame specified by the stack descriptor POS. (VARIABLES(VARIABLES (Function) NIL NIL ("11") 5) POS) [Function] Returns a list of the variables bound at POS. (STKARGS(STKARGS (Function) NIL NIL ("11") 5) POS %) [Function] Returns a list of the values of the variables bound at POS. Evaluating Expressions in Stack Frames 1 The following functions are used to evaluate an expression in a different environment: (ENVEVAL(ENVEVAL (Function) NIL NIL ("11") 5) FORM APOS CPOS AFLG CFLG) [Function] Evaluates FORM in the environment specified by APOS and CPOS. That is, a new active frame is created with the frame specified by the stack descriptor APOS as its ALINK, and the frame specified by the stack descriptor CPOS as its CLINK. Then FORM is evaluated. If AFLG is not NIL, and APOS is a stack pointer, then APOS will be released. Similarly, if CFLG is not NIL, and CPOS is a stack pointer, then CPOS will be released. (ENVAPPLY(ENVAPPLY (Function) NIL NIL ("11") 6) FN ARGS APOS CPOS AFLG CFLG) [Function] APPLYs FN to ARGS in the environment specified by APOS and CPOS. AFLG and CFLG have the same interpretation as with ENVEVAL. (EVALV(EVALV (Function) NIL NIL ("11") 6) VAR POS RELFLG) [Function] Evaluates VAR, where VAR is assumed to be a symbol, in the access environment specifed by the stack descriptor POS. If VAR is unbound, EVALV returns NOBIND and does not generate an error. If RELFLG is non-NIL and POS is a stack pointer, it will be released after the variable is looked up. While EVALV could be defined as (ENVEVAL VAR POS NIL RELFLG) it is in fact somewhat faster. (STKEVAL(STKEVAL (Function) NIL NIL ("11") 6) POS FORM FLG %) [Function] Evaluates FORM in the access environment of the frame specified by the stack descriptor POS. If FLG is not NIL and POS is a stack pointer, releases POS. The definition of STKEVAL is (ENVEVAL FORM POS NIL FLG). (STKAPPLY(STKAPPLY (Function) NIL NIL ("11") 6) POS FN ARGS FLG) [Function] Like STKEVAL but applies FN to ARGS. Altering Flow of Control 1 The following functions are used to alter the normal flow of control, possibly jumping to a different frame on the stack. RETEVAL and RETAPPLY allow evaluating an expression in the specified environment first. (RETFROM(RETFROM (Function) NIL NIL ("11") 6) POS VAL FLG) [Function] Return from the frame specified by the stack descriptor POS, with the value VAL. If FLG is not NIL, and POS is a stack pointer, then POS is released. An attempt to RETFROM the top level (e.g., (RETFROM T)) causes an error, Illegal stack arg. RETFROM can be written in terms of ENVEVAL as follows: (RETFROM (LAMBDA (POS VAL FLG) (ENVEVAL (LIST 'QUOTE VAL) NIL (if (STKNTH -1 POS (if FLG then POS)) else (ERRORX (LIST 19 POS))) NIL T))) (RETTO(RETTO (Function) NIL NIL ("11") 6) POS VAL FLG) [Function] Like RETFROM, but returns to the frame specified by POS. (RETEVAL(RETEVAL (Function) NIL NIL ("11") 7) POS FORM FLG %) [Function] Evaluates FORM in the access environment of the frame specified by the stack descriptor POS, and then returns from POS with that value. If FLG is not NIL and POS is a stack pointer, then POS is released. The definition of RETEVAL is equivalent to (ENVEVAL FORM POS (STKNTH -1 POS) FLG T), but RETEVAL does not create a stack pointer. (RETAPPLY(RETAPPLY (Function) NIL NIL ("11") 7) POS FN ARGS FLG) [Function] Like RETEVAL but applies FN to ARGS. Releasing and Reusing Stack Pointers 1 The following functions and variables are used for manipulating stack pointers: (STACKP(STACKP (Function) NIL NIL ("11") 7) X) [Function] Returns X if X is a stack pointer, otherwise returns NIL. (RELSTK(RELSTK (Function) NIL NIL ("11") 7) POS) [Function] Release the stack pointer POS (see below). If POS is not a stack pointer, does nothing. Returns POS. (RELSTKP(RELSTKP (Function) NIL NIL ("11") 7) X) [Function] Returns T is X is a released stack pointer, NIL otherwise. (CLEARSTK(CLEARSTK (Function) NIL NIL ("11") 7) FLG) [Function] If FLG is T, returns a list of all the active (unreleased) stack pointers. If FLG is NIL, this call is a no-op. The abillity to clear all stack pointers is inconsistent with the modularity implicit in a multi processing environment. CLEARSTKLST(CLEARSTKLST (Variable) NIL NIL ("11") 7) [Variable] A variable used by the top-level executive. Every time the top-level executive is re-entered (e.g., following errors, or Control-D), CLEARSTKLST is checked. If its value is T, all active stack pointers are released using CLEARSTK. If its value is a list, then all stack pointers on that list are released. If its value is NIL, nothing is released. CLEARSTKLST is initially T. NOCLEARSTKLST(NOCLEARSTKLST (Variable) NIL NIL ("11") 7) [Variable] A variable used by the top-level executive. If CLEARSTKLST is T (see above) all active stack pointers except those on NOCLEARSTKLST are released. NOCLEARSTKLST is initially NIL. Creating a single stack pointer can cause the retention of a large amount of stack space. Furthermore, this space will not be freed until the next garbage collection, even if the stack pointer is no longer being used, unless the stack pointer is explicitly released or reused. If there is sufficient amount of stack space tied up in this fashion, a STACK OVERFLOW condition can occur, even in the simplest of computations. For this reason, you should consider releasing a stack pointer when the environment referenced by the stack pointer is no longer needed. The effects of releasing a stack pointer are: 1. The link between the stack pointer and the stack is broken by setting the contents of the stack pointer to the released mark. A released stack pointer prints as #ADR/#0. 2. If this stack pointer was the last remaining reference to a frame extension; that is, if no other stack pointer references the frame extension and the extension is not contained in the active control or access chain, then the extension may be reclaimed, and is reclaimed immediately. The process repeats for the access and control chains of the reclaimed extension so that all stack space that was reachable only from the released stack pointer is reclaimed. A stack pointer may be released using the function RELSTK, but there are some cases for which RELSTK is not sufficient. For example, if a function contains a call to RETFROM in which a stack pointer was used to specify where to return to, it would not be possible to simultaneously release the stack pointer. (A RELSTK appearing in the function following the call to RETFROM would not be executed!) To permit release of a stack pointer in this situation, the stack functions that relinquish control have optional flag arguments to denote whether or not a stack pointer is to be released (AFLG and CFLG). Note that in this case releasing the stack pointer will not cause the stack space to be reclaimed immediately because the frame referenced by the stack pointer will have become part of the active environment. Another way to avoid creating new stack pointers is to reuse stack pointers that are no longer needed. The stack functions that create stack pointers (STKPOS, STKNTH, and STKSCAN) have an optional argument that is a stack pointer to reuse. When a stack pointer is reused, two things happen. First the stack pointer is released (see above). Then the pointer to the new frame extension is deposited in the stack pointer. The old stack pointer (with its new contents) is returned as the value of the function. Note that the reused stack pointer will be released even if the function does not find the specified frame. Even if stack pointers are explicitly being released, creating many stack pointers can cause a garbage collection of stack pointer space. Thus, if your application requires creating many stack pointers, you definitely should take advantage of reusing stack pointers. Backtrace Functions 1 The following functions perform a backtrace, printing information about every frame on the stack. Arguments allow only backtracing a selected range of the stack, skipping selected frames, and printing different amounts of information about each frame. (BACKTRACE(BACKTRACE (Function) NIL NIL ("11") 8) IPOS EPOS FLAGS FILE PRINTFN) [Function] Performs a backtrace beginning at the frame specified by the stack descriptor IPOS, and ending with the frame specified by the stack descriptor EPOS. FLAGS is a number in which the options of the BACKTRACE are encoded. If a bit is set, the corresponding information is included in the backtrace. 1Q - print arguments of non-SUBRs 2Q - print temporaries of the interpreter 4Q - print SUBR arguments and local variables 10Q - omit printing of UNTRACE: and function names 20Q - follow access chain instead of control chain 40Q - print temporaries, i.e. the blips (see the stack and interpreter section below) For example: If FLAGS = 47Q, everything is printed. If FLAGS = 21Q, follows the access chain, prints arguments. FILE is the file that the backtrace is printed to. FILE must be open. PRINTFN is used when printing the values of variables, temporaries, blips, etc. PRINTFN = NIL defaults to PRINT. (BAKTRACE(BAKTRACE (Function) NIL NIL ("11") 9) IPOS EPOS SKIPFNS FLAGS FILE) [Function] Prints a backtrace from IPOS to EPOS onto FILE. FLAGS specifies the options of the backtrace, e.g., do/don't print arguments, do/don't print temporaries of the interpreter, etc., and is the same as for BACKTRACE. SKIPFNS is a list of functions. As BAKTRACE scans down the stack, the stack name of each frame is passed to each function in SKIPFNS, and if any of them returnS non-NIL, POS is skipped (including all variables). BAKTRACE collapses the sequence of several function calls corresponding to a call to a system package into a single function using BAKTRACELST as described below. For example, any call to the editor is printed as **EDITOR**, a break is printed as **BREAK**, etc. BAKTRACE is used by the BT, BTV, BTV+, BTV*, and BTV! break commands, with FLAGS = 0, 1, 5, 7, and 47Q respectively. If SYSPRETTYFLG = T, the values arguments and local variables will be prettyprinted. BAKTRACELST(BAKTRACELST (Variable) NIL NIL ("11") 9) [Variable] Used to tell BAKTRACE (therefore, the BT, BTV, etc. commands) to abbreviate various sequences of function calls on the stack by a single key, e.g. **BREAK**, **EDITOR**, etc. Each entry on BAKTRACELST is a list of the form (FRAMENAME KEY . PATTERN) or (FRAMENAME (KEY1 . PATTERN1) ... (KEYN . PATTERNN)), where a pattern is a list of elements that are either atoms, which match a single frame, or lists, which are interpreted as a list of alternative patterns, e.g. (PROGN **BREAK** EVAL ((ERRORSET BREAK1A BREAK1) (BREAK1))) BAKTRACE operates by scanning up the stack and, at each point, comparing the current frame name, with the frame names on BAKTRACELST, i.e. it does an ASSOC. If the frame name does appear, BAKTRACE attempts to match the stack as of that point with (one of) the patterns. If the match is successful, BAKTRACE prints the corresponding key, and continues with where the match left off. If the frame name does not appear, or the match fails, BAKTRACE simply prints the frame name and continues with the next higher frame (unless the SKIPFNS applied to the frame name are non-NIL as described above). Matching is performed by comparing symbols in the pattern with the current frame name, and matching lists as patterns, i.e. sequences of function calls, always working up the stack. For example, either of the sequence of function calls ... BREAK1 BREAK1A ERRORSET EVAL PROGN ... or ... BREAK1 EVAL PROGN ... would match with the sample entry given above, causing **BREAK** to be printed. Special features: f The symbol & can be used to match any frame. f The pattern - can be used to match nothing. - is useful for specifying an optional match, e.g. the example above could also have been written as (PROGN **BREAK** EVAL ((ERRORSET BREAK1A) -) BREAK1). f It is not necessary to provide in the pattern for matching dummy frames, i.e. frames for which DUMMYFRAMEP (see below) is true. When working on a match, the matcher automatically skips over these frames when they do not match. f If a match succeeds and the KEY is NIL, nothing is printed. For example, (*PROG*LAM NIL EVALA *ENV). This sequence will occur following an error which then causes a break if some of the function's arguments are LOCALVARS. Other Stack Functions (DUMMYFRAMEP(DUMMYFRAMEP (Function) NIL NIL ("11") 10) POS) [Function] Returns T if you never wrote a call to the function at POS, e.g. in Interlisp-10, DUMMYFRAMEP is T for *PROG*LAM, *ENV*, and FOOBLOCK frames (see the Block Compiling section of Chapter 18). REALFRAMEP and REALSTKNTH can be used to write functions which manipulate the stack and work on either interpreted or compiled code: (REALFRAMEP(REALFRAMEP (Function) NIL NIL ("11") 10) POS INTERPFLG) [Function] Returns POS if POS is a real frame, i.e. if POS is not a dummy frame and POS is a frame that does not disappear when compiled (such as COND); otherwise NIL. If INTERPFLG = T, returns T if POS is not a dummy frame. For example, if (STKNAME POS) = COND, (REALFRAMEP POS) is NIL, but (REALFRAMEP POS T) is T. (REALSTKNTH(REALFRAMEP (Function) NIL NIL ("11") 10) N POS INTERPFLG OLDPOS) [Function] Returns a stack pointer to the Nth (or -Nth) frames for which (REALFRAMEP POS INTERPFLG) is POS. (MAPDL(REALFRAMEP (Function) NIL NIL ("11") 10) MAPDLFN MAPDLPOS) [Function] Starts at MAPDLPOS and applies the function MAPDLFN to two arguments (the frame name and a stack pointer to the frame), for each frame until the top of the stack is reached. Returns NIL. For example, [MAPDL (FUNCTION (LAMBDA (X POS) (if (IGREATERP (STKNARGS POS) 2) then (PRINT X)] will print all functions of more than two arguments. (SEARCHPDL(SEARCHPDL (Function) NIL NIL ("11") 11) SRCHFN SRCHPOS) [Function] Like MAPDL, but searches the stack starting at position SRCHPOS until it finds a frame for which SRCHFN, a function of two arguments applied to the name of the frame and the frame itself, is not NIL. Returns (NAME . FRAME) if such a frame is found, otherwise NIL. The Stack and the Interpreter 1 In addition to the names and values of arguments for functions, information regarding partially-evaluated expressions is kept on the push-down list. For example, consider the following definition of the function FACT (intentionally faulty): (FACT [LAMBDA (N) (COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N]) In evaluating the form (FACT 1), as soon as FACT is entered, the interpreter begins evaluating the implicit PROGN following the LAMBDA. The first function entered in this process is COND. COND begins to process its list of clauses. After calling ZEROP and getting a NIL value, COND proceeds to the next clause and evaluates T. Since T is true, the evaluation of the implicit PROGN that is the consequent of the T clause is begun. This requires calling the function ITIMES. However before ITIMES can be called, its arguments must be evaluated. The first argument is evaluated by retrieving the current binding of N from its value cell; the second involves a recursive call to FACT, and another implicit PROGN, etc. At each stage of this process, some portion of an expression has been evaluated, and another is awaiting evaluation. The output below (from Interlisp-10) illustrates this by showing the state of the push-down list at the point in the computation of (FACT 1) when the unbound atom L is reached. FACT(1) u.b.a. L {in FACT} in ((ZEROP NO L) (L broken) :BTV! *TAIL* (L) *ARG1 (((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N))))) COND *FORM* (COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N))))) *TAIL* ((COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N)))))) N 0 FACT *FORM* (FACT (SUB1 N)) *FN* ITIMES *TAIL* ((FACT (SUB1 N))) *ARGVAL* 1 *FORM* (ITIMES N (FACT (SUB1 N))) *TAIL* ((ITIMES N (FACT (SUB1 N)))) *ARG1 (((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N))))) COND *FORM* (COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N))))) *TAIL* ((COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N)))))) N 1 FACT **TOP** Internal calls to EVAL, e.g., from COND and the interpreter, are marked on the push-down list by a special mark or blip which the backtrace prints as *FORM*. The genealogy of *FORM*'s is thus a history of the computation. Other temporary information stored on the stack by the interpreter includes the tail of a partially evaluated implicit PROGN (e.g., a cond clause or lambda expression) and the tail of a partially evaluated form (i.e., those arguments not yet evaluated), both indicated on the backtrace by *TAIL*, the values of arguments that have already been evaluated, indicated by *ARGVAL*, and the names of functions waiting to be called, indicated by *FN*. *ARG1, ..., *ARGn are used by the backtrace to indicate the (unnamed) arguments to SUBRs. Note that a function is not actually entered and does not appear on the stack, until its arguments have been evaluated (except for nlambda functions, of course). Also note that the *ARG1, *FORM*, *TAIL*, etc. bindings comprise the actual working storage. In other words, in the above example, if a (lower) function changed the value of the *ARG1 binding, the COND would continue interpreting the new binding as a list of COND clauses. Similarly, if the *ARGVAL* binding were changed, the new value would be given to ITIMES as its first argument after its second argument had been evaluated, and ITIMES was actually called. *FORM*, *TAIL*, *ARGVAL*, etc., do not actually appear as variables on the stack, i.e., evaluating *FORM* or calling STKSCAN to search for it will not work. However, the functions BLIPVAL, SETBLIPVAL, and BLIPSCAN described below are available for accessing these internal blips. These functions currently know about four different types of blips: *FN* The name of a function about to be called *ARGVAL* An argument for a function about to be called *FORM* A form in the process of evaluation *TAIL* The tail of a COND clause, implicit PROGN, PROG, etc. (BLIPVAL(BLIPVAL (Function) NIL NIL ("11") 12) BLIPTYP IPOS FLG) [Function] Returns the value of the specified blip of type BLIPTYP. If FLG is a number N, finds the Nth blip of the desired type, searching the control chain beginning at the frame specified by the stack descriptor IPOS. If FLG is NIL, 1 is used. If FLG is T, returns the number of blips of the specified type at IPOS. (SETBLIPVAL(SETBLIPVAL (Function) NIL NIL ("11") 12) BLIPTYP IPOS N VAL) [Function] Sets the value of the specified blip of type BLIPTYP. Searches for the Nth blip of the desired type, beginning with the frame specified by the stack descriptor IPOS, and following the control chain. (BLIPSCAN(BLIPSCAN (Function) NIL NIL ("11") 13) BLIPTYP IPOS) [Function] Returns a stack pointer to the frame in which a blip of type BLIPTYP is located. Search begins at the frame specified by the stack descriptor IPOS and follows the control chain. Generators 1 A generator is like a subroutine except that it retains information about previous times it has been called. Some of this state may be data (for example, the seed in a random number generator), and some may be in program state (as in a recursive generator which finds all the atoms in a list structure). For example, if LISTGEN is defined by: (DEFINEQ (LISTGEN (L) (if L then (PRODUCE (CAR L)) (LISTGEN (CDR L)))) we can use the function GENERATOR (described below) to create a generator that uses LISTGEN to produce the elements of a list one at a time, e.g., (SETQ GR (GENERATOR (LISTGEN '(A B C)))) creates a generator, which can be called by (GENERATE GR) to produce as values on successive calls, A, B, C. When GENERATE (not GENERATOR) is called the first time, it simply starts evaluating (LISTGEN '(A B C)). PRODUCE gets called from LISTGEN, and pops back up to GENERATE with the indicated value after saving the state. When GENERATE gets called again, it continues from where the last PRODUCE left off. This process continues until finally LISTGEN completes and returns a value (it doesn't matter what it is). GENERATE then returns GR itself as its value, so that the program that called GENERATE can tell that it is finished, i.e., there are no more values to be generated. (GENERATOR(GENERATOR (Function) NIL NIL ("11") 13) FORM COMVAR) [NLambda Function] An nlambda function that creates a generator which uses FORM to compute values. GENERATOR returns a generator handle which is represented by a dotted pair of stack pointers. COMVAR is optional. If its value (EVAL of) is a generator handle, the list structure and stack pointers will be reused. Otherwise, a new generator handle will be constructed. GENERATOR compiles open. (PRODUCE(PRODUCE (Function) NIL NIL ("11") 13) VAL) [Function] Used from within a generator to return VAL as the value of the corresponding call to GENERATE. (GENERATE(GENERATE (Function) NIL NIL ("11") 13) HANDLE VAL) [Function] Restarts the generator represented by HANDLE. VAL is returned as the value of the PRODUCE which last suspended the operation of the generator. When the generator runs out of values, GENERATE returns HANDLE itself. Examples: The following function will go down recursively through a list structure and produce the atoms in the list structure one at a time. (DEFINEQ (LEAVESG (L) (if (ATOM L) then (PRODUCE L) else (LEAVESG (CAR L)) (if (CDR L) then (LEAVESG (CDR L)] The following function prints each of these atoms as it appears. It illustrates how a loop can be set up to use a generator. (DEFINEQ (PLEAVESG1 (L) (PROG (X LHANDLE) (SETQ LHANDLE (GENERATOR (LEAVESG L))) LP (SETQ X (GENERATE LHANDLE)) (if (EQ X LHANDLE) then (RETURN NIL)) (PRINT X) (GO LP))] The loop terminates when the value of the generator is EQ to the dotted pair which is the value produced by the call to GENERATOR. A CLISP iterative operator, OUTOF, is provided which makes it much easier to write the loop in PLEAVESG1. OUTOF (or outof) can precede a form which is to be used as a generator. On each iteration, the iteration variable will be set to successive values returned by the generator; the loop will be terminated automatically when the generator runs out. Therefore, the following is equivalent to the above program PLEAVESG1: (DEFINEQ (PLEAVESG2 (L) (for X outof (LEAVESG L) do (PRINT X))] Here is another example; the following form will print the first N atoms. (for X outof (MAPATOMS (FUNCTION PRODUCE)) as I from 1 to N do (PRINT X)) Coroutines 1 This package provides facilities for the creation and use of fully general coroutine structures. It uses a stack pointer to preserve the state of a coroutine, and allows arbitrary switching between N different coroutines, rather than just a call to a generator and return. This package is slightly more efficient than the generator package described above, and allows more flexibility on specification of what to do when a coroutine terminates. (COROUTINE(COROUTINE (Function) NIL NIL ("11") 14) CALLPTR COROUTPTR COROUTFORM ENDFORM) [NLambda Function] This nlambda function is used to create a coroutine and initialize the linkage. CALLPTR and COROUTPTR are the names of two variables, which will be set to appropriate stack pointers. If the values of CALLPTR or COROUTPTR are already stack pointers, the stack pointers will be reused. COROUTFORM is the form which is evaluated to start the coroutine; ENDFORM is a form to be evaluated if COROUTFORM actually returns when it runs out of values. COROUTINE compiles open. (RESUME(RESUME (Function) NIL NIL ("11") 15) FROMPTR TOPTR VAL) [Function] Used to transfer control from one coroutine to another. FROMPTR should be the stack pointer for the current coroutine, which will be smashed to preserve the current state. TOPTR should be the stack pointer which has preserved the state of the coroutine to be transferred to, and VAL is the value that is to be returned to the latter coroutine as the value of the RESUME which suspended the operation of that coroutine. For example, the following is the way one might write the LEAVES program using the coroutine package: (DEFINEQ (LEAVESC (L COROUTPTR CALLPTR) (if (ATOM L) then (RESUME COROUTPTR CALLPTR L) else (LEAVESC (CAR L) COROUTPTR CALLPTR) (if (CDR L) then (LEAVESC (CDR L) COROUTPTR CALLPTR))))] A function PLEAVESC which uses LEAVESC can be defined as follows: (DEFINEQ (PLEAVESC (L) (bind PLHANDLE LHANDLE first (COROUTINE PLHANDLE LHANDLE (LEAVESC L LHANDLE PLHANDLE) (RETFROM 'PLEAVESC)) do (PRINT (RESUME PLHANDLE LHANDLE))))] By RESUMEing LEAVESC repeatedly, this function will print all the leaves of list L and then return out of PLEAVESC via the RETFROM. The RETFROM is necessary to break out of the non-terminating do-loop. This was done to illustrate the additional flexibility allowed through the use of ENDFORM. We use two coroutines working on two trees in the example EQLEAVES, defined below. EQLEAVES tests to see whether two trees have the same leaf set in the same order, e.g., (EQLEAVES '(A B C) '(A B (C))) is true. (DEFINEQ (EQLEAVES (L1 L2) (bind LHANDLE1 LHANDLE2 PE EL1 EL2 first (COROUTINE PE LHANDLE1 (LEAVESC L1 LHANDLE1 PE) 'NO-MORE) (COROUTINE PE LHANDLE2 (LEAVESC L2 LHANDLE2 PE) 'NO-MORE) do (SETQ EL1 (RESUME PE LHANDLE1)) (SETQ EL2 (RESUME PE LHANDLE2)) (if (NEQ EL1 EL2) then (RETURN NIL)) repeatuntil (EQ EL1 'NO-MORE) finally (RETURN T)))] Possibilities Lists 1 A possibilities list is the interface between a generator and a consumer. The possibilities list is initialized by a call to POSSIBILITIES, and elements are obtained from it by using TRYNEXT. By using the spaghetti stack to maintain separate environments, this package allows a regime in which a generator can put a few items in a possibilities list, suspend itself until they have been consumed, and be subsequently aroused and generate some more. (POSSIBILITIES FORM) [NLambda Function] This nlambda function is used for the initial creation of a possibilities list. FORM will be evaluated to create the list. It should use the functions NOTE and AU-REVOIR described below to generate possibilities. Normally, one would set some variable to the possibilities list which is returned, so it can be used later, e.g.: (SETQ PLIST (POSSIBILITIES (GENERFN V1 V2))). POSSIBILITIES compiles open. (NOTE(NOTE (Function) NIL NIL ("11") 16) VAL LSTFLG) [Function] Used within a generator to put items on the possibilities list being generated. If LSTFLG is equal to NIL, VAL is treated as a single item. If LSTFLG is non-NIL, then the list VAL is NCONCed on the end of the possibilities list. Note that it is perfectly reasonable to create a possibilities list using a second generator, and NOTE that list as possibilities for the current generator with LSTFLG equal to T. The lower generator will be resumed at the appropriate point. (AU-REVOIR(AU-REVOIR (Function) NIL NIL ("11") 16) VAL) [NoSpread Function] Puts VAL on the possibilities list if it is given, and then suspends the generator and returns to the consumer in such a fashion that control will return to the generator at the AU-REVOIR if the consumer exhausts the possibilities list. NIL is not put on the possibilities list unless it is explicitly given as an argument to AU-REVOIR, i.e., (AU-REVOIR) and (AU-REVOIR NIL) are not the same. AU-REVOIR and ADIEU are lambda nospreads to enable them to distinguish these two cases. (ADIEU(ADIEU (Function) NIL NIL ("11") 16) VAL) [NoSpread Function] Like AU-REVOIR but releases the generator instead of suspending it. (TRYNEXT(TRYNEXT (Function) NIL NIL ("11") 16) PLST ENDFORM VAL) [NLambda Function] This nlambda function allows a consumer to use a possibilities list. It removes the first item from the possibilities list named by PLST (i.e. PLST must be an atom whose value is a possiblities list), and returns that item, provided it is not a generator handle. If a generator handle is encountered, the generator is reawakened. When it returns a possibilities list, this list is added to the front of the current list. When a call to TRYNEXT causes a generator to be awakened, VAL is returned as the value of the AU-REVOIR which put that generator to sleep. If PLST is empty, it evaluates ENDFORM in the caller's environment. TRYNEXT compiles open. (CLEANPOSLST(CLEANPOSLST (Function) NIL NIL ("11") 16) PLST) [Function] This function is provided to release any stack pointers which may be left in the PLST which was not used to exhaustion. For example, FIB is a generator for fibonnaci numbers. It starts out by NOTEing its two arguments, then suspends itself. Thereafter, on being re-awakened, it will NOTE two more terms in the series and suspends again. PRINTFIB uses FIB to print the first N fibonacci numbers. (DEFINEQ (FIB (F1 F2) (do (NOTE F1) (NOTE F2) (SETQ F1 (IPLUS F1 F2)) (SETQ F2 (IPLUS F1 F2)) (AU-REVOIR)] Note that this AU-REVOIR just suspends the generator and adds nothing to the possibilities list except the generator. (DEFINEQ (PRINTFIB (N) (PROG ((FL (POSSIBILITIES (FIB 0 1)))) (RPTQ N (PRINT (TRYNEXT FL))) (CLEANPOSLST FL)] Note that FIB itself will never terminate. [This page intentionally left blank] (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "11-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "11-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "11-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "11-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (102 612 510 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "11-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "11-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))))),**,0052$$5~x~,<<,6,,002$$2HfH,~~,l~30BT0 +T0 +T1EVENT-T@ PAGEHEADING RIGHTPAGE/,/A PAGEHEADING RIGHTPAGET,ZZ/HH,<<,HH,@ PAGEHEADINGLEFTBACKT/TITAN +TIMESROMAN +PALATINO PALATINO PALATINO TITAN TITAN TITAN MODERN +CLASSIC +CLASSIC + HELVETICACLASSIC +MODERNMODERN +  IM.CHAP.GETFNMODERN +%  HRULE.GETFNMODERND5Z  HRULE.GETFNY!oy3F! h1k < &   HRULE.GETFNN l +)d +S  m%  HRULE.GETFN $IM.INDEX.GETFNCLASSIC + + U4+^7( '$IM.INDEX.GETFNCLASSIC + +  ?%/ 9&7' +E%IM.INDEX.GETFNCLASSIC + +F +(IM.INDEX.GETFNCLASSIC + + 1  +(IM.INDEX.GETFNCLASSIC + + ) ,%0 +%"  HRULE.GETFN k?]-  %IM.INDEX.GETFNCLASSIC + +  's; 'IM.INDEX.GETFNCLASSIC + + 0  $IM.INDEX.GETFNCLASSIC + +  .C" +(IM.INDEX.GETFNCLASSIC + + -D!  'IM.INDEX.GETFNCLASSIC + +  +C- +IM.INDEX.GETFNCLASSIC + +  C-6&IM.INDEX.GETFNCLASSIC + + h 'IM.INDEX.GETFNCLASSIC + +)%IM.INDEX.GETFNCLASSIC + + 7'  HRULE.GETFN W%IM.INDEX.GETFNCLASSIC + +     +![2"&IM.INDEX.GETFNCLASSIC + +     !&#IM.INDEX.GETFNCLASSIC + +   +W  %Q  %IM.INDEX.GETFNCLASSIC + +    +J &IM.INDEX.GETFNCLASSIC + +      HRULE.GETFN {D%IM.INDEX.GETFNCLASSIC + +  8         *  #IM.INDEX.GETFNCLASSIC + +  %IM.INDEX.GETFNCLASSIC + +    +J!  "&IM.INDEX.GETFNCLASSIC + +    %  HRULE.GETFN P$IM.INDEX.GETFNCLASSIC + +'$IM.INDEX.GETFNCLASSIC + +0%IM.INDEX.GETFNCLASSIC + + &IM.INDEX.GETFNCLASSIC + +D )IM.INDEX.GETFNCLASSIC + /_  +IM.INDEX.GETFNTITAN 0 ' +  1.3%C1<7\6  HRULE.GETFN  'IM.INDEX.GETFNCLASSIC + +    N>) \  *   3 V.0J &IM.INDEX.GETFNCLASSIC + +     R!'} H + B )IM.INDEX.GETFNCLASSIC + f  +   ;q "gS#J9  +! +!d4 +b z +$q   *IM.INDEX.GETFNCLASSIC + +. 9 + +l +)IM.INDEX.GETFNCLASSIC + + ;   +(    +)IM.INDEX.GETFNCLASSIC + +     )IM.INDEX.GETFNCLASSIC + +  + ! 15 (IM.INDEX.GETFNCLASSIC + + ."-+ %  HRULE.GETFN       <17+ )6w>  +$ 8>@%'8>@ oI@B:7IK 9 ++/%&IM.INDEX.GETFNCLASSIC + +  0  r7 +)IM.INDEX.GETFNCLASSIC + +   -X#'IM.INDEX.GETFNPALATINO =K  +  HRULE.GETFN 7 #% 38 ), * 8851@FO (IM.INDEX.GETFNCLASSIC + + 8  : &IM.INDEX.GETFNCLASSIC + +'+'IM.INDEX.GETFNCLASSIC + + &!^   +  ~ )"7? > $ @AJ +  HRULE.GETFN (IM.INDEX.GETFNCLASSIC + +  + Q d A +8 +. %IM.INDEX.GETFNCLASSIC + +  :nfQ2:&(&-B  %1)+V :P +'EE((#  HRULE.GETFN~ -  +QD  . #IM.INDEX.GETFNCLASSIC + + T "; +A (IM.INDEX.GETFNCLASSIC + + 2V    E$IM.INDEX.GETFNCLASSIC + + 6&IM.INDEX.GETFNCLASSIC + +  $$! ( *IM.INDEX.GETFNCLASSIC + +Q# 9X3 ^,* +%z \ No newline at end of file diff --git a/docs/turpin-irm/IRM-1/12-MISC.TEDIT b/docs/turpin-irm/IRM-1/12-MISC.TEDIT new file mode 100644 index 00000000..f632b3b2 Binary files /dev/null and b/docs/turpin-irm/IRM-1/12-MISC.TEDIT differ diff --git a/docs/turpin-irm/IRM-2/001-TITLEPAGE.PDF b/docs/turpin-irm/IRM-2/001-TITLEPAGE.PDF new file mode 100644 index 00000000..2fcad64e Binary files /dev/null and b/docs/turpin-irm/IRM-2/001-TITLEPAGE.PDF differ diff --git a/docs/turpin-irm/IRM-2/001-TITLEPAGE.TEDIT b/docs/turpin-irm/IRM-2/001-TITLEPAGE.TEDIT new file mode 100644 index 00000000..631ee7a9 Binary files /dev/null and b/docs/turpin-irm/IRM-2/001-TITLEPAGE.TEDIT differ diff --git a/docs/turpin-irm/IRM-2/003-TOC.PDF b/docs/turpin-irm/IRM-2/003-TOC.PDF new file mode 100644 index 00000000..c6cae0fa Binary files /dev/null and b/docs/turpin-irm/IRM-2/003-TOC.PDF differ diff --git a/docs/turpin-irm/IRM-2/003-TOC.TEdit b/docs/turpin-irm/IRM-2/003-TOC.TEdit new file mode 100644 index 00000000..505344e1 Binary files /dev/null and b/docs/turpin-irm/IRM-2/003-TOC.TEdit differ diff --git a/docs/turpin-irm/IRM-2/13-EXECUTIVE.TEDIT b/docs/turpin-irm/IRM-2/13-EXECUTIVE.TEDIT new file mode 100644 index 00000000..e7bc6f9f Binary files /dev/null and b/docs/turpin-irm/IRM-2/13-EXECUTIVE.TEDIT differ diff --git a/docs/turpin-irm/IRM-2/14-ERRORS.TEDIT b/docs/turpin-irm/IRM-2/14-ERRORS.TEDIT new file mode 100644 index 00000000..75bcdca5 Binary files /dev/null and b/docs/turpin-irm/IRM-2/14-ERRORS.TEDIT differ diff --git a/docs/turpin-irm/IRM-2/15-BREAKING.TEDIT b/docs/turpin-irm/IRM-2/15-BREAKING.TEDIT new file mode 100644 index 00000000..69d22950 Binary files /dev/null and b/docs/turpin-irm/IRM-2/15-BREAKING.TEDIT differ diff --git a/docs/turpin-irm/IRM-2/16-SEDIT.TEDIT b/docs/turpin-irm/IRM-2/16-SEDIT.TEDIT new file mode 100644 index 00000000..756a5f26 Binary files /dev/null and b/docs/turpin-irm/IRM-2/16-SEDIT.TEDIT differ diff --git a/docs/turpin-irm/IRM-2/17-FILEPACKAGE.TEDIT b/docs/turpin-irm/IRM-2/17-FILEPACKAGE.TEDIT new file mode 100644 index 00000000..29196c90 --- /dev/null +++ b/docs/turpin-irm/IRM-2/17-FILEPACKAGE.TEDIT @@ -0,0 +1,239 @@ +INTERLISP-D REFERENCE MANUAL FILE MANAGER "17"17. FILE MANAGER 2 Warning: The subsystem within Medley used for managing collections of definitions (of functions, variables, etc.) is known as the "File Manager." This terminology is confusing, because the word "file" is also used in the more conventional sense as meaning a collection of data stored on some physical media. Unfortunately, it is not possible to change this terminology at this time, because many functions and variables (MAKEFILE, FILEPKGTYPES, etc.) incorporate the word "file" in their names. Most implementations of Lisp treat symbolic files as unstructured text, much as they are treated in most conventional programming environments. Function definitions are edited with a character-oriented text editor, and then the changed definitions (or sometimes the entire file) is read or compiled to install those changes in the running memory image. Interlisp incorporates a different philosophy. A symbolic file is considered as a database of information about a group of data objects---function definitions, variable values, record declarations, etc. The text in a symbolic file is never edited directly. Definitions are edited only after their textual representations on files have been converted to data-structures that reside inside the Lisp address space. The programs for editing definitions inside Medley can therefore make use of the full set of data-manipulation capabilities that the environment already provides, and editing operations can be easily intermixed with the processes of evaluation and compilation. Medley is thus a "resident" programming environment, and as such it provides facilities for moving definitions back and forth between memory and the external databases on symbolic files, and for doing the bookkeeping involved when definitions on many symbolic files with compiled counterparts are being manipulated. The file manager provides those capabilities. It shoulders the burden of keeping track of where things are and what things have changed so that you don't have to. The file manager also keeps track of which files have been modified and need to be updated and recompiled. The file manager is integrated into many other system packages. For example, if only the compiled version of a file is loaded and you attempt to edit a function, the file manager will attempt to load the source of that function from the appropriate symbolic file. In many cases, if a datum is needed by some program, the file manager will automatically retrieve it from a file if it is not already in your working environment. Some of the operations of the file manager are rather complex. For example, the same function may appear in several different files, or the symbolic or compiled files may be in different directories, etc. Therefore, this chapter does not document how the file manager works in each and every situation, but instead makes the deliberately vague statement that it does the "right" thing with respect to keeping track of what has been changed, and what file operations need to be performed in accordance with those changes. For a simple illustration of what the file manager does, suppose that the symbolic file FOO contains the functions FOO1 and FOO2, and that the file BAR contains the functions BAR1 and BAR2. These two files could be loaded into the environment with the function LOAD: (LOAD 'FOO) FILE CREATED 4-MAR-83 09:26:55 FOOCOMS {DSK}FOO.;1 (LOAD 'BAR) FILE CREATED 4-MAR-83 09:27:24 BARCOMS {DSK}BAR.;1 Now, suppose that we change the definition of FOO2 with the editor, and we define two new functions, NEW1 and NEW2. At that point, the file manager knows that the in-memory definition of FOO2 is no longer consistent with the definition in the file FOO, and that the new functions have been defined but have not yet been associated with a symbolic file and saved on permanent storage. The function FILES? summarizes this state of affairs and enters into an interactive dialog in which we can specify what files the new functions are to belong to. (FILES?) FOO...to be dumped. plus the functions: NEW1,NEW2 want to say where the above go ? Yes (functions) NEW1 File name: BAR NEW2 File name: ZAP new file ? Yes NIL The file manager knows that the file FOO has been changed, and needs to be dumped back to permanent storage. This can be done with MAKEFILE. (MAKEFILE 'FOO) {DSK}FOO.;2 Since we added NEW1 to the old file BAR and established a new file ZAP to contain NEW2, both BAR and ZAP now also need to be dumped. This is confirmed by a second call to FILES?: (FILES?) BAR, ZAP...to be dumped. FOO...to be listed. FOO...to be compiled NIL We are also informed that the new version we made of FOO needs to be listed (sent to a printer) and that the functions on the file must be compiled. Rather than doing several MAKEFILEs to dump the files BAR and ZAP, we can simply call CLEANUP. Without any further user interaction, this will dump any files whose definitions have been modified. CLEANUP will also send any unlisted files to the printer and recompile any files which need to be recompiled. CLEANUP is a useful function to use at the end of a debugging session. It will call FILES? if any new objects have been defined, so you do not lose the opportunity to say explicitly where those belong. In effect, the function CLEANUP executes all the operations necessary to make the your permanent files consistent with the definitions in the current core-image. (CLEANUP) FOO...compiling {DSK}FOO.;2 . . . BAR...compiling {DSK}BAR.;2 . . . ZAP...compiling {DSK}ZAP.;1 . . . In addition to the definitions of functions, symbolic files in Interlisp can contain definitions of a variety of other types, e.g. variable values, property lists, record declarations, macro definitions, hash arrays, etc. In order to treat such a diverse assortment of data uniformly from the standpoint of file operations, the file manager uses the concept of a typed definition, of which a function definition is just one example. A typed definition associates with a name (usually a symbol), a definition of a given type (called the file manager type). Note that the same name may have several definitions of different types. For example, a symbol may have both a function definition and a variable definition. The file manager also keeps track of the files that a particular typed definition is stored on, so one can think of a typed definition as a relation between four elements: a name, a definition, a type, and a file. Symbolic files on permanent storage devices are referred to by names that obey the naming conventions of those devices, usually including host, directory, and version fields. When such definition groups are noticed by the file manager, they are assigned simple root names and these are used by all file manager operations to refer to those groups of definitions. The root name for a group is computed from its full permanent storage name by applying the function ROOTFILENAME; this strips off the host, directory, version, etc., and returns just the simple name field of the file. For each file, the file manager also has a data structure that describes what definitions it contains. This is known as the commands of the file, or its "filecoms". By convention, the filecoms of a file whose root name is X is stored as the value of the symbol XCOMS. For example, the value of FOOCOMS is the filecoms for the file FOO. This variable can be directly manipulated, but the file manager contains facilities such as FILES? which make constructing and updating filecoms easier, and in some cases automatic. See the Functions for Manipulating File Command Lists section. The file manager is able to maintain its databases of information because it is notified by various other routines in the system when events take place that may change that database. A file is "noticed" when it is loaded, or when a new file is stored (though there are ways to explicitly notice files without completely loading all their definitions). Once a file is noticed, the file manager takes it into account when modifying filecoms, dumping files, etc. The file manager also needs to know what typed definitions have been changed or what new definitions have been introduced, so it can determine which files need to be updated. This is done by "marking changes". All the system functions that perform file manager operations (LOAD, TCOMPL, PRETTYDEF, etc.), as well as those functions that define or change data, (EDITF, EDITV, EDITP, DWIM corrections to user functions) interact with the file manager. Also, typed-in assignment of variables or property values is noticed by the file manager. (Note that modifications to variable or property values during the execution of a function body are not noticed.) In some cases the marking procedure can be subtle, e.g. if you edit a property list using EDITP, only those properties whose values are actually changed (or added) are marked. All file manager operations can be disabled with FILEPKGFLG. FILEPKGFLG(FILEPKGFLG (Variable) NIL NIL ("17") 4) [Variable] The file manager can be disabled by setting FILEPKGFLG to NIL. This will turn off noticing files and marking changes. FILEPKGFLG is initially T. The rest of this chapter goes into further detail about the file manager. Functions for loading and storing symbolic files are presented first, followed by functions for adding and removing typed definitions from files, moving typed definitions from one file to another, determining which file a particular definition is stored in, and so on. Loading Files 1 The functions below load information from symbolic files into the Interlisp environment. A symbolic file contains a sequence of Interlisp expressions that can be evaluated to establish specified typed definitions. The expressions on symbolic files are read using FILERDTBL as the read table. The loading functions all have an argument LDFLG. LDFLG affects the operation of DEFINE, DEFINEQ, RPAQ, RPAQ?, and RPAQQ. While a source file is being loaded, DFNFLG (Chapter 10) is rebound to LDFLG. Thus, if LDFLG = NIL, and a function is redefined, a message is printed and the old definition saved. If LDFLG = T , the old definition is simply overwritten. If LDFLG = PROP, the functions are stored as "saved" definitions on the property lists under the property EXPR instead of being installed as the active definitions. If LDFLG = ALLPROP, not only function definitions but also variables set by RPAQQ, RPAQ, RPAQ? are stored on property lists (except when the variable has the value NOBIND, in which case they are set to the indicated value regardless of DFNFLG). Another option is available for loading systems for others to use and who wish to suppress the saving of information used to aid in development and debugging. If LDFLG = SYSLOAD, LOAD will: 1. Rebind DFNFLG to T, so old definitions are simply overwritten 2. Rebind LISPXHIST to NIL, thereby making the LOAD not be undoable and eliminating the cost of saving undo information (Chapter 13) 3. Rebind ADDSPELLFLG to NIL, to suppress adding to spelling lists 4. Rebind FILEPKGFLG to NIL, to prevent the file from being "noticed" by the file manager 5. Rebind BUILDMAPFLG to NIL, to prevent a file map from being constructed 6. After the load has completed, set the filecoms variable and any filevars variables to NOBIND 7. Add the file name to SYSFILES rather than FILELST A filevars variable is any variable appearing in a file manager command of the form (FILECOM * VARIABLE) (see the FileVars section). Therefore, if the filecoms includes (FNS * FOOFNS), FOOFNS is set to NOBIND. If you want the value of such a variable to be retained, even when the file is loaded with LDFLG = SYSLOAD, then you should replace the variable with an equivalent, non-atomic expression, such as (FNS * (PROGN FOOFNS)). All functions that have LDFLG as an argument perform spelling correction using LOADOPTIONS as a spelling list when LDFLG is not a member of LOADOPTIONS. LOADOPTIONS is initially (NIL T PROP ALLPROP SYSLOAD). (LOAD(LOAD (Function) NIL NIL ("17") 5) FILE LDFLG PRINTFLG) [Function] Reads successive expressions from FILE (with FILERDTBL as read table) and evaluates each as it is read, until it reads either NIL, or the single atom STOP. Note that LOAD can be used to load both symbolic and compiled files. Returns FILE (full name). If PRINTFLG = T, LOAD prints the value of each expression; otherwise it does not. (LOAD?(LOAD? (Function) NIL NIL ("17") 5) FILE LDFLG PRINTFLG) [Function] Similar to LOAD except that it does not load FILE if it has already been loaded, in which case it returns NIL. LOAD? loads FILE except when the same version of the file has been loaded (either from the same place, or from a copy of it from a different place). Specifically, LOAD? considers that FILE has already been loaded if the full name of FILE is on LOADEDFILELST (see the Noticing Files section) or the date stored on the FILEDATES property of the root file name of FILE is the same as the FILECREATED expression on FILE. (LOADFNS(LOADFNS (Function) NIL NIL ("17") 5) FNS FILE LDFLG VARS) [Function] Permits selective loading of definitions. FNS is a list of function names, a single function name, or T, meaning to load all of the functions on the file. FILE can be either a compiled or symbolic file. If a compiled definition is loaded, so are all compiler-generated subfunctions. The interpretation of LDFLG is the same as for LOAD. If FILE = NIL, LOADFNS will use WHEREIS (see the Storing Files section) to determine where the first function in FNS resides, and load from that file. Note that the file must previously have been "noticed". If WHEREIS returns NIL, and the WHEREIS library package has been loaded, LOADFNS will use the WHEREIS data base to find the file containing FN. VARS specifies which non-DEFINEQ expressions are to be loaded (i.e., evaluated). It is interpreted as follows: T Means to load all non-DEFINEQ expressions. NIL Means to load none of the non-DEFINEQ expressions. VARS Means to evaluate all variable assignment expressions (beginning with RPAQ, RPAQQ, or RPAQ?, see the Functions Used Within Source Files section). Any other symbol Means the same as specifying a list containing that atom. A list If VARS is a list that is not a valid function definition, each element in VARS is "matched" against each non-DEFINEQ expression, and if any elements in VARS "match" successfully, the expression is evaluated. "Matching" is defined as follows: If an element of VARS is an atom, it matches an expression if it is EQ to either the CAR or the CADR of the expression. If an element of VARS is a list, it is treated as an edit pattern (see Chapter 16), and matched with the entire expression (using EDIT4E, described in Chapter 16). For example, if VARS was (FOOCOMS DECLARE: (DEFLIST & (QUOTE MACRO))), this would cause (RPAQQ FOOCOMS ...), all DECLARE:s, and all DEFLISTs which set up MACROs to be read and evaluated. A function definition If VARS is a list and a valid function definition ((FNTYP VARS) is true), then LOADFNS will invoke that function on every non-DEFINEQ expression being considered, applying it to two arguments, the first and second elements in the expression. If the function returns NIL, the expression will be skipped; if it returns a non-NIL symbol (e.g., T), the expression will be evaluated; and if it returns a list, this list is evaluated instead of the expression. The file pointer is set to the very beginning of the expression before calling the VARS function definition, so it may read the entire expression if necessary. If the function returns a symbol, the file pointer is reset and the expression is READ or SKREAD. However, the file pointer is not reset when the function returns a list, so the function must leave it set immediately after the expression that it has presumably read. LOADFNS returns a list of: 1. The names of the functions that were found 2. A list of those functions not found (if any) headed by the symbol NOT-FOUND: 3. All of the expressions that were evaluated 4. A list of those members of VARS for which no corresponding expressions were found (if any), again headed by the symbol NOT-FOUND: For example: (LOADFNS '(FOO FIE FUM) FILE NIL '(BAZ (DEFLIST &))) (FOO FIE (NOT-FOUND: FUM) (RPAQ BAZ ...) (NOT-FOUND: (DEFLIST &))) (LOADVARS(LOADVARS (Function) NIL NIL ("17") 6) VARS FILE LDFLG) [Function] Same as (LOADFNS NIL FILE LDFLG VARS). (LOADFROM(LOADFROM (Function) NIL NIL ("17") 7) FILE FNS LDFLG) [Function] Same as (LOADFNS FNS FILE LDFLG T). Once the file manager has noticed a file, you can edit functions contained in the file without explicitly loading them. Similarly, those functions which have not been modified do not have to be loaded in order to write out an updated version of the file. Files are normally noticed (i.e., their contents become known to the file manager) when either the symbolic or compiled versions of the file are loaded. If the file is not going to be loaded completely, the preferred way to notice it is with LOADFROM. You can also load some functions at the same time by giving LOADFROM a second argument, but it is normally used simply to inform the file manager about the existence and contents of a particular file. (LOADBLOCK(LOADBLOCK (Function) NIL NIL ("17") 7) FN FILE LDFLG) [Function] Calls LOADFNS on those functions contained in the block declaration containing FN (see Chapter 18). LOADBLOCK is designed primarily for use with symbolic files, to load the EXPRs for a given block. It will not load a function which already has an in-core EXPR definition, and it will not load the block name, unless it is also one of the block functions. (LOADCOMP(LOADCOMP (Function) NIL NIL ("17") 7) FILE LDFLG) [Function] Performs all operations on FILE associated with compilation, i.e. evaluates all expressions under a DECLARE: EVAL@COMPILE, and "notices" the function and variable names by adding them to the lists NOFIXFNSLST and NOFIXVARSLST (see Chapter 21). Thus, if building a system composed of many files with compilation information scattered among them, all that is required to compile one file is to LOADCOMP the others. (LOADCOMP?(LOADCOMP? (Function) NIL NIL ("17") 7) FILE LDFLG) [Function] Similar to LOADCOMP, except it does not load if file has already been loaded (with LOADCOMP), in which case its value is NIL. LOADCOMP? will load the file even if it has been loaded with LOAD, LOADFNS, etc. The only time it will not load the file is if the file has already been loaded with LOADCOMP. FILESLOAD provides an easy way for you to load a series of files, setting various options: (FILESLOAD(FILESLOAD (Function) NIL NIL ("17") 7) FILE1 ... FILEN) [NLambda NoSpread Function] Loads the files FILE1 ... FILEN (all arguments unevaluated). If any of these arguments are lists, they specify certain loading options for all following files (unless changed by another list). Within these lists, the following commands are recognized: FROM DIR Search the specified directories for the file. DIR can either be a single directory, or a list of directories to search in order. For example, (FILESLOAD (FROM {ERIS}SOURCES>) ...) will search the directory {ERIS}SOURCES> for the files. If this is not specified, the default is to search the contents of DIRECTORIES (see Chapter 24). If FROM is followed by the key word VALUEOF, the following word is evaluated, and the value is used as the list of directories to search. For example, (FILESLOAD (FROM VALUEOF FOO) ...) will search the directory list that is the value of the variable FOO. As a special case, if DIR is a symbol, and the symbol DIRDIRECTORIES is bound, the value of this variable is used as the directory search list. For example, since the variable LISPUSERSDIRECTORIES (see Chapter 24) is commonly used to contain a list of directories containing "library" packages, (FILESLOAD (FROM LISPUSERS) ...) can be used instead of (FILESLOAD (FROM VALUEOF LISPUSERSDIRECTORIES) ...) If a FILESLOAD is read and evaluated while loading a file, and it doesn't contain a FROM expression, the default is to search the directory containing the FILESLOAD expression before the value of DIRECTORIES. FILESLOAD expressions can be dumped on files using the FILES file manager command. SOURCE Load the source version of the file rather than the compiled version. COMPILED Load the compiled version of the file. If COMPILED is specified, the compiled version will be loaded, if it is found. The source will not be loaded. If neither SOURCE or COMPILED is specified, the compiled version of the file will be loaded if it is found, otherwise the source will be loaded if it is found. LOAD Load the file by calling LOAD, if it has not already been loaded. This is the default unless LOADCOMP or LOADFROM is specified. If LOAD is specified, FILESLOAD considers that the file has already been loaded if the root name of the file has a non-NIL FILEDATES property. This is a somewhat different algorithm than LOAD? uses. In particular, FILESLOAD will not load a newer version of a file that has already been loaded. LOADCOMP Load the file with LOADCOMP? rather than LOAD. Automatically implies SOURCE. LOADFROM Load the file with LOADFROM rather than LOAD. NIL, T, PROP ALLPROP SYSLOAD The loading function is called with its LDFLG argument set to the specified token. LDFLG affects the operation of the loading functions by resetting DFNFLG (see Chapter 10) to LDFLG during the loading. If none of these tokens are specified, the value of the variable LDFLG is used if it is bound, otherwise NIL is used. NOERROR If NOERROR is specified, no error occurs when a file is not found. Each list determines how all further files in the lists are loaded, unless changed by another list. The tokens above can be joined together in a single list. For example, (FILESLOAD (LOADCOMP) NET (SYSLOAD FROM VALUEOF NEWDIRECTORIES) CJSYS) will call LOADCOMP? to load the file NET searching the value of DIRECTORIES, and then call LOADCOMP? to load the file CJSYS with LDFLG set to SYSLOAD, searching the directory list that is the value of the variable NEWDIRECTORIES. FILESLOAD expressions can be dumped on files using the FILES file manager command. Storing Files 1 (MAKEFILE(MAKEFILE (Function) NIL NIL ("17") 9) FILE OPTIONS REPRINTFNS SOURCEFILE) [Function] Makes a new version of the file FILE, storing the information specified by FILE's filecoms. Notices FILE if not previously noticed. Then, it adds FILE to NOTLISTEDFILES and NOTCOMPILEDFILES. OPTIONS is a symbol or list of symbols which specify options. By specifying certain options, MAKEFILE can automatically compile or list FILE. Note that if FILE does not contain any function definitions, it is not compiled even when OPTIONS specifies C or RC. The options are spelling corrected using the list MAKEFILEOPTIONS. If spelling correction fails, MAKEFILE generates an error. The options are interpreted as follows: C RC After making FILE, MAKEFILE will compile FILE by calling TCOMPL (if C is specified) or RECOMPILE (if RC is specified). If there are any block declarations specified in the filecoms for FILE, BCOMPL or BRECOMPILE will be called instead. If F, ST, STF, or S is the next item on OPTIONS following C or RC, it is given to the compiler as the answer to the compiler's question LISTING? (see Chapter 18). For example, (MAKEFILE 'FOO '(C F LIST)) will dump FOO, then TCOMPL or BCOMPL it specifying that functions are not to be redefined, and finally list the file. LIST After making FILE, MAKEFILE calls LISTFILES to print a hardcopy listing of FILE. CLISPIFY MAKEFILE calls PRETTYDEF with CLISPIFYPRETTYFLG = T (see Chapter 21). This causes CLISPIFY to be called on each function defined as an EXPR before it is prettyprinted. Alternatively, if FILE has the property FILETYPE with value CLISP or a list containing CLISP, PRETTYDEF is called with CLISPIFYPRETTYFLG reset to CHANGES, which will cause CLISPIFY to be called on all functions marked as having been changed. If FILE has property FILETYPE with value CLISP, the compiler will DWIMIFY its functions before compiling them (see Chapter 18). FAST MAKEFILE calls PRETTYDEF with PRETTYFLG = NIL (see Chapter 26). This causes data objects to be printed rather than prettyprinted, which is much faster. REMAKE MAKEFILE "remakes" FILE: The prettyprinted definitions of functions that have not changed are copied from an earlier version of the symbolic file. Only those functions that have changed are prettyprinted. NEW MAKEFILE does not remake FILE. If MAKEFILEREMAKEFLG = T (the initial setting), the default for all calls to MAKEFILE is to remake. The NEW option can be used to override this default. REPRINTFNS and SOURCEFILE are used when remaking a file. FILE is not added to NOTLISTEDFILES if FILE has on its property list the property FILETYPE with value DON'TLIST, or a list containing DON'TLIST. FILE is not added to NOTCOMPILEDFILES if FILE has on its property list the property FILETYPE with value DON'TCOMPILE, or a list containing DON'TCOMPILE. Also, if FILE does not contain any function definitions, it is not added to NOTCOMPILEDFILES, and it is not compiled even when OPTIONS specifies C or RC. If a remake is not being performed, MAKEFILE checks the state of FILE to make sure that the entire source file was actually LOADed. If FILE was loaded as a compiled file, MAKEFILE prints the message CAN'T DUMP: ONLY THE COMPILED FILE HAS BEEN LOADED. Similarly, if only some of the symbolic definitions were loaded via LOADFNS or LOADFROM, MAKEFILE prints CAN'T DUMP: ONLY SOME OF ITS SYMBOLICS HAVE BEEN LOADED. In both cases, MAKEFILE will then ask you if it should dump anyway; if you decline, MAKEFILE does not call PRETTYDEF, but simply returns (FILE NOT DUMPED) as its value. You can indicate that FILE must be block compiled together with other files as a unit by putting a list of those files on the property list of each file under the property FILEGROUP. If FILE has a FILEGROUP property, the compiler will not be called until all files on this property have been dumped that need to be. MAKEFILE operates by rebinding PRETTYFLG, PRETTYTRANFLG, and CLISPIFYPRETTYFLG, evaluating each expression on MAKEFILEFORMS (under errorset protection), and then calling PRETTYDEF. PRETTYDEF calls PRETTYPRINT with its second argument PRETTYDEFLG = T, so whenever PRETTYPRINT (and hence MAKEFILE) start printing a new function, the name of that function is printed if more than 30 seconds (real time) have elapsed since the last time it printed the name of a function. (MAKEFILES(MAKEFILES (Function) NIL NIL ("17") 11) OPTIONS FILES) [Function] Performs (MAKEFILE FILE OPTIONS) for each file on FILES that needs to be dumped. If FILES = NIL, FILELST is used. For example, (MAKEFILES 'LIST) will make and list all files that have been changed. In this case, if any typed definitions for any items have been defined or changed and they are not contained in one of the files on FILELST, MAKEFILES calls ADDTOFILES? to allow you to specify where these go. MAKEFILES returns a list of all files that are made. (CLEANUP(CLEANUP (Function) NIL NIL ("17") 11) FILE1 FILE2 ... FILEN) [NLambda NoSpread Function] Dumps, lists, and recompiles (with RECOMPILE or BRECOMPILE) any of the specified files (unevaluated) requiring the corresponding operation. If no files are specified, FILELST is used. CLEANUP returns NIL. CLEANUP uses the value of the variable CLEANUPOPTIONS as the OPTIONS argument to MAKEFILE. CLEANUPOPTIONS is initially (RC), to indicate that the files should be recompiled. If CLEANUPOPTIONS is set to (RC F), no listing will be performed, and no functions will be redefined as the result of compiling. Alternatively, if FILE1 is a list, it will be interpreted as the list of options regardless of the value of CLEANUPOPTIONS. (FILES?(FILES? (Function) NIL NIL ("17") 11)) [Function] Prints on the terminal the names of those files that have been modified but not dumped, dumped but not listed, dumped but not compiled, plus the names of any functions and other typed definitions (if any) that are not contained in any file. If there are any, FILES? then calls ADDTOFILES? to allow you to specify where these go. (ADDTOFILES?(ADDTOFILES? (Function) NIL NIL ("17") 12) %) [Function] Called from MAKEFILES, CLEANUP, and FILES? when there are typed definitions that have been marked as changed which do not belong to any file. ADDTOFILES? lists the names of the changed items, and asks if you want to specify where these items should be put. If you answer N(o), ADDTOFILES? returns NIL without taking any action. If you answer ], this is taken to be an answer to each question that would be asked, and all the changed items are marked as dummy items to be ignored. Otherwise, ADDTOFILES? prints the name of each changed item, and accepts one of the following responses: A file name A filevar If you give a file name or a variable whose value is a list (a filevar), the item is added to the corresponding file or list, using ADDTOFILE. If your response is not the name of a file on FILELST or a variable whose value is a list, you will be asked whether it is a new file. If you say no, then ADDTOFILES? will check whether the item is the name of a list, i.e., whether its value is a list. If not, youwill be asked whether it is a new list. line-feed Same as your previous response. space carriage return Take no action. ] The item is marked as a dummy item by adding it to NILCOMS. This tells the file manager simply to ignore this item. [ The "definition" of the item in question is prettyprinted to the terminal, and then you are asked again about its disposition. ( ADDTOFILES? prompts with "LISTNAME: (", you type in the name of a list, i.e. a variable whose value is a list, terminated by a ). The item will then only be added to (under) a command in which the named list appears as a filevar. If none are found, a message is printed, and you are asked again. For example, you define a new function FOO3. When asked where it goes, you type (FOOFNS). If the command (FNS * FOOFNS) is found, FOO3 will be added to the value of FOOFNS. If instead you type (FOOCOMS), and the command (COMS * FOOCOMS) is found, then FOO3 will be added to a command for dumping functions that is contained in FOOCOMS. If the named list is not also the name of a file, you can simply type it in without parenthesis as described above. @ ADDTOFILES? prompts with "Near: (", you type in the name of an object, and the item is then inserted in a command for dumping objects (of its type) that contains the indicated name. The item is inserted immediately after the indicated name. (LISTFILES FILE1 FILE2 ... FILEN) [NLambda NoSpread Function] Lists each of the specified files (unevaluated). If no files are given, NOTLISTEDFILES is used. Each file listed is removed from NOTLISTEDFILES if the listing is completed. For each file not found, LISTFILES prints the message FILENAME NOT FOUND and proceeds to the next file. LISTFILES calls the function LISTFILES1 on each file to be listed. Normally, LISTFILES1 is defined to simply call SEND.FILE.TO.PRINTER (see Chapter 29), but you can advise or redefine LISTFILES1 for more specialized applications. Any lists inside the argument list to LISTFILES are interpreted as property lists that set the various printing options, such as the printer, number of copies, banner page name, etc (see see Chapter 29). Later properties override earlier ones. For example, (LISTFILES FOO (HOST JEDI) FUM (#COPIES 3) FIE) will cause one copy of FOO to be printed on the default printer, and one copy of FUM and three copies of FIE to be printed on the printer JEDI. (COMPILEFILES(COMPILEFILES (Function) NIL NIL ("17") 13) FILE1 FILE2 ... FILEN) [NLambda NoSpread Function] Executes the RC and C options of MAKEFILE for each of the specified files (unevaluated). If no files are given, NOTCOMPILEDFILES is used. Each file compiled is removed from NOTCOMPILEDFILES. If FILE1 is a list, it is interpreted as the OPTIONS argument to MAKEFILES. This feature can be used to supply an answer to the compiler's LISTING? question, e.g., (COMPILEFILES (STF)) will compile each file on NOTCOMPILEDFILES so that the functions are redefined without the EXPRs definitions being saved. (WHEREIS(WHEREIS (Function) NIL NIL ("17") 13) NAME TYPE FILES FN) [Function] TYPE is a file manager type. WHEREIS sweeps through all the files on the list FILES and returns a list of all files containing NAME as a TYPE. WHEREIS knows about and expands all file manager commands and file manager macros. TYPE = NIL defaults to FNS (to retrieve function definitions). If FILES is not a list, the value of FILELST is used. If FN is given, it should be a function (with arguments NAME, FILE, and TYPE) which is applied for every file in FILES that contains NAME as a TYPE. In this case, WHEREIS returns NIL. If the WHEREIS library package has been loaded, WHEREIS is redefined so that FILES = T means to use the whereis package data base, so WHEREIS will find NAME even if the file has not been loaded or noticed. FILES = NIL always means use FILELST. Remaking a Symbolic File 1 Most of the time that a symbolic file is written using MAKEFILE, only a few of the functions that it contains have been changed since the last time the file was written. Rather than prettprinting all of the functions, it is often considerably faster to "remake" the file, copying the prettprinted definitions of unchanged functions from an earlier version of the symbolic file, and only prettyprinting those functions that have been changed. MAKEFILE will remake the symbolic file if the REMAKE option is specified. If the NEW option is given, the file is not remade, and all of the functions are prettprinted. The default action is specified by the value of MAKEFILEREMAKEFLG: if T (its initial value), MAKEFILE will remake files unless the NEW option is given; if NIL, MAKEFILE will not remake unless the REMAKE option is given. Note: If the file has never been loaded or dumped, for example if the filecoms were simply set up in memory, then MAKEFILE will never attempt to remake the file, regardless of the setting of MAKEFILEREMAKEFLG, or whether the REMAKE option was specified. When MAKEFILE is remaking a symbolic file, you can explicitly indicate the functions which are to be prettyprinted and the file to be used for copying the rest of the function definitions from via the REPRINTFNS and SOURCEFILE arguments to MAKEFILE. Normally, both of these arguments are defaulted to NIL. In this case, REPRINTFNS will be set to those functions that have been changed since the last version of the file was written. For SOURCEFILE, MAKEFILE obtains the full name of the most recent version of the file (that it knows about) from the FILEDATES property of the file, and checks to make sure that the file still exists and has the same file date as that stored on the FILEDATES property. If it does, MAKEFILE uses that file as SOURCEFILE. This procedure permits you to LOAD or LOADFROM a file in a different directory, and still be able to remake the file with MAKEFILE. In the case where the most recent version of the file cannot be found, MAKEFILE will attempt to remake using the original version of the file (i.e., the one first loaded), specifying as REPRINTFNS the union of all changes that have been made since the file was first loaded, which is obtained from the FILECHANGES property of the file. If both of these fail, MAKEFILE prints the message "CAN'T FIND EITHER THE PREVIOUS VERSION OR THE ORIGINAL VERSION OF FILE, SO IT WILL HAVE TO BE WRITTEN ANEW", and does not remake the file, i.e. will prettyprint all of the functions. When a remake is specified, MAKEFILE also checks to see how the file was originally loaded. If the file was originally loaded as a compiled file, MAKEFILE will call LOADVARS to obtain those DECLARE: expressions that are contained on the symbolic file, but not the compiled file, and hence have not been loaded. If the file was loaded by LOADFNS (but not LOADFROM), then LOADVARS is called to obtain any non-DEFINEQ expressions. Before calling LOADVARS to re-load definitions, MAKEFILE asks you, e.g. "Only the compiled version of FOO was loaded, do you want to LOADVARS the (DECLARE: .. DONTCOPY ..) expressions from {DSK}FOO.;3?". You can respond Yes to execute the LOADVARS and continue the MAKEFILE, No to proceed with the MAKEFILE without performing the LOADVARS, or Abort to abort the MAKEFILE. You may wish to skip the LOADVARS if you had circumvented the file manager in some way, and loading the old definitions would overwrite new ones. Remaking a symbolic file is considerably faster if the earlier version has a file map indicating where the function definitions are located (see the File Maps section), but it does not depend on this information. Loading Files in a Distributed Environment 1 Each Interlisp source and compiled code file contains the full filename of the file, including the host and directory names, in a FILECREATED expression at the beginning of the file. The compiled code file also contains the full file name of the source file it was created from. In earlier versions of Interlisp, the file manager used this information to locate the appropriate source file when "remaking" or recompiling a file. This turned out to be a bad feature in distributed environments, where users frequently move files from one place to another, or where files are stored on removable media. For example, suppose you MAKEFILE to a floppy, and then copy the file to a file server. If you loaded and edited the file from a file server, and tried to do MAKEFILE, it would try to locate the source file on the floppy, which is probably no longer loaded. Currently, the file manager searches for sources file on the connected directory, and on the directory search path (on the variable DIRECTORIES). If it is not found, the host/directory information from the FILECREATED expression be used. Warning: One situation where the new algorithm does the wrong thing is if you explicitly LOADFROM a file that is not on your directory search path. Future MAKEFILEs and CLEANUPs will search the connected directory and DIRECTORIES to find the source file, rather than using the file that the LOADFROM was done from. Even if the correct file is on the directory search path, you could still create a bad file if there is another version of the file in an earlier directory on the search path. In general, you should either explicitly specify the SOURCEFILE argument to MAKEFILE to tell it where to get the old source, or connect to the directory where the correct source file is. Marking Changes 1 The file manager needs to know what typed definitions have been changed, so it can determine which files need to be updated. This is done by "marking changes". All the system functions that perform file manager operations (LOAD, TCOMPL, PRETTYDEF, etc.), as well as those functions that define or change data, (EDITF, EDITV, EDITP, DWIM corrections to user functions) interact with the file manager by marking changes. Also, typed-in assignment of variables or property values is noticed by the file manager. (If a program modifies a variable or property value, this is not noticed.) In some cases the marking procedure can be subtle, e.g. if you edit a property list using EDITP, only those properties whose values are actually changed (or added) are marked. The various system functions which create or modify objects call MARKASCHANGED to mark the object as changed. For example, when a function is defined via DEFINE or DEFINEQ, or modified via EDITF, or a DWIM correction, the function is marked as being a changed object of type FNS. Similarly, whenever a new record is declared, or an existing record redeclared or edited, it is marked as being a changed object of type RECORDS, and so on for all of the other file manager types. You can also call MARKASCHANGED directly to mark objects of a particular file manager type as changed: (MARKASCHANGED(MARKASCHANGED (Function) NIL NIL ("17") 16) NAME TYPE REASON) [Function] Marks NAME of type TYPE as being changed. MARKASCHANGED returns NAME. MARKASCHANGED is undoable. REASON is a symbol that indicated how NAME was changed. MARKASCHANGED recognizes the following values for REASON: DEFINED Used to indicate the creation of NAME, e.g. from DEFINEQ (Chapter 10). CHANGED Used to indicate a change to NAME, e.g. from the editor. DELETED Used to indicate the deletion of NAME, e.g. by DELDEF. CLISP Used to indicate the modification of NAME by CLISP translation. For backwards compatibility, MARKASCHANGED also accepts a REASON of T (=DEFINED) and NIL (=CHANGED). New programs should avoid using these values. The variable MARKASCHANGEDFNS is a list of functions that MARKASCHANGED calls (with arguments NAME, TYPE, and REASON). Functions can be added to this list to "advise" MARKASCHANGED to do additional work for all types of objects. The WHENCHANGED file manager type property (see the Defining New File Manager Types section) can be used to specify additional actions when MARKASCHANGED gets called on specific types of objects. (UNMARKASCHANGED(UNMARKASCHANGED (Function) NIL NIL ("17") 16) NAME TYPE) [Function] Unmarks NAME of type TYPE as being changed. Returns NAME if NAME was marked as changed and is now unmarked, NIL otherwise. UNMARKASCHANGED is undoable. (FILEPKGCHANGES(FILEPKGCHANGES (Function) NIL NIL ("17") 16) TYPE LST) [NoSpread Function] If LST is not specified (as opposed to being NIL), returns a list of those objects of type TYPE that have been marked as changed but not yet associated with their corresponding files (see the File Manager Types section). If LST is specified, FILEPKGCHANGES sets the corresponding list. (FILEPKGCHANGES) returns a list of all objects marked as changed as a list of elements of the form (TYPENAME . CHANGEDOBJECTS). Some properties (e.g. EXPR, ADVICE, MACRO, I.S.OPR, etc.) are used to implement other file manager types. For example, if you change the value of the property I.S.OPR, you are really changing an object of type I.S.OPR. The effect is the same as though you had redefined the i.s.opr via a direct call to the function I.S.OPR. If a property whose value has been changed or added does not correspond to a specific file manager type, then it is marked as a changed object of type PROPS whose name is (VARIABLENAME PROPNAME) (except if the property name has a property PROPTYPE with value IGNORE). Similarly, if you change a variable which implements the file manager type ALISTS (as indicated by the appearance of the property VARTYPE with value ALIST on the variable's property list), only those entries that are actually changed are marked as being changed objects of type ALISTS. The "name" of the object will be (VARIABLENAME KEY) where KEY is CAR of the entry on the alist that is being marked. If the variable corresponds to a specific file manager type other than ALISTS, e.g., USERMACROS, LISPXMACROS, etc., then an object of that type is marked. In this case, the name of the changed object will be CAR of the corresponding entry on the alist. For example, if you edit LISPXMACROS and change a definition for PL, then the object PL of type LISPXMACROS is marked as being changed. Noticing Files 1 Already existing files are "noticed" by LOAD or LOADFROM (or by LOADFNS or LOADVARS when the VARS argument is T. New files are noticed when they are constructed by MAKEFILE, or when definitions are first associated with them via FILES? or ADDTOFILES?. Noticing a file updates certain lists and properties so that the file manager functions know to include the file in their operations. For example, CLEANUP will only dump files that have been noticed. You can explicitly tell the file manager to notice a newly-created file by defining the filecoms for the file, and calling ADDFILE: (ADDFILE(ADDFILE (Function) NIL NIL ("17") 17) FILE) [Function] Tells the file manager that FILE should be recognized as a file; it adds FILE to FILELST, and also sets up the FILE property of FILE to reflect the current set of changes which are "registered against" FILE. The file manager uses information stored on the property list of the root name of noticed files. The following property names are used: FILE (FILE% (Property) NIL NIL ("17") 17) [Property Name] When a file is noticed, the property FILE, value ((FILECOMS . LOADTYPE)) is added to the property list of its root name. FILECOMS is the variable containing the filecoms of the file. LOADTYPE indicates how the file was loaded, e.g., completely loaded, only partially loaded as with LOADFNS, loaded as a compiled file, etc. The property FILE is used to determine whether or not the corresponding file has been modified since the last time it was loaded or dumped. CDR of the FILE property records by type those items that have been changed since the last MAKEFILE. Whenever a file is dumped, these items are moved to the property FILECHANGES, and CDR of the FILE property is reset to NIL. FILECHANGES(FILECHANGES (Property) NIL NIL ("17") 17) [Property Name] The property FILECHANGES contains a list of all changed items since the file was loaded (there may have been several sequences of editing and rewriting the file). When a file is dumped, the changes in CDR of the FILE property are added to the FILECHANGES property. FILEDATES(FILEDATES (Property) NIL NIL ("17") 18) [Property Name] The property FILEDATES contains a list of version numbers and corresponding file dates for this file. These version numbers and dates are used for various integrity checks in connection with remaking a file. FILEMAP(FILEMAP (Property) NIL NIL ("17") 18) [Property Name] The property FILEMAP is used to store the filemap for the file. This is used to directly load individual functions from the middle of a file. To compute the root name, ROOTFILENAME is applied to the name of the file as indicated in the FILECREATED expression appearing at the front of the file, since this name corresponds to the name the file was originally made under. The file manager detects that the file being noticed is a compiled file (regardless of its name), by the appearance of more than one FILECREATED expressions. In this case, each of the files mentioned in the following FILECREATED expressions are noticed. For example, if you perform (BCOMPL '(FOO FIE)), and subsequently loads FOO.DCOM, both FOO and FIE will be noticed. When a file is noticed, its root name is added to the list FILELST: FILELST(FILELST (Variable) NIL NIL ("17") 18) [Variable] Contains a list of the root names of the files that have been noticed. LOADEDFILELST(LOADEDFILELST (Variable) NIL NIL ("17") 18) [Variable] Contains a list of the actual names of the files as loaded by LOAD, LOADFNS, etc. For example, if you perform (LOAD 'EDITA.COM;3), EDITA will be added to FILELST, but EDITA.COM;3 is added to LOADEDFILELST. LOADEDFILELST is not used by the file manager; it is maintained solely for your benefit. Distributing Change Information 1 Periodically, the function UPDATEFILES is called to find which file(s) contain the elements that have been changed. UPDATEFILES is called by FILES?, CLEANUP, and MAKEFILES, i.e., any procedure that requires the FILE property to be up to date. This procedure is followed rather than updating the FILE property after each change because scanning FILELST and examining each file manager command can be a time-consuming process; this is not so noticeable when performed in conjunction with a large operation like loading or writing a file. UPDATEFILES operates by scanning FILELST and interrogating the file manager commands for each file. When (if) any files are found that contain the corresponding typed definition, the name of the element is added to the value of the property FILE for the corresponding file. Thus, after UPDATEFILES has completed operating, the files that need to be dumped are simply those files on FILELST for which CDR of their FILE property is non-NIL. For example, if you load the file FOO containing definitions for FOO1, FOO2, and FOO3, edit FOO2, and then call UPDATEFILES, (GETPROP 'FOO 'FILE) will be ((FOOCOMS . T) (FNS FOO2)). If any objects marked as changed have not been transferred to the FILE property for some file, e.g., you define a new function but forget (or declines) to add it to the file manager commands for the corresponding file, then both FILES? and CLEANUP will print warning messages, and then call ADDTOFILES? to permit you to specify on which files these items belong. You can also invoke UPDATEFILES directly: (UPDATEFILES(UPDATEFILES (Function) NIL NIL ("17") 19) % %) [Function] (UPDATEFILES) will update the FILE properties of the noticed files. File Manager Types 1 In addition to the definitions of functions and values of variables, source files in Interlisp can contain a variety of other information, e.g. property lists, record declarations, macro definitions, hash arrays, etc. In order to treat such a diverse assortment of data uniformly from the standpoint of file operations, the file manager uses the concept of a typed definition, of which a function definition is just one example. A typed definition associates with a name (usually a symbol), a definition of a given type (called the file manager type). Note that the same name may have several definitions of different types. For example, a symbol may have both a function definition and a variable definition. The file manager also keeps track of the file that a particular typed definition is stored on, so one can think of a typed definition as a relation between four elements: a name, a definition, a type, and a file. A file manager type is an abstract notion of a class of objects which share the property that every object of the same file manager type is stored, retrieved, edited, copied etc., by the file manager in the same way. Each file manager type is identified by a symbol, which can be given as an argument to the functions that manipulate typed definitions. You may define new file manager types, as described in the Defining New Package Types section. FILEPKGTYPES(FILEPKGTYPES (Variable) NIL NIL ("17") 19) [Variable] The value of FILEPKGTYPES is a list of all file manager types, including any that you may have defined. The file manager is initialized with the following built-in file manager types: ADVICE(ADVICE (File Package Type) NIL NIL ("17") 19) [File Manager Type] Used to access "advice" modifying a function (see Chapter 15). ALISTS(ALISTS (File Package Type) NIL NIL ("17") 19) [File Manager Type] Used to access objects stored on an association list that is the value of a symbol (see Chapter 3). A variable is declared to have an association list as its value by putting on its property list the property VARTYPE with value ALIST. In this case, each dotted pair on the list is an object of type ALISTS. When the value of such a variable is changed, only those entries in the association list that are actually changed or added are marked as changed objects of type ALISTS (with "name" (SYMBOL KEY)). Objects of type ALISTS are dumped via the ALISTS or ADDVARS file manager commands. Note that some association lists are used to "implement" other file manager types. For example, the value of the global variable USERMACROS implements the file manager type USERMACROS and the values of LISPXMACROS and LISPXHISTORYMACROS implement the file manager type LISPXMACROS. This is indicated by putting on the property list of the variable the property VARTYPE with value a list of the form (ALIST FILEPKGTYPE). For example, (GETPROP 'LISPXHISTORYMACROS 'VARTYPE) => (ALIST LISPXMACROS). COURIERPROGRAMS(COURIERPROGRAMS (File Package Type) NIL NIL ("17") 20) [File Manager Type] Used to access Courier programs (see Chapter 31). EXPRESSIONS(EXPRESSIONS (File Package Type) NIL NIL ("17") 20) [File Manager Type] Used to access lisp expressions that are put on a file by using the REMEMBER programmers assistant command (Chapter 13), or by explicitly putting the P file manager command on the filecoms. FIELDS(FIELDS (File Package Type) NIL NIL ("17") 20) [File Manager Type] Used to access fields of records. The "definition" of an object of type FIELDS is a list of all the record declarations which contain the name. See Chapter 8. FILEPKGCOMS (FILEPKGCOMS% (File Package Type) NIL NIL ("17") 20) [File Manager Type] Used to access file manager commands and types. A single name can be defined both as a file manager type and a file manager command. The "definition" of an object of type FILEPKGCOMS is a list structure of the form ((COM . COMPROPS) (TYPE . TYPEPROPS)), where COMPROPS is a property list specifying how the name is defined as a file manager command by FILEPKGCOM (see the Defining New File Manager Commands section), and TYPEPROPS is a property list specifying how the name is defined as a file manager type by FILEPKGTYPE (see the Defining New File Manager Types section). FILES(FILES (File Package Type) NIL NIL ("17") 20) [File Manager Type] Used to access files. This file manager type is most useful for renaming files. The "definition" of a file is not a useful structure. FILEVARS(FILEVARS (File Package Type) NIL NIL ("17") 20) [File Manager Type] Used to access Filevars (see the FileVars section). FNS(FNS (File Package Type) NIL NIL ("17") 20) [File Manager Type] Used to access function definitions. I.S.OPRS(I.S.OPRS (File Package Type) NIL NIL ("17") 21) [File Manager Type] Used to access the definitions of iterative statement operators (see Chapter 9). LISPXMACROS(LISPXMACROS (File Package Type) NIL NIL ("17") 21) [File Manager Type] Used to access programmer's assistant commands defined on the variables LISPXMACROS and LISPXHISTORYMACROS (see Chapter 13). MACROS(MACROS (File Package Type) NIL NIL ("17") 21) [File Manager Type] Used to access macro definitions (see Chapter 10). PROPS(PROPS (File Package Type) NIL NIL ("17") 21) [File Manager Type] Used to access objects stored on the property list of a symbol (see Chapter 2). When a property is changed or added, an object of type PROPS, with "name" (SYMBOL PROPNAME) is marked as being changed. Note that some symbol properties are used to implement other file manager types. For example, the property MACRO implements the file manager type MACROS, the property ADVICE implements ADVICE, etc. This is indicated by putting the property PROPTYPE, with value of the file manager type on the property list of the property name. For example, (GETPROP 'MACRO 'PROPTYPE) => MACROS. When such a property is changed or added, an object of the corresponding file manager type is marked. If (GETPROP PROPNAME 'PROPTYPE) => IGNORE, the change is ignored. The FILE, FILEMAP, FILEDATES, etc. properties are all handled this way. (IGNORE cannot be the name of a file manager type implemented as a property). RECORDS(RECORDS (File Package Type) NIL NIL ("17") 21) [File Manager Type] Used to access record declarations (see Chapter 8). RESOURCES(RESOURCES (File Package Type) NIL NIL ("17") 21) [File Manager Type] Used to access resources (see Chapter 12). TEMPLATES(TEMPLATES (File Package Type) NIL NIL ("17") 21) [File Manager Type] Used to access Masterscope templates (see Chapter 19). USERMACROS(USERMACROS (File Package Type) NIL NIL ("17") 21) [File Manager Type] Used to access user edit macros (see Chapter 16). VARS(VARS (File Package Type) NIL NIL ("17") 21) [File Manager Type] Used to access top-level variable values. Functions for Manipulating Typed Definitions The functions described below can be used to manipulate typed definitions, without needing to know how the manipulations are done. For example, (GETDEF 'FOO 'FNS) will return the function definition of FOO, (GETDEF 'FOO 'VARS) will return the variable value of FOO, etc. All of the functions use the following conventions: 1. All functions which make destructive changes are undoable. 2. Any argument that expects a list of symbols will also accept a single symbol, operating as though it were enclosed in a list. For example, if the argument FILES should be a list of files, it may also be a single file. 3. TYPE is a file manager type. TYPE = NIL is equivalent to TYPE = FNS. The singular form of a file manager type is also recognized, e.g. TYPE = VAR is equivalent to TYPE = VARS. 4. FILES = NIL is equivalent to FILES = FILELST. 5. SOURCE is used to indicate the source of a definition, that is, where the definition should be found. SOURCE can be one of: CURRENT Get the definition currently in effect. SAVED Get the "saved" definition, as stored by SAVEDEF. FILE Get the definition contained on the (first) file determined by WHEREIS. WHEREIS is called with FILES = T, so that if the WHEREIS library package is loaded, the WHEREIS data base will be used to find the file containing the definition. ? Get the definition currently in effect if there is one, else the saved definition if there is one, otherwise the definition from a file determined by WHEREIS. Like specifying CURRENT, SAVED, and FILE in order, and taking the first definition that is found. a file name a list of file names Get the definition from the first of the indicated files that contains one. NIL In most cases, giving SOURCE = NIL (or not specifying it at all) is the same as giving ?, to get either the current, saved, or filed definition. However, with HASDEF, SOURCE = NIL is interpreted as equal to SOURCE = CURRENT, which only tests if there is a current definition. The operation of most of the functions described below can be changed or extended by modifying the appropriate properties for the corresponding file manager type using the function FILEPKGTYPE, described in the Defining New File Manager Types section. (GETDEF(GETDEF (Function) NIL NIL ("17") 23) NAME TYPE SOURCE OPTIONS) [Function] Returns the definition of NAME, of type TYPE, from SOURCE. For most types, GETDEF returns the expression which would be pretty printed when dumping NAME as TYPE. For example, for TYPE = FNS, an EXPR definition is returned, for TYPE = VARS, the value of NAME is returned, etc. OPTIONS is a list which specifies certain options: NOERROR GETDEF causes an error if an appropriate definition cannot be found, unless OPTIONS is or contains NOERROR. In this case, GETDEF returns the value of the NULLDEF file manager type property (see the Defining New File Manager Types section), usually NIL. a string If OPTIONS is or contains a string, that string will be returned if no definition is found (and NOERROR is not among the options). The caller can thus determine whether a definition was found, even for types for which NIL or NOBIND are acceptable definitions. NOCOPY GETDEF returns a copy of the definition unless OPTIONS is or contains NOCOPY. EDIT If OPTIONS is or contains EDIT, GETDEF returns a copy of the definition unless it is possible to edit the definition "in place." With some file manager types, such as functions, it is meaningful (and efficient) to edit the definition by destructively modifying the list structure, without calling PUTDEF. However, some file manager types (like records) need to be "installed" with PUTDEF after they are edited. The default EDITDEF (see the Defining New File Manager Types section) calls GETDEF with OPTIONS of (EDIT NOCOPY), so it doesn't use a copy unless it has to, and only calls PUTDEF if the result of editing is not EQUAL to the old definition. NODWIM A FNS definition will be dwimified if it is likely to contain CLISP unless OPTIONS is or contains NODWIM. (PUTDEF(PUTDEF (Function) NIL NIL ("17") 23) NAME TYPE DEFINITION REASON) [Function] Defines NAME of type TYPE with DEFINITION. For TYPE = FNS, does a DEFINE; for TYPE = VARS, does a SAVESET, etc. For TYPE = FILES, PUTDEF establishes the command list, notices NAME, and then calls MAKEFILE to actually dump the file NAME, copying functions if necessary from the "old" file (supplied as part of DEFINITION). PUTDEF calls MARKASCHANGED (see the Mrking Changes section) to mark NAME as changed, giving a reason of REASON. If REASON is NIL, the default is DEFINED. If TYPE = FNS, PUTDEF prints a warning if you try to redefine a function on the list UNSAFE.TO.MODIFY.FNS (see Chapter 10). (HASDEF(HASDEF (Function) NIL NIL ("17") 24) NAME TYPE SOURCE SPELLFLG) [Function] Returns (OR NAME T) if NAME is the name of something of type TYPE. If not, attempts spelling correction if SPELLFLG = T, and returns the spelling-corrected NAME. Otherwise returns NIL. HASDEF for type FNS (or NIL) indicates that NAME has an editable source definition. If NAME is a function that exists on a file for which you have loaded only the compiled version and not the source, HASDEF returns NIL. (HASDEF NIL TYPE) returns T if NIL has a valid definition. If SOURCE = NIL, HASDEF interprets this as equal to SOURCE = CURRENT, which only tests if there is a current definition. (TYPESOF(TYPESOF (Function) NIL NIL ("17") 24) NAME POSSIBLETYPES IMPOSSIBLETYPES SOURCE) [Function] Returns a list of the types in POSSIBLETYPES but not in IMPOSSIBLETYPES for which NAME has a definition. FILEPKGTYPES is used if POSSIBLETYPES is NIL. (COPYDEF(COPYDEF (Function) NIL NIL ("17") 24) OLD NEW TYPE SOURCE OPTIONS) [Function] Defines NEW to have a copy of the definition of OLD by doing PUTDEF on a copy of the definition retrieved by (GETDEF OLD TYPE SOURCE OPTIONS). NEW is substituted for OLD in the copied definition, in a manner that may depend on the TYPE. For example, (COPYDEF 'PDQ 'RST 'FILES) sets up RSTCOMS to be a copy of PDQCOMS, changes things like (VARS * PDQVARS) to be (VARS * RSTVARS) in RSTCOMS, and performs a MAKEFILE on RST such that the appropriate definitions get copied from PDQ. COPYDEF disables the NOCOPY option of GETDEF, so NEW will always have a copy of the definition of OLD. COPYDEF substitutes NEW for OLD throughout the definition of OLD. This is usually the right thing to do, but in some cases, e.g., where the old name appears within a quoted expression but was not used in the same context, you must re-edit the definition. (DELDEF(DELDEF (Function) NIL NIL ("17") 24) NAME TYPE) [Function] Removes the definition of NAME as a TYPE that is currently in effect. (SHOWDEF(SHOWDEF (Function) NIL NIL ("17") 24) NAME TYPE FILE) [Function] Prettyprints the definition of NAME as a TYPE to FILE. This shows you how NAME would be written to a file. Used by ADDTOFILES? (see the Storing Files section). (EDITDEF(EDITDEF (Function) NIL NIL ("17") 24) NAME TYPE SOURCE EDITCOMS) [Function] Edits the definition of NAME as a TYPE. Essentially performs (PUTDEF NAME TYPE (EDITE (GETDEF NAME TYPE SOURCE) EDITCOMS)) (SAVEDEF(SAVEDEF (Function) NIL NIL ("17") 25) NAME TYPE DEFINITION) [Function] Sets the "saved" definition of NAME as a TYPE to DEFINITION. If DEFINITION = NIL, the current definition of NAME is saved. If TYPE = FNS (or NIL), the function definition is saved on NAME's property list under the property EXPR, or CODE (depending on the FNTYP of the function definition). If (GETD NAME) is non-NIL, but (FNTYP FN) = NIL, SAVEDEF saves the definition on the property name LIST. This can happen if a function was somehow defined with an illegal expr definition, such as (LAMMMMDA (X) ...). If TYPE = VARS, the definition is stored as the value of the VALUE property of NAME. For other types, the definition is stored in an internal data structure, from where it can be retrieved by GETDEF or UNSAVEDEF. (UNSAVEDEF(UNSAVEDEF (Function) NIL NIL ("17") 25) NAME TYPE) [Function] Restores the "saved" definition of NAME as a TYPE, making it be the current definition. Returns PROP. If TYPE = FNS (or NIL), UNSAVEDEF unsaves the function definition from the EXPR property if any, else CODE, and returns the property name used. UNSAVEDEF also recognizes TYPE = EXPR, CODE, or LIST, meaning to unsave the definition only from the corresponding property only. If DFNFLG is not T (see Chapter 10), the current definition of NAME, if any, is saved using SAVEDEF. Thus one can use UNSAVEDEF to switch back and forth between two definitions. (LOADDEF(LOADDEF (Function) NIL NIL ("17") 25) NAME TYPE SOURCE) [Function] Equivalent to (PUTDEF NAME TYPE (GETDEF NAME TYPE SOURCE)). LOADDEF is essentially a generalization of LOADFNS, e.g. it enables loading a single record declaration from a file. (LOADDEF FN) will give FN an EXPR definition, either obtained from its property list or a file, unless it already has one. (CHANGECALLERS(CHANGECALLERS (Function) NIL NIL ("17") 25) OLD NEW TYPES FILES METHOD) [Function] Finds all of the places where OLD is used as any of the types in TYPES and changes those places to use NEW. For example, (CHANGECALLERS 'NLSETQ 'ERSETQ) will change all calls to NLSETQ to be calls to ERSETQ. Also changes occurrences of OLD to NEW inside the filecoms of any file, inside record declarations, properties, etc. CHANGECALLERS attempts to determine if OLD might be used as more than one type; for example, if it is both a function and a record field. If so, rather than performing the transformation OLD -> NEW automatically, you are allowed to edit all of the places where OLD occurs. For each occurrence of OLD, you are asked whether you want to make the replacement. If you respond with anything except Yes or No, the editor is invoked on the expression containing that occurrence. There are two different methods for determining which functions are to be examined. If METHOD = EDITCALLERS, EDITCALLERS is used to search FILES (see Chapter 16). If METHOD = MASTERSCOPE, then the Masterscope database is used instead. METHOD = NIL defaults to MASTERSCOPE if the value of the variable DEFAULTRENAMEMETHOD is MASTERSCOPE and a Masterscope database exists, otherwise it defaults to EDITCALLERS. (RENAME(RENAME (Function) NIL NIL ("17") 26) OLD NEW TYPES FILES METHOD) [Function] First performs (COPYDEF OLD NEW TYPE) for all TYPE inside TYPES. It then calls CHANGECALLERS to change all occurrences of OLD to NEW, and then "deletes" OLD with DELDEF. For example, if you have a function FOO which you now wish to call FIE, simply perform (RENAME 'FOO 'FIE), and FIE will be given FOO's definition, and all places that FOO are called will be changed to call FIE instead. METHOD is interpreted the same as the METHOD argument to CHANGECALLERS, above. (COMPARE(COMPARE (Function) NIL NIL ("17") 26) NAME1 NAME2 TYPE SOURCE1 SOURCE2) [Function] Compares the definition of NAME1 with that of NAME2, by calling COMPARELISTS (Chapter 3) on (GETDEF NAME1 TYPE SOURCE1) and (GETDEF NAME2 TYPE SOURCE2), which prints their differences on the terminal. For example, if the current value of the variable A is (A B C (D E F) G), and the value of the variable B on the file FOO is (A B C (D F E) G), then: (COMPARE 'A 'B 'VARS 'CURRENT 'FOO) A from CURRENT and B from TEST differ: (E -> F) (F -> E) T (COMPAREDEFS(COMPAREDEFS (Function) NIL NIL ("17") 26) NAME TYPE SOURCES) [Function] Calls COMPARELISTS (Chapter 3) on all pairs of definitions of NAME as a TYPE obtained from the various SOURCES (interpreted as a list of source specifications). Defining New File Manager Types All manipulation of typed definitions in the file manager is done using the type-independent functions GETDEF, PUTDEF, etc. Therefore, to define a new file manager type, it is only necessary to specify (via the function FILEPKGTYPE) what these functions should do when dealing with a typed definition of the new type. Each file manager type has the following properties, whose values are functions or lists of functions: These functions are defined to take a TYPE argument so that you may have the same function for more than one type. GETDEF(GETDEF (File Package Type Property) NIL NIL ("17") 26) [File Manager Type Property] Value is a function of three arguments, NAME, TYPE, and OPTIONS, which should return the current definition of NAME as a type TYPE. Used by GETDEF (see the Functions for Manipulating Typed Definitions section), which passes its OPTIONS argument. If there is no GETDEF property, a file manager command for dumping NAME is created (by MAKENEWCOM). This command is then used to write the definition of NAME as a type TYPE onto the file FILEPKG.SCRATCH (in Medley, this file is created on the {CORE} device). This expression is then read back in and returned as the current definition. In some situations, the function HASDEF needs to call GETDEF to determine whether a definition exists. In this case, OPTIONS will include the symbol HASDEF, and it is permissable for a GETDEF function to return T or NIL, rather than creating a complex structure which will not be used. NULLDEF(NULLDEF (File Package Type Property) NIL NIL ("17") 27) [File Manager Type Property] The value of the NULLDEF property is returned by GETDEF (see the Functions for Manipulating Typed Definitions section) when there is no definition and the NOERROR option is supplied. For example, the NULLDEF of VARS is NOBIND. FILEGETDEF(FILEGETDEF (File Package Type Property) NIL NIL ("17") 27) [File Manager Type Property] This enables you to provide a way of obtaining definitions from a file that is more efficient than the default procedure used by GETDEF (see the Functions for Manipulating Typed Definitions section). Value is a function of four arguments, NAME, TYPE, FILE, and OPTIONS. The function is applied by GETDEF when it is determined that a typed definition is needed from a particular file. The function must open and search the given file and return any TYPE definition for NAME that it finds. CANFILEDEF(CANFILEDEF (File Package Type Property) NIL NIL ("17") 27) [File Manager Type Property] If the value of this property is non-NIL, this indicates that definitions of this file manager type are not loaded when a file is loaded with LOADFROM (see the Loading Files section). The default is NIL. Initially, only FNS has this property set to non-NIL. PUTDEF(PUTDEF (File Package Type Property) NIL NIL ("17") 27) [File Manager Type Property] Value is a function of three arguments, NAME, TYPE, and DEFINITION, which should store DEFINITION as the definition of NAME as a type TYPE. Used by PUTDEF (see the Functions for Manipulating Typed Definitions section). HASDEF(HASDEF (File Package Type Property) NIL NIL ("17") 27) [File Manager Type Property] Value is a function of three arguments, NAME, TYPE, and SOURCE, which should return (OR NAME T) if NAME is the name of something of type TYPE. SOURCE is as interpreted by HASDEF (see the Functions for Manipulating Typed Definitions section), which uses this property. EDITDEF(EDITDEF (File Package Type Property) NIL NIL ("17") 27) [File Manager Type Property] Value is a function of four arguments, NAME, TYPE, SOURCE, and EDITCOMS, which should edit the definition of NAME as a type TYPE from the source SOURCE, interpreting the edit commands EDITCOMS. If sucessful, should return NAME (or a spelling-corrected NAME). If it returns NIL, the "default" editor is called. Used by EDITDEF (see the Functions for Manipulating Typed Definitions section). DELDEF(DELDEF (File Package Type Property) NIL NIL ("17") 28) [File Manager Type Property] Value is a function of two arguments, NAME, and TYPE, which removes the definition of NAME as a TYPE that is currently in effect. Used by DELDEF (see the Functions for Manipulating Typed Definitions section). NEWCOM(NEWCOM (File Package Type Property) NIL NIL ("17") 28) [File Manager Type Property] Value is a function of four arguments, NAME, TYPE, LISTNAME, and FILE. Specifies how to make a new (instance of a) file manager command to dump NAME, an object of type TYPE. The function should return the new file manager command. Used by ADDTOFILE and SHOWDEF. If LISTNAME is non-NIL, this means that you specified LISTNAME as the filevar in interaction with ADDTOFILES? (see the FileVars section). If no NEWCOM is specified, the default is to call DEFAULTMAKENEWCOM, which will construct and return a command of the form (TYPE NAME). You can advise or redefine DEFAULTMAKENEWCOM . WHENCHANGED(WHENCHANGED (File Package Type Property) NIL NIL ("17") 28) [File Manager Type Property] Value is a list of functions to be applied to NAME, TYPE, and REASON when NAME, an instance of type TYPE, is changed or defined (see MARKASCHANGED, in the Marking Changes section). Used for various applications, e.g. when an object of type I.S.OPRS changes, it is necessary to clear the corresponding translatons from CLISPARRAY. The WHENCHANGED functions are called before the object is marked as changed, so that it can, in fact, decide that the object is not to be marked as changed, and execute (RETFROM 'MARKASCHANGED). The REASON argument passed to WHENCHANGED functions is either DEFINED or CHANGED. WHENFILED (WHENFILED% (File Package Type Property) NIL NIL ("17") 28) [File Manager Type Property] Value is a list of functions to be applied to NAME, TYPE, and FILE when NAME, an instance of type TYPE, is added to FILE. WHENUNFILED(WHENUNFILED (File Package Type Property) NIL NIL ("17") 28) [File Manager Type Property] Value is a list of functions to be applied to NAME, TYPE, and FILE when NAME, an instance of type TYPE, is removed from FILE. DESCRIPTION(DESCRIPTION (File Package Type Property) NIL NIL ("17") 28) [File Manager Type Property] Value is a string which describes instances of this type. For example, for type RECORDS, the value of DESCRIPTION is the string "record declarations". The function FILEPKGTYPE is used to define new file manager types, or to change the properties of existing types. It is possible to redefine the attributes of system file manager types, such as FNS or PROPS. (FILEPKGTYPE(FILEPKGTYPE (Function) NIL NIL ("17") 29) TYPE PROP1 VAL1 ... PROPN VALN) [NoSpread Function] Nospread function for defining new file manager types, or changing properties of existing file manager types. PROPi is one of the property names given above; VALi is the value to be given to that property. Returns TYPE. (FILEPKGTYPE TYPE PROP) returns the value of the property PROP, without changing it. (FILEPKGTYPE TYPE) returns a property list of all of the defined properties of TYPE, using the property names as keys. Specifying TYPE as the symbol TYPE can be used to define one file manager type as a synonym of another. For example, (FILEPKGTYPE 'R 'TYPE 'RECORDS) defines R as a synonym for the file manager type RECORDS. File Manager Commands 1 The basic mechanism for creating symbolic files is the function MAKEFILE (see the Storing Files section). For each file, the file manager has a data structure known as the "filecoms", which specifies what typed descriptions are contained in the file. A filecoms is a list of file manager commands, each of which specifies objects of a certain file manager type which should be dumped. For example, the filecoms ((FNS FOO) (VARS FOO BAR BAZ) (RECORDS XYZZY)) has a FNS, a VARS, and a RECORDS file manager command. This filecoms specifies that the function definition for FOO, the variable values of FOO, BAR, and BAZ, and the record declaration for XYZZY should be dumped. By convention, the filecoms of a file X is stored as the value of the symbol XCOMS. For example, (MAKEFILE 'FOO.;27) will use the value of FOOCOMS as the filecoms. This variable can be directly manipulated, but the file manager contains facilities which make constructing and updating filecoms easier, and in some cases automatic (see the Functions for Manipulating File Command Lists section). A file manager command is an instruction to MAKEFILE to perform an explicit, well-defined operation, usually printing an expression. Usually there is a one-to-one correspondence between file manager types and file manager commands; for each file manager type, there is a file manager command which is used for writing objects of that type to a file, and each file manager command is used to write objects of a particular type. However, in some cases, the same file manager type can be dumped by several different file manager commands. For example, the file manager commands PROP, IFPROP, and PROPS all dump out objects with the file manager type PROPS. This means if you change an object of file manager type PROPS via EDITP, a typed-in call to PUTPROP, or via an explicit call to MARKASCHANGED, this object can be written out with any of the above three commands. Thus, when the file manager attempts to determine whether this typed object is contained on a particular file, it must look at instances of all three file manager commands PROP, IFPROP, and PROPS, to see if the corresponding atom and property are specified. It is also permissible for a single file manager command to dump several different file manager types. For example, you can define a file manager command which dumps both a function definition and its macro. Conversely, some file manager comands do not dump any file manager types at all, such as the E command. For each file manager command, the file manager must be able to determine what typed definitions the command will cause to be printed so that the file manager can determine on what file (if any) an object of a given type is contained (by searching through the filecoms). Similarly, for each file manager type, the file manager must be able to construct a command that will print out an object of that type. In other words, the file manager must be able to map file manager commands into file manager types, and vice versa. Information can be provided to the file manager about a particular file manager command via the function FILEPKGCOM (see the Defining New File Manager Commands section), and information about a particular file manager type via the function FILEPKGTYPE (see the prior section). In the absence of other information, the default is simply that a file manager command of the form (X NAME) prints out the definition of NAME as a type X, and, conversely, if NAME is an object of type X, then NAME can be written out by a command of the form (X NAME). If a file manager function is given a command or type that is not defined, it attempts spelling correction using FILEPKGCOMSPLST as a spelling list (unless DWIMFLG or NOSPELLFLG = NIL; see Chapter 20). If successful, the corrected version of the list of file manager commands is written (again) on the output file, since at this point, the uncorrected list of file manager commands would already have been printed on the output file. When the file is loaded, this will result in FILECOMS being reset, and may cause a message to be printed, e.g., (FOOCOMS RESET). The value of FOOCOMS would then be the corrected version. If the spelling correction is unsuccessful, the file manager functions generate an error, BAD FILE PACKAGE COMMAND. File package commands can be used to save on the output file definitions of functions, values of variables, property lists of atoms, advised functions, edit macros, record declarations, etc. The interpretation of each file manager command is documented in the following sections. (USERMACROS(USERMACROS (Command) NIL NIL ("17") 30) SYMBOL1 ... SYMBOLN) [File Manager Command] Each symbol SYMBOLi is the name of a user edit macro. Writes expressions to add the edit macro definitions of SYMBOLi to USERMACROS, and adds the names of the commands to the appropriate spelling lists. If SYMBOLi is not a user macro, a warning message "no EDIT MACRO for SYMBOLi" is printed. Functions and Macros (FNS(FNS (Command) NIL NIL ("17") 30)(FNS (Command) NIL NIL ("17") 30) FN1 ... FNN) [File Manager Command] Writes a DEFINEQ expression with the function definitions of FN1 ... FNN. You should never print a DEFINEQ expression directly onto a file (by using the P file manager command, for example), because MAKEFILE generates the filemap of function definitions from the FNS file manager commands (see the File Maps section). (ADVISE(ADVISE (Command) NIL NIL ("17") 31) FN1 ... FNN) [File Manager Command] For each function FNi, writes expressions to reinstate the function to its advised state when the file is loaded. See Chapter 15. When advice is applied to a function programmatically or by hand, it is additive. That is, if a function already has some advice, further advice is added to the already-existing advice. However, when advice is applied to a function as a result of loading a file with an ADVISE file manager command, the new advice replaces any earlier advice. ADVISE works this way to prevent problems with loading different versions of the same advice. If you really want to apply additive advice, a file manager command such as (P (ADVISE ...)) should be used (see the Miscellaneous File Manager Commands section). (ADVICE(ADVICE (Command) NIL NIL ("17") 31) FN1 ... FNN) [File Manager Command] For each function FNi, writes a PUTPROPS expression which will put the advice back on the property list of the function. You can then use READVISE (see Chapter 15) to reactivate the advice. (MACROS(MACROS (Command) NIL NIL ("17") 31) SYMBOL1 ... SYMBOLN) [File Manager Command] Each SYMBOLi is a symbol with a MACRO definition (and/or a DMACRO, 10MACRO, etc.). Writes out an expression to restore all of the macro properties for each SYMBOLi, embedded in a DECLARE: EVAL@COMPILE so the macros will be defined when the file is compiled. See Chapter 10. Variables (VARS(VARS (Command) NIL NIL ("17") 31) VAR1 ... VARN) [File Manager Command] For each VARi, writes an expression to set its top level value when the file is loaded. If VARi is atomic, VARS writes out an expression to set VARi to the top-level value it had at the time the file was written. If VARi is non-atomic, it is interpreted as (VAR FORM), and VARS write out an expression to set VAR to the value of FORM (evaluated when the file is loaded). VARS prints out expressions using RPAQQ and RPAQ, which are like SETQQ and SETQ except that they also perform some special operations with respect to the file manager (see the Functions Used within Source Files section). VARS cannot be used for putting arbitrary variable values on files. For example, if the value of a variable is an array (or many other data types), a symbol which represents the array is dumped in the file instead of the array itself. The HORRIBLEVARS file manager command provides a way of saving and reloading variables whose values contain re-entrant or circular list structure, user data types, arrays, or hash arrays. (INITVARS(INITVARS (Command) NIL NIL ("17") 32) VAR1 ... VARN) [File Manager Command] INITVARS is used for initializing variables, setting their values only when they are currently NOBIND. A variable value defined in an INITVARS command will not change an already established value. This means that re-loading files to get some other information will not automatically revert to the initialization values. The format of an INITVARS command is just like VARS. The only difference is that if VARi is atomic, the current value is not dumped; instead NIL is defined as the initialization value. Therefore, (INITVARS FOO (FUM 2)) is the same as (VARS (FOO NIL)(FUM 2)), if FOO and FUM are both NOBIND. INITVARS writes out an RPAQ? expression on the file instead of RPAQ or RPAQQ. (ADDVARS(ADDVARS (Command) NIL NIL ("17") 32) (VAR1 . LST1)...(VARN . LSTN)) [File Manager Command] For each (VARi . LSTi), writes an ADDTOVAR (see the Functions Used Within Source Files section) to add each element of LSTi to the list that is the value of VARi at the time the file is loaded. The new value of VARi will be the union of its old value and LSTi. If the value of VARi is NOBIND, it is first set to NIL. For example, (ADDVARS (DIRECTORIES LISP LISPUSERS)) will add LISP and LISPUSERS to the value of DIRECTORIES. If LSTi is not specified, VARi is initialized to NIL if its current value is NOBIND. In other words, (ADDVARS (VAR)) will initialize VAR to NIL if VAR has not previously been set. (APPENDVARS(APPENDVARS (Command) NIL NIL ("17") 32) (VAR1 . LST1) ... (VARN . LSTN)) [File Manager Command] The same as ADDVARS, except that the values are added to the end of the lists (using APPENDTOVAR, in the Functions Used Within Source Files section), rather than at the beginning. (UGLYVARS(UGLYVARS (Command) NIL NIL ("17") 32) VAR1 ... VARN) [File Manager Command] Like VARS, except that the value of each VARi may contain structures for which READ is not an inverse of PRINT, e.g. arrays, readtables, user data types, etc. Uses HPRINT (see Chapter 25). ((HORRIBLEVARS (Command) NIL NIL ("17") 32)HORRIBLEVARS VAR1 ... VARN) [File Manager Command] Like UGLYVARS, except structures may also contain circular pointers. Uses HPRINT (see Chapter 25). The values of VAR1 ... VARN are printed in the same operation, so that they may contain pointers to common substructures. UGLYVARS does not do any checking for circularities, which results in a large speed and internal-storage advantage over HORRIBLEVARS. Thus, if it is known that the data structures do not contain circular pointers, UGLYVARS should be used instead of HORRIBLEVARS. (ALISTS(ALISTS (Command) NIL NIL ("17") 33) (VAR1 KEY1 KEY2 ...)...(VARN KEY3 KEY4 ...)) [File Manager Command] VARi is a variable whose value is an association list, such as EDITMACROS, BAKTRACELST, etc. For each VARi, ALISTS writes out expressions which will restore the values associated with the specified keys. For example, (ALISTS (BREAKMACROS BT BTV)) will dump the definition for the BT and BTV commands on BREAKMACROS. Some association lists (USERMACROS, LISPXMACROS, etc.) are used to implement other file manager types, and they have their own file manager commands. (SPECVARS(SPECVARS (Command) NIL NIL ("17") 33) VAR1 ... VARN) [File Manager Command] (LOCALVARS(LOCALVARS (Command) NIL NIL ("17") 33) VAR1 ... VARN) [File Manager Command] (GLOBALVARS(GLOBALVARS (Command) NIL NIL ("17") 33) VAR1 ... VARN) [File Manager Command] Outputs the corresponding compiler declaration embedded in a DECLARE: DOEVAL@COMPILE DONTCOPY. See Chapter 18. (CONSTANTS(CONSTANTS (Command) NIL NIL ("17") 33) VAR1 ... VARN) [File Manager Command] Like VARS, for each VARi writes an expression to set its top level value when the file is loaded. Also writes a CONSTANTS expression to declare these variables as constants (see Chapter 18). Both of these expressions are wrapped in a (DECLARE: EVAL@COMPILE ...) expression, so they can be used by the compiler. Like VARS, VARi can be non-atomic, in which case it is interpreted as (VAR FORM), and passed to CONSTANTS (along with the variable being initialized to FORM). Symbol Properties (PROP(PROP (Command) NIL NIL ("17") 33) PROPNAME SYMBOL1 ... SYMBOLN) [File Manager Command] Writes a PUTPROPS expression to restore the value of the PROPNAME property of each symbol SYMBOLi when the file is loaded. If PROPNAME is a list, expressions will be written for each property on that list. If PROPNAME is the symbol ALL, the values of all user properties (on the property list of each SYMBOLi) are saved. SYSPROPS is a list of properties used by system functions. Only properties not on that list are dumped when the ALL option is used. If SYMBOLi does not have the property PROPNAME (as opposed to having the property with value NIL), a warning message "NO PROPNAME PROPERTY FOR SYMBOLi" is printed. The command IFPROP can be used if it is not known whether or not an atom will have the corresponding property. (IFPROP(IFPROP (Command) NIL NIL ("17") 34) PROPNAME SYMBOL1 ... SYMBOLN) [File Manager Command] Same as the PROP file manager command, except that it only saves the properties that actually appear on the property list of the corresponding atom. For example, if FOO1 has property PROP1 and PROP2, FOO2 has PROP3, and FOO3 has property PROP1 and PROP3, then (IFPROP (PROP1 PROP2 PROP3) FOO1 FOO2 FOO3) will save only those five property values. (PROPS(PROPS (Command) NIL NIL ("17") 34) (SYMBOL1 PROPNAME1)...(SYMBOLN PROPNAMEN)) [File Manager Command] Similar to PROP command. Writes a PUTPROPS expression to restore the value of PROPNAMEi for each SYMBOLi when the file is loaded. As with the PROP command, if SYMBOLi does not have the property PROPNAME (as opposed to having the property with NIL value), a warning message "NO PROPNAMEi PROPERTY FOR SYMBOLi" is printed. Miscellaneous File Manager Commands (RECORDS(RECORDS (Command) NIL NIL ("17") 34) REC1 ... RECN) [File Manager Command] Each RECi is the name of a record (see Chapter 8). Writes expressions which will redeclare the records when the file is loaded. (INITRECORDS(INITRECORDS (Command) NIL NIL ("17") 34) REC1 ... RECN) [File Manager Command] Similar to RECORDS, INITRECORDS writes expressions on a file that will, when loaded, perform whatever initialization/allocation is necessary for the indicated records. However, the record declarations themselves are not written out. This facility is useful for building systems on top of Interlisp, in which the implementor may want to eliminate the record declarations from a production version of the system, but the allocation for these records must still be done. (LISPXMACROS(LISPXMACROS (Command) NIL NIL ("17") 34) SYMBOL1 ... SYMBOLN) [File Manager Command] Each SYMBOLi is defined on LISPXMACROS or LISPXHISTORYMACROS (see Chapter 13). Writes expressions which will save and restore the definition for each macro, as well as making the necessary additions to LISPXCOMS (I.S.OPRS(I.S.OPRS (Command) NIL NIL ("17") 34) OPR1 ... OPRN) [File Manager Command] Each OPRi is the name of a user-defined i.s.opr (see Chapter 9). Writes expressions which will redefine the i.s.oprs when the file is loaded. (RESOURCES(RESOURCES (Command) NIL NIL ("17") 34) RESOURCE1 ... RESOURCEN) [File Manager Command] Each RESOURCESi is the name of a resource (see Chapter 12). Writes expressions which will redeclare the resource when the file is loaded. (INITRESOURCES(INITRESOURCES (Command) NIL NIL ("17") 35) RESOURCE1 ... RESOURCEN) [File Manager Command] Parallel to INITRECORDS, INITRESOURCES writes expressions on a file to perform whatever initialization/allocation is necessary for the indicated resources, without writing the resource declaration itself. (COURIERPROGRAMS(COURIERPROGRAMS (Command) NIL NIL ("17") 35) NAME1 ... NAMEN) [File Manager Command] Each NAMEi is the name of a Courier program (see Chapter 31). Writes expressions which will redeclare the Courier program when the file is loaded. (TEMPLATES(TEMPLATES (Command) NIL NIL ("17") 35) SYMBOL1 ... SYMBOLN) [File Manager Command] Each SYMBOLi is a symbol which has a Masterscope template (see Chapter 19). Writes expressions which will restore the templates when the file is loaded. (FILES(FILES (Command) NIL NIL ("17") 35) FILE1 ... FILEN) [File Manager Command] Used to specify auxiliary files to be loaded in when the file is loaded. Dumps an expression calling FILESLOAD (see the Loading Files section), with FILE1 ... FILEN as the arguments. FILESLOAD interprets FILE1 ... FILEN as files to load, possibly interspersed with lists used to specify certain loading options. (FILEPKGCOMS(FILEPKGCOMS (Command) NIL NIL ("17") 35) SYMBOL1 ... SYMBOLN) [File Manager Command] Each symbol SYMBOLi is either the name of a user-defined file manager command or a user-defined file manager type (or both). Writes expressions which will restore each command/type. If SYMBOLi is not a file manager command or type, a warning message "no FILE PACKAGE COMMAND for SYMBOLi" is printed. ((* (Command) NIL NIL ("17") 35)* . TEXT) [File Manager Command] Used for inserting comments in a file. The file manager command is simply written on the output file; it will be ignored when the file is loaded. If the first element of TEXT is another *, a form-feed is printed on the file before the comment. (P(P (Command) NIL NIL ("17") 35) EXP1 ... EXPN) [File Manager Command] Writes each of the expressions EXP1 ... EXPN on the output file, where they will be evaluated when the file is loaded. (E(E (Command) NIL NIL ("17") 35) FORM1 ... FORMN) [File Manager Command] Each of the forms FORM1 ... FORMN is evaluated at output time, when MAKEFILE interpretes this file manager command. (COMS(COMS (Command) NIL NIL ("17") 36) COM1 ... COMN) [File Manager Command] Each of the commands COM1 ... COMN is interpreted as a file manager command. (ORIGINAL(ORIGINAL (Command) NIL NIL ("17") 36) COM1 ... COMN) [File Manager Command] Each of the commands COMi will be interpreted as a file manager command without regard to any file manager macros (as defined by the MACRO property of the FILEPKGCOM function, in the Defining New File Manager Commands section). Useful for redefining a built-in file manager command in terms of itself. Some of the "built-in" file manager commands are defined by file manager macros, so interpreting them (or new user-defined file manager commands) with ORIGINAL will fail. ORIGINAL was never intended to be used outside of a file manager command macro. DECLARE: (DECLARE:(DECLARE: (Command) NIL NIL ("17") 36) . FILEPKGCOMS/FLAGS) [File Manager Command] Normally expressions written onto a symbolic file are evaluated when loaded; copied to the compiled file when the symbolic file is compiled (see Chapter 18); and not evaluated at compile time. DECLARE: allows you to override these defaults. FILEPKGCOMS/FLAGS is a list of file manager commands, possibly interspersed with "tags". The output of those file manager commands within FILEPKGCOMS/FLAGS is embedded in a DECLARE: expression, along with any tags that are specified. For example, (DECLARE: EVAL@COMPILE DONTCOPY (FNS ...) (PROP ...)) would produce (DECLARE: EVAL@COMPILE DONTCOPY (DEFINEQ ...) (PUTPROPS ...)). DECLARE: is defined as an nlambda nospread function, which processes its arguments by evaluating or not evaluating each expression depending on the setting of internal state variables. The initial setting is to evaluate, but this can be overridden by specifying the DONTEVAL@LOAD tag. DECLARE: expressions are specially processed by the compiler. For the purposes of compilation, DECLARE: has two principal applications: to specify forms that are to be evaluated at compile time, presumably to affect the compilation, e.g., to set up macros; and/or to indicate which expressions appearing in the symbolic file are not to be copied to the output file. (Normally, expressions are not evaluated and are copied.) Each expression in CDR of a DECLARE: form is either evaluated/not-evaluated and copied/not-copied depending on the settings of two internal state variables, initially set for copy and not-evaluate. These state variables can be reset for the remainder of the expressions in the DECLARE: by means of the tags DONTCOPY, EVAL@COMPILE, etc. The tags are: EVAL@LOAD DOEVAL@LOAD Evaluate the following forms when the file is loaded (unless overridden by DONTEVAL@LOAD). DONTEVAL@LOAD Do not evaluate the following forms when the file is loaded. EVAL@LOADWHEN This tag can be used to provide conditional evaluation. The value of the expression immediately following the tag determines whether or not to evaluate subsequent expressions when loading. ... EVAL@LOADWHEN T ... is equivalent to ... EVAL@LOAD ... COPY DOCOPY When compiling, copy the following forms into the compiled file. DONTCOPY When compiling, do not copy the following forms into the compiled file. Note: If the file manager commands following DONTCOPY include record declarations for datatypes, or records with initialization forms, it is necessary to include a INITRECORDS file manager command (see the prior section) outside of the DONTCOPY form so that the initialization information is copied. For example, if FOO was defined as a datatype, (DECLARE: DONTCOPY (RECORDS FOO)) (INITRECORDS FOO) would copy the data type declaration for FOO, but would not copy the whole record declaration. COPYWHEN When compiling, if the next form evaluates to non-NIL, copy the following forms into the compiled file. EVAL@COMPILE DOEVAL@COMPILE When compiling, evaluate the following forms. DONTEVAL@COMPILE When compiling, do not evaluate the following forms. EVAL@COMPILEWHEN When compiling, if the next form evaluates to non-NIL, evaluate the following forms. FIRST For expressions that are to be copied to the compiled file, the tag FIRST can be used to specify that the following expressions in the DECLARE: are to appear at the front of the compiled file, before anything else except the FILECREATED expressions (see the Symbolic File Format section). For example, (DECLARE: COPY FIRST (P (PRINT MESS1 T)) NOTFIRST (P (PRINT MESS2 T))) will cause (PRINT MESS1 T) to appear first in the compiled file, followed by any functions, then (PRINT MESS2 T). NOTFIRST Reverses the effect of FIRST. The value of DECLARETAGSLST is a list of all the tags used in DECLARE: expressions. If a tag not on this list appears in a DECLARE: file manager command, spelling correction is performed using DECLARETAGSLST as a spelling list. Note that the function LOADCOMP (see the Loading Files section) provides a convenient way of obtaining information from the DECLARE: expressions in a file, without reading in the entire file. This information may be used for compiling other files. (BLOCKS(BLOCKS (Command) NIL NIL ("17") 38) BLOCK1 ... BLOCKN) [File Manager Command] For each BLOCKi, writes a DECLARE: expression which the block compile functions interpret as a block declaration. See Chapter 18. Exporting Definitions When building a large system in Interlisp, it is often the case that there are record definitions, macros and the like that are needed by several different system files when running, analyzing and compiling the source code of the system, but which are not needed for running the compiled code. By using the DECLARE: file manager command with tag DONTCOPY (see the prior section), these definitions can be kept out of the compiled files, and hence out of the system constructed by loading the compiled files files into Interlisp. This saves loading time, space in the resulting system, and whatever other overhead might be incurred by keeping those definitions around, e.g., burden on the record package to consider more possibilities in translating record accesses, or conflicts between system record fields and user record fields. However, if the implementor wants to debug or compile code in the resulting system, the definitions are needed. And even if the definitions had been copied to the compiled files, a similar problem arises if one wants to work on system code in a regular Interlisp environment where none of the system files had been loaded. One could mandate that any definition needed by more than one file in the system should reside on a distinguished file of definitions, to be loaded into any environment where the system files are worked on. Unfortunately, this would keep the definitions away from where they logically belong. The EXPORT mechanism is designed to solve this problem. To use the mechanism, the implementor identifies any definitions needed by files other than the one in which the definitions reside, and wraps the corresponding file manager commands in the EXPORT file manager command. Thereafter, GATHEREXPORTS can be used to make a single file containing all the exports. (EXPORT COM1 ... COMN) [File Manager Command] This command is used for "exporting" definitions. Like COM, each of the commands COM1 ... COMN is interpreted as a file manager command. The commands are also flagged in the file as being "exported" commands, for use with GATHEREXPORTS. (GATHEREXPORTS(GATHEREXPORTS (Function) NIL NIL ("17") 38) FROMFILES TOFILE FLG) [Function] FROMFILES is a list of files containing EXPORT commands. GATHEREXPORTS extracts all the exported commands from those files and produces a loadable file TOFILE containing them. If FLG = EVAL, the expressions are evaluated as they are gathered; i.e., the exports are effectively loaded into the current environment as well as being written to TOFILE. (IMPORTFILE(IMPORTFILE (Function) NIL NIL ("17") 39) FILE RETURNFLG) [Function] If RETURNFLG is NIL, this loads any exported definitions from FILE into the current environment. If RETURNFLG is T, this returns a list of the exported definitions (evaluable expressions) without actually evaluating them. (CHECKIMPORTS(CHECKIMPORTS (Function) NIL NIL ("17") 39) FILES NOASKFLG) [Function] Checks each of the files in FILES to see if any exists in a version newer than the one from which the exports in memory were taken (GATHEREXPORTS and IMPORTFILE note the creation dates of the files involved), or if any file in the list has not had its exports loaded at all. If there are any such files, you are asked for permission to IMPORTFILE each such file. If NOASKFLG is non-NIL, IMPORTFILE is performed without asking. For example, suppose file FOO contains records R1, R2, and R3, macros BAR and BAZ, and constants CON1 and CON2. If the definitions of R1, R2, BAR, and BAZ are needed by files other than FOO, then the file commands for FOO might contain the command (DECLARE: EVAL@COMPILE DONTCOPY (EXPORT (RECORDS R1 R2) (MACROS BAR BAZ)) (RECORDS R3) (CONSTANTS BAZ)) None of the commands inside this DECLARE: would appear on FOO's compiled file, but (GATHEREXPORTS '(FOO) 'MYEXPORTS) would copy the record definitions for R1 and R2 and the macro definitions for BAR and BAZ to the file MYEXPORTS. FileVars In each of the file manager commands described above, if the symbol * follows the command type, the form following the *, i.e., CADDR of the command, is evaluated and its value used in executing the command, e.g., (FNS * (APPEND FNS1 FNS2)). When this form is a symbol, e.g. (FNS * FOOFNS), we say that the variable is a "filevar". Note that (COMS * FORM) provides a way of computing what should be done by MAKEFILE. Example: (SETQ FOOFNS '(FOO1 FOO2 FOO3)) (FOO1 FOO2 FOO3) (SETQ FOOCOMS '((FNS * FOOFNS) (VARS FIE) (PROP MACRO FOO1 FOO2) (P (MOVD 'FOO1 'FIE1))] (MAKEFILE 'FOO) would create a file FOO containing: (FILECREATED "time and date the file was made" . "other information") (PRETTYCOMPRINT FOOCOMS) (RPAQQ FOOCOMS ((FNS * FOOFNS) ...) (RPAQQ FOOFNS (FOO1 FOO3 FOO3)) (DEFINEQ "definitions of FOO1, FOO2, and FOO3") (RPAQQ FIE "value of FIE") (PUTPROPS FOO1 MACRO PROPVALUE) (PUTPROPS FOO2 MACRO PROPVALUE) (MOVD (QUOTE FOO1) (QUOTE FIE1)) STOP For the PROP and IFPROP commands (see the Litatom Properties section), the * follows the property name instead of the command, e.g., (PROP MACRO * FOOMACROS). Also, in the form (* * comment ...), the word comment is not treated as a filevar. Defining New File Manager Commands A file manager command is defined by specifying the values of certain properties. You can specify the various attributes of a file manager command for a new command, or respecify them for an existing command. The following properties are used: MACRO(MACRO (Property) NIL NIL ("17") 40) [File Manager Command Property] Defines how to dump the file manager command. Used by MAKEFILE. Value is a pair (ARGS . COMS). The "arguments" to the file manager command are substituted for ARGS throughout COMS, and the result treated as a list of file manager commands. For example, following (FILEPKGCOM 'FOO 'MACRO '((X Y) . COMS)), the file manager command (FOO A B) will cause A to be substituted for X and B for Y throughout COMS, and then COMS treated as a list of commands. The substitution is carried out by SUBPAIR (see Chapter 3), so that the "argument list" for the macro can also be atomic. For example, if (X . COMS) was used instead of ((X Y) . COMS), then the command (FOO A B) would cause (A B) to be substituted for X throughout COMS. Filevars are evaluated before substitution. For example, if the symbol * follows NAME in the command, CADDR of the command is evaluated substituting in COMS. ADD(ADD (Property) NIL NIL ("17") 40) [File Manager Command Property] Specifies how (if possible) to add an instance of an object of a particular type to a given file manager command. Used by ADDTOFILE. Value is FN, a function of three arguments, COM, a file manager command CAR of which is EQ to COMMANDNAME, NAME, a typed object, and TYPE, its type. FN should return T if it (undoably) adds NAME to COM, NIL if not. If no ADD property is specified, then the default is (1) if (CAR COM) = TYPE and (CADR COM) = *, and (CADDR COM) is a filevar (i.e. a literal atom), add NAME to the value of the filevar, or (2) if (CAR COM) = TYPE and (CADR COM) is not *, add NAME to (CDR COM). Actually, the function is given a fourth argument, NEAR, which if non-NIL, means the function should try to add the item after NEAR. See discussion of ADDTOFILES?, in the Storing Files section. DELETE(DELETE (Property) NIL NIL ("17") 41) [File Manager Command Property] Specifies how (if possible) to delete an instance of an object of a particular type from a given file manager command. Used by DELFROMFILES. Value is FN, a function of three arguments, COM, NAME, and TYPE, same as for ADD. FN should return T if it (undoably) deletes NAME from COM, NIL if not. If no DELETE property is specified, then the default is either (CAR COM) = TYPE and (CADR COM) = *, and (CADDR COM) is a filevar (i.e. a literal atom), and NAME is contained in the value of the filevar, then remove NAME from the filevar, or if (CAR COM) = TYPE and (CADR COM) is not *, and NAME is contained in (CDR COM), then remove NAME from (CDR COM). If FN returns the value of ALL, it means that the command is now "empty", and can be deleted entirely from the command list. CONTENTS(CONTAIN (Property) NIL NIL ("17") 41) [File Manager Command Property] CONTAIN(CONTAIN (Property) NIL NIL ("17") 41) [File Manager Command Property] Determines whether an instance of an object of a given type is contained in a given file manager command. Used by WHEREIS and INFILECOMS?. Value is FN, a function of three arguments, COM, a file manager command CAR of which is EQ to COMMANDNAME, NAME, and TYPE. The interpretation of NAME is as follows: if NAME is NIL, FN should return a list of elements of type TYPE contained in COM. If NAME is T, FN should return T if there are any elements of type TYPE in COM. If NAME is an atom other than T or NIL, return T if NAME of type TYPE is contained in COM. Finally, if NAME is a list, return a list of those elements of type TYPE contained in COM that are also contained in NAME. It is sufficient for the CONTENTS function to simply return the list of items of type TYPE in command COM, i.e. it can in fact ignore the NAME argument. The NAME argument is supplied mainly for those situations where producing the entire list of items involves significantly more computation or creates more storage than simply determining whether a particular item (or any item) of type TYPE is contained in the command. If a CONTENTS property is specified and the corresponding function application returns NIL and (CAR COM) = TYPE, then the operation indicated by NAME is performed on the value of (CADDR COM), if (CADR COM) = *, otherwise on (CDR COM). In other words, by specifying a CONTENTS property that returns NIL, e.g. the function NILL, you specify that a file manager command of name FOO produces objects of file manager type FOO and only objects of type FOO. If the CONTENTS property is not provided, the command is simply expanded according to its MACRO definition, and each command on the resulting command list is then interrogated. If COMMANDNAME is a file manager command that is used frequently, its expansion by the various parts of the system that need to interrogate files can result in a large number of CONSes and garbage collections. By informing the file manager as to what this command actually does and does not produce via the CONTENTS property, this expansion is avoided. For example, suppose you have a file manager command called GRAMMARS which dumps various property lists but no functions. The file manager could ignore this command when seeking information about FNS. The function FILEPKGCOM is used to define new file manager commands, or to change the properties of existing commands. It is possible to redefine the attributes of system file manager commands, such as FNS or PROPS, and to cause unpredictable results. (FILEPKGCOM(FILEPKGCOM (Function) NIL NIL ("17") 42) COMMANDNAME PROP1 VAL1 ... PROPN VALN) [NoSpread Function] Nospread function for defining new file manager commands, or changing properties of existing file manager commands. PROPi is one of of the property names described above; VALi is the value to be given that property of the file manager command COMMANDNAME. Returns COMMANDNAME. (FILEPKGCOM COMMANDNAME PROP) returns the value of the property PROP, without changing it. (FILEPKGCOM COMMANDNAME) returns a property list of all of the defined properties of COMMANDNAME, using the property names as keys. Specifying TYPE as the symbol COM can be used to define one file manager command as a synonym of another. For example, (FILEPKGCOM 'INITVARIABLES 'COM 'INITVARS) defines INITVARIABLES as a synonym for the file manager command INITVARS. Functions for Manipulating File Command Lists 1 The following functions may be used to manipulate filecoms. The argument COMS does not have to correspond to the filecoms for some file. For example, COMS can be the list of commands generated as a result of expanding a user-defined file manager command. The following functions will accept a file manager command as a valid value for their TYPE argument, even if it does not have a corresponding file manager type. User-defined file manager commands are expanded as necessary. (INFILECOMS?(INFILECOMS? (Function) NIL NIL ("17") 42) NAME TYPE COMS) [Function] COMS is a list of file manager commands, or a variable whose value is a list of file manager commands. TYPE is a file manager type. INFILECOMS? returns T if NAME of type TYPE is "contained" in COMS. If NAME = NIL, INFILECOMS? returns a list of all elements of type TYPE. If NAME = T, INFILECOMS? returns T if there are any elements of type TYPE in COMS. (ADDTOFILE(INFILECOMS? (Function) NIL NIL ("17") 43) NAME TYPE FILE NEAR LISTNAME) [Function] Adds NAME of type TYPE to the file manager commands for FILE. If NEAR is given and it is the name of an item of type TYPE already on FILE, then NAME is added to the command that dumps NEAR. If LISTNAME is given and is the name of a list of items of TYPE items on FILE, then NAME is added to that list. Uses ADDTOCOMS and MAKENEWCOM. Returns FILE. ADDTOFILE is undoable. (DELFROMFILES(DELFROMFILES (Function) NIL NIL ("17") 43) NAME TYPE FILES) [Function] Deletes all instances of NAME of type TYPE from the filecoms for each of the files on FILES. If FILES is a non-NIL symbol, (LIST FILES) is used. FILES = NIL defaults to FILELST. Returns a list of files from which NAME was actually removed. Uses DELFROMCOMS. DELFROMFILES is undoable. Deleting a function will also remove the function from any BLOCKS declarations in the filecoms. (ADDTOCOMS(ADDTOCOMS (Function) NIL NIL ("17") 43) COMS NAME TYPE NEAR LISTNAME) [Function] Adds NAME as a TYPE to COMS, a list of file manager commands or a variable whose value is a list of file manager commands. Returns NIL if ADDTOCOMS was unable to find a command appropriate for adding NAME to COMS. NEAR and LISTNAME are described in the discussion of ADDTOFILE. ADDTOCOMS is undoable. The exact algorithm for adding commands depends the particular command itself. See discussion of the ADD property, in the description of FILEPKGCOM. ADDTOCOMS will not attempt to add an item to any command which is inside of a DECLARE: unless you specified a specific name via the LISTNAME or NEAR option of ADDTOFILES?. (DELFROMCOMS(DELFROMCOMS (Function) NIL NIL ("17") 43) COMS NAME TYPE) [Function] Deletes NAME as a TYPE from COMS. Returns NIL if DELFROMCOMS was unable to modify COMS to delete NAME. DELFROMCOMS is undoable. (MAKENEWCOM(MAKENEWCOM (Function) NIL NIL ("17") 43) NAME TYPE) [Function] Returns a file manager command for dumping NAME of type TYPE. Uses the procedure described in the discussion of NEWCOM, in the Defining New File Manager Types section. (MOVETOFILE(MOVETOFILE (Function) NIL NIL ("17") 43) TOFILE NAME TYPE FROMFILE) [Function] Moves the definition of NAME as a TYPE from FROMFILE to TOFILE by modifying the file commands in the appropriate way (with DELFROMFILES and ADDTOFILE). Note that if FROMFILE is specified, the definition will be retrieved from that file, even if there is another definition currently in your environment. (FILECOMSLST(FILECOMSLST (Function) NIL NIL ("17") 43) FILE TYPE) [Function] Returns a list of all objects of type TYPE in FILE. (FILEFNSLST(FILEFNSLST (Function) NIL NIL ("17") 44) FILE) [Function] Same as (FILECOMSLST FILE 'FNS). (FILECOMS(FILECOMS (Function) NIL NIL ("17") 44) FILE TYPE) [Function] Returns (PACK* FILE (OR TYPE 'COMS)). Note that (FILECOMS 'FOO) returns the symbol FOOCOMS, not the value of FOOCOMS. (SMASHFILECOMS(SMASHFILECOMS (Function) NIL NIL ("17") 44) FILE) [Function] Maps down (FILECOMSLST FILE 'FILEVARS) and sets to NOBIND all filevars (see the FileVars section), i.e., any variable used in a command of the form (COMMAND * VARIABLE). Also sets (FILECOMS FILE) to NOBIND. Returns FILE. Symbolic File Format 1 The file manager manipulates symbolic files in a particular format. This format is defined so that the information in the file is easily readable when the file is listed, as well as being easily manipulated by the file manager functions. In general, there is no reason for you to manually change the contents of a symbolic file. However, to allow you to extend the file manager, this section describes some of the functions used to write symbolic files, and other matters related to their format. (PRETTYDEF(PRETTYDEF (Function) NIL NIL ("17") 44) PRTTYFNS PRTTYFILE PRTTYCOMS REPRINTFNS SOURCEFILE CHANGES) [Function] Writes a symbolic file in PRETTYPRINT format for loading, using FILERDTBL as its read table. PRETTYDEF returns the name of the symbolic file that was created. PRETTYDEF operates under a RESETLST (see Chapter 14), so if an error occurs, or a Control-D is typed, all files that PRETTYDEF has opened will be closed, the (partially complete) file being written will be deleted, and any undoable operations executed will be undone. The RESETLST also means that any RESETSAVEs executed in the file manager commands will also be protected. PRTTYFNS is an optional list of function names. It is equivalent to including (FNS * PRTTYFNS) in the file manager commands in PRTTYCOMS. PRTTYFNS is an anachronism from when PRETTYDEF did not use a list of file manager commands, and should be specified as NIL. PRTTYFILE is the name of the file on which the output is to be written. PRTTYFILE has to be a symbnol. If PRTTYFILE = NIL, the primary output file is used. PRTTYFILE is opened if not already open, and it becomes the primary output file. PRTTYFILE is closed at end of PRETTYDEF, and the primary output file is restored. PRTTYCOMS is a list of file manager commands interpreted as described in the File Manager Commands section. If PRTTYCOMS is atomic, its top level value is used and an RPAQQ is written which will set that atom to the list of commands when the file is subsequently loaded. A PRETTYCOMPRINT expression (see below) will also be written which informs you of the named atom or list of commands when the file is subsequently loaded. In addition, if any of the functions in the file are nlambda functions, PRETTYDEF will automatically print a DECLARE: expression suitable for informing the compiler about these functions, in case you recompile the file without having first loaded the nlambda functions (see Chapter 18). REPRINTFNS and SOURCEFILE are for use in conjunction with remaking a file (see the Remaking a Symbolic File section). REPRINTFNS can be a list of functions to be prettyprinted, or EXPRS, meaning prettyprint all functions with EXPR definitions, or ALL meaning prettyprint all functions either defined as EXPRs, or with EXPR properties. Note that doing a remake with REPRINTFNS = NIL makes sense if there have been changes in the file, but not to any of the functions, e.g., changes to variables or property lists. SOURCEFILE is the name of the file from which to copy the definitions for those functions that are not going to be prettyprinted, i.e., those not specified by REPRINTFNS. SOURCEFILE = T means to use most recent version (i.e., highest number) of PRTTYFILE, the second argument to PRETTYDEF. If SOURCEFILE cannot be found, PRETTYDEF prints the message "FILE NOT FOUND, SO IT WILL BE WRITTEN ANEW", and proceeds as it does when REPRINTFNS and SOURCEFILE are both NIL. PRETTYDEF calls PRETTYPRINT with its second argument PRETTYDEFLG = T, so whenever PRETTYPRINT starts a new function, it prints (on the terminal) the name of that function if more than 30 seconds (real time) have elapsed since the last time it printed the name of a function. Note that normally if PRETTYPRINT is given a symbol which is not defined as a function but is known to be on one of the files noticed by the file manager, PRETTYPRINT will load in the definition (using LOADFNS) and print it. This is not done when PRETTYPRINT is called from PRETTYDEF. In Medley the SYSPRETTYFLG is ignored in the Interlisp exec. (PRINTFNS(PRINTFNS (Function) NIL NIL ("17") 45) X) [Function] X is a list of functions. PRINTFNS prettyprints a DEFINEQ epression that defines the functions to the primary output stream using the primary read table. Used by PRETTYDEF to implement the FNS file manager command. (PRINTDATE(PRINTDATE (Function) NIL NIL ("17") 45) FILE CHANGES) [Function] Prints the FILECREATED expression at beginning of PRETTYDEF files. CHANGES used by the file manager. (FILECREATED(FILECREATED (Function) NIL NIL ("17") 45) X) [NLambda NoSpread Function] Prints a message (using LISPXPRINT) followed by the time and date the file was made, which is (CAR X). The message is the value of PRETTYHEADER, initially "FILE CREATED". If PRETTYHEADER = NIL, nothing is printed. (CDR X) contains information about the file, e.g., full name, address of file map, list of changed items, etc. FILECREATED also stores the time and date the file was made on the property list of the file under the property FILEDATES and performs other initialization for the file manager. (PRETTYCOMPRINT(PRETTYCOMPRINT (Function) NIL NIL ("17") 46) X) [NLambda Function] Prints X (unevaluated) using LISPXPRINT, unless PRETTYHEADER = NIL. PRETTYHEADER (PRETTYHEADER% (Variable) NIL NIL ("17") 46) [Variable] Value is the message printed by FILECREATED. PRETTYHEADER is initially "FILE CREATED". If PRETTYHEADER = NIL, neither FILECREATED nor PRETTYCOMPRINT will print anything. Thus, setting PRETTYHEADER to NIL will result in "silent loads". PRETTYHEADER is reset to NIL during greeting (see Chapter 12). (FILECHANGES(FILECHANGES (Function) NIL NIL ("17") 46) FILE TYPE) [Function] Returns a list of the changed objects of file manager type TYPE from the FILECREATED expression of FILE. If TYPE = NIL, returns an alist of all of the changes, with the file manager types as the CARs of the elements.. (FILEDATE(FILEDATE (Function) NIL NIL ("17") 46) FILE) [Function] Returns the file date contained in the FILECREATED expression of FILE. (LISPSOURCEFILEP(LISPSOURCEFILEP (Function) NIL NIL ("17") 46) FILE) [Function] Returns a non-NIL value if FILE is in file manager format and has a file map, NIL otherwise. Copyright Notices The system has a facility for automatically printing a copyright notice near the front of files, right after the FILECREATED expression, specifying the years it was edited and the copyright owner. The format of the copyright notice is: (* Copyright (c) 1981 by Foo Bars Corporation) Once a file has a copyright notice then every version will have a new copyright notice inserted into the file without your intervention. (The copyright information necessary to keep the copyright up to date is stored at the end of the file.). Any year the file has been edited is considered a "copyright year" and therefore kept with the copyright information. For example, if a file has been edited in 1981, 1982, and 1984, then the copyright notice would look like: (* Copyright (c) 1981,1982,1984 by Foo Bars Corporation) When a file is made, if it has no copyright information, the system will ask you to specify the copyright owner (if COPYRIGHTFLG = T). You may specify one of the names from COPYRIGHTOWNERS, or give one of the following responses: f Type a left-square-bracket. The system will then prompt for an arbitrary string which will be used as the owner-string f Type a right-square-bracket, which specifies that you really do not want a copyright notice. f Type "NONE" which specifies that this file should never have a copyright notice. For example, if COPYRIGHTOWNERS has the value ((BBN "Bolt Beranek and Newman Inc.") (XEROX "Xerox Corporation")) then for a new file FOO the following interaction will take place: Do you want to Copyright FOO? Yes Copyright owner: (user typed ?) one of: BBN - Bolt Beranek and Newman Inc. XEROX - Xerox Corporation NONE - no copyright ever for this file [ - new copyright owner -- type one line of text ] - no copyright notice for this file now Copyright owner: BBN Then "Foo Bars Corporation" in the above copyright notice example would have been "Bolt Beranek and Newman Inc." The following variables control the operation of the copyright facility: COPYRIGHTFLG(COPYRIGHTFLG (Variable) NIL NIL ("17") 47) [Variable] The value of COPYRIGHTFLG determines whether copyright information is maintained in files. Its value is interpreted as follows: NIL The system will preserve old copyright information, but will not ask you about copyrighting new files. This is the default value of COPYRIGHTFLG. T When a file is made, if it has no copyright information, the system will ask you to specify the copyright owner. NEVER The system will neither prompt for new copyright information nor preserve old copyright information. DEFAULT The value of DEFAULTCOPYRIGHTOWNER (below) is used for putting copyright information in files that don't have any other copyright. The prompt "Copyright owner for file xx:" will still be printed, but the default will be filled in immediately. COPYRIGHTOWNERS(COPYRIGHTOWNERS (Variable) NIL NIL ("17") 48) [Variable] COPYRIGHTOWNERS is a list of entries of the form (KEY OWNERSTRING), where KEY is used as a response to ASKUSER and OWNERSTRING is a string which is the full identification of the owner. DEFAULTCOPYRIGHTOWNER(DEFAULTCOPYRIGHTOWNER (Variable) NIL NIL ("17") 48) [Variable] If you do not respond in DWIMWAIT seconds to the copyright query, the value of DEFAULTCOPYRIGHTOWNER is used. Functions Used Within Source Files The following functions are normally only used within symbolic files, to set variable values, property values, etc. Most of these have special behavior depending on file manager variables. (RPAQ(RPAQ (Function) NIL NIL ("17") 48) VAR VALUE) [NLambda Function] An nlambda function like SETQ that sets the top level binding of VAR (unevaluated) to VALUE. (RPAQQ(RPAQQ (Function) NIL NIL ("17") 48) VAR VALUE) [NLambda Function] An nlambda function like SETQQ that sets the top level binding of VAR (unevaluated) to VALUE (unevaluated). (RPAQ?(RPAQ? (Function) NIL NIL ("17") 48) VAR VALUE) [NLambda Function] Similar to RPAQ, except that it does nothing if VAR already has a top level value other than NOBIND. Returns VALUE if VAR is reset, otherwise NIL. RPAQ, RPAQQ, and RPAQ? generate errors if X is not a symbol. All are affected by the value of DFNFLG (see Chapter 10). If DFNFLG = ALLPROP (and the value of VAR is other than NOBIND), instead of setting X, the corresponding value is stored on the property list of VAR under the property VALUE. All are undoable. (ADDTOVAR(ADDTOVAR (Function) NIL NIL ("17") 48) VAR X1 X2 ... XN) [NLambda NoSpread Function] Each Xi that is not a member of the value of VAR is added to it, i.e. after ADDTOVAR completes, the value of VAR will be (UNION (LIST X1 X2 ... XN) VAR). ADDTOVAR is used by PRETTYDEF for implementing the ADDVARS command. It performs some file manager related operations, i.e. "notices" that VAR has been changed. Returns the atom VAR (not the value of VAR). (APPENDTOVAR VAR X1 X2 ... XN) [NLambda NoSpread Function] Similar to ADDTOVAR, except that the values are added to the end tof the list, rather than at the beginning. (PUTPROPS(PUTPROPS (Function) NIL NIL ("17") 49) ATM PROP1 VAL1 ... PROPN VALN) [NLambda NoSpread Function] Nlambda nospread version of PUTPROP (none of the arguments are evaluated). For i = 1...N, puts property PROPi, value VALi, on the property list of ATM. Performs some file manager related operations, i.e., "notices" that the corresponding properties have been changed. (SAVEPUT(SAVEPUT (Function) NIL NIL ("17") 49) ATM PROP VAL) [Function] Same as PUTPROP, but marks the corresponding property value as having been changed (used by the file manager). File Maps A file map is a data structure which contains a symbolic 'map' of the contents of a file. Currently, this consists of the begin and end byte address (see GETFILEPTR, in Chapter 25) for each DEFINEQ expression in the file, the begin and end address for each function definition within the DEFINEQ, and the begin and end address for each compiled function. MAKEFILE, PRETTYDEF, LOADFNS, RECOMPILE, and numerous other system functions depend heavily on the file map for efficient operation. For example, the file map enables LOADFNS to load selected function definitions simply by setting the file pointer to the corresponding address using SETFILEPTR, and then performing a single READ. Similarly, the file map is heavily used by the "remake" option of MAKEFILE (see the Remaking a Symbolic File section): those function definitions that have been changed since the previous version are prettyprinted; the rest are simply copied from the old file to the new one, resulting in a considerable speedup. Whenever a file is written by MAKEFILE, a file map for the new file is built. Building the map in this case essentially comes for free, since it requires only reading the current file pointer before and after each definition is written or copied. However, building the map does require that PRETTYPRINT know that it is printing a DEFINEQ expression. For this reason, you should never print a DEFINEQ expression onto a file yourself, but should instead always use the FNS file manager command (see the Functions and Macros section). The file map is stored on the property list of the root name of the file, under the property FILEMAP. In addition, MAKEFILE writes the file map on the file itself. For cosmetic reasons, the file map is written as the last expression in the file. However, the address of the file map in the file is (over)written into the FILECREATED expression that appears at the beginning of the file so that the file map can be rapidly accessed without having to scan the entire file. In most cases, LOAD and LOADFNS do not have to build the file map at all, since a file map will usually appear in the corresponding file, unless the file was written with BUILDMAPFLG = NIL, or was written outside of Interlisp. Currently, file maps for compiled files are not written onto the files themselves. However, LOAD and LOADFNS will build maps for a compiled file when it is loaded, and store it on the property FILEMAP. Similary, LOADFNS will obtain and use the file map for a compiled file, when available. The use and creation of file maps is controlled by the following variables: BUILDMAPFLG(BUILDMAPFLG (Variable) NIL NIL ("17") 50) [Variable] Whenever a file is read by LOAD or LOADFNS, or written by MAKEFILE, a file map is automatically built unless BUILDMAPFLG = NIL. (BUILDMAPFLG is initially T.) While building the map will not help the first reference to a file, it will help in future references. For example, if you perform (LOADFROM 'FOO) where FOO does not contain a file map, the LOADFROM will be (slightly) slower than if FOO did contain a file map, but subsequent calls to LOADFNS for this version of FOO will be able to use the map that was built as the result of the LOADFROM, since it will be stored on FOO's FILEMAP property. USEMAPFLG(USEMAPFLG (Variable) NIL NIL ("17") 50) [Variable] If USEMAPFLG = T (the initial setting), the functions that use file maps will first check the FILEMAP property to see if a file map for this file was previously obtained or built. If not, the first expression on the file is checked to see if it is a FILECREATED expression that also contains the address of a file map. If the file map is not on the FILEMAP property or in the file, a file map will be built (unless BUILDMAPFLG = NIL). If USEMAPFLG = NIL, the FILEMAP property and the file will not be checked for the file map. This allows you to recover in those cases where the file and its map for some reason do not agree. For example, if you use a text editor to change a symbolic file that contains a map (not recommended), inserting or deleting just one character will throw that map off. The functions which use file maps contain various integrity checks to enable them to detect that something is wrong, and to generate the error FILEMAP DOES NOT AGREE WITH CONTENTS OF FILE. In such cases, you can set USEMAPFLG to NIL, causing the map contained in the file to be ignored, and then reexecute the operation. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "17-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "17-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "17-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "17-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "17-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "17-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))2,HH206H/HH55550< +H,HH56``5552HH5lxx52HH30<6T/55,xx,xx,xx0T56TT2xx5555-T,306T2306 +T2<<306 +T2@ PAGEHEADINGLEFTBACKT,ll,ll,ll,ll3(T,<<,,,F PAGEHEADING VERSOHEAD, PALATINO PALATINO PALATINO PALATINO TITAN TITAN TITAN TITAN TITAN MODERN +MODERN +MODERN +' 1  +& IM.CHAP.GETFNMODERN + +  HRULE.GETFNMODERN + .  3.0M00 0X      J + * * * + * * * 0. 3  J 9  + + * *" *% * * * * * 0% \ + * 0      D + * * * * 05 ]0    i h N  + + * * * * * * * * * * * * 2 0l)0 + K&   _ 0   A   M Q01 + % +(IM.INDEX.GETFNTITAN   , +  ; +   .[, + HRULE.GETFNMODERN + /  0+     (   V 0 [ ; + :   F B 0 +   +  , +   R +  ' + +  ? +  /Y   0T   B   ^ + ; + 02    % "IM.INDEX.GETFNMODERN +   " H   @    = % #IM.INDEX.GETFNMODERN +    9    - < #  % %IM.INDEX.GETFNMODERN +   +9 5    + J`  + "  '  P         G   7L D $i/   &m -,     +   (  6     .E + .X +  +  *B  # &IM.INDEX.GETFNTITAN     # &IM.INDEX.GETFNTITAN     "G ? # 'IM.INDEX.GETFNPALATINO    B @ O `# &IM.INDEX.GETFNPALATINO   E L    # 'IM.INDEX.GETFNPALATINO    @   4  \ " T# 'IM.INDEX.GETFNPALATINO      1^/  T   m" B  m c 4  F C   .   F ( p     A    X  8  G               ('= W # +  ;$G $ +      A $ .  , + HRULE.GETFNMODERN + # &IM.INDEX.GETFNPALATINO  + +  '+   W #I   5 ! > +           S  +          G "    R              -       +  B   7       l          5  .  + +   '    '   ? #   $ 7   2 G   0   =    $  n$      . $      # (IM.INDEX.GETFNPALATINO        "   * ,# &IM.INDEX.GETFNPALATINO   #  + n          7  rU # %IM.INDEX.GETFNTITAN      (# *IM.INDEX.GETFNTITAN         e w   +  S 0 g +!  3 ;     Z  %       G v   #    I , 8  +   + ' +  2 + $ & 0   7    # +IM.INDEX.GETFNTITAN       H . %  B   1  # &IM.INDEX.GETFNTITAN     *, M  ) + 3%    "  0 3   , + HRULE.GETFNMODERN + "7 |" &         s E  "  + + 6  +l + ] {   +!  L J "A +j / B $ M" o    +               q"M,+ + HRULE.GETFNMODERN + " "" ~ \" @  Z ;  * >  +  g, + HRULE.GETFNMODERN + "   A   ` Q"A M    F  7" H# ,IM.INDEX.GETFNPALATINO       %"  " + &     2   4 6 } * # .IM.INDEX.GETFNTITAN    ,  # -IM.INDEX.GETFNTITAN   ' +  =   /    n , e     .  0K 1  | $    y  +  e D    , + HRULE.GETFNMODERN + /(    +  6 9    \05 # &IM.INDEX.GETFNTITAN  )  F/# %IM.INDEX.GETFN %    27 M "  |  L D     # *IM.INDEX.GETFNMODERN +      + # (IM.INDEX.GETFNMODERN +    #&IM.INDEX.GETFNMODERN +   z / 8  J 7    0; #&IM.INDEX.GETFNMODERN + F # ,IM.INDEX.GETFNMODERN + >  $#     K , + HRULE.GETFNMODERN + / O    ( Q - 0   * U  +  %          >c  + X a0% # *IM.INDEX.GETFNTITAN      ! , + HRULE.GETFNMODERN + /h(0# +IM.INDEX.GETFNTITAN    N /P#.IM.INDEX.GETFNTITAN  > #.IM.INDEX.GETFNTITAN  dm  C        + " +   ! R    ?  #7IM.INDEX.GETFNMODERN + 0 # 3IM.INDEX.GETFNMODERN + D J ' #.IM.INDEX.GETFNMODERN + I R #  5IM.INDEX.GETFN  !  +  T + ; Q 3 #-IM.INDEX.GETFNMODERN +  #0IM.INDEX.GETFNMODERN + 3 #+IM.INDEX.GETFNMODERN + $ #0IM.INDEX.GETFNMODERN + P # 3IM.INDEX.GETFNMODERN + H   #.IM.INDEX.GETFNMODERN + 2 #-IM.INDEX.GETFNMODERN +    l "   2 _& m     . H #/IM.INDEX.GETFNMODERN + 3 # 1IM.INDEX.GETFNMODERN + * # 1IM.INDEX.GETFNMODERN + 6 # +2IM.INDEX.GETFNMODERN + 1 #,IM.INDEX.GETFNMODERN + ) , + " (  # <>:  E    a)* @     D    :b 5 H   5- <# %IM.INDEX.GETFNTITAN    + C     ,  F   W  V t     )      O % 9  < !  F # %IM.INDEX.GETFNMODERN + +    +        ' J +  *      @ # %IM.INDEX.GETFNMODERN +    "+  %  &'m         5# &IM.INDEX.GETFNMODERN +         # &IM.INDEX.GETFNMODERN +  % + *  >         7     # %IM.INDEX.GETFNMODERN +   # &IM.INDEX.GETFNMODERN +  & ! # &IM.INDEX.GETFNMODERN +           # &IM.INDEX.GETFNMODERN + +   + +    '$   "       + ^  / n   # (IM.INDEX.GETFNMODERN +  #0   *  '    N  -  2 # &IM.INDEX.GETFNMODERN +      $ C   Y # ,IM.INDEX.GETFNMODERN +   !   Q  @!_  FX   2    =  # %IM.INDEX.GETFNPALATINO       '     # $ +    # &IM.INDEX.GETFNMODERN +        22     * -    # *IM.INDEX.GETFNMODERN +   ,3  + /g  h 0&I#7IM.INDEX.GETFNMODERN + (0   R  . + 9  ) X!  :    B #8IM.INDEX.GETFNMODERN +   d '    # +;IM.INDEX.GETFNMODERN +  i  # +;IM.INDEX.GETFNMODERN + % f 2   #7IM.INDEX.GETFNMODERN + ( + +   @ #7IM.INDEX.GETFNMODERN + (  " Z #8IM.INDEX.GETFNMODERN + '& ! + @ #7IM.INDEX.GETFNMODERN + &"' A #7IM.INDEX.GETFNMODERN + 'LE   $  & 8    # <IM.INDEX.GETFNMODERN + . _ F +  q&     #  <IM.INDEX.GETFN . # <IM.INDEX.GETFNMODERN + . # <IM.INDEX.GETFNMODERN + Q    /    # *IM.INDEX.GETFNMODERN +  o +5  #  =$  T  (  , + HRULE.GETFNMODERN + /@ V   0   Q    ! 0&&   0,    1 ;       o 0w + } ~    -   0q   + ;   0# +(IM.INDEX.GETFNTITAN    \ + H)    + # !IM.INDEX.GETFNTITAN !IM.INDEX.GETFNMODERN +    -  / - 8 3 # $IM.INDEX.GETFNMODERN +  n D  F # $IM.INDEX.GETFNTITAN     c + # $IM.INDEX.GETFNTITAN      S J  + # "IM.INDEX.GETFNTITAN  +   O  !E%   &       # &IM.INDEX.GETFNTITAN    W "   "5 5     +   #   # %IM.INDEX.GETFNTITAN             M"3(   & +      +    # +(IM.INDEX.GETFNTITAN          B S # &IM.INDEX.GETFNTITAN  +   "  6  # *IM.INDEX.GETFN  +    > " ` p 3   # $IM.INDEX.GETFNTITAN      ; +   h "    +  f # &IM.INDEX.GETFNTITAN    ! 'IM.INDEX.GETFNTITAN    ! +(IM.INDEX.GETFNTITAN   =   # 'IM.INDEX.GETFNTITAN    Y r 2 7   /  + # "IM.INDEX.GETFNTITAN      (L B D" /     \ # $IM.INDEX.GETFNTITAN             + + # #IM.INDEX.GETFNTITAN          $ +  )     # + # %IM.INDEX.GETFNTITAN   w # )IM.INDEX.GETFNTITAN     # )IM.INDEX.GETFNTITAN       # &IM.INDEX.GETFNTITAN    # 'IM.INDEX.GETFNTITAN    { # +IM.INDEX.GETFNTITAN       # -IM.INDEX.GETFNTITAN    # 'IM.INDEX.GETFNTITAN    # #IM.INDEX.GETFNTITAN   f '   \ # )IM.INDEX.GETFNTITAN    :   # IM.INDEX.GETFN    8 # IM.INDEX.GETFNTITAN    J # IM.INDEX.GETFNTITAN      ' # "IM.INDEX.GETFNTITAN    * # &IM.INDEX.GETFNMODERN +  l  +   G  + # &IM.INDEX.GETFNMODERN + +     (z C5 H   X =         K   >        ?  I0 o = I $  + 3 3 3    .  5 3 E > R C     G    # 6 >  ] s # $IM.INDEX.GETFNMODERN +     `  + /4  0 .0 $ ?#   8    # ,IM.INDEX.GETFNTITAN       R   # +)IM.INDEX.GETFNMODERN + +     +#  k # +IM.INDEX.GETFNMODERN + +   c  +  +   +  /              ) ( ( ( ( 0!  ! '      + /D 2  Q $ 6    0 ! (  ( ( ( (  0    ( ($ ( ( +    (  (  (  (! ( 0  4 9   " + /#$IM.INDEX.GETFNMODERN + !7    C U!         # a       +  - #"IM.INDEX.GETFNTITAN  !{ !       3      )(       3 6  #%IM.INDEX.GETFNTITAN    !    3      )7          ^ #&IM.INDEX.GETFNTITAN   !&IM.INDEX.GETFNTITAN   s  !    *      4 5 ! J   #      #   2 '   K R  ~ c   /  +   &# +)IM.INDEX.GETFNMODERN +   t2D      #   = $  W*  +  ,. + HRULE.GETFNMODERN + /JAe0V;-M#  *IM.INDEX.GETFNTITAN   e     (    # *IM.INDEX.GETFNTITAN    "0 $0 +  +   # +IM.INDEX.GETFNMODERN +   , +     &  ; # (IM.INDEX.GETFNMODERN +  i  5$  f ! +  E .    # *IM.INDEX.GETFNTITAN         # )IM.INDEX.GETFNTITAN   + 4 1 # +)IM.INDEX.GETFNTITAN   =    # *IM.INDEX.GETFNTITAN  +  & # +)IM.INDEX.GETFNTITAN      # 'IM.INDEX.GETFNTITAN          # ,IM.INDEX.GETFNTITAN    +  +  [       , + HRULE.GETFNMODERN + /# (IM.INDEX.GETFNTITAN    + +  +    9  R   @G  !  I  @   % I  + g / f    + +^ +4 )  5  ,   +Y9 +  <   +  (  + + +       z $ '    "# 'IM.INDEX.GETFNTITAN     j   # (IM.INDEX.GETFNTITAN       # *IM.INDEX.GETFNTITAN    + <       i e 8 # -IM.INDEX.GETFNMODERN +   +   #  -IM.INDEX.GETFN      +  %  !  " # *IM.INDEX.GETFNMODERN +  ; +  M # 'IM.INDEX.GETFNPALATINO  ' # .IM.INDEX.GETFNPALATINO   +/  + /q q-/ 00-9 0t  * *z_ F0 +& * 0 ,+" *  * *# * *' *1 ** * * 0q0I# +IM.INDEX.GETFNMODERN +   h   r f  m F #.IM.INDEX.GETFNTITAN   "     = #4IM.INDEX.GETFNTITAN   .  " + /# #IM.INDEX.GETFNMODERN +   $ # $IM.INDEX.GETFNMODERN +   $ # $IM.INDEX.GETFNMODERN +    !*  /   4   < # 'IM.INDEX.GETFNTITAN   &         Q% #     Z# 'IM.INDEX.GETFNTITAN    - v # &IM.INDEX.GETFNTITAN    _  + / +  [ <0     m +  D 0   8 D >0]  7    '0<  U  G0L# *IM.INDEX.GETFNMODERN +     +     " # 1  A   + # (IM.INDEX.GETFNMODERN + +  N  Y ;    )   Yhz \ No newline at end of file diff --git a/docs/turpin-irm/IRM-2/18-COMPILER.TEDIT b/docs/turpin-irm/IRM-2/18-COMPILER.TEDIT new file mode 100644 index 00000000..5acaf3bb --- /dev/null +++ b/docs/turpin-irm/IRM-2/18-COMPILER.TEDIT @@ -0,0 +1,116 @@ +INTERLISP-D REFERENCE MANUAL COMPILER "18"18. COMPILER 2 The compiler is contained in the standard Medley system. It may be used to compile functions defined in Medley, or to compile definitions stored in a file. The resulting compiled code may be stored as it is compiled, so as to be available for immediate use, or it may be written onto a file for subsequent loading. The most common way to use the compiler is to use one of the file package functions, such as MAKEFILE (Chapter 17), which automatically updates source files, and produces compiled versions. However, it is also possible to compile individual functions defined in Medley, by directly calling the compiler using functions such as COMPILE. No matter how the compiler is called, the function COMPSET is called which asks you certain questions concerning the compilation. (COMPSET sets the free variables LAPFLG, STRF, SVFLG, LCFIL and LSTFIL which determine various modes of operation.) Those that can be answered "yes" or "no" can be answered with YES, Y, or T for "yes"; and NO, N, or NIL for "no". The questions are: LISTING? This asks whether to generate a listing of the compiled code. The LAP and machine code are usually not of interest but can be helpful in debugging macros. Possible answers are: 1 Prints output of pass 1, the LAP macro code 2 Prints output of pass 2, the machine code YES Prints output of both passes NO Prints no listings The variable LAPFLG is set to the answer. FILE: This question (which only appears if the answer to LISTING? is affirmative) ask where the compiled code listing(s) should be written. Answering T will print the listings at the terminal. The variable LSTFIL is set to the answer. REDEFINE? This question asks whether the functions compiled should be redefined to their compiled definitions. If this is answered YES, the compiled code is stored and the function definition changed, otherwise the function definition remains unchanged. The compiler does not respect the value of DFNFLG (Chapter 10) when it redefines functions to their compiled definitions. Therefore, if you set DFNFLG to PROP to completely avoid inadvertantly redefining something in your running system, you must not answer YES to this question. The variable STRF is set to T (if this is answered YES) or NIL. SAVE EXPRS? This question asks whether the original defining EXPRs of functions should be saved. If answered YES, then before redefining a function to its compiled definition, the EXPR definition is saved on the property list of the function name. Otherwise they are discarded. It is very useful to save the EXPR definitions, just in case the compiled function needs to be changed. The editing functions will retrieve this saved definition if it exists, rather than reading from a source file. The variable SVFLG is set to T (if this is answered YES) or NIL. OUTPUT FILE? This question asks whether (and where) the compiled definitions should be written into a file for later loading. If you answer with the name of a file, that file will be used. If you answer Y or YES, you will be asked the name of the file. If the file named is already open, it will continue to be used. If you answer T or TTY:, the output will be typed on the teletype (not particularly useful). If you answer N, NO, or NIL, output will not be done. The variable LCFIL is set to the name of the file. To make answering these questions easier, there are four other possible answers to the LISTING? question, which specify common compiling modes: S Same as last setting. Uses the same answers to compiler questions as given for the last compilation. F Compile to File, without redefining functions. ST STore new definitions, saving EXPR definitions. STF STore new definitions; Forget EXPR definitions. Implicit in these answers are the answers to the questions on disposition of compiled code and EXPR definitions, so the questions REDEFINE? and SAVE EXPRS? would not be asked if these answers were given. OUTPUT FILE? would still be asked, however. For example: COMPILE((FACT FACT1 FACT2)) LISTING? ST OUTPUT FILE? FACT.DCOM (FACT COMPILING) . . (FACT REDEFINED) . . (FACT2 REDEFINED) (FACT FACT1 FACT2) This process caused the functions FACT, FACT1, and FACT2 to be compiled, redefined, and the compiled definitions also written on the file FACT.DCOM for subsequent loading. Compiler(COMPILER NIL Compiler NIL ("18") 2 SUBNAME PRINTOUT SUBTEXT printout) Printout 1 In Medley, for each function FN compiled, whether by TCOMPL, RECOMPILE, or COMPILE, the compiler prints: (FN (ARG1 ... ARGN) (uses: VAR1 ... VARN) (calls: FN1 ... FNN)) The message is printed at the beginning of the second pass of the compilation of FN. (ARG1 ... ARGN) is the list of arguments to FN; following uses: are the free variables referenced or set in FN (not including global variables); following calls: are the undefined functions called within FN. If the compilation of FN causes the generation of one or more auxilary functions, a compiler message will be printed for these functions before the message for FN, e.g., (FOOA0027 (X) (uses: XX)) (FOO (A B)) When compiling a block, the compiler first prints (BLKNAME BLKFN1 BLKFN2 ...). Then the normal message is printed for the entire block. The names of the arguments to the block are generated by suffixing # and a number to the block name, e.g., (FOOBLOCK (FOOBLOCK#0 FOOBLOCK#1) FREE-VARIABLES). Then a message is printed for each entry to the block. In addition to the above output, both RECOMPILE and BRECOMPILE print the name of each function that is being copied from the old compiled file to the new compiled file. The normal compiler message is printed for each function that is actually compiled. The compiler prints out error messages when it encounters problems compiling a function. For example: ----- In BAZ: ***** (BAZ - illegal RETURN) ----- The above error message indicates that an illegal RETURN compiler error occurred while trying to compile the function BAZ. Some compiler errors cause the compilation to terminate, producing nothing; however, there are other compiler errors which do not stop compilation. The compiler error messages are described in the last section of this chapter. Compiler printout and error messages go to the file COUTFILE, initially T. COUTFILE can also be set to the name of a file opened for output, in which case all compiler printout will go to COUTFILE, i.e. the compiler will compile "silently." However, any error messages will be printed to both COUTFILE as well as T. Global(COMPILER NIL Compiler NIL ("18") 3 SUBNAME GLOBAL% VARIABLES SUBTEXT global% variables) Variables(VARIABLES NIL Variables NIL ("18") 3 SUBNAME GLOBAL SUBTEXT global) 1 Variables that appear on the list GLOBALVARS, or have the property GLOBALVAR with value T, or are declared with the GLOBALVARS file package command, are called global variables. Such variables are always accessed through their top level value when they are used freely in a compiled function. In other words, a reference to the value of a global variable is equivalent to calling GETTOPVAL on the variable, regardless of whether or not it is bound in the current access chain. Similarly, (SETQ VARIABLE VALUE) will compile as (SETTOPVAL (QUOTE VARIABLE) VALUE). All system parameters, unless otherwise specified, are declared as global variables. Thus, rebinding these variables in a deep bound system like Medley will not affect the behavior of the system: instead, the variables must be reset to their new values, and if they are to be restored to their original values, reset again. For example, you might write (SETQ GLOBALVARIABLE NEWVALUE) FORM (SETQ GLOBALVARIABLE OLDVALUE) In this case, if an error occurred during the evaluation of FORM, or a Control-D was typed, the global variable would not be restored to its original value. The function RESETVAR provides a convenient way of resetting global variables in such a way that their values are restored even if an error occurred or Control-D is typed. Note: The variables that a given function accesses as global variables can be determined by using the function CALLS. Local Variables(VARIABLES NIL Variables NIL ("18") 4 SUBNAME LOCAL SUBTEXT local)(COMPILER NIL Compiler NIL ("18") 4 SUBNAME LOCAL% VARIABLES SUBTEXT local% variables) and Special Variables(VARIABLES NIL Variables NIL ("18") 4 SUBNAME SPECIAL SUBTEXT special)(COMPILER NIL Compiler NIL ("18") 4 SUBNAME SPECIAL% VARIABLES SUBTEXT special% variables) 1 In normal compiled and interpreted code, all variable bindings are accessible by lower level functions because the variable's name is associated with its value. We call such variables special variables, or specvars. As mentioned earlier, the block compiler normally does not associate names with variable values. Such unnamed variables are not accessible from outside the function which binds them and are therefore local to that function. We call such unnamed variables local variables, or localvars. The time economies of local variables can be achieved without block compiling by use of declarations. Using local variables will increase the speed of compiled code; the price is the work of writing the necessary specvar declarations for those variables which need to be accessed from outside the block. LOCALVARS and SPECVARS are variables that affect compilation. During regular compilation, SPECVARS is normally T, and LOCALVARS is NIL or a list. This configuration causes all variables bound in the functions being compiled to be treated as special except those that appear on LOCALVARS. During block compilation, LOCALVARS is normally T and SPECVARS is NIL or a list. All variables are then treated as local except those that appear on SPECVARS. Declarations to set LOCALVARS and SPECVARS to other values, and therefore affect how variables are treated, may be used at several levels in the compilation process with varying scope. 1. The declarations may be included in the filecoms of a file, by using the LOCALVARS and SPECVARS file package commands. The scope of the declaration is then the entire file: ... (LOCALVARS . T) (SPECVARS X Y) ... 2. The declarations may be included in block declarations; the scope is then the block, e.g., (BLOCKS ((FOOBLOCK FOO FIE (SPECVARS . T) (LOCALVARS X))) 3. The declarations may also appear in individual functions, or in PROG's or LAMBDA's within a function, using the DECLARE function. In this case, the scope of the declaration is the function or the PROG or LAMBDA in which it appears. LOCALVARS and SPECVARS declarations must appear immediately after the variable list in the function, PROG, or LAMBDA, but intervening comments are permitted. For example: (DEFINEQ ((FOO (LAMBDA (X Y) (DECLARE (LOCALVARS Y)) (PROG (X Y Z) (DECLARE (LOCALVARS X)) ... ] If the above function is compiled (non-block), the outer X will be special, the X bound in the PROG will be local, and both bindings of Y will be local. Declarations for LOCALVARS and SPECVARS can be used in two ways: either to cause variables to be treated the same whether the function(s) are block compiled or compiled normally, or to affect one compilation mode while not affecting the default in the other mode. For example: (LAMBDA (X Y) (DECLARE (SPECVARS . T)) (PROG (Z) ... ] will cause X, Y, and Z to be specvars for both block and normal compilation while (LAMBDA (X Y) (DECLARE (SPECVARS X)) ... ] will make X a specvar when block compiling, but when regular compiling the declaration will have no effect, because the default value of specvars would be T, and therefore both X and Y will be specvars by default. Although LOCALVARS and SPECVARS declarations have the same form as other components of block declarations such as (LINKFNS . T), their operation is somewhat different because the two variables are not independent. (SPECVARS . T) will cause SPECVARS to be set to T, and LOCALVARS to be set to NIL. (SPECVARS V1 V2 ...) will have no effect if the value of SPECVARS is T, but if it is a list (or NIL), SPECVARS will be set to the union of its prior value and (V1 V2 ...). The operation of LOCALVARS is analogous. Thus, to affect both modes of compilation one of the two (LOCALVARS or SPECVARS) must be declared T before specifying a list for the other. Note: The variables that a given function binds as local variables or accesses as special variables can be determined by using the function CALLS. Note: LOCALVARS and SPECVARS declarations affect the compilation of local variables within a function, but the arguments to functions are always accessible as specvars. This can be changed by redefining the following function: (DASSEM.SAVELOCALVARS(DASSEM.SAVELOCALVARS (Function) NIL NIL ("18") 5) FN) [Function] This function is called by the compiler to determine whether argument information for FN should be written on the compiled file for FN. If it returns NIL, the argument information is not saved, and the function is stored with arguments U, V, W, etc instead of the originals. Initially, DASSEM.SAVELOCALVARS is defined to return T. (MOVD 'NILL 'DASSEM.SAVELOCALVARS) causes the compiler to retain no local variable or argument names. Alternatively, DASSEM.SAVELOCALVARS could be redefined as a more complex predicate, to allow finer discrimination. Constants(COMPILER NIL Compiler NIL ("18") 5 SUBNAME CONSTANTS SUBTEXT constants) 1 Interlisp allows the expression of constructions which are intended to be description of their constant values. The following functions are used to define constant values. The function SELECTC provides a mechanism for comparing a value to a number of constants. (CONSTANT(CONSTANT (Function) NIL NIL ("18") 5) X) [Function] This function enables you to define that the expression X should be treated as a "constant" value. When CONSTANT is interpreted, X is evaluted each time it is encountered. If the CONSTANT form is compiled, however, the expression will be evaluated only once. If the value of X has a readable print name, then it will be evaluated at compile-time, and the value will be saved as a literal in the compiled function's definition, as if (QUOTE VALUE-OF-EXPRESSION) had appeared instead of (CONSTANT EXPRESSION). If the value of X does not have a readable print name, then the expression X itself will be saved with the function, and it will be evaluated when the function is first loaded. The value will then be stored in the function's literals, and will be retrieved on future references. If a program needed a list of 30 NILs, you could specify (CONSTANT (to 30 collect NIL)) instead of (QUOTE (NIL NIL ...)). The former is more concise and displays the important parameter much more directly than the latter. CONSTANT can also be used to denote values that cannot be quoted directly, such as (CONSTANT (PACK NIL)), (CONSTANT (ARRAY 10)). It is also useful to parameterize quantities that are constant at run time but may differ at compile time, e.g., (CONSTANT BITSPERWORD) in a program is exactly equivalent to 36, if the variable BITSPERWORD is bound to 36 when the CONSTANT expression is evaluated at compile time. Whereas the function CONSTANT attempts to evaluate the expression as soon as possible (compile-time, load-time, or first-run-time), other options are available, using the folowing two function: (LOADTIMECONSTANT(LOADTIMECONSTANT (Function) NIL NIL ("18") 6) X) [Function] Similar to CONSTANT, except that the evaluation of X is deferred until the compiled code for the containing function is loaded in. For example, (LOADTIMECONSTANT (DATE)) will return the date the code was loaded. If LOADTIMECONSTANT is interpreted, it merely returns the value of X. (DEFERREDCONSTANT(DEFERREDCONSTANT (Function) NIL NIL ("18") 6) X) [Function] Similar to CONSTANT, except that the evaluation of X is always deferred until the compiled function is first run. This is useful when the storage for the constant is excessive so that it shouldn't be allocated until (unless) the function is actually invoked. If DEFERREDCONSTANT is interpreted, it merely returns the value of X. (CONSTANTS(CONSTANTS (Function) NIL NIL ("18") 6) VAR1 VAR2 ... VARN) [NLambda NoSpread Function] Defines VAR1, ... VARN (unevaluated) to be compile-time constants. Whenever the compiler encounters a (free) reference to one of these constants, it will compile the form (CONSTANT VARi) instead. If VARi is a list of the form (VAR FORM), a free reference to the variable will compile as (CONSTANT FORM). The compiler prints a warning if user code attempts to bind a variable previously declared as a constant. Constants can be saved using the CONSTANTS file package command. Compiling(COMPILING NIL Compiling NIL ("18") 6 SUBNAME FUNCTION% CALLS SUBTEXT function% calls) Function Calls 1 When compiling the call to a function, the compiler must know the type of the function, to determine how the arguments should be prepared (evaluated/unevaluated, spread/nospread). There are three seperate cases: lambda, nlambda spread, and nlambda nospread functions. To determine which of these three cases is appropriate, the compiler will first look for a definition among the functions in the file that is being compiled. The function can be defined anywhere in any of the files given as arguments to BCOMPL, TCOMPL, BRECOMPILE or RECOMPILE. If the function is not contained in the file, the compiler will look for other information in the variables NLAMA, NLAML, and LAMS, which can be set by you: NLAMA (NLAMA% (Variable) NIL NIL ("18") 7) [Variable] (For NLAMbda Atoms) A list of functions to be treated as nlambda nospread functions by the compiler. NLAML(NLAML (Variable) NIL NIL ("18") 7) [Variable] (For NLAMbda List) A list of functions to be treated as nlambda spread functions by the compiler. LAMS(LAMS (Variable) NIL NIL ("18") 7) [Variable] A list of functions to be treated as lambda functions by the compiler. Note that including functions on LAMS is only necessary to override in-core nlambda definitions, since in the absence of other information, the compiler assumes the function is a lambda. If the function is not contained in a file, or on the lists NLAMA, NLAML, or LAMS, the compiler will look for a current definition in the Interlisp system, and use its type. If there is no current definition, next COMPILEUSERFN is called: COMPILEUSERFN(COMPILEUSERFN (Variable) NIL NIL ("18") 7) [Variable] When compiling a function call, if the function type cannot be found by looking in files, the variables NLAMA, NLAML, or LAMS, or at a current definition, then if the value of COMPILEUSERFN is not NIL, the compiler calls (the value of) COMPILEUSERFN giving it as arguments CDR of the form and the form itself, i.e., the compiler does (APPLY* COMPILEUSERFN (CDR FORM) FORM). If a non-NIL value is returned, it is compiled instead of FORM. If NIL is returned, the compiler compiles the original expression as a call to a lambda spread that is not yet defined. COMPILEUSERFN is only called when the compiler encounters a list CAR of which is not the name of a defined function. You can instruct the compiler about how to compile other data types via COMPILETYPELST. CLISP uses COMPILEUSERFN to tell the compiler how to compile iterative statements, IF-THEN-ELSE statements, and pattern match constructs. If the compiler cannot determine the function type by any of the means above, it assumes that the function is a lambda function, and its arguments are to be evaluated. If there are nlambda functions called from the functions being compiled, and they are only defined in a separate file, they must be included on NLAMA or NLAML, or the compiler will incorrectly assume that their arguments are to be evaluated, and compile the calling function correspondingly. This is only necessary if the compiler does not "know" about the function. If the function is defined at compile time, or is handled via a macro, or is contained in the same group of files as the functions that call it, the compiler will automatically handle calls to that function correctly. FUNCTION and Functional Arguments 1 Compiling(COMPILING NIL Compiling NIL ("18") 7 SUBNAME FUNCTION% FUNCTION SUBTEXT FUNCTION% function)(COMPILING NIL Compiling NIL ("18") 7 SUBNAME FUNCTIONAL% ARGUMENTS SUBTEXT functional% arguments) the function FUNCTION may involve creating and compiling a seperate "auxiliary function", which will be called at run time. An auxiliary function is named by attaching a GENSYM to the end of the name of the function in which they appear, e.g., FOOA0003. For example, suppose FOO is defined as (LAMBDA (X) ... (FOO1 X (FUNCTION ...)) ...) and compiled. When FOO is run, FOO1 will be called with two arguments, X, and FOOA000N and FOO1 will call FOOA000N each time it uses its functional argument. Compiling FUNCTION will not create an auxiliary function if it is a functional argument to a function that compiles open, such as most of the mapping functions (MAPCAR, MAPLIST, etc.). A considerable savings in time could be achieved by making FOO1 compile open via a computed macro, e.g. (PUTPROP 'FOO1 'MACRO '(Z (LIST (SUBST (CADADR Z) (QUOTE FN) DEF) (CAR Z))) DEF is the definition of FOO1 as a function of just its first argument, and FN is the name used for its functional argument in its definition. In this case, (FOO1 X (FUNCTION ...)) would compile as an expression, containing the argument to FUNCTION as an open LAMBDA expression. Thus you save not only the function call to FOO1, but also each of the function calls to its functional argument. For example, if FOO1 operates on a list of length ten, eleven function calls will be saved. Of course, this savings in time costs space, and you must decide which is more important. Open Functions 1 When a function is called from a compiled function, a system routine is invoked that sets up the parameter and control push lists as necessary for variable bindings and return information. If the amount of time spent inside the function is small, this function calling time will be a significant percentage of the total time required to use the function. Therefore, many "small" functions, e.g., CAR, CDR, EQ, NOT, CONS are always compiled "open", i.e., they do not result in a function call. Other larger functions such as PROG, SELECTQ, MAPC, etc. are compiled open because they are frequently used. You can make other functions compile open via MACRO definitions. You can also affect the compiled code via COMPILEUSERFN and COMPILETYPELST. COMPILETYPELST 1 Most of the compiler's mechanism deals with how to handle forms (lists) and variables (symbols). You can affect the compiler's behaviour with respect to lists and literal atoms in a number of ways, e.g. macros, declarations, COMPILEUSERFN, etc. COMPILETYPELST allows you to tell the compiler what to do when it encounters a data type other than a list or an atom. It is the facility in the compiler that corresponds to DEFEVAL for the interpreter. COMPILETYPELST(COMPILETYPELST (Variable) NIL NIL ("18") 8) [Variable] A list of elements of the form (TYPENAME . FUNCTION). Whenever the compiler encounters a datum that is not a list and not an atom (or a number) in a context where the datum is being evaluated, the type name of the datum is looked up on COMPILETYPELST. If an entry appears CAR of which is equal to the type name, CDR of that entry is applied to the datum. If the value returned by this application is not EQ to the datum, then that value is compiled instead. If the value is EQ to the datum, or if there is no entry on COMPILETYPELST for this type name, the compiler simply compiles the datum as (QUOTE DATUM). Compiling(COMPILING NIL Compiling NIL ("18") 9 SUBNAME CLISP SUBTEXT CLISP) CLISP(CLISP NIL NIL NIL ("18") 9 SUBNAME COMPILING SUBTEXT compiling) 1 Since the compiler does not know about CLISP, in order to compile functions containing CLISP constructs, the definitions must first be DWIMIFYed. You can automate this process in several ways: 1. If the variable DWIMIFYCOMPFLG is T, the compiler will always DWIMIFY expressions before compiling them. DWIMIFYCOMPFLG is initially NIL. 2. If a file has the property FILETYPE with value CLISP on its property list, TCOMPL, BCOMPL, RECOMPILE, and BRECOMPILE will operate as though DWIMIFYCOMPFLG is T and DWIMIFY all expressions before compiling. 3. If the function definition has a local CLISP declaration, including a null declaration, i.e., just (CLISP:), the definition will be automatically DWIMIFYed before compiling. Note: COMPILEUSERFN is defined to call DWIMIFY on iterative statements, IF-THEN statements, and fetch, replace, and match expressions, i.e., any CLISP construct which can be recognized by its CAR of form. Thus, if the only CLISP constructs in a function appear inside of iterative statements, IF statements, etc., the function does not have to be dwimified before compiling. If DWIMIFY is ever unsuccessful in processing a CLISP expression, it will print the error message UNABLE TO DWIMIFY followed by the expression, and go into a break unless DWIMESSGAG = T. In this case, the expression is just compiled as is, i.e. as though CLISP had not been enabled. You can exit the break in one of these ways: 1. Type OK to the break, which will cause the compiler to try again, e.g. you could define some missing records while in the break, and then continue 2. Type , which will cause the compiler to simply compile the expression as is, i.e. as though CLISP had not been enabled in the first place 3. Return an expression to be compiled in its place by using the RETURN break command. Note: TCOMPL, BCOMPL, RECOMPILE, and BRECOMPILE all scan the entire file before doing any compiling, and take note of the names of all functions that are defined in the file as well as the names of all variables that are set by adding them to NOFIXFNSLST and NOFIXVARSLST, respectively. Thus, if a function is not currently defined, but is defined in the file being compiled, when DWIMIFY is called before compiling, it will not attempt to interpret the function name as CLISP when it appears as CAR of a form. DWIMIFY also takes into account variables that have been declared to be LOCALVARS, or SPECVARS, either via block declarations or DECLARE expressions in the function being compiled, and does not attempt spelling correction on these variables. The declaration USEDFREE may also be used to declare variables simply used freely in a function. These variables will also be left alone by DWIMIFY. Finally, NOSPELLFLG is reset to T when compiling functions from a file (as opposed to from their in-core definition) so as to suppress spelling correction. Compiler(COMPILER NIL Compiler NIL ("18") 9 SUBNAME FUNCTIONS SUBTEXT functions) Functions 1 Normally, the compiler is envoked through file package commands that keep track of the state of functions, and manage a set of files, such as MAKEFILE. However, it is also possible to explicitly call the compiler using one of a number of functions. Functions may be compiled from in-core definitions (via COMPILE), or from definitions in files (TCOMPL), or from a combination of in-core and file definitions (RECOMPILE). TCOMPL and RECOMPILE produce "compiled" files. Compiled files usually have the same name as the symbolic file they were made from, suffixed with DCOM (the compiled file extension is stored as the value of the variable COMPILE.EXT). The file name is constructed from the name field only, e.g., (TCOMPL 'FOO.TEM;3) produces FOO.DCOM on the connected directory. The version number will be the standard default. A "compiled file" contains the same expressions as the original symbolic file, except for the following: 1. A special FILECREATED expression appears at the front of the file which contains information used by the file package, and which causes the message COMPILED ON DATE to be printed when the file is loaded (the actual string printed is the value of COMPILEHEADER). 2. Every DEFINEQ in the symbolic file is replaced by the corresponding compiled definitions in the compiled file. 3. Expressions following a DONTCOPY tag inside of a DECLARE: that appears in the symbolic file are not copied to the compiled file. The compiled definitions appear at the front of the compiled file, i.e., before the other expressions in the symbolic file, regardless of where they appear in the symbolic file. The only exceptions are expressions that follow a FIRST tag inside of a DECLARE:. This "compiled" file can be loaded into any Interlisp system with LOAD. Note: When a function is compiled from its in-core definition (as opposed to being compiled from a definition in a file), and the function has been modified by BREAK, TRACE, BREAKIN, or ADVISE, it is first restored to its original state, and a message is printed out, e.g., FOO UNBROKEN. If the function is not defined by an expr definition, the value of the function's EXPR property is used for the compilation, if there is one. If there is no EXPR property, and the compilation is being performed by RECOMPILE, the definition of the function is obtained from the file (using LOADFNS). Otherwise, the compiler prints (FN NOT COMPILEABLE), and goes on to the next function. (COMPILE(COMPILE (Function) NIL NIL ("18") 10) X FLG) [Function] X is a list of functions (if atomic, (LIST X) is used). COMPILE first asks the standard compiler questions, and then compiles each function on X, using its in-core definition. Returns X. If compiled definitions are being written to a file, the file is closed unless FLG = T. (COMPILE1(COMPILE1 (Function) NIL NIL ("18") 10) FN DEF) [Function] Compiles DEF, redefining FN if STRF = T (STRF is one of the variables set by COMPSET). COMPILE1 is used by COMPILE, TCOMPL, and RECOMPILE. If DWIMIFYCOMPFLG is T, or DEF contains a CLISP declaration, DEF is dwimified before compiling. (TCOMPL(TCOMPL (Function) NIL NIL ("18") 10) FILES) [Function] TCOMPL is used to "compile files"; given a symbolic LOAD file (e.g., one created by MAKEFILE), it produces a "compiled file". FILES is a list of symbolic files to be compiled (if atomic, (LIST FILES) is used). TCOMPL asks the standard compiler questions, except for "OUTPUT FILE:". The output from the compilation of each symbolic file is written on a file of the same name suffixed with DCOM, e.g., (TCOMPL '(SYM1 SYM2)) produces two files, SYM1.DCOM and SYM2.DCOM. TCOMPL processes the files one at a time, reading in the entire file. For each FILECREATED expression, the list of functions that were marked as changed by the file package is noted, and the FILECREATED expression is written onto the output file. For each DEFINEQ expression, TCOMPL adds any nlambda functions defined in the DEFINEQ to NLAMA or NLAML, and adds lambda functions to LAMS, so that calls to these functions will be compiled correctly. NLAMA, NLAML, and LAMS are rebound to their top level values (using RESETVAR) by all of the compiling functions, so that any additions to these lists while inside of these functions will not propagate outside. Expressions beginning with DECLARE: are processed specially. All other expressions are collected to be subsequently written onto the output file. After processing the file in this fashion, TCOMPL compiles each function, except for those functions which appear on the list DONTCOMPILEFNS (initially NIL), and writes the compiled definition onto the output file. TCOMPL then writes onto the output file the other expressions found in the symbolic file. DONTCOMPILEFNS might be used for functions that compile open, since their definitions would be superfluous when operating with the compiled file. Note that DONTCOMPILEFNS can be set via block declarations. Note: If the rootname of a file has the property FILETYPE with value CLISP, or value a list containing CLISP, TCOMPL rebinds DWIMIFYCOMPFLG to T while compiling the functions on FILE, so the compiler will DWIMIFY all expressions before compiling them. TCOMPL returns a list of the names of the output files. All files are properly terminated and closed. If the compilation of any file is aborted via an error or Control-D, all files are properly closed, and the (partially complete) compiled file is deleted. (RECOMPILE(RECOMPILE (Function) NIL NIL ("18") 11) PFILE CFILE FNS) [Function] The purpose of RECOMPILE is to allow you to update a compiled file without recompiling every function in the file. RECOMPILE does this by using the results of a previous compilation. It produces a compiled file similar to one that would have been produced by TCOMPL, but at a considerable savings in time by only compiling selected functions, and copying the compiled definitions for the remainder of the functions in the file from an earlier TCOMPL or RECOMPILE file. PFILE is the name of the Pretty file (source file) to be compiled; CFILE is the name of the Compiled file containing compiled definitions that may be copied. FNS indicates which functions in PFILE are to be recompiled, e.g., have been changed or defined for the first time since CFILE was made. Note that PFILE, not FNS, drives RECOMPILE. RECOMPILE asks the standard compiler questions, except for "OUTPUT FILE:". As with TCOMPL, the output automatically goes to PFILE.DCOM. RECOMPILE processes PFILE the same as does TCOMPL except that DEFINEQ expressions are not actually read into core. Instead, RECOMPILE uses the filemap to obtain a list of the functions contained in PFILE. The filemap enables RECOMPILE to skip over the DEFINEQs in the file by simply resetting the file pointer, so that in most cases the scan of the symbolic file is very fast (the only processing required is the reading of the non-DEFINEQs and the processing of the DECLARE: expressions as with TCOMPL). A map is built if the symbolic file does not already contain one, for example if it was written in an earlier system, or with BUILDMAPFLG = NIL. After this initial scan of PFILE, RECOMPILE then processes the functions defined in the file. For each function in PFILE, RECOMPILE determines whether or not the function is to be (re)compiled. Functions that are members of DONTCOMPILEFNS are simply ignored. Otherwise, a function is recompiled if : 1. FNS is a list and the function is a member of that list 2. FNS = T or EXPRS and the function is defined by an expr definition 3. FNS = CHANGES and the function is marked as having been changed in the FILECREATED expression in PFILE 4. FNS = ALL If a function is not to be recompiled, RECOMPILE obtains its compiled definition from CFILE, and copies it (and all generated subfunctions) to the output file, PFILE.DCOM. If the function does not appear on CFILE, RECOMPILE simply recompiles it. Finally, after processing all functions, RECOMPILE writes out all other expressions that were collected in the prescan of PFILE. Note: If FNS = ALL, CFILE is superfluous, and does not have to be specified. This option may be used to compile a symbolic file that has never been compiled before, but which has already been loaded (since using TCOMPL would require reading the file in a second time). If CFILE = NIL, PFILE.DCOM (the old version of the output file) is used for copying from. If both FNS and CFILE are NIL, FNS is set to the value of RECOMPILEDEFAULT, which is initially CHANGES. Thus you can perform his edits, dump the file, and then simply (RECOMPILE 'FILE) to update the compiled file. The value of RECOMPILE is the file name of the new compiled file, PFILE.DCOM. If RECOMPILE is aborted due to an error or Control-D, the new (partially complete) compiled file will be closed and deleted. RECOMPILE is designed to allow you to conveniently and efficiently update a compiled file, even when the corresponding symbolic file has not been (completely) loaded. For example, you can perform a LOADFROM to "notice" a symbolic file, edit the functions he wants to change (the editor will automatically load those functions not already loaded), call MAKEFILE to update the symbolic file (MAKEFILE will copy the unchanged functions from the old symbolic file), and then perform (RECOMPILE PFILE). Note: Since PRETTYDEF automatically outputs a suitable DECLARE: expression to indicate which functions in the file (if any) are defined as NLAMBDAs, calls to these functions will be handled correctly, even though the NLAMBDA functions themselves may never be loaded, or even looked at, by RECOMPILE. Block(BLOCK NIL Block NIL ("18") 12 SUBNAME COMPILING SUBTEXT compiling) Compiling(COMPILING NIL Compiling NIL ("18") 12 SUBNAME BLOCK SUBTEXT block) 1 In Interlisp-10, block compiling provides a way of compiling several functions into a single block. Function calls between the component functions of the block are very fast. Thus, compiling a block consisting of just a single recursive function may be yield great savings if the function calls itself many times. The output of a block compilation is a single, usually large, function. Calls from within the block to functions outside of the block look like regular function calls. A block can be entered via several different functions, called entries. These must be specified when the block is compiled. In Medley, block compiling is handled somewhat differently; block compiling provides a mechanism for hiding function names internal to a block, but it does not provide a performance improvement. Block compiling in Medley works by automatically renaming the block functions with special names, and calling these functions with the normal function-calling mechanisms. Specifically, a function FN is renamed to \BLOCK-NAME/FN. For example, function FOO in block BAR is renamed to \BAR/FOO. Note that it is possible with this scheme to break functions internal to a block. Block(BLOCK NIL Block NIL ("18") 13 SUBNAME DECLARATIONS SUBTEXT declarations) Declarations Block compiling a file frequently involves giving the compiler a lot of information about the nature and structure of the compilation, e.g., block functions, entries, specvars, etc. To help with this, there is the BLOCKS file package command, which has the form: (BLOCKS BLOCK1... BLOCKN) where each BLOCKi is a block declaration. The BLOCKS command outputs a DECLARE: expression, which is noticed by BCOMPL and BRECOMPILE. BCOMPL and BRECOMPILE are sensitive to these declarations and take the appropriate action. Note: Masterscope includes a facility for checking the block declarations of a file or files for various anomalous conditions, e.g. functions in block declarations which aren't on the file(s), functions in ENTRIES not in the block, variables that may not need to be SPECVARS because they are not used freely below the places they are bound, etc. A block declaration is a list of the form: (BLKNAME BLKFN1 ... BLKFNM (VAR1 . VALUE1) ... (VARN . VALUEN)) BLKNAME is the name of a block. BLKFN1 ... BLKFNM are the functions in the block and correspond to BLKFNS in the call to BLOCKCOMPILE. The (VARi . VALUEi) expressions indicate the settings for variables affecting the compilation of that block. If VALUEi is atomic, then VARi is set to VALUEi, otherwise VARi is set to the UNION of VALUEi and the current value of the variable VARi. Also, expressions of the form (VAR * FORM) will cause FORM to be evaluated and the resulting list used as described above (e.g. (GLOBALVARS * MYGLOBALVARS)). For example, consider the block declaration below. The block name is EDITBLOCK, it includes a number of functions (EDITL0, EDITL1, ... EDITH), and it sets the variables ENTRIES, SPECVARS, RETFNS, and GLOBALVARS. (EDITBLOCK EDITL0 EDITL1 UNDOEDITL EDITCOM EDITCOMA EDITMAC EDITCOMS EDIT]UNDO UNDOEDITCOM EDITH (ENTRIES EDITL0 ## UNDOEDITL) (SPECVARS L COM LCFLG #1 #2 #3 LISPXBUFS) (RETFNS EDITL0) (GLOBALVARS EDITCOMSA EDITCOMSL EDITOPS)) Whenever BCOMPL or BRECOMPILE encounter a block declaration, they rebind RETFNS, SPECVARS, GLOBALVARS, BLKLIBRARY, and DONTCOMPILEFNS to their top level values, bind BLKAPPLYFNS and ENTRIES to NIL, and bind BLKNAME to the first element of the declaration. They then scan the rest of the declaration, setting these variables as described above. When the declaration is exhausted, the block compiler is called and given BLKNAME, the list of block functions, and ENTRIES. If a function appears in a block declaration, but is not defined in one of the files, then if it has an in-core definition, this definition is used and a message printed NOT ON FILE, COMPILING IN CORE DEFINITION. Otherwise, the message NOT COMPILEABLE, is printed and the block declaration processed as though the function were not on it, i.e. calls to the function will be compiled as external function calls. Since all compiler variables are rebound for each block declaration, the declaration only has to set those variables it wants changed. Furthermore, setting a variable in one declaration has no effect on the variable's value for another declaration. After finishing all blocks, BCOMPL and BRECOMPILE treat any functions in the file that did not appear in a block declaration in the same way as do TCOMPL and RECOMPILE. If you wish a function compiled separately as well as in a block, or if you wish to compile some functions (not blockcompile), with some compiler variables changed, you can use a special pseudo-block declaration of the form (NIL BLKFN1 ... BLKFNM (VAR1 . VALUE1) ... (VARN . VALUEN)) which means that BLKFN1 ... BLKFNM should be compiled after first setting VAR1 ... VARN as described above. The following variables control other aspects of compiling a block: RETFNS(RETFNS (Variable) NIL NIL ("18") 14) [Variable] Value is a list of internal block functions whose names must appear on the stack, e.g., if the function is to be returned from RETFROM, RETTO, RETEVAL, etc. Usually, internal calls between functions in a block are not put on the stack. BLKAPPLYFNS(BLKAPPLYFNS (Variable) NIL NIL ("18") 14) [Variable] Value is a list of internal block functions called by other functions in the same block using BLKAPPLY or BLKAPPLY* for efficiency reasons. Normally, a call to APPLY from inside a block would be the same as a call to any other function outside of the block. If the first argument to APPLY turned out to be one of the entries to the block, the block would have to be reentered. BLKAPPLYFNS enables a program to compute the name of a function in the block to be called next, without the overhead of leaving the block and reentering it. This is done by including on the list BLKAPPLYFNS those functions which will be called in this fashion, and by using BLKAPPLY in place of APPLY, and BLKAPPLY* in place of APPLY*. If BLKAPPLY or BLKAPPLY* is given a function not on BLKAPPLYFNS, the effect is the same as a call to APPLY or APPLY* and no error is generated. Note however, that BLKAPPLYFNS must be set at compile time, not run time, and furthermore, that all functions on BLKAPPLYFNS must be in the block, or an error is generated (at compile time), NOT ON BLKFNS. BLKAPPLYFNS(BLKAPPLYFNS (Variable) NIL NIL ("18") 14) [Variable] Value is a list of functions that are considered to be in the "block library" of functions that should automatically be included in the block if they are called within the block. Compiling a function open via a macro provides a way of eliminating a function call. For block compiling, the same effect can be achieved by including the function in the block. A further advantage is that the code for this function will appear only once in the block, whereas when a function is compiled open, its code appears at each place where it is called. The block library feature provides a convenient way of including functions in a block. It is just a convenience since you can always achieve the same effect by specifying the function(s) in question as one of the block functions, provided it has an expr definition at compile time. The block library feature simply eliminates the burden of supplying this definition. To use the block library feature, place the names of the functions of interest on the list BLKLIBRARY, and their expr definitions on the property list of the functions under the property BLKLIBRARYDEF. When the block compiler compiles a form, it first checks to see if the function being called is one of the block functions. If not, and the function is on BLKLIBRARY, its definition is obtained from the property value of BLKLIBRARYDEF, and it is automatically included as part of the block. Block(BLOCK NIL Block NIL ("18") 15 SUBNAME COMPILING% FUNCTIONS SUBTEXT compiling% functions) Compiling Functions There are three user level functions for block compiling, BLOCKCOMPILE, BCOMPL, and BRECOMPILE, corresponding to COMPILE, TCOMPL, and RECOMPILE. Note that all of the remarks on macros, globalvars, compiler messages, etc., all apply equally for block compiling. Using block declarations, you can intermix in a single file functions compiled normally and block compiled functions. (BLOCKCOMPILE(BLOCKCOMPILE (Function) NIL NIL ("18") 15) BLKNAME BLKFNS ENTRIES FLG) [Function] BLKNAME is the name of a block, BLKFNS is a list of the functions comprising the block, and ENTRIES a list of entries to the block. Each of the entries must also be on BLKFNS or an error is generated, NOT ON BLKFNS. If only one entry is specified, the block name can also be one of the BLKFNS, e.g., (BLOCKCOMPILE 'FOO '(FOO FIE FUM) '(FOO)). However, if more than one entry is specified, an error will be generated, CAN'T BE BOTH AN ENTRY AND THE BLOCK NAME. If ENTRIES is NIL, (LIST BLKNAME) is used, e.g., (BLOCKCOMPILE 'COUNT '(COUNT COUNT1)) If BLKFNS is NIL, (LIST BLKNAME) is used, e.g., (BLOCKCOMPILE 'EQUAL) BLOCKCOMPILE asks the standard compiler questions, and then begins compiling. As with COMPILE, if the compiled code is being written to a file, the file is closed unless FLG = T. The value of BLOCKCOMPILE is a list of the entries, or if ENTRIES = NIL, the value is BLKNAME. The output of a call to BLOCKCOMPILE is one function definition for BLKNAME, plus definitions for each of the functions on ENTRIES if any. These entry functions are very short functions which immediately call BLKNAME. (BCOMPL(BCOMPL (Function) NIL NIL ("18") 15) FILES CFILE) [Function] FILES is a list of symbolic files (if atomic, (LIST FILES) is used). BCOMPL differs from TCOMPL in that it compiles all of the files at once, instead of one at a time, in order to permit one block to contain functions in several files. (If you have several files to be BCOMPLed separately, you must make several calls to BCOMPL.) Output is to CFILE if given, otherwise to a file whose name is (CAR FILES) suffixed with DCOM. For example, (BCOMPL '(EDIT WEDIT)) produces one file, EDIT.DCOM. BCOMPL asks the standard compiler questions, except for "OUTPUT FILE:", then processes each file exactly the same as TCOMPL. BCOMPL next processes the block declarations as described above. Finally, it compiles those functions not mentioned in one of the block declarations, and then writes out all other expressions. If any of the files have property FILETYPE with value CLISP, or a list containing CLISP, then DWIMIFYCOMPFLG is rebound to T for all of the files. The value of BCOMPL is the output file (the new compiled file). If the compilation is aborted due to an error or Control-D, all files are closed and the (partially complete) output file is deleted. It is permissible to TCOMPL files set up for BCOMPL; the block declarations will simply have no effect. Similarly, you can BCOMPL a file that does not contain any block declarations and the result will be the same as having TCOMPLed it. (BRECOMPILE(BRECOMPILE (Function) NIL NIL ("18") 16) FILES CFILE FNS %) [Function] BRECOMPILE plays the same role for BCOMPL that RECOMPILE plays for TCOMPL. Its purpose is to allow you to update a compiled file without requiring an entire BCOMPL. FILES is a list of symbolic files (if atomic, (LIST FILES) is used). CFILE is the compiled file produced by BCOMPL or a previous BRECOMPILE that contains compiled definitions that may be copied. The interpretation of FNS is the same as with RECOMPILE. BRECOMPILE asks the standard compiler questions, except for "OUTPUT FILE:". As with BCOMPL, output automatically goes to FILE.DCOM, where FILE is the first file in FILES. BRECOMPILE processes each file the same as RECOMPILE, then processes each block declaration. If any of the functions in the block are to be recompiled, the entire block must be (is) recompiled. Otherwise, the block is copied from CFILE as with RECOMPILE. For pseudo-block declarations of the form (NIL FN1 ...), all variable assignments are made, but only those functions indicated by FNS are recompiled. After completing the block declarations, BRECOMPILE processes all functions that do not appear in a block declaration, recompiling those dictated by FNS, and copying the compiled definitions of the remaining from CFILE. Finally, BRECOMPILE writes onto the output file the "other expressions" collected in the initial scan of FILES. The value of BRECOMPILE is the output file (the new compiled file). If the compilation is aborted due to an error or Control-D, all files are closed and the (partially complete) output file is deleted. If CFILE = NIL, the old version of FILE.DCOM is used, as with RECOMPILE. In addition, if FNS and CFILE are both NIL, FNS is set to the value of RECOMPILEDEFAULT, initially CHANGES. Compiler(COMPILER NIL Compiler NIL ("18") 16 SUBNAME ERROR% MESSAGES SUBTEXT error% messages) Error Messages(ERROR% MESSAGES NIL Error% messages NIL ("18") 16 SUBNAME COMPILER SUBTEXT compiler) 1 Messages describing errors in the function being compiled are also printed on the terminal. These messages are always preceded by *****. Unless otherwise indicated below, the compilation will continue. (FN NOT ON FILE, COMPILING IN CORE DEFINITION(FN% NOT% ON% FILE,% COMPILING% IN% CORE% DEFINITION (Error Message) NIL NIL ("18") 16)) From calls to BCOMPL and BRECOMPILE. (FN NOT COMPILEABLE(FN% NOT% COMPILEABLE (Error Message) NIL NIL ("18") 16)) An EXPR definition for FN could not be found. In this case, no code is produced for FN, and the compiler proceeds to the next function to be compiled, if any. (FN NOT FOUND(FN% NOT% FOUND (Error Message) NIL NIL ("18") 17)) Occurs when RECOMPILE or BRECOMPILE try to copy the compiled definition of FN from CFILE, and cannot find it. In this case, no code is copied and the compiler proceeds to the next function to be compiled, if any. (FN NOT ON BLKFNS(FN% NOT% ON% BLKFNS (Error Message) NIL NIL ("18") 17)) FN was specified as an entry to a block, or else was on BLKAPPLYFNS, but did not appear on the BLKFNS. In this case, no code is produced for the entire block and the compiler proceeds to the next function to be compiled, if any. (FN CAN'T BE BOTH AN ENTRY AND THE BLOCK NAME(FN% CAN'T% BE% BOTH% AN% ENTRY% AND% THE% BLOCK% NAME (Error Message) NIL NIL ("18") 17)) In this case, no code is produced for the entire block and the compiler proceeds to the next function to be compiled, if any. (BLKNAME - USED BLKAPPLY WHEN NOT APPLICABLE(BLKNAME% -% USED% BLKAPPLY% WHEN% NOT% APPLICABLE (Error Message) NIL NIL ("18") 17)) BLKAPPLY is used in the block BLKNAME, but there are no BLKAPPLYFNS or ENTRIES declared for the block. (VAR SHOULD BE A SPECVAR - USED FREELY BY FN(VAR% SHOULD% BE% A% SPECVAR% -% USED% FREELY% BY% FN (Error Message) NIL NIL ("18") 17)) While compiling a block, the compiler has already generated code to bind VAR as a LOCALVAR, but now discovers that FN uses VAR freely. VAR should be declared a SPECVAR and the block recompiled. ((* --) COMMENT USED FOR VALUE(%(*% --%)% COMMENT% USED% FOR% VALUE (Error Message) NIL NIL ("18") 17)) A comment appears in a context where its value is being used, e.g. (LIST X (* --) Y). The compiled function will run, but the value at the point where the comment was used is undefined. ((FORM) - NON-ATOMIC CAR OF FORM(%(FORM%)% -% NON-ATOMIC% CAR% OF% FORM (Error Message) NIL NIL ("18") 17)) If you intended to treat the value of FORM as a function, you should use APPLY* (Chapter 10). FORM is compiled as if APPLY* had been used. ((SETQ VAR EXPR --) BAD SETQ(%(SETQ% VAR% EXPR% --%)% BAD% SETQ (Error Message) NIL NIL ("18") 17)) SETQ of more than two arguments. (FN - USED AS ARG TO NUMBER FN?(FN% -% USED% AS% ARG% TO% NUMBER% FN? (Error Message) NIL NIL ("18") 17)) The value of a predicate, such as GREATERP or EQ, is used as an argument to a function that expects numbers, such as IPLUS. (FN - NO LONGER INTERPRETED AS FUNCTIONAL ARGUMENT(FN% -% NO% LONGER% INTERPRETED% AS% FUNCTIONAL% ARGUMENT (Error Message) NIL NIL ("18") 17)) The compiler has assumed FN is the name of a function. If you intended to treat the value of FN as a function, APPLY* (Chapter 10) should be used. This message is printed when FN is not defined, and is also a local variable of the function being compiled. (FN - ILLEGAL RETURN(FN% -% ILLEGAL% RETURN (Error Message) NIL NIL ("18") 17)) RETURN encountered when not in PROG. (TG - ILLEGAL GO(TG% -% ILLEGAL% GO (Error Message) NIL NIL ("18") 17)) GO encountered when not in a PROG. (TG - MULTIPLY DEFINED TAG(TG% -% MULTIPLY% DEFINED% TAG (Error Message) NIL NIL ("18") 18)) TG is a PROG label that is defined more than once in a single PROG. The second definition is ignored. (TG - UNDEFINED TAG(TG% -% UNDEFINED% TAG (Error Message) NIL NIL ("18") 18)) TG is a PROG label that is referenced but not defined in a PROG. (VAR - NOT A BINDABLE VARIABLE(VAR% -% NOT% A% BINDABLE% VARIABLE (Error Message) NIL NIL ("18") 18)) VAR is NIL, T, or else not a literal atom. (VAR VAL -- BAD PROG BINDING(VAR% VAL% --% BAD% PROG% BINDING (Error Message) NIL NIL ("18") 18)) Occurs when there is a prog binding of the form (VAR VAL1 ... VALN). (TG - MULTIPLY DEFINED TAG, LAP(TG% -% MULTIPLY% DEFINED% TAG,% LAP (Error Message) NIL NIL ("18") 18)) TG is a label that was encountered twice during the second pass of the compilation. If this error occurs with no indication of a multiply defined tag during pass one, the tag is in a LAP macro. (TG - UNDEFINED TAG, LAP(TG% -% UNDEFINED% TAG,% LAP (Error Message) NIL NIL ("18") 18)) TG is a label that is referenced during the second pass of compilation and is not defined. LAP treats TG as though it were a COREVAL, and continues the compilation. (TG - MULTIPLY DEFINED TAG, ASSEMBLE(TG% -% MULTIPLY% DEFINED% TAG,% ASSEMBLE (Error Message) NIL NIL ("18") 18)) TG is a label that is defined more than once in an assemble form. (TG - UNDEFINED TAG, ASSEMBLE(TG% -% UNDEFINED% TAG,% ASSEMBLE (Error Message) NIL NIL ("18") 18)) TG is a label that is referenced but not defined in an assemble form. (OP - OPCODE? - ASSEMBLE(OP% -% OPCODE?% -% ASSEMBLE (Error Message) NIL NIL ("18") 18)) OP appears as CAR of an assemble statement, and is illegal. (NO BINARY CODE GENERATED OR LOADED FOR FN(NO% BINARY% CODE% GENERATED% OR% LOADED% FOR% FN (Error Message) NIL NIL ("18") 18)) A previous error condition was sufficiently serious that binary code for FN cannot be loaded without causing an error. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "18-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "18-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "18-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "18-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "18-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "18-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))(,HH2`~~2H``2066,``F PAGEHEADING VERSOHEAD@ PAGEHEADINGLEFTBACKT2Zll2Hll0T/HH306 +T8ZZ8ZZ26TT2Hll,xx,xx55-T,2,ll,<<,ll2l~~;ll2Hll2Hll2Hll/2Hll22,,/,3(T PALATINO PALATINO PALATINO PALATINO TITAN TITAN TITAN TITAN MODERN +MODERN +MODERN +   + + IM.CHAP.GETFNMODERN + + HRULE.GETFNMODERN + $=$]6Jm Cm   +      3V8   zx`T    1-D`    zU +!#W1  d   "         #_  2 . #"R ( +FIM.INDEX.GETFN + + HRULE.GETFNMODERN + "  +#Q>2+# #1(!&#&  +#g#*>#4 ib ( +XIM.INDEX.GETFN + +DIM.INDEX.GETFN + HRULE.GETFNMODERN + "" +   + c#\ z#<ko( +BIM.INDEX.GETFNVIM.INDEX.GETFN +FIM.INDEX.GETFNZIM.INDEX.GETFN + HRULE.GETFNMODERN + "QR#1# E  t   5# L O!(^ ;C N O8  #9%#  # < # +#  S X   1  J )  2IM.INDEX.GETFNTITAN  +  V,2  #TP( +HIM.INDEX.GETFN + HRULE.GETFNMODERN +  G &IM.INDEX.GETFNTITAN  +  802H   + : ! g Kt; *  .IM.INDEX.GETFNTITAN    ]/0 .IM.INDEX.GETFNTITAN  +   0  'IM.INDEX.GETFNTITAN  +    + 3  j ! ( +VIM.INDEX.GETFN + HRULE.GETFNMODERN + &  o  %IM.INDEX.GETFN  W #IM.INDEX.GETFNTITAN   T "IM.INDEX.GETFNTITAN   i <   +IM.INDEX.GETFNTITAN   h3 $ : .r /y  ; +  (" + HRULE.GETFNMODERN + & \IM.INDEX.GETFNbIM.INDEX.GETFND, $ , +E)  /P< :S( + HRULE.GETFNMODERN + &j j9 ( + HRULE.GETFNMODERN + & KQ ,IM.INDEX.GETFNTITAN   $VB)?( +BIM.INDEX.GETFN +@IM.INDEX.GETFN + HRULE.GETFNMODERN + &4%   +#*7' GcP X8B  +  B*l A #{u  + {( +HIM.INDEX.GETFN + HRULE.GETFNMODERN + &!:   ~E A +O i  R  bK |54ER UG5 B#$ &IM.INDEX.GETFNTITAN  $ P) O 'IM.INDEX.GETFNTITAN         %IM.INDEX.GETFNTITAN   .#8 3 n   J e 7 +@.p +M =U$2  "(   (IM.INDEX.GETFNTITAN    \   )BS   3  #   8 A   I ^?64 +:  ' &E& A H +3 : B   , q . Q +  "LGA ( +CIM.INDEX.GETFN + +CIM.INDEX.GETFN + HRULE.GETFNMODERN + $f% + +U + +IIM.INDEX.GETFN + '+% ! + +F5H%+%2 _  '! F %F % + )-**%  +, + +!  #% %~u% +b %(%D %IM.INDEX.GETFNTITAN   V  *IM.INDEX.GETFNTITAN   ^  wZ  D     &0 < B *IM.INDEX.GETFNTITAN    l q [ +V  +8 8  + +YIM.INDEX.GETFN + ':  +    +IM.INDEX.GETFNTITAN  + +  6! $ I)M    KM !   0P  %IM.INDEX.GETFNTITAN  + + +  (  +!-  3 0     I_  )IM.INDEX.GETFNTITAN  + + +    +  U ) " +O  +3   +! -  -K ) +b=  +V  +   + ( +UIM.INDEX.GETFN +UIM.INDEX.GETFN + HRULE.GETFNMODERN + 'D *WIM.INDEX.GETFN   +  +8IM.INDEX.GETFN  <I  + 2IM.INDEX.GETFN    +(~  + 7IM.INDEX.GETFN   6   +)YIM.INDEX.GETFN Q *  +!UIM.INDEX.GETFN    +%XIM.INDEX.GETFNPALATINO  I + HIM.INDEX.GETFN Cg JIM.INDEX.GETFN &  FIM.INDEX.GETFN  IIM.INDEX.GETFN "E /\IM.INDEX.GETFN :<N :IM.INDEX.GETFN   6IM.INDEX.GETFN  AIM.INDEX.GETFN 2% 9IM.INDEX.GETFN / FIM.INDEX.GETFN  DIM.INDEX.GETFN 0 GIM.INDEX.GETFN  ?IM.INDEX.GETFN Z! !LIM.INDEX.GETFN B DIM.INDEX.GETFN D ?IM.INDEX.GETFN  + (TIM.INDEX.GETFNPALATINO  I,  +z \ No newline at end of file diff --git a/docs/turpin-irm/IRM-2/19-DWIM.TEDIT b/docs/turpin-irm/IRM-2/19-DWIM.TEDIT new file mode 100644 index 00000000..cf78df7a --- /dev/null +++ b/docs/turpin-irm/IRM-2/19-DWIM.TEDIT @@ -0,0 +1,119 @@ +INTERLISP-D REFERENCE MANUAL DWIM "19"19. DWIM 2 A surprisingly large percentage of the errors made by Interlisp users are of the type that could be corrected by another Lisp programmer without any information about the purpose of the program or expression in question, e.g., misspellings, certain kinds of parentheses errors, etc. To correct these types of errors we have implemented in Medley a DWIM facility, short for Do-What-I-Mean. DWIM is called automatically whenever an error occurs in the evaluation of an Interlisp expression. (Currently, DWIM only operates on unbound atoms and undefined function errors.) DWIM then proceeds to try to correct the mistake using the current context of computation plus information about what you had previously been doing (and what mistakes you had been making) as guides to the remedy of the error. If DWIM is able to make the correction, the computation continues as though no error had occurred. Otherwise, the procedure is the same as though DWIM had not intervened: a break occurs, or an unwind to the last ERRORSET (see Chapter 14). The following protocol illustrates the operation of DWIM. For example, suppose you define the factorial function (FACT N) as follows: DEFINEQ((FACT (LAMBDA (N) (COND ((ZEROP N0 1) ((T (ITIMS N (FACCT 9SUB1 N] (FACT) Note that the definition of FACT contains several mistakes: ITIMES and FACT have been misspelled; the 0 in N0 was intended to be a right parenthesis, but the Shift key was not pressed; similarly, the 9 in 9SUB1 was intended to be a left parenthesis; and finally, there is an extra left parenthesis in front of the T that begins the final clause in the conditional. PRETTYPRNT((FACCT] =PRETTYPRINT =FACT (FACT [LAMBDA (N) (COND ((ZEROP N0 1) ((T (ITIMS N (FACCT 9SUB1 N]) (FACT) After defining FACT, you want to look at its definition using PRETTYPRINT, which you unfortunately misspell. Since there is no function PRETTYPRNT in the system, an undefined function error occurs, and DWIM is called. DWIM invokes its spelling corrector, which searches a list of functions frequently used (by this user) for the best possible match. Finding one that is extremely close, DWIM proceeds on the assumption that PRETTYPRNT meant PRETTYPRINT, notifies you of this, and calls PRETTYPRINT. At this point, PRETTYPRINT would normally print (FACCT NOT PRINTABLE) and exit, since FACCT has no definition. Note that this is not an Interlisp error condition, so that DWIM would not be called as described above. However, it is obviously not what you meant. This sort of mistake is corrected by having PRETTYPRINT itself explicitly invoke the spelling corrector portion of DWIM whenever given a function with no EXPR definition. Thus, with the aid of DWIM PRETTYPRINT is able to determine that you want to see the definition of the function FACT, and proceeds accordingly. FACT(3] N0 [IN FACT] -> N ) ? YES [IN FACT] (COND -- ((T --))) -> (COND -- (T --)) ITIMS [IN FACT] -> ITIMES FACCT [IN FACT] -> FACT 9SUB1 [IN FACT] -> ( SUB1 ? YES 6 PP FACT (FACT [LAMBDA (N) (COND ((ZEROP N) 1) (T (ITIMES N (FACT (SUB1 N]) FACT You now call FACT. During its execution, five errors occur, and DWIM is called five times. At each point, the error is corrected, a message is printed describing the action taken, and the computation is allowed to continue as if no error had occurred. Following the last correction, 6 is printed, the value of (FACT 3). Finally, you prettyprint the new, now correct, definition of FACT. In this particular example, you were operating in TRUSTING mode, which gives DWIM carte blanche for most corrections. You can also operate in CAUTIOUS mode, in which case DWIM will inform you of intended corrections before they are made, and allow you to approve or disapprove of them. If DWIM was operating in CAUTIOUS mode in the example above, it would proceed as follows: FACT(3) N0 [IN FACT] -> N ) ? YES U.D.F. T [IN FACT] FIX? YES [IN FACT] (COND -- ((T --))) -> (COND -- (T --)) ITIMS [IN FACT] -> ITIMES ? ...YES FACCT [IN FACT] -> FACT ? ...YES 9SUB1 [IN FACT] -> ( SUB1 ? NO U.B.A. (9SUB1 BROKEN) : For most corrections, if you do not respond in a specified interval of time, DWIM automatically proceeds with the correction, so that you need intervene only when you do not approve. In the example, you responded to the first, second, and fifth questions; DWIM responded for you on the third and fourth. DWIM uses ASKUSER for its interactions with you (see Chapter 26). Whenever an interaction is about to take place and you have typed ahead, ASKUSER types several bells to warn you to stop typing, then clears and saves the input buffers, restoring them after the interaction is complete. Thus if you typed ahead before a DWIM interaction, DWIM will not confuse your type-ahead with the answer to its question, nor will your type-ahead be lost. The bells are printed by the function PRINTBELLS, which can be advised or redefined for specialized applications, e.g. to flash the screen for a display terminal. A great deal of effort has gone into making DWIM "smart", and experience with a large number of users indicates that DWIM works very well; DWIM seldom fails to correct an error you feel it should have, and almost never mistakenly corrects an error. However, it is important to note that even when DWIM is wrong, no harm is done: since an error had occurred, you would have had to intervene anyway if DWIM took no action. Thus, if DWIM mistakenly corrects an error, you simply interrupt or abort the computation, reverse the DWIM change using UNDO (see Chapter 13), and make the correction you would have had to make without DWIM. An exception is if DWIM's correction mistakenly caused a destructive computation to be initiated, and information was lost before you could interrupt. We have not yet had such an incident occur. (DWIM(DWIM (Function) NIL NIL ("20") 2) X) [Function] Used to enable/disable DWIM. If X is the symbol C, DWIM is enabled in CAUTIOUS mode, so that DWIM will ask you before making corrections. If X is T, DWIM is enabled in TRUSTING mode, so DWIM will make most corrections automatically. If X is NIL, DWIM is disabled. Medley initially has DWIM enabled in CAUTIOUS mode. DWIM returns CAUTIOUS, TRUSTING or NIL, depending to what mode it has just been put into. For corrections to expressions typed in for immediate execution (typed into LISPX, Chapter 13), DWIM always acts as though it were in TRUSTING mode, i.e., no approval necessary. For certain types of corrections, e.g., run-on spelling corrections, 9-0 errors, etc., DWIM always acts like it was in CAUTIOUS mode, and asks for approval. In either case, DWIM always informs you of its action as described below. Spelling Correction Protocol 1 One type of error that DWIM can correct is the misspelling of a function or a variable name. When an unbound symbol or undefined function error occurs, DWIM tries to correct the spelling of the bad symbol. If a symbol is found whose spelling is "close" to the offender, DWIM proceeds as follows: If the correction occurs in the typed-in expression, DWIM prints =CORRECT-SPELLING and continues evaluating the expression. For example: (SETQ FOO (IPLUSS 1 2)) =IPLUS 3 If the correction does not occur in type-in, DWIM prints BAD-SPELLING [IN FUNCTION-NAME] -> CORRECT-SPELLING The appearance of -> is to call attention to the fact that the user's function will be or has been changed. Then, if DWIM is in TRUSTING mode, it prints a carriage return, makes the correction, and continues the computation. If DWIM is in CAUTIOUS mode, it prints a few spaces and ? and then wait for approval. The user then has six options: 1. Type Y. DWIM types es, and proceeds with the correction. 2. Type N. DWIM types o, and does not make the correction. 3. Type . DWIM does not make the correction, and furthermore guarantees that the error will not cause a break. 4. Type Control-E. For error correction, this has the same effect as typing N. 5. Do nothing. In this case DWIM waits for DWIMWAIT seconds, and if you have not responded, DWIM will type ... followed by the default answer. The default on spelling corrections is determined by the value of the variable FIXSPELLDEFAULT, whose top level value is initially Y. 6. Type space or carriage-return. In this case DWIM will wait indefinitely. This option is intended for those cases where you want to think about your answer, and want to insure that DWIM does not get "impatient" and answer for you. The procedure for spelling correction on other than Interlisp errors is analogous. If the correction is being handled as type-in, DWIM prints = followed by the correct spelling, and returns it to the function that called DWIM. Otherwise, DWIM prints the incorrect spelling, followed by the correct spelling. Then, if DWIM is in TRUSTING mode, DWIM prints a carriage-return and returns the correct spelling. Otherwise, DWIM prints a few spaces and a ? and waits for approval. You can then respond with Y, N, Control-E, space, carriage return, or do nothing as described above. The spelling corrector itself is not ERRORSET protected like the DWIM error correction routines. Therefore, typing N and typing Control-E may have different effects when the spelling corrector is called directly. The former simply instructs the spelling corrector to return NIL, and lets the calling function decide what to do next; the latter causes an error which unwinds to the last ERRORSET, however far back that may be. Parentheses Errors Protocol 1 When an unbound symbol or undefined error occurs, and the offending symbol contains 9 or 0, DWIM tries to correct errors caused by typing 9 for left parenthesis and 0 for right parenthesis. In these cases, the interaction with you is similar to that for spelling correction. If the error occurs in type-in, DWIM types =CORRECTION, and continues evaluating the expression. For example: (SETQ FOO 9IPLUS 1 2] = ( IPLUS 3 If the correction does not occur in type-in, DWIM prints BAD-ATOM [IN FUNCTION-NAME] -> CORRECTION ? and then waits for approval. You then have the same six options as for spelling correction, except the waiting time is 3*DWIMWAIT seconds. If you type Y, DWIM operates as if it were in TRUSTING mode, i.e., it makes the correction and prints its message. Actually, DWIM uses the value of the variables LPARKEY and RPARKEY to determine the corresponding lower case character for left and right parentheses. LPARKEY and RPARKEY are initially 9 and 0 respectively, but they can be reset for other keyboard layouts, e.g., on some terminals left parenthesis is over 8, and right parenthesis is over 9. Undefined Function T Errors 1 When an undefined function error occurs, and the offending function is T, DWIM tries to correct certain types of parentheses errors involving a T clause in a conditional. DWIM recognizes errors of the following forms: (COND --) (T --) The T clause appears outside and immediately following the COND. (COND -- (-- & (T --))) The T clause appears inside a previous clause. (COND -- ((T --))) The T clause has an extra pair of parentheses around it. For undefined function errors that are not one of these three types, DWIM takes no corrective action at all, and the error will occur. If the error occurs in type-in, DWIM simply types T FIXED and makes the correction. Otherwise if DWIM is in TRUSTING mode, DWIM makes the correction and prints the message: [IN FUNCTION-NAME] {BAD-COND} -> {CORRECTED-COND} If DWIM is in CAUTIOUS mode, DWIM prints UNDEFINED FUNCTION T [IN FUNCTION-NAME] FIX? and waits for approval. You then have the same options as for spelling corrections and parenthesis errors. If you type Y or default, DWIM makes the correction and prints its message. Having made the correction, DWIM must then decide how to proceed with the computation. In the first case, (COND --) (T --), DWIM cannot know whether the T clause would have been executed if it had been inside of the COND. Therefore DWIM asks you CONTINUE WITH T CLAUSE (with a default of YES). If you type N, DWIM continues with the form after the COND, i.e., the form that originally followed the T clause. In the second case, (COND -- (-- & (T --))), DWIM has a different problem. After moving the T clause to its proper place, DWIM must return as the value of & as the value of the COND. Since this value is no longer around, DWIM asks you OK TO REEVALUATE and then prints the expression corresponding to &. If you type Y, or default, DWIM continues by reevaluating &, otherwise DWIM aborts, and a U.D.F. T error will then occur (even though the COND has in fact been fixed). If DWIM can determine for itself that the form can safely be reevaluated, it does not consult you before reevaluating. DWIM can do this if the form is atomic, or CAR of the form is a member of the list OKREEVALST, and each of the arguments can safely be reevaluated. For example, (SETQ X (CONS (IPLUS Y Z) W)) is safe to reevaluate because SETQ, CONS, and IPLUS are all on OKREEVALST. In the third case, (COND -- ((T --))), there is no problem with continuation, so no further interaction is necessary. DWIM(DWIM NIL NIL NIL ("20") 5 SUBNAME OPERATION SUBTEXT operation) Operation 1 Whenever the interpreter encounters an atomic form with no binding, or a non-atomic form CAR of which is not a function or function object, it calls the function FAULTEVAL. Similarly, when APPLY is given an undefined function, FAULTAPPLY is called. When DWIM is enabled, FAULTEVAL and FAULTAPPLY are redefined to first call the DWIM package, which tries to correct the error. If DWIM cannot decide how to fix the error, or you disapprove of DWIM's correction (by typing N), or you type Control-E, then FAULTEVAL and FAULTAPPLY cause an error or break. If you type to DWIM, DWIM exits by performing (RETEVAL 'FAULTEVAL '(ERROR!)), so that an error will be generated at the position of the call to FAULTEVAL. If DWIM can (and is allowed to) correct the error, it exits by performing RETEVAL of the corrected form, as of the position of the call to FAULTEVAL or FAULTAPPLY. Thus in the example at the beginning of the chapter, when DWIM determined that ITIMS was ITIMES misspelled, DWIM called RETEVAL with (ITIMES N (FACCT 9SUB1 N)). Since the interpreter uses the value returned by FAULTEVAL exactly as though it were the value of the erroneous form, the computation will thus proceed exactly as though no error had occurred. In addition to continuing the computation, DWIM also repairs the cause of the error whenever possible; in the above example, DWIM also changed (with RPLACA) the expression (ITIMS N (FACCT 9SUB1 N)) that caused the error. Note that if your program had computed the form and called EVAL, it would not be possible to repair the cause of the error, although DWIM could correct the misspelling each time it occurred. Error correction in DWIM is divided into three categories: unbound atoms, undefined CAR of form, and undefined function in APPLY. Assuming that the user approves DWIM's corrections, the action taken by DWIM for the various types of errors in each of these categories is summarized below. DWIM Correction: Unbound Atoms If DWIM is called as the result of an unbound atom error, it proceeds as follows: 1. If the first character of the unbound atom is ', DWIM assumes that you (intentionally) typed 'ATOM for (QUOTE ATOM) and makes the appropriate change. No message is typed, and no approval is requested. If the unbound atom is just ' itself, DWIM assumes you want the next expression quoted, e.g., (CONS X '(A B C)) will be changed to (CONS X (QUOTE (A B C))). Again no message will be printed or approval asked. If no expression follows the ', DWIM gives up. Note: ' is normally defined as a read-macro character which converts 'FOO to (QUOTE FOO) on input, so DWIM will not see the ' in the case of expressions that are typed-in. 2. If CLISP (see Chapter 21) is enabled, and the atom is part of a CLISP construct, the CLISP transformation is performed and the result returned. For example, N-1 is transformed to (SUB1 N), and (... FOO_3 ...) is transformed into (... (SETQ FOO 3) ...). 3. If the atom contains an 9 (actually LPARKEY (see the DWIM Functions and Variables section below), DWIM assumes the 9 was intended to be a left parenthesis, and calls the editor to make appropriate repairs on the expression containing the atom. DWIM assumes that you did not notice the mistake, i.e., that the entire expression was affected by the missing left parenthesis. For example, if you type (SETQ X (LIST (CONS 9CAR Y) (CDR Z)) Y), the expression will be changed to (SETQ X (LIST (CONS (CAR Y) (CDR Z)) Y)). The 9 does not have to be the first character of the atom: DWIM will handle (CONS X9CAR Y) correctly. 4. If the atom contains a 0 (actually RPARKEY, see the DWIM Functions and Variables section below), DWIM assumes the 0 was intended to be a right parenthesis and operates as in the case above. 5. If the atom begins with a 7, the 7 is treated as a '. For example, 7FOO becomes 'FOO, and then (QUOTE FOO). 6. The expressions on DWIMUSERFORMS (see the DWIMUSERFORMS section below) are evaluated in the order that they appear. If any of these expressions returns a non-NIL value, this value is treated as the form to be used to continue the computation, it is evaluated and its value is returned by DWIM. 7. If the unbound atom occurs in a function, DWIM attempts spelling correction using the LAMBDA and PROG variables of the function as the spelling list. 8. If the unbound atom occurred in a type-in to a break, DWIM attempts spelling correction using the LAMBDA and PROG variables of the broken function as the spelling list. 9. Otherwise, DWIM attempts spelling correction using SPELLINGS3 (see the Spelling Lists section below). 10. If all of the above fail, DWIM gives up. Undefined CAR of Form If DWIM is called as the result of an undefined CAR of form error, it proceeds as follows: 1. If CAR of the form is T, DWIM assumes a misplaced T clause and operates as described in the Undefined Function T Errors section above. 2. If CAR of the form is F/L, DWIM changes the "F/L" to "FUNCTION(LAMBDA". For example, (F/L (Y) (PRINT (CAR Y))) is changed to (FUNCTION (LAMBDA (Y) (PRINT (CAR Y))). No message is printed and no approval requested. If you omit the variable list, DWIM supplies (X), e.g., (F/L (PRINT (CAR X))) is changed to (FUNCTION (LAMBDA (X) (PRINT (CAR X)))). DWIM determines that you have supplied the variable list when more than one expression follows F/L, CAR of the first expression is not the name of a function, and every element in the first expression is atomic. For example, DWIM will supply (X) when correcting (F/L (PRINT (CDR X)) (PRINT (CAR X))). 3. If CAR of the form is a CLISP word (IF, FOR, DO, FETCH, etc.), the indicated CLISP transformation is performed, and the result is returned as the corrected form. See Chapter 21. 4. If CAR of the form has a function definition, DWIM attempts spelling correction on CAR of the definition using as spelling list the value of LAMBDASPLST, initially (LAMBDA NLAMBDA). 5. If CAR of the form has an EXPR or CODE property, DWIM prints CAR-OF-FORM UNSAVED, performs an UNSAVEDEF, and continues. No approval is requested. 6. If CAR of the form has a FILEDEF property, the definition is loaded from a file (except when DWIMIFYing). If the value of the property is atomic, the entire file is to be loaded. If the value is a list, CAR is the name of the file and CDR the relevant functions, and LOADFNS will be used. For both cases, LDFLG will be SYSLOAD (see Chapter 17). DWIM uses FINDFILE (Chapter 24), so that the file can be on any of the directories on DIRECTORIES, initially (NIL NEWLISP LISP LISPUSERS). If the file is found, DWIM types SHALL I LOAD followed by the file name or list of functions. If you approve, DWIM loads the function(s) or file, and continues the computation. 7. If CLISP is enabled, and CAR of the form is part of a CLISP construct, the indicated transformation is performed, e.g., (NN-1) becomes (SETQ N (SUB1 N)). 8. If CAR of the form contains an 9, DWIM assumes a left parenthesis was intended e.g., (CONS9CAR X). 9. If CAR of the form contains a 0, DWIM assumes a right parenthesis was intended. 10. If CAR of the form is a list, DWIM attempts spelling correction on CAAR of the form using LAMBDASPLST as spelling list. If successful, DWIM returns the corrected expression itself. 11. The expressions on DWIMUSERFORMS are evaluated in the order they appear. If any returns a non-NIL value, this value is treated as the corrected form, it is evaluated, and DWIM returns its value. 12. Otherwise, DWIM attempts spelling correction using SPELLINGS2 as the spelling list (see the Spelling Lists section below). When DWIMIFYing, DWIM also attemps spelling correction on function names not defined but previously encountered, using NOFIXFNSLST as a spelling list (see Chapter 21). 13. If all of the above fail, DWIM gives up. Undefined Function in APPLY If DWIM is called as the result of an undefined function in APPLY error, it proceeds as follows: 1. If the function has a definition, DWIM attempts spelling correction on CAR of the definition using LAMBDASPLST as spelling list. 2. If the function has an EXPR or CODE property, DWIM prints FN UNSAVED, performs an UNSAVEDEF and continues. No approval is requested. 3. If the function has a property FILEDEF, DWIM proceeds as in case 6 of undefined CAR of form. 4. If the error resulted from type-in, and CLISP is enabled, and the function name contains a CLISP operator, DWIM performs the indicated transformation, e.g., type FOO(APPEND FIE FUM). 5. If the function name contains an 9, DWIM assumes a left parenthesis was intended, e.g., EDIT9FOO]. 6. If the "function" is a list, DWIM attempts spelling correction on CAR of the list using LAMBDASPLST as spelling list. 7. The expressions on DWIMUSERFORMS are evaluated in the order they appear, and if any returns a non-NIL value, this value is treated as the function used to continue the computation, i.e., it will be applied to its arguments. 8. DWIM attempts spelling correction using SPELLINGS1 as the spelling list. 9. DWIM attempts spelling correction using SPELLINGS2 as the spelling list. 10. If all fail, DWIM gives up. DWIMUSERFORMS 1 The variable DWIMUSERFORMS(DWIMUSERFORMS (Variable) NIL NIL ("20") 8) provides a convenient way of adding to the transformations that DWIM performs. For example, you might want to change atoms of the form $X to (QA4LOOKUP X). Before attempting spelling correction, but after performing other transformations (F/L, 9, 0, CLISP, etc.), DWIM evaluates the expressions on DWIMUSERFORMS in the order they appear. If any expression returns a non-NIL value, this value is treated as the transformed form to be used. If DWIM was called from FAULTEVAL, this form is evaluated and the resulting value is returned as the value of FAULTEVAL. If DWIM is called from FAULTAPPLY, this form is treated as a function to be applied to FAULTARGS, and the resulting value is returned as the value of FAULTAPPLY. If all of the expressions on DWIMUSERFORMS return NIL, DWIM proceeds as though DWIMUSERFORMS = NIL, and attempts spelling correction. Note that DWIM simply takes the value and returns it; the expressions on DWIMUSERFORMS are responsible for making any modifications to the original expression. The expressions on DWIMUSERFORMS should make the transformation permanent, either by associating it with FAULTX via CLISPTRAN, or by destructively changing FAULTX. In order for an expression on DWIMUSERFORMS to be able to be effective, it needs to know various things about the context of the error. Therefore, several of DWIM's internal variables have been made SPECVARS (see Chapter 18) and are therefore "visible" to DWIMUSERFORMS. Below are a list of those variables that may be useful. FAULTX(FAULTX (Variable) NIL NIL ("20") 8) [Variable] For unbound atom and undefined car of form errors, FAULTX is the atom or form. For undefined function in APPLY errors, FAULTX is the name of the function. FAULTARGS(FAULTARGS (Variable) NIL NIL ("20") 8) [Variable] For undefined function in APPLY errors, FAULTARGS is the list of arguments. FAULTARGS may be modified or reset by expressions on DWIMUSERFORMS. FAULTAPPLYFLG(FAULTAPPLYFLG (Variable) NIL NIL ("20") 8) [Variable] Value is T for undefined function in APPLY errors; NIL otherwise. The value of FAULTAPPLYFLG after an expression on DWIMUSERFORMS returns a non-NIL value determines how the latter value is to be treated. Following an undefined function in APPLY error, if an expression on DWIMUSERFORMS sets FAULTAPPLYFLG to NIL, the value returned is treated as a form to be evaluated, rather than a function to be applied. FAULTAPPLYFLG is necessary to distinguish between unbound atom and undefined function in APPLY errors, since FAULTARGS may be NIL and FAULTX atomic in both cases. TAIL(TAIL (Variable) NIL NIL ("20") 9) [Variable] For unbound atom errors, TAIL is the tail of the expression CAR of which is the unbound atom. DWIMUSERFORMS expression can replace the atom by another expression by performing (/RPLACA TAIL EXPR) PARENT(PARENT (Variable) NIL NIL ("20") 9) [Variable] For unbound atom errors, PARENT is the form in which the unbound atom appears. TAIL is a tail of PARENT. TYPE-IN?(TYPE-IN? (Variable) NIL NIL ("20") 9) [Variable] True if the error occurred in type-in. FAULTFN(FAULTFN (Variable) NIL NIL ("20") 9) [Variable] Name of the function in which error occurred. FAULTFN is TYPE-IN when the error occurred in type-in, and EVAL or APPLY when the error occurred under an explicit call to EVAL or APPLY. DWIMIFYFLG(DWIMIFYFLG (Variable) NIL NIL ("20") 9) [Variable] True if the error was encountered while DWIMIFYing (as opposed to happening while running a program). EXPR(EXPR (Variable) NIL NIL ("20") 9) [Variable] Definition of FAULTFN, or argument to EVAL, i.e., the superform in which the error occurs. The initial value of DWIMUSERFORMS is ((DWIMLOADFNS?)). DWIMLOADFNS? is a function for automatically loading functions from files. If DWIMLOADFNSFLG is T (its initial value), and CAR of the form is the name of a function, and the function is contained on a file that has been noticed by the file package, the function is loaded, and the computation continues. DWIM(DWIM NIL NIL NIL ("20") 9 SUBNAME FUNCTIONS SUBTEXT functions) Functions and Variables(DWIM NIL NIL NIL ("20") 9 SUBNAME VARIABLES SUBTEXT variables) 1 DWIMWAIT(DWIMWAIT (Variable) NIL NIL ("20") 9) [Variable] Value is the number of seconds that DWIM will wait before it assumes that you are not going to respond to a question and uses the default response FIXSPELLDEFAULT. DWIM operates by dismissing for 250 milliseconds, then checking to see if anything has been typed. If not, it dismisses again, etc. until DWIMWAIT seconds have elapsed. Thus, there will be a delay of at most 1/4 second before DWIM responds to your answer. FIXSPELLDEFAULT(FIXSPELLDEFAULT (Variable) NIL NIL ("20") 9) [Variable] If approval is requested for a spelling correction, and you do not respond, defaults to value of FIXSPELLDEFAULT, initially Y. FIXSPELLDEFAULT is rebound to N when DWIMIFYing. ADDSPELLFLG(ADDSPELLFLG (Variable) NIL NIL ("20") 9) [Variable] If NIL, suppresses calls to ADDSPELL. Initially T. NOSPELLFLG(NOSPELLFLG (Variable) NIL NIL ("20") 10) [Variable] If T, suppresses all spelling correction. If some other non-NIL value, suppresses spelling correction in programs but not type-in. NOSPELLFLG is initially NIL. It is rebound to T when compiling from a file. RUNONFLG(RUNONFLG (Variable) NIL NIL ("20") 10) [Variable] If NIL, suppresses run-on spelling corrections. Initially NIL. DWIMLOADFNSFLG(DWIMLOADFNSFLG (Variable) NIL NIL ("20") 10) [Variable] If T, tells DWIM that when it encounters a call to an undefined function contained on a file that has been noticed by the file package, to simply load the function. DWIMLOADFNSFLG is initially T (see above). LPARKEY(LPARKEY (Variable) NIL NIL ("20") 10) [Variable] RPARKEY(RPARKEY (Variable) NIL NIL ("20") 10) [Variable] DWIM uses the value of the variables LPARKEY and RPARKEY (initially 9 and 0 respectively) to determine the corresponding lower case character for left and right parentheses. LPARKEY and RPARKEY can be reset for other keyboard layouts. For example, on some terminals left parenthesis is over 8, and right parenthesis is over 9. OKREEVALST(OKREEVALST (Variable) NIL NIL ("20") 10) [Variable] The value of OKREEVALST is a list of functions that DWIM can safely reevaluate. If a form is atomic, or CAR of the form is a member of OKREEVALST, and each of the arguments can safely be reevaluated, then the form can be safely reevaluated. For example, (SETQ X (CONS (IPLUS Y Z) W)) is safe to reevaluate because SETQ, CONS, and IPLUS are all on OKREEVALST. DWIMFLG(DWIMFLG (Variable) NIL NIL ("20") 10) [Variable] DWIMFLG = NIL, all DWIM operations are disabled. (DWIM 'C) and (DWIM T) set DWIMFLG to T; (DWIM NIL) sets DWIMFLG to NIL. APPROVEFLG (APPROVEFLG% (Variable) NIL NIL ("20") 10) [Variable] APPROVEFLG = T if DWIM should ask the user for approval before making a correction that will modify the definition of one of his functions; NIL otherwise. When DWIM is put into CAUTIOUS mode with (DWIM 'C), APPROVEFLG is set to T; for TRUSTING mode, APPROVEFLG is set to NIL. LAMBDASPLST(LAMBDASPLST (Variable) NIL NIL ("20") 10) [Variable] DWIM uses the value of LAMBDASPLST as the spelling list when correcting "bad" function definitions. Initially (LAMBDA NLAMBDA). You may wish to add to LAMBDASPLST if you elect to define new "function types" via an appropriate DWIMUSERFORMS entry. For example, the QLAMBDAs of SRI's QLISP are handled in this way. Spelling Correction(SPELLING% CORRECTION NIL Spelling% correction NIL ("20") 10) 1 The spelling corrector is given as arguments a misspelled word (word means symbol), a spelling list (a list of words), and a number: XWORD, SPLST, and REL respectively. Its task is to find that word on SPLST which is closest to XWORD, in the sense described below. This word is called a respelling of XWORD. REL specifies the minimum "closeness" between XWORD and a respelling. If the spelling corrector cannot find a word on SPLST closer to XWORD than REL, or if it finds two or more words equally close, its value is NIL, otherwise its value is the respelling. The spelling corrector can also be given an optional functional argument, FN, to be used for selecting out a subset of SPLST, i.e., only those members of SPLST that satisfy FN will be considered as possible respellings. The exact algorithm for computing the spelling metric is described later, but briefly "closeness" is inversely proportional to the number of disagreements between the two words, and directly proportional to the length of the longer word. For example, PRTTYPRNT is "closer" to PRETTYPRINT than CS is to CONS even though both pairs of words have the same number of disagreements. The spelling corrector operates by proceeding down SPLST, and computing the closeness between each word and XWORD, and keeping a list of those that are closest. Certain differences between words are not counted as disagreements, for example a single transposition, e.g., CONS to CNOS, or a doubled letter, e.g., CONS to CONSS, etc. In the event that the spelling corrector finds a word on SPLST with no disagreements, it will stop searching and return this word as the respelling. Otherwise, the spelling corrector continues through the entire spelling list. Then if it has found one and only one "closest" word, it returns this word as the respelling. For example, if XWORD is VONS, the spelling corrector will probably return CONS as the respelling. However, if XWORD is CONZ, the spelling corrector will not be able to return a respelling, since CONZ is equally close to both CONS and COND. If the spelling corrector finds an acceptable respelling, it interacts with you as described earlier. In the special case that the misspelled word contains one or more $s (escape), the spelling corrector searches for those words on SPLST that match XWORD, where a $ can match any number of characters (including 0), e.g., FOO$ matches FOO1 and FOO, but not NEWFOO. $FOO$ matches all three. Both completion and correction may be involved, e.g. RPETTY$ will match PRETTYPRINT, with one mistake. The entire spelling list is always searched, and if more than one respelling is found, the spelling corrector prints AMBIGUOUS, and returns NIL. For example, CON$ would be ambiguous if both CONS and COND were on the spelling list. If the spelling corrector finds one and only one respelling, it interacts with you as described earlier. For both spelling correction and spelling completion, regardless of whether or not you approve of the spelling corrector's choice, the respelling is moved to the front of SPLST. Since many respellings are of the type with no disagreements, this procedure has the effect of considerably reducing the time required to correct the spelling of frequently misspelled words. Synonyms(SPELLING% CORRECTION NIL Spelling% correction NIL ("20") 11 SUBNAME SYNONYMS SUBTEXT synonyms) Spelling lists also provide a way of defining synonyms for a particular context. If a dotted pair appears on a spelling list (instead of just an atom), CAR is interpreted as the correct spelling of the misspelled word, and CDR as the antecedent for that word. If CAR is identical with the misspelled word, the antecedent is returned without any interaction or approval being necessary. If the misspelled word corrects to CAR of the dotted pair, the usual interaction and approval will take place, and then the antecedent, i.e., CDR of the dotted pair, is returned. For example,you could make IFLG synonymous with CLISPIFTRANFLG by adding (IFLG . CLISPIFTRANFLG) to SPELLINGS3, the spelling list for unbound atoms. Similarly, you could make OTHERWISE mean the same as ELSEIF by adding (OTHERWISE . ELSEIF) to CLISPIFWORDSPLST, or make L be synonymous with LAMBDA by adding (L . LAMBDA) to LAMBDASPLST. You can also use L as a variable without confusion, since the association of L with LAMBDA occurs only in the appropriate context. Spelling Lists(SPELLING% CORRECTION NIL Spelling% correction NIL ("20") 11 SUBNAME SPELLING% LISTS SUBTEXT spelling% lists) Any list of atoms can be used as a spelling list, e.g., BROKENFNS, FILELST, etc. Various system packages have their own spellings lists, e.g., LISPXCOMS, CLISPFORWORDSPLST, EDITCOMSA, etc. These are documented under their corresponding sections, and are also indexed under "spelling lists." In addition to these spelling lists, the system maintains, i.e., automatically adds to, and occasionally prunes, four lists used solely for spelling correction: SPELLINGS1, SPELLINGS2, SPELLINGS3, and USERWORDS. These spelling lists are maintained only when ADDSPELLFLG is non-NIL. ADDSPELLFLG is initially T. SPELLINGS1(SPELLINGS1 (Variable) NIL NIL ("20") 11) [Variable] SPELLINGS1 is a list of functions used for spelling correction when an input is typed in apply format, and the function is undefined, e.g., EDTIF(FOO). SPELLINGS1 is initialized to contain DEFINEQ, BREAK, MAKEFILE, EDITF, TCOMPL, LOAD, etc. Whenever LISPX is given an input in apply format, i.e., a function and arguments, the name of the function is added to SPELLINGS1 if the function has a definition. For example, typing CALLS(EDITF) will cause CALLS to be added to SPELLINGS1. Thus if you typed CALLS(EDITF) and later typed CALLLS(EDITV), since SPELLINGS1 would then contain CALLS, DWIM would be successful in correcting CALLLS to CALLS. SPELLINGS2(SPELLINGS2 (Variable) NIL NIL ("20") 12) [Variable] SPELLINGS2 is a list of functions used for spelling correction for all other undefined functions. It is initialized to contain functions such as ADD1, APPEND, COND, CONS, GO, LIST, NCONC, PRINT, PROG, RETURN, SETQ, etc. Whenever LISPX is given a non-atomic form, the name of the function is added to SPELLINGS2. For example, typing (RETFROM (STKPOS (QUOTE FOO) 2)) to a break would add RETFROM to SPELLINGS2. Function names are also added to SPELLINGS2 by DEFINE, DEFINEQ, LOAD (when loading compiled code), UNSAVEDEF, EDITF, and PRETTYPRINT. SPELLINGS3(SPELLINGS3 (Variable) NIL NIL ("20") 12) [Variable] SPELLINGS3 is a list of words used for spelling correction on all unbound atoms. SPELLINGS3 is initialized to EDITMACROS, BREAKMACROS, BROKENFNS, and ADVISEDFNS. Whenever LISPX is given an atom to evaluate, the name of the atom is added to SPELLINGS3 if the atom has a value. Atoms are also added to SPELLINGS3 whenever they are edited by EDITV, and whenever they are set via RPAQ or RPAQQ. For example, when a file is loaded, all of the variables set in the file are added to SPELLINGS3. Atoms are also added to SPELLINGS3 when they are set by a LISPX input, e.g., typing (SETQ FOO (REVERSE (SETQ FIE ...))) will add both FOO and FIE to SPELLINGS3. USERWORDS(USERWORDS (Variable) NIL NIL ("20") 12) [Variable] USERWORDS is a list containing both functions and variables that you have referred to, e.g., by breaking or editing. USERWORDS is used for spelling correction by ARGLIST, UNSAVEDEF, PRETTYPRINT, BREAK, EDITF, ADVISE, etc. USERWORDS is initially NIL. Function names are added to it by DEFINE, DEFINEQ, LOAD, (when loading compiled code, or loading exprs to property lists) UNSAVEDEF, EDITF, EDITV, EDITP, PRETTYPRINT, etc. Variable names are added to USERWORDS at the same time as they are added to SPELLINGS3. In addition, the variable LASTWORD is always set to the last word added to USERWORDS, i.e., the last function or variable referred to by the user, and the respelling of NIL is defined to be the value of LASTWORD. Thus, if you had just defined a function, you can then prettyprint it by typing PP(). Each of the above four spelling lists are divided into two sections separated by a special marker (the value of the variable SPELLSTR1). The first section contains the "permanent" words; the second section contains the temporary words. New words are added to the corresponding spelling list at the front of its temporary section (except that functions added to SPELLINGS1 or SPELLINGS2 by LISPX are always added to the end of the permanent section. If the word is already in the temporary section, it is moved to the front of that section; if the word is in the permanent section, no action is taken. If the length of the temporary section then exceeds a specified number, the last (oldest) word in the temporary section is forgotten, i.e., deleted. This procedure prevents the spelling lists from becoming cluttered with unimportant words that are no longer being used, and thereby slowing down spelling correction time. Since the spelling corrector usually moves each word selected as a respelling to the front of its spelling list, the word is thereby moved into the permanent section. Thus once a word is misspelled and corrected, it is considered important and will never be forgotten. The spelling correction algorithm will not alter a spelling list unless it contains the special marker (the value of SPELLSTR1). This provides a way to ensure that a spelling list will not be altered. #SPELLINGS1(#SPELLINGS1 (Variable) NIL NIL ("20") 13) [Variable] #SPELLINGS2(#SPELLINGS2 (Variable) NIL NIL ("20") 13) [Variable] #SPELLINGS3(#SPELLINGS3 (Variable) NIL NIL ("20") 13) [Variable] #USERWORDS(#USERWORDS (Variable) NIL NIL ("20") 13) [Variable] The maximum length of the temporary section for SPELLINGS1, SPELLINGS2, SPELLINGS3 and USERWORDS is given by the value of #SPELLINGS1, #SPELLINGS2, #SPELLINGS3, and #USERWORDS, initialized to 30, 30, 30, and 60 respectively. You can alter these values to modify the performance behavior of spelling correction. Generators for Spelling Correction(SPELLING% CORRECTION NIL Spelling% correction NIL ("20") 13 SUBNAME GENERATORS% FOR SUBTEXT generators% for) For some applications, it is more convenient to generate candidates for a respelling one by one, rather than construct a complete list of all possible candidates, e.g., spelling correction involving a large directory of files, or a natural language data base. For these purposes, SPLST can be an array (of any size). The first element of this array is the generator function, which is called with the array itself as its argument. Thus the function can use the remainder of the array to store "state" information, e.g., the last position on a file, a pointer into a data structure, etc. The value returned by the function is the next candidate for respelling. If NIL is returned, the spelling "list" is considered to be exhausted, and the closest match is returned. If a candidate is found with no disagreements, it is returned immediately without waiting for the "list" to exhaust. SPLST can also be a generator, i.e. the value of the function GENERATOR (Chapter 11). The generator SPLST will be started up whenever the spelling corrector needs the next candidate, and it should return candidates via the function PRODUCE. For example, the following could be used as a "spelling list" which effectively contains all functions in the system: [GENERATOR (MAPATOMS (FUNCTION (LAMBDA (X) (if (GETD X) then (PRODUCE X] Spelling Corrector Algorithm(SPELLING% CORRECTION NIL Spelling% correction NIL ("20") 13 SUBNAME ALGORITHM SUBTEXT algorithm) The basic philosophy of DWIM spelling correction is to count the number of disagreements between two words, and use this number divided by the length of the longer of the two words as a measure of their relative disagreement. One minus this number is then the relative agreement or closeness. For example, CONS and CONX differ only in their last character. Such substitution errors count as one disagreement, so that the two words are in 75% agreement. Most calls to the spelling corrector specify a relative agreement of 70, so that a single substitution error is permitted in words of four characters or longer. However, spelling correction on shorter words is possible since certain types of differences such as single transpositions are not counted as disagreements. For example, AND and NAD have a relative agreement of 100. Calls to the spelling corrector from DWIM use the value of FIXSPELLREL, which is initially 70. Note that by setting FIXSPELLREL to 100, only spelling corrections with "zero" mistakes, will be considered, e.g., transpositions, double characters, etc. The central function of the spelling corrector is CHOOZ. CHOOZ takes as arguments: a word, a minimum relative agreement, a spelling list, and an optional functional argument, XWORD, REL, SPLST, and FN respectively. CHOOZ proceeds down SPLST examining each word. Words not satisfying FN (if FN is non-NIL), or those obviously too long or too short to be sufficiently close to XWORD are immediately rejected. For example, if REL = 70, and XWORD is 5 characters long, words longer than 7 characters will be rejected. Special treatment is necessary for words shorter than XWORD, since doubled letters are not counted as disagreements. For example, CONNSSS and CONS have a relative agreement of 100. CHOOZ handles this by counting the number of doubled characters in XWORD before it begins scanning SPLST, and taking this into account when deciding whether to reject shorter words. If TWORD, the current word on SPLST, is not rejected, CHOOZ computes the number of disagreements between it and XWORD by calling a subfunction, SKOR. SKOR operates by scanning both words from left to right one character at a time. SKOR operates on the list of character codes for each word. This list is computed by CHOOZ before calling SKOR. Characters are considered to agree if they are the same characters or appear on the same key (i.e., a shift mistake). The variable SPELLCASEARRAY is a CASEARRAY which is used to determine equivalence classes for this purpose. It is initialized to equivalence lowercase and upper case letters, as well as the standard key transitions: for example, 1 with !, 3 with #, etc. If the first character in XWORD and TWORD do not agree, SKOR checks to see if either character is the same as one previously encountered, and not accounted-for at that time. (In other words, transpositions are not handled by lookahead, but by lookback.) A displacement of two or fewer positions is counted as a tranposition; a displacement by more than two positions is counted as a disagreement.In either case, both characters are now considered as accounted for and are discarded, and SKORing continues. If the first character in XWORD and TWORD do not agree, and neither agree with previously unaccounted-for characters, and TWORD has more characters remaining than XWORD, SKOR removes and saves the first character of TWORD, and continues by comparing the rest of TWORD with XWORD as described above. If TWORD has the same or fewer characters remaining than XWORD, the procedure is the same except that the character is removed from XWORD. In this case, a special check is first made to see if that character is equal to the previous character in XWORD, or to the next character in XWORD, i.e., a double character typo, and if so, the character is considered accounted-for, and not counted as a disagreement. In this case, the "length" of XWORD is also decremented. Otherwise making XWORD sufficiently long by adding double characters would make it be arbitrarily close to TWORD, e.g., XXXXXX would correct to PP. When SKOR has finished processing both XWORD and TWORD in this fashion, the value of SKOR is the number of unaccounted-for characters, plus the number of disagreements, plus the number of tranpositions, with two qualifications: 1. If both XWORD and TWORD have a character unaccounted-for in the same position, the two characters are counted only once, i.e., substitution errors count as only one disagreement, not two 2. If there are no unaccounted-for characters and no disagreements, transpositions are not counted. This permits spelling correction on very short words, such as edit commands, e.g., XRT->XTR. Transpositions are also not counted when FASTYPEFLG = T, for example, IPULX and IPLUS will be in 80% agreement with FASTYPEFLG = T, only 60% with FASTYPEFLG = NIL. The rationale behind this is that transpositions are much more common for fast typists, and should not be counted as disagreements, whereas more deliberate typists are not as likely to combine tranpositions and other mistakes in a single word, and therefore can use more conservative metric. FASTYPEFLG is initially NIL. Spelling Corrector(SPELLING% CORRECTION NIL Spelling% correction NIL ("20") 14 SUBNAME FUNCTIONS SUBTEXT functions) Functions and Variables(SPELLING% CORRECTION NIL Spelling% correction NIL ("20") 14 SUBNAME VARIABLES SUBTEXT variables) (ADDSPELL(ADDSPELL (Function) NIL NIL ("20") 14) X SPLST N) [Function] Adds X to one of the spelling lists as determined by the value of SPLST: NIL Adds X to USERWORDS and to SPELLINGS2. Used by DEFINEQ. 0 Adds X to USERWORDS. Used by LOAD when loading EXPRs to property lists. 1 Adds X to SPELLINGS1 (at end of permanent section). Used by LISPX. 2 Adds X to SPELLINGS2 (at end of permanent section). Used by LISPX. 3 Adds X to USERWORDS and SPELLINGS3. a spelling list If SPLST is a spelling list, X is added to it. In this case, N is the (optional) length of the temporary section. If X is already on the spelling list, and in its temporary section, ADDSPELL moves X to the front of that section. ADDSPELL sets LASTWORD to X when SPLST = NIL, 0 or 3. If X is not a symbol, ADDSPELL takes no action. Note that the various systems calls to ADDSPELL, e.g., from DEFINE, EDITF, LOAD, etc., can all be suppressed by setting or binding ADDSPELLFLG to NIL (see the DWIM Functions and Variables section above). (MISSPELLED?(MISSPELLED? (Function) NIL NIL ("20") 15) XWORD REL SPLST FLG TAIL FN) [Function] If XWORD = NIL or $ (), MISSPELLED? prints = followed by the value of LASTWORD, and returns this as the respelling, without asking for approval. Otherwise, MISSPELLED? checks to see if XWORD is really misspelled, i.e., if FN applied to XWORD is true, or XWORD is already contained on SPLST. In this case, MISSPELLED? simply returns XWORD. Otherwise MISSPELLED? computes and returns (FIXSPELL XWORD REL SPLST FLG TAIL FN). (FIXSPELL(FIXSPELL (Function) NIL NIL ("20") 15) XWORD REL SPLST FLG TAIL FN TIEFLG DONTMOVETOPFLG) [Function] The value of FIXSPELL is either the respelling of or NIL. If for some reason itself is on , then FIXSPELL aborts and calls ERROR!. If there is a possibility that is spelled correctly, MISSPELLED? should be used instead of FIXSPELL. FIXSPELL performs all of the interactions described earlier, including requesting your approval if necessary. If XWORD = NIL or $ (escape), the respelling is the value of LASTWORD, and no approval is requested. If XWORD contains lowercase characters, and the corresponding uppercase word is correct, i.e. on SPLST or satisfies FN, the uppercase word is returned and no interaction is performed. If FIXSPELL.UPPERCASE.QUIET is NIL (the default), a warning "=XX" is printed when coercing from "xx" to "XX". If FIXSPELL.UPPERCASE.QUIET is non-NIL, no warning is given. If REL = NIL, defaults to the value of FIXSPELLREL (initially 70). If FLG = NIL, the correction is handled in type-in mode, i.e., approval is never requested, and XWORD is not typed. If FLG = T, XWORD is typed (before the =) and approval is requested if APPROVEFLG = T. If FLG = NO-MESSAGE, the correction is returned with no further processing. In this case, a run-on correction will be returned as a dotted pair of the two parts of the word, and a synonym correction as a list of the form (WORD1 WORD2), where WORD1 is (the corrected version of) XWORD, and WORD2 is the synonym. The effect of the function CHOOZ can be obtained by calling FIXSPELL with FLG = NO-MESSAGE. If TAIL is not NIL, and the correction is successful, CAR of TAIL is replaced by the respelling (using /RPLACA). FIXSPELL will attempt to correct misspellings caused by running two words together, if the global variable RUNONFLG is non-NIL (default is NIL). In this case, approval is always requested. When a run-on error is corrected, CAR of TAIL is replaced by the two words, and the value of FIXSPELL is the first one. For example, if FIXSPELL is called to correct the edit command (MOVE TO AFTERCOND 3 2) with TAIL = (AFTERCOND 3 2), TAIL would be changed to (AFTER COND 2 3), and FIXSPELL would return AFTER (subject to yourapproval where necessary). If TAIL = T, FIXSPELL will also perform run-on corrections, returning a dotted pair of the two words in the event the correction is of this type. If TIEFLG = NIL and a tie occurs, i.e., more than one word on SPLST is found with the same degree of "closeness", FIXSPELL returns NIL, i.e., no correction. If TIEFLG = PICKONE and a tie occurs, the first word is taken as the correct spelling. If TIEFLG = LIST, the value of FIXSPELL is a list of the respellings (even if there is only one), and FIXSPELL will not perform any interaction with you, nor modify TAIL, the idea being that the calling program will handle those tasks. Similarly, if TIEFLG = EVERYTHING, a list of all candidates whose degree of closeness is above REL will be returned, regardless of whether some are better than others. No interaction will be performed. If DONTMOVETOPFLG = T and a correction occurs, it will not be moved to the front of the spelling list. Also, the spelling list will not be altered unless it contains the special marker used to separate the temporary and perminant parts of the system spelling lists (the value of SPELLSTR1). (FNCHECK(FNCHECK (Function) NIL NIL ("20") 16) FN NOERRORFLG SPELLFLG PROPFLG TAIL) [Function] The task of FNCHECK is to check whether FN is the name of a function and if not, to correct its spelling. If FN is the name of a function or spelling correction is successful, FNCHECK adds the (corrected) name of the function to USERWORDS using ADDSPELL, and returns it as its value. Since FNCHECK is called by many low level functions such as ARGLIST, UNSAVEDEF, etc., spelling correction only takes place when DWIMFLG = T, so that these functions can operate in a small Interlisp system which does not contain DWIM. NOERRORFLG informs FNCHECK whether or not the calling function wants to handle the unsuccessful case: if NOERRORFLG is T, FNCHECK simply returns NIL, otherwise it prints fn NOT A FUNCTION and generates a non-breaking error. If FN does not have a definition, but does have an EXPR property, then spelling correction is not attempted. Instead, if PROPFLG = T, FN is considered to be the name of a function, and is returned. If PROPFLG = NIL, FN is not considered to be the name of a function, and NIL is returned or an error generated, depending on the value of NOERRORFLG. FNCHECK calls MISSPELLED? to perform spelling correction, so that if FN = NIL, the value of LASTWORD will be returned. SPELLFLG corresponds to MISSPELLED?'s fourth argument, FLG. If SPELLFLG = T, approval will be asked if DWIM was enabled in CAUTIOUS mode, i.e., if APPROVEFLG = T. TAIL corresponds to the fifth argument to MISSPELLED?. FNCHECK is currently used by ARGLIST, UNSAVEDEF, PRETTYPRINT, BREAK0, BREAKIN, ADVISE, and CALLS. For example, BREAK0 calls FNCHECK with NOERRORFLG = T since if FNCHECK cannot produce a function, BREAK0 wants to define a dummy one. CALLS however calls FNCHECK with NOERRORFLG = NIL, since it cannot operate without a function. Many other system functions call MISSPELLED? or FIXSPELL directly. For example, BREAK1 calls FIXSPELL on unrecognized atomic inputs before attempting to evaluate them, using as a spelling list a list of all break commands. Similarly, LISPX calls FIXSPELL on atomic inputs using a list of all LISPX commands. When UNBREAK is given the name of a function that is not broken, it calls FIXSPELL with two different spelling lists, first with BROKENFNS, and if that fails, with USERWORDS. MAKEFILE calls MISSPELLED? using FILELST as a spelling list. Finally, LOAD, BCOMPL, BRECOMPILE, TCOMPL, and RECOMPILE all call MISSPELLED? if their input file(s) won't open. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "19-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "19-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "19-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "19-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "19-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "19-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))56065,HH56T +T//HH,HH,``,``@ PAGEHEADINGLEFTBACKT-T,306T3llT3HHT306 +T25-HHT,HH,ll/2Z2Z,HH,ll,3(T,HH,, PALATINO PALATINO PALATINO TITAN TITAN TITAN MODERN +MODERNMODERN + +  +  IM.CHAP.GETFNMODERN +   HRULE.GETFNMODERN + ]&mA>H7  + [h2  +&+ @ +9 XJ! + "  ''P, <#) J " + # 0@2>s9  %#!M,{ +s,E`ZN"IM.INDEX.GETFNTITAN  - +/$  4M"|/5   HRULE.GETFNMODERN + ~s589  X ]"= $ $ aN ) !Q%NKH4G%Gm   HRULE.GETFNMODERN + T0 +9 +-  +y = +!Vr   HRULE.GETFNMODERN + GHJ6*4249 y .K>  +#. ,) +1 (q'% +E  +Q?IM.INDEX.GETFN   HRULE.GETFNMODERN + YF ! +   +U:  +,D C:  += 4 +N7F6<$#$R K2(W U> / 7 '$(7   HK     + h.(1:(8$ +)  0(T&S +'[%}(!7     ,=i  D  $ \/ += #* ?J$ +Db &/ 5 K  +#* +%6 F  B{, +, +    HRULE.GETFNMODERN +   +IM.INDEX.GETFNAD V <F M  +6 6 +   /; ^ I  1 ;$IM.INDEX.GETFNTITAN  31   'IM.INDEX.GETFNTITAN     ,   +IM.INDEX.GETFNTITAN      ]  a L  "IM.INDEX.GETFNTITAN    E  $IM.INDEX.GETFNTITAN  1 &IM.INDEX.GETFNTITAN  & %IM.INDEX.GETFNTITAN  /)3  +(IM.INDEX.GETFNTITAN  (6 "IM.INDEX.GETFNTITAN  0   C?IM.INDEX.GETFN?IM.INDEX.GETFN  HRULE.GETFNMODERN + &IM.INDEX.GETFNTITAN  n -IM.INDEX.GETFNTITAN  a   )IM.INDEX.GETFNTITAN     +)IM.INDEX.GETFNTITAN   )E + 'IM.INDEX.GETFNTITAN  5 -IM.INDEX.GETFNTITAN   &IM.INDEX.GETFNTITAN  +  &IM.INDEX.GETFNTITAN  ! dc   +)IM.INDEX.GETFNTITAN    +1 +n  + &IM.INDEX.GETFNTITAN     +  + +IM.INDEX.GETFN  +v     +  +   *IM.INDEX.GETFNTITAN   M @ ) =IM.INDEX.GETFN  HRULE.GETFNMODERN + 17 ++D ?s+-  |4A.!HiB?  +9  +J   _IM.INDEX.GETFN D& h>  +A   +   ;)mIM.INDEX.GETFN 8 F   + + + &   +)IM.INDEX.GETFNTITAN   + + +i +#   +   +)  +)IM.INDEX.GETFNTITAN   +B +  +# +    +)IM.INDEX.GETFNTITAN   +G + +   + @ +2 + X + +# +  (IM.INDEX.GETFNTITAN    A# $   $C  # ' +) UR}  + +u M *IM.INDEX.GETFNTITAN   *IM.INDEX.GETFNTITAN   *IM.INDEX.GETFNTITAN   +)IM.INDEX.GETFNTITAN  0 + + +     +2V "mIM.INDEX.GETFN 0~9 x ?aIM.INDEX.GETFN ^ . {2q+H+H6G#>N5MQ Q$*)1FX 'U hS+ + + +( +aIM.INDEX.GETFNaIM.INDEX.GETFN 'IM.INDEX.GETFNTITAN  <  +    +) +)  + 4@ ' 4 7 *IM.INDEX.GETFNTITAN   + N          'IM.INDEX.GETFNTITAN  + +   !*9 f* YF! T + +- +#'c S0$'/ }// I?7R +>i"  &IM.INDEX.GETFNTITAN  +  DA. / 2Y + O +%.CB.> + ,  +&    + + +.! &?/    +  + $ z \ No newline at end of file diff --git a/docs/turpin-irm/IRM-2/20-CLISP.TEDIT b/docs/turpin-irm/IRM-2/20-CLISP.TEDIT new file mode 100644 index 00000000..b16edf46 --- /dev/null +++ b/docs/turpin-irm/IRM-2/20-CLISP.TEDIT @@ -0,0 +1,107 @@ +INTERLISP-D REFERENCE MANUAL CLISP "20"20. CLISP 2 The syntax of Lisp is very simple. It can be described concisely, but it makes Lisp difficult to read and write without tools. Unlike many languages, there are no reserved words in Lisp such as IF, THEN, FOR, DO, etc., nor reserved characters like +, -, =, , etc. The only components of the language are atoms and delimiters. This eliminates the need for parsers and precedence rules, and makes Lisp programs easy to mainpuilate. For example, a Lisp interpreter can be written in one or two pages of Lisp code. This makes Lisp the most suitable programming language for writing programs that deal with other programs as data. Human language is based on more complicated structures and relies more on special words to carry the meaning. The definiton of the factorial function looks like this in Lisp: (COND ((ZEROP N) 1) (T (TIMES N (FACTORIAL ((SUB1 N)))))) This definition is easy to read for a machine but difficult to read for a human. CLISP(CLISP NIL NIL NIL ("21") 1) is designed to make Interlisp programs easier to read and write. CLISP does this by translating various operators, conditionals, and iterative statements to Interlisp. For example, factorial can be written in CLISP: (IF N = 0 THEN 1 ELSE N*(FACTORIAL N-1)) CLISP will translate this expression to the form in the example above. The translation will take place when the form is read so there are no performance penalties. You should view CLISP as a shothand for produceing Lisp programs. CLISP makes a program easy to read and sometimes more compact. CLISP is implemented via the error correction machinery in Interlisp (see Chapter 20). Any expression that Interlisp thinks is well-formed will never be seen by CLISP This means that interpreted programs that do not use CLISP constructs do not pay for its availability by slower execution time. In fact, the Interlisp interpreter does not know about CLISP at all. When the interpreter finds an error it calls an error routine which in turn invokes the Do-What-I-Mean (DWIM) analyzer. The DWIM analyzer knows how to deal with CLISP expressions. If the expression in question turns out to be a CLISP construct, the translated form is returned to the interpreter. In addition, the original CLISP expression is modified so that it becomes the correctly translated Interlisp form. In this way, the analysis and translation are done only once. Integrating CLISP into Medley makes possible Do-What-I-Mean features for CLISP constructs as well as for pure Lisp expressions. For example, if you have defined a function named GET-PARENT, CLISP would know not to attempt to interpret the form (GET-PARENT) as an arithmetic infix operation. (Actually, CLISP would never get to see this form, since it does not contain any errors.) If you mistakenly write (GET-PRAENT), CLISP would know you meant (GET-PARENT), and not (DIFFERENCE GET PRAENT), by using the information that PARENT is not the name of a variable, and that GET-PARENT is the name of a user function whose spelling is "very close" to that of GET-PRAENT. Similarly, by using information about the program's environment not readily available to a preprocessor, CLISP can successfully resolve the following sorts of ambiguities: 1. (LIST X*FACT N), where FACT is the name of a variable, means (LIST (X*FACT) N). 2. (LIST X*FACT N), where FACT is not the name of a variable but instead is the name of a function, means (LIST X*(FACT N)), i.e., N is FACT's argument. 3. (LIST X*FACT(N)), FACT the name of a function (and not the name of a variable), means (LIST X*(FACT N)). 4. Cases 1, 2 and 3 with FACT misspelled! The first expression is correct both from the standpoint of CLISP syntax and semantics so the change would be made notification. In the other cases, you would be informed or consulted about what was taking place. For example, suppose you write the expression (LIST X*FCCT N). Assume also that there was both a function named FACT and a variable named FCT. 1. You will first be asked if FCCT is a misspelling of FCT. If you say YES, the expression will be interpreted as (LIST (X*FCT) N). If you say NO, you will be asked if FCCT was a misspelling of FACT, i.e., if you intended X*FCCT N to mean X*(FACT N). 2. If you say YES to this question, the indicated transformation will be performed. If you say NO, the system will ask if X*FCCT should be treated as CLISP, since FCCT is not the name of a (bound) variable. 3. If you say YES, the expression will be transformed, if NO, it will be left alone, i.e., as (LIST X*FCCT N). Note that we have not even considered the case where X*FCCT is itself a misspelling of a variable name, e.g., a variable named XFCT (as with GET-PRAENT). This sort of transformation will be considered after you said NO to X*FCCT N -> X*(FACT N). The question of whether X*FCCT should be treated as CLISP is important because Interlisp users may have programs that employ identifiers containing CLISP operators. Thus, if CLISP encounters the expression A/B in a context where either A or B are not the names of variables, it will ask you if A/B is intended to be CLISP, in case you really do have a free variable named A/B. Note: Through the discussion above, we speak of CLISP or DWIM asking you. Actually, if you typed in the expression in question for immediate execution, you are simply informed of the transformation, on the grounds that you would prefer an occasional misinterpretation rather than being continuously bothered, especially since you can always retype what you intended if a mistake occurs, and ask the programmer's assistant to UNDO the effects of the mistaken operations if necessary. For transformations on expressions in your programs, you can tell CLISP whether you wish to operate in CAUTIOUS or TRUSTING mode. In the former case (most typical) you will be asked to approve transformations, in the latter, CLISP will operate as it does on type-in, i.e., perform the transformation after informing you. CLISP can also handle parentheses errors caused by typing 8 or 9 for ( or ). (On most terminals, 8 and 9 are the lowercase characters for ( and ), i.e., ( and 8 appear on the same key, as do ) and 9.) For example, if you write N*8FACTORIAL N-1, the parentheses error can be detected and fixed before the infix operator * is converted to the Interlisp function TIMES. CLISP is able to distinguish this situation from cases like N*8*X meaning (TIMES N 8 X), or N*8X, where 8X is the name of a variable, again by using information about the programming environment. In fact, by integrating CLISP with DWIM, CLISP has been made sufficiently tolerant of errors that almost everything can be misspelled! For example, CLISP can successfully translate the definition of FACTORIAL: (IFF N = 0 THENN1 ESLE N*8FACTTORIALNN-1) to the corresponding COND, while making five spelling corrections and fixing the parenthesis error. CLISP also contains a facility for converting from Interlisp back to CLISP, so that after running the above incorrect definition of FACTORIAL, you could "clispify" the now correct version to obtain (IF N = 0 THEN 1 ELSE N*(FACTORIAL N-1)). This sort of robustness prevails throughout CLISP. For example, the iterative statement permits you to say things like: (FOR OLD X FROM M TO N DO (PRINT X) WHILE (PRIMEP X)) However, you can also write OLD (XM), (OLD XM), (OLD (XM)), permute the order of the operators, e.g., (DO PRINT X TO N FOR OLD XM WHILE PRIMEP X), omit either or both sets of parentheses, misspell any or all of the operators FOR, OLD, FROM, TO, DO, or WHILE, or leave out the word DO entirely! And, of course, you can also misspell PRINT, PRIMEP, M or N! In this example, the only thing you could not misspell is the first X, since it specifies the name of the variable of iteration. The other two instances of X could be misspelled. CLISP is well integrated into Medley. For example, the above iterative statement translates into an equivalent Interlisp form using PROG, COND, GO, etc. When the interpreter subsequently encounters this CLISP expression, it automatically obtains and evaluates the translation. Similarly, the compiler "knows" to compile the translated form. However, if you PRETTYPRINT your program, PRETTYPRINT "knows" to print the original CLISP at the corresponding point in your function. Similarly, when you edit your program, the editor keeps the translation invisible to you. If you modify the CLISP, the translation is automatically discarded and recomputed the next time the expression is evaluated. In short, CLISP is not a language at all, but rather a system. It plays a role analagous to that of the programmer's assistant (Chapter 13). Whereas the programmer's assistant is an invisible intermediary agent between your console requests and the Interlisp executive, CLISP sits between your programs and the Interlisp interpreter. Only a small effort has been devoted to defining the core syntax of CLISP. Instead, most of the effort has been concentrated on providing a facility which "makes sense" out of the input expressions using context information as well as built-in and acquired information about user and system programs. It has been said that communication is based on the intention of the speaker to produce an effect in the recipient. CLISP operates under the assumption that what you say is intended to represent a meaningful operation, and therefore tries very hard to make sense out of it. The motivation behind CLISP is not to provide you with many different ways of saying the same thing, but to enable you to worry less about the syntactic aspects of your communication with the system. In other words, it gives you a new degree of freedom by permitting you to concentrate more on the problem at hand, rather than on translation into a formal and unambiguous language. DWIM and CLISP are invoked on iterative statements because CAR of the iterative statement is not the name of a function, and hence generates an error. If you define a function by the same name as an i.s. operator, e.g., WHILE, TO, etc., the operator will no longer have the CLISP interpretation when it appears as CAR of a form, although it will continue to be treated as an i.s. operator if it appears in the interior of an i.s. To alert you, a warning message is printed, e.g., (WHILE DEFINED, THEREFORE DISABLED IN CLISP). CLISP(CLISP NIL NIL NIL ("21") 4 SUBNAME INTERACTION% WITH% USER SUBTEXT interaction% with% user) Interaction with User 1 Syntactically and semantically well formed CLISP transformations are always performed without informing you. Other CLISP transformations described in the previous section, e.g., misspellings of operands, infix operators, parentheses errors, unary minus - binary minus errors, all follow the same protocol as other DWIM transformations (Chapter 19). That is, if DWIM has been enabled in TRUSTING mode, or the transformation is in an expression you typed in for immediate execution, your approval is not requested, but you are informed. However, if the transformation involves a user program, and DWIM was enabled in CAUTIOUS mode, you will be asked to approve. If you say NO, the transformation is not performed. Thus, in the previous section, phrases such as "one of these (transformations) succeeds" and "the transformation LAST-ELL -> LAST-EL would be found" etc., all mean if you are in CAUTIOUS mode and the error is in a program, the corresponding transformation will be performed only if you approve (or defaults by not responding). If you say NO, the procedure followed is the same as though the transformation had not been found. For example, if A*B appears in the function FOO, and B is not bound (and no other transformations are found) you would be asked A*B [IN FOO] TREAT AS CLISP ? (The waiting time on such interactions is three times as long as for simple corrections, i.e., 3*DWIMWAIT). In certain situations, DWIM asks for approval even if DWIM is enabled in TRUSTING mode. For example, you are always asked to approve a spelling correction that might also be interpreted as a CLISP transformation, as in LAST-ELL -> LAST-EL. If you approved, A*B would be transformed to (ITIMES A B), which would then cause a U.B.A.B. error in the event that the program was being run (remember the entire discussion also applies to DWIMifying). If you said NO, A*B would be left alone. If the value of CLISPHELPFLG = NIL (initally T), you will not be asked to approve any CLISP transformation. Instead, in those situations where approval would be required, the effect is the same as though you had been asked and said NO. CLISP(CLISP NIL NIL NIL ("21") 4 SUBNAME CHARACTER% OPERATORS SUBTEXT character% operators) Character Operators 1 CLISP recognizes a number of special characters operators, both prefix and infix, which are translated into common expressions. For example, the character + is recognized to represent addition, so CLISP translates the symbol A+B to the form (IPLUS A B). Note that CLISP is invoked, and this translation is made, only if an error occurs, such as an unbound atom error or an undefined function error for the perfectly legitamate symbol A+B. Therefore you may choose not to use these facilities with no penalty, similar to other CLISP facilities. You have a lot of flexability in using CLISP character operators. A list can always be substituted for a symbol, and vice versa, without changing the interpretation of a phrase. For example, if the value of (FOO X) is A, and the value of (FIE Y) is B, then (LIST (FOO X)+(FIE Y)) has the same value as (LIST A+B). Note that the first expression is a list of four elements: the atom "LIST", the list "(FOO X)", the atom "+", and the list "(FIE X)", whereas the second expression, (LIST A+B), is a list of only two elements: the symbol "LIST" and the symbol "A+B". Since (LIST (FOO X)+(FIE Y)) is indistinguishable from (LIST (FOO X) + (FIE Y)) because spaces before or after parentheses have no effect on the Interlisp READ program, to be consistent, extra spaces have no effect on atomic operands either. In other words, CLISP will treat (LIST A+ B), (LIST A +B), and (LIST A + B) the same as (LIST A+B). Note: CLISP does not use its own special READ program because this would require you to explicitly identify CLISP expressions, instead of being able to intermix Interlisp and CLISP. +(+ (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] -(- (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] *(* (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] /(/ (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] ( (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] CLISP recognizes +, -, *, /, and as the normal arithmetic infix operators. The - is also recognized as the prefix operator, unary minus. These are converted to PLUS, DIFFERENCE (or in the case of unary minus, MINUS), TIMES, QUOTIENT, and EXPT. Normally, CLISP uses the "generic" arithmetic functions PLUS, TIMES, etc. CLISP contains a facility for declaring which type of arithmetic is to be used, either by making a global declaration, or by separate declarations about individual functions or variables. The usual precedence rules apply (although you can easily change them), i.e., * has higher precedence than + so that A+B*C is the same as A+(B*C), and both * and / are lower than so that 2*X2 is the same as 2*(X2). Operators of the same precedence group from left to right, e.g., A/B/C is equivalent to (A/B)/C. Minus is binary whenever possible, i.e., except when it is the first operator in a list, as in (-A) or (-A), or when it immediately follows another operator, as in A*-B. Note that grouping with parentheses can always be used to override the normal precedence grouping, or when you are not sure how a particular expression will parse. The complete order of precedence for CLISP operators is given below. Note that + in front of a number will disappear when the number is read, e.g., (FOO X +2) is indistinguishable from (FOO X 2). This means that (FOO X +2) will not be interpreted as CLISP, or be converted to (FOO (IPLUS X 2)). Similarly, (FOO X -2) will not be interpreted the same as (FOO X-2). To circumvent this, always type a space between the + or - and a number if an infix operator is intended, e.g., write (FOO X + 2). =(= (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] GT(GT (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] LT(LT (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] GE(GE (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] LE(LE (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] These are infix operators for "Equal", "Greater Than", "Less Than", "Greater Than or Equal", and "Less Than or Equal". GT, LT, GE, and LE are all affected by the same declarations as + and *, with the initial default to use GREATERP and LESSP. Note that only single character operators, e.g., +, , =, etc., can appear in the interior of an atom. All other operators must be set off from identifiers with spaces. For example, XLTY will not be recognized as CLISP. In some cases, DWIM will be able to diagnose this situation as a run-on spelling error, in which case after the atom is split apart, CLISP will be able to perform the indicated transformation. A number of Lisp functions, such as EQUAL, MEMBER, AND, OR, etc., can also be treated as CLISP infix operators. New infix operators can be easily added (see the CLISP Internal Convetions section below). Spelling correction on misspelled infix operators is peformed using CLISPINFIXSPLST as a spelling list. AND is higher than OR, and both AND and OR are lower than the other infix operators, so (X OR Y AND Z) is the same as (X OR (Y AND Z)), and (X AND Y EQUAL Z) is the same as (X AND (Y EQUAL Z)). All of the infix predicates have lower precedence than Interlisp forms, since it is far more common to apply a predicate to two forms, than to use a Boolean as an argument to a function. Therefore, (FOO X GT FIE Y) is translated as ((FOO X) GT (FIE Y)), rather than as (FOO (X GT (FIE Y))). However, you can easily change this. :(: (CLISP Operator) NIL NIL ("21") 6) [CLISP Operator] X:N extracts the Nth element of the list X. FOO:3 specifies the third element of FOO, or (CADDR FOO). If N is less than zero, this indicates elements counting from the end of the list; i.e. FOO:-1 is the last element of FOO. : operators can be nested, so FOO:1:2 means the second element of the first element of FOO, or (CADAR FOO). The : operator can also be used for extracting substructures of records (see Chapter 8). Record operations are implemented by replacing expressions of the form X:FOO by (fetch FOO of X). Both lower- and uppercase are acceptable. : is also used to indicate operations in the pattern match facility (see Chapter 12). X:(& 'A -- 'B) translates to (match X with (& 'A -- 'B)) .(%. (CLISP Operator) NIL NIL ("21") 6) [CLISP Operator] In combination with :, a period can be used to specify the "data path" for record operations. For example, if FOO is a field of the BAR record, X:BAR.FOO is translated into (fetch (BAR FOO) of X). Subrecord fields can be specified with multiple periods: X:BAR.FOO.BAZ translates into (fetch (BAR FOO BAZ) of X). Note: If a record contains fields with periods in them, CLISPIFY will not translate a record operation into a form using periods to specify the data path. For example, CLISPIFY will NOT translate (fetch A.B of X) into X:A.B. ::(:: (CLISP Operator) NIL NIL ("21") 6) [CLISP Operator] X:N, returns the Nth tail of the list X. For example, FOO::3 is (CDDDR FOO), and FOO::-1 is (LAST FOO). [CLISP Operator] is used to indicate assignment. For example, XY translates to (SETQ X Y). If X does not have a value, and is not the name of one of the bound variables of the function in which it appears, spelling correction is attempted. However, since this may simply be a case of assigning an initial value to a new free variable, DWIM will always ask for approval before making the correction. In conjunction with : and ::, can also be used to perform a more general type of assignment, involving structure modification. For example, X:2Y means "make the second element of X be Y", in Interlisp terms (RPLACA (CDR X) Y). Note that the value of this operation is the value of RPLACA, which is (CDR X), rather than Y. Negative numbers can also be used, e.g., X:-2_Y, which translates to (RPLACA (NLEFT X 2) Y). You can indicate you want /RPLACA and /RPLACD used (undoable version of RPLACA and RPLACD, see Chapter 13), or FRPLACA and FRPLACD (fast versions of RPLACA and RPLACD, see Chapter 3), by means of CLISP declarations. The initial default is to use RPLACA and RPLACD. is also used to indicate assignment in record operations (X:FOOY translates to (replace FOO of X with Y).), and pattern match operations (Chapter 12). has different precedence on the left from on the right. On the left, is a "tight" operator, i.e., high precedence, so that A+BC is the same as A+(BC). On the right, has broader scope so that AB+C is the same as A(B+C). On type-in, $FORM (where $ is the escape key) is equivalent to set the "last thing mentioned", i.e., is equivalent to (SET LASTWORD FORM) (see Chapter 20). For example, immediately after examining the value of LONGVARIABLENAME, you could set it by typing $ followed by a form. Note that an atom of the form XY, appearing at the top level of a PROG, will not be recognized as an assignment statement because it will be interpreted as a PROG label by the Interlisp interpreter, and therefore will not cause an error, so DWIM and CLISP will never get to see it. Instead, one must write (XY). < [CLISP Operator] > [CLISP Operator] Angle brackets are used in CLISP to indicate list construction. The appearance of a "<" corresponds to a "(" and indicates that a list is to be constructed containing all the elements up to the corresponding ">". For example, > translates to (LIST A B (LIST C)). ! can be used to indicate that the next expression is to be inserted in the list as a segment, e.g., translates to (CONS A (CONS B C)) and to (APPEND A B (LIST C)). !! is used to indicate that the next expression is to be inserted as a segment, and furthermore, all list structure to its right in the angle brackets is to be physically attached to it, e.g., translates to (NCONC1 A B), and to (NCONC A (APPEND B C)). Not (NCONC (APPEND A B) C), which would have the same value, but would attach C to B, and not attach either to A. Note that <, !, !!, and > need not be separate atoms, for example, may be written equally well as < A B !C >. Also, arbitrary Interlisp or CLISP forms may be used within angle brackets. For example, one can write which translates to (CONS (SETQ FOO (FIE X)) Y). CLISPIFY converts expressions in CONS, LIST, APPEND, NCONC, NCONC1, /NCONC, and /NCONC1 into equivalent CLISP expressions using <, >, !, and !!. Note: brackets differ from other CLISP operators. For example, translates to (LIST A B (QUOTE C)) even though following ', all operators are ignored for the rest of the identifier. (This is true only if a previous unmatched < has been seen, e.g., (PRINT 'A>B) will print the atom A>B.) Note however that D> is equivalent to (LIST A B (QUOTE C>) D). ' [CLISP Operator] CLISP recognizes ' as a prefix operator. ' means QUOTE when it is the first character in an identifier, and is ignored when it is used in the interior of an identifier. Thus, X = 'Y means (EQ X (QUOTE Y)), but X = CAN'T means (EQ X CAN'T), not (EQ X CAN) followed by (QUOTE T). This enables users to have variable and function names with ' in them (so long as the ' is not the first character). Following ', all operators are ignored for the rest of the identifier, e.g., '*A means (QUOTE *A), and 'X=Y means (QUOTE X=Y), not (EQ (QUOTE X) Y). To write (EQ (QUOTE X) Y), one writes Y='X, or 'X =Y. This is one place where an extra space does make a difference. On type-in, '$ (escape) is equivalent to (QUOTE VALUE-OF-LASTWORD) (see Chapter 19). For example, after calling PRETTYPRINT on LONGFUNCTION, you could move its definition to FOO by typing (MOVD '$ 'FOO). Note that this is not (MOVD $ 'FOO), which would be equivalent to (MOVD LONGFUNCTION 'FOO), and would (probably) cause a U.B.A. LONGFUNCTION error, nor MOVD($ FOO), which would actually move the definition of $ to FOO, since DWIM and the spelling corrector would never be invoked. ~ [CLISP Operator] CLISP recognizes ~ as a prefix operator meaning NOT. ~ can negate a form, as in ~(ASSOC X Y), or ~X, or negate an infix operator, e.g., (A ~GT B) is the same as (A LEQ B). Note that ~A = B means (EQ (NOT A) B). When ~ negates an operator, e.g., ~=, ~LT, the two operators are treated as a single operator whose precedence is that of the second operator. When ~ negates a function, e.g., (~FOO X Y), it negates the whole form, i.e., (~(FOO X Y)). Order of Precedence of CLISP Operators: ' : (left precedence) - (unary), ~ *, / +, - (binary) (right precedence) = Interlisp forms LT, GT, EQUAL, MEMBER, etc. AND OR IF, THEN, ELSEIF, ELSE iterative statement operators Declarations 1 CLISP declarations are used to affect the choice of Interlisp function used as the translation of a particular operator. For example, A+B can be translated as either (PLUS A B), (FPLUS A B), or (IPLUS A B), depending on the declaration in effect. Similarly X:1Y can mean (RPLACA X Y), (FRPLACA X Y), or (/RPLACA X Y), and either (NCONC1 A B) or (/NCONC1 A B). Note that the choice of function on all CLISP transformations are affected by the CLISP declaration in effect, i.e., iterative statements, pattern matches, record operations, as well as infix and prefix operators. (CLISPDEC(CLISPDEC (Function) NIL NIL ("21") 8) DECLST) [Function] Puts into effect the declarations in DECLST. CLISPDEC performs spelling corrections on words not recognized as declarations. CLISPDEC is undoable. You can makes (changes) a global declaration by calling CLISPDEC with DECLST a list of declarations, e.g., (CLISPDEC '(FLOATING UNDOABLE)). Changing a global declaration does not affect the speed of subsequent CLISP transformations, since all CLISP transformation are table driven (i.e., property list), and global declarations are accomplished by making the appropriate internal changes to CLISP at the time of the declaration. If a function employs local declarations (described below), there will be a slight loss in efficiency owing to the fact that for each CLISP transformation, the declaration list must be searched for possibly relevant declarations. Declarations are implemented in the order that they are given, so that later declarations override earlier ones. For example, the declaration FAST specifies that FRPLACA, FRPLACD, FMEMB, and FLAST be used in place of RPLACA, RPLACD, MEMB, and LAST; the declaration RPLACA specifies that RPLACA be used. Therefore, the declarations (FAST RPLACA RPLACD) will cause FMEMB, FLAST, RPLACA, and RPLACD to be used. The initial global declaration is MIXED and STANDARD. The table below gives the declarations available in CLISP, and the Interlisp functions they indicate: Declaration: Interlisp Functions to be used: MIXED PLUS, MINUS, DIFFERENCE, TIMES, QUOTIENT, LESSP, GREATERP INTEGER or FIXED IPLUS, IMINUS, IDIFFERENCE, ITIMES, IQUOTIENT, ILESSP, IGREATERP FLOATING FPLUS, FMINUS, FDIFFERENCE, FTIMES, FQUOTIENT, LESSP, FGREATERP FAST FRPLACA, FRPLACD, FMEMB, FLAST, FASSOC UNDOABLE /RPLACA, /RPLACD, /NCONC, /NCONC1, /MAPCONC, /MAPCON STANDARD RPLACA, RPLACD, MEMB, LAST, ASSOC, NCONC, NCONC1, MAPCONC, MAPCON RPLACA, RPLACD, /RPLACA, etc. corresponding function You can also make local declarations affecting a selected function or functions by inserting an expression of the form (CLISP: . DECLARATIONS) immediately following the argument list, i.e., as CADDR of the definition. Such local declarations take precedence over global declarations. Declarations affecting selected variables can be indicated by lists, where the first element is the name of a variable, and the rest of the list the declarations for that variable. For example, (CLISP: FLOATING (X INTEGER)) specifies that in this function integer arithmetic be used for computations involving X, and floating arithmetic for all other computations, where "involving" means where the variable itself is an operand. For example, with the declaration (FLOATING (X INTEGER)) in effect, (FOO X)+(FIE X) would translate to FPLUS, i.e., use floating arithmetic, even though X appears somewhere inside of the operands, whereas X+(FIE X) would translate to IPLUS. If there are declarations involving both operands, e.g., X+Y, with (X FLOATING) (Y INTEGER), whichever appears first in the declaration list will be used. You can also make local record declarations by inserting a record declaration, e.g., (RECORD --), (ARRAYRECORD --), etc., in the local declaration list. In addition, a local declaration of the form (RECORDS A B C) is equivalent to having copies of the global declarations A, B, and C in the local declaration. Local record declarations override global record declarations for the function in which they appear. Local declarations can also be used to override the global setting of certain DWIM/CLISP parameters effective only for transformations within that function, by including in the local declaration an expression of the form (VARIABLE = VALUE), e.g., (PATVARDEFAULT = QUOTE). The CLISP: expression is converted to a comment of a special form recognized by CLISP. Whenever a CLISP transformation that is affected by declarations is about to be performed in a function, this comment will be searched for a relevant declaration, and if one is found, the corresponding function will be used. Otherwise, if none are found, the global declaration(s) currently in effect will be used. Local declarations are effective in the order that they are given, so that later declarations can be used to override earlier ones, e.g., (CLISP: FAST RPLACA RPLACD) specifies that FMEMB, FLAST, RPLACA, and RPLACD be used. An exception to this is that declarations for specific variables take precedence of general, function-wide declarations, regardless of the order of appearance, as in (CLISP: (X INTEGER) FLOATING). CLISPIFY also checks the declarations in effect before selecting an infix operator to ensure that the corresponding CLISP construct would in fact translate back to this form. For example, if a FLOATING declaration is in effect, CLISPIFY will convert (FPLUS X Y) to X+Y, but leave (IPLUS X Y) as is. If (FPLUS X Y) is CLISPIFYed while a FLOATING declaration is under effect, and then the declaration is changed to INTEGER, when X+Y is translated back to Interlisp, it will become (IPLUS X Y). CLISP Operation 1 CLISP is a part of the basic Medley system. Without any special preparations, you can include CLISP constructs in programs, or type them in directly for evaluation (in EVAL or APPLY format), then, when the "error" occurrs, and DWIM is called, it will destructively transform the CLISP to the equivalent Interlisp expression and evaluate the Interlisp expression. CLISP transformations, like all DWIM corrections, are undoable. User approval is not requested, and no message is printed. This entire discussion also applies to CLISP transformation initiated by calls to DWIM from DWIMIFY. However, if a CLISP construct contains an error, an appropriate diagnostic is generated, and the form is left unchanged. For example, if you write (LIST X+Y*), the error diagnostic MISSING OPERAND AT X+Y* IN (LIST X+Y*) would be generated. Similarly, if you write (LAST+EL X), CLISP knows that ((IPLUS LAST EL) X) is not a valid Interlisp expression, so the error diagnostic MISSING OPERATOR IN (LAST+EL X) is generated. (For example, you might have meant to say (LAST+EL*X).) If LAST+EL were the name of a defined function, CLISP would never see this form. Since the bad CLISP transformation might not be CLISP at all, for example, it might be a misspelling of a user function or variable, DWIM holds all CLISP error messages until after trying other corrections. If one of these succeeds, the CLISP message is discarded. Otherwise, if all fail, the message is printed (but no change is made). For example, suppose you type (R/PLACA X Y). CLISP generates a diagnostic, since ((IQUOTIENT R PLACA) X Y) is obviously not right. However, since R/PLACA spelling corrects to /RPLACA, this diagnostic is never printed. Note: CLISP error messages are not printed on type-in. For example, typing X+*Y will just produce a U.B.A. X+*Y message. If a CLISP infix construct is well formed from a syntactic standpoint, but one or both of its operands are atomic and not bound, it is possible that either the operand is misspelled, e.g., you wrote X+YY for X+Y, or that a CLISP transformation operation was not intended at all, but that the entire expression is a misspelling. For the purpose of DWIMIFYing, "not bound" means no top level value, not on list of bound variables built up by DWIMIFY during its analysis of the expression, and not on NOFIXVARSLST, i.e., not previously seen. For example, if you have a variable named LAST-EL, and write (LIST LAST-ELL). Therefore, CLISP computes, but does not actually perform, the indicated infix transformation. DWIM then continues, and if it is able to make another correction, does so, and ignores the CLISP interpretation. For example, with LAST-ELL, the transformation LAST-ELL -> LAST-EL would be found. If no other transformation is found, and DWIM is about to interpret a construct as CLISP for which one of the operands is not bound, DWIM will ask you whether CLISP was intended, in this case by printing LAST-ELL TREAT AS CLISP ?. Note: If more than one infix operator was involved in the CLISP construct, e.g., X+Y+Z, or the operation was an assignment to a variable already noticed, or TREATASCLISPFLG is T (initially NIL), you will simply be informed of the correction, e.g., X+Y+Z TREATED AS CLISP. Otherwise, even if DWIM was enabled in TRUSTING mode, you will be asked to approve the correction. The same sort of procedure is followed with 8 and 9 errors. For example, suppose you write FOO8*X where FOO8 is not bound. The CLISP transformation is noted, and DWIM proceeds. It next asks you to approve FOO8*X -> FOO ( *X. For example, this would make sense if you have (or plan to define) a function named *X. If you refuses, you are asked whether FOO8*X is to be treated as CLISP. Similarly, if FOO8 were the name of a variable, and you write FOOO8*X, you will first be asked to approve FOOO8*X -> FOOO ( XX, and if you refuse, then be offered the FOOO8 -> FOO8 correction. The 8-9 transformation is tried before spelling correction since it is empirically more likely that an unbound atom or undefined function containing an 8 or a 9 is a parenthesis error, rather than a spelling error. CLISP also contains provision for correcting misspellings of infix operators (other than single characters), IF words, and i.s. operators. This is implemented in such a way that the user who does not misspell them is not penalized. For example, if you write IF N = 0 THEN 1 ELSSE N*(FACT N-1) CLISP does not operate by checking each word to see if it is a misspelling of IF, THEN, ELSE, or ELSEIF, since this would seriously degrade CLISP's performance on all IF statements. Instead, CLISP assumes that all of the IF words are spelled correctly, and transforms the expression to (COND ((ZEROP N) 1 ELSSE N*(FACT N-1))). Later, after DWIM cannot find any other interpretation for ELSSE, and using the fact that this atom originally appeared in an IF statement, DWIM attempts spelling correction, using (IF THEN ELSE ELSEIF) for a spelling list. When this is successful, DWIM "fails" all the way back to the original IF statement, changes ELSSE to ELSE, and starts over. Misspellings of AND, OR, LT, GT, etc. are handled similarly. CLISP also contains many Do-What-I-Mean features besides spelling corrections. For example, the form (LIST +X Y) would generate a MISSING OPERATOR error. However, (LIST -X Y) makes sense, if the minus is unary, so DWIM offers this interpretation to you. Another common error, especially for new users, is to write (LIST X*FOO(Y)) or (LIST X*FOO Y), where FOO is the name of a function, instead of (LIST X*(FOO Y)). Therefore, whenever an operand that is not bound is also the name of a function (or corrects to one), the above interpretations are offered. CLISP Translations 1 The translation of CLISP character operators and the CLISP word IF are handled by replacing the CLISP expression with the corresponding Interlisp expression, and discarding the original CLISP. This is done because (1) the CLISP expression is easily recomputable (by CLISPIFY) and (2) the Interlisp expressions are simple and straightforward. Another reason for discarding the original CLISP is that it may contain errors that were corrected in the course of translation (e.g., FOOFOOO:1, N*8FOO X), etc.). If the original CLISP were retained, either you would have to go back and fix these errors by hand, thereby negating the advantage of having DWIM perform these corrections, or else DWIM would have to keep correcting these errors over and over. Note that CLISPIFY is sufficiently fast that it is practical for you to configure your Interlisp system so that all expressions are automatically CLISPIFYed immediately before they are presented to you. For example, you can define an edit macro to use in place of P which calls CLISPIFY on the current expression before printing it. Similarly, you can inform PRETTYPRINT to call CLISPIFY on each expression before printing it, etc. Where (1) or (2) are not the case, e.g., with iterative statements, pattern matches, record expressions, etc. the original CLISP is retained (or a slightly modified version thereof), and the translation is stored elsewhere (by the function CLISPTRAN, in the Miscellaneous Functions and Variables), usually in the hash array CLISPARRAY. The interpreter automatically checks this array when given a form CAR of which is not a function. Similarly, the compiler performs a GETHASH when given a form it does not recognize to see if it has a translation, which is then compiled instead of the form. Whenever you change a CLISP expresson by editing it, the editor automatically deletes its translation (if one exists), so that the next time it is evaluated or DWIMIFIed, the expression will be retranslated (if the value of CLISPRETRANFLG is T, DWIMIFY will also (re)translate any expressions which have translations stored remotely, see the CLISPIFY section). The function PPT and the edit commands PPT and CLISP: are available for examining translations (see the Miscellaneous Functions and Variables section). You can also indicate that you want the original CLISP retained by embedding it in an expression of the form (CLISP . CLISP-EXPRESSION), e.g., (CLISP X:5:3) or (CLISP ). In such cases, the translation will be stored remotely as described above. Furthermore, such expressions will be treated as CLISP even if infix and prefix transformations have been disabled by setting CLISPFLG to NIL (see the Miscellaneous Functions and Variables section). In other words, you can instruct the system to interpret as CLISP infix or prefix constructs only those expressions that are specifically flagged as such. You can also include CLISP declarations by writing (CLISP DECLARATIONS . FORM), e.g., (CLISP (CLISP: FLOATING) ...). These declarations will be used in place of any CLISP declarations in the function definition. This feature provides a way of including CLISP declarations in macro definitions. Note: CLISP translations can also be used to supply an interpretation for function objects, as well as forms, either for function objects that are used openly, i.e., appearing as CAR of form, function objects that are explicitly APPLYed, as with arguments to mapping functions, or function objects contained in function definition cells. In all cases, if CAR of the object is not LAMBDA or NLAMBDA, the interpreter and compiler will check CLISPARRAY. DWIMIFY 1 DWIMIFY is effectively a preprocessor for CLISP. DWIMIFY operates by scanning an expression as though it were being interpreted, and for each form that would generate an error, calling DWIM to "fix" it. DWIMIFY performs all DWIM transformations, not just CLISP transformations, so it does spelling correction, fixes 8-9 errors, handles F/L, etc. Thus you will see the same messages, and be asked for approval in the same situations, as you would if the expression were actually run. If DWIM is unable to make a correction, no message is printed, the form is left as it was, and the analysis proceeds. DWIMIFY knows exactly how the interpreter works. It knows the syntax of PROGs, SELECTQs, LAMBDA expressions, SETQs, et al. It knows how variables are bound, and that the argument of NLAMBDAs are not evaluated (you can inform DWIMIFY of a function or macro's nonstandard binding or evaluation by giving it a suitable INFO property, see below). In the course of its analysis of a particular expression, DWIMIFY builds a list of the bound variables from the LAMBDA expressions and PROGs that it encounters. It uses this list for spelling corrections. DWIMIFY also knows not to try to "correct" variables that are on this list since they would be bound if the expression were actually being run. However, note that DWIMIFY cannot, a priori, know about variables that are used freely but would be bound in a higher function if the expression were evaluated in its normal context. Therefore, DWIMIFY will try to "correct" these variables. Similarly, DWIMIFY will attempt to correct forms for which CAR is undefined, even when the form is not in error from your standpoint, but the corresponding function has simply not yet been defined. Note: DWIMIFY rebinds FIXSPELLDEFAULT to N, so that if you are not at the terminal when DWIMIFYing (or compiling), spelling corrections will not be performed. DWIMIFY will also inform you when it encounters an expression with too many arguments (unless DWIMCHECK#ARGSFLG = NIL), because such an occurrence, although does not cause an error in the Interlisp interpreter, nevertheless is frequently symptomatic of a parenthesis error. For example, if you wrote (CONS (QUOTE FOO X)) instead of (CONS (QUOTE FOO) X), DWIMIFY will print: POSSIBLE PARENTHESIS ERROR IN (QUOTE FOO X) TOO MANY ARGUMENTS (MORE THAN 1) DWIMIFY will also check to see if a PROG label contains a clisp character (unless DWIMCHECKPROGLABELSFLG = NIL, or the label is a member of NOFIXVARSLST), and if so, will alert you by printing the message SUSPICIOUS PROG LABEL, followed by the label. The PROG label will not be treated as CLISP. Note that in most cases, an attempt to transform a form that is already as you intended will have no effect (because there will be nothing to which that form could reasonably be transformed). However, in order to avoid needless calls to DWIM or to avoid possible confusion, you can inform DWIMIFY not to attempt corrections or transformations on certain functions or variables by adding them to the list NOFIXFNSLST or NOFIXVARSLST respectively. Note that you could achieve the same effect by simply setting the corresponding variables, and giving the functions dummy definitions. DWIMIFY will never attempt corrections on global variables, i.e., variables that are a member of the list GLOBALVARS, or have the property GLOBALVAR with value T, on their property list. Similarly, DWIMIFY will not attempt to correct variables declared to be SPECVARS in block declarations or via DECLARE expressions in the function body. You can also declare variables that are simply used freely in a function by using the USEDFREE declaration. DWIMIFY and DWIMIFYFNS (used to DWIMIFY several functions) maintain two internal lists of those functions and variables for which corrections were unsuccessfully attempted. These lists are initialized to the values of NOFIXFNSLST and NOFIXVARSLST. Once an attempt is made to fix a particular function or variable, and the attempt fails, the function or variable is added to the corresponding list, so that on subsequent occurrences (within this call to DWIMIFY or DWIMIFYFNS), no attempt at correction is made. For example, if FOO calls FIE several times, and FIE is undefined at the time FOO is DWIMIFYed, DWIMIFY will not bother with FIE after the first occurrence. In other words, once DWIMIFY "notices" a function or variable, it no longer attempts to correct it. DWIMIFY and DWIMIFYFNS also "notice" free variables that are set in the expression being processed. Moreover, once DWIMIFY "notices" such functions or variables, it subsequently treats them the same as though they were actually defined or set. Note that these internal lists are local to each call to DWIMIFY and DWIMIFYFNS, so that if a function containing FOOO, a misspelled call to FOO, is DWIMIFYed before FOO is defined or mentioned, if the function is DWIMIFYed again after FOO has been defined, the correction will be made. You can undo selected transformations performed by DWIMIFY, as described in Chapter 13. (DWIMIFY(DWIMIFY (Function) NIL NIL ("21") 13) X QUIETFLG L) [Function] Performs all DWIM and CLISP corrections and transformations on X that would be performed if X were run, and prints the result unless QUIETFLG = T. If X is an atom and L is NIL, X is treated as the name of a function, and its entire definition is DWIMIFYed. If X is a list or L is not NIL, X is the expression to be DWIMIFYed. If L is not NIL, it is the edit push-down list leading to X, and is used for determining context, i.e., what bound variables would be in effect when X was evaluated, whether X is a form or sequence of forms, e.g., a COND clause, etc. If X is an iterative statement and L is NIL, DWIMIFY will also print the translation, i.e., what is stored in the hash array. (DWIMIFYFNS(DWIMIFYFNS (Function) NIL NIL ("21") 13) FN1 ... FNN) [NLambda NoSpread Function] DWIMIFYs each of the functions given. If only one argument is given, it is evalued. If its value is a list, the functions on this list are DWIMIFYed. If only one argument is given, it is atomic, its value is not a list, and it is the name of a known file, DWIMIFYFNS will operate on (FILEFNSLST FN1), e.g. (DWIMIFYFNS FOO.LSP) will DWIMIFY every function in the file FOO.LSP. Every 30 seconds, DWIMIFYFNS prints the name of the function it is processing, a la PRETTYPRINT. Value is a list of the functions DWIMIFYed. DWIMINMACROSFLG(DWIMINMACROSFLG (Variable) NIL NIL ("21") 14) [Variable] Controls how DWIMIFY treats the arguments in a "call" to a macro, i.e., where the CAR of the form is undefined, but has a macro definition. If DWIMINMACROSFLG is T, then macros are treated as LAMBDA functions, i.e., the arguments are assumed to be evaluated, which means that DWIMIFY will descend into the argument list. If DWIMINMACROSFLG is NIL, macros are treated as NLAMBDA functions. DWIMINMACROSFLG is initially T. INFO(INFO (Property) NIL NIL ("21") 14) [Property Name] Used to inform DWIMIFY of nonstandard behavior of particular forms with respect to evaluation, binding of arguments, etc. The INFO property of a symbol is a single atom or list of atoms chosen from among the following: EVAL Informs DWIMIFY (and CLISP and Masterscope) that an nlambda function does evaluate its arguments. Can also be placed on a macro name to override the behavior of DWIMINMACROSFLG = NIL. NOEVAL Informs DWIMIFY that a macro does not evaluate all of its arguments, even when DWIMINMACROSFLG = T. BINDS Placed on the INFO property of a function or the CAR of a special form to inform DWIMIFY that the function or form binds variables. In this case, DWIMIFY assumes that CADR of the form is the variable list, i.e., a list of symbols, or lists of the form (VAL VALUE). LAMBDA, NLAMBDA, PROG, and RESETVARS are handled in this fashion. LABELS Informs CLISPIFY that the form interprets top-level symbols as labels, so that CLISPIFY will never introduce an atom (by packing) at the top level of the expression. PROG is handled in this fashion. NOFIXFNSLST (NOFIXFNSLST% (Variable) NIL NIL ("21") 14) [Variable] List of functions that DWIMIFY will not try to correct. NOFIXVARSLST(NOFIXVARSLST (Variable) NIL NIL ("21") 14) [Variable] List of variables that DWIMIFY will not try to correct. NOSPELLFLG(NOSPELLFLG (Variable) NIL NIL ("21") 14) [Variable] If T, DWIMIFY will not perform any spelling corrections. Initially NIL. NOSPELLFLG is reset to T when compiling functions whose definitions are obtained from a file, as opposed to being in core. CLISPHELPFLG (CLISPHELPFLG% (Variable) NIL NIL ("21") 14) [Variable] If NIL, DWIMIFY will not ask you for approval of any CLISP transformations. Instead, in those situations where approval would be required, the effect is the same as though you had been asked and said NO. Initially T. DWIMIFYCOMPFLG (DWIMIFYCOMPFLG% (Variable) NIL NIL ("21") 14) [Variable] If T, DWIMIFY is called before compiling an expression. Initially NIL. DWIMCHECK#ARGSFLG(DWIMCHECK#ARGSFLG (Variable) NIL NIL ("21") 14) [Variable] If T, causes DWIMIFY to check for too many arguments in a form. Initially T. DWIMCHECKPROGLABELSFLG(DWIMCHECKPROGLABELSFLG (Variable) NIL NIL ("21") 14) [Variable] If T, causes DWIMIFY to check whether a PROG label contains a CLISP character. Initially T. DWIMESSGAG(DWIMESSGAG (Variable) NIL NIL ("21") 15) [Variable] If T, suppresses all DWIMIFY error messages. Initially NIL. CLISPRETRANFLG(CLISPRETRANFLG (Variable) NIL NIL ("21") 15) [Variable] If T, informs DWIMIFY to (re)translate all expressions which have remote translations in the CLISP hash array. Initially NIL. CLISPIFY 1 CLISPIFY converts Interlisp expressions to CLISP. Note that the expression given to CLISPIFY need not have originally been input as CLISP, i.e., CLISPIFY can be used on functions that were written before CLISP was even implemented. CLISPIFY is cognizant of declaration rules as well as all of the precedence rules. For example, CLISPIFY will convert (IPLUS A (ITIMES B C)) into A+B*C, but (ITIMES A (IPLUS B C)) into A*(B+C). CLISPIFY handles such cases by first DWIMIFYing the expression. CLISPIFY also knows how to handle expressions consisting of a mixture of Interlisp and CLISP, e.g., (IPLUS A B*C) is converted to A+B*C, but (ITIMES A B+C) to (A*(B+C)). CLISPIFY converts calls to the six basic mapping functions, MAP, MAPC, MAPCAR, MAPLIST, MAPCONC, and MAPCON, into equivalent iterative statements. It also converts certain easily recognizable internal PROG loops to the corresponding iterative statements. CLISPIFY can convert all iterative statements input in CLISP back to CLISP, regardless of how complicated the translation was, because the original CLISP is saved. CLISPIFY is not destructive to the original Interlisp expression, i.e., CLISPIFY produces a new expression without changing the original. The new expression may however contain some "pieces" of the original, since CLISPIFY attempts to minimize the number of CONSes by not copying structure whenever possible. CLISPIFY will not convert expressions appearing as arguments to NLAMBDA functions, except for those functions whose INFO property is or contains the atom EVAL. CLISPIFY also contains built in information enabling it to process special forms such as PROG, SELECTQ, etc. If the INFO property is or contains the atom LABELS, CLISPIFY will never create an atom (by packing) at the top level of the expression. PROG is handled in this fashion. Note: Disabling a CLISP operator with CLDISABLE (see the Miscellaneous Functions and Variables section) will also disable the corresponding CLISPIFY transformation. Thus, if is "turned off", AB will not transform to (SETQ A B), nor vice versa. (CLISPIFY X EDITCHAIN) [Function] Clispifies X. If X is an atom and EDITCHAIN is NIL, X is treated as the name of a function, and its definition (or EXPR property) is clispified. After CLISPIFY has finished, X is redefined (using /PUTD) with its new CLISP definition. The value of CLISPIFY is X. If X is atomic and not the name of a function, spelling correction is attempted. If this fails, an error is generated. If X is a list, or EDITCHAIN is not NIL, X itself is the expression to be clispified. If EDITCHAIN is not NIL, it is the edit push-down list leading to X and is used to determine context as with DWIMIFY, as well as to obtain the local declarations, if any. The value of CLISPIFY is the clispified version of X. (CLISPIFYFNS FN1 ... FNN) [NLambda NoSpread Function] Like DWIMIFYFNS except calls CLISPIFY instead of DWIMIFY. CL:FLG(CL:FLG (Variable) NIL NIL ("21") 16) [Variable] Affects CLISPIFY's handling of forms beginning with CAR, CDR, ... CDDDDR, as well as pattern match and record expressions. If CL:FLG is NIL, these are not transformed into the equivalent : expressions. This will prevent CLISPIFY from constructing any expression employing a : infix operator, e.g., (CADR X) will not be transformed to X:2. If CL:FLG is T, CLISPIFY will convert to : notation only when the argument is atomic or a simple list (a function name and one atomic argument). If CL:FLG is ALL, CLISPIFY will convert to : expressions whenever possible. CL:FLG is initially T. CLREMPARSFLG (CLREMPARSFLG% (Variable) NIL NIL ("21") 16) [Variable] If T, CLISPIFY will remove parentheses in certain cases from simple forms, where "simple" means a function name and one or two atomic arguments. For example, (COND ((ATOM X) --)) will CLISPIFY to (IF ATOM X THEN --). However, if CLREMPARSFLG is set to NIL, CLISPIFY will produce (IF (ATOM X) THEN --). Regardless of the flag setting, the expression can be input in either form. CLREMPARSFLG is initially NIL. CLISPIFYPACKFLG (CLISPIFYPACKFLG% (Variable) NIL NIL ("21") 16) [Variable] CLISPIFYPACKFLG affects the treatment of infix operators with atomic operands. If CLISPIFYPACKFLG is T, CLISPIFY will pack these into single atoms, e.g., (IPLUS A (ITIMES B C)) becomes A+B*C. If CLISPIFYPACKFLG is NIL, no packing is done, e.g., the above becomes A + B * C. CLISPIFYPACKFLG is initially T. CLISPIFYUSERFN(CLISPIFYUSERFN (Variable) NIL NIL ("21") 16) [Variable] If T, causes the function CLISPIFYUSERFN, which should be a function of one argument, to be called on each form (list) not otherwise recognized by CLISPIFY. If a non-NIL value is returned, it is treated as the clispified form. Initially NIL Note that CLISPIFYUSERFN must be both set and defined to use this feature. FUNNYATOMLST(FUNNYATOMLST (Variable) NIL NIL ("21") 16) [Variable] Suppose you have variables named A, B, and A*B. If CLISPIFY were to convert (ITIMES A B) to A*B, A*B would not translate back correctly to (ITIMES A B), since it would be the name of a variable, and therefore would not cause an error. You can prevent this from happening by adding A*B to the list FUNNYATOMLST. Then, (ITIMES A B) would CLISPIFY to A * B. Note that A*B's appearance on FUNNYATOMLST would not enable DWIM and CLISP to decode A*B+C as (IPLUS A*B C); FUNNYATOMLST is used only by CLISPIFY. Thus, if an identifier contains a CLISP character, it should always be separated (with spaces) from other operators. For example, if X* is a variable, you should write (SETQ X* FORM) in CLISP as X* FORM, not X*FORM. In general, it is best to avoid use of identifiers containing CLISP character operators as much as possible. Miscellaneous Functions and Variables 1 CLISPFLG(CLISPFLG (Variable) NIL NIL ("21") 16) [Variable] If CLISPFLG = NIL, disables all CLISP infix or prefix transformations (but does not affect IF/THEN/ELSE statements, or iterative statements). If CLISPFLG = TYPE-IN, CLISP transformations are performed only on expressions that are typed in for evaluation, i.e., not on user programs. If CLISPFLG = T, CLISP transformations are performed on all expressions. The initial value for CLISPFLG is T. CLISPIFYing anything will cause CLISPFLG to be set to T. CLISPCHARS (CLISPCHARS% (Variable) NIL NIL ("21") 17) [Variable] A list of the operators that can appear in the interior of an atom. Currently (+ - * / ~ ' = : < > +- ~= @ !). CLISPCHARRAY(CLISPCHARRAY (Variable) NIL NIL ("21") 17) [Variable] A bit table of the characters on CLISPCHARS used for calls to STRPOSL (Chapter 4). CLISPCHARRAY is initialized by performing (SETQ CLISPCHARRAY (MAKEBITTABLE CLISPCHARS)). (CLISPINFIXSPLST% (Variable) NIL NIL ("21") 17)CLISPINFIXSPLST [Variable] A list of infix operators used for spelling correction. CLISPARRAY (CLISPARRAY (Variable) NIL NIL ("21") 17) [Variable] Hash array used for storing CLISP translations. CLISPARRAY is checked by FAULTEVAL and FAULTAPPLY on erroneous forms before calling DWIM, and by the compiler. (CLEARCLISPARRAY(CLEARCLISPARRAY (Function) NIL NIL (21) 17) NAME --) [Function] Macro and CLISP expansions are cached in CLISPARRAY, the systems CLISP hash array. When anything changes that would invalidate an expansion, it needs to be removed from the cache. CLEARCLISPARRAY does this for you. The system does this automatically whenever you define redefine a CLISP or macro form. If you have changed something that a CLISP word or a macro depends on the system will not be able to detect this, so you will have to invalidate the cahce by calling CLEARCLISPARRAY. You can clear the whole cache by calling (CLRHASH CLISPARRAY). (CLISPTRAN(CLISPTRAN (Function) NIL NIL ("21") 17) X TRAN) [Function] Gives X the translation TRAN by storing (key X, value TRAN) in the hash array CLISPARRAY. CLISPTRAN is called for all CLISP translations, via a non-linked, external function call, so it can be advised. (CLISPDEC(CLISPDEC (Function) NIL NIL ("21") 17) DECLST) [Function] Puts into effect the declarations in DECLST. CLISPDEC performs spelling corrections on words not recognized as declarations. CLISPDEC is undoable. (CLDISABLE(CLDISABLE (Function) NIL NIL ("21") 17) OP) [Function] Disables the CLISP operator OP. For example, (CLDISABLE '-) makes - be just another character. CLDISABLE can be used on all CLISP operators, e.g., infix operators, prefix operators, iterative statement operators, etc. CLDISABLE is undoable. Note: Simply removing a character operator from CLISPCHARS will prevent it from being treated as a CLISP operator when it appears as part of an atom, but it will continue to be an operator when it appears as a separate atom, e.g. (FOO + X) vs FOO+X. CLISPIFTRANFLG(CLISPIFTRANFLG (Variable) NIL NIL ("21") 17) [Variable] Affects handling of translations of IF-THEN-ELSE statements (see Chapter 9). If T, the translations are stored elsewhere, and the (modified) CLISP retained. If NIL, the corresponding COND expression replaces the CLISP. Initially T. CLISPIFYPRETTYFLG(CLISPIFYPRETTYFLG (Variable) NIL NIL ("21") 17) [Variable] If non-NIL, causes PRETTYPRINT (and therefore PP and MAKEFILE) to CLISPIFY selected function definitions before printing them according to the following interpretations of CLISPIFYPRETTYFLG: ALL Clispify all functions. T or EXPRS Clispify all functions currently defined as EXPRs. CHANGES Clispify all functions marked as having been changed. a list Clispify all functions in that list. CLISPIFYPRETTYFLG is (temporarily) reset to T when MAKEFILE is called with the option CLISPIFY, and reset to CHANGES when the file being dumped has the property FILETYPE value CLISP. CLISPIFYPRETTYFLG is initially NIL. Note: If CLISPIFYPRETTYFLG is non-NIL, and the only transformation performed by DWIM are well formed CLISP transformations, i.e., no spelling corrections, the function will not be marked as changed, since it would only have to be re-clispified and re-prettyprinted when the file was written out. (PPT X) [NLambda NoSpread Function] Both a function and an edit macro for prettyprinting translations. It performs a PP after first resetting PRETTYTRANFLG to T, thereby causing any translations to be printed instead of the corresponding CLISP. CLISP:(CL (Editor Command) NIL NIL ("21") 18) [Editor Command] Edit macro that obtains the translation of the correct expression, if any, from CLISPARRAY, and calls EDITE on it. CL(CL (Editor Command) NIL NIL ("21") 18) [Editor Command] Edit macro. Replaces current expression with CLISPIFYed current expression. Current expression can be an element or tail. DW(DW (Editor Command) NIL NIL ("21") 18) [Editor Command] Edit macro. DWIMIFYs current expression, which can be an element (atom or list) or tail. Both CL and DW can be called when the current expression is either an element or a tail and will work properly. Both consult the declarations in the function being edited, if any, and both are undoable. (LOWERCASE(LOWERCASE (Function) NIL NIL ("21") 18) FLG) [Function] If FLG = T, LOWERCASE makes the necessary internal modifications so that CLISPIFY will use lower case versions of AND, OR, IF, THEN, ELSE, ELSEIF, and all i.s. operators. This produces more readable output. Note that you can always type in either upper or lower case (or a combination), regardless of the action of LOWERCASE. If FLG = NIL, CLISPIFY will use uppercase versions of AND, OR, et al. The value of LOWERCASE is its previous "setting". LOWERCASE is undoable. The initial setting for LOWERCASE is T. CLISP Internal Conventions 1 CLISP is almost entirely table driven by the property lists of the corresponding infix or prefix operators. For example, much of the information used for translating the + infix operator is stored on the property list of the symbol "+". Thus it is relatively easy to add new infix or prefix operators or change old ones, simply by adding or changing selected property values. (There is some built in information for handling minus, :, ', and ~, i.e., you could not yourself add such "special" operators, although you can disable or redefine them.) Global declarations operate by changing the LISPFN and CLISPINFIX properties of the appropriate operators. CLISPTYPE(CLISPTYPE (Property) NIL NIL ("21") 18) [Property Name] The property value of the property CLISPTYPE is the precedence number of the operator: higher values have higher precedence, i.e., are tighter. Note that the actual value is unimportant, only the value relative to other operators. For example, CLISPTYPE for :, , and * are 14, 6, and 4 respectively. Operators with the same precedence group left to right, e.g., / also has precedence 4, so A/B*C is (A/B)*C. An operator can have a different left and right precedence by making the value of CLISPTYPE be a dotted pair of two numbers, e.g., CLISPTYPE of is (8 . -12). In this case, CAR is the left precedence, and CDR the right, i.e., CAR is used when comparing with operators on the left, and CDR with operators on the right. For example, A*BC+D is parsed as A*(B(C+D)) because the left precedence of is 8, which is higher than that of *, which is 4. The right precedence of is -12, which is lower than that of +, which is 2. If the CLISPTYPE property for any operator is removed, the corresponding CLISP transformation is disabled, as well as the inverse CLISPIFY transformation. UNARYOP(UNARYOP (Property) NIL NIL ("21") 19) [Property Name] The value of property UNARYOP must be T for unary operators or brackets. The operand is always on the right, i.e., unary operators or brackets are always prefix operators. BROADSCOPE(BROADSCOPE (Property) NIL NIL ("21") 19) [Property Name] The value of property BROADSCOPE is T if the operator has lower precedence than Interlisp forms, e.g., LT, EQUAL, AND, etc. For example, (FOO X AND Y) parses as ((FOO X) AND Y). If the BROADSCOPE property were removed from the property list of AND, (FOO X AND Y) would parse as (FOO (X AND Y)). LISPFN(LISPFN (Property) NIL NIL ("21") 19) [Property Name] The value of the property LISPFN is the name of the function to which the infix operator translates. For example, the value of LISPFN for is EXPT, for ' QUOTE, etc. If the value of the property LISPFN is NIL, the infix operator itself is also the function, e.g., AND, OR, EQUAL. SETFN(SETFN (Property) NIL NIL ("21") 19) [Property Name] If FOO has a SETFN property FIE, then (FOO --)X translates to (FIE -- X). For example, if you make ELT be an infix operator, e.g. #, by putting appropriate CLISPTYPE and LISPFN properties on the property list of # then you can also make # followed by translate to SETA, e.g., X#NY to (SETA X N Y), by putting SETA on the property list of ELT under the property SETFN. Putting the list (ELT) on the property list of SETA under property SETFN will enable SETA forms to CLISPIFY back to ELT's. CLISPINFIX(CLISPINFIX (Property) NIL NIL ("21") 19) [Property Name] The value of this property is the CLISP infix to be used in CLISPIFYing. This property is stored on the property list of the corresponding Interlisp function, e.g., the value of property CLISPINFIX for EXPT is , for QUOTE is ' etc. CLISPWORD(CLISPWORD (Property) NIL NIL ("21") 19) [Property Name] Appears on the property list of clisp operators which can appear as CAR of a form, such as FETCH, REPLACE, IF, iterative statement operators, etc. Value of property is of the form (KEYWORD . NAME), where NAME is the lowercase version of the operator, and KEYWORD is its type, e.g. FORWORD, IFWORD, RECORDWORD, etc. KEYWORD can also be the name of a function. When the atom appears as CAR of a form, the function is applied to the form and the result taken as the correct form. In this case, the function should either physically change the form, or call CLISPTRAN to store the translation. As an example, to make & be an infix character operator meaning OR, you could do the following: (PUTPROP '& 'CLISPTYPE (GETPROP 'OR 'CLISPTYPE)) (PUTPROP '& 'LISPFN 'OR) (PUTPROP '& 'BROADSCOPE T) (PUTPROP 'OR 'CLISPINFIX '&) (SETQ CLISPCHARS (CONS '& CLISPCHARS)) (SETQ CLISPCHARRAY (MAKEBITTABLE CLISPCHARS)) (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "20-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "20-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "20-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "20-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "20-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "20-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))F PAGEHEADING RIGHTPAGE55552`~~306 +T,HH,HH206206 +,HH56TT26TT5<0<@ PAGEHEADINGLEFTBACKT-T3(T,,ll,,3(T,3(T//,HH,,, PALATINO PALATINO PALATINO TITAN TITAN TITAN TITAN MODERNMODERN +MODERNMODERN +   + IM.CHAP.GETFNMODERN +  HRULE.GETFNMODERN %u:WIM.INDEX.GETFN!i +8    + ) +J +!E @ 2(  O"))"8D + A4K :"L(>  !  ! 9 y 5 ,,P1G;  ,P ;U  \IM.INDEX.GETFN  HRULE.GETFNMODERN + 1 .gJa +I }   VIM.INDEX.GETFN  HRULE.GETFNMODERN + E  l +/    ! + Lu     + ) %IM.INDEX.GETFNTITAN   + %IM.INDEX.GETFN  +%IM.INDEX.GETFNTITAN    +%IM.INDEX.GETFNTITAN   +%IM.INDEX.GETFNPALATINO   8Q +! 8 N  Db9 +D +  +6 +% 7<   %IM.INDEX.GETFNTITAN    +&IM.INDEX.GETFNTITAN   +&IM.INDEX.GETFNTITAN   +&IM.INDEX.GETFNTITAN   +&IM.INDEX.GETFNTITAN   w ." 1^$ .( %IM.INDEX.GETFNTITAN     T2   . V  &IM.INDEX.GETFNTITAN   o  >  :i  &IM.INDEX.GETFN     +   . +1 o## + Q ;/ E7 \ J  "X N =    +  U  e  +  U     4 *   +k )A Y      z  +  ? +B +   B /  #      .@    %    k +# (                HRULE.GETFNMODERN +  +  5 +      &IM.INDEX.GETFNMODERN + +  %H 8: '   " f.       +  +  w  3W -3 '@ U U;_         E1   HRULE.GETFNMODERN +  &. >: Gt ')$L  +V3 * RG 8*4\cW)+,%) m @<5?'>>5]$f  '  HRULE.GETFNMODERN + @  +o J  -m K +EA9{cm    + /z) +  HRULE.GETFNMODERN + + +qBF$TR/D4) .@@   !* 5 "h  c +  &6z + +   +63I +^z9 +# +-03&IM.INDEX.GETFNMODERN + +  ?( D+Z) I  +)IM.INDEX.GETFNPALATINO  o +   +8  !.IM.INDEX.GETFNPALATINO  >;N* #IM.INDEX.GETFNPALATINO  iY 6Y *;Q  ?P ,IM.INDEX.GETFN   +IM.INDEX.GETFNPALATINO   +)IM.INDEX.GETFNPALATINO  7 + c -IM.INDEX.GETFN   /IM.INDEX.GETFN  60IM.INDEX.GETFNPALATINO   75IM.INDEX.GETFNPALATINO   . +)IM.INDEX.GETFNPALATINO  -IM.INDEX.GETFNPALATINO   +e  HRULE.GETFNMODERN + M,PY\  4_3@$/8-"Q"M' ]. +   >!/t  0 +*E    + %IM.INDEX.GETFNTITAN   $70!.k    -IM.INDEX.GETFN    O  0IM.INDEX.GETFN  D* .  -IM.INDEX.GETFNTITAN   k E +3 +IM.INDEX.GETFNTITAN   ! '      + !    o &  HRULE.GETFNMODERN + 'IM.INDEX.GETFNTITAN   J ' x :   + +IM.INDEX.GETFN  O#  +IM.INDEX.GETFNMODERN +  ! + - 0IM.INDEX.GETFNTITAN   7  + )IM.INDEX.GETFNTITAN  1 +  += ,IM.INDEX.GETFNTITAN   ) +* (IM.INDEX.GETFNTITAN    + f 'IM.INDEX.GETFNTITAN   +  %I   (IM.INDEX.GETFNTITAN     s 0 + -IM.INDEX.GETFNTITAN   $ !P+ 0IM.INDEX.GETFNMODERN +    b-7- , +Cw    R T 'IM.INDEX.GETFNMODERN +  P +  'IM.INDEX.GETFNMODERN +  .E 'IM.INDEX.GETFNMODERN +   F  (IM.INDEX.GETFNTITAN      4!aE    '    HRULE.GETFNMODERN + >/i, +* (IM.INDEX.GETFNTITAN    #   _ R (  + .  $'%  r&IM.INDEX.GETFNTITAN      +)IM.INDEX.GETFNTITAN     +B   + +1  %IM.INDEX.GETFNTITAN   `%8 $IM.INDEX.GETFNTITAN    + + + $     +  +)IM.INDEX.GETFNTITAN   <x +  (IM.INDEX.GETFNTITAN    DH/ + ?  (2(/ z \ No newline at end of file diff --git a/docs/turpin-irm/IRM-2/21-PERFORMANCE.TEDIT b/docs/turpin-irm/IRM-2/21-PERFORMANCE.TEDIT new file mode 100644 index 00000000..910113d2 --- /dev/null +++ b/docs/turpin-irm/IRM-2/21-PERFORMANCE.TEDIT @@ -0,0 +1,57 @@ +INTERLISP-D REFERENCE MANUAL PERFORMANCE ISSUES "22"21. PERFORMANCE ISSUES 2 This chapter describes a number of areas that often contribute to performance problems in Medley programs. Many performance problems can be improved by optimizing the use of storage, since allocating and reclaiming large amounts of storage is expensive. Another tactic that can sometimes yield performance improvements is to change the use of variable bindings on the stack to reduce variable lookup time. There are a number of tools that can be used to determine which parts of a computation cause performance bottlenecks. Storage Allocation and Garbage Collection 1 As an Medley application program runs, it creates data structures (allocated out of free storage space), manipulates them, and then discards them. If there were no way to reclaim this space, over time the Medley memory would fill up, and the computation would come to a halt. Actually, long before this could happen the system would probably become intolerably slow, due to data fragmentation, which occurs when the data currently in use are spread over many virtual memory pages, so that most of the computer time must be spent swapping disk pages into physical memory. The problem of fragmentation will occur in any situation where the virtual memory is significantly larger than the real physical memory. To reduce swapping, you want to keep the "working set" (the set of pages containing actively referenced data) as small as possible. You can write programs that don't generate much garbage data, or which recycle data, but such programs tend to be complex and hard to debug. Spending effort writing such programs defeats the whole point of using a system with automatic storage allocation. An important part of any Lisp implementation is the garbage collector that finds discarded data and reclaims its space. There are several well-known approaches to garbage collection. One method is the traditional mark-and-sweep, which identifies garbage data by marking all accessible data structures, and then sweeping through the data spaces to find all unmarked objects (i.e., not referenced by any other object). This method is guaranteed to reclaim all garbage, but it takes time proportional to the number of allocated objects, which may be very large. Also, the time that a mark-and-sweep garbage collection takes is independent of the amount of garbage collected; it is possible to sweep through the whole virtual memory, and only recover a small amount of garbage. For interactive applications, it is not acceptable to have long interruptions in a computation for to garbage collect. Medley solves this problem by using a reference-counting garbage collector. With this scheme, there is a table containing counts of how many times each object is referenced. This table is updated as pointers are created and discarded, incurring a small overhead distributed over the computation as a whole. (Note: References from the stack are not counted, but are handled separately at "sweep" time; thus the vast majority of data manipulations do not cause updates to this table.) At opportune moments, the garbage collector scans this table, and reclaims all objects that are no longer accessible (have a reference count of zero). The pause while objects are reclaimed is only the time for scanning the reference count tables (small) plus time proportional to the amount of garbage that has to be collected (typically less than a second). Opportune times occur when a certain number of cells have been allocated or when the system has been waiting for you to type something for long enough. The frequency of garbage collection is controlled by the functions and variables described below. For the best system performance, it is desirable to adjust these parameters for frequent, short garbage collections, which will not interrupt interactive applications for very long, and which will have the added benefit of reducing data fragmentation, keeping the working set small. One problem with the Medley garbage collector is that not all garbage is guaranteed to be collected. Circular data structures, which point to themselves directly or indirectly, are never reclaimed, since their reference counts are always at least one. With time, this unreclaimable garbage may increase the working set to unacceptable levels. Some users have worked with the same Medley virtual memory for a very long time, but it is a good idea to occasionally save all of your functions in files, reinitialize Medley, and rebuild your system. Many users end their working day by issuing a command to rebuild their system and then leaving the machine to perform this task in their absence. If the system seems to be spending too much time swapping (an indication of fragmented working set), this procedure is definitely recommended. Another limitation of the reference-counting garbage collector is that the table in which reference counts are maintained is of fixed size. For typical Lisp objects that are pointed to from exactly one place (e.g., the individual conses in a list), no burden is placed on this table, since objects whose reference count is 1 are not explicitly represented in the table. However, large, "rich" data structures, with many interconnections, backward links, cross references, etc, can contribute many entries to the reference count table. For example, if you created a data structure that functioned as a doubly-linked list, such a structure would contribute an entry (reference count 2) for each element. When the reference count table fills up, the garbage collector can no longer maintain consistent reference counts, so it stops doing so altogether. At this point, a window appears on the screen with the following message, and the debugger is entered: Internal garbage collector(GARBAGE% COLLECTOR NIL garbage% collector NIL (4) NIL) tables have overflowed, due to too many pointers with reference count greater than 1. *** The garbage collector is now disabled. *** Save your work and reload as soon as possible. [This message is slightly misleading, in that it should say "count not equal to 1". In the current implementation, the garbage collection of a large pointer array whose elements are not otherwise pointed to can place a special burden on the table, as each element's reference count simultaneously drops to zero and is thus added to the reference count table for the short period before the element is itself reclaimed.] If you exit the debugger window (e.g., with the RETURN command), your computation can proceed; however, the garbage collector is no longer operating. Thus, your virtual memory will become cluttered with objects no longer accessible, and if you continue for long enough in the same virtual memory image you will eventually fill up the virtual memory backing store and grind to a halt. Garbage collection in Medley is controlled by the following functions and variables: (RECLAIM(RECLAIM (Function) NIL NIL ("22") 2)) [Function] Initiates a garbage collection. Returns 0. (RECLAIMMIN(RECLAIMMIN (Function) NIL NIL ("22") 2) N) [Function] Sets the frequency of garbage collection. Interlisp keeps track of the number of cells of any type that have been allocated; when it reaches a given number, a garbage collection occurs. If N is non-NIL, this number is set to N. Returns the current setting of the number. RECLAIMWAIT(RECLAIMWAIT (Variable) NIL NIL ("22") 2) [Variable] Medley will invoke a RECLAIM if the system is idle and waiting for your input for RECLAIMWAIT seconds (currently set for 4 seconds). (GCGAG(GCGAG (Function) NIL NIL ("22") 2) MESSAGE) [Function] Sets the behavior that occurs while a garbage collection is taking place. If MESSAGE is non-NIL, the cursor is complemented during a RECLAIM; if MESSAGE = NIL, nothing happens. The value of GCGAG is its previous setting. (GCTRP(GCGAG (Function) NIL NIL ("22") 2)) [Function] Returns the number of cells until the next garbage collection, according to the RECLAIMMIN number. The amount of storage allocated to different data types, how much of that storage is in use, and the amount of data fragmentation can be determined using the following function: (STORAGE(STORAGE (Function) NIL NIL ("22") 2) TYPES PAGETHRESHOLD) [Function] STORAGE prints out a summary, for each data type, of the amount of space allocated to the data type, and how much of that space is currently in use. If TYPES is non-NIL, STORAGE only lists statistics for the specified types. TYPES can be a symbol or a list of types. If PAGETHRESHOLD is non-NIL, then STORAGE only lists statistics for types that have at least PAGETHRESHOLD pages allocated to them. STORAGE prints out a table with the column headings Type, Assigned, Free Items, In use, and Total alloc. Type is the name of the data type. Assigned is how much of your virtual memory is set aside for items of this type. Currently, memory is allocated in quanta of two pages (1024 bytes). The numbers under Assigned show the number of pages and the total number of items that fit on those pages. Free Items shows how many items are available to be allocated (using the create construct, Chapter 8); these constitute the "free list" for that data type. In use shows how many items of this type are currently in use, i.e., have pointers to them and hence have not been garbage collected. If this number is higher than your program seems to warrant, you may want to look for storage leaks. The sum of Free Items and In use is always the same as the total Assigned items. Total alloc is the total number of items of this type that have ever been allocated (see BOXCOUNT, in the Performance Measuring section below). Note: The information about the number of items of type LISTP is only approximate, because list cells are allocated in a special way that precludes easy computation of the number of items per page. Note: When a data type is redeclared, the data type name is reassigned. Pages which were assigned to instances of the old data type are labeled **DEALLOC**. At the end of the table printout, STORAGE prints a "Data Spaces Summary" listing the number of pages allocated to the major data areas in the virtual address space: the space for fixed-length items (including datatypes), the space for variable-length items, and the space for symbols. Variable-length data types such as arrays have fixed-length "headers," which is why they also appear in the printout of fixed-length data types. Thus, the line printed for the BITMAP data type says how many bitmaps have been allocated, but the "assigned pages" column counts only the headers, not the space used by the variable-length part of the bitmap. This summary also lists "Remaining Pages" in relation to the largest possible virtual memory, not the size of the virtual memory backing file in use. This file may fill up, causing a STORAGE FULL error, long before the "Remaining Pages" numbers reach zero. STORAGE also prints out information about the sizes of the entries on the variable-length data free list. The block sizes are broken down by the value of the variable STORAGE.ARRAYSIZES, initially (4 16 64 256 1024 4096 16384 NIL), which yields a printout of the form: variable-datum free list: le 4 26 items; 104 cells. le 16 72 items; 783 cells. le 64 36 items; 964 cells. le 256 28 items; 3155 cells. le 1024 3 items; 1175 cells. le 4096 5 items; 8303 cells. le 16384 3 items; 17067 cells. others 1 items; 17559 cells. This information can be useful in determining if the variable-length data space is fragmented. If most of the free space is composed of small items, then the allocator may not be able to find room for large items, and will extend the variable datum space. If this is extended too much, this could cause an ARRAYS FULL error, even if there is a lot of space left in little chunks. (STORAGE.LEFT(STORAGE.LEFT (Function) NIL NIL ("22") 3)) [Function] Provides a programmatic way of determining how much storage is left in the major data areas in the virtual address space. Returns a list of the form (MDSFREE MDSFRAC 8MBFRAC ATOMFREE ATOMFRAC), where the elements are interpreted as follows: MDSFREE The number of free pages left in the main data space (which includes both fixed-length and variable-length data types). MDSFRAC The fraction of the total possible main data space that is free. 8MBFRAC The fraction of the total main data space that is free, relative to eight megabytes. This number is useful when using Medley on some early computers where the hardware limits the address space to eight megabytes. The function 32MBADDRESSABLE returns non-NIL if the currently running Medley system can use the full 32 megabyte address space. ATOMFREE The number of free pages left in the symbol space. ATOMFRAC The fraction of the total symbol space that is free. Note: Another important space resource is the amount of the virtual memory backing file in use (see VMEMSIZE, Chapter 12). The system will crash if the virtual memory file is full, even if the address space is not exhausted. Variable Bindings 1 Different implementations of Lisp use different methods of accessing free variables. The binding of variables occurs when a function or a PROG is entered. For example, if the function FOO has the definition (LAMBDA (A B) BODY), the variables A and B are bound so that any reference to A or B from BODY or any function called from BODY will refer to the arguments to the function FOO and not to the value of A or B from a higher level function. All variable names (symbols) have a top level value cell which is used if the variable has not been bound in any function. In discussions of variable access, it is useful to distinguish between three types of variable access: local, special and global. Local variable access is the use of a variable that is bound within the function from which it is used. Special variable access is the use of a variable that is bound by another function. Global variable access is the use of a variable that has not been bound in any function. We will often refer to a variable all of whose accesses are local as a "local variable." Similarly, a variable all of whose accesses are global we call a "global variable." In a deep bound system, a variable is bound by saving on the stack the variable's name together with a value cell which contains that variable's new value. When a variable is accessed, its value is found by searching the stack for the most recent binding (occurrence) and retrieving the value stored there. If the variable is not found on the stack, the variable's top level value cell is used. In a shallow bound system, a variable is bound by saving on the stack the variable name and the variable's old value and putting the new value in the variable's top level value cell. When a variable is accessed, its value is always found in its top level value cell. The deep binding scheme has one disadvantage: the amount of cpu time required to fetch the value of a variable depends on the stack distance between its use and its binding. The compiler can determine local variable accesses and compiles them as fetches directly from the stack. Thus this computation cost only arises in the use of variable not bound in the local frame ("free" variables). The process of finding the value of a free variable is called free variable lookup. In a shallow bound system, the amount of cpu time required to fetch the value of a variable is constant regardless of whether the variable is local, special or global. The disadvantages of this scheme are that the actual binding of a variable takes longer (thus slowing down function call), the cells that contain the current in use values are spread throughout the space of all symbol value cells (thus increasing the working set size of functions) and context switching between processes requires unwinding and rewinding the stack (thus effectively prohibiting the use of context switching for many applications). Medley uses deep binding, because of the working set considerations and the speed of context switching. The free variable lookup routine is microcoded, thus greatly reducing the search time. In benchmarks, the largest percentage of free variable lookup time was 20 percent of the total ellapsed time; the normal time was between 5 and 10 percent. Because of the deep binding, you can sometimes significantly improve performance by declaring global variables. If a variable is declared global, the compiler will compile an access to that variable as a retrieval of its top level value, completely bypassing a stack search. This should be done only for variables that are never bound in functions, such as global databases and flags. Global variable declarations should be done using the GLOBALVARS file manager command (Chapter 17). Its form is (GLOBALVARS VAR1 ... VARN). Another way of improving performance is to declare variables as local within a function. Normally, all variables bound within a function have their names put on the stack, and these names are scanned during free variable lookup. If a variable is declared to be local within a function, its name is not put on the stack, so it is not scanned during free variable lookup, which may increase the speed of lookups. The compiler can also make some other optimizations if a variable is known to be local to a function. A variable may be declared as local within a function by including the form (DECLARE (LOCALVARS VAR1 ... VARN)) following the argument list in the definition of the function. Local variable declarations only effect the compilation of a function. Interpreted functions put all of their variable names on the stack, regardless of any declarations. Performance Measuring 1 This section describes functions that gather and display statistics about a computation, such as as the elapsed time, and the number of data objects of different types allocated. TIMEALL and TIME gather statistics on the evaluation of a specified form. BREAKDOWN gathers statistics on individual functions called during a computation. These functions can be used to determine which parts of a computation are consuming the most resources (time, storage, etc.), and could most profitably be improved. (TIMEALL(TIMEALL (Function) NIL NIL ("22") 6) TIMEFORM NUMBEROFTIMES TIMEWHAT INTERPFLG) [NLambda Function] Evaluates the form TIMEFORM and prints statistics on time spent in various categories (elapsed, keyboard wait, swapping time, gc) and data type allocation. For more accurate measurement on small computations, NUMBEROFTIMES may be specified (its default is 1) to cause TIMEFORM to be executed NUMBEROFTIMES times. To improve the accuracy of timing open-coded operations in this case, TIMEALL compiles a form to execute TIMEFORM NUMBEROFTIMES times (unless INTERPFLG is non-NIL), and then times the execution of the compiled form. Note: If TIMEALL is called with NUMBEROFTIMES > 1, the dummy form is compiled with compiler optimizations on. This means that it is not meaningful to use TIMEALL with very simple forms that are optimized out by the compiler. For example, (TIMEALL '(IPLUS 2 3) 1000) will time a compiled function which simply returns the number 5, since (IPLUS 2 3) is optimized to the integer 5. TIMEWHAT restricts the statistics to specific categories. It can be an atom or list of datatypes to monitor, and/or the atom TIME to monitor time spent. Note that ordinarily, TIMEALL monitors all time and datatype usage, so this argument is rarely needed. TIMEALL returns the value of the last evaluation of TIMEFORM. (TIME(TIME (Function) NIL NIL ("22") 6) TIMEX TIMEN TIMETYP) [NLambda Function] TIME evaluates the form TIMEX, and prints out the number of CONS cells allocated and computation time. Garbage collection time is subtracted out. This function has been largely replaced by TIMEALL. If TIMEN is greater than 1, TIMEX is executed TIMEN times, and TIME prints out (number of conses)/TIMEN, and (computation time)/TIMEN. If TIMEN = NIL, it defaults to 1. This is useful for more accurate measurement on small computations. If TIMETYP is 0, TIME measures and prints total real time as well as computation time. If TIMETYP = 3, TIME measures and prints garbage collection time as well as computation time. If TIMETYP = T, TIME measures and prints the number of pagefaults. TIME returns the value of the last evaluation of TIMEX. (BOXCOUNT(BOXCOUNT (Function) NIL NIL ("22") 6) TYPE N) [Function] Returns the number of data objects of type TYPE allocated since this Interlisp system was created. TYPE can be any data type name (see TYPENAME, Chapter 8). If TYPE is NIL, it defaults to FIXP. If N is non-NIL, the corresponding counter is reset to N. (CONSCOUNT(CONSCOUNT (Function) NIL NIL ("22") 6) N) [Function] Returns the number of CONS cells allocated since this Interlisp system was created. If N is non-NIL, resets the counter to N. Equivalent to (BOXCOUNT 'LISTP N). (PAGEFAULTS(PAGEFAULTS (Function) NIL NIL ("22") 6)) [Function] Returns the number of page faults since this Interlisp system was created. BREAKDOWN 1 TIMEALL collects statistics for whole computations. BREAKDOWN is available to analyze the breakdown of computation time (or any other measureable quantity) function by function. (BREAKDOWN(BREAKDOWN (Function) NIL NIL ("22") 7) FN1 ... FNN) [NLambda NoSpread Function] You call BREAKDOWN giving it a list of function names (unevaluated). These functions are modified so that they keep track of various statistics. To remove functions from those being monitored, simply UNBREAK (Chapter 15) the functions, thereby restoring them to their original state. To add functions, call BREAKDOWN on the new functions. This will not reset the counters for any functions not on the new list. However (BREAKDOWN) will zero the counters of all functions being monitored. The procedure used for measuring is such that if one function calls other and both are "broken down", then the time (or whatever quantity is being measured) spent in the inner function is not charged to the outer function as well. BREAKDOWN will not give accurate results if a function being measured is not returned from normally, e.g., a lower RETFROM (or ERROR) bypasses it. In this case, all of the time (or whatever quantity is being measured) between the time that function is entered and the time the next function being measured is entered will be charged to the first function. (BRKDWNRESULTS(BRKDWNRESULTS (Function) NIL NIL ("22") 7) RETURNVALUESFLG) [Function] BRKDWNRESULTS prints the analysis of the statistics requested as well as the number of calls to each function. If RETURNVALUESFLG is non-NIL, BRKDWNRESULTS will not to print the results, but instead return them in the form of a list of elements of the form (FNNAME #CALLS VALUE). Example: (BREAKDOWN SUPERPRINT SUBPRINT COMMENT1) (SUPERPRINT SUBPRINT COMMENT1) (PRETTYDEF '(SUPERPRINT) 'FOO) FOO.;3 (BRKDWNRESULTS) FUNCTIONS TIME #CALLS PER CALL % SUPERPRINT 8.261 365 0.023 20 SUBPRINT 31.910 141 0.226 76 COMMENT1 1.612 8 0.201 4 TOTAL 41.783 514 0.081 NIL (BRKDWNRESULTS T) ((SUPERPRINT 365 8261) (SUBPRINT 141 31910) (COMMENT1 8 1612)) BREAKDOWN can be used to measure other statistics, by setting the following variables: BRKDWNTYPE(BRKDWNTYPE (Variable) NIL NIL ("22") 7) [Variable] To use BREAKDOWN to measure other statistics, before calling BREAKDOWN, set the variable BRKDWNTYPE to the quantity of interest, e.g., TIME, CONSES, etc, or a list of such quantities. Whenever BREAKDOWN is called with BRKDWNTYPE not NIL, BREAKDOWN performs the necessary changes to its internal state to conform to the new analysis. In particular, if this is the first time an analysis is being run with a particular statistic, a measuring function will be defined, and the compiler will be called to compile it. The functions being broken down will be redefined to call this measuring function. When BREAKDOWN is through initializing, it sets BRKDWNTYPE back to NIL. Subsequent calls to BREAKDOWN will measure the new statistic until BRKDWNTYPE is again set and a new BREAKDOWN performed. BRKDWNTYPES(BRKDWNTYPES (Variable) NIL NIL ("22") 8) [Variable] The list BRKDWNTYPES contains the information used to analyze new statistics. Each entry on BRKDWNTYPES should be of the form (TYPE FORM FUNCTION), where TYPE is a statistic name (as would appear in BRKDWNTYPE), FORM computes the statistic, and FUNCTION (optional) converts the value of form to some more interesting quantity. For example, (TIME (CLOCK 2) (LAMBDA (X) (FQUOTIENT X 1000))) measures computation time and reports the result in seconds instead of milliseconds. BRKDWNTYPES currently contains entries for TIME, CONSES, PAGEFAULTS, BOXES, and FBOXES. Example: (SETQ BRKDWNTYPE '(TIME CONSES)) (TIME CONSES) (BREAKDOWN MATCH CONSTRUCT) (MATCH CONSTRUCT) (FLIP '(A B C D E F G H C Z) '(.. $1 .. #2 ..) '(.. #3 ..)) (A B D E F G H Z) (BRKDWNRESULTS) FUNCTIONS TIME #CALLS PER CALL % MATCH 0.036 1 0.036 54 CONSTRUCT 0.031 1 0.031 46 TOTAL 0.067 2 0.033 FUNCTIONS CONSES #CALLS PER CALL % MATCH 32 1 32.000 40 CONSTRUCT 49 1 49.000 60 TOTAL 81 2 40.500 NIL Occasionally, a function being analyzed is sufficiently fast that the overhead involved in measuring it obscures the actual time spent in the function. If you were using TIME, you would specify a value for TIMEN greater than 1 to give greater accuracy. A similar option is available for BREAKDOWN. You can specify that a function(s) be executed a multiple number of times for each measurement, and the average value reported, by including a number in the list of functions given to BREAKDOWN. For example, BREAKDOWN(EDITCOM EDIT4F 10 EDIT4E EQP) means normal breakdown for EDITCOM and EDIT4F but executes (the body of) EDIT4E and EQP 10 times each time they are called. Of course, the functions so measured must not cause any harmful side effects, since they are executed more than once for each call. The printout from BRKDWNRESULTS will look the same as though each function were run only once, except that the measurement will be more accurate. Another way of obtaining more accurate measurement is to expand the call to the measuring function in-line. If the value of BRKDWNCOMPFLG is non-NIL (initially NIL), then whenever a function is broken-down, it will be redefined to call the measuring function, and then recompiled. The measuring function is expanded in-line via an appropriate macro. In addition, whenever BRKDWNTYPE is reset, the compiler is called for all functions for which BRKDWNCOMPFLG was set at the time they were originally broken-down, i.e. the setting of the flag at the time a function is broken-down determines whether the call to the measuring code is compiled in-line. GAINSPACE 1 If you have large programs and databases, you may sometimes find yourself in a situation where you need to obtain more space, and are willing to pay the price of eliminating some or all of the context information that the various user-assistance facilities such as the programmer's assistant, file package, CLISP, etc., have accumulated during the course of his session. The function GAINSPACE provides an easy way to selectively throw away accumulated data: (GAINSPACE(GAINSPACE (Function) NIL NIL ("22") 9)) [Function] Prints a list of deletable objects, allowing you to specify at each point what should be discarded and what should be retained. For example: (GAINSPACE) purge history lists ? Yes purge everything, or just the properties, e.g., SIDE, LISPXPRINT, etc. ? just the properties discard definitions on property lists ? Yes discard old values of variables ? Yes erase properties ? No erase CLISP translations? Yes GAINSPACE is driven by the list GAINSPACEFORMS. Each element on GAINSPACEFORMS is of the form (PRECHECK MESSAGE FORM KEYLST). If PRECHECK, when evaluated, returns NIL, GAINSPACE skips to the next entry. For example, you will not be asked whether or not to purge the history list if it is not enabled. Otherwise, ASKUSER (Chapter 26) is called with the indicated MESSAGE and the (optional) KEYLST. If you respond No, i.e., ASKUSER returns N, GAINSPACE skips to the next entry. Otherwise, FORM is evaluated with the variable RESPONSE bound to the value of ASKUSER. In the above example, the FORM for the "purge history lists" question calls ASKUSER to ask "purge everything, ..." only if you had responded Yes. If you had responded with Everything, the second question would not have been asked. The "erase properties" question is driven by a list SMASHPROPSMENU. Each element on this list is of the form (MESSAGE . PROPS). You are prompted with MESSAGE (by ASKUSER), and if your response is Yes, PROPS is added to the list SMASHPROPS. The "discard definitions on property lists" and "discard old values of variables" questions also add to SMASHPROPS. You will not be prompted for any entry on SMASHPROPSMENU for which all of the corresponding properties are already on SMASHPROPS. SMASHPROPS is initially set to the value of SMASHPROPSLST. This permits you to specify in advance those properties which you always want discarded, and not be asked about them subsequently. After finishing all the entries on GAINSPACEFORMS, GAINSPACE checks to see if the value of SMASHPROPS is non-NIL, and if so, does a MAPATOMS, i.e., looks at every atom in the system, and erases the indicated properties. You can change or add new entries to GAINSPACEFORMS or SMASHPROPSMENU, so that GAINSPACE can also be used to purge structures that your programs have accumulated. Using Data Types Instead of Records 1 If a program uses large numbers of large data structures, there are several advantages to representing them as user data types rather than as list structures. The primary advantage is increased speed: accessing and setting the fields of a data type can be significantly faster than walking through a list with repeated CARs and CDRs. Also, Compiled code for referencing data types is usually smaller. Finally, by reducing the number of objects created (one object against many list cells), this can reduce the expense of garbage collection. User data types are declared by using the DATATYPE record type (Chapter 8). If a list structure has been defined using the RECORD record type (Chapter 8), and all accessing operations are written using the record package's fetch, replace, and create operations, changing from RECORDs to DATATYPEs only requires editing the record declaration (using EDITREC, Chapter 8) to replace declaration type RECORD by DATATYPE, and recompiling. Note: There are some minor disadvantages: First, there is an upper limit on the number of data types that can exist. Also, space for data types is allocated two pages at a time. Each data type which has any instances allocated has at least two pages assigned to it, which may be wasteful of space if there are only a few examples of a given data type. These problems should not effect most applications programs. Using Fast and Destructive Functions 1 Among the functions used for manipulating objects of various data types, there are a number of functions which have "fast" and "destructive" versions. You should be aware of what these functions do, and when they should be used. Fast functions: By convention, a function named by prefixing an existing function name with F indicates that the new function is a "fast" version of the old. These usually have the same definitions as the slower versions, but they compile open and run without any "safety" error checks. For example, FNTH runs faster than NTH, however, it does not make as many checks (for lists ending with anything but NIL, etc). If these functions are given arguments that are not in the form that they expect, their behavior is unpredictable; they may run forever, or cause a system error. In general, you should only use "fast" functions in code that has already been completely debugged, to speed it up. Destructive functions: By convention, a function named by prefixing an existing function with D indicates the new function is a "destructive" version of the old one, which does not make any new structure but cannibalizes its argument(s). For example, REMOVE returns a copy of a list with a particular element removed, but DREMOVE actually changes the list structure of the list. (Unfortunately, not all destructive functions follow this naming convention: the destructive version of APPEND is NCONC.) You should be careful when using destructive functions that they do not inadvertantly change data structures. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "21-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "21-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "21-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "21-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "21-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "21-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))A PAGEHEADING RIGHTPAGET,~~,~~26TT52`~~,HH@ PAGEHEADINGLEFTBACKT-T,206 +306 +T,3(T,,,3(T/ PALATINO PALATINO PALATINO TITAN TITAN TITAN TITAN TITAN VH(DEFAULTFONT 1 (GACHA 10) (GACHA 8) (TERMINAL 8) (POSTSCRIPT (GACHA 8))) +MODERN +MODERN +    IM.CHAP.GETFNMODERN + + + HRULE.GETFNMODERN + * +  HRULE.GETFNMODERN + M~G7IM.INDEX.GETFNU %IM.INDEX.GETFNTITAN  +   +(IM.INDEX.GETFNMODERN + -  )IM.INDEX.GETFNMODERN + 6 '  #IM.INDEX.GETFNTITAN  N&!  #IM.INDEX.GETFNTITAN  P +   %IM.INDEX.GETFNTITAN   1) 4 -# R +?N +!  N/9 "f > !'########4 >   *IM.INDEX.GETFNMODERN + 1yBV T46dv +  HRULE.GETFNMODERN + ++ $- i]6 +1 L + +  HRULE.GETFNMODERN + F +v %IM.INDEX.GETFNTITAN  &5 . O  6  nNH  v/L-  "IM.INDEX.GETFNTITAN  +   Y'N/-  &IM.INDEX.GETFNMODERN + +5 (   'IM.INDEX.GETFNTITAN  >   +(IM.INDEX.GETFNTITAN  J  + +  HRULE.GETFNMODERN + . u  'IM.INDEX.GETFNTITAN    7e i :* a   +IM.INDEX.GETFNTITAN   f f + ))))"?  M +(IM.INDEX.GETFNTITAN   -  +$/  + e " +  & +    )IM.INDEX.GETFNMODERN +   I ) +X0W   + "=(((#(((# M  ' s}   +&  + +  HRULE.GETFNMODERN +  B  'IM.INDEX.GETFNTITAN   I,&  +  &  +  +1, +% +-> + +"   +P% + K$ +  HRULE.GETFNMODERN + @ *J^6) ) +  HRULE.GETFNMODERN + _O! aAr z \ No newline at end of file diff --git a/docs/medley-irm/22-PERFORMANCE.TEDIT b/docs/turpin-irm/IRM-2/22-PERFORMANCE.TEDIT similarity index 100% rename from docs/medley-irm/22-PERFORMANCE.TEDIT rename to docs/turpin-irm/IRM-2/22-PERFORMANCE.TEDIT diff --git a/docs/turpin-irm/IRM-2/22-PROCESSES.TEDIT b/docs/turpin-irm/IRM-2/22-PROCESSES.TEDIT new file mode 100644 index 00000000..3b350df8 Binary files /dev/null and b/docs/turpin-irm/IRM-2/22-PROCESSES.TEDIT differ diff --git a/docs/medley-irm/23-PROCESSES.TEDIT b/docs/turpin-irm/IRM-2/23-PROCESSES.TEDIT similarity index 100% rename from docs/medley-irm/23-PROCESSES.TEDIT rename to docs/turpin-irm/IRM-2/23-PROCESSES.TEDIT diff --git a/docs/turpin-irm/IRM-2/TOP-AND-BOTTOM.TEDIT b/docs/turpin-irm/IRM-2/TOP-AND-BOTTOM.TEDIT new file mode 100644 index 00000000..81f5f3fe Binary files /dev/null and b/docs/turpin-irm/IRM-2/TOP-AND-BOTTOM.TEDIT differ diff --git a/docs/turpin-irm/IRM-3/001-TITLEPAGE.TEDIT b/docs/turpin-irm/IRM-3/001-TITLEPAGE.TEDIT new file mode 100644 index 00000000..0daa8765 Binary files /dev/null and b/docs/turpin-irm/IRM-3/001-TITLEPAGE.TEDIT differ diff --git a/docs/medley-irm/23-SSTREAMS.TEDIT b/docs/turpin-irm/IRM-3/23-SSTREAMS.TEDIT similarity index 100% rename from docs/medley-irm/23-SSTREAMS.TEDIT rename to docs/turpin-irm/IRM-3/23-SSTREAMS.TEDIT diff --git a/docs/turpin-irm/IRM-3/23-STREAMS.TEDIT b/docs/turpin-irm/IRM-3/23-STREAMS.TEDIT new file mode 100644 index 00000000..f32d621e Binary files /dev/null and b/docs/turpin-irm/IRM-3/23-STREAMS.TEDIT differ diff --git a/docs/turpin-irm/IRM-3/24-IO.TEDIT b/docs/turpin-irm/IRM-3/24-IO.TEDIT new file mode 100644 index 00000000..519b9b5c --- /dev/null +++ b/docs/turpin-irm/IRM-3/24-IO.TEDIT @@ -0,0 +1,141 @@ + INTERLISP-D REFERENCE MANUAL I/O FUNCTIONS "25"24. INPUT/OUTPUT FUNCTIONS 2 This chapter describes the standard I/O functions used for reading and printing characters and Interlisp expressions on files and other streams. First, the primitive input functions are presented, then the output functions, then functions for random-access operations (such as searching a file for a given stream, or changing the "next-character" pointer to a position in a file). Next, the PRINTOUT statement is documented (see below), which provides an easy way to write complex output operations. Finally, read tables, used to parse characters as Interlisp expressions, are documented. Specifying Streams for Input/Output Functions(INPUT/OUTPUT% FUNCTIONS NIL Input/Output% Functions NIL ("25") 1 SUBNAME SPECIFYING% STREAMS% FOR SUBTEXT specifying% streams% for) 1 Most of the input/output functions in Interlisp-D have an argument named STREAM or FILE, specifying on which open stream the function's action should occur (the name FILE is used in older functions that predate the concept of stream; the two should, however, be treated synonymously). The value of this argument should be one of the following: a stream An object of type STREAM, as returned by OPENSTREAM (Chapter 23) or other stream-producing functions, is always the most precise and efficient way to designate a stream argument. T The litatom T designates the terminal input or output stream of the currently running process, controlling input from the keyboard and output to the display screen. For functions where the direction (input or output) is ambiguous, T is taken to designate the terminal output stream. The T streams are always open; they cannot be closed. The terminal output stream can be set to a given window or display stream by using TTYDISPLAYSTREAM (Chapter 28). The terminal input stream cannot be changed. For more information on terminal I/O, see Chapter 30. NIL The litatom NIL designates the "primary" input or output stream. These streams are initially the same as the terminal input/output streams, but they can be changed by using the functions INPUT and OUTPUT. For functions where the direction (input or output) is ambiguous, e.g., GETFILEPTR, the argument NIL is taken to mean the primary input stream, if that stream is not identical to the terminal input stream, else the primary output stream. a window Uses the display stream of the window . Valid for output only. a file name As of this writing, the name of an open file (as a litatom) can be used as a stream argument. However, there are inefficiencies and possible future incompatibilities associated with doing so. See Chapter 24 for details. (GETSTREAM(GETSTREAM (Function) NIL NIL ("25") 2) FILE ACCESS) [Function] Coerces the argument FILE to a stream by the above rules. If ACCESS is INPUT, OUTPUT, or BOTH, produces the stream designated by FILE that is open for ACCESS. If ACCESS=NIL, returns a stream for FILE open for any kind of input/output (see the list above for the ambiguous cases). If FILE does not designate a stream open in the specified mode, causes an error, FILE NOT OPEN. (STREAMP(STREAMP (Function) NIL NIL ("25") 2) X) [Function] Returns X if X is a STREAM, otherwise NIL. Input Functions 1 While the functions described below can take input from any stream, some special actions occur when the input is from the terminal (the T input stream, see above). When reading from the terminal, the input is buffered a line at a time, unless buffering has been inhibited by CONTROL (Chapter 30) or the input is being read by READC or PEEKC. Using specified editing characters, you can erase a character at a time, a word at a time, or the whole line. The keys that perform these editing functions are assignable via SETSYNTAX, with the initial settings chosen to be those most natural for the given operating system. In Interlisp-D, the initial settings are as follows: characters are deleted one at a time by Backspace; words are erased by control-W; the whole line is erased by Control-Q. On the Interlisp-D display, deleting a character or a line causes the characters to be physically erased from the screen. In Interlisp-10, the deleting action can be modified for various types of display terminals by using DELETECONTROL (Chapter 30). Unless otherwise indicated, when the end of file is encountered while reading from a file, all input functions generate an error, END OF FILE. Note that this does not close the input file. The ENDOFSTREAMOP stream attribute (Chapter 24) is useful for changing the behavior at end of file. Most input functions have a RDTBL argument, which specifies the read table to be used for input. Unless otherwise specified, if RDTBL is NIL, the primary read table is used. If the FILE or STREAM argument to an input function is NIL, the primary input stream is used. (INPUT(INPUT (Function) NIL NIL ("25") 2) FILE) [Function] Sets FILE as the primary input stream; returns the old primary input stream. FILE must be open for input. (INPUT) returns the current primary input stream, which is not changed. Note: If the primary input stream is set to a file, the file's full name, rather than the stream itself, is returned. See discussion in Chapter 24. (READ(READ (Function) NIL NIL ("25") 3) FILE RDTBL FLG) [Function] Reads one expression from FILE. Atoms are delimited by the break and separator characters as defined in RDTBL. To include a break or separator character in an atom, the character must be preceded by the character %, e.g., AB%(C is the atom AB(C, %% is the atom %, %control-K is the atom Control-K. For input from the terminal, an atom containing an interrupt character can be input by typing instead the corresponding alphabetic character preceded by Control-V, e.g., ^VD for Control-D. Strings are delimited by double quotes. To input a string containing a double quote or a %, precede it by %, e.g., "AB%"C" is the string AB"C. Note that % can always be typed even if next character is not "special", e.g., %A%B%C is read as ABC. If an atom is interpretable as a number, READ creates a number, e.g., 1E3 reads as a floating point number, 1D3 as a literal atom, 1.0 as a number, 1,0 as a literal atom, etc. An integer can be input in a non-decimal radix by using syntax such as 123Q, |b10101, |5r1234 (see Chapter 7). The function RADIX, sets the radix used to print integers. When reading from the terminal, all input is line-buffered to enable the action of the backspacing control characters, unless inhibited by CONTROL (Chapter 30). Thus no characters are actually seen by the program until a carriage-return (actually the character with terminal syntax class EOL, see Chapter 30), is typed. However, for reading by READ, when a matching right parenthesis is encountered, the effect is the same as though a carriage-return were typed, i.e., the characters are transmitted. To indicate this, Interlisp also prints a carriage-return line-feed on the terminal. The line buffer is also transmitted to READ whenever an IMMEDIATE read macro character is typed (see below). FLG=T suppresses the carriage-return normally typed by READ following a matching right parenthesis. (However, the characters are still given to READ; i.e., you do not have to type the carriage-return.) (RATOM FILE RDTBL) [Function] Reads in one atom from FILE. Separation of atoms is defined by RDTBL. % is also defined for RATOM, and the remarks concerning line-buffering and editing control characters also apply. If the characters comprising the atom would normally be interpreted as a number by READ, that number is returned by RATOM. Note however that RATOM takes no special action for " whether or not it is a break character, i.e., RATOM never makes a string. (RSTRING(RSTRING (Function) NIL NIL ("25") 3) FILE RDTBL) [Function] Reads characters from FILE up to, but not including, the next break or separator character, and returns them as a string. Backspace, Control-W, Control-Q, Control-V, and % have the same effect as with READ. Note that the break or separator character that terminates a call to RATOM or RSTRING is not read by that call, but remains in the buffer to become the first character seen by the next reading function that is called. If that function is RSTRING, it will return the null string. This is a common source of program bugs. (RATOMS(RATOMS (Function) NIL NIL ("25") 4) A FILE RDTBL) [Function] Calls RATOM repeatedly until the atom A is read. Returns a list of the atoms read, not including A. (RATEST(RATEST (Function) NIL NIL ("25") 4) FLG) [Function] If FLG = T, RATEST returns T if a separator was encountered immediately prior to the atom returned by the last RATOM or READ, NIL otherwise. If FLG = NIL, RATEST returns T if last atom read by RATOM or READ was a break character, NIL otherwise. If FLG = 1, RATEST returns T if last atom read (by READ or RATOM) contained a % used to quote the next character (as in %[ or %A%B%C), NIL otherwise. (READC (READC% (Function) NIL NIL ("25") 4)FILE RDTBL) [Function] Reads and returns the next character, including %, ", etc, i.e., is not affected by break or separator characters. The action of READC is subject to line-buffering, i.e., READC does not return a value until the line has been terminated even if a character has been typed. Thus, the editing control characters have their usual effect. RDTBL does not directly affect the value returned, but is used as usual in line-buffering, e.g., determining when input has been terminated. If (CONTROL T) has been executed (Chapter 30), defeating line-buffering, the RDTBL argument is irrelevant, and READC returns a value as soon as a character is typed (even if the character typed is one of the editing characters, which ordinarily would never be seen in the input buffer). (PEEKC (PEEKC% (Function) NIL NIL ("25") 4)FILE) [Function] Returns the next character, but does not actually read it and remove it from the buffer. If reading from the terminal, the character is echoed as soon as PEEKC reads it, even though it is then "put back" into the system buffer, where Backspace, Control-W, etc. could change it. Thus it is possible for the value returned by PEEKC to "disagree" in the first character with a subsequent READ. (LASTC(LASTC (Function) NIL NIL ("25") 4) FILE) [Function] Returns the last character read from FILE. LASTC can return an incorrect result when called immediatley following a PEEKC on a file that contains run-coded NS characters. (READCCODE(READCCODE (Function) NIL NIL ("25") 4) FILE RDTBL) [Function] Returns the next character code from STREAM; thus, this operation is equivalent to, but more efficient than, (CHCON1 (READC FILE RDTBL)). (PEEKCCODE(PEEKCCODE (Function) NIL NIL ("25") 5) FILE) [Function] Returns, without consuming, the next character code from STREAM; thus, this operation is equivalent to, but more efficient than, (CHCON1 (PEEKC FILE)). (BIN(BIN (Function) NIL NIL ("25") 5) STREAM) [Function] Returns the next byte from STREAM. This operation is useful for reading streams of binary, rather than character, data. Note: BIN is similar to READCCODE, except that BIN always reads a single byte, whereas READCCODE reads a "character" that can consist of more than one byte, depending on the character and its encoding. READ, RATOM, RATOMS, PEEKC, READC all wait for input if there is none. The only way to test whether or not there is input is to use READP: (READP(READP (Function) NIL NIL ("25") 5) FILE FLG) [Function] Returns T if there is anything in the input buffer of FILE, NIL otherwise. This operation is only interesting for streams whose source of data is dynamic, e.g., the terminal or a byte stream over a network; for other streams, such as to files, (READP FILE) is equivalent to (NOT (EOFP FILE)). Note that because of line-buffering, READP may return T, indicating there is input in the buffer, but READ may still have to wait. Frequently, the terminal's input buffer contains a single EOL character left over from a previous input. For most applications, this situation wants to be treated as though the buffer were empty, and so READP returns NIL in this case. However, if FLG=T, READP returns T if there is any character in the input buffer, including a single EOL. FLG is ignored for streams other than the terminal. (EOFP(EOFP (Function) NIL NIL ("25") 5) FILE) [Function] Returns true if FILE is at "end of file", i.e., the next call to an input function would cause an END OF FILE error; NIL otherwise. For randomly accessible files, this can also be thought of as the file pointer pointing beyond the last byte of the file. FILE must be open for (at least) input, or an error is generated, FILE NOT OPEN. Note that EOFP can return NIL and yet the next call to READ might still cause an END OF FILE error, because the only characters remaining in the input were separators or otherwise constituted an incomplete expression. The function SKIPSEPRS is sometimes more useful as a way of detecting end of file when it is known that all the expressions in the file are well formed. (WAITFORINPUT (WAITFORINPUT% (Function) NIL NIL ("25") 5)FILE) [Function] Waits until input is available from FILE or from the terminal, i.e. from T. WAITFORINPUT is functionally equivalent to (until (OR (READP T) (READP FILE)) do NIL), except that it does not use up machine cycles while waiting. Returns the device for which input is now available, i.e. FILE or T. FILE can also be an integer, in which case WAITFORINPUT waits until there is input available from the terminal, or until FILE milliseconds have elapsed. Value is T if input is now available, NIL in the case that WAITFORINPUT timed out. (SKREAD(SKREAD (Function) NIL NIL ("25") 6) FILE REREADSTRING RDTBL) [Function] "Skip Read". SKREAD consumes characters from FILE as if one call to READ had been performed, without paying the storage and compute cost to really read in the structure. REREADSTRING is for the case where the caller has already performed some READC's and RATOM's before deciding to skip this expression. In this case, REREADSTRING should be the material already read (as a string), and SKREAD operates as though it had seen that material first, thus setting up its parenthesis count, double-quote count, etc. The read table RDTBL is used for reading from FILE. If RDTBL is NIL, it defaults to the value of FILERDTBL. SKREAD may have difficulties if unusual read macros are defined in RDTBL. SKREAD does not recognize read macro characters in REREADSTRING, nor SPLICE or INFIX read macros. This is only a problem if the read macros are defined to parse subsequent input in the stream that does not follow the normal parenthesis and string-quote conventions. SKREAD returns %) if the read terminated on an unbalanced closing parenthesis; %] if the read terminated on an unbalanced %], i.e., one which also would have closed any extant open left parentheses; otherwise NIL. (SKIPSEPRS(SKIPSEPRS (Function) NIL NIL ("25") 6) FILE RDTBL) [Function] Consumes characters from FILE until it encounters a non-separator character (as defined by RDTBL). SKIPSEPRS returns, but does not consume, the terminating character, so that the next call to READC would return the same character. If no non-separator character is found before the end of file is reached, SKIPSEPRS returns NIL and leaves the stream at end of file. This function is useful for skipping over "white space" when scanning a stream character by character, or for detecting end of file when reading expressions from a stream with no pre-arranged terminating expression. Output Functions(OUTPUT% FUNCTIONS NIL Output% Functions NIL ("25") 6) 1 Unless otherwise specified by DEFPRINT, pointers other than lists, strings, atoms, or numbers, are printed in the form {DATATYPE} followed by the octal representation of the address of the pointer (regardless of radix). For example, an array pointer might print as {ARRAYP}#43,2760. This printed representation is for compactness of display on your terminal, and will not read back in correctly; if the form above is read, it will produce the litatom {ARRAYP}#43,2760. Note: The term "end-of-line" appearing in the description of an output function means the character or characters used to terminate a line in the file system being used by the given implementation of Interlisp. For example, in Interlisp-D end-of-line is indicated by the character carriage-return. Some of the functions described below have a RDTBL argument, which specifies the read table to be used for output. If RDTBL is NIL, the primary read table is used. Most of the functions described below have an argument FILE, which specifies the stream on which the operation is to take place. If FILE is NIL, the primary output stream is used . (OUTPUT(OUTPUT (Function) NIL NIL ("25") 7) FILE) [Function] Sets FILE as the primary output stream; returns the old primary output stream. FILE must be open for output. (OUTPUT) returns the current primary output stream, which is not changed. Note: If the primary output stream is set to a file, the file's full name, rather than the stream itself, is returned. See the discussion in Chapter 24. (PRIN1(PRIN1 (Function) NIL NIL ("25") 7) X FILE) [Function] Prints X on FILE. (PRIN2(PRIN2 (Function) NIL NIL ("25") 7) X FILE RDTBL) [Function] Prints X on FILE with %'s and "'s inserted where required for it to read back in properly by READ, using RDTBL. Both PRIN1 and PRIN2 print any kind of Lisp expression, including lists, atoms, numbers, and strings. PRIN1 is generally used for printing expressions where human readability, rather than machine readability, is important, e.g., when printing text rather than program fragments. PRIN1 does not print double quotes around strings, or % in front of special characters. PRIN2 is used for printing Interlisp expressions which can then be read back into Interlisp with READ; i.e., break and separator characters in atoms will be preceded by %'s. For example, the atom "()" is printed as %(%) by PRIN2. If the integer output radix (as set by RADIX) is not 10, PRIN2 prints the integer using the input syntax for non-decimal integers (see Chapter 7) but PRIN1 does not (but both print the integer in the output radix). (PRIN3(PRIN3 (Function) NIL NIL ("25") 7) X FILE) [Function] (PRIN4(PRIN4 (Function) NIL NIL ("25") 7) X FILE RDTBL) [Function] PRIN3 and PRIN4 are the same as PRIN1 and PRIN2 respectively, except that they do not increment the horizontal position counter nor perform any linelength checks. They are useful primarily for printing control characters. (PRINT(PRINT (Function) NIL NIL ("25") 7) X FILE RDTBL) [Function] Prints the expression X using PRIN2 followed by an end-of-line. Returns X. (PRINTCCODE(PRINT (Function) NIL NIL ("25") 8) CHARCODE FILE) [Function] Outputs a single character whose code is CHARCODE to FILE. This is similar to (PRIN1 (CHARACTER CHARCODE)), except that numeric characters are guaranteed to print "correctly"; e.g., (PRINTCCODE (CHARCODE 9)) always prints "9", independent of the setting of RADIX. PRINTCCODE may actually print more than one byte on FILE, due to character encoding and end of line conventions; thus, no assumptions should be made about the relative motion of the file pointer (see GETFILEPTR) during this operation. (BOUT(BOUT (Function) NIL NIL ("25") 8) STREAM BYTE) [Function] Outputs a single 8-bit byte to STREAM. This is similar to PRINTCCODE, but for binary streams the character position in STREAM is not updated (as with PRIN3), and end of line conventions are ignored. Note: BOUT is similar to PRINTCCODE, except that BOUT always writes a single byte, whereas PRINTCCODE writes a "character" that can consist of more than one byte, depending on the character and its encoding. (SPACES(SPACES (Function) NIL NIL ("25") 8) N FILE) [Function] Prints N spaces. Returns NIL. (TERPRI (TERPRI% (Function) NIL NIL ("25") 8)FILE) [Function] Prints an end-of-line character. Returns NIL. (FRESHLINE(FRESHLINE (Function) NIL NIL ("25") 8) STREAM) [Function] Equivalent to TERPRI, except it does nothing if it is already at the beginning of the line. Returns T if it prints an end-of-line, NIL otherwise. (TAB(TAB (Function) NIL NIL ("25") 8) POS MINSPACES FILE) [Function] Prints the appropriate number of spaces to move to position POS. MINSPACES indicates how many spaces must be printed (if NIL, 1 is used). If the current position plus MINSPACES is greater than POS, TAB does a TERPRI and then (SPACES POS). If MINSPACES is T, and the current position is greater than POS, then TAB does nothing. Note: A sequence of PRINT, PRIN2, SPACES, and TERPRI expressions can often be more conveniently coded with a single PRINTOUT statement. (SHOWPRIN2(SHOWPRIN2 (Function) NIL NIL ("25") 8) X FILE RDTBL) [Function] Like PRIN2 except if SYSPRETTYFLG=T, prettyprints X instead. Returns X. (SHOWPRINT(SHOWPRINT (Function) NIL NIL ("25") 9) X FILE RDTBL) [Function] Like PRINT except if SYSPRETTYFLG=T, prettyprints X instead, followed by an end-of-line. Returns X. SHOWPRINT and SHOWPRIN2 are used by the programmer's assistant (Chapter 13) for printing the values of expressions and for printing the history list, by various commands of the break package (Chapter 14), e.g. ?= and BT commands, and various other system packages. The idea is that by simply settting or binding SYSPRETTYFLG to T (initially NIL), you instruct the system when interacting with you to PRETTYPRINT expressions (Chapter 26) instead of printing them. (PRINTBELLS(PRINTBELLS (Function) NIL NIL ("25") 9) ) [Function] Used by DWIM (Chapter 19) to print a sequence of bells to alert you to stop typing. Can be advised or redefined for special applications, e.g., to flash the screen on a display terminal. (FORCEOUTPUT(FORCEOUTPUT (Function) NIL NIL ("25") 9) STREAM WAITFORFINISH) [Function] Forces any buffered output data in STREAM to be transmitted. If WAITFORFINISH is non-NIL, this doesn't return until the data has been forced out. (POSITION(POSITION (Function) NIL NIL ("25") 9) FILE N) [Function] Returns the column number at which the next character will be read or printed. After a end of line, the column number is 0. If N is non-NIL, resets the column number to be N. Note that resetting POSITION only changes Lisp's belief about the current column number; it does not cause any horizontal motion. Also note that (POSITION FILE) is not the same as (GETFILEPTR FILE) which gives the position in the file, not on the line. (LINELENGTH(LINELENGTH (Function) NIL NIL ("25") 9) N FILE) [Function] Sets the length of the print line for the output file FILE to N; returns the former setting of the line length. FILE defaults to the primary output stream. (LINELENGTH NIL FILE) returns the current setting for FILE. When a file is first opened, its line length is set to the value of the variable FILELINELENGTH. Whenever printing an atom or string would increase a file's position beyond the line length of the file, an end of line is automatically inserted first. This action can be defeated by using PRIN3 and PRIN4. (SETLINELENGTH(SETLINELENGTH (Function) NIL NIL ("25") 9) N) [Function] Sets the line length for the terminal by doing (LINELENGTH N T). If N is NIL, it determines N by consulting the operating system's belief about the terminal's characteristics. In Interlisp-D, this is a no-op. PRINTLEVEL When using Interlisp one often has to handle large, complicated lists, which are difficult to understand when printed out. PRINTLEVEL allows you to specify in how much detail lists should be printed. The print functions PRINT, PRIN1, and PRIN2 are all affected by level parameters set by: (PRINTLEVEL(PRINTLEVEL (Function) NIL NIL ("25") 10) CARVAL CDRVAL) [Function] Sets the CAR print level to CARVAL, and the CDR print level to CDRVAL. Returns a list cell whose CAR and CDR are the old settings. PRINTLEVEL is initialized with the value (1000 . -1). In order that PRINTLEVEL can be used with RESETFORM or RESETSAVE, if CARVAL is a list cell it is equivalent to (PRINTLEVEL (CAR CARVAL) (CDR CARVAL)). (PRINTLEVEL N NIL) changes the CAR printlevel without affecting the CDR printlevel. (PRINTLEVEL NIL N) changes the CDR printlevel with affecting the CAR printlevel. (PRINTLEVEL) gives the current setting without changing either. Note: Control-P (Chapter 30) can be used to change the PRINTLEVEL setting dynamically, even while Interlisp is printing. The CAR printlevel specifies how "deep" to print a list. Specifically, it is the number of unpaired left parentheses which will be printed. Below that level, all lists will be printed as &. If the CAR printlevel is negative, the action is similar except that an end-of-line is inserted after each right parentheses that would be immediately followed by a left parenthesis. The CDR printlevel specifies how "long" to print a list. It is the number of top level list elements that will be printed before the printing is terminated with --. For example, if CDRVAL=2, (A B C D E) will print as (A B --). For sublists, the number of list elements printed is also affected by the depth of printing in the CAR direction: Whenever the sum of the depth of the sublist (i.e. the number of unmatched left parentheses) and the number of elements is greater than the CDR printlevel, -- is printed. This gives a "triangular" effect in that less is printed the farther one goes in either CAR or CDR direction. If the CDR printlevel is negative, then it is the same as if the CDR printlevel were infinite. Examples: After: (A (B C (D (E F) G) H) K L) prints as: (PRINTLEVEL 3 -1) (A (B C (D & G) H) K L) (PRINTLEVEL 2 -1) (A (B C & H) K L) (PRINTLEVEL 1 -1) (A & K L) (PRINTLEVEL 0 -1) & (PRINTLEVEL 1000 2) (A (B --) --) (PRINTLEVEL 1000 3) (A (B C --) K --) (PRINTLEVEL 1 3) (A & K --) PLVLFILEFLG (PLVLFILEFLG% (Variable) NIL NIL ("25") 11) [Variable] Normally, PRINTLEVEL only affects terminal output. Output to all other files acts as though the print level is infinite. However, if PLVLFILEFLG is T (initially NIL), then PRINTLEVEL affects output to files as well. The following three functions are useful for printing isolated expressions at a specified print level without going to the overhead of resetting the global print level. (LVLPRINT (LVLPRINT% (Function) NIL NIL ("25") 11)X FILE CARLVL CDRLVL TAIL) [Function] Performs PRINT of X to FILE, using as CAR and CDR print levels the values CARLVL and CDRLVL, respectively. Uses the T read table. If TAIL is specified, and X is a tail of it, then begins its printing with "...", rather than on open parenthesis. (LVLPRIN2(LVLPRIN2 (Function) NIL NIL ("25") 11) X FILE CARLVL CDRLVL TAIL) [Function] Similar to LVLPRIN2, but performs a PRIN2. (LVLPRIN1(LVLPRIN1 (Function) NIL NIL ("25") 11) X FILE CARLVL CDRLVL TAIL) [Function] Similar to LVLPRIN1, but performs a PRIN1. Printing Numbers(PRINTING% NUMBERS NIL Printing% Numbers NIL ("25") 11) How the ordinary printing functions (PRIN1, PRIN2, etc.) print numbers can be affected in several ways. RADIX influences the printing of integers, and FLTFMT influences the printing of floating point numbers. The setting of the variable PRXFLG determines how the symbol-manipulation functions handle numbers. The PRINTNUM package permits greater controls on the printed appearance of numbers, allowing such things as left-justification, suppression of trailing decimals, etc. (RADIX(RADIX (Function) NIL NIL ("25") 11) N) [Function] Resets the output radix for integers to the absolute value of N. The value of RADIX is its previous setting. (RADIX) gives the current setting without changing it. The initial setting is 10. Note that RADIX affects output only. There is no input radix; on input, numbers are interpreted as decimal unless they are entered in a non-decimal radix with syntax such as 123Q, |b10101, |5r1234 (see Chapter 7). RADIX does not affect the behavior of UNPACK, etc., unless the value of PRXFLG (below) is T. For example, if PRXFLG is NIL and the radix is set to 8 with (RADIX 8), the value of (UNPACK 9) is (9), not (1 1). Using PRINTNUM (below) or the PRINTOUT command .I (below) is often a more convenient and appropriate way to print a single number in a specified radix than to globally change RADIX. (FLTFMT(FLTFMT (Function) NIL NIL ("25") 12) FORMAT) [Function] Resets the output format for floating point numbers to the FLOAT format FORMAT (see PRINTNUM below for a description of FLOAT formats). FORMAT=T specifies the default "free" formatting: some number of significant digits (a function of the implementation) are printed, with trailing zeros suppressed; numbers with sufficiently large or small exponents are instead printed in exponent notation. FLTFMT returns its current setting. (FLTFMT) returns the current setting without changing it. The initial setting is T. Note: In Interlisp-D, FLTFMT ignores the WIDTH and PAD fields of the format (they are implemented only by PRINTNUM). Whether print name manipulation functions (UNPACK, NCHARS, etc.) use the values of RADIX and FLTFMT is determined by the variable PRXFLG: PRXFLG(PRXFLG (Variable) NIL NIL ("25") 12) [Variable] If PRXFLG=NIL (the initial setting), then the "PRIN1" name used by PACK, UNPACK, MKSTRING, etc., is computed using base 10 for integers and the system default floating format for floating point numbers, independent of the current setting of RADIX or FLTFMT. If PRXFLG=T, then RADIX and FLTFMT do dictate the "PRIN1" name of numbers. Note that in this case, PACK and UNPACK are not inverses. Examples with (RADIX 8), (FLTFMT '(FLOAT 4 2)): With PRXFLG=NIL, (UNPACK 13) => (1 3) (PACK '(A 9)) => A9 (UNPACK 1.2345) => (1 %. 2 3 4 5) With PRXFLG=T, (UNPACK 13) => (1 5) (PACK '(A 9)) => A11 (UNPACK 1.2345) => (1 %. 2 3) Note that PRXFLG does not effect the radix of "PRIN2" names, so with (RADIX 8), (NCHARS 9 T), which uses PRIN2 names, would return 3, (since 9 would print as 11Q) for either setting of PRXFLG. Warning: Some system functions will not work correctly if PRXFLG is not NIL. Therefore, resetting the global value of PRXFLG is not recommended. It is much better to rebind PRXFLG as a SPECVAR for that part of a program where it needs to be non-NIL. The basic function for printing numbers under format control is PRINTNUM. Its utility is considerably enhanced when used in conjunction with the PRINTOUT package, which implements a compact language for specifying complicated sequences of elementary printing operations, and makes fancy output formats easy to design and simple to program. (PRINTNUM FORMAT NUMBER FILE) [Function] Prints NUMBER on FILE according to the format FORMAT. FORMAT is a list structure with one of the forms described below. If FORMAT is a list of the form (FIX WIDTH RADIX PAD0 LEFTFLUSH), this specifies a FIX format. NUMBER is rounded to the nearest integer, and then printed in a field WIDTH characters long with radix set to RADIX (or 10 if RADIX=NIL; note that the setting from the function RADIX is not used as the default). If PAD0 and LEFTFLUSH are both NIL, the number is right-justified in the field, and the padding characters to the left of the leading digit are spaces. If PAD0 is T, the character "0" is used for padding. If LEFTFLUSH is T, then the number is left-justified in the field, with trailing spaces to fill out WIDTH characters. The following examples illustrate the effects of the FIX format options on the number 9 (the vertical bars indicate the field width): FORMAT: (PRINTNUM FORMAT 9) prints: (FIX 2) | 9| (FIX 2 NIL T) |09| (FIX 12 8 T) |000000000011| (FIX 5 NIL NIL T) |9 | If FORMAT is a list of the form (FLOAT WIDTH DECPART EXPPART PAD0 ROUND), this specifies a FLOAT format. NUMBER is printed as a decimal number in a field WIDTH characters wide, with DECPART digits to the right of the decimal point. If EXPPART is not 0 (or NIL), the number is printed in exponent notation, with the exponent occupying EXPPART characters in the field. EXPPART should allow for the character E and an optional sign to be printed before the exponent digits. As with FIX format, padding on the left is with spaces, unless PAD0 is T. If ROUND is given, it indicates the digit position at which rounding is to take place, counting from the leading digit of the number. Interlisp-D interprets WIDTH=NIL to mean no padding, i.e., to use however much space the number needs, and interprets DECPART=NIL to mean as many decimal places as needed. The following examples illustrate the effects of the FLOAT format options on the number 27.689 (the vertical bars indicate the field width): FORMAT: (PRINTNUM FORMAT 27.689) prints: (FLOAT 7 2) | 27.69| (FLOAT 7 2 NIL 0) |0027.69| (FLOAT 7 2 2) | 2.77E1| (FLOAT 11 2 4) | 2.77E+01| (FLOAT 7 2 NIL NIL 1) | 30.00| (FLOAT 7 2 NIL NIL 2) | 28.00| NILNUMPRINTFLG(NILNUMPRINTFLG (Variable) NIL NIL ("25") 14) [Variable] If PRINTNUM's NUMBER argument is not a number and not NIL, a NON-NUMERIC ARG error is generated. If NUMBER is NIL, the effect depends on the setting of the variable NILNUMPRINTFLG. If NILNUMPRINTFLG is NIL, then the error occurs as usual. If it is non-NIL, then no error occurs, and the value of NILNUMPRINTFLG is printed right-justified in the field described by FORMAT. This option facilitates the printing of numbers in aggregates with missing values coded as NIL. User Defined Printing Initially, Interlisp only knows how to print in an interesting way objects of type litatom, number, string, list and stackp. All other types of objects are printed in the form {datatype} followed by the octal representation of the address of the pointer, a format that cannot be read back in to produce an equivalent object. When defining user data types (using the DATATYPE record type, Chapter 8), it is often desirable to specify as well how objects of that type should be printed, so as to make their contents readable, or at least more informative to the viewer. The function DEFPRINT is used to specify the printing format of a data type. (DEFPRINT(DEFPRINT (Function) NIL NIL ("25") 14) TYPE FN) [Function] TYPE is a type name. Whenever a printing function (PRINT, PRIN1, PRIN2, etc.) or a function requiring a print name (CHCON, NCHARS, etc.) encounters an object of the indicated type, FN is called with two arguments: the item to be printed and the name of the stream, if any, to which the object is to be printed. The second argument is NIL on calls that request the print name of an object without actually printing it. If FN returns a list of the form (ITEM1 . ITEM2), ITEM1 is printed using PRIN1 (unless it is NIL), and then ITEM2 is printed using PRIN2 (unless it is NIL). No spaces are printed between the two items. Typically, ITEM1 is a read macro character. If FN returns NIL, the datum is printed in the system default manner. If FN returns T, nothing further is printed; FN is assumed to have printed the object to the stream itself. Note that this case if permitted only when the second argument passed to FN is non-NIL; otherwise, there is no destination for FN to do its printing, so it must return as in one of the other two cases. Printing Unusual Data Structures HPRINT (for "Horrible Print") and HREAD provide a mechanism for printing and reading back in general data structures that cannot normally be dumped and loaded easily, such as (possibly re-entrant or circular) structures containing user datatypes, arrays, hash tables, as well as list structures. HPRINT will correctly print and read back in any structure containing any or all of the above, chasing all pointers down to the level of literal atoms, numbers or strings. HPRINT currently cannot handle compiled code arrays, stack positions, or arbitrary unboxed numbers. HPRINT operates by simulating the Interlisp PRINT routine for normal list structures. When it encounters a user datatype (see Chapter 8), or an array or hash array, it prints the data contained therein, surrounded by special characters defined as read macro characters. While chasing the pointers of a structure, it also keeps a hash table of those items it encounters, and if any item is encountered a second time, another read macro character is inserted before the first occurrence (by resetting the file pointer with SETFILEPTR) and all subsequent occurrences are printed as a back reference using an appropriate macro character. Thus the inverse function, HREAD merely calls the Interlisp READ routine with the appropriate read table. (HPRINT(HPRINT (Function) NIL NIL ("25") 15) EXPR FILE UNCIRCULAR DATATYPESEEN) [Function] Prints EXPR on FILE. If UNCIRCULAR is non-NIL, HPRINT does no checking for any circularities in EXPR (but is still useful for dumping arbitrary structures of arrays, hash arrays, lists, user data types, etc., that do not contain circularities). Specifying UNCIRCULAR as non-NIL results in a large speed and internal-storage advantage. Normally, when HPRINT encounters a user data type for the first time, it outputs a summary of the data type's declaration. When this is read in, the data type is redeclared. If DATATYPESEEN is non-NIL, HPRINT assumes that the same data type declarations will be in force at read time as were at HPRINT time, and not output declarations. HPRINT is intended primarily for output to random access files, since the algorithm depends on being able to reset the file pointer. If FILE is not a random access file (and UNCIRCULAR = NIL), a temporary file, HPRINT.SCRATCH, is opened, EXPR is HPRINTed on it, and then that file is copied to the final output file and the temporary file is deleted. You can not use HPRINT to save things that contains pointers to raw storage. Fontdescriptors contain pointers to raw storage and windows contain pointers to fontdescriptors. Netiher can therefor be saved with HPRINT. (HREAD(HREAD (Function) NIL NIL ("25") 15) FILE) [Function] Reads and returns an HPRINT-ed expression from FILE. (HCOPYALL(HCOPYALL (Function) NIL NIL ("25") 15) X) [Function] Copies data structure X. X may contain circular pointers as well as arbitrary structures. Note: HORRIBLEVARS and UGLYVARS (Chapter 17) are two file package commands for dumping and reloading circular and re-entrant data structures. They provide a convenient interface to HPRINT and HREAD. When HPRINT is dumping a data structure that contains an instance of an Interlisp datatype, the datatype declaration is also printed onto the file. Reading such a data structure with HREAD can cause problems if it redefines a system datatype. Redefining a system datatype will almost definitely cause serious errors. The Interlisp system datatypes do not change very often, but there is always a possibility when loading in old files created under an old Interlisp release. To prevent accidental system crashes, HREAD will not redefine datatypes. Instead, it will cause an error "attempt to read DATATYPE with different field specification than currently defined". Continuing from this error will redefine the datatype. Random Access File Operations 1 For most applications, files are read starting at their beginning and proceeding sequentially, i.e., the next character read is the one immediately following the last character read. Similarly, files are written sequentially. However, for files on some devices, it is also possible to read/write characters at arbitrary positions in a file, essentially treating the file as a large block of auxiliary storage. For example, one application might involve writing an expression at the beginning of the file, and then reading an expression from a specified point in its middle. This particular example requires the file be open for both input and output. However, random file input or output can also be performed on files that have been opened for only input or only output. Associated with each file is a "file pointer" that points to the location where the next character is to be read from or written to. The file position of a byte is the number of bytes that precede it in the file, i.e., 0 is the position of the beginning of the file. The file pointer to a file is automatically advanced after each input or output operation. This section describes functions which can be used to reposition the file pointer on those files that can be randomly accessed. A file used in this fashion is much like an array in that it has a certain number of addressable locations that characters can be put into or taken from. However, unlike arrays, files can be enlarged. For example, if the file pointer is positioned at the end of a file and anything is written, the file "grows." It is also possible to position the file pointer beyond the end of file and then to write. (If the program attempts to read beyond the end of file, an END OF FILE error occurs.) In this case, the file is enlarged, and a "hole" is created, which can later be written into. Note that this enlargement only takes place at the end of a file; it is not possible to make more room in the middle of a file. In other words, if expression A begins at position 1000, and expression B at 1100, and the program attempts to overwrite A with expression C, whose printed representation is 200 bytes long, part of B will be altered. Warning: File positions are always in terms of bytes, not characters. You should thus be very careful about computing the space needed for an expression. In particular, NS characters may take multiple bytes (see below). Also, the end-of-line character (see Chapter 24) may be represented by a different number of characters in different implementations. Output functions may also introduce end-of-line's as a result of LINELENGTH considerations. Therefore NCHARS (see Chapter 2) does not specify how many bytes an expression takes to print, even ignoring line length considerations. (GETFILEPTR(GETFILEPTR (Function) NIL NIL ("25") 16) FILE) [Function] Returns the current position of the file pointer for FILE, i.e., the byte address at which the next input/output operation will commence. (SETFILEPTR(SETFILEPTR (Function) NIL NIL ("25") 16) FILE ADR) [Function] Sets the file pointer for FILE to the position ADR; returns ADR. The special value ADR=-1 is interpreted to mean the address of the end of file. Note: If a file is opened for output only, the end of file is initially zero, even if an old file by the same name had existed (see OPENSTREAM, Chapter 24). If a file is opened for both input and output, the initial file pointer is the beginning of the file, but (SETFILEPTR FILE -1) sets it to the end of the file. If the file had been opened in append mode by (OPENSTREAM FILE 'APPEND), the file pointer right after opening would be set to the end of the existing file, in which case a SETFILEPTR to position the file at the end would be unnecessary. (GETEOFPTR(GETEOFPTR (Function) NIL NIL ("25") 17) FILE) [Function] Returns the byte address of the end of file, i.e., the number of bytes in the file. Equivalent to performing (SETFILEPTR FILE -1) and returning (GETFILEPTR FILE) except that it does not change the current file pointer. (RANDACCESSP(RANDACCESSP (Function) NIL NIL ("25") 17) FILE) [Function] Returns FILE if FILE is randomly accessible, NIL otherwise. The file T is not randomly accessible, nor are certain network file connections in Interlisp-D. FILE must be open or an error is generated, FILE NOT OPEN. (COPYBYTES(COPYBYTES (Function) NIL NIL ("25") 17) SRCFIL DSTFIL START END) [Function] Copies bytes from SRCFIL to DSTFIL, starting from position START and up to but not including position END. Both SRCFIL and DSTFIL must be open. Returns T. If END=NIL, START is interpreted as the number of bytes to copy (starting at the current position). If START is also NIL, bytes are copied until the end of the file is reached. Warning: COPYBYTES does not take any account of multi-byte NS characters (see Chapter 2). COPYCHARS (below) should be used whenever copying information that might include NS characters. (COPYCHARS(COPYCHARS (Function) NIL NIL ("25") 17) SRCFIL DSTFIL START END) [Function] Like COPYBYTES except that it copies NS characters (see Chapter 2), and performs the proper conversion if the end-of-line conventions of SRCFIL and DSTFIL are not the same (see Chapter 24). START and END are interpreted the same as with COPYBYTES, i.e., as byte (not character) specifications in SRCFIL. The number of bytes actually output to DSTFIL might be more or less than the number of bytes specified by START and END, depending on what the end-of-line conventions are. In the case where the end-of-line conventions happen to be the same, COPYCHARS simply calls COPYBYTES. (FILEPOS(FILEPOS (Function) NIL NIL ("25") 17) STR FILE START END SKIP TAIL CASEARRAY) [Function] Analogous to STRPOS (see Chapter 4), but searches a file rather than a string. FILEPOS searches FILE for the string STR. Search begins at START (or the current position of the file pointer, if START=NIL), and goes to END (or the end of FILE, if END=NIL). Returns the address of the start of the match, or NIL if not found. SKIP can be used to specify a character which matches any character in the file. If TAIL is T, and the search is successful, the value is the address of the first character after the sequence of characters corresponding to STR, instead of the starting address of the sequence. In either case, the file is left so that the next i/o operation begins at the address returned as the value of FILEPOS. CASEARRAY should be a "case array" that specifies that certain characters should be transformed to other characters before matching. Case arrays are returned by CASEARRAY or SEPRCASE below. CASEARRAY=NIL means no transformation will be performed. A case array is an implementation-dependent object that is logically an array of character codes with one entry for each possible character. FILEPOS maps each character in the file "through" CASEARRAY in the sense that each character code is transformed into the corresponding character code from CASEARRAY before matching. Thus if two characters map into the same value, they are treated as equivalent by FILEPOS. CASEARRAY and SETCASEARRAY provide an implementation-independent interface to case arrays. For example, to search without regard to upper and lower case differences, CASEARRAY would be a case array where all characters map to themselves, except for lower case characters, whose corresponding elements would be the upper case characters. To search for a delimited atom, one could use " ATOM " as the pattern, and specify a case array in which all of the break and separator characters mapped into the same code as space. For applications calling for extensive file searches, the function FFILEPOS is often faster than FILEPOS. (FFILEPOS(FFILEPOS (Function) NIL NIL ("25") 18) PATTERN FILE START END SKIP TAIL CASEARRAY) [Function] Like FILEPOS, except much faster in most applications. FFILEPOS is an implementation of the Boyer-Moore fast string searching algorithm. This algorithm preprocesses the string being searched for and then scans through the file in steps usually equal to the length of the string. Thus, FFILEPOS speeds up roughly in proportion to the length of the string, e.g., a string of length 10 will be found twice as fast as a string of length 5 in the same position. Because of certain fixed overheads, it is generally better to use FILEPOS for short searches or short strings. (CASEARRAY(CASEARRAY (Function) NIL NIL ("25") 18) OLDARRAY) [Function] Creates and returns a new case array, with all elements set to themselves, to indicate the identity mapping. If OLDARRAY is given, it is reused. (SETCASEARRAY(SETCASEARRAY (Function) NIL NIL ("25") 18) CASEARRAY FROMCODE TOCODE) [Function] Modifies the case array CASEARRAY so that character code FROMCODE is mapped to character code TOCODE. (GETCASEARRAY(GETCASEARRAY (Function) NIL NIL ("25") 19) CASEARRAY FROMCODE) [Function] Returns the character code that FROMCODE is mapped to in CASEARRAY. (SEPRCASE(SEPRCASE (Function) NIL NIL ("25") 19) CLFLG) [Function] Returns a new case array suitable for use by FILEPOS or FFILEPOS in which all of the break/separators of FILERDTBL are mapped into character code zero. If CLFLG is non-NIL, then all CLISP characters are mapped into this character as well. This is useful for finding a delimited atom in a file. For example, if PATTERN is " FOO ", and (SEPRCASE T) is used for CASEARRAY, then FILEPOS will find "(FOO_". UPPERCASEARRAY(UPPERCASEARRAY (Variable) NIL NIL ("25") 19) [Variable] Value is a case array in which every lowercase character is mapped into the corresponding uppercase character. Useful for searching text files. Input/Output Operations with Characters and Bytes 1 Interlisp-D supports the 16-bit NS character set (see Chapter 2). All of the standard string and print name functions accept litatoms and strings containing NS characters. In almost all cases, a program does not have to distinguish between NS characters or 8-bit characters. The exception to this rule is the handling of input/output operations. Interlisp-D uses two ways of writing 16-bit NS characters on files. One way is to write the full 16-bits (two bytes) every time a character is output. The other way is to use "run-encoding." Each 16 NS character can be decoded into a character set (an integer from 0 to 254 inclusive) and a character number (also an integer from 0 to 254 inclusive). In run-encoding, the byte 255 (illegal as either a character set number or a character number) is used to signal a change to a given character set, and the following bytes are all assumed to come from the same character set (until the next change-character set sequence). Run-encoding can reduce the number of bytes required to encode a string of NS characters, as long as there are long sequences of characters from the same character set (usually the case). Note that characters are not the same as bytes. A single character can take anywhere from one to four bytes bytes, depending on whether it is in the same character set as the preceeding character, and whether run-encoding is enabled. Programs which assume that characters are equal to bytes must be changed to work with NS characters. The functions BIN and BOUT (see above) should only be used to read and write single eight-bit bytes. The functions READCCODE and PRINTCCODE (see above) should be used to read and write single character codes, interpreting run-encoded NS characters. COPYBYTES should only be used to copy blocks of 8-bit data; COPYCHARS should be used to copy characters. Most I/O functions (READC, PRIN1, etc.) read or write 16-bit NS characters. The use of NS characters has serious consequences for any program that uses file pointers to access a file in a random access manner. At any point when a file is being read or written, it has a "current character set." If the file pointer is changed with SETFILEPTR to a part of the file with a different character set, any characters read or written may have the wrong character set. The current character set can be accessed with the following function: (CHARSET(CHARSET (Function) NIL NIL ("25") 20) STREAM CHARACTERSET) [Function] Returns the current character set of the stream STREAM. If CHARACTERSET is non-NIL, the current character set for STREAM is set. Note that for output streams this may cause bytes to be written to the stream. If CHARACTERSET is T, run encoding for STREAM is disabled: both the character set and the character number (two bytes total) will be written to the stream for each character printed. PRINTOUT 1 Interlisp provides many facilities for controlling the format of printed output. By executing various sequences of PRIN1, PRIN2, TAB, TERPRI, SPACES, PRINTNUM, and PRINTDEF, almost any effect can be achieved. PRINTOUT implements a compact language for specifying complicated sequences of these elementary printing functions. It makes fancy output formats easy to design and simple to program. PRINTOUT is a CLISP word (like FOR and IF) for interpreting a special printing language in which you can describe the kinds of printing desired. The description is translated by DWIMIFY to the appropriate sequence of PRIN1, TAB, etc., before it is evaluated or compiled. PRINTOUT printing descriptions have the following general form: (PRINTOUT STREAM PRINTCOM1 ... PRINTCOMN) STREAM is evaluated to obtain the stream to which the output from this specification is directed. The PRINTOUT commands are strung together, one after the other without punctuation, after STREAM. Some commands occupy a single position in this list, but many commands expect to find arguments following the command name in the list. The commands fall into several logical groups: one set deals with horizontal and vertical spacing, another group provides controls for certain formatting capabilities (font changes and subscripting), while a third set is concerned with various ways of actually printing items. Finally, there is a command that permits escaping to a simple Lisp evaluation in the middle of a PRINTOUT form. The various commands are described below. The following examples give a general flavor of how PRINTOUT is used: Example 1: Suppose you want to print out on the terminal the values of three variables, X, Y, and Z, separated by spaces and followed by a carriage return. This could be done by: (PRIN1 X T) (SPACES 1 T) (PRIN1 Y T) (SPACES 1 T) (PRIN1 Z T) (TERPRI T) or by the more concise PRINTOUT form: (PRINTOUT T X , Y , Z T) Here the first T specifies output to the terminal, the commas cause single spaces to be printed, and the final T specifies a TERPRI. The variable names are not recognized as special PRINTOUT commands, so they are printed using PRIN1 by default. Example 2: Suppose the values of X and Y are to be pretty-printed lined up at position 10, preceded by identifying strings. If the output is to go to the primary output stream, you could write either: (PRIN1 "X =") (PRINTDEF X 10 T) (TERPRI ) (PRIN1 "Y =") (PRINTDEF Y 10 T) (TERPRI) or the equivalent: (PRINTOUT NIL "X =" 10 .PPV X T "Y =" 10 .PPV Y T) Since strings are not recognized as special commands, "X =" is also printed with PRIN1 by default. The positive integer means TAB to position 10, where the .PPV command causes the value of X to be prettyprinted as a variable. By convention, special atoms used as PRINTOUT commands are prefixed with a period. The T causes a carriage return, so the Y information is printed on the next line. Example 3. As a final example, suppose that the value of X is an integer and the value of Y is a floating-point number. X is to be printed right-flushed in a field of width 5 beginning at position 15, and Y is to be printed in a field of width 10 also starting at position 15 with 2 places to the right of the decimal point. Furthermore, suppose that the variable names are to appear in the font class named BOLDFONT and the values in font class SMALLFONT. The program in ordinary Interlisp that would accomplish these effects is too complicated to include here. With PRINTOUT, one could write: (PRINTOUT NIL .FONT BOLDFONT "X =" 15 .FONT SMALLFONT .I5 X T .FONT BOLDFONT "Y =" 15 .FONT SMALLFONT .F10.2 Y T .FONT BOLDFONT) The .FONT commands do whatever is necessary to change the font on a multi-font output device. The .I5 command sets up a FIX format for a call to the function PRINTNUM (see above) to print X in the desired format. The .F10.2 specifies a FLOAT format for PRINTNUM. Horizontal Spacing Commands The horizontal spacing commands provide convenient ways of calling TAB and SPACES. In the following descriptions, N stands for a literal positive integer (not for a variable or expression whose value is an integer). N (N a number) [PRINTOUT Command] Used for absolute spacing. It results in a TAB to position N (literally, a (TAB N)). If the line is currently at position N or beyond, the file will be positioned at position N on the next line. .TAB(TAB (Command) .TAB NIL ("25") 22)(.TAB (Command) NIL NIL ("25") 22) POS [PRINTOUT Command] Specifies TAB to position (the value of) POS. This is one of several commands whose effect could be achieved by simply escaping to Lisp, and executing the corresponding form. It is provided as a separate command so that the PRINTOUT form is more concise and is prettyprinted more compactly. Note that .TAB N and N, where N is an integer, are equivalent. .TAB0(TAB0 (Command) .TAB0 NIL ("25") 22)(.TAB0 (Command) NIL NIL ("25") 22) POS [PRINTOUT Command] Like .TAB except that it can result in zero spaces (i.e. the call to TAB specifies MINSPACES=0). -N (N a number) [PRINTOUT Command] Negative integers indicate relative (as opposed to absolute) spacing. Translates as (SPACES |N|). ,(, (Command) NIL NIL ("25") 22) [PRINTOUT Command] ,,(,, (Command) NIL NIL ("25") 22) [PRINTOUT Command] ,,,(,,, (Command) NIL NIL ("25") 22) [PRINTOUT Command] (1, 2 or 3 commas) Provides a short-hand way of specifying 1, 2 or 3 spaces, i.e., these commands are equivalent to -1, -2, and -3, respectively. .SP(SP (Command) .SP NIL ("25") 22)(.SP (Command) NIL NIL ("25") 22) DISTANCE [PRINTOUT Command] Translates as (SPACES DISTANCE). Note that .SP N and -N, where N is an integer, are equivalent. Vertical Spacing Commands Vertical spacing is obtained by calling TERPRI or printing form-feeds. The relevant commands are: T(T (Command) NIL NIL ("25") 23) [PRINTOUT Command] Translates as (TERPRI), i.e., move to position 0 (the first column) of the next line. To print the letter T, use the string "T". .SKIP(SKIP (Command) .SKIP NIL ("25") 23)(.SKIP (Command) NIL NIL ("25") 23) LINES [PRINTOUT Command] Equivalent to a sequence of LINES (TERPRI)'s. The .SKIP command allows for skipping large constant distances and for computing the distance to be skipped. .PAGE(PAGE (Command) .PAGE NIL ("25") 23)(.PAGE (Command) NIL NIL ("25") 23) [PRINTOUT Command] Puts a form-feed (Control-L) out on the file. Care is taken to make sure that Interlisp's view of the current line position is correctly updated. Special Formatting Controls There are a small number of commands for invoking some of the formatting capabilities of multi-font output devices. The available commands are: .FONT(FONT (Command) .FONT NIL ("25") 23)(.FONT (Command) NIL NIL ("25") 23) FONTSPEC [PRINTOUT Command] Changes printing to the font FONTSPEC, which can be a font descriptor, a "font list" such as '(MODERN 10), an image stream (coerced to its current font), or a windows (coerced to the current font of its display stream). The DSPFONT is changed permanently. See fonts (Chapter 27) for more information. FONTSPEC may also be a positive integer N, which is taken as an abbreviated reference to the font class named FONTN (e.g. 1 => FONT1). .SUP(SUP (Command) .SUP NIL ("25") 23)(.SUP (Command) NIL NIL ("25") 23) [PRINTOUT Command] Specifies superscripting. All subsequent characters are printed above the base of the current line. Note that this is absolute, not relative: a .SUP following a .SUP is a no-op. .SUB(SUB (Command) .SUB NIL ("25") 23)(.SUB (Command) NIL NIL ("25") 23) [PRINTOUT Command] Specifies subscripting. Subsequent printing is below the base of the current line. As with superscripting, the effect is absolute. .BASE(BASE (Command) .BASE NIL ("25") 23)(.BASE (Command) NIL NIL ("25") 23) [PRINTOUT Command] Moves printing back to the base of the current line. Un-does a previous .SUP or .SUB; a no-op, if printing is currently at the base. Printing Specifications The value of any expression in a PRINTOUT form that is not recognized as a command itself or as a command argument is printed using PRIN1 by default. For example, title strings can be printed by simply including the string as a separate PRINTOUT command, and the values of variables and forms can be printed in much the same way. Note that a literal integer, say 51, cannot be printed by including it as a command, since it would be interpreted as a TAB; the desired effect can be obtained by using instead the string specification "51", or the form (QUOTE 51). For those instances when PRIN1 is not appropriate, e.g., PRIN2 is required, or a list structures must be prettyprinted, the following commands are available: .P2(P2 (Command) .P2 NIL ("25") 24)(.P2 (Command) NIL NIL ("25") 24) THING [PRINTOUT Command] Causes THING to be printed using PRIN2; translates as (PRIN2 THING). .PPF(PPF (Command) .PPF NIL ("25") 24)(.PPF (Command) NIL NIL ("25") 24) THING [PRINTOUT Command] Causes THING to be prettyprinted at the current line position via PRINTDEF (see Chapter 26). The call to PRINTDEF specifies that THING is to be printed as if it were part of a function definition. That is, SELECTQ, PROG, etc., receive special treatment. .PPV(PPV (Command) .PPV NIL ("25") 24)(.PPV (Command) NIL NIL ("25") 24) THING [PRINTOUT Command] Prettyprints THING as a variable; no special interpretation is given to SELECTQ, PROG, etc. .PPFTL(PPFTL (Command) .PPFTL NIL ("25") 24)(.PPFTL (Command) NIL NIL ("25") 24) THING [PRINTOUT Command] Like .PPF, but prettyprints THING as a tail, that is, without the initial and final parentheses if it is a list. Useful for prettyprinting sub-lists of a list whose other elements are formatted with other commands. .PPVTL(PPVTL (Command) .PPVTL NIL ("25") 24)(.PPVTL (Command) NIL NIL ("25") 24) THING [PRINTOUT Command] Like .PPV, but prettyprints THING as a tail. Paragraph Format Interlisp's prettyprint routines are designed to display the structure of expressions, but they are not really suitable for formatting unstructured text. If a list is to be printed as a textual paragraph, its internal structure is less important than controlling its left and right margins, and the indentation of its first line. The .PARA and .PARA2 commands allow these parameters to be conveniently specified. .PARA(PARA (Command) .PARA NIL ("25") 24)(.PARA (Command) NIL NIL ("25") 24) LMARG RMARG LIST [PRINTOUT Command] Prints LIST in paragraph format, using PRIN1. Translates as (PRINTPARA LMARG RMARG LIST) (see below). Example: (PRINTOUT T 10 .PARA 5 -5 LST) will print the elements of LST as a paragraph with left margin at 5, right margin at (LINELENGTH)-5, and the first line indented to 10. .PARA2(PARA2 (Command) .PARA2 NIL ("25") 25)(.PARA2 (Command) NIL NIL ("25") 25) LMARG RMARG LIST [PRINTOUT Command] Print as paragraph using PRIN2 instead of PRIN1. Translates as (PRINTPARA LMARG RMARG LIST T). Right-Flushing Two commands are provided for printing simple expressions flushed-right against a specified line position, using the function FLUSHRIGHT (see below). They take into account the current position, the number of characters in the print-name of the expression, and the position the expression is to be flush against, and then print the appropriate number of spaces to achieve the desired effect. Note that this might entail going to a new line before printing. Note also that right-flushing of expressions longer than a line (e.g. a large list) makes little sense, and the appearance of the output is not guaranteed. .FR(FR (Command) .FR NIL ("25") 25)(.FR (Command) NIL NIL ("25") 25) POS EXPR [PRINTOUT Command] Flush-right using PRIN1. The value of POS determines the position that the right end of EXPR will line up at. As with the horizontal spacing commands, a negative position number means |POS| columns from the current position, a positive number specifies the position absolutely. POS=0 specifies the right-margin, i.e. is interpreted as (LINELENGTH). .FR2(FR2 (Command) .FR2 NIL ("25") 25)(.FR2 (Command) NIL NIL ("25") 25) POS EXPR [PRINTOUT Command] Flush-right using PRIN2 instead of PRIN1. Centering Commands for centering simple expressions between the current line position and another specified position are also available. As with right flushing, centering of large expressions is not guaranteed. .CENTER(CENTER (Command) .CENTER NIL ("25") 25)(.CENTER (Command) NIL NIL ("25") 25) POS EXPR [PRINTOUT Command] Centers EXPR between the current line position and the position specified by the value of POS. A positive POS is an absolute position number, a negative POS specifies a position relative to the current position, and 0 indicates the right-margin. Uses PRIN1 for printing. .CENTER2(CENTER2 (Command) .CENTER2 NIL ("25") 25)(.CENTER2 (Command) NIL NIL ("25") 25) POS EXPR [PRINTOUT Command] Centers using PRIN2 instead of PRIN1. Numbering The following commands provide FORTRAN-like formatting capabilities for integer and floating-point numbers. Each command specifies a printing format and a number to be printed. The format specification translates into a format-list for the function PRINTNUM. .I(I (Command) .I NIL ("25") 26)(.I (Command) NIL NIL ("25") 26)FORMAT NUMBER [PRINTOUT Command] Specifies integer printing. Translates as a call to the function PRINTNUM with a FIX format-list constructed from FORMAT. The atomic format is broken apart at internal periods to form the format-list. For example, .I5.8.T yields the format-list (FIX 5 8 T), and the command sequence (PRINTOUT T .I5.8.T FOO) translates as (PRINTNUM '(FIX 5 8 T) FOO). This expression causes the value of FOO to be printed in radix 8 right-flushed in a field of width 5, with 0's used for padding on the left. Internal NIL's in the format specification may be omitted, e.g., the commands .I5..T and .I5.NIL.T are equivalent. The format specification .I1 is often useful for forcing a number to be printed in radix 10 (but not otherwise specially formatted), independent of the current setting of RADIX. .F(F (Command) .F NIL ("25") 26)(.F (Command) NIL NIL ("25") 26) FORMAT NUMBER [PRINTOUT Command] Specifies floating-number printing. Like the .I format command, except translates with a FLOAT format-list. .N(N (Command) .N NIL ("25") 26)(.N (Command) NIL NIL ("25") 26) FORMAT NUMBER [PRINTOUT Command] The .I and .F commands specify calls to PRINTNUM with quoted format specifications. The .N command translates as (PRINTNUM FORMAT NUMBER), i.e., it permits the format to be the value of some expression. Note that, unlike the .I and .F commands, FORMAT is a separate element in the command list, not part of an atom beginning with .N. Escaping to Lisp There are many reasons for taking control away from PRINTOUT in the middle of a long printing expression. Common situations involve temporary changes to system printing parameters (e.g. LINELENGTH), conditional printing (e.g. print FOO only if FIE is T), or lower-level iterative printing within a higher-level print specification. #(# (Command) NIL NIL ("25") 26) FORM [PRINTOUT Command] The escape command. FORM is an arbitrary Lisp expression that is evaluated within the context established by the PRINTOUT form, i.e., FORM can assume that the primary output stream has been set to be the FILE argument to PRINTOUT. Note that nothing is done with the value of FORM; any printing desired is accomplished by FORM itself, and the value is discarded. Note: Although PRINTOUT logically encloses its translation in a RESETFORM (Chapter 14) to change the primary output file to the FILE argument (if non-NIL), in most cases it can actually pass FILE (or a locally bound variable if FILE is a non-trivial expression) to each printing function. Thus, the RESETFORM is only generated when the # command is used, or user-defined commands (below) are used. If many such occur in repeated PRINTOUT forms, it may be more efficient to embed them all in a single RESETFORM which changes the primary output file, and then specify FILE=NIL in the PRINTOUT expressions themselves. User-Defined Commands The collection of commands and options outlined above is aimed at fulfilling all common printing needs. However, certain applications might have other, more specialized printing idioms, so a facility is provided whereby you can define new commands. This is done by adding entries to the global list PRINTOUTMACROS to define how the new commands are to be translated. PRINTOUTMACROS(PRINTOUTMACROS (Variable) NIL NIL ("25") 27) [Variable] PRINTOUTMACROS is an association-list whose elements are of the form (COMM FN). Whenever COMM appears in command position in the sequence of PRINTOUT commands (as opposed to an argument position of another command), FN is applied to the tail of the command-list (including the command). After inspecting as much of the tail as necessary, the function must return a list whose CAR is the translation of the user-defined command and its arguments, and whose CDR is the list of commands still remaining to be translated in the normal way. For example, suppose you want to define a command "?", which will cause its single argument to be printed with PRIN1 only if it is not NIL. This can be done by entering (? ?TRAN) on PRINTOUTMACROS, and defining the function ?TRAN as follows: (DEFINEQ (?TRAN (COMS) (CONS (SUBST (CADR COMS) 'ARG '(PROG ((TEMP ARG)) (COND (TEMP (PRIN1 TEMP))))) (CDDR COMS))] Note that ?TRAN does not do any printing itself; it returns a form which, when evaluated in the proper context, will perform the desired action. This form should direct all printing to the primary output file. Special Printing Functions The paragraph printing commands are translated into calls on the function PRINTPARA, which may also be called directly: (PRINTPARA(PRINTPARA (Function) NIL NIL ("25") 27) LMARG RMARG LIST P2FLAG PARENFLAG FILE) [Function] Prints LIST on FILE in line-filled paragraph format with its first element beginning at the current line position and ending at or before RMARG, and with subsequent lines appearing between LMARG and RMARG. If P2FLAG is non-NIL, prints elements using PRIN2, otherwise PRIN1. If PARENFLAG is non-NIL, then parentheses will be printed around the elements of LIST. If LMARG is zero or positive, it is interpreted as an absolute column position. If it is negative, then the left margin will be at |LMARG|+(POSITION). If LMARG=NIL, the left margin will be at (POSITION), and the paragraph will appear in block format. If RMARG is positive, it also is an absolute column position (which may be greater than the current (LINELENGTH)). Otherwise, it is interpreted as relative to (LINELENGTH), i.e., the right margin will be at (LINELENGTH)+|RMARG|. Example: (TAB 10) (PRINTPARA 5 -5 LST T) will PRIN2 the elements of LST in a paragraph with the first line beginning at column 10, subsequent lines beginning at column 5, and all lines ending at or before (LINELENGTH)-5. The current (LINELENGTH) is unaffected by PRINTPARA, and upon completion, FILE will be positioned immediately after the last character of the last item of LIST. PRINTPARA is a no-op if LIST is not a list. The right-flushing and centering commands translate as calls to the function FLUSHRIGHT: (FLUSHRIGHT(FLUSHRIGHT (Function) NIL NIL ("25") 28) POS X MIN P2FLAG CENTERFLAG FILE) [Function] If CENTERFLAG=NIL, prints X right-flushed against position POS on FILE; otherwise, centers X between the current line position and POS. Makes sure that it spaces over at least MIN spaces before printing by doing a TERPRI if necessary; MIN=NIL is equivalent to MIN=1. A positive POS indicates an absolute position, while a negative POS signifies the position which is |POS| to the right of the current line position. POS=0 is interpreted as (LINELENGTH), the right margin. READFILE and WRITEFILE 1 For those applications where you simply want to simply read all of the expressions on a file, and not evaluate them, the function READFILE is available: (READFILE(READFILE (Function) NIL NIL ("25") 28) FILE RDTBL ENDTOKEN) [NoSpread Function] Reads successive expressions from file using READ (with read table RDTBL) until the single litatom ENDTOKEN is read, or an end of file encountered. Returns a list of these expressions. If RDTBL is not specified, it defaults to FILERDTBL. If ENDTOKEN is not specified, it defaults to the litatom STOP. (WRITEFILE(WRITEFILE (Function) NIL NIL ("25") 28) X FILE) [Function] Writes a date expression onto FILE, followed by successive expressions from X, using FILERDTBL as a read table. If X is atomic, its value is used. If FILE is not open, it is opened. If FILE is a list, (CAR FILE) is used and the file is left opened. Otherwise, when X is finished, the litatom STOP is printed on FILE and it is closed. Returns FILE. (ENDFILE(ENDFILE (Function) NIL NIL ("25") 29) FILE) [Function] Prints STOP on FILE and closes it. Read Tables 1 Many Interlisp input functions treat certain characters in special ways. For example, READ recognizes that the right and left parenthesis characters are used to specify list structures, and that the quote character is used to delimit text strings. The Interlisp input and (to a certain extent) output routines are table driven by read tables. Read tables are objects that specify the syntactic properties of characters for input routines. Since the input routines parse character sequences into objects, the read table in use determines which sequences are recognized as literal atoms, strings, list structures, etc. Most Interlisp input functions take an optional read table argument, which specifies the read table to use when reading an expression. If NIL is given as the read table, the "primary read table" is used. If T is specified, the system terminal read table is used. Some functions will also accept the atom ORIG (not the value of ORIG) as indicating the "original" system read table. Some output functions also take a read table argument. For example, PRIN2 prints an expression so that it would be read in correctly using a given read table. The Interlisp-D system uses the following read tables: T for input/output from terminals, the value of FILERDTBL for input/output from files, the value of EDITRDTBL for input from terminals while in the tty-based editor, the value of DEDITRDTBL for input from terminals while in the display-based editor, and the value of CODERDTBL for input/output from compiled files. These five read tables are initially copies of the ORIG read table, with changes made to some of them to provide read macros that are specific to terminal input or file input. Using the functions described below, you may further change, reset, or copy these tables. However, in the case of FILERDTBL and CODERDTBL, you are cautioned that changing these tables may prevent the system from being able to read files made with the original tables, or prevent users possessing only the standard tables from reading files made using the modified tables. You can also create new read tables, and either explicitly pass them to input/output functions as arguments, or install them as the primary read table, via SETREADTABLE, and then not specify a RDTBL argument, i.e., use NIL. Read Table Functions (READTABLEP(READTABLEP (Function) NIL NIL ("25") 29) RDTBL) [Function] Returns RDTBL if RDTBL is a real read table (not T or ORIG), otherwise NIL. (GETREADTABLE(GETREADTABLE (Function) NIL NIL ("25") 30) RDTBL) [Function] If RDTBL=NIL, returns the primary read table. If RDTBL=T, returns the system terminal read table. If RDTBL is a real read table, returns RDTBL. Otherwise, generates an ILLEGAL READTABLE error. (SETREADTABLE(SETREADTABLE (Function) NIL NIL ("25") 30) RDTBL FLG) [Function] Sets the primary read table to RDTBL. If FLG=T, SETREADTABLE sets the system terminal read table, T. Note that you can reset the other system read tables with SETQ, e.g., (SETQ FILERDTBL (GETREADTABLE)). Generates an ILLEGAL READTABLE error if RDTBL is not NIL, T, or a real read table. Returns the previous setting of the primary read table, so SETREADTABLE is suitable for use with RESETFORM (Chapter 14). (COPYREADTABLE(COPYREADTABLE (Function) NIL NIL ("25") 30) RDTBL) [Function] Returns a copy of RDTBL. RDTBL can be a real read table, NIL, T, or ORIG (in which case COPYREADTABLE returns a copy of the original system read table), otherwise COPYREADTABLE generates an ILLEGAL READTABLE error. Note that COPYREADTABLE is the only function that creates a read table. (RESETREADTABLE(RESETREADTABLE (Function) NIL NIL ("25") 30) RDTBL FROM) [Function] Copies (smashes) FROM into RDTBL. FROM and RDTBL can be NIL, T, or a real read table. In addition, FROM can be ORIG, meaning use the system's original read table. Syntax Classes A read table is an object that contains information about the "syntax class" of each character. There are nine basic syntax classes: LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, STRINGDELIM, ESCAPE, BREAKCHAR, SEPRCHAR, and OTHER, each associated with a primitive syntactic property. In addition, there is an unlimited assortment of user-defined syntax classes, known as "read macros". The basic syntax classes are interpreted as follows: LEFTPAREN (normally left parenthesis) Begins list structure. RIGHTPAREN (normally right parenthesis) Ends list structure. LEFTBRACKET (normally left bracket) Begins list structure. Also matches RIGHTBRACKET characters. RIGHTBRACKET (normally left bracket) Ends list structure. Can close an arbitrary numbers of LEFTPAREN lists, back to the last LEFTBRACKET. STRINGDELIM (normally double quote) Begins and ends text strings. Within the string, all characters except for the one(s) with class ESCAPE are treated as ordinary, i.e., interpreted as if they were of syntax class OTHER. To include the string delimiter inside a string, prefix it with the ESCAPE character. ESCAPE (normally percent sign) Inhibits any special interpretation of the next character, i.e., the next character is interpreted to be of class OTHER, independent of its normal syntax class. BREAKCHAR (None initially) Is a break character, i.e., delimits atoms, but is otherwise an ordinary character. SEPRCHAR (space, carriage return, etc.) Delimits atoms, and is otherwise ignored. OTHER Characters that are not otherwise special belong to the class OTHER. Characters of syntax class LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, and STRINGDELIM are all break characters. That is, in addition to their interpretation as delimiting list or string structures, they also terminate the reading of an atom. Characters of class BREAKCHAR serve only to terminate atoms, with no other special meaning. In addition, if a break character is the first non-separator encountered by RATOM, it is read as a one-character atom. In order for a break character to be included in an atom, it must be preceded by the ESCAPE character. Characters of class SEPRCHAR also terminate atoms, but are otherwise completely ignored; they can be thought of as logically spaces. As with break characters, they must be preceded by the ESCAPE character in order to appear in an atom. For example, if $ were a break character and * a separator character, the input stream ABC**DEF$GH*$$ would be read by six calls to RATOM returning respectively ABC, DEF, $, GH, $, $. Although normally there is only one character in a read table having each of the list- and string-delimiting syntax classes (such as LEFTPAREN), it is perfectly acceptable for any character to have any syntax class, and for more than one to have the same class. Note that a "syntax class" is an abstraction: there is no object referencing a collection of characters called a syntax class. Instead, a read table provides the association between a character and its syntax class, and the input/output routines enforce the abstraction by using read tables to drive the parsing. The functions below are used to obtain and set the syntax class of a character in a read table. CH can either be a character code (a integer), or a character (a single-character atom). Single-digit integers are interpreted as character codes, rather than as characters. For example, 1 indicates Control-A, and 49 indicates the character 1. Note that CH can be a full sixteen-bit NS character (see Chapter 2). Note: Terminal tables, described in Chapter 30, also associate characters with syntax classes, and they can also be manipulated with the functions below. The set of read table and terminal table syntax classes are disjoint, so there is never any ambiguity about which type of table is being referred to. (GETSYNTAX CH TABLE) [Function] Returns the syntax class of CH, a character or a character code, with respect to TABLE. TABLE can be NIL, T, ORIG, or a real read table or terminal table. CH can also be a syntax class, in which case GETSYNTAX returns a list of the character codes in TABLE that have that syntax class. (SETSYNTAX CHAR CLASS TABLE) [Function] Sets the syntax class of CHAR, a character or character code, in TABLE. TABLE can be either NIL, T, or a real read table or terminal table. SETSYNTAX returns the previous syntax class of CHAR. CLASS can be any one of the following: f The name of one of the basic syntax classes. f A list, which is interpreted as a read macro (see below). f NIL, T, ORIG, or a real read table or terminal table, which means to give CHAR the syntax class it has in the table indicated by CLASS. For example, (SETSYNTAX '%( 'ORIG TABLE) gives the left parenthesis character in TABLE the same syntax class that it has in the original system read table. f A character code or character, which means to give CHAR the same syntax class as the character CHAR in TABLE. For example, (SETSYNTAX '{ '%[ TABLE) gives the left brace character the same syntax class as the left bracket. (SYNTAXP(SYNTAXP (Function) NIL NIL ("25") 32) CODE CLASS TABLE) [Function] CODE is a character code; TABLE is NIL, T, or a real read table or terminal table. Returns T if CODE has the syntax class CLASS in TABLE; NIL otherwise. CLASS can also be a read macro type (MACRO, SPLICE, INFIX), or a read macro option (FIRST, IMMEDIATE, etc.), in which case SYNTAXP returns T if the syntax class is a read macro with the specified property. SYNTAXP will not accept a character as an argument, only a character code. For convenience in use with SYNTAXP, the atom BREAK may be used to refer to all break characters, i.e., it is the union of LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, STRINGDELIM, and BREAKCHAR. For purely symmetrical reasons, the atom SEPR corresponds to all separator characters. However, since the only separator characters are those that also appear in SEPRCHAR, SEPR and SEPRCHAR are equivalent. Note that GETSYNTAX never returns BREAK or SEPR as a value although SETSYNTAX and SYNTAXP accept them as arguments. Instead, GETSYNTAX returns one of the disjoint basic syntax classes that comprise BREAK. BREAK as an argument to SETSYNTAX is interpreted to mean BREAKCHAR if the character is not already of one of the BREAK classes. Thus, if %( is of class LEFTPAREN, then (SETSYNTAX '%( 'BREAK) doesn't do anything, since %( is already a break character, but (SETSYNTAX '%( 'BREAKCHAR) means make %( be just a break character, and therefore disables the LEFTPAREN function of %(. Similarly, if one of the format characters is disabled completely, e.g., by (SETSYNTAX '%( 'OTHER), then (SETSYNTAX '%( 'BREAK) would make %( be only a break character; it would not restore %( as LEFTPAREN. The following functions provide a way of collectively accessing and setting the separator and break characters in a read table: (GETSEPR(GETSEPR (Function) NIL NIL ("25") 33) RDTBL) [Function] Returns a list of separator character codes in RDTBL. Equivalent to (GETSYNTAX 'SEPR RDTBL). (GETBRK(GETBRK (Function) NIL NIL ("25") 33) RDTBL) [Function] Returns a list of break character codes in RDTBL. Equivalent to (GETSYNTAX 'BREAK RDTBL). (SETSEPR(SETSEPR (Function) NIL NIL ("25") 33) LST FLG RDTBL) [Function] Sets or removes the separator characters for RDTBL. LST is a list of charactors or character codes. FLG determines the action of SETSEPR as follows: If FLG=NIL, makes RDTBL have exactly the elements of LST as separators, discarding from RDTBL any old separator characters not in LST. If FLG=0, removes from RDTBL as separator characters all elements of LST. This provides an "UNSETSEPR". If FLG=1, makes each of the characters in LST be a separator in RDTBL. If LST=T, the separator characters are reset to be those in the system's read table for terminals, regardless of the value of FLG, i.e., (SETSEPR T) is equivalent to (SETSEPR (GETSEPR T)). If RDTBL is T, then the characters are reset to those in the original system table. Returns NIL. (SETBRK(SETBRK (Function) NIL NIL ("25") 33) LST FLG RDTBL) [Function] Sets the break characters for RDTBL. Similar to SETSEPR. As with SETSYNTAX to the BREAK class, if any of the list- or string-delimiting break characters are disabled by an appropriate SETBRK (or by making it be a separator character), its special action for READ will not be restored by simply making it be a break character again with SETBRK. However, making these characters be break characters when they already are will have no effect. The action of the ESCAPE character (normally %) is not affected by SETSEPR or SETBRK. It can be disabled by setting its syntax to the class OTHER, and other characters can be used for escape on input by assigning them the class ESCAPE. As of this writing, however, there is no way to change the output escape character; it is "hardwired" as %. That is, on output, characters of special syntax that need to be preceded by the ESCAPE character will always be preceded by %, independent of the syntax of % or which, if any characters, have syntax ESCAPE. The following function can be used for defeating the action of the ESCAPE character or characters: (ESCAPE(ESCAPE (Function) NIL NIL ("25") 34) FLG RDTBL) [Function] If FLG=NIL, makes characters of class ESCAPE behave like characters of class OTHER on input. Normal setting is (ESCAPE T). ESCAPE returns the previous setting. Read Macros This is a description of the OLD-INTERLISP-T read macros. Read macros are user-defined syntax classes that can cause complex operations when certain characters are read. Read macro characters are defined by specifying as a syntax class an expression of the form: (TYPE OPTION1 ... OPTIONN FN) where TYPE is one of MACRO, SPLICE, or INFIX, and FN is the name of a function or a lambda expression. Whenever READ encounters a read macro character, it calls the associated function, giving it as arguments the input stream and read table being used for that call to READ. The interpretation of the value returned depends on the type of read macro: MACRO This is the simplest type of read macro. The result returned from the macro is treated as the expression to be read, instead of the read macro character. Often the macro reads more input itself. For example, in order to cause ~EXPR to be read as (NOT EXPR), one could define ~ as the read macro: [MACRO (LAMBDA (FL RDTBL) (LIST 'NOT (READ FL RDTBL] SPLICE The result (which should be a list or NIL) is spliced into the input using NCONC. For example, if $ is defined by the read macro: (SPLICE (LAMBDA NIL (APPEND FOO))) and the value of FOO is (A B C), then when you input (X $ Y), the result will be (X A B C Y). INFIX The associated function is called with a third argument, which is a list, in TCONC format (Chapter 3), of what has been read at the current level of list nesting. The function's value is taken as a new TCONC list which replaces the old one. For example, the infix operator + could be defined by the read macro: (INFIX (LAMBDA (FL RDTBL Z) (RPLACA (CDR Z) (LIST (QUOTE IPLUS) (CADR Z) (READ FL RDTBL))) Z)) If an INFIX read macro character is encountered not in a list, the third argument to its associated function is NIL. If the function returns NIL, the read macro character is essentially ignored and reading continues. Otherwise, if the function returns a TCONC list of one element, that element is the value of the READ. If it returns a TCONC list of more than one element, the list is the value of the READ. The specification for a read macro character can be augmented to specify various options OPTION1 ... OPTIONN, e.g., (MACRO FIRST IMMEDIATE FN). The following three disjoint options specify when the read macro character is to be effective: ALWAYS The default. The read macro character is always effective (except when preceded by the % character), and is a break character, i.e., a member of (GETSYNTAX 'BREAK RDTBL). FIRST The character is interpreted as a read macro character only when it is the first character seen after a break or separator character; in all other situations, the character is treated as having class OTHER. The read macro character is not a break character. For example, the quote character is a FIRST read macro character, so that DON'T is read as the single atom DON'T, rather than as DON followed by (QUOTE T). ALONE The read macro character is not a break character, and is interpreted as a read macro character only when the character would have been read as a separate atom if it were not a read macro character, i.e., when its immediate neighbors are both break or separator characters. Making a FIRST or ALONE read macro character be a break character (with SETBRK) disables the read macro interpretation, i.e., converts it to syntax class BREAKCHAR. Making an ALWAYS read macro character be a break character is a no-op. The following two disjoint options control whether the read macro character is to be protected by the ESCAPE character on output when a litatom containing the character is printed: ESCQUOTE or ESC The default. When printed with PRIN2, the read macro character will be preceded by the output escape character (%) as needed to permit the atom containing it to be read correctly. Note that for FIRST macros, this means that the character need be quoted only when it is the first character of the atom. NOESCQUOTE or NOESC The read macro character will always be printed without an escape. For example, the ? read macro in the T read table is a NOESCQUOTE character. Unless you are very careful what you are doing, read macro characters in FILERDTBL should never be NOESCQUOTE, since symbols that happen to contain the read macro character will not read back in correctly. The following two disjoint options control when the macro's function is actually executed: IMMEDIATE or IMMED The read macro character is immediately activated, i.e., the current line is terminated, as if an EOL had been typed, a carriage-return line-feed is printed, and the entire line (including the macro character) is passed to the input function. IMMEDIATE read macro characters enable you to specify a character that will take effect immediately, as soon as it is encountered in the input, rather than waiting for the line to be terminated. Note that this is not necessarily as soon as the character is typed. Characters that cause action as soon as they are typed are interrupt characters (see Chapter 30). Note that since an IMMEDIATE macro causes any input before it to be sent to the reader, characters typed before an IMMEDIATE read macro character cannot be erased by Control-A or Control-Q once the IMMEDIATE character has been typed, since they have already passed through the line buffer. However, an INFIX read macro can still alter some of what has been typed earlier, via its third argument. NONIMMEDIATE or NONIMMED The default. The read macro character is a normal character with respect to the line buffering, and so will not be activated until a carriage-return or matching right parenthesis or bracket is seen. Making a read macro character be both ALONE and IMMEDIATE is a contradiction, since ALONE requires that the next character be input in order to see if it is a break or separator character. Thus, ALONE read macros are always NONIMMEDIATE, regardless of whether or not IMMEDIATE is specified. Read macro characters can be "nested". For example, if = is defined by (MACRO (LAMBDA (FL RDTBL) (EVAL (READ FL RDTBL)))) and ! is defined by (SPLICE (LAMBDA (FL RDTBL) (READ FL RDTBL))) then if the value of FOO is (A B C), and (X =FOO Y) is input, (X (A B C) Y) will be returned. If (X !=FOO Y) is input, (X A B C Y) will be returned. Note: If a read macro's function calls READ, and the READ returns NIL, the function cannot distinguish the case where a RIGHTPAREN or RIGHTBRACKET followed the read macro character, (e.g. "(A B ')"), from the case where the atom NIL (or "()") actually appeared. In Interlisp-D, a READ inside of a read macro when the next input character is a RIGHTPAREN or RIGHTBRACKET reads the character and returns NIL, just as if the READ had not occurred inside a read macro. If a call to READ from within a read macro encounters an unmatched RIGHTBRACKET within a list, the bracket is simply put back into the buffer to be read (again) at the higher level. Thus, inputting an expression such as (A B '(C D] works correctly. (INREADMACROP(INREADMACROP (Function) NIL NIL ("25") 37)) [Function] Returns NIL if currently not under a read macro function, otherwise the number of unmatched left parentheses or brackets. (READMACROS(READMACROS (Function) NIL NIL ("25") 37) FLG RDTBL) [Function] If FLG=NIL, turns off action of read macros in read table RDTBL. If FLG=T, turns them on. Returns previous setting. The following read macros are standardly defined in Interlisp in the T and EDITRDTBL read tables: ' (single-quote) Returns the next expression, wrapped in a call to QUOTE; e.g., 'FOO reads as (QUOTE FOO). The macro is defined as a FIRST read macro, so that the quote character has no effect in the middle of a symbol. The macro is also ignored if the quote character is immediately followed by a separator character. Control-Y Defined in T and EDITRDTBL. Returns the result of evaluating the next expression. For example, if the value of FOO is (A B), then (LIST 1 control-YFOO 2) is read as (LIST 1 (A B) 2). Note that no structure is copied; the third element of that input expression is still EQ to the value of FOO. Control-Y can thus be used to read structures that ordinarily have no read syntax. For example, the value returned from reading (KEY1 Control-Y(ARRAY 10)) has an array as its second element. Control-Y can be thought of as an "un-quote" character. The choice of character to perform this function is changeable with SETTERMCHARS (see Chapter 16). ` (backquote) Backquote makes it easier to write programs to construct complex data structures. Backquote is like quote, except that within the backquoted expression, forms can be evaluated. The general idea is that the backquoted expression is a "template" containing some constant parts (as with a quoted form) and some parts to be filled in by evaluating something. Unlike with control-Y, however, the evaluation occurs not at the time the form is read, but at the time the backquoted expression is evaluated. That is, the backquote macro returns an expression which, when evaluated, produces the desired structure. Within the backquoted expression, the character "," (comma) introduces a form to be evaluated. The value of a form preceded by ",@" is to be spliced in, using APPEND. If it is permissible to destroy the list being spliced in (i.e., NCONC may be used in the translation), then ",." can be used instead of ",@". For example, if the value of FOO is (1 2 3 4), then the form `(A ,(CAR FOO) ,@(CDDR FOO) D E) evaluates to (A 1 3 4 D E); it is logically equivalent to writing (CONS 'A (CONS (CAR FOO) (APPEND (CDDR FOO) '(D E)))) . Backquote is particularly useful for writing macros. For example, the body of a macro that refers to X as the macro's argument list might be `(COND ((FIXP ,(CAR X)) ,(CADR X)) (T .,(CDDR X))) which is equivalent to writing (LIST 'COND (LIST (LIST 'FIXP (CAR X)) (CADR X)) (CONS 'T (CDDR X))) Note that comma does not have any special meaning outside of a backquote context. For users without a backquote character on their keyboards, backquote can also be written as |' (vertical-bar, quote). ? Implements the ?= command for on-line help regarding the function currently being "called" in the typein (see Chapter 26). | (vertical bar) When followed by an end of line, tab or space, | is ignored, i.e., treated as a separator character, enabling the editor's CHANGECHAR feature (see Chapter 26). Otherwise it is a "dispatching" read macro whose meaning depends on the character(s) following it. The following are currently defined: ' (quote) -- A synonym for backquote. . (period) -- Returns the evaluation of the next expression, i.e., this is a synonym for Control-Y. , (comma) -- Returns the evaluation of the next expression at load time, i.e., the following expression is quoted in such a manner that the compiler treats it as a literal whose value is not determined until the compiled expression is loaded. O or o (the letter O) -- Treats the next number as octal, i.e., reads it in radix 8. For example, |o12 = 10 (decimal). B or b -- Treats the next number as binary, i.e., reads it in radix 2. For example, |b101 = 5 (decimal). X or x -- Treats the next number as hexadecimal, i.e., reads it in radix 16. The uppercase letters A though F are used as the digits after 9. For example, |x1A = 26 (decimal). R or r -- Reads the next number in the radix specified by the (decimal) number that appears between the | and the R. When inputting a number in a radix above ten, the upper-case letters A through Z can be used as the digits after 9 (but there is no digit above Z, so it is not possible to type all base-99 digits). For example, |3r120 reads 120 in radix 3, returning 15. (, {, ^ -- Used internally by HPRINT and HREAD (see above) to print and read unusual expressions. The dispatching characters that are letters can appear in either upper- or lowercase. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "24-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "24-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "24-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "24-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "24-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "24-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))25xlx2lxx2Hll5xlx3(T/HHF PAGEHEADING RIGHTPAGE@ PAGEHEADINGLEFTBACKT-T,3T206 +306T306 +T306 +T2/52HHl2l/2(,2l,,HH/5,2lll2HHl2l22l2l5,,52l +2Hll3(T2Hl +l,HH,,3(T,,, TITAN PALATINO PALATINO PALATINO TITAN TITAN TITAN MODERN +MODERN +   IM.CHAP.GETFN + HRULE.GETFNMODERN + 0*-IM.INDEX.GETFN  HRULE.GETFNMODERN + -IO + 81Ut J +J 'IM.INDEX.GETFNTITAN  ,%$UJ %IM.INDEX.GETFNTITAN  , /  HRULE.GETFNMODERN + .,  - - 6 S-a"-"$#IM.INDEX.GETFNTITAN  ,E,A,"IM.INDEX.GETFNTITAN  ,Ki   ,tR ,)#a ),6  ,,2V6 ,%W,S/%IM.INDEX.GETFNTITAN  ,.EL$IM.INDEX.GETFNTITAN ,;$IM.INDEX.GETFNTITAN  , S ,  , ) %IM.INDEX.GETFN ,0N% ? +%IM.INDEX.GETFNTITAN  ,8#IM.INDEX.GETFNTITAN  ,%E2 'IM.INDEX.GETFNTITAN  ,B'IM.INDEX.GETFNTITAN  ,/B!IM.INDEX.GETFNMODERN + ,X+ % j.c#IM.INDEX.GETFNTITAN  ,- +,% /,:   31"IM.INDEX.GETFNTITAN  ,N > , +    ,IM.INDEX.GETFNMODERN + ,$! *z,' B%  $IM.INDEX.GETFNMODERN +  ,c =; 8u, =- , >)U 'IM.INDEX.GETFNMODERN + ,> Tm  *6IM.INDEX.GETFN  HRULE.GETFNMODERN + .RXP),--E"-7J&$IM.INDEX.GETFNTITAN  ,G,B"#IM.INDEX.GETFNTITAN  ,#IM.INDEX.GETFNTITAN  ,>.S1"\D* X<#IM.INDEX.GETFNTITAN  +  #IM.INDEX.GETFNTITAN  ,#IM.INDEX.GETFNTITAN  ,& +#IM.INDEX.GETFNTITAN  ,)L!, +* +"IM.INDEX.GETFNTITAN  , +3.( +& +j $IM.INDEX.GETFNTITAN  ,&IM.INDEX.GETFN ,* 'IM.INDEX.GETFNTITAN  ,Q !IM.INDEX.GETFNTITAN   ,< /,  + ++@  'IM.INDEX.GETFNTITAN  ,  'IM.INDEX.GETFNTITAN  , /. + I  +Y  ;(IM.INDEX.GETFNMODERN + , )IM.INDEX.GETFNMODERN +  ,#, :&IM.INDEX.GETFNMODERN + ,,v   !  +(IM.INDEX.GETFNPALATINO  ,62)!T,Et +IM.INDEX.GETFNPALATINO  ,/ u .| +X. +)IM.INDEX.GETFNMODERN + ,  + + , +  $  "  4(8 +7 , +, f| f7, +'  '+'%'''#'''  ,IM.INDEX.GETFN , + +s   +".)IM.INDEX.GETFN ,  0" 'IM.INDEX.GETFNMODERN + , 'IM.INDEX.GETFNMODERN + ,  7IM.INDEX.GETFN .%8*QG$IM.INDEX.GETFNMODERN + ,>L, +!    +, ~ %IM.INDEX.GETFNTITAN  ,; ,J, 4.+%IM.INDEX.GETFNMODERN +  , +",  !& %$& +% , +   K,;,25.@J ,; ,  +@# *  +z S ,5N$    $ $$ $, ++/K K4~,V+,5S$   $ $$$ $$-IM.INDEX.GETFNMODERN + ,"40)6^  . +8'IM.INDEX.GETFNMODERN + ,0.4Q, =, 5, )H   .^-& +*%IM.INDEX.GETFNMODERN + +  , ++ +:, W$," + b , $IM.INDEX.GETFNMODERN + , 'IM.INDEX.GETFNMODERN + ,? , , ,&7R;/  HRULE.GETFNMODERN + - K9- +A i)0:- +` +)IM.INDEX.GETFNTITAN  ,5Q +)IM.INDEX.GETFNTITAN  , +8 +z P e +7 (IM.INDEX.GETFNTITAN  ,n  : *IM.INDEX.GETFNPALATINO ,W(  (IM.INDEX.GETFNMODERN + ,&,W 9, + I W (IM.INDEX.GETFNMODERN + , {%" 2*={  &IM.INDEX.GETFNMODERN +  , = +26,QP-,    ,,+ a e  C,K  !C'IM.INDEX.GETFNMODERN +  ,,,B& (IM.INDEX.GETFNMODERN + ,q +IM.INDEX.GETFNMODERN +  ,  +IM.INDEX.GETFNMODERN +  ,  'IM.INDEX.GETFNMODERN + ,-) *     -IM.INDEX.GETFNTITAN  ,/2  HRULE.GETFNMODERN + .]-0-/"-Z  +o 3 9,- +&IM.INDEX.GETFNMODERN +  ,0  Y,  *   HRULE.GETFNMODERN + .t&- -8 +-aNg +- PQ      --_ 4% -  + - -6)J+"*- 1 T s-Z#   .C"(: + ,, )4"IM.INDEX.GETFNMODERN +"IM.INDEX.GETFN , +F $IM.INDEX.GETFNMODERN +#IM.INDEX.GETFN ,<    ,UIM.INDEX.GETFN   IM.INDEX.GETFN   !IM.INDEX.GETFN ,t IM.INDEX.GETFNTITAN !IM.INDEX.GETFNPALATINO ,  .(5IM.INDEX.GETFNPALATINO ,U$IM.INDEX.GETFNTITAN #IM.INDEX.GETFNPALATINO , d$IM.INDEX.GETFNTITAN #IM.INDEX.GETFNPALATINO ,.$IM.INDEX.GETFNTITAN #IM.INDEX.GETFN,8 xF, E "IM.INDEX.GETFNTITAN "IM.INDEX.GETFNPALATINO ,  "IM.INDEX.GETFNTITAN "IM.INDEX.GETFNPALATINO ,$IM.INDEX.GETFNPALATINO #IM.INDEX.GETFN,I1 .![ea +-a IM.INDEX.GETFNTITAN !IM.INDEX.GETFNPALATINO ,"IM.INDEX.GETFNTITAN "IM.INDEX.GETFNPALATINO ,6 I#"IM.INDEX.GETFNTITAN "IM.INDEX.GETFNPALATINO , 6&IM.INDEX.GETFNTITAN $IM.INDEX.GETFNPALATINO ,&IM.INDEX.GETFNPALATINO $IM.INDEX.GETFN, .PA$IM.INDEX.GETFNTITAN #IM.INDEX.GETFNPALATINO , +, +7 (&IM.INDEX.GETFNTITAN $IM.INDEX.GETFNPALATINO ,  +.~ + IM.INDEX.GETFNTITAN !IM.INDEX.GETFNPALATINO ,/^[6 "IM.INDEX.GETFNPALATINO "IM.INDEX.GETFN,  +.(IM.INDEX.GETFNTITAN %IM.INDEX.GETFNPALATINO ,N,`*IM.INDEX.GETFNPALATINO &IM.INDEX.GETFN,   .IM.INDEX.GETFNTITAN IM.INDEX.GETFN,B` 'pB ,IM.INDEX.GETFNTITAN IM.INDEX.GETFN,.* IM.INDEX.GETFNTITAN IM.INDEX.GETFN ,) Y O .4 +$ PIM.INDEX.GETFNMODERN + ,Y B &*%+) 7&!D ]? 9.-8-IM.INDEX.GETFNTITAN ,6 0CE,YMM,3;    +, +.J % (IM.INDEX.GETFNTITAN  ,w.  :,} + +1,\ 0 % ,   M .M + +)IM.INDEX.GETFNTITAN  +, +  '+#2".    HRULE.GETFNMODERN + .'IM.INDEX.GETFNMODERN + ,-O," . (IM.INDEX.GETFNMODERN + ,* #  7&IM.INDEX.GETFNMODERN + ,*   HRULE.GETFNMODERN + .W-CaxV-8/ + F +N [  -   +)IM.INDEX.GETFNTITAN   +IM.INDEX.GETFNTITAN &. +IM.INDEX.GETFNTITAN  &=  +T   ,IM.INDEX.GETFNTITAN    + -IM.INDEX.GETFNTITAN &/  .  +     5 +4 ?   R   |LG * gK?  +     | *) zr & b 92 ,3*,+ * $+ &"0=>3)F7(K&IM.INDEX.GETFNPALATINO 3    B5 ,  +    ,v +  % @  /  # /  O   &IM.INDEX.GETFNPALATINO /%IM.INDEX.GETFNPALATINO + +&IM.INDEX.GETFNPALATINO -. %) #v G%IM.INDEX.GETFNPALATINO  aDAJ9SlT&*C%IM.INDEX.GETFNPALATINO ! +    # =O21$'"% NyC%211 11$1 %=o7=YbY98;   1L  7hI!LRg +V +V  +a] c e W J `Y ( k  8      ( + 3 + +!); + !' 2    +IM.INDEX.GETFNTITAN  ]   +)IM.INDEX.GETFNMODERN +  0,E A   W  Y   n3OD( # ( %h'211122 1 11:`  j +%c: ^ P ^ lH !D%5Xz \ No newline at end of file diff --git a/docs/turpin-irm/IRM-3/24-IO/TEDIT b/docs/turpin-irm/IRM-3/24-IO/TEDIT new file mode 100644 index 00000000..20b45b69 --- /dev/null +++ b/docs/turpin-irm/IRM-3/24-IO/TEDIT @@ -0,0 +1,182 @@ + INTERLISP-D REFERENCE MANUAL I/O FUNCTIONS "25"25. INPUT/OUTPUT FUNCTIONS 2 This chapter describes the standard I/O functions used for reading and printing characters and Interlisp expressions on files and other streams. First, the primitive input functions are presented, then the output functions, then functions for random-access operations (such as searching a file for a given stream, or changing the "next-character" pointer to a position in a file). Next, the PRINTOUT statement is documented (see below), which provides an easy way to write complex output operations. Finally, read tables, used to parse characters as Interlisp expressions, are documented. Specifying Streams for Input/Output Functions(INPUT/OUTPUT% FUNCTIONS NIL Input/Output% Functions NIL NIL 1 SUBNAME SPECIFYING% STREAMS% FOR SUBTEXT specifying% streams% for) 1 Most of the input/output functions in Interlisp-D have an argument named STREAM or FILE, specifying on which open stream the function's action should occur (the name FILE is used in older functions that predate the concept of stream; the two should, however, be treated synonymously). The value of this argument should be one of the following: a stream An object of type STREAM, as returned by OPENSTREAM (Chapter 24) or other stream-producing functions, is always the most precise and efficient way to designate a stream argument. T The litatom T designates the terminal input or output stream of the currently running process, controlling input from the keyboard and output to the display screen. For functions where the direction (input or output) is ambiguous, T is taken to designate the terminal output stream. The T streams are always open; they cannot be closed. The terminal output stream can be set to a given window or display stream by using TTYDISPLAYSTREAM (Chapter 28). The terminal input stream cannot be changed. For more information on terminal I/O, see Chapter 30. NIL The litatom NIL designates the "primary" input or output stream. These streams are initially the same as the terminal input/output streams, but they can be changed by using the functions INPUT and OUTPUT. For functions where the direction (input or output) is ambiguous, e.g., GETFILEPTR, the argument NIL is taken to mean the primary input stream, if that stream is not identical to the terminal input stream, else the primary output stream. a window Uses the display stream of the window . Valid for output only. a file name As of this writing, the name of an open file (as a litatom) can be used as a stream argument. However, there are inefficiencies and possible future incompatibilities associated with doing so. See Chapter 24 for details. (GETSTREAM(GETSTREAM (Function) NIL NIL NIL 1) FILE ACCESS) [Function] Coerces the argument FILE to a stream by the above rules. If ACCESS is INPUT, OUTPUT, or BOTH, produces the stream designated by FILE that is open for ACCESS. If ACCESS=NIL, returns a stream for FILE open for any kind of input/output (see the list above for the ambiguous cases). If FILE does not designate a stream open in the specified mode, causes an error, FILE NOT OPEN. (STREAMP(STREAMP (Function) NIL NIL NIL 2) X) [Function] Returns X if X is a STREAM, otherwise NIL. Input Functions 1 While the functions described below can take input from any stream, some special actions occur when the input is from the terminal (the T input stream, see above). When reading from the terminal, the input is buffered a line at a time, unless buffering has been inhibited by CONTROL (Chapter 30) or the input is being read by READC or PEEKC. Using specified editing characters, the user can erase a character at a time, a word at a time, or the whole line. The keys that perform these editing functions are assignable via SETSYNTAX, with the initial settings chosen to be those most natural for the given operating system. In Interlisp-D, the initial settings are as follows: characters are deleted one at a time by Backspace; words are erased by control-W; the whole line is erased by Control-Q. On the Interlisp-D display, deleting a character or a line causes the characters to be physically erased from the screen. In Interlisp-10, the deleting action can be modified for various types of display terminals by using DELETECONTROL (Chapter 30). Unless otherwise indicated, when the end of file is encountered while reading from a file, all input functions generate an error, END OF FILE. Note that this does not close the input file. The ENDOFSTREAMOP stream attribute (Chapter 24) is useful for changing the behavior at end of file. Most input functions have a RDTBL argument, which specifies the read table to be used for input. Unless otherwise specified, if RDTBL is NIL, the primary read table is used. If the FILE or STREAM argument to an input function is NIL, the primary input stream is used. (INPUT(INPUT (Function) NIL NIL NIL 2) FILE) [Function] Sets FILE as the primary input stream; returns the old primary input stream. FILE must be open for input. (INPUT) returns the current primary input stream, which is not changed. Note: If the primary input stream is set to a file, the file's full name, rather than the stream itself, is returned. See discussion in Chapter 24. (READ(READ (Function) NIL NIL NIL 2) FILE RDTBL FLG) [Function] Reads one expression from FILE. Atoms are delimited by the break and separator characters as defined in RDTBL. To include a break or separator character in an atom, the character must be preceded by the character %, e.g., AB%(C is the atom AB(C, %% is the atom %, %control-K is the atom Control-K. For input from the terminal, an atom containing an interrupt character can be input by typing instead the corresponding alphabetic character preceded by Control-V, e.g., ^VD for Control-D. Strings are delimited by double quotes. To input a string containing a double quote or a %, precede it by %, e.g., "AB%"C" is the string AB"C. Note that % can always be typed even if next character is not "special", e.g., %A%B%C is read as ABC. If an atom is interpretable as a number, READ creates a number, e.g., 1E3 reads as a floating point number, 1D3 as a literal atom, 1.0 as a number, 1,0 as a literal atom, etc. An integer can be input in a non-decimal radix by using syntax such as 123Q, |b10101, |5r1234 (see Chapter 7). The function RADIX, sets the radix used to print integers. When reading from the terminal, all input is line-buffered to enable the action of the backspacing control characters, unless inhibited by CONTROL (Chapter 30). Thus no characters are actually seen by the program until a carriage-return (actually the character with terminal syntax class EOL, see Chapter 30), is typed. However, for reading by READ, when a matching right parenthesis is encountered, the effect is the same as though a carriage-return were typed, i.e., the characters are transmitted. To indicate this, Interlisp also prints a carriage-return line-feed on the terminal. The line buffer is also transmitted to READ whenever an IMMEDIATE read macro character is typed (see below). FLG=T suppresses the carriage-return normally typed by READ following a matching right parenthesis. (However, the characters are still given to READ; i.e., the user does not have to type the carriage-return.) (RATOM FILE RDTBL) [Function] Reads in one atom from FILE. Separation of atoms is defined by RDTBL. % is also defined for RATOM, and the remarks concerning line-buffering and editing control characters also apply. If the characters comprising the atom would normally be interpreted as a number by READ, that number is returned by RATOM. Note however that RATOM takes no special action for " whether or not it is a break character, i.e., RATOM never makes a string. (RSTRING(RSTRING (Function) NIL NIL NIL 3) FILE RDTBL) [Function] Reads characters from FILE up to, but not including, the next break or separator character, and returns them as a string. Backspace, Control-W, Control-Q, Control-V, and % have the same effect as with READ. Note that the break or separator character that terminates a call to RATOM or RSTRING is not read by that call, but remains in the buffer to become the first character seen by the next reading function that is called. If that function is RSTRING, it will return the null string. This is a common source of program bugs. (RATOMS(RATOMS (Function) NIL NIL NIL 3) A FILE RDTBL) [Function] Calls RATOM repeatedly until the atom A is read. Returns a list of the atoms read, not including A. (RATEST(RATEST (Function) NIL NIL NIL 3) FLG) [Function] If FLG = T, RATEST returns T if a separator was encountered immediately prior to the atom returned by the last RATOM or READ, NIL otherwise. If FLG = NIL, RATEST returns T if last atom read by RATOM or READ was a break character, NIL otherwise. If FLG = 1, RATEST returns T if last atom read (by READ or RATOM) contained a % used to quote the next character (as in %[ or %A%B%C), NIL otherwise. (READC (READC% (Function) NIL NIL NIL 3)FILE RDTBL) [Function] Reads and returns the next character, including %, ", etc, i.e., is not affected by break or separator characters. The action of READC is subject to line-buffering, i.e., READC does not return a value until the line has been terminated even if a character has been typed. Thus, the editing control characters have their usual effect. RDTBL does not directly affect the value returned, but is used as usual in line-buffering, e.g., determining when input has been terminated. If (CONTROL T) has been executed (Chapter 30), defeating line-buffering, the RDTBL argument is irrelevant, and READC returns a value as soon as a character is typed (even if the character typed is one of the editing characters, which ordinarily would never be seen in the input buffer). (PEEKC (PEEKC% (Function) NIL NIL NIL 4)FILE %) [Function] Returns the next character, but does not actually read it and remove it from the buffer. If reading from the terminal, the character is echoed as soon as PEEKC reads it, even though it is then "put back" into the system buffer, where Backspace, Control-W, etc. could change it. Thus it is possible for the value returned by PEEKC to "disagree" in the first character with a subsequent READ. (LASTC(LASTC (Function) NIL NIL NIL 4) FILE) [Function] Returns the last character read from FILE. (READCCODE(READCCODE (Function) NIL NIL NIL 4) FILE RDTBL) [Function] Returns the next character code from STREAM; thus, this operation is equivalent to, but more efficient than, (CHCON1 (READC FILE RDTBL)). (PEEKCCODE(PEEKCCODE (Function) NIL NIL NIL 4) FILE %) [Function] Returns, without consuming, the next character code from STREAM; thus, this operation is equivalent to, but more efficient than, (CHCON1 (PEEKC FILE)). (BIN(BIN (Function) NIL NIL NIL 4) STREAM) [Function] Returns the next byte from STREAM. This operation is useful for reading streams of binary, rather than character, data. Note: BIN is similar to READCCODE, except that BIN always reads a single byte, whereas READCCODE reads a "character" that can consist of more than one byte, depending on the character and its encoding. READ, RATOM, RATOMS, PEEKC, READC all wait for input if there is none. The only way to test whether or not there is input is to use READP: (READP(READP (Function) NIL NIL NIL 4) FILE FLG) [Function] Returns T if there is anything in the input buffer of FILE, NIL otherwise. This operation is only interesting for streams whose source of data is dynamic, e.g., the terminal or a byte stream over a network; for other streams, such as to files, (READP FILE) is equivalent to (NOT (EOFP FILE)). Note that because of line-buffering, READP may return T, indicating there is input in the buffer, but READ may still have to wait. Frequently, the terminal's input buffer contains a single EOL character left over from a previous input. For most applications, this situation wants to be treated as though the buffer were empty, and so READP returns NIL in this case. However, if FLG=T, READP returns T if there is any character in the input buffer, including a single EOL. FLG is ignored for streams other than the terminal. (EOFP(EOFP (Function) NIL NIL NIL 5) FILE) [Function] Returns true if FILE is at "end of file", i.e., the next call to an input function would cause an END OF FILE error; NIL otherwise. For randomly accessible files, this can also be thought of as the file pointer pointing beyond the last byte of the file. FILE must be open for (at least) input, or an error is generated, FILE NOT OPEN. Note that EOFP can return NIL and yet the next call to READ might still cause an END OF FILE error, because the only characters remaining in the input were separators or otherwise constituted an incomplete expression. The function SKIPSEPRS is sometimes more useful as a way of detecting end of file when it is known that all the expressions in the file are well formed. (WAITFORINPUT (WAITFORINPUT% (Function) NIL NIL NIL 5)FILE) [Function] Waits until input is available from FILE or from the terminal, i.e. from T. WAITFORINPUT is functionally equivalent to (until (OR (READP T) (READP FILE)) do NIL), except that it does not use up machine cycles while waiting. Returns the device for which input is now available, i.e. FILE or T. FILE can also be an integer, in which case WAITFORINPUT waits until there is input available from the terminal, or until FILE milliseconds have elapsed. Value is T if input is now available, NIL in the case that WAITFORINPUT timed out. (SKREAD(SKREAD (Function) NIL NIL NIL 5) FILE REREADSTRING RDTBL) [Function] "Skip Read". SKREAD consumes characters from FILE as if one call to READ had been performed, without paying the storage and compute cost to really read in the structure. REREADSTRING is for the case where the caller has already performed some READC's and RATOM's before deciding to skip this expression. In this case, REREADSTRING should be the material already read (as a string), and SKREAD operates as though it had seen that material first, thus setting up its parenthesis count, double-quote count, etc. The read table RDTBL is used for reading from FILE. If RDTBL is NIL, it defaults to the value of FILERDTBL. SKREAD may have difficulties if unusual read macros are defined in RDTBL. SKREAD does not recognize read macro characters in REREADSTRING, nor SPLICE or INFIX read macros. This is only a problem if the read macros are defined to parse subsequent input in the stream that does not follow the normal parenthesis and string-quote conventions. SKREAD returns %) if the read terminated on an unbalanced closing parenthesis; %] if the read terminated on an unbalanced %], i.e., one which also would have closed any extant open left parentheses; otherwise NIL. (SKIPSEPRS(SKIPSEPRS (Function) NIL NIL NIL 5) FILE RDTBL) [Function] Consumes characters from FILE until it encounters a non-separator character (as defined by RDTBL). SKIPSEPRS returns, but does not consume, the terminating character, so that the next call to READC would return the same character. If no non-separator character is found before the end of file is reached, SKIPSEPRS returns NIL and leaves the stream at end of file. This function is useful for skipping over "white space" when scanning a stream character by character, or for detecting end of file when reading expressions from a stream with no pre-arranged terminating expression. Output Functions(OUTPUT% FUNCTIONS NIL Output% Functions NIL NIL 6) 1 Unless otherwise specified by DEFPRINT, pointers other than lists, strings, atoms, or numbers, are printed in the form {DATATYPE} followed by the octal representation of the address of the pointer (regardless of radix). For example, an array pointer might print as {ARRAYP}#43,2760. This printed representation is for compactness of display on the user's terminal, and will not read back in correctly; if the form above is read, it will produce the litatom {ARRAYP}#43,2760. Note: The term "end-of-line" appearing in the description of an output function means the character or characters used to terminate a line in the file system being used by the given implementation of Interlisp. For example, in Interlisp-D end-of-line is indicated by the character carriage-return. Some of the functions described below have a RDTBL argument, which specifies the read table to be used for output. If RDTBL is NIL, the primary read table is used. Most of the functions described below have an argument FILE, which specifies the stream on which the operation is to take place. If FILE is NIL, the primary output stream is used . (OUTPUT(OUTPUT (Function) NIL NIL NIL 6) FILE) [Function] Sets FILE as the primary output stream; returns the old primary output stream. FILE must be open for output. (OUTPUT) returns the current primary output stream, which is not changed. Note: If the primary output stream is set to a file, the file's full name, rather than the stream itself, is returned. See the discussion in Chapter 24. (PRIN1(PRIN1 (Function) NIL NIL NIL 6) X FILE) [Function] Prints X on FILE. (PRIN2(PRIN2 (Function) NIL NIL NIL 6) X FILE RDTBL) [Function] Prints X on FILE with %'s and "'s inserted where required for it to read back in properly by READ, using RDTBL. Both PRIN1 and PRIN2 print any kind of Lisp expression, including lists, atoms, numbers, and strings. PRIN1 is generally used for printing expressions where human readability, rather than machine readability, is important, e.g., when printing text rather than program fragments. PRIN1 does not print double quotes around strings, or % in front of special characters. PRIN2 is used for printing Interlisp expressions which can then be read back into Interlisp with READ; i.e., break and separator characters in atoms will be preceded by %'s. For example, the atom "()" is printed as %(%) by PRIN2. If the integer output radix (as set by RADIX) is not 10, PRIN2 prints the integer using the input syntax for non-decimal integers (see Chapter 7) but PRIN1 does not (but both print the integer in the output radix). (PRIN3(PRIN3 (Function) NIL NIL NIL 6) X FILE) [Function] (PRIN4(PRIN4 (Function) NIL NIL NIL 6) X FILE RDTBL) [Function] PRIN3 and PRIN4 are the same as PRIN1 and PRIN2 respectively, except that they do not increment the horizontal position counter nor perform any linelength checks. They are useful primarily for printing control characters. (PRINT(PRINT (Function) NIL NIL NIL 6) X FILE RDTBL) [Function] Prints the expression X using PRIN2 followed by an end-of-line. Returns X. (PRINTCCODE(PRINT (Function) NIL NIL NIL 7) CHARCODE FILE) [Function] Outputs a single character whose code is CHARCODE to FILE. This is similar to (PRIN1 (CHARACTER CHARCODE)), except that numeric characters are guaranteed to print "correctly"; e.g., (PRINTCCODE (CHARCODE 9)) always prints "9", independent of the setting of RADIX. PRINTCCODE may actually print more than one byte on FILE, due to character encoding and end of line conventions; thus, no assumptions should be made about the relative motion of the file pointer (see GETFILEPTR) during this operation. (BOUT(BOUT (Function) NIL NIL NIL 7) STREAM BYTE) [Function] Outputs a single 8-bit byte to STREAM. This is similar to PRINTCCODE, but for binary streams the character position in STREAM is not updated (as with PRIN3), and end of line conventions are ignored. Note: BOUT is similar to PRINTCCODE, except that BOUT always writes a single byte, whereas PRINTCCODE writes a "character" that can consist of more than one byte, depending on the character and its encoding. (SPACES(SPACES (Function) NIL NIL NIL 7) N FILE) [Function] Prints N spaces. Returns NIL. (TERPRI (TERPRI% (Function) NIL NIL NIL 7)FILE) [Function] Prints an end-of-line character. Returns NIL. (FRESHLINE(FRESHLINE (Function) NIL NIL NIL 7) STREAM) [Function] Equivalent to TERPRI, except it does nothing if it is already at the beginning of the line. Returns T if it prints an end-of-line, NIL otherwise. (TAB(TAB (Function) NIL NIL NIL 7) POS MINSPACES FILE) [Function] Prints the appropriate number of spaces to move to position POS. MINSPACES indicates how many spaces must be printed (if NIL, 1 is used). If the current position plus MINSPACES is greater than POS, TAB does a TERPRI and then (SPACES POS). If MINSPACES is T, and the current position is greater than POS, then TAB does nothing. Note: A sequence of PRINT, PRIN2, SPACES, and TERPRI expressions can often be more conveniently coded with a single PRINTOUT statement. (SHOWPRIN2(SHOWPRIN2 (Function) NIL NIL NIL 7) X FILE RDTBL) [Function] Like PRIN2 except if SYSPRETTYFLG=T, prettyprints X instead. Returns X. (SHOWPRINT(SHOWPRINT (Function) NIL NIL NIL 7) X FILE RDTBL) [Function] Like PRINT except if SYSPRETTYFLG=T, prettyprints X instead, followed by an end-of-line. Returns X. SHOWPRINT and SHOWPRIN2 are used by the programmer's assistant (Chapter 13) for printing the values of expressions and for printing the history list, by various commands of the break package (Chapter 14), e.g. ?= and BT commands, and various other system packages. The idea is that by simply settting or binding SYSPRETTYFLG to T (initially NIL), the user instructs the system when interacting with the user to PRETTYPRINT expressions (Chapter 26) instead of printing them. (PRINTBELLS(PRINTBELLS (Function) NIL NIL NIL 8) %) [Function] Used by DWIM (Chapter 20) to print a sequence of bells to alert the user to stop typing. Can be advised or redefined for special applications, e.g., to flash the screen on a display terminal. (FORCEOUTPUT(FORCEOUTPUT (Function) NIL NIL NIL 8) STREAM WAITFORFINISH) [Function] Forces any buffered output data in STREAM to be transmitted. If WAITFORFINISH is non-NIL, this doesn't return until the data has been forced out. (POSITION(POSITION (Function) NIL NIL NIL 8) FILE N) [Function] Returns the column number at which the next character will be read or printed. After a end of line, the column number is 0. If N is non-NIL, resets the column number to be N. Note that resetting POSITION only changes Lisp's belief about the current column number; it does not cause any horizontal motion. Also note that (POSITION FILE) is not the same as (GETFILEPTR FILE) which gives the position in the file, not on the line. (LINELENGTH(LINELENGTH (Function) NIL NIL NIL 8) N FILE) [Function] Sets the length of the print line for the output file FILE to N; returns the former setting of the line length. FILE defaults to the primary output stream. (LINELENGTH NIL FILE) returns the current setting for FILE. When a file is first opened, its line length is set to the value of the variable FILELINELENGTH. Whenever printing an atom or string would increase a file's position beyond the line length of the file, an end of line is automatically inserted first. This action can be defeated by using PRIN3 and PRIN4. (SETLINELENGTH(SETLINELENGTH (Function) NIL NIL NIL 8) N) [Function] Sets the line length for the terminal by doing (LINELENGTH N T). If N is NIL, it determines N by consulting the operating system's belief about the terminal's characteristics. In Interlisp-D, this is a no-op. PRINTLEVEL When using Interlisp one often has to handle large, complicated lists, which are difficult to understand when printed out. PRINTLEVEL allows the user to specify in how much detail lists should be printed. The print functions PRINT, PRIN1, and PRIN2 are all affected by level parameters set by: (PRINTLEVEL(PRINTLEVEL (Function) NIL NIL NIL 8) CARVAL CDRVAL) [Function] Sets the CAR print level to CARVAL, and the CDR print level to CDRVAL. Returns a list cell whose CAR and CDR are the old settings. PRINTLEVEL is initialized with the value (1000 . -1). In order that PRINTLEVEL can be used with RESETFORM or RESETSAVE, if CARVAL is a list cell it is equivalent to (PRINTLEVEL (CAR CARVAL) (CDR CARVAL)). (PRINTLEVEL N NIL) changes the CAR printlevel without affecting the CDR printlevel. (PRINTLEVEL NIL N) changes the CDR printlevel with affecting the CAR printlevel. (PRINTLEVEL) gives the current setting without changing either. Note: Control-P (Chapter 30) can be used to change the PRINTLEVEL setting dynamically, even while Interlisp is printing. The CAR printlevel specifies how "deep" to print a list. Specifically, it is the number of unpaired left parentheses which will be printed. Below that level, all lists will be printed as &. If the CAR printlevel is negative, the action is similar except that an end-of-line is inserted after each right parentheses that would be immediately followed by a left parenthesis. The CDR printlevel specifies how "long" to print a list. It is the number of top level list elements that will be printed before the printing is terminated with --. For example, if CDRVAL=2, (A B C D E) will print as (A B --). For sublists, the number of list elements printed is also affected by the depth of printing in the CAR direction: Whenever the sum of the depth of the sublist (i.e. the number of unmatched left parentheses) and the number of elements is greater than the CDR printlevel, -- is printed. This gives a "triangular" effect in that less is printed the farther one goes in either CAR or CDR direction. If the CDR printlevel is negative, then it is the same as if the CDR printlevel were infinite. Examples: After: (A (B C (D (E F) G) H) K L) prints as: (PRINTLEVEL 3 -1) (A (B C (D & G) H) K L) (PRINTLEVEL 2 -1) (A (B C & H) K L) (PRINTLEVEL 1 -1) (A & K L) (PRINTLEVEL 0 -1) & (PRINTLEVEL 1000 2) (A (B --) --) (PRINTLEVEL 1000 3) (A (B C --) K --) (PRINTLEVEL 1 3) (A & K --) PLVLFILEFLG (PLVLFILEFLG% (Variable) NIL NIL NIL 9) [Variable] Normally, PRINTLEVEL only affects terminal output. Output to all other files acts as though the print level is infinite. However, if PLVLFILEFLG is T (initially NIL), then PRINTLEVEL affects output to files as well. The following three functions are useful for printing isolated expressions at a specified print level without going to the overhead of resetting the global print level. (LVLPRINT (LVLPRINT% (Function) NIL NIL NIL 9)X FILE CARLVL CDRLVL TAIL) [Function] Performs PRINT of X to FILE, using as CAR and CDR print levels the values CARLVL and CDRLVL, respectively. Uses the T read table. If TAIL is specified, and X is a tail of it, then begins its printing with "...", rather than on open parenthesis. (LVLPRIN2(LVLPRIN2 (Function) NIL NIL NIL 9) X FILE CARLVL CDRLVL TAIL) [Function] Similar to LVLPRIN2, but performs a PRIN2. (LVLPRIN1(LVLPRIN1 (Function) NIL NIL NIL 10) X FILE CARLVL CDRLVL TAIL) [Function] Similar to LVLPRIN1, but performs a PRIN1. Printing Numbers(PRINTING% NUMBERS NIL Printing% Numbers NIL NIL 10) How the ordinary printing functions (PRIN1, PRIN2, etc.) print numbers can be affected in several ways. RADIX influences the printing of integers, and FLTFMT influences the printing of floating point numbers. The setting of the variable PRXFLG determines how the symbol-manipulation functions handle numbers. The PRINTNUM package permits greater controls on the printed appearance of numbers, allowing such things as left-justification, suppression of trailing decimals, etc. (RADIX(RADIX (Function) NIL NIL NIL 10) N) [Function] Resets the output radix for integers to the absolute value of N. The value of RADIX is its previous setting. (RADIX) gives the current setting without changing it. The initial setting is 10. Note that RADIX affects output only. There is no input radix; on input, numbers are interpreted as decimal unless they are entered in a non-decimal radix with syntax such as 123Q, |b10101, |5r1234 (see Chapter 7). RADIX does not affect the behavior of UNPACK, etc., unless the value of PRXFLG (below) is T. For example, if PRXFLG is NIL and the radix is set to 8 with (RADIX 8), the value of (UNPACK 9) is (9), not (1 1). Using PRINTNUM (below) or the PRINTOUT command .I (below) is often a more convenient and appropriate way to print a single number in a specified radix than to globally change RADIX. (FLTFMT(FLTFMT (Function) NIL NIL NIL 10) FORMAT) [Function] Resets the output format for floating point numbers to the FLOAT format FORMAT (see PRINTNUM below for a description of FLOAT formats). FORMAT=T specifies the default "free" formatting: some number of significant digits (a function of the implementation) are printed, with trailing zeros suppressed; numbers with sufficiently large or small exponents are instead printed in exponent notation. FLTFMT returns its current setting. (FLTFMT) returns the current setting without changing it. The initial setting is T. Note: In Interlisp-D, FLTFMT ignores the WIDTH and PAD fields of the format (they are implemented only by PRINTNUM). Whether print name manipulation functions (UNPACK, NCHARS, etc.) use the values of RADIX and FLTFMT is determined by the variable PRXFLG: PRXFLG(PRXFLG (Variable) NIL NIL NIL 10) [Variable] If PRXFLG=NIL (the initial setting), then the "PRIN1" name used by PACK, UNPACK, MKSTRING, etc., is computed using base 10 for integers and the system default floating format for floating point numbers, independent of the current setting of RADIX or FLTFMT. If PRXFLG=T, then RADIX and FLTFMT do dictate the "PRIN1" name of numbers. Note that in this case, PACK and UNPACK are not inverses. Examples with (RADIX 8), (FLTFMT '(FLOAT 4 2)): With PRXFLG=NIL, (UNPACK 13) => (1 3) (PACK '(A 9)) => A9 (UNPACK 1.2345) => (1 %. 2 3 4 5) With PRXFLG=T, (UNPACK 13) => (1 5) (PACK '(A 9)) => A11 (UNPACK 1.2345) => (1 %. 2 3) Note that PRXFLG does not effect the radix of "PRIN2" names, so with (RADIX 8), (NCHARS 9 T), which uses PRIN2 names, would return 3, (since 9 would print as 11Q) for either setting of PRXFLG. Warning: Some system functions will not work correctly if PRXFLG is not NIL. Therefore, resetting the global value of PRXFLG is not recommended. It is much better to rebind PRXFLG as a SPECVAR for that part of a program where it needs to be non-NIL. The basic function for printing numbers under format control is PRINTNUM. Its utility is considerably enhanced when used in conjunction with the PRINTOUT package, which implements a compact language for specifying complicated sequences of elementary printing operations, and makes fancy output formats easy to design and simple to program. (PRINTNUM FORMAT NUMBER FILE) [Function] Prints NUMBER on FILE according to the format FORMAT. FORMAT is a list structure with one of the forms described below. If FORMAT is a list of the form (FIX WIDTH RADIX PAD0 LEFTFLUSH), this specifies a FIX format. NUMBER is rounded to the nearest integer, and then printed in a field WIDTH characters long with radix set to RADIX (or 10 if RADIX=NIL; note that the setting from the function RADIX is not used as the default). If PAD0 and LEFTFLUSH are both NIL, the number is right-justified in the field, and the padding characters to the left of the leading digit are spaces. If PAD0 is T, the character "0" is used for padding. If LEFTFLUSH is T, then the number is left-justified in the field, with trailing spaces to fill out WIDTH characters. The following examples illustrate the effects of the FIX format options on the number 9 (the vertical bars indicate the field width): FORMAT: (PRINTNUM FORMAT 9) prints: (FIX 2) | 9| (FIX 2 NIL T) |09| (FIX 12 8 T) |000000000011| (FIX 5 NIL NIL T) |9 | If FORMAT is a list of the form (FLOAT WIDTH DECPART EXPPART PAD0 ROUND), this specifies a FLOAT format. NUMBER is printed as a decimal number in a field WIDTH characters wide, with DECPART digits to the right of the decimal point. If EXPPART is not 0 (or NIL), the number is printed in exponent notation, with the exponent occupying EXPPART characters in the field. EXPPART should allow for the character E and an optional sign to be printed before the exponent digits. As with FIX format, padding on the left is with spaces, unless PAD0 is T. If ROUND is given, it indicates the digit position at which rounding is to take place, counting from the leading digit of the number. Interlisp-D interprets WIDTH=NIL to mean no padding, i.e., to use however much space the number needs, and interprets DECPART=NIL to mean as many decimal places as needed. The following examples illustrate the effects of the FLOAT format options on the number 27.689 (the vertical bars indicate the field width): FORMAT: (PRINTNUM FORMAT 27.689) prints: (FLOAT 7 2) | 27.69| (FLOAT 7 2 NIL T) |0027.69| (FLOAT 7 2 2) | 2.77E1| (FLOAT 11 2 4) | 2.77E+01| (FLOAT 7 2 NIL NIL 1) | 30.00| (FLOAT 7 2 NIL NIL 2) | 28.00| NILNUMPRINTFLG(NILNUMPRINTFLG (Variable) NIL NIL NIL 12) [Variable] If PRINTNUM's NUMBER argument is not a number and not NIL, a NON-NUMERIC ARG error is generated. If NUMBER is NIL, the effect depends on the setting of the variable NILNUMPRINTFLG. If NILNUMPRINTFLG is NIL, then the error occurs as usual. If it is non-NIL, then no error occurs, and the value of NILNUMPRINTFLG is printed right-justified in the field described by FORMAT. This option facilitates the printing of numbers in aggregates with missing values coded as NIL. User Defined Printing Initially, Interlisp only knows how to print in an interesting way objects of type litatom, number, string, list and stackp. All other types of objects are printed in the form {datatype} followed by the octal representation of the address of the pointer, a format that cannot be read back in to produce an equivalent object. When defining user data types (using the DATATYPE record type, Chapter 8), it is often desirable to specify as well how objects of that type should be printed, so as to make their contents readable, or at least more informative to the viewer. The function DEFPRINT is used to specify the printing format of a data type. (DEFPRINT(DEFPRINT (Function) NIL NIL NIL 12) TYPE FN) [Function] TYPE is a type name. Whenever a printing function (PRINT, PRIN1, PRIN2, etc.) or a function requiring a print name (CHCON, NCHARS, etc.) encounters an object of the indicated type, FN is called with two arguments: the item to be printed and the name of the stream, if any, to which the object is to be printed. The second argument is NIL on calls that request the print name of an object without actually printing it. If FN returns a list of the form (ITEM1 . ITEM2), ITEM1 is printed using PRIN1 (unless it is NIL), and then ITEM2 is printed using PRIN2 (unless it is NIL). No spaces are printed between the two items. Typically, ITEM1 is a read macro character. If FN returns NIL, the datum is printed in the system default manner. If FN returns T, nothing further is printed; FN is assumed to have printed the object to the stream itself. Note that this case if permitted only when the second argument passed to FN is non-NIL; otherwise, there is no destination for FN to do its printing, so it must return as in one of the other two cases. Printing Unusual Data Structures HPRINT (for "Horrible Print") and HREAD provide a mechanism for printing and reading back in general data structures that cannot normally be dumped and loaded easily, such as (possibly re-entrant or circular) structures containing user datatypes, arrays, hash tables, as well as list structures. HPRINT will correctly print and read back in any structure containing any or all of the above, chasing all pointers down to the level of literal atoms, numbers or strings. HPRINT currently cannot handle compiled code arrays, stack positions, or arbitrary unboxed numbers. HPRINT operates by simulating the Interlisp PRINT routine for normal list structures. When it encounters a user datatype (see Chapter 8), or an array or hash array, it prints the data contained therein, surrounded by special characters defined as read macro characters. While chasing the pointers of a structure, it also keeps a hash table of those items it encounters, and if any item is encountered a second time, another read macro character is inserted before the first occurrence (by resetting the file pointer with SETFILEPTR) and all subsequent occurrences are printed as a back reference using an appropriate macro character. Thus the inverse function, HREAD merely calls the Interlisp READ routine with the appropriate read table. (HPRINT(HPRINT (Function) NIL NIL NIL 13) EXPR FILE UNCIRCULAR DATATYPESEEN) [Function] Prints EXPR on FILE. If UNCIRCULAR is non-NIL, HPRINT does no checking for any circularities in EXPR (but is still useful for dumping arbitrary structures of arrays, hash arrays, lists, user data types, etc., that do not contain circularities). Specifying UNCIRCULAR as non-NIL results in a large speed and internal-storage advantage. Normally, when HPRINT encounters a user data type for the first time, it outputs a summary of the data type's declaration. When this is read in, the data type is redeclared. If DATATYPESEEN is non-NIL, HPRINT assumes that the same data type declarations will be in force at read time as were at HPRINT time, and not output declarations. HPRINT is intended primarily for output to random access files, since the algorithm depends on being able to reset the file pointer. If FILE is not a random access file (and UNCIRCULAR = NIL), a temporary file, HPRINT.SCRATCH, is opened, EXPR is HPRINTed on it, and then that file is copied to the final output file and the temporary file is deleted. (HREAD(HREAD (Function) NIL NIL NIL 13) FILE) [Function] Reads and returns an HPRINT-ed expression from FILE. (HCOPYALL(HCOPYALL (Function) NIL NIL NIL 13) X) [Function] Copies data structure X. X may contain circular pointers as well as arbitrary structures. Note: HORRIBLEVARS and UGLYVARS (Chapter 17) are two file package commands for dumping and reloading circular and re-entrant data structures. They provide a convenient interface to HPRINT and HREAD. When HPRINT is dumping a data structure that contains an instance of an Interlisp datatype, the datatype declaration is also printed onto the file. Reading such a data structure with HREAD can cause problems if it redefines a system datatype. Redefining a system datatype will almost definitely cause serious errors. The Interlisp system datatypes do not change very often, but there is always a possibility when loading in old files created under an old Interlisp release. To prevent accidental system crashes, HREAD will not redefine datatypes. Instead, it will cause an error "attempt to read DATATYPE with different field specification than currently defined". Continuing from this error will redefine the datatype. Random Access File Operations 1 For most applications, files are read starting at their beginning and proceeding sequentially, i.e., the next character read is the one immediately following the last character read. Similarly, files are written sequentially. However, for files on some devices, it is also possible to read/write characters at arbitrary positions in a file, essentially treating the file as a large block of auxiliary storage. For example, one application might involve writing an expression at the beginning of the file, and then reading an expression from a specified point in its middle. This particular example requires the file be open for both input and output. However, random file input or output can also be performed on files that have been opened for only input or only output. Associated with each file is a "file pointer" that points to the location where the next character is to be read from or written to. The file position of a byte is the number of bytes that precede it in the file, i.e., 0 is the position of the beginning of the file. The file pointer to a file is automatically advanced after each input or output operation. This section describes functions which can be used to reposition the file pointer on those files that can be randomly accessed. A file used in this fashion is much like an array in that it has a certain number of addressable locations that characters can be put into or taken from. However, unlike arrays, files can be enlarged. For example, if the file pointer is positioned at the end of a file and anything is written, the file "grows." It is also possible to position the file pointer beyond the end of file and then to write. (If the program attempts to read beyond the end of file, an END OF FILE error occurs.) In this case, the file is enlarged, and a "hole" is created, which can later be written into. Note that this enlargement only takes place at the end of a file; it is not possible to make more room in the middle of a file. In other words, if expression A begins at position 1000, and expression B at 1100, and the program attempts to overwrite A with expression C, whose printed representation is 200 bytes long, part of B will be altered. Warning: File positions are always in terms of bytes, not characters. The user should thus be very careful about computing the space needed for an expression. In particular, NS characters may take multiple bytes (see below). Also, the end-of-line character (see Chapter 24) may be represented by a different number of characters in different implementations. Output functions may also introduce end-of-line's as a result of LINELENGTH considerations. Therefore NCHARS (see Chapter 2) does not specify how many bytes an expression takes to print, even ignoring line length considerations. (GETFILEPTR(GETFILEPTR (Function) NIL NIL NIL 14) FILE) [Function] Returns the current position of the file pointer for FILE, i.e., the byte address at which the next input/output operation will commence. (SETFILEPTR(SETFILEPTR (Function) NIL NIL NIL 14) FILE ADR) [Function] Sets the file pointer for FILE to the position ADR; returns ADR. The special value ADR=-1 is interpreted to mean the address of the end of file. Note: If a file is opened for output only, the end of file is initially zero, even if an old file by the same name had existed (see OPENSTREAM, Chapter 24). If a file is opened for both input and output, the initial file pointer is the beginning of the file, but (SETFILEPTR FILE -1) sets it to the end of the file. If the file had been opened in append mode by (OPENSTREAM FILE 'APPEND), the file pointer right after opening would be set to the end of the existing file, in which case a SETFILEPTR to position the file at the end would be unnecessary. (GETEOFPTR(GETEOFPTR (Function) NIL NIL NIL 15) FILE) [Function] Returns the byte address of the end of file, i.e., the number of bytes in the file. Equivalent to performing (SETFILEPTR FILE -1) and returning (GETFILEPTR FILE) except that it does not change the current file pointer. (RANDACCESSP(RANDACCESSP (Function) NIL NIL NIL 15) FILE) [Function] Returns FILE if FILE is randomly accessible, NIL otherwise. The file T is not randomly accessible, nor are certain network file connections in Interlisp-D. FILE must be open or an error is generated, FILE NOT OPEN. (COPYBYTES(COPYBYTES (Function) NIL NIL NIL 15) SRCFIL DSTFIL START END) [Function] Copies bytes from SRCFIL to DSTFIL, starting from position START and up to but not including position END. Both SRCFIL and DSTFIL must be open. Returns T. If END=NIL, START is interpreted as the number of bytes to copy (starting at the current position). If START is also NIL, bytes are copied until the end of the file is reached. Warning: COPYBYTES does not take any account of multi-byte NS characters (see Chapter 2). COPYCHARS (below) should be used whenever copying information that might include NS characters. (COPYCHARS(COPYCHARS (Function) NIL NIL NIL 15) SRCFIL DSTFIL START END) [Function] Like COPYBYTES except that it copies NS characters (see Chapter 2), and performs the proper conversion if the end-of-line conventions of SRCFIL and DSTFIL are not the same (see Chapter 24). START and END are interpreted the same as with COPYBYTES, i.e., as byte (not character) specifications in SRCFIL. The number of bytes actually output to DSTFIL might be more or less than the number of bytes specified by START and END, depending on what the end-of-line conventions are. In the case where the end-of-line conventions happen to be the same, COPYCHARS simply calls COPYBYTES. (FILEPOS(FILEPOS (Function) NIL NIL NIL 15) PATTERN FILE START END SKIP TAIL CASEARRAY) [Function] Analogous to STRPOS (see Chapter 4), but searches a file rather than a string. FILEPOS searches FILE for the string PATTERN. Search begins at START (or the current position of the file pointer, if START=NIL), and goes to END (or the end of FILE, if END=NIL). Returns the address of the start of the match, or NIL if not found. SKIP can be used to specify a character which matches any character in the file. If TAIL is T, and the search is successful, the value is the address of the first character after the sequence of characters corresponding to PATTERN, instead of the starting address of the sequence. In either case, the file is left so that the next i/o operation begins at the address returned as the value of FILEPOS. CASEARRAY should be a "case array" that specifies that certain characters should be transformed to other characters before matching. Case arrays are returned by CASEARRAY or SEPRCASE below. CASEARRAY=NIL means no transformation will be performed. A case array is an implementation-dependent object that is logically an array of character codes with one entry for each possible character. FILEPOS maps each character in the file "through" CASEARRAY in the sense that each character code is transformed into the corresponding character code from CASEARRAY before matching. Thus if two characters map into the same value, they are treated as equivalent by FILEPOS. CASEARRAY and SETCASEARRAY provide an implementation-independent interface to case arrays. For example, to search without regard to upper and lower case differences, CASEARRAY would be a case array where all characters map to themselves, except for lower case characters, whose corresponding elements would be the upper case characters. To search for a delimited atom, one could use " ATOM " as the pattern, and specify a case array in which all of the break and separator characters mapped into the same code as space. For applications calling for extensive file searches, the function FFILEPOS is often faster than FILEPOS. (FFILEPOS(FFILEPOS (Function) NIL NIL NIL 16) PATTERN FILE START END SKIP TAIL CASEARRAY) [Function] Like FILEPOS, except much faster in most applications. FFILEPOS is an implementation of the Boyer-Moore fast string searching algorithm. This algorithm preprocesses the string being searched for and then scans through the file in steps usually equal to the length of the string. Thus, FFILEPOS speeds up roughly in proportion to the length of the string, e.g., a string of length 10 will be found twice as fast as a string of length 5 in the same position. Because of certain fixed overheads, it is generally better to use FILEPOS for short searches or short strings. (CASEARRAY(CASEARRAY (Function) NIL NIL NIL 16) OLDARRAY) [Function] Creates and returns a new case array, with all elements set to themselves, to indicate the identity mapping. If OLDARRAY is given, it is reused. (SETCASEARRAY(SETCASEARRAY (Function) NIL NIL NIL 16) CASEARRAY FROMCODE TOCODE) [Function] Modifies the case array CASEARRAY so that character code FROMCODE is mapped to character code TOCODE. (GETCASEARRAY(GETCASEARRAY (Function) NIL NIL NIL 16) CASEARRAY FROMCODE) [Function] Returns the character code that FROMCODE is mapped to in CASEARRAY. (SEPRCASE(SEPRCASE (Function) NIL NIL NIL 16) CLFLG) [Function] Returns a new case array suitable for use by FILEPOS or FFILEPOS in which all of the break/separators of FILERDTBL are mapped into character code zero. If CLFLG is non-NIL, then all CLISP characters are mapped into this character as well. This is useful for finding a delimited atom in a file. For example, if PATTERN is " FOO ", and (SEPRCASE T) is used for CASEARRAY, then FILEPOS will find "(FOO_". UPPERCASEARRAY(UPPERCASEARRAY (Variable) NIL NIL NIL 17) [Variable] Value is a case array in which every lowercase character is mapped into the corresponding uppercase character. Useful for searching text files. Input/Output Operations with Characters and Bytes 1 Interlisp-D supports the 16-bit NS character set (see Chapter 2). All of the standard string and print name functions accept litatoms and strings containing NS characters. In almost all cases, a program does not have to distinguish between NS characters or 8-bit characters. The exception to this rule is the handling of input/output operations. Interlisp-D uses two ways of writing 16-bit NS characters on files. One way is to write the full 16-bits (two bytes) every time a character is output. The other way is to use "run-encoding." Each 16 NS character can be decoded into a character set (an integer from 0 to 254 inclusive) and a character number (also an integer from 0 to 254 inclusive). In run-encoding, the byte 255 (illegal as either a character set number or a character number) is used to signal a change to a given character set, and the following bytes are all assumed to come from the same character set (until the next change-character set sequence). Run-encoding can reduce the number of bytes required to encode a string of NS characters, as long as there are long sequences of characters from the same character set (usually the case). Note that characters are not the same as bytes. A single character can take anywhere from one to four bytes bytes, depending on whether it is in the same character set as the preceeding character, and whether run-encoding is enabled. Programs which assume that characters are equal to bytes must be changed to work with NS characters. The functions BIN and BOUT (see above) should only be used to read and write single eight-bit bytes. The functions READCCODE and PRINTCCODE (see above) should be used to read and write single character codes, interpreting run-encoded NS characters. COPYBYTES should only be used to copy blocks of 8-bit data; COPYCHARS should be used to copy characters. Most I/O functions (READC, PRIN1, etc.) read or write 16-bit NS characters. The use of NS characters has serious consequences for any program that uses file pointers to access a file in a random access manner. At any point when a file is being read or written, it has a "current character set." If the file pointer is changed with SETFILEPTR to a part of the file with a different character set, any characters read or written may have the wrong character set. The current character set can be accessed with the following function: (CHARSET(CHARSET (Function) NIL NIL NIL 17) STREAM CHARACTERSET) [Function] Returns the current character set of the stream STREAM. If CHARACTERSET is non-NIL, the current character set for STREAM is set. Note that for output streams this may cause bytes to be written to the stream. If CHARACTERSET is T, run encoding for STREAM is disabled: both the character set and the character number (two bytes total) will be written to the stream for each character printed. PRINTOUT 1 Interlisp provides many facilities for controlling the format of printed output. By executing various sequences of PRIN1, PRIN2, TAB, TERPRI, SPACES, PRINTNUM, and PRINTDEF, almost any effect can be achieved. PRINTOUT implements a compact language for specifying complicated sequences of these elementary printing functions. It makes fancy output formats easy to design and simple to program. PRINTOUT is a CLISP word (like FOR and IF) for interpreting a special printing language in which the user can describe the kinds of printing desired. The description is translated by DWIMIFY to the appropriate sequence of PRIN1, TAB, etc., before it is evaluated or compiled. PRINTOUT printing descriptions have the following general form: (PRINTOUT STREAM PRINTCOM1 ... PRINTCOMN) STREAM is evaluated to obtain the stream to which the output from this specification is directed. The PRINTOUT commands are strung together, one after the other without punctuation, after STREAM. Some commands occupy a single position in this list, but many commands expect to find arguments following the command name in the list. The commands fall into several logical groups: one set deals with horizontal and vertical spacing, another group provides controls for certain formatting capabilities (font changes and subscripting), while a third set is concerned with various ways of actually printing items. Finally, there is a command that permits escaping to a simple Lisp evaluation in the middle of a PRINTOUT form. The various commands are described below. The following examples give a general flavor of how PRINTOUT is used: Example 1: Suppose the user wanted to print out on the terminal the values of three variables, X, Y, and Z, separated by spaces and followed by a carriage return. This could be done by: (PRIN1 X T) (SPACES 1 T) (PRIN1 Y T) (SPACES 1 T) (PRIN1 Z T) (TERPRI T) or by the more concise PRINTOUT form: (PRINTOUT T X , Y , Z T) Here the first T specifies output to the terminal, the commas cause single spaces to be printed, and the final T specifies a TERPRI. The variable names are not recognized as special PRINTOUT commands, so they are printed using PRIN1 by default. Example 2: Suppose the values of X and Y are to be pretty-printed lined up at position 10, preceded by identifying strings. If the output is to go to the primary output stream, the user could write either: (PRIN1 "X =") (PRINTDEF X 10 T) (TERPRI ) (PRIN1 "Y =") (PRINTDEF Y 10 T) (TERPRI) or the equivalent: (PRINTOUT NIL "X =" 10 .PPV X T "Y =" 10 .PPV Y T) Since strings are not recognized as special commands, "X =" is also printed with PRIN1 by default. The positive integer means TAB to position 10, where the .PPV command causes the value of X to be prettyprinted as a variable. By convention, special atoms used as PRINTOUT commands are prefixed with a period. The T causes a carriage return, so the Y information is printed on the next line. Example 3. As a final example, suppose that the value of X is an integer and the value of Y is a floating-point number. X is to be printed right-flushed in a field of width 5 beginning at position 15, and Y is to be printed in a field of width 10 also starting at position 15 with 2 places to the right of the decimal point. Furthermore, suppose that the variable names are to appear in the font class named BOLDFONT and the values in font class SMALLFONT. The program in ordinary Interlisp that would accomplish these effects is too complicated to include here. With PRINTOUT, one could write: (PRINTOUT NIL .FONT BOLDFONT "X =" 15 .FONT SMALLFONT .I5 X T .FONT BOLDFONT "Y =" 15 .FONT SMALLFONT .F10.2 Y T .FONT BOLDFONT) The .FONT commands do whatever is necessary to change the font on a multi-font output device. The .I5 command sets up a FIX format for a call to the function PRINTNUM (see above) to print X in the desired format. The .F10.2 specifies a FLOAT format for PRINTNUM. Horizontal Spacing Commands The horizontal spacing commands provide convenient ways of calling TAB and SPACES. In the following descriptions, N stands for a literal positive integer (not for a variable or expression whose value is an integer). N (N a number) [PRINTOUT Command] Used for absolute spacing. It results in a TAB to position N (literally, a (TAB N)). If the line is currently at position N or beyond, the file will be positioned at position N on the next line. .TAB(TAB (Command) .TAB NIL NIL 19)(.TAB (Command) NIL NIL NIL 19) POS [PRINTOUT Command] Specifies TAB to position (the value of) POS. This is one of several commands whose effect could be achieved by simply escaping to Lisp, and executing the corresponding form. It is provided as a separate command so that the PRINTOUT form is more concise and is prettyprinted more compactly. Note that .TAB N and N, where N is an integer, are equivalent. .TAB0(TAB0 (Command) .TAB0 NIL NIL 19)(.TAB0 (Command) NIL NIL NIL 19) POS [PRINTOUT Command] Like .TAB except that it can result in zero spaces (i.e. the call to TAB specifies MINSPACES=0). -N (N a number) [PRINTOUT Command] Negative integers indicate relative (as opposed to absolute) spacing. Translates as (SPACES |N|). ,(, (Command) NIL NIL NIL 19) [PRINTOUT Command] ,,(,, (Command) NIL NIL NIL 19) [PRINTOUT Command] ,,,(,,, (Command) NIL NIL NIL 19) [PRINTOUT Command] (1, 2 or 3 commas) Provides a short-hand way of specifying 1, 2 or 3 spaces, i.e., these commands are equivalent to -1, -2, and -3, respectively. .SP(SP (Command) .SP NIL NIL 19)(.SP (Command) NIL NIL NIL 19) DISTANCE [PRINTOUT Command] Translates as (SPACES DISTANCE). Note that .SP N and -N, where N is an integer, are equivalent. Vertical Spacing Commands Vertical spacing is obtained by calling TERPRI or printing form-feeds. The relevant commands are: T(T (Command) NIL NIL NIL 20) [PRINTOUT Command] Translates as (TERPRI), i.e., move to position 0 (the first column) of the next line. To print the letter T, use the string "T". .SKIP(SKIP (Command) .SKIP NIL NIL 20)(.SKIP (Command) NIL NIL NIL 20) LINES [PRINTOUT Command] Equivalent to a sequence of LINES (TERPRI)'s. The .SKIP command allows for skipping large constant distances and for computing the distance to be skipped. .PAGE(PAGE (Command) .PAGE NIL NIL 20)(.PAGE (Command) NIL NIL NIL 20) [PRINTOUT Command] Puts a form-feed (Control-L) out on the file. Care is taken to make sure that Interlisp's view of the current line position is correctly updated. Special Formatting Controls There are a small number of commands for invoking some of the formatting capabilities of multi-font output devices. The available commands are: .FONT(FONT (Command) .FONT NIL NIL 20)(.FONT (Command) NIL NIL NIL 20) FONTSPEC [PRINTOUT Command] Changes printing to the font FONTSPEC, which can be a font descriptor, a "font list" such as '(MODERN 10), an image stream (coerced to its current font), or a windows (coerced to the current font of its display stream). See fonts (Chapter 27) for more information. FONTSPEC may also be a positive integer N, which is taken as an abbreviated reference to the font class named FONTN (e.g. 1 => FONT1). .SUP(SUP (Command) .SUP NIL NIL 20)(.SUP (Command) NIL NIL NIL 20) [PRINTOUT Command] Specifies superscripting. All subsequent characters are printed above the base of the current line. Note that this is absolute, not relative: a .SUP following a .SUP is a no-op. .SUB(SUB (Command) .SUB NIL NIL 20)(.SUB (Command) NIL NIL NIL 20) [PRINTOUT Command] Specifies subscripting. Subsequent printing is below the base of the current line. As with superscripting, the effect is absolute. .BASE(BASE (Command) .BASE NIL NIL 20)(.BASE (Command) NIL NIL NIL 20) [PRINTOUT Command] Moves printing back to the base of the current line. Un-does a previous .SUP or .SUB; a no-op, if printing is currently at the base. Printing Specifications The value of any expression in a PRINTOUT form that is not recognized as a command itself or as a command argument is printed using PRIN1 by default. For example, title strings can be printed by simply including the string as a separate PRINTOUT command, and the values of variables and forms can be printed in much the same way. Note that a literal integer, say 51, cannot be printed by including it as a command, since it would be interpreted as a TAB; the desired effect can be obtained by using instead the string specification "51", or the form (QUOTE 51). For those instances when PRIN1 is not appropriate, e.g., PRIN2 is required, or a list structures must be prettyprinted, the following commands are available: .P2(P2 (Command) .P2 NIL NIL 21)(.P2 (Command) NIL NIL NIL 21) THING [PRINTOUT Command] Causes THING to be printed using PRIN2; translates as (PRIN2 THING). .PPF(PPF (Command) .PPF NIL NIL 21)(.PPF (Command) NIL NIL NIL 21) THING [PRINTOUT Command] Causes THING to be prettyprinted at the current line position via PRINTDEF (see Chapter 26). The call to PRINTDEF specifies that THING is to be printed as if it were part of a function definition. That is, SELECTQ, PROG, etc., receive special treatment. .PPV(PPV (Command) .PPV NIL NIL 21)(.PPV (Command) NIL NIL NIL 21) THING [PRINTOUT Command] Prettyprints THING as a variable; no special interpretation is given to SELECTQ, PROG, etc. .PPFTL(PPFTL (Command) .PPFTL NIL NIL 21)(.PPFTL (Command) NIL NIL NIL 21) THING [PRINTOUT Command] Like .PPF, but prettyprints THING as a tail, that is, without the initial and final parentheses if it is a list. Useful for prettyprinting sub-lists of a list whose other elements are formatted with other commands. .PPVTL(PPVTL (Command) .PPVTL NIL NIL 21)(.PPVTL (Command) NIL NIL NIL 21) THING [PRINTOUT Command] Like .PPV, but prettyprints THING as a tail. Paragraph Format Interlisp's prettyprint routines are designed to display the structure of expressions, but they are not really suitable for formatting unstructured text. If a list is to be printed as a textual paragraph, its internal structure is less important than controlling its left and right margins, and the indentation of its first line. The .PARA and .PARA2 commands allow these parameters to be conveniently specified. .PARA(PARA (Command) .PARA NIL NIL 21)(.PARA (Command) NIL NIL NIL 21) LMARG RMARG LIST [PRINTOUT Command] Prints LIST in paragraph format, using PRIN1. Translates as (PRINTPARA LMARG RMARG LIST) (see below). Example: (PRINTOUT T 10 .PARA 5 -5 LST) will print the elements of LST as a paragraph with left margin at 5, right margin at (LINELENGTH)-5, and the first line indented to 10. .PARA2(PARA2 (Command) .PARA2 NIL NIL 21)(.PARA2 (Command) NIL NIL NIL 21) LMARG RMARG LIST [PRINTOUT Command] Print as paragraph using PRIN2 instead of PRIN1. Translates as (PRINTPARA LMARG RMARG LIST T). Right-Flushing Two commands are provided for printing simple expressions flushed-right against a specified line position, using the function FLUSHRIGHT (see below). They take into account the current position, the number of characters in the print-name of the expression, and the position the expression is to be flush against, and then print the appropriate number of spaces to achieve the desired effect. Note that this might entail going to a new line before printing. Note also that right-flushing of expressions longer than a line (e.g. a large list) makes little sense, and the appearance of the output is not guaranteed. .FR(FR (Command) .FR NIL NIL 22)(.FR (Command) NIL NIL NIL 22) POS EXPR [PRINTOUT Command] Flush-right using PRIN1. The value of POS determines the position that the right end of EXPR will line up at. As with the horizontal spacing commands, a negative position number means |POS| columns from the current position, a positive number specifies the position absolutely. POS=0 specifies the right-margin, i.e. is interpreted as (LINELENGTH). .FR2(FR2 (Command) .FR2 NIL NIL 22)(.FR2 (Command) NIL NIL NIL 22) POS EXPR [PRINTOUT Command] Flush-right using PRIN2 instead of PRIN1. Centering Commands for centering simple expressions between the current line position and another specified position are also available. As with right flushing, centering of large expressions is not guaranteed. .CENTER(CENTER (Command) .CENTER NIL NIL 22)(.CENTER (Command) NIL NIL NIL 22) POS EXPR [PRINTOUT Command] Centers EXPR between the current line position and the position specified by the value of POS. A positive POS is an absolute position number, a negative POS specifies a position relative to the current position, and 0 indicates the right-margin. Uses PRIN1 for printing. .CENTER2(CENTER2 (Command) .CENTER2 NIL NIL 22)(.CENTER2 (Command) NIL NIL NIL 22) POS EXPR [PRINTOUT Command] Centers using PRIN2 instead of PRIN1. Numbering The following commands provide FORTRAN-like formatting capabilities for integer and floating-point numbers. Each command specifies a printing format and a number to be printed. The format specification translates into a format-list for the function PRINTNUM. .I(I (Command) .I NIL NIL 22)(.I (Command) NIL NIL NIL 22)FORMAT NUMBER [PRINTOUT Command] Specifies integer printing. Translates as a call to the function PRINTNUM with a FIX format-list constructed from FORMAT. The atomic format is broken apart at internal periods to form the format-list. For example, .I5.8.T yields the format-list (FIX 5 8 T), and the command sequence (PRINTOUT T .I5.8.T FOO) translates as (PRINTNUM '(FIX 5 8 T) FOO). This expression causes the value of FOO to be printed in radix 8 right-flushed in a field of width 5, with 0's used for padding on the left. Internal NIL's in the format specification may be omitted, e.g., the commands .I5..T and .I5.NIL.T are equivalent. The format specification .I1 is often useful for forcing a number to be printed in radix 10 (but not otherwise specially formatted), independent of the current setting of RADIX. .F(F (Command) .F NIL NIL 22)(.F (Command) NIL NIL NIL 22) FORMAT NUMBER [PRINTOUT Command] Specifies floating-number printing. Like the .I format command, except translates with a FLOAT format-list. .N(N (Command) .N NIL NIL 23)(.N (Command) NIL NIL NIL 23) FORMAT NUMBER [PRINTOUT Command] The .I and .F commands specify calls to PRINTNUM with quoted format specifications. The .N command translates as (PRINTNUM FORMAT NUMBER), i.e., it permits the format to be the value of some expression. Note that, unlike the .I and .F commands, FORMAT is a separate element in the command list, not part of an atom beginning with .N. Escaping to Lisp There are many reasons for taking control away from PRINTOUT in the middle of a long printing expression. Common situations involve temporary changes to system printing parameters (e.g. LINELENGTH), conditional printing (e.g. print FOO only if FIE is T), or lower-level iterative printing within a higher-level print specification. #(# (Command) NIL NIL NIL 23) FORM [PRINTOUT Command] The escape command. FORM is an arbitrary Lisp expression that is evaluated within the context established by the PRINTOUT form, i.e., FORM can assume that the primary output stream has been set to be the FILE argument to PRINTOUT. Note that nothing is done with the value of FORM; any printing desired is accomplished by FORM itself, and the value is discarded. Note: Although PRINTOUT logically encloses its translation in a RESETFORM (Chapter 14) to change the primary output file to the FILE argument (if non-NIL), in most cases it can actually pass FILE (or a locally bound variable if FILE is a non-trivial expression) to each printing function. Thus, the RESETFORM is only generated when the # command is used, or user-defined commands (below) are used. If many such occur in repeated PRINTOUT forms, it may be more efficient to embed them all in a single RESETFORM which changes the primary output file, and then specify FILE=NIL in the PRINTOUT expressions themselves. User-Defined Commands The collection of commands and options outlined above is aimed at fulfilling all common printing needs. However, certain applications might have other, more specialized printing idioms, so a facility is provided whereby the user can define new commands. This is done by adding entries to the global list PRINTOUTMACROS to define how the new commands are to be translated. PRINTOUTMACROS(PRINTOUTMACROS (Variable) NIL NIL NIL 23) [Variable] PRINTOUTMACROS is an association-list whose elements are of the form (COMM FN). Whenever COMM appears in command position in the sequence of PRINTOUT commands (as opposed to an argument position of another command), FN is applied to the tail of the command-list (including the command). After inspecting as much of the tail as necessary, the function must return a list whose CAR is the translation of the user-defined command and its arguments, and whose CDR is the list of commands still remaining to be translated in the normal way. For example, suppose the user wanted to define a command "?", which will cause its single argument to be printed with PRIN1 only if it is not NIL. This can be done by entering (? ?TRAN) on PRINTOUTMACROS, and defining the function ?TRAN as follows: (DEFINEQ (?TRAN (COMS) (CONS (SUBST (CADR COMS) 'ARG '(PROG ((TEMP ARG)) (COND (TEMP (PRIN1 TEMP))))) (CDDR COMS))] Note that ?TRAN does not do any printing itself; it returns a form which, when evaluated in the proper context, will perform the desired action. This form should direct all printing to the primary output file. Special Printing Functions The paragraph printing commands are translated into calls on the function PRINTPARA, which may also be called directly: (PRINTPARA(PRINTPARA (Function) NIL NIL NIL 24) LMARG RMARG LIST P2FLAG PARENFLAG FILE) [Function] Prints LIST on FILE in line-filled paragraph format with its first element beginning at the current line position and ending at or before RMARG, and with subsequent lines appearing between LMARG and RMARG. If P2FLAG is non-NIL, prints elements using PRIN2, otherwise PRIN1. If PARENFLAG is non-NIL, then parentheses will be printed around the elements of LIST. If LMARG is zero or positive, it is interpreted as an absolute column position. If it is negative, then the left margin will be at |LMARG|+(POSITION). If LMARG=NIL, the left margin will be at (POSITION), and the paragraph will appear in block format. If RMARG is positive, it also is an absolute column position (which may be greater than the current (LINELENGTH)). Otherwise, it is interpreted as relative to (LINELENGTH), i.e., the right margin will be at (LINELENGTH)+|RMARG|. Example: (TAB 10) (PRINTPARA 5 -5 LST T) will PRIN2 the elements of LST in a paragraph with the first line beginning at column 10, subsequent lines beginning at column 5, and all lines ending at or before (LINELENGTH)-5. The current (LINELENGTH) is unaffected by PRINTPARA, and upon completion, FILE will be positioned immediately after the last character of the last item of LIST. PRINTPARA is a no-op if LIST is not a list. The right-flushing and centering commands translate as calls to the function FLUSHRIGHT: (FLUSHRIGHT(FLUSHRIGHT (Function) NIL NIL NIL 24) POS X MIN P2FLAG CENTERFLAG FILE) [Function] If CENTERFLAG=NIL, prints X right-flushed against position POS on FILE; otherwise, centers X between the current line position and POS. Makes sure that it spaces over at least MIN spaces before printing by doing a TERPRI if necessary; MIN=NIL is equivalent to MIN=1. A positive POS indicates an absolute position, while a negative POS signifies the position which is |POS| to the right of the current line position. POS=0 is interpreted as (LINELENGTH), the right margin. READFILE and WRITEFILE 1 For those applications where the user simply wants to simply read all of the expressions on a file, and not evaluate them, the function READFILE is available: (READFILE(READFILE (Function) NIL NIL NIL 25) FILE RDTBL ENDTOKEN) [NoSpread Function] Reads successive expressions from file using READ (with read table RDTBL) until the single litatom ENDTOKEN is read, or an end of file encountered. Returns a list of these expressions. If RDTBL is not specified, it defaults to FILERDTBL. If ENDTOKEN is not specified, it defaults to the litatom STOP. (WRITEFILE(WRITEFILE (Function) NIL NIL NIL 25) X FILE) [Function] Writes a date expression onto FILE, followed by successive expressions from X, using FILERDTBL as a read table. If X is atomic, its value is used. If FILE is not open, it is opened. If FILE is a list, (CAR FILE) is used and the file is left opened. Otherwise, when X is finished, the litatom STOP is printed on FILE and it is closed. Returns FILE. (ENDFILE(ENDFILE (Function) NIL NIL NIL 25) FILE) [Function] Prints STOP on FILE and closes it. Read Tables 1 Many Interlisp input functions treat certain characters in special ways. For example, READ recognizes that the right and left parenthesis characters are used to specify list structures, and that the quote character is used to delimit text strings. The Interlisp input and (to a certain extent) output routines are table driven by read tables. Read tables are objects that specify the syntactic properties of characters for input routines. Since the input routines parse character sequences into objects, the read table in use determines which sequences are recognized as literal atoms, strings, list structures, etc. Most Interlisp input functions take an optional read table argument, which specifies the read table to use when reading an expression. If NIL is given as the read table, the "primary read table" is used. If T is specified, the system terminal read table is used. Some functions will also accept the atom ORIG (not the value of ORIG) as indicating the "original" system read table. Some output functions also take a read table argument. For example, PRIN2 prints an expression so that it would be read in correctly using a given read table. The Interlisp-D system uses the following read tables: T for input/output from terminals, the value of FILERDTBL for input/output from files, the value of EDITRDTBL for input from terminals while in the tty-based editor, the value of DEDITRDTBL for input from terminals while in the display-based editor, and the value of CODERDTBL for input/output from compiled files. These five read tables are initially copies of the ORIG read table, with changes made to some of them to provide read macros that are specific to terminal input or file input. Using the functions described below, the user may further change, reset, or copy these tables. However, in the case of FILERDTBL and CODERDTBL, the user is cautioned that changing these tables may prevent the system from being able to read files made with the original tables, or prevent users possessing only the standard tables from reading files made using the modified tables. The user can also create new read tables, and either explicitly pass them to input/output functions as arguments, or install them as the primary read table, via SETREADTABLE, and then not specify a RDTBL argument, i.e., use NIL. Read Table Functions (READTABLEP(READTABLEP (Function) NIL NIL NIL 26) RDTBL) [Function] Returns RDTBL if RDTBL is a real read table (not T or ORIG), otherwise NIL. (GETREADTABLE(GETREADTABLE (Function) NIL NIL NIL 26) RDTBL) [Function] If RDTBL=NIL, returns the primary read table. If RDTBL=T, returns the system terminal read table. If RDTBL is a real read table, returns RDTBL. Otherwise, generates an ILLEGAL READTABLE error. (SETREADTABLE(SETREADTABLE (Function) NIL NIL NIL 26) RDTBL FLG) [Function] Sets the primary read table to RDTBL. If FLG=T, SETREADTABLE sets the system terminal read table, T. Note that the user can reset the other system read tables with SETQ, e.g., (SETQ FILERDTBL (GETREADTABLE)). Generates an ILLEGAL READTABLE error if RDTBL is not NIL, T, or a real read table. Returns the previous setting of the primary read table, so SETREADTABLE is suitable for use with RESETFORM (Chapter 14). (COPYREADTABLE(COPYREADTABLE (Function) NIL NIL NIL 26) RDTBL) [Function] Returns a copy of RDTBL. RDTBL can be a real read table, NIL, T, or ORIG (in which case COPYREADTABLE returns a copy of the original system read table), otherwise COPYREADTABLE generates an ILLEGAL READTABLE error. Note that COPYREADTABLE is the only function that creates a read table. (RESETREADTABLE(RESETREADTABLE (Function) NIL NIL NIL 26) RDTBL FROM) [Function] Copies (smashes) FROM into RDTBL. FROM and RDTBL can be NIL, T, or a real read table. In addition, FROM can be ORIG, meaning use the system's original read table. Syntax Classes A read table is an object that contains information about the "syntax class" of each character. There are nine basic syntax classes: LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, STRINGDELIM, ESCAPE, BREAKCHAR, SEPRCHAR, and OTHER, each associated with a primitive syntactic property. In addition, there is an unlimited assortment of user-defined syntax classes, known as "read macros". The basic syntax classes are interpreted as follows: LEFTPAREN (normally left parenthesis) Begins list structure. RIGHTPAREN (normally right parenthesis) Ends list structure. LEFTBRACKET (normally left bracket) Begins list structure. Also matches RIGHTBRACKET characters. RIGHTBRACKET (normally left bracket) Ends list structure. Can close an arbitrary numbers of LEFTPAREN lists, back to the last LEFTBRACKET. STRINGDELIM (normally double quote) Begins and ends text strings. Within the string, all characters except for the one(s) with class ESCAPE are treated as ordinary, i.e., interpreted as if they were of syntax class OTHER. To include the string delimiter inside a string, prefix it with the ESCAPE character. ESCAPE (normally percent sign) Inhibits any special interpretation of the next character, i.e., the next character is interpreted to be of class OTHER, independent of its normal syntax class. BREAKCHAR (None initially) Is a break character, i.e., delimits atoms, but is otherwise an ordinary character. SEPRCHAR (space, carriage return, etc.) Delimits atoms, and is otherwise ignored. OTHER Characters that are not otherwise special belong to the class OTHER. Characters of syntax class LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, and STRINGDELIM are all break characters. That is, in addition to their interpretation as delimiting list or string structures, they also terminate the reading of an atom. Characters of class BREAKCHAR serve only to terminate atoms, with no other special meaning. In addition, if a break character is the first non-separator encountered by RATOM, it is read as a one-character atom. In order for a break character to be included in an atom, it must be preceded by the ESCAPE character. Characters of class SEPRCHAR also terminate atoms, but are otherwise completely ignored; they can be thought of as logically spaces. As with break characters, they must be preceded by the ESCAPE character in order to appear in an atom. For example, if $ were a break character and * a separator character, the input stream ABC**DEF$GH*$$ would be read by six calls to RATOM returning respectively ABC, DEF, $, GH, $, $. Although normally there is only one character in a read table having each of the list- and string-delimiting syntax classes (such as LEFTPAREN), it is perfectly acceptable for any character to have any syntax class, and for more than one to have the same class. Note that a "syntax class" is an abstraction: there is no object referencing a collection of characters called a syntax class. Instead, a read table provides the association between a character and its syntax class, and the input/output routines enforce the abstraction by using read tables to drive the parsing. The functions below are used to obtain and set the syntax class of a character in a read table. CH can either be a character code (a integer), or a character (a single-character atom). Single-digit integers are interpreted as character codes, rather than as characters. For example, 1 indicates Control-A, and 49 indicates the character 1. Note that CH can be a full sixteen-bit NS character (see Chapter 2). Note: Terminal tables, described in Chapter 30, also associate characters with syntax classes, and they can also be manipulated with the functions below. The set of read table and terminal table syntax classes are disjoint, so there is never any ambiguity about which type of table is being referred to. (GETSYNTAX CH TABLE) [Function] Returns the syntax class of CH, a character or a character code, with respect to TABLE. TABLE can be NIL, T, ORIG, or a real read table or terminal table. CH can also be a syntax class, in which case GETSYNTAX returns a list of the character codes in TABLE that have that syntax class. (SETSYNTAX CHAR CLASS TABLE) [Function] Sets the syntax class of CHAR, a character or character code, in TABLE. TABLE can be either NIL, T, or a real read table or terminal table. SETSYNTAX returns the previous syntax class of CHAR. CLASS can be any one of the following: f The name of one of the basic syntax classes. f A list, which is interpreted as a read macro (see below). f NIL, T, ORIG, or a real read table or terminal table, which means to give CHAR the syntax class it has in the table indicated by CLASS. For example, (SETSYNTAX '%( 'ORIG TABLE) gives the left parenthesis character in TABLE the same syntax class that it has in the original system read table. f A character code or character, which means to give CHAR the same syntax class as the character CHAR in TABLE. For example, (SETSYNTAX '{ '%[ TABLE) gives the left brace character the same syntax class as the left bracket. (SYNTAXP(SYNTAXP (Function) NIL NIL NIL 28) CODE CLASS TABLE) [Function] CODE is a character code; TABLE is NIL, T, or a real read table or terminal table. Returns T if CODE has the syntax class CLASS in TABLE; NIL otherwise. CLASS can also be a read macro type (MACRO, SPLICE, INFIX), or a read macro option (FIRST, IMMEDIATE, etc.), in which case SYNTAXP returns T if the syntax class is a read macro with the specified property. SYNTAXP will not accept a character as an argument, only a character code. For convenience in use with SYNTAXP, the atom BREAK may be used to refer to all break characters, i.e., it is the union of LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, STRINGDELIM, and BREAKCHAR. For purely symmetrical reasons, the atom SEPR corresponds to all separator characters. However, since the only separator characters are those that also appear in SEPRCHAR, SEPR and SEPRCHAR are equivalent. Note that GETSYNTAX never returns BREAK or SEPR as a value although SETSYNTAX and SYNTAXP accept them as arguments. Instead, GETSYNTAX returns one of the disjoint basic syntax classes that comprise BREAK. BREAK as an argument to SETSYNTAX is interpreted to mean BREAKCHAR if the character is not already of one of the BREAK classes. Thus, if %( is of class LEFTPAREN, then (SETSYNTAX '%( 'BREAK) doesn't do anything, since %( is already a break character, but (SETSYNTAX '%( 'BREAKCHAR) means make %( be just a break character, and therefore disables the LEFTPAREN function of %(. Similarly, if one of the format characters is disabled completely, e.g., by (SETSYNTAX '%( 'OTHER), then (SETSYNTAX '%( 'BREAK) would make %( be only a break character; it would not restore %( as LEFTPAREN. The following functions provide a way of collectively accessing and setting the separator and break characters in a read table: (GETSEPR(GETSEPR (Function) NIL NIL NIL 28) RDTBL) [Function] Returns a list of separator character codes in RDTBL. Equivalent to (GETSYNTAX 'SEPR RDTBL). (GETBRK(GETBRK (Function) NIL NIL NIL 28) RDTBL) [Function] Returns a list of break character codes in RDTBL. Equivalent to (GETSYNTAX 'BREAK RDTBL). (SETSEPR(SETSEPR (Function) NIL NIL NIL 29) LST FLG RDTBL) [Function] Sets or removes the separator characters for RDTBL. LST is a list of charactors or character codes. FLG determines the action of SETSEPR as follows: If FLG=NIL, makes RDTBL have exactly the elements of LST as separators, discarding from RDTBL any old separator characters not in LST. If FLG=0, removes from RDTBL as separator characters all elements of LST. This provides an "UNSETSEPR". If FLG=1, makes each of the characters in LST be a separator in RDTBL. If LST=T, the separator characters are reset to be those in the system's read table for terminals, regardless of the value of FLG, i.e., (SETSEPR T) is equivalent to (SETSEPR (GETSEPR T)). If RDTBL is T, then the characters are reset to those in the original system table. Returns NIL. (SETBRK(SETBRK (Function) NIL NIL NIL 29) LST FLG RDTBL) [Function] Sets the break characters for RDTBL. Similar to SETSEPR. As with SETSYNTAX to the BREAK class, if any of the list- or string-delimiting break characters are disabled by an appropriate SETBRK (or by making it be a separator character), its special action for READ will not be restored by simply making it be a break character again with SETBRK. However, making these characters be break characters when they already are will have no effect. The action of the ESCAPE character (normally %) is not affected by SETSEPR or SETBRK. It can be disabled by setting its syntax to the class OTHER, and other characters can be used for escape on input by assigning them the class ESCAPE. As of this writing, however, there is no way to change the output escape character; it is "hardwired" as %. That is, on output, characters of special syntax that need to be preceded by the ESCAPE character will always be preceded by %, independent of the syntax of % or which, if any characters, have syntax ESCAPE. The following function can be used for defeating the action of the ESCAPE character or characters: (ESCAPE(ESCAPE (Function) NIL NIL NIL 29) FLG RDTBL) [Function] If FLG=NIL, makes characters of class ESCAPE behave like characters of class OTHER on input. Normal setting is (ESCAPE T). ESCAPE returns the previous setting. Read Macros Read macros are user-defined syntax classes that can cause complex operations when certain characters are read. Read macro characters are defined by specifying as a syntax class an expression of the form: (TYPE OPTION1 ... OPTIONN FN) where TYPE is one of MACRO, SPLICE, or INFIX, and FN is the name of a function or a lambda expression. Whenever READ encounters a read macro character, it calls the associated function, giving it as arguments the input stream and read table being used for that call to READ. The interpretation of the value returned depends on the type of read macro: MACRO This is the simplest type of read macro. The result returned from the macro is treated as the expression to be read, instead of the read macro character. Often the macro reads more input itself. For example, in order to cause ~EXPR to be read as (NOT EXPR), one could define ~ as the read macro: [MACRO (LAMBDA (FL RDTBL) (LIST 'NOT (READ FL RDTBL] SPLICE The result (which should be a list or NIL) is spliced into the input using NCONC. For example, if $ is defined by the read macro: (SPLICE (LAMBDA NIL (APPEND FOO))) and the value of FOO is (A B C), then when the user inputs (X $ Y), the result will be (X A B C Y). INFIX The associated function is called with a third argument, which is a list, in TCONC format (Chapter 3), of what has been read at the current level of list nesting. The function's value is taken as a new TCONC list which replaces the old one. For example, the infix operator + could be defined by the read macro: (INFIX (LAMBDA (FL RDTBL Z) (RPLACA (CDR Z) (LIST (QUOTE IPLUS) (CADR Z) (READ FL RDTBL))) Z)) If an INFIX read macro character is encountered not in a list, the third argument to its associated function is NIL. If the function returns NIL, the read macro character is essentially ignored and reading continues. Otherwise, if the function returns a TCONC list of one element, that element is the value of the READ. If it returns a TCONC list of more than one element, the list is the value of the READ. The specification for a read macro character can be augmented to specify various options OPTION1 ... OPTIONN, e.g., (MACRO FIRST IMMEDIATE FN). The following three disjoint options specify when the read macro character is to be effective: ALWAYS The default. The read macro character is always effective (except when preceded by the % character), and is a break character, i.e., a member of (GETSYNTAX 'BREAK RDTBL). FIRST The character is interpreted as a read macro character only when it is the first character seen after a break or separator character; in all other situations, the character is treated as having class OTHER. The read macro character is not a break character. For example, the quote character is a FIRST read macro character, so that DON'T is read as the single atom DON'T, rather than as DON followed by (QUOTE T). ALONE The read macro character is not a break character, and is interpreted as a read macro character only when the character would have been read as a separate atom if it were not a read macro character, i.e., when its immediate neighbors are both break or separator characters. Making a FIRST or ALONE read macro character be a break character (with SETBRK) disables the read macro interpretation, i.e., converts it to syntax class BREAKCHAR. Making an ALWAYS read macro character be a break character is a no-op. The following two disjoint options control whether the read macro character is to be protected by the ESCAPE character on output when a litatom containing the character is printed: ESCQUOTE or ESC The default. When printed with PRIN2, the read macro character will be preceded by the output escape character (%) as needed to permit the atom containing it to be read correctly. Note that for FIRST macros, this means that the character need be quoted only when it is the first character of the atom. NOESCQUOTE or NOESC The read macro character will always be printed without an escape. For example, the ? read macro in the T read table is a NOESCQUOTE character. Unless you are very careful what you are doing, read macro characters in FILERDTBL should never be NOESCQUOTE, since symbols that happen to contain the read macro character will not read back in correctly. The following two disjoint options control when the macro's function is actually executed: IMMEDIATE or IMMED The read macro character is immediately activated, i.e., the current line is terminated, as if an EOL had been typed, a carriage-return line-feed is printed, and the entire line (including the macro character) is passed to the input function. IMMEDIATE read macro characters enable the user to specify a character that will take effect immediately, as soon as it is encountered in the input, rather than waiting for the line to be terminated. Note that this is not necessarily as soon as the character is typed. Characters that cause action as soon as they are typed are interrupt characters (see Chapter 30). Note that since an IMMEDIATE macro causes any input before it to be sent to the reader, characters typed before an IMMEDIATE read macro character cannot be erased by Control-A or Control-Q once the IMMEDIATE character has been typed, since they have already passed through the line buffer. However, an INFIX read macro can still alter some of what has been typed earlier, via its third argument. NONIMMEDIATE or NONIMMED The default. The read macro character is a normal character with respect to the line buffering, and so will not be activated until a carriage-return or matching right parenthesis or bracket is seen. Making a read macro character be both ALONE and IMMEDIATE is a contradiction, since ALONE requires that the next character be input in order to see if it is a break or separator character. Thus, ALONE read macros are always NONIMMEDIATE, regardless of whether or not IMMEDIATE is specified. Read macro characters can be "nested". For example, if = is defined by (MACRO (LAMBDA (FL RDTBL) (EVAL (READ FL RDTBL)))) and ! is defined by (SPLICE (LAMBDA (FL RDTBL) (READ FL RDTBL))) then if the value of FOO is (A B C), and (X =FOO Y) is input, (X (A B C) Y) will be returned. If (X !=FOO Y) is input, (X A B C Y) will be returned. Note: If a read macro's function calls READ, and the READ returns NIL, the function cannot distinguish the case where a RIGHTPAREN or RIGHTBRACKET followed the read macro character, (e.g. "(A B ')"), from the case where the atom NIL (or "()") actually appeared. In Interlisp-D, a READ inside of a read macro when the next input character is a RIGHTPAREN or RIGHTBRACKET reads the character and returns NIL, just as if the READ had not occurred inside a read macro. If a call to READ from within a read macro encounters an unmatched RIGHTBRACKET within a list, the bracket is simply put back into the buffer to be read (again) at the higher level. Thus, inputting an expression such as (A B '(C D] works correctly. (INREADMACROP(INREADMACROP (Function) NIL NIL NIL 32)) [Function] Returns NIL if currently not under a read macro function, otherwise the number of unmatched left parentheses or brackets. (READMACROS(READMACROS (Function) NIL NIL NIL 32) FLG RDTBL) [Function] If FLG=NIL, turns off action of read macros in read table RDTBL. If FLG=T, turns them on. Returns previous setting. The following read macros are standardly defined in Interlisp in the T and EDITRDTBL read tables: ' (single-quote) Returns the next expression, wrapped in a call to QUOTE; e.g., 'FOO reads as (QUOTE FOO). The macro is defined as a FIRST read macro, so that the quote character has no effect in the middle of a symbol. The macro is also ignored if the quote character is immediately followed by a separator character. Control-Y Defined in T and EDITRDTBL. Returns the result of evaluating the next expression. For example, if the value of FOO is (A B), then (LIST 1 control-YFOO 2) is read as (LIST 1 (A B) 2). Note that no structure is copied; the third element of that input expression is still EQ to the value of FOO. Control-Y can thus be used to read structures that ordinarily have no read syntax. For example, the value returned from reading (KEY1 Control-Y(ARRAY 10)) has an array as its second element. Control-Y can be thought of as an "un-quote" character. The choice of character to perform this function is changeable with SETTERMCHARS (see Chapter 16). ` (backquote) Backquote makes it easier to write programs to construct complex data structures. Backquote is like quote, except that within the backquoted expression, forms can be evaluated. The general idea is that the backquoted expression is a "template" containing some constant parts (as with a quoted form) and some parts to be filled in by evaluating something. Unlike with control-Y, however, the evaluation occurs not at the time the form is read, but at the time the backquoted expression is evaluated. That is, the backquote macro returns an expression which, when evaluated, produces the desired structure. Within the backquoted expression, the character "," (comma) introduces a form to be evaluated. The value of a form preceded by ",@" is to be spliced in, using APPEND. If it is permissible to destroy the list being spliced in (i.e., NCONC may be used in the translation), then ",." can be used instead of ",@". For example, if the value of FOO is (1 2 3 4), then the form `(A ,(CAR FOO) ,@(CDDR FOO) D E) evaluates to (A 1 3 4 D E); it is logically equivalent to writing (CONS 'A (CONS (CAR FOO) (APPEND (CDDR FOO) '(D E)))) . Backquote is particularly useful for writing macros. For example, the body of a macro that refers to X as the macro's argument list might be `(COND ((FIXP ,(CAR X)) ,(CADR X)) (T .,(CDDR X))) which is equivalent to writing (LIST 'COND (LIST (LIST 'FIXP (CAR X)) (CADR X)) (CONS 'T (CDDR X))) Note that comma does not have any special meaning outside of a backquote context. For users without a backquote character on their keyboards, backquote can also be written as |' (vertical-bar, quote). ? Implements the ?= command for on-line help regarding the function currently being "called" in the typein (see Chapter 26). | (vertical bar) When followed by an end of line, tab or space, | is ignored, i.e., treated as a separator character, enabling the editor's CHANGECHAR feature (see Chapter 26). Otherwise it is a "dispatching" read macro whose meaning depends on the character(s) following it. The following are currently defined: ' (quote) -- A synonym for backquote. . (period) -- Returns the evaluation of the next expression, i.e., this is a synonym for Control-Y. , (comma) -- Returns the evaluation of the next expression at load time, i.e., the following expression is quoted in such a manner that the compiler treats it as a literal whose value is not determined until the compiled expression is loaded. O or o (the letter O) -- Treats the next number as octal, i.e., reads it in radix 8. For example, |o12 = 10 (decimal). B or b -- Treats the next number as binary, i.e., reads it in radix 2. For example, |b101 = 5 (decimal). X or x -- Treats the next number as hexadecimal, i.e., reads it in radix 16. The uppercase letters A though F are used as the digits after 9. For example, |x1A = 26 (decimal). R or r -- Reads the next number in the radix specified by the (decimal) number that appears between the | and the R. When inputting a number in a radix above ten, the upper-case letters A through Z can be used as the digits after 9 (but there is no digit above Z, so it is not possible to type all base-99 digits). For example, |3r120 reads 120 in radix 3, returning 15. (, {, ^ -- Used internally by HPRINT and HREAD (see above) to print and read unusual expressions. The dispatching characters that are letters can appear in either upper- or lowercase. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "25-" "") STARTINGPAGE# 1) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "25-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (TEXT NIL NIL (54 54 504 690) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "25-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "25-" "")) (54 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGV) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE VERSOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "25-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 10 FAMILY HELVETICA OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "25-" "")) (270 15 288 36) NIL) (HEADING NIL (HEADINGTYPE FOOTINGR) (54 27 558 36) NIL) (HEADING NIL (HEADINGTYPE RECTOHEAD) (54 762 558 36) NIL) (TEXT NIL NIL (54 54 504 684) NIL)))))5/HH5F PAGEHEADING RIGHTPAGE@ PAGEHEADINGLEFTBACKT-T,3T206 +306T306 +T306 +T2/52HHl2l5/2(,2l,,HH2lxx2Hll/5,2lll2HHl2l22l0HHT3$$lT2l5,,53$$(T2l +2Hll3(T2Hl +l,HH,5,3(T,,, TITAN TITAN  +TIMESROMAN  +TIMESROMAN  +TIMESROMAN  +TIMESROMAN MODERN +MODERN +MODERN +     IM.CHAP.GETFNMODERN + HRULE.GETFNMODERN + 3,-IM.INDEX.GETFN HRULE.GETFNMODERN + /IO + 81Ut J +J +$IM.INDEX.GETFN  .%$UJ  "IM.INDEX.GETFN . 2 HRULE.GETFNMODERN + 1,  / / 6 S/a"/"$  IM.INDEX.GETFN .E.A. IM.INDEX.GETFN .Ki   .tR .)#a ).6  ,.2V=  + .%W.S/ "IM.INDEX.GETFN + .1EL !IM.INDEX.GETFN .; !IM.INDEX.GETFN . S .  . )  "IM.INDEX.GETFN + .0N% ? "IM.INDEX.GETFNTITAN  .8  IM.INDEX.GETFN .% +$IM.INDEX.GETFN + .B $IM.INDEX.GETFNTITAN   ./B IM.INDEX.GETFNMODERN + .X- % j1c  IM.INDEX.GETFN .- +.% /.:   31 +IM.INDEX.GETFN .N > . +    )IM.INDEX.GETFNMODERN + .$! z.' B%   !IM.INDEX.GETFNMODERN + .c =; 8u. =- . >)U +$IM.INDEX.GETFNMODERN + + .> Tm  ,3IM.INDEX.GETFN HRULE.GETFNMODERN + 1R^P+,/-E"/7J& !IM.INDEX.GETFN .G.B!  IM.INDEX.GETFN .  IM.INDEX.GETFN  .>1S1"\D* X<  IM.INDEX.GETFN +   IM.INDEX.GETFN  .  IM.INDEX.GETFN  .&  IM.INDEX.GETFN  .)L!. +* + IM.INDEX.GETFN  . +3.* +& +j  !IM.INDEX.GETFN . #IM.INDEX.GETFN .* +$IM.INDEX.GETFN .Q  IM.INDEX.GETFN .< /,  + +-@  +$IM.INDEX.GETFN  .   +$IM.INDEX.GETFN  .  /1 + I  + ; %IM.INDEX.GETFNMODERN + . &IM.INDEX.GETFNMODERN + .#. : #IM.INDEX.GETFNMODERN + ..v   !  %IM.INDEX.GETFN +TIMESROMAN  .62)!T.Et (IM.INDEX.GETFN +TIMESROMAN  ./ u) 1| +]. %IM.INDEX.GETFNMODERN +  .  + + . +  $  "  4*8 +7 . +. f| f7. +(  (+(%(((#('(  (IM.INDEX.GETFN . + +s   +"1 %IM.INDEX.GETFN .  0"  #IM.INDEX.GETFNMODERN + .  $IM.INDEX.GETFNMODERN + .  )4IM.INDEX.GETFN 1%8*QG !IM.INDEX.GETFNMODERN + .>L. +!    +. ~  "IM.INDEX.GETFN .; .J. 41+ "IM.INDEX.GETFNMODERN +  .",  !' &$' +& . +   K.;,251@J  .; .  +@# *  +z S .5N%    % %% %. ++/K K4~.V+.5S%   % %%% %% *IM.INDEX.GETFNMODERN + ."40)6^ ) 1 +8 $IM.INDEX.GETFNMODERN + .0.4Q. =. 5. )H )  1^/& +* "IM.INDEX.GETFNMODERN +! . ++ +:. W$." + b  !IM.INDEX.GETFNMODERN + .  $IM.INDEX.GETFNMODERN + .? . . .&7R;2 HRULE.GETFNMODERN + / K9/ +A i)0:/ +` &IM.INDEX.GETFN .5Q &IM.INDEX.GETFN . +8 +z P e +7 +%IM.INDEX.GETFN .n  : 'IM.INDEX.GETFN +TIMESROMAN .W(  +%IM.INDEX.GETFNMODERN + .&.W 9. + I W +%IM.INDEX.GETFNMODERN + . {%" 2*={   #IM.INDEX.GETFNMODERN +* . = +26.QP-.    ,.+ a e  C.K   C $IM.INDEX.GETFNMODERN +* .,.B& +%IM.INDEX.GETFNMODERN + .q (IM.INDEX.GETFNMODERN + .  (IM.INDEX.GETFNMODERN + .   $IM.INDEX.GETFNMODERN + .-) *    *IM.INDEX.GETFN .22 HRULE.GETFNMODERN + 1]/0//"/Z  +o 3 9,/ + #IM.INDEX.GETFNMODERN + .0  Y.  ,  HRULE.GETFNMODERN + 1t&/ -8 +/aNg +/ WQ      //_ 4% /  + / /6)J+"*/ 1 T s/Z#  ) 1C"(:  + ., )4 IM.INDEX.GETFNMODERN +IM.INDEX.GETFN . +F  !IM.INDEX.GETFNMODERN + IM.INDEX.GETFN .<     .U IM.INDEX.GETFN  IM.INDEX.GETFN   IM.INDEX.GETFN .t IM.INDEX.GETFNIM.INDEX.GETFN +TIMESROMAN .  )1(5 IM.INDEX.GETFN +TIMESROMAN .U !IM.INDEX.GETFN IM.INDEX.GETFN +TIMESROMAN . d !IM.INDEX.GETFN IM.INDEX.GETFN +TIMESROMAN .)1 !IM.INDEX.GETFN IM.INDEX.GETFN.8 . E  IM.INDEX.GETFNIM.INDEX.GETFN +TIMESROMAN .   IM.INDEX.GETFNIM.INDEX.GETFN +TIMESROMAN . !IM.INDEX.GETFN +TIMESROMAN  IM.INDEX.GETFN.I1) 1![ea +/a IM.INDEX.GETFNIM.INDEX.GETFN +TIMESROMAN . IM.INDEX.GETFNIM.INDEX.GETFN +TIMESROMAN .6 I# IM.INDEX.GETFNIM.INDEX.GETFN +TIMESROMAN . 6 #IM.INDEX.GETFN!IM.INDEX.GETFN +TIMESROMAN . #IM.INDEX.GETFN +TIMESROMAN !IM.INDEX.GETFN. "1PA !IM.INDEX.GETFN IM.INDEX.GETFN +TIMESROMAN . +. +7 ( #IM.INDEX.GETFN!IM.INDEX.GETFN +TIMESROMAN .  +"1~ + IM.INDEX.GETFNIM.INDEX.GETFN +TIMESROMAN ./^[7  IM.INDEX.GETFN +TIMESROMAN IM.INDEX.GETFN. " +1 %IM.INDEX.GETFN"IM.INDEX.GETFN +TIMESROMAN .N,` 'IM.INDEX.GETFN +TIMESROMAN #IM.INDEX.GETFN. "  1 IM.INDEX.GETFNIM.INDEX.GETFN.B` 'pB . IM.INDEX.GETFNIM.INDEX.GETFN..*IM.INDEX.GETFNIM.INDEX.GETFN .) Y O) 14 +$ P IM.INDEX.GETFN .Y B &*%-) 7&!D ]? 9)128 *IM.INDEX.GETFN.6 0CE.YMM.:;    +. +)1J % +%IM.INDEX.GETFN&.w.  :.} + +1.\ 0 % .   M 1M + &IM.INDEX.GETFN . +  '+#2".   HRULE.GETFNMODERN + 1 $IM.INDEX.GETFNMODERN + .-O." . +%IM.INDEX.GETFNMODERN + .* #  7 #IM.INDEX.GETFNMODERN + .,  HRULE.GETFNMODERN + 1W/CaxV/8/ + F +N [  / )  &IM.INDEX.GETFN  (IM.INDEX.GETFN&. (IM.INDEX.GETFN  &B  +T   )IM.INDEX.GETFN   +  *IM.INDEX.GETFN +&/ ) 1  +     5 +4 ?   R   |LG *gK?  +     | *) zr & b 92 +.3*.+ * + +$+ &"0=>3)F7(K #IM.INDEX.GETFN +TIMESROMAN 3    B5 ,  +    ,v +  % @  /  # /  O    #IM.INDEX.GETFN +TIMESROMAN / "IM.INDEX.GETFN +TIMESROMAN + + #IM.INDEX.GETFN +TIMESROMAN  -. %) $v G "IM.INDEX.GETFN +TIMESROMAN   aDAJ9SlT&*C "IM.INDEX.GETFN +TIMESROMAN  ! + #   $ =O054$0'"%0 0NyC%544 44$4 0%=o7=Yb0Y908;  0 1L  7hI!LRg +V +V  +a] c e W J `Y ( k  8      ( + 3 + +!); + !' 2   (IM.INDEX.GETFN ]  &IM.INDEX.GETFNMODERN +   0,E A   W  Y   n3OD( # ( %h'544455 4 44:`  j +%c: ^ P ^ lH !D%5Xz \ No newline at end of file diff --git a/docs/turpin-irm/IRM-3/25-USERIO-PACKAGES.TEDIT b/docs/turpin-irm/IRM-3/25-USERIO-PACKAGES.TEDIT new file mode 100644 index 00000000..23cd7dcd Binary files /dev/null and b/docs/turpin-irm/IRM-3/25-USERIO-PACKAGES.TEDIT differ diff --git a/docs/turpin-irm/IRM-3/26-GRAPHICS.TEDIT b/docs/turpin-irm/IRM-3/26-GRAPHICS.TEDIT new file mode 100644 index 00000000..9ac57b7b --- /dev/null +++ b/docs/turpin-irm/IRM-3/26-GRAPHICS.TEDIT @@ -0,0 +1,301 @@ +INTERLISP-D REFERENCE MANUAL GRAPHICS OUTPUT OPERATIONS "27"26. GRAPHICS OUTPUT OPERATIONS 2 Streams are used as the basis for all I/O operations. Files are implemented as streams that can support character printing and reading operations, and file pointer manipulation. An image stream is a type of stream that also provides an interface for graphical operations. All of the operations that can applied to streams can be applied to image streams. For example, an image stream can be passed as the argument to PRINT, to print something on an image stream. In addition, special functions are provided to draw lines and curves and perform other graphical operations. Calling these functions on a stream that is not an image stream will generate an error. Primitive Graphics Concepts 1 The Interlisp-D graphics system is based on manipulating bitmaps (rectangular arrays of pixels), positions, regions, and textures. These objects are used by all of the graphics functions. Positions A position denotes a point in an X,Y coordinate system. A POSITION is an instance of a record with fields XCOORD and YCOORD and is manipulated with the standard record package facilities. For example, (create POSITION XCOORD 10 YCOORD 20) creates a position representing the point (10,20). (POSITIONP X) [Function] Returns X if X is a position; NIL otherwise. Regions A Region denotes a rectangular area in a coordinate system. Regions are characterized by the coordinates of their bottom left corner and their width and height. A REGION is a record with fields LEFT, BOTTOM, WIDTH, and HEIGHT. It can be manipulated with the standard record package facilities. There are access functions for the REGION record that return the TOP and RIGHT of the region. The following functions are provided for manipulating regions: (CREATEREGION LEFT BOTTOM WIDTH HEIGHT) [Function] Returns an instance of the REGION record which has LEFT, BOTTOM, WIDTH and HEIGHT as respectively its LEFT, BOTTOM, WIDTH, and HEIGHT fields. Example: (CREATEREGION 10 -20 100 200) will create a region that denotes a rectangle whose width is 100, whose height is 200, and whose lower left corner is at the position (10,-20). (REGIONP X) [Function] Returns X if X is a region, NIL otherwise. (INTERSECTREGIONS(INTERSECTREGIONS (Function) NIL NIL ("27") 2) REGION1 REGION2 ... REGIONn) [NoSpread Function] Returns a region which is the intersection of a number of regions. Returns NIL if the intersection is empty. (UNIONREGIONS(UNIONREGIONS (Function) NIL NIL ("27") 2) REGION1 REGION2 ... REGIONn) [NoSpread Function] Returns a region which is the union of a number of regions, i.e. the smallest region that contains all of them. Returns NIL if there are no regions given. (REGIONSINTERSECTP(REGIONSINTERSECTP (Function) NIL NIL ("27") 2) REGION1 REGION2) [Function] Returns T if REGION1 intersects REGION2. Returns NIL if they do not intersect. (SUBREGIONP(SUBREGIONP (Function) NIL NIL ("27") 2) LARGEREGION SMALLREGION) [Function] Returns T if SMALLREGION is a subregion (is equal to or entirely contained in) LARGEREGION; otherwise returns NIL. (EXTENDREGION(EXTENDREGION (Function) NIL NIL ("27") 2) REGION INCLUDEREGION) [Function] Changes (destructively modifies) the region REGION so that it includes the region INCLUDEREGION. It returns REGION. (MAKEWITHINREGION(MAKEWITHINREGION (Function) NIL NIL ("27") 2) REGION LIMITREGION) [Function] Changes (destructively modifies) the left and bottom of the region REGION so that it is within the region LIMITREGION, if possible. If the dimension of REGION are larger than LIMITREGION, REGION is moved to the lower left of LIMITREGION. If LIMITREGION is NIL, the value of the variable WHOLEDISPLAY (the screen region) is used. MAKEWITHINREGION returns the modified REGION. (INSIDEP(INSIDEP (Function) NIL NIL ("27") 2) REGION POSORX Y) [Function] If POSORX and Y are numbers, it returns T if the point (POSORX,Y) is inside of REGION. If POSORX is a POSITION, it returns T if POSORX is inside of REGION. If REGION is a WINDOW, the window's interior region in window coordinates is used. Otherwise, it returns NIL. Bitmaps The display primitives manipulate graphical images in the form of bitmaps. A bitmap is a rectangular array of "pixels," each of which is an integer representing the color of one point in the bitmap image. A bitmap is created with a specific number of bits allocated for each pixel. Most bitmaps used for the display screen use one bit per pixel, so that at most two colors can be represented. If a pixel is 0, the corresponding location on the image is white. If a pixel is 1, its location is black. This interpretation can be changed for the display screen with the function VIDEOCOLOR. Bitmaps with more than one bit per pixel are used to represent color or grey scale images. Bitmaps use a positive integer coordinate system with the lower left corner pixel at coordinate (0,0). Bitmaps are represented as instances of the datatype BITMAP. Bitmaps can be saved on files with the VARS file package command. (BITMAPCREATE WIDTH HEIGHT BITSPERPIXEL) [Function] Creates and returns a new bitmap which is WIDTH pixels wide by HEIGHT pixels high, with BITSPERPIXEL bits per pixel. If BITSPERPIXEL is NIL, it defaults to 1. (BITMAPP(BITMAPP (Function) NIL NIL ("27") 3) X) [Function] Returns X if X is a bitmap, NIL otherwise. (BITMAPWIDTH(BITMAPWIDTH (Function) NIL NIL ("27") 3) BITMAP) [Function] Returns the width of BITMAP in pixels. (BITMAPHEIGHT(BITMAPHEIGHT (Function) NIL NIL ("27") 3) BITMAP) [Function] Returns the height of BITMAP in pixels. (BITSPERPIXEL BITMAP) [Function] Returns the number of bits per pixel of BITMAP. (BITMAPBIT(BITMAPBIT (Function) NIL NIL ("27") 3) BITMAP X Y NEWVALUE) [Function] If NEWVALUE is between 0 and the maximum value for a pixel in BITMAP, the pixel (X,Y) is changed to NEWVALUE and the old value is returned. If NEWVALUE is NIL, BITMAP is not changed but the value of the pixel is returned. If NEWVALUE is anything else, an error is generated. If (X,Y) is outside the limits of BITMAP, 0 is returned and no pixels are changed. BITMAP can also be a window or display stream. Note: non-window image streams are "write-only"; the NEWVALUE argument must be non-NIL. (BITMAPCOPY(BITMAPCOPY (Function) NIL NIL ("27") 3) BITMAP) [Function] Returns a new bitmap which is a copy of BITMAP (same dimensions, bits per pixel, and contents). (EXPANDBITMAP(EXPANDBITMAP (Function) NIL NIL ("27") 3) BITMAP WIDTHFACTOR HEIGHTFACTOR) [Function] Returns a new bitmap that is WIDTHFACTOR times as wide as BITMAP a nd HEIGHTFACTOR times as high. Each pixel of BITMAP is copied into a WIDTHFACTOR times HEIGHTFACTOR block of pixels. If NIL, WIDTHFACTOR defaults to 4, HEIGHTFACTOR to 1. (ROTATEBITMAP(EXPANDBITMAP (Function) NIL NIL ("27") 4) BITMAP) [Function] Given an m-high by n-wide bitmap, this function returns an n-high by m-wide bitmap. The returned bitmap is the image of the original bitmap, rotated 90 degrees clockwise. (SHRINKBITMAP(SHRINKBITMAP (Function) NIL NIL ("27") 4) BITMAP WIDTHFACTOR HEIGHTFACTOR DESTINATIONBITMAP) [Function] Returns a copy of BITMAP that has been shrunken by WIDTHFACTOR and HEIGHTFACTOR in the width and height, respectively. If NIL, WIDTHFACTOR defaults to 4, HEIGHTFACTOR to 1. If DESTINATIONBITMAP is not provided, a bitmap that is 1/WIDTHFACTOR by 1/HEIGHTFACTOR the size of BITMAP is created and returned. WIDTHFACTOR and HEIGHTFACTOR must be positive integers. (PRINTBITMAP(PRINTBITMAP (Function) NIL NIL ("27") 4) BITMAP FILE) [Function] Prints the bitmap BITMAP on the file FILE in a format that can be read back in by READBITMAP. (READBITMAP(READBITMAP (Function) NIL NIL ("27") 4) FILE) [Function] Creates a bitmap by reading an expression (written by PRINTBITMAP) from the file FILE. (EDITBM(EDITBM (Function) NIL NIL ("27") 4) BMSPEC) [Function] EDITBM provides an easy-to-use interactive editing facility for various types of bitmaps. If BMSPEC is a bitmap, it is edited. If BMSPEC is an atom whose value is a bitmap, its value is edited. If BMSPEC is NIL, EDITBM asks for dimensions and creates a bitmap. If BMSPEC is a region, that portion of the screen bitmap is used. If BMSPEC is a window, it is brought to the top and its contents edited. EDITBM sets up the bitmap being edited in an editing window. The editing window has two major areas: a gridded edit area in the lower part of the window and a display area in the upper left part. In the edit area, the left button will add points, the middle button will erase points. The right button provides access to the normal window commands to reposition and reshape the window. The actual size bitmap is shown in the display area. For example, the following is a picture of the bitmap editing window editing a eight-high by eighteen-wide bitmap: `*~RsFuvmmqmmmv[bfwUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTxDUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTDxUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTHDUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTDUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPƪUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPƪ If the bitmap is too large to fit in the edit area, only a portion will be editable. This portion can be changed by scrolling both up and down in the left margin and left and right in the bottom margin. Pressing the middle button while in the display area will bring up a menu that allows global placement of the portion of the bitmap being edited. To allow more of the bitmap to be editing at once, the window can be reshaped to make it larger or the GridSize command described below can be used to reduce the size of a bit in the edit area. The bitmap editing window can be reshaped to provide more or less room for editing. When this happens, the space allocated to the editing area will be changed to fit in the new region. Whenever the left or middle button is down and the cursor is not in the edit area, the section of the display of the bitmap that is currently in the edit area is complemented. Pressing the left button while not in the edit region will put the lower left 16 x 16 section of the bitmap into the cursor for as long as the left button is held down. Pressing the middle button while not in either the edit area or the display area (i.e., while in the grey area in the upper right or in the title) will bring up a command menu. `Nn|BB8<BD|@d@D@:  $ DPp!#$$DP!$DD$"p!'Ā$$"!$$$!!$D#p!#!@ _R0@ H@ H@ H20! @@ @"!GC@H!I p$$p ! @#@$H@'Ð@$ !$H#P g  ~  xBDHPhDBxA  PxPĄĄx There are commands to stop editing, to restore the bitmap to its initial state and to clear the bitmap. Holding the middle button down over a command will result in an explanatory message being printed in the prompt window. The commands are described below: Paint Puts the current bitmap into a window and call the window PAINT command on it. The PAINT command implements drawing with various brush sizes and shapes but only on an actual sized bitmap. The PAINT mode is left by pressing the RIGHT button and selecting the QUIT command from the menu. At this point, you will be given a choice of whether or not the changes you made while in PAINT mode should be made to the current bitmap. ShowAsTile Tesselates the current bitmap in the upper part of the window. This is useful for determining how a bitmap will look if it were made the display background (using the function CHANGEBACKGROUND). Note: The tiled display will not automatically change as the bitmap changes; to update it, use the ShowAsTile command again. Grid,On/Off Turns the editing grid display on or off. GridSize Allows specification of the size of the editing grid. Another menu will appear giving a choice of several sizes. If one is selected, the editing portion of the bitmap editor will be redrawn using the selected grid size, allowing more or less of the bitmap to be edited without scrolling. The original size is chosen hueristically and is typically about 8. It is particularly useful when editing large bitmaps to set the edit grid size smaller than the original. Reset Sets all or part of the bitmap to the contents it had when EDITBM was called. Another menu will appear giving a choice between resetting the entire bitmap or just the portion that is in the edit area. The second menu also acts as a confirmation, since not selecting one of the choices on this menu results in no action being taken. Clear Sets all or part of the bitmap to 0. As with the Reset command, another menu gives a choice between clearing the entire bitmap or just the portion that is in the edit area. Cursor Sets the cursor to the lower left part of the bitmap. This prompts the user to specify the cursor "hot spot" by clicking in the lower left corner of the grid. OK Copies the changed image into the original bitmap, stops the bitmap editor and closes the edit windows. The changes the bitmap editor makes during the interaction occur on a copy of the original bitmap. Unless the bitmap editor is exited via OK, no changes are made in the original. Stop Stops the bitmap editor without making any changes to the original bitmap. Textures A Texture denotes a pattern of gray which can be used to (conceptually) tessellate the plane to form an infinite sheet of gray. It is currently either a 4 by 4 pattern or a 16 by N (N <= 16) pattern. Textures are created from bitmaps using the following function: (CREATETEXTUREFROMBITMAP(CREATETEXTUREFROMBITMAP (Function) NIL NIL ("27") 6) BITMAP) [Function] Returns a texture object that will produce the texture of BITMAP. If BITMAP is too large, its lower left portion is used. If BITMAP is too small, it is repeated to fill out the texture. (TEXTUREP(TEXTUREP (Function) NIL NIL ("27") 7) OBJECT) [Function] Returns OBJECT if it is a texture; NIL otherwise. The functions which accept textures (TEXTUREP, BITBLT, DSPTEXTURE, etc.) also accept bitmaps up to 16 bits wide by 16 bits high as textures. When a region is being filled with a bitmap texture, the texture is treated as if it were 16 bits wide (if less, the rest is filled with white space). The common textures white and black are available as system constants WHITESHADE and BLACKSHADE. The global variable GRAYSHADE is used by many system facilities as a background gray shade and can be set by the user. (EDITSHADE(EDITSHADE (Function) NIL NIL ("27") 7) SHADE) [Function] Opens a window that allows the user to edit textures. Textures can be either small (4 by 4) patterns or large (16 by 16). In the edit area, the left button adds bits to the shade and the middle button erases bits from the shade. The top part of the window is painted with the current texture whenever all mouse keys are released. Thus it is possible to directly compare two textures that differ by more than one pixel by holding a mouse key down until all changes are made. When the "quit" button is selected, the texture being edited is returned. If SHADE is a texture object, EDITSHADE starts with it. If SHADE is T, it starts with a large (16 by 16) white texture. Otherwise, it starts with WHITESHADE. The following is a picture of the texture editor, editing a large (16 by 16) pattern: @,,000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000``0``0``0`>`0`H`0`H`0`H`0`H`0`H`0`H`0``0``0``0``0``0``000000 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00UUUuU]UWUUUuU]UW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW0ꪺꪺ0 TUU@UTUU@W00 TUU@UTUU@W00 TUU@UTUU@W00 TUU@UTUU@W00UUU`WUUU`W00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW0ꪪꪪ0 TUPUTUPW0**0 TUPUTUPW0**0 TUPUTUPW0**0 TUPUTUPW0**0U@5UXWU@5UXW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW0ꪪꪪ0 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00UUUuUXWUUUuUXW0 0 0 0 0 0 0 0 0@ @ 0 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00UUUuUXWUUUuUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW0ꪪꪪ0 TUPUTUPW0**0 TUPUTUPW0**0 TUPUTUPW0**0 TUPUTUPW0**0U@5UPWU@5UXW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW0ꪪꪪ0 TUU@UTUU@W00 TUU@UTUU@W00 TUU@UTUU@W00 TUU@UTUU@W00TUU`WTUU`W00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW0ꪺꪺ0 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00UUUuU]UWTUUuU]UW0 0 0 0 0 0 0 0 0@ @ 000000 Opening Image Streams 1 An image stream is an output stream which "knows" how to process graphic commands to a graphics output device. Besides accepting the normal character-output functions (PRINT, etc.), an image stream can also be passed as an argument to functions to draw curves, to print characters in multiple fonts, and other graphics operations. Each image stream has an "image stream type," a litatom that specifies the type of graphic output device that the image stream is processing graphics commands for. Currently, the built-in image stream types are DISPLAY (for the display screen), INTERPRESS (for Interpress format printers), and PRESS (for Press format printers). There are also library packages available that define image stream types for the IRIS display, 4045 printer, FX-80 printer, C150 printer, etc. Image streams to the display (display streams) interpret graphics commands by immediately executing the appropriate operations to cause the desired image to appear on the display screen. Image streams for hardcopy devices such as Interpress printers interpret the graphic commands by saving information in a file, which can later be sent to the printer. Note: Not all graphics operations can be properly executed for all image stream types. For example, BITBLT may not be supported to all printers. This functionality is still being developed, but even in the long run some operations may be beyond the physical or logical capabilities of some devices or image file formats. In these cases, the stream will approximate the specified image as best it can. (OPENIMAGESTREAM(OPENIMAGESTREAM (Function) NIL NIL ("27") 8) FILE IMAGETYPE OPTIONS) [Function] Opens and returns an image stream of type IMAGETYPE on a destination specified by FILE. If FILE is a file name on a normal file storage device, the image stream will store graphics commands on the specified file, which can be transmitted to a printer by explicit calls to LISTFILES and SEND.FILE.TO.PRINTER. If IMAGETYPE is DISPLAY, then the user is prompted for a window to open. FILE in this case will be used as the title of the window. If FILE is a file name on the LPT device, this indicates that the graphics commands should be stored in a temporary file, and automatically sent to the printer when the image stream is closed by CLOSEF. FILE = NIL is equivalent to FILE = {LPT}. File names on the LPT device are of the form {LPT}PRINTERNAME.TYPE, where PRINTERNAME, TYPE, or both may be omitted. PRINTERNAME is the name of the particular printer to which the file will be transmitted on closing; it defaults to the first printer on DEFAULTPRINTINGHOST that can print IMAGETYPE files. The TYPE extension supplies the value of IMAGETYPE when it is defaulted (see below). OPENIMAGESTREAM will generate an error if the specified printer does not accept the kind of file specified by IMAGETYPE. If IMAGETYPE is NIL, the image type is inferred from the extension field of FILE and the EXTENSIONS properties in the list PRINTFILETYPES. Thus, the extensions IP, IPR, and INTERPRESS indicate Interpress format, and the extension PRESS indicates Press format. If FILE is a printer file with no extension (of the form {LPT}PRINTERNAME), then IMAGETYPE will be the type that the indicated printer can print. If FILE has no extension but is not on the printer device {LPT}, then IMAGETYPE will default to the type accepted by the first printer on DEFAULTPRINTINGHOST. OPTIONS is a list in property list format, (PROP1 VAL1 PROP2 VAL2 %), used to specify certain attributes of the image stream; not all attributes are meaningful or interpreted by all types of image streams. Acceptable properties are: REGION Value is the region on the page (in stream scale units, 0,0 being the lower-left corner of the page) that text will fill up. It establishes the initial values for DSPLEFTMARGIN, DSPRIGHTMARGIN, DSPBOTTOMMARGIN (the point at which carriage returns cause page advancement) and DSPTOPMARGIN (where the stream is positioned at the beginning of a new page). If this property is not given, the value of the variable DEFAULTPAGEREGION, is used. FONTS Value is a list of fonts that are expected to be used in the image stream. Some image streams (e.g. Interpress) are more efficient if the expected fonts are specified in advance, but this is not necessary. The first font in this list will be the initial font of the stream, otherwise the default font for that image stream type will be used. HEADING Value is the heading to be placed automatically on each page. NIL means no heading. Examples: Suppose that Tremor: is an Interpress printer, Quake is a Press printer, and DEFAULTPRINTINGHOST is (Tremor: Quake): (OPENIMAGESTREAM) returns an Interpress image stream on printer Tremor:. (OPENIMAGESTREAM NIL 'PRESS) returns a Press stream on Quake. (OPENIMAGESTREAM '{LPT}.INTERPRESS) returns an Interpress stream on Tremor:. (OPENIMAGESTREAM '{CORE}FOO.PRESS) returns a Press stream on the file {CORE}FOO.PRESS. (IMAGESTREAMP(IMAGESTREAMP (Function) NIL NIL ("27") 9) X IMAGETYPE) [NoSpread Function] Returns X (possibly coerced to a stream) if it is an output image stream of type IMAGETYPE (or of any type if IMAGETYPE = NIL), otherwise NIL. (IMAGESTREAMTYPE(IMAGESTREAMTYPE (Function) NIL NIL ("27") 9) STREAM) [Function] Returns the image stream type of STREAM. (IMAGESTREAMTYPEP(IMAGESTREAMTYPEP (Function) NIL NIL ("27") 9) STREAM TYPE) [Function] Returns T if STREAM is an image stream of type TYPE. Accessing Image Stream Fields 1 The following functions manipulate the fields of an image stream. These functions return the old value (the one being replaced). A value of NIL for the new value will return the current setting without changing it. These functions do not change any of the bits drawn on the image stream; they just affect future operations done on the image stream. (DSPCLIPPINGREGION(DSPCLIPPINGREGION (Function) NIL NIL ("27") 10) REGION STREAM) [Function] The clipping region is a region that limits the extent of characters printed and lines drawn (in the image stream's coordinate system). Initially set so that no clipping occurs. Warning: For display streams, the window system maintains the clipping region during window operations. Users should be very careful about changing this field. (DSPFONT(DSPFONT (Function) NIL NIL ("27") 10) FONT STREAM) [Function] The font field specifies the font used when printing characters to the image stream. Note: DSPFONT determines its new font descriptor from FONT by the same coercion rules that FONTPROP and FONTCREATE use , with one additional possibility: If FONT is a list of the form (PROP1 VAL1 PROP2 VAL2 ...) where PROP1 is acceptable as a font-property to FONTCOPY, then the new font is obtained by (FONTCOPY (DSPFONT NIL STREAM) PROP1 VAL1 PROP2 VAL2 ...). For example, (DSPFONT '(SIZE 12) STREAM) would change the font to the 12 point version of the current font, leaving all other font properties the same. (DSPTOPMARGIN(DSPTOPMARGIN (Function) NIL NIL ("27") 10) YPOSITION STREAM) [Function] The top margin is an integer that is the Y position after a new page (in the image stream's coordinate system). This function has no effect on windows. (DSPBOTTOMMARGIN(DSPBOTTOMMARGIN (Function) NIL NIL ("27") 10) YPOSITION STREAM) [Function] The bottom margin is an integer that is the minimum Y position that characters will be printed by PRIN1 (in the image stream's coordinate system). This function has no effect on windows. (DSPLEFTMARGIN(DSPLEFTMARGIN (Function) NIL NIL ("27") 10) XPOSITION STREAM) [Function] The left margin is an integer that is the X position after an end-of-line (in the image stream's coordinate system). Initially the left edge of the clipping region. (DSPRIGHTMARGIN(DSPRIGHTMARGIN (Function) NIL NIL ("27") 10) XPOSITION STREAM) [Function] The right margin is an integer that is the maximum X position that characters will be printed by PRIN1 (in the image stream's coordinate system). This is initially the position of the right edge of the window or page. The line length of a window or image stream (as returned by LINELENGTH) is computed by dividing the distance between the left and right margins by the width of an uppercase "A" in the current font. The line length is changed whenever the font, left margin, or right margin are changed or whenever the window is reshaped. (DSPOPERATION(DSPOPERATION (Function) NIL NIL ("27") 11) OPERATION STREAM) [Function] The operation is the default BITBLT operation used when printing or drawing on the image stream. One of REPLACE, PAINT, INVERT, or ERASE. Initially REPLACE. This is a meaningless operation for most printers which support the model that once dots are deposited on a page they cannot be removed. (DSPLINEFEED(DSPLINEFEED (Function) NIL NIL ("27") 11) DELTAY STREAM) [Function] The linefeed is an integer that specifies the Y increment for each linefeed, normally negative. Initially minus the height of the initial font. (DSPCLEOL(DSPLINEFEED (Function) NIL NIL ("27") 11) DSPSTREAM XPOS YPOS HEIGHT) [Function] "Clear to end of line". Clears a region from (XPOS,YPOS) to the right margin of the display, with a height of HEIGHT. If XPOS and YPOS are NIL, clears the remainder of the current display line, using the height of the current font. (DSPRUBOUTCHAR(DSPRUBOUTCHAR (Function) NIL NIL ("27") 11) DSPSTREAM CHAR X Y TTBL) [Function] Backs up over character code CHAR in the DSPSTREAM, erasing it. If X, Y are supplied, the rubbing out starts from the position specified. DSPRUBOUTCHAR assumes CHAR was printed with the terminal table TTBL, so it knows to handle control characters, etc. TTBL defaults to the primary terminal table. (DSPSCALE(DSPSCALE (Function) NIL NIL ("27") 11) SCALE STREAM) [Function] Returns the scale of the image stream STREAM, a number indicating how many units in the streams coordinate system correspond to one printer's point (1/72 of an inch). For example, DSPSCALE returns 1 for display streams, and 35.27778 for Interpress and Press streams (the number of micas per printer's point). In order to be device-independent, user graphics programs must either not specify position values absolutely, or must multiply absolute point quantities by the DSPSCALE of the destination stream. For example, to set the left margin of the Interpress stream XX to one inch, do (DSPLEFTMARGIN (TIMES 72 (DSPSCALE NIL XX)) XX) The SCALE argument to DSPSCALE is currently ignored. In a future release it will enable the scale of the stream to be changed under user control, so that the necessary multiplication will be done internal to the image stream interface. In this case, it would be possible to set the left margin of the Interpress stream XX to one inch by doing (DSPSCALE 1 XX) (DSPLEFTMARGIN 72 XX) (DSPSPACEFACTOR FACTOR STREAM) [Function] The space factor is the amount by which to multiply the natural width of all following space characters on STREAM; this can be used for the justification of text. The default value is 1. For example, if the natural width of a space in STREAM's current font is 12 units, and the space factor is set to two, spaces appear 24 units wide. The values returned by STRINGWIDTH and CHARWIDTH are also affected. The following two functions only have meaning for image streams that can display color: (DSPCOLOR COLOR STREAM) [Function] Sets the default foreground color of STREAM. Returns the previous foreground color. If COLOR is NIL, it returns the current foreground color without changing anything. The default color is white (DSPBACKCOLOR COLOR STREAM) [Function] Sets the background color of STREAM. Returns the previous background color. If COLOR is NIL, it returns the current background color without changing anything. The default background color is black. Current Position of an Image Stream 1 Each image stream has a "current position," which is a position (in the image stream's coordinate system) where the next printing operation will start from. The functions which print characters or draw on an image stream update these values appropriately. The following functions are used to explicitly access the current position of an image stream: (DSPXPOSITION XPOSITION STREAM) [Function] Returns the X coordinate of the current position of STREAM. If XPOSITION is non-NIL, the X coordinate is set to it (without changing the Y coordinate). (DSPYPOSITION YPOSITION STREAM) [Function] Returns the Y coordinate of the current position of STREAM. If YPOSITION is non-NIL, the Y coordinate is set to it (without changing the X coordinate). (MOVETO X Y STREAM) [Function] Changes the current position of STREAM to the point (X,Y). (RELMOVETO DX DY STREAM) [Function] Changes the current position to the point (DX,DY) coordinates away from current position of STREAM. (MOVETOUPPERLEFT STREAM REGION) [Function] Moves the current position to the beginning position of the top line of text. If REGION is non-NIL, it must be a REGION and the X position is changed to the left edge of REGION and the Y position changed to the top of REGION less the font ascent of STREAM. If REGION is NIL, the X coordinate is changed to the left margin of STREAM and the Y coordinate is changed to the top of the clipping region of STREAM less the font ascent of STREAM. Moving Bits Between Bitmaps With BITBLT 1 BITBLT is the primitive function for moving bits from one bitmap to another, or from a bitmap to an image stream. (BITBLT SOURCE SOURCELEFT SOURCEBOTTOM DESTINATION DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT SOURCETYPE OPERATION TEXTURE CLIPPINGREGION) [Function] Transfers a rectangular array of bits from SOURCE to DESTINATION. SOURCE can be a bitmap, or a display stream or window, in which case its associated bitmap is used. DESTINATION can be a bitmap or an arbitrary image stream. WIDTH and HEIGHT define a pair of rectangles, one in each of the SOURCE and DESTINATION whose left, bottom corners are at, respectively, (SOURCELEFT, SOURCEBOTTOM) and (DESTINATIONLEFT, DESTINATIONBOTTOM). If these rectangles overlap the boundaries of either source or destination they are both reduced in size (without translation) so that they fit within their respective boundaries. If CLIPPINGREGION is non-NIL it should be a REGION and is interpreted as a clipping region within DESTINATION; clipping to this region may further reduce the defining rectangles. These (possibly reduced) rectangles define the source and destination rectangles for BITBLT. The mode of transferring bits is defined by SOURCETYPE and OPERATION. SOURCETYPE and OPERATION specify whether the source bits should come from SOURCE or TEXTURE, and how these bits are combined with those of DESTINATION. SOURCETYPE and OPERATION are described further below. TEXTURE is a texture. BITBLT aligns the texture so that the upper-left pixel of the texture coincides with the upper-left pixel of the destination bitmap. SOURCELEFT, SOURCEBOTTOM, DESTINATIONLEFT, and DESTINATIONBOTTOM default to 0. WIDTH and HEIGHT default to the width and height of the SOURCE. TEXTURE defaults to white. SOURCETYPE defaults to INPUT. OPERATION defaults to REPLACE. If CLIPPINGREGION is not provided, no additional clipping is done. BITBLT returns T if any bits were moved; NIL otherwise. Note: If SOURCE or DESTINATION is a window or image stream, the remaining arguments are interpreted as values in the coordinate system of the window or image stream and the operation of BITBLT is translated and clipped accordingly. Also, if a window or image stream is used as the destination to BITBLT, its clipping region further limits the region involved. SOURCETYPE specifies whether the source bits should come from the bitmap SOURCE, or from the texture TEXTURE. SOURCETYPE is interpreted as follows: INPUT The source bits come from SOURCE. TEXTURE is ignored. INVERT The source bits are the inverse of the bits from SOURCE. TEXTURE is ignored. TEXTURE The source bits come from TEXTURE. SOURCE, SOURCELEFT, and SOURCEBOTTOM are ignored. OPERATION specifies how the source bits (as specified by SOURCETYPE) are combined with the bits in DESTINATION and stored back into DESTINATION. DESTINATION is one of the following: REPLACE All source bits (on or off) replace destination bits. PAINT Any source bits that are on replace the corresponding destination bits. Source bits that are off have no effect. Does a logical OR between the source bits and the destination bits. INVERT Any source bits that are on invert the corresponding destination bits. Does a logical XOR between the source bits and the destination bits. ERASE Any source bits that are on erase the corresponding destination bits. Does a logical AND operation between the inverse of the source bits and the destination bits. Different combinations of SOURCETYPE and OPERATION can be specified to achieve many different effects. Given the following bitmaps as the values of SOURCE, TEXTURE, and DESTINATION: @<F??~~??<Ǿal0ql09>l0 l0' @;FDDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" 3 f3f3f3f3 f3< @PZxbgl6gllfÃ7glnf7mlnf6lmlc6lmx6xg BITBLT would produce the results given below for the difference combinations of SOURCETYPE and OPERATION (assuming CLIPPINGREGION, SOURCELEFT, etc. are set correctly, of course): @Pf???~~~~~~?~~?~~?~~??????????????????????????????7`6ٌ8ٌflٌg|wc`v3<6 y mq9mqmyهmmmm9e @Pf~~~?~?~?~??????????????????????????????7`6ٌ8ٌflٌg|wc`v3<6 xCflf09lv0mmv0myn0mcn09cf0 @Pf~~~?~?~?~???????????????????????????????7`6ٌ8ٌflٌg|wc`v3<6 ll9n0mn>mm0mm09l> @Pf?7`6ٌ8ٌflٌg|wc`v3<6 }a09a0m}>maðma9}> @Pf??mm0mm09l> @Pf???~~~~~~maðma9}> @PfDDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#}|`a`8``fl|`|g|``c`a`3<}Ǚ|y mq9mqmyهmmmm9e @PfDDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""}|`a`8``fl|`|g|``c`a`3<}Ǚ|xCflf09lv0mmv0myn0mcn09cf0 @PfDDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""}|`a`8``fl|`|g|``c`a`3<}Ǚ|ll9n0mn>mm0mm09l> @Pfٙٙٙٙٙٙٙٙ}|`a`8``fl|`|g|``c`a`3<}Ǚ|}a09a0m}>maðma9}> (BLTSHADE TEXTURE DESTINATION DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT OPERATION CLIPPINGREGION) [Function] BLTSHADE is the SOURCETYPE = TEXTURE case of BITBLT. It fills the specified region of the destination bitmap DESTINATION with the texture TEXTURE. DESTINATION can be a bitmap or image stream. (BITMAPIMAGESIZE BITMAP DIMENSION STREAM) [Function] Returns the size that BITMAP will be when BITBLTed to STREAM, in STREAM's units. DIMENSION can be one of WIDTH, HEIGHT, or NIL, in which case the dotted pair (WIDTH . HEIGHT) will be returned. Drawing Lines 1 Interlisp-D provides several functions for drawing lines and curves on image streams. The line drawing functions are intended for interactive applications where efficiency is important. They do not allow the use of "brush" patterns, like the curve drawing functions, but (for display streams) they support drawing a line in INVERT mode, so redrawing the line will erase it. DRAWCURVE can be used to draw lines using a brush. (DRAWLINE X1 Y1 X2 Y2 WIDTH OPERATION STREAM COLOR DASHING) [Function] Draws a straight line from the point (X1,Y1) to the point (X2,Y2) on the image stream STREAM. The position of STREAM is set to (X2,Y2). If X1 equals X2 and Y1 equals Y2, a point is drawn at (X1,Y1). WIDTH is the width of the line, in the units of the device. If WIDTH is NIL, the default is 1. OPERATION is the BITBLT operation used to draw the line. If OPERATION is NIL, the value of DSPOPERATION for the image stream is used. COLOR is a color specification that determines the color used to draw the line for image streams that support color. If COLOR is NIL, the DSPCOLOR of STREAM is used. DASHING is a list of positive integers that determines the dashing characteristics of the line. The line is drawn for the number of points indicated by the first element of the dashing list, is not drawn for the number of points indicated by the second element. The third element indicates how long it will be on again, and so forth. The dashing sequence is repeated from the beginning when the list is exhausted. A brush LINEWITHBRUSH-by-LINEWITHBRUSH is used. If DASHING is NIL, the line is not dashed. (DRAWBETWEEN POSITION1 POSITION2 WIDTH OPERATION STREAM COLOR DASHING) [Function] Draws a line from the point POSITION1 to the point POSITION2 onto the destination bitmap of STREAM. The position of STREAM is set to POSITION2. In the Medley release, when using the color argument, Interpress (DRAWLINE (Function) NIL NIL ("27") 16)DRAWLINE treats 16x16 bitmaps or negative numbers as shades/textures. Positive numbers continue to refer to color maps, and so cannot be used as textures. To convert an integer shade into a negative number use NEGSHADE (e.g. (NEGSHADE 42495) is -23041). (DRAWTO X Y WIDTH OPERATION STREAM COLOR DASHING) [Function] Draws a line from the current position to the point (X,Y) onto the destination bitmap of STREAM. The position of STREAM is set to (X,Y). (RELDRAWTO DX DY WIDTH OPERATION STREAM COLOR DASHING) [Function] Draws a line from the current position to the point (DX,DY) coordinates away onto the destination bitmap of STREAM. The position of STREAM is set to the end of the line. If DX and DY are both 0, nothing is drawn. Drawing Curves 1 A curve is drawn by placing a brush pattern centered at each point along the curve's trajectory. A brush pattern is defined by its shape, size, and color. The predefined brush shapes are ROUND, SQUARE, HORIZONTAL, VERTICAL, and DIAGONAL; new brush shapes can be created using the INSTALLBRUSH function, described below. A brush size is an integer specifying the width of the brush in the units of the device. The color is a color specification, which is only used if the curve is drawn to an image stream that supports colors. A brush is specified to the various drawing functions as a list of the form (SHAPE WIDTH COLOR), for example (SQUARE 2) or (VERTICAL 4 RED). A brush can also be specified as a positive integer, which is interpreted as a ROUND brush of that width. If a brush is a litatom, it is assumed to be a function which is called at each point of the curve's trajectory (with three arguments: the X-coordinate of the point, the Y-coordinate, and the image stream), and should do whatever image stream operations are necessary to draw each point. Finally, if a brush is specified as NIL, a (ROUND 1) brush is used as default. The appearance of a curve is also determined by its dashing characteristics. Dashing is specified by a list of positive integers. If a curve is dashed, the brush is placed along the trajectory for the number of units indicated by the first element of the dashing list. The brush is off, not placed in the bitmap, for a number of units indicated by the second element. The third element indicates how long it will be on again, and so forth. The dashing sequence is repeated from the beginning when the list is exhausted. The units used to measure dashing are the units of the brush. For example, specifying the dashing as (1 1) with a brush of (ROUND 16) would put the brush on the trajectory, skip 16 points, and put down another brush. A curve is not dashed if the dashing argument to the drawing function is NIL. The curve functions use the image stream's clipping region and operation. Most types of image streams only support the PAINT operation when drawing curves. When drawing to a display stream, the curve-drawing functions accept the operation INVERT if the brush argument is 1. For brushes larger than 1, these functions will use the ERASE operation instead of INVERT. For display streams, the curve-drawing functions treat the REPLACE operation the same as PAINT. (DRAWCURVE KNOTS CLOSED BRUSH DASHING STREAM) [Function] Draws a "parametric cubic spline curve" on the image stream STREAM. KNOTS is a list of positions to which the curve will be fitted. If CLOSED is non-NIL, the curve will be closed; otherwise it ends at the first and last positions in KNOTS. BRUSH and DASHING are interpreted as described above. For example, (DRAWCURVE '((10 . 10)(50 . 50)(100 . 10)(150 . 50)) NIL '(ROUND 5) '(1 1 1 2) XX) would draw a curve like the following on the display stream XX: `;  > > >                 p p p p       > > > q  p   (DRAWCIRCLE CENTERX CENTERY RADIUS BRUSH DASHING STREAM) [Function] Draws a circle of radius RADIUS about the point (CENTERX,CENTERY) onto the image stream STREAM. STREAM's position is left at (CENTERX,CENTERY). The other arguments are interpreted as described above. (DRAWARC CENTERX CENTERY RADIUS STARTANGLE NDEGREES BRUSH DASHINGSTREAM) [Function] Draws an arc of the circle whose center point is (CENTERX CENTERY) and whose radius is RADIUS from the position at STARTANGLE degrees for NDEGREES number of degrees. If STARTANGLE is 0, the starting point will be (CENTERX (CENTERY + RADIUS)). If NDEGREES is positive, the arc will be counterclockwise. If NDEGREES is negative, the arc will be clockwise. The other arguments are interpreted as described in DRAWCIRCLE. (DRAWELLIPSE CENTERX CENTERY SEMIMINORRADIUS SEMIMAJORRADIUS ORIENTATION BRUSH DASHING STREAM) [Function] Draws an ellipse with a minor radius of SEMIMINORRADIUS and a major radius of SEMIMAJORRADIUS about the point (CENTERX,CENTERY) onto the image stream STREAM. ORIENTATION is the angle of the major axis in degrees, positive in the counterclockwise direction. STREAM's position is left at (CENTERX,CENTERY). The other arguments are interpreted as described above. New brush shapes can be defined using the following function: (INSTALLBRUSH BRUSHNAME BRUSHFN BRUSHARRAY) [Function] Installs a new brush called BRUSHNAME with creation-function BRUSHFN and optional array BRUSHARRAY. BRUSHFN should be a function of one argument (a width), which returns a bitmap of the brush for that width. BRUSHFN will be called to create new instances of BRUSHNAME-type brushes; the sixteen smallest instances will be pre-computed and cached. "Hand-crafted" brushes can be supplied as the BRUSHARRAY argument. Changing an existing brush can be done by calling INSTALLBRUSH with new BRUSHFN and/or BRUSHARRAY. (DRAWPOINT X Y BRUSH STREAM OPERATION) [Function] Draws BRUSH centered around point (X, Y) on STREAM, using the operation OPERATION. BRUSH may be a bitmap or a brush. Miscellaneous Drawing and Printing Operations 1 (DSPFILL REGION TEXTURE OPERATION STREAM) [Function] Fills REGION of the image stream STREAM (within the clipping region) with the texture TEXTURE. If REGION is NIL, the whole clipping region of STREAM is used. If TEXTURE or OPERATION is NIL, the values for STREAM are used. (DRAWPOLYGON(DRAWPOLYGON (Function) NIL NIL ("27") 18) POINTS CLOSED BRUSH DASHING STREAM) [Function] Draws a polygon on the image stream STREAM. POINTS is a list of positions to which the figure will be fitted (the vertices of the polygon). If CLOSED is non-NIL, then the starting position is specified only once in POINTS. If CLOSED is NIL, then the starting vertex must be specified twice in POINTS. BRUSH and DASHING are interpreted as described in Chapter 27 of the Interlisp-D Reference Manual. For example, (DRAWPOLYGON '((100 . 100) (50 . 125) (150 . 175) (200 . 100) (150 . 50)) T '(ROUND 3) '(4 2) XX) will draw a polygon like the following on the display stream XX. `p88>>8>@~p~8~@pπp8~~>q19qpA8 p8pp@8 p8pp@8 p@ (FILLPOLYGON POINTS TEXTURE OPERATION WINDNUMBER STREAM) [Function] OPERATION is the BITBLT operation (see page 27.15 in the Interlisp-D Reference Manual) used to fill the polygon. If the OPERATION is NIL, the OPERATION defaults to the STREAM default OPERATION. WINDNUMBER is the number for the winding rule convention . This number is either 0 or 1; 0 indicates the "zero" winding rule, 1 indicates the "odd" winding rule. When filling a polygon, there is more than one way of dealing with the situation where two polygon sides intersect, or one polygon is fully inside the other. Currently, FILLPOLYGON to a display stream uses the "odd" winding rule, which means that intersecting polygon sides define areas that are filled or not filled somewhat like a checkerboard. For example, (FILLPOLYGON '( ((110 . 110)(150 . 200)(190 . 110)) ((135 . 125)(160 . 125)(160 . 150)(135 . 150)) ) GRAYSHADE WINDOW) will produce a display something like this: `/E@@@ + +PP**PUT**UTUT*UTUTTUP +* +*P@P + +@((*PUT** +UPTUPT*UTUUUPU + +U@UUU@TU@P@ + + + +@PP**PP  @ This fill convention also takes into account all polygons in POINTS, if it specifies multiple polygons. (FILLCIRCLE CENTERX CENTERY RADIUS TEXTURE STREAM) [Function] Fills in a circular area of radius RADIUS about the point (CENTERX,CENTERY) in STREAM with TEXTURE. STREAM's position is left at (CENTERX,CENTERY). (DSPRESET STREAM) [Function] Sets the X coordinate of STREAM to its left margin, sets its Y coordinate to the top of the clipping region minus the font ascent. For a display stream, this also fills its destination bitmap with its background texture. (DSPNEWPAGE STREAM) [Function] Starts a new page. The X coordinate is set to the left margin, and the Y coordinate is set to the top margin plus the linefeed. (CENTERPRINTINREGION EXP REGION STREAM) [Function] Prints EXP so that is it centered within REGION of the STREAM. If REGION is NIL, EXP will be centered in the clipping region of STREAM. Drawing and Shading Grids 1 A grid is a partitioning of an arbitrary coordinate system (hereafter referred to as the "source system") into rectangles. This section describes functions that operate on grids. It includes functions to draw the outline of a grid, to translate between positions in a source system and grid coordinates (the coordinates of the rectangle which contains a given position), and to shade grid rectangles. A grid is defined by its "unit grid," a region (called a grid specification) which is the origin rectangle of the grid in terms of the source system. Its LEFT field is interpreted as the X-coordinate of the left edge of the origin rectangle, its BOTTOM field is the Y-coordinate of the bottom edge of the origin rectangle, its WIDTH is the width of the grid rectangles, and its HEIGHT is the height of the grid rectangles. (GRID GRIDSPEC WIDTH HEIGHT BORDER STREAM GRIDSHADE) [Function] Outlines the grid defined by GRIDSPEC which is WIDTH rectangles wide and HEIGHT rectangles high on STREAM. Each box in the grid has a border within it that is BORDER points on each side; so the resulting lines in the grid are 2*BORDER thick. If BORDER is the atom POINT, instead of a border the lower left point of each grid rectangle will be turned on. If GRIDSHADE is non-NIL, it should be a texture and the border lines will be drawn using that texture. (SHADEGRIDBOX X Y SHADE OPERATION GRIDSPEC GRIDBORDER STREAM) [Function] Shades the grid rectangle (X,Y) of GRIDSPEC with texture SHADE using OPERATION on STREAM. GRIDBORDER is interpreted the same as for GRID. The following two functions map from the X,Y coordinates of the source system into the grid X,Y coordinates: (GRIDXCOORD XCOORD GRIDSPEC) [Function] Returns the grid X-coordinate (in the grid specified by GRIDSPEC) that contains the source system X-coordinate XCOORD. (GRIDYCOORD YCOORD GRIDSPEC) [Function] Returns the grid Y-coordinate (in the grid specified by GRIDSPEC) that contains the source system Y-coordinate YCOORD. The following two functions map from the grid X,Y coordinates into the X,Y coordinates of the source system: (LEFTOFGRIDCOORD GRIDX GRIDSPEC) [Function] Returns the source system X-coordinate of the left edge of a grid rectangle at grid X-coordinate GRIDX (in the grid specified by GRIDSPEC). (BOTTOMOFGRIDCOORD GRIDY GRIDSPEC) [Function] Returns the source system Y-coordinate of the bottom edge of a grid rectangle at grid Y-coordinate GRIDY (in the grid specified by GRIDSPEC). Display Streams 1 Display streams (image streams of type DISPLAY) are used to control graphic output operations to a bitmap, known as the "destination" bitmap of the display stream. For each window on the screen, there is an associated display stream which controls graphics operations to a specific part of the screen bitmap. Any of the functions that take a display stream will also take a window, and use the associated display stream. Display streams can also have a destination bitmap that is not connected to any window or display device. (DSPCREATE DESTINATION) [Function] Creates and returns a display stream. If DESTINATION is specified, it is used as the destination bitmap, otherwise the screen bitmap is used. (DSPDESTINATION DESTINATION DISPLAYSTREAM) [Function] Returns the current destination bitmap for DISPLAYSTREAM, setting it to DESTINATION if non-NIL. DESTINATION can be either the screen bitmap, or an auxilliary bitmap in order to construct figures, possibly save them, and then display them in a single operation. Warning: The window system maintains the destination of a window's display stream. Users should be very careful about changing this field. (DSPXOFFSET XOFFSET DISPLAYSTREAM) [Function] (DSPYOFFSET YOFFSET DISPLAYSTREAM) [Function] Each display stream has its own coordinate system, separate from the coordinate system of its destination bitmap. Having the coordinate system local to the display stream allows objects to be displayed at different places by translating the display stream's coordinate system relative to its destination bitmap. This local coordinate system is defined by the X offset and Y offset. DSPXOFFSET returns the current X offset for DISPLAYSTREAM, the X origin of the display stream's coordinate system in the destination bitmap's coordinate system. It is set to XOFFSET if non-NIL. DSPYOFFSET returns the current Y offset for DISPLAYSTREAM, the Y origin of the display stream's coordinate system in the destination bitmap's coordinate system. It is set to YOFFSET if non-NIL. The X offset and Y offset for a display stream are both initially 0 (no X or Y-coordinate translation). Warning: The window system maintains the X and Y offset of a window's display stream. Users should be very careful about changing these fields. (DSPTEXTURE TEXTURE DISPLAYSTREAM) [Function] Returns the current texture used as the background pattern for DISPLAYSTREAM. It is set to TEXTURE if non-NIL. Initially the value of WHITESHADE. (DSPSOURCETYPE SOURCETYPE DISPLAYSTREAM) [Function] Returns the current BITBLT sourcetype used when printing characters to the display stream. It is set to SOURCETYPE, if non-NIL. Must be either INPUT or INVERT. Initially INPUT. (DSPSCROLL SWITCHSETTING DISPLAYSTREAM) [Function] Returns the current value of the "scroll flag," a flag that determines the scrolling behavior of the display stream; either ON or OFF. If ON, the bits in the display streams's destination bitmap are moved after any linefeed that moves the current position out of the destination bitmap. Any bits moved out of the current clipping region are lost. Does not adjust the X offset, Y offset, or clipping region of the display stream. Initially OFF. Sets the scroll flag to SWITCHSETTING, if non-NIL. Note: The word "scrolling" also describes the use of "scroll bars" on the left and bottom of a window to move an object displayed in a window. Each window has an associated display stream. To get the window of a particular display stream, use WFROMDS: (WFROMDS DISPLAYSTREAM DONTCREATE) [Function] Returns the window associated with DISPLAYSTREAM, creating a window if one does not exist (and DONTCREATE is NIL). Returns NIL if the destination of DISPLAYSTREAM is not a screen bitmap that supports a window system. If DONTCREATE is non-NIL, WFROMDS will never create a window, and returns NIL if DISPLAYSTREAM does not have an associated window. TTYDISPLAYSTREAM calls WFROMDS with DONTCREATE = T, so it will not create a window unnecessarily. Also, if WFROMDS does create a window, it calls CREATEW with NOOPENFLG = T. (DSPBACKUP WIDTH DISPLAYSTREAM) [Function] Backs up DISPLAYSTREAM over a character which is WIDTH screen points wide. DSPBACKUP fills the backed over area with the display stream's background texture and decreases the X position by WIDTH. If this would put the X position less than DISPLAYSTREAM's left margin, its operation is stopped at the left margin. It returns T if any bits were written, NIL otherwise. Fonts 1 A font is the collection of images that are printed or displayed when characters are output to a graphic output device. Some simple displays and printers can only print characters using one font. Bitmap displays and graphic printers can print characters using a large number of fonts. Fonts are identified by a distinctive style or family (such as Modern or Classic), a size (such as 10 points), and a face (such as bold or italic). Fonts also have a rotation that indicates the orientation of characters on the screen or page. A normal horizontal font (also called a portrait font) has a rotation of 0; the rotation of a vertical (landscape) font is 90 degrees. While any combination can be specified, in practice the user will find that only certain combinations of families, sizes, faces, and rotations are available for any graphic output device. To specify a font to the functions described below, a FAMILY is represented by a literal atom, a SIZE by a positive integer, and a FACE by a three-element list of the form (WEIGHT SLOPE EXPANSION). WEIGHT, which indicates the thickness of the characters, can be BOLD, MEDIUM, or LIGHT; SLOPE can be ITALIC or REGULAR; and EXPANSION can be REGULAR, COMPRESSED, or EXPANDED, indicating how spread out the characters are. For convenience, faces may also be specified by three-character atoms, where each character is the first letter of the corresponding field. Thus, MRR is a synonym for (MEDIUM REGULAR REGULAR). In addition, certain common face combinations may be indicated by special literal atoms: STANDARD = (MEDIUM REGULAR REGULAR) = MRR ITALIC = (MEDIUM ITALIC REGULAR) = MIR BOLD = (BOLD REGULAR REGULAR) = BRR BOLDITALIC = (BOLD ITALIC REGULAR) = BIR Interlisp represents all the information related to a font in an object called a font descriptor. Font descriptors contain the family, size, etc. properties used to represent the font. In addition, for each character in the font, the font descriptor contains width information for the character and (for display fonts) a bitmap containing the picture of the character. The font functions can take fonts specified in a variety of different ways. DSPFONT, FONTCREATE, FONTCOPY, etc. can be applied to font descriptors, "font lists" such as '(MODERN 10), image streams (coerced to its current font), or windows (coerced to the current font of its display stream). The printout command ".FONT" will also accept fonts specified in any of these forms. In general font files use the following format: The family name (e.g., Modern); a two digit size (e.g., 08); a three letter Face (e.g., BIR, for Bold Italic Regular); the letter C followed by the font's character set in base 8 (e.g., C41); and finally an extension (e.g., Displayfont). ((SKETCH a% figure% from% a% document VERSION 3 PRIRANGE (19 . 0) SKETCHCONTEXT ((ROUND 1 BLACK) (GACHA 10 (MEDIUM REGULAR REGULAR)) (CENTER BASELINE) (CURVE 18.0 8) NIL NIL (CENTER CENTER) (NIL NIL NIL) T NIL NIL 1 NIL)) ((0.05 13.0 (PRI 3)) (TEXT (88.0 . 120.0) ("Family") 1 (CENTER BASELINE) (MODERN 10 (MEDIUM REGULAR REGULAR)) ((72.5 116.5 31 13)) BLACK)) ((0.05 13.0 (PRI 4)) (TEXT (112.0 . 176.0) ("Size") 1 (CENTER BASELINE) (MODERN 10 (MEDIUM REGULAR REGULAR)) ((102.5 172.5 19 13)) BLACK)) ((0.05 13.0 (PRI 5)) (TEXT (132.0 . 120.0) ("Face") 1 (CENTER BASELINE) (MODERN 10 (MEDIUM REGULAR REGULAR)) ((121.5 116.5 21 13)) BLACK)) ((0.05 13.0 (PRI 7)) (TEXT (152.0 . 144.0) ("Modern08-BIR-C41.Displayfont") 1 (CENTER BASELINE) (TERMINAL 10 (MEDIUM REGULAR REGULAR)) ((68.0 140.5 168 13)) BLACK)) ((0.05 13.0 (PRI 8)) (TEXT (192.0 . 168.0) ("CharacterSet (base 8)") 1 (CENTER BASELINE) (MODERN 10 (MEDIUM REGULAR REGULAR)) ((142.5 164.5 99 13)) BLACK)) ((0.05 13.0 (PRI 9)) (TEXT (208.0 . 120.0) ("Extension") 1 (CENTER BASELINE) (MODERN 10 (MEDIUM REGULAR REGULAR)) ((185.0 116.5 46 13)) BLACK)) ((0.0 6.0 (PRI 11)) (WIRE ((88 . 128) (88 . 140)) (ROUND 1 BLACK) (NIL (SOLID 18.0 8)) NIL 1 NIL (NIL ((88 . 140) (90.47214 . 132.3916) (85.52786 . 132.3916))))) ((0.0 6.0 (PRI 13)) (WIRE ((208 . 128) (208 . 140)) (ROUND 1 BLACK) (NIL (SOLID 18.0 8)) NIL 1 NIL (NIL ((208 . 140) (210.4721 . 132.3916) (205.5279 . 132.3916))))) ((0.0 6.0 (PRI 14)) (WIRE ((156 . 164) (156 . 152)) (ROUND 1 BLACK) (NIL (SOLID 18.0 8)) NIL 1 NIL (NIL ((156 . 152) (153.5279 . 159.6085) (158.4721 . 159.6085))))) ((0.0 6.0 (PRI 15)) (WIRE ((112 . 164) (112 . 152)) (ROUND 1 BLACK) (NIL (SOLID 18.0 8)) NIL 1 NIL (NIL ((112 . 152) (109.5279 . 159.6085) (114.4721 . 159.6085))))) ((0.05 11.0 (PRI 16)) (TEXT (112.0 . 168.0) ("(two digits)") 1 (CENTER BASELINE) (MODERN 8 (MEDIUM REGULAR REGULAR)) ((90.5 165.5 43 11)) BLACK)) ((0.0 10.0 (PRI 18)) (WIRE ((120 . 144) (120 . 136) (140 . 136) (140 . 144)) (ROUND 1 BLACK) NIL NIL 1 NIL NIL)) ((0.0 6.0 (PRI 19)) (WIRE ((132 . 124) (132 . 136)) (ROUND 1 BLACK) NIL NIL 1 NIL NIL))) (68.0 116.0 174.0 69.0) 1.0 4 (FONTCREATE FAMILY SIZE FACE ROTATION DEVICE NOERRORFLG CHARSET) [Function] Returns a font descriptor for the specified font. FAMILY is a litatom specifying the font family. SIZE is an integer indicating the size of the font in points. FACE specifies the face characteristics in one of the formats listed above; if FACE is NIL, STANDARD is used. ROTATION, which specifies the orientation of the font, is 0 (or NIL) for a portrait font and 90 for a landscape font. DEVICE indicates the output device for the font, and can be any image stream type , such as DISPLAY, INTERPRESS, etc. DEVICE may also be an image stream, in which case the type of the stream determines the font device. DEVICE defaults to DISPLAY. The FAMILY argument to FONTCREATE may also be a list, in which case it is interpreted as a font-specification quintuple, a list of the form (FAMILY SIZE FACE ROTATION DEVICE). Thus, (FONTCREATE '(GACHA 10 BOLD)) is equivalent to (FONTCREATE 'GACHA 10 'BOLD). FAMILY may also be a font descriptor, in which case that descriptor is simply returned. If a font descriptor has already been created for the specified font, FONTCREATE simply returns it. If it has not been created, FONTCREATE has to read the font information from a font file that contains the information for that font. The name of an appropriate font file, and the algorithm for searching depends on the device that the font is for, and is described in more detail below. If an appropriate font file is found, it is read into a font descriptor. If no file is found, for DISPLAY fonts FONTCREATE looks for fonts with less face information and fakes the remaining faces (such as by doubling the bit pattern of each character or slanting it). For hardcopy printer fonts, there is no acceptable faking algorithm. If no acceptable font is found, the action of FONTCREATE is determined by NOERRORFLG. If NOERRORFLG is NIL, it generates a FONT NOT FOUND error with the offending font specification; otherwise, FONTCREATE returns NIL. CHARSET is the character set which will be read to create the font. Defaults to 0. For more information on character sets, see NS Characters. (FONTP X) [Function] Returns X if X is a font descriptor; NIL otherwise. (FONTPROP FONT PROP) [Function] Returns the value of the PROP property of font FONT. The following font properties are recognized: FAMILY The style of the font, represented as a literal atom, such as CLASSIC or MODERN. SIZE A positive integer giving the size of the font, in printer's points (1/72 of an inch). WEIGHT The thickness of the characters; one of BOLD, MEDIUM, or LIGHT. SLOPE The "slope" of the characters in the font; one of ITALIC or REGULAR. EXPANSION The extent to which the characters in the font are spread out; one of REGULAR, COMPRESSED, or EXPANDED. Most available fonts have EXPANSION = REGULAR. FACE A three-element list of the form (WEIGHT SLOPE EXPANSION), giving all of the typeface parameters. ROTATION An integer that gives the orientation of the font characters on the screen or page, in degrees. A normal horizontal font (also called a portrait font) has a rotation of 0; the rotation of a vertical (landscape) font is 90. DEVICE The device that the font can be printed on; one of DISPLAY, INTERPRESS, etc. ASCENT An integer giving the maximum height of any character in the font from its base line (the printing position). The top line will be at BASELINE+ASCENT-1. DESCENT An integer giving the maximum extent of any character below the base line, such as the lower part of a "p". The bottom line of a character will be at BASELINE-DESCENT. HEIGHT Equal to ASCENT + DESCENT. SPEC The (FAMILY SIZE FACE ROTATION DEVICE) quintuple by which the font is known to Lisp. DEVICESPEC The (FAMILY SIZE FACE ROTATION DEVICE) quintuple that identifies what will be used to represent the font on the display or printer. It will differ from the SPEC property only if an implicit coercion is done to approximate the specified font with one that actually exists on the device. SCALE The units per printer's point (1/72 of an inch) in which the font is measured. For example, this is 35.27778 (the number of micas per printer's point) for Interpress fonts, which are measured in terms of micas. (FONTCOPY OLDFONT PROP1 VAL1 PROP2 VAL2 ...) [NoSpread Function] Returns a font descriptor that is a copy of the font OLDFONT, but which differs from OLDFONT in that OLDFONT's properties are replaced by the specified properties and values. Thus, (FONTCOPY FONT 'WEIGHT 'BOLD 'DEVICE 'INTERPRESS) will return a bold Interpress font with all other properties the same as those of FONT. FONTCOPY accepts the properties FAMILY, SIZE, WEIGHT, SLOPE, EXPANSION, FACE, ROTATION, and DEVICE. If the first property is a list, it is taken to be the PROP1 VAL1 PROP2 VAL2 ... sequence. Thus, (FONTCOPY FONT '(WEIGHT BOLD DEVICE INTERPRESS)) is equivalent to the example above. If the property NOERROR is specified with value non-NIL, FONTCOPY will return NIL rather than causing an error if the specified font cannot be created. (FONTSAVAILABLE FAMILY SIZE FACE ROTATION DEVICE CHECKFILESTOO?) [Function] Returns a list of available fonts that match the given specification. FAMILY, SIZE, FACE, ROTATION, and DEVICE are the same as for FONTCREATE. Additionally, any of them can be the atom *, in which case all values of that field are matched. If CHECKFILESTOO? is NIL, only fonts already loaded into virtual memory will be considered. If CHECKFILESTOO? is non-NIL, the font directories for the specified device will be searched. When checking font files, the ROTATION is ignored. Note: The search is conditional on the status of the server which holds the font. Thus a file server crash may prevent FONTCREATE from finding a file that an earlier FONTSAVAILABLE returned. Each element of the list returned will be of the form (FAMILY SIZE FACE ROTATION DEVICE). Examples: (FONTSAVAILABLE 'MODERN 10 'MRR 0 'DISPLAY) will return ((MODERN 10 (MEDIUM REGULAR REGULAR) 0 DISPLAY)) if the regular Modern 10 font for the display is in virtual memory; NIL otherwise. (FONTSAVAILABLE '* 14 '* '* 'INTERPRESS T) will return a list of all the size 14 Interpress fonts, whether they are in virtual memory or in font files. (SETFONTDESCRIPTOR FAMILY SIZE FACE ROTATION DEVICE FONT) [Function] Indicates to the system that FONT is the font that should be associated with the FAMILY SIZE FACE ROTATION DEVICE characteristics. If FONT is NIL, the font associated with these characteristics is cleared and will be recreated the next time it is needed. As with FONTPROP and FONTCOPY, FONT is coerced to a font descriptor if it is not one already. This functions is useful when it is desirable to simulate an unavailable font or to use a font with characteristics different from the interpretations provided by the system. (DEFAULTFONT DEVICE FONT %) [Function] Returns the font that would be used as the default (if NIL were specified as a font argument) for image stream type DEVICE. If FONT is a font descriptor, it is set to be the default font for DEVICE. (CHARWIDTH CHARCODE FONT) [Function] CHARCODE is an integer that represents a valid character (as returned by CHCON1). Returns the amount by which an image stream's X-position will be incremented when the character is printed. (CHARWIDTHY CHARCODE FONT) [Function] Like CHARWIDTH, but returns the Y component of the character's width, the amount by which an image stream's Y-position will be incremented when the character is printed. This will be zero for most characters in normal portrait fonts, but may be non-zero for landscape fonts or for vector-drawing fonts. (STRINGWIDTH STR FONT FLG RDTBL) [Function] Returns the amount by which a stream's X-position will be incremented if the printname for the Interlisp-D object STR is printed in font FONT. If FONT is NIL, DEFAULTFONT is used as FONT. If FONT is an image stream, its font is used. If FLG is non-NIL, the PRIN2-pname of STR with respect to the readtable RDTBL is used. (STRINGREGION STR STREAM PRIN2FLG RDTBL) [Function] Returns the region occupied by STR if it were printed at the current location in the image stream STREAM. This is useful, for example, for determining where text is in a window to allow the user to select it. The arguments PRIN2FLG and RDTBL are passed to STRINGWIDTH. Note: STRINGREGION does not take into account any carriage returns in the string, or carriage returns that may be automatically printed if STR is printed to STREAM. Therefore, the value returned is meaningless for multi-line strings. The following functions allow the user to access and change the bitmaps for individual characters in a display font. Note: Character code 256 can be used to access the "dummy" character, used for characters in the font with no bitmap defined. (GETCHARBITMAP CHARCODE FONT) [Function] Returns a bitmap containing a copy of the image of the character CHARCODE in the font FONT. (PUTCHARBITMAP CHARCODE FONT NEWCHARBITMAP NEWCHARDESCENT) [Function] Changes the bitmap image of the character CHARCODE in the font FONT to the bitmap NEWCHARBITMAP. If NEWCHARDESCENT is non-NIL, the descent of the character is changed to the value of NEWCHARDESCENT. (EDITCHAR CHARCODE FONT) [Function] Calls the bitmap editor (EDITBM) on the bitmap image of the character CHARCODE in the font FONT. CHARCODE can be a character code (as returned by CHCON1) or an atom or string, in which case the first character of CHARCODE is used. (WRITESTRIKEFONTFILE(WRITESTRIKEFONTFILE (Function) NIL NIL (4) NIL) FONT CHARSET FILENAME) [Function] Takes a display font font descriptor(FONT% DESCRIPTOR NIL font% descriptor NIL (4) NIL) and a character set number, and writes that character set into a file suitable for reading in again. Note that the font descriptor's current state is used (which was perhaps modified by INSPECTing the datum), so this provides a mechanism for creating/modifying new fonts. For example: (WRITESTRIKEFONTFILE (FONTCREATE 'GACHA 10) 0 '{DSK}Magic10-MRR-C0.DISPLAYFONT) If your DISPLAYFONTDIRECTORIES(DISPLAYFONTDIRECTORIES (Variable) NIL NIL (4) NIL) includes {DSK}, then a subsequent (FONTCREATE 'MAGIC 10) will create a new font descriptor whose appearance is the same as the old Gacha font descriptor. However, the new font is identical to the old one in appearance only. The individual datatype fields and bitmap may not be the same as those in the old font descriptor, due to peculiarities of different font file formats. Font Files and Font Directories 1 If FONTCREATE is called to create a font that has not been loaded into Interlisp, FONTCREATE has to read the font information from a font file that contains the information for that font. For printer devices, the font files have to contain width information for each character in the font. For display fonts, the font files have to contain, in addition, bitmap images for each character in the fonts. The font file names, formats, and searching algorithms are different for each device. There are a set of variables for each device, that determine the directories that are searched for font files. All of these variables must be set before Interlisp can auto-load font files. These variables should be initialized in the site-specific INIT file. DISPLAYFONTDIRECTORIES [Variable] Value is a list of directories searched to find font bitmap files for display fonts. DISPLAYFONTEXTENSIONS [Variable] Value is a list of file extensions used when searching DISPLAYFONTDIRECTORIES for display fonts. Initially set to (DISPLAYFONT), but when using older font files it may be necessary to add STRIKE and AC to this list. INTERPRESSFONTDIRECTORIES [Variable] Value is a list of directories searched to find font widths files for Interpress fonts. PRESSFONTWIDTHSFILES [Variable] Value is a list of files (not directories) searched to find font widths files for Press fonts. Press font widths are packed into large files (usually named FONTS.WIDTHS). Font Profiles 1 PRETTYPRINT contains a facility for printing different elements (user functions, system functions, clisp words, comments, etc.) in different fonts to emphasize (or deemphasize) their importance, and in general to provide for a more pleasing appearance. Of course, in order to be useful, this facility requires that the user is printing on a device (such as a bitmapped display or a laser printer) which supports multiple fonts. PRETTYPRINT signals font changes by inserting into the file a user-defined escape sequence (the value of the variable FONTESCAPECHAR) followed by the character code which specifies, by number, which font to use, i.e. A for font number 1, etc. Thus, if FONTESCAPECHAR were the character F, FC would be output to change to font 3, FA to change to font 1, etc. If FONTESCAPECHAR consists of characters which are separator charactors in FILERDTBL, then a file with font changes in it can also be loaded back in. Currently, PRETTYPRINT uses the following font classes. The user can specify separate fonts for each of these classes, or use the same font for several different classes. LAMBDAFONT The font for printing the name of the function being prettyprinted, before the actual definition (usually a large font). CLISPFONT If CLISPFLG is on, the font for printing any clisp words, i.e. atoms with property CLISPWORD. COMMENTFONT The font used for comments. USERFONT The font for the name of any function in the file, or any member of the list FONTFNS. SYSTEMFONT The font for any other (defined) function. CHANGEFONT The font for an expression marked by the editor as having been changed. PRETTYCOMFONT The font for the operand of a file package command. DEFAULTFONT The font for everything else. Note that not all combinations of fonts will be aesthetically pleasing (or even readable!) and the user may have to experiment to find a compatible set. Although in some implementations LAMBDAFONT et al. may be defined as variables, one should not set them directly, but should indicate what font is to be used for each class by calling the function FONTPROFILE: (FONTPROFILE PROFILE) [Function] Sets up the font classes as determined by PROFILE, a list of elements which defines the correspondence between font classes and specific fonts. Each element of PROFILE is a list of the form: (FONTCLASS FONT# DISPLAYFONT PRESSFONT INTERPRESSFONT) FONTCLASS is the font class name and FONT# is the font number for that class. For each font class name, the escape sequence will consist of FONTESCAPECHAR followed by the character code for the font number, e.g. A for font number 1, etc. If FONT# is NIL for any font class, the font class named DEFAULTFONT (which must always be specified) is used. Alternatively, if FONT# is the name of a previously defined font class, this font class will be equivalenced to the previously defined one. DISPLAYFONT, PRESSFONT, and INTERPRESSFONT are font specifications (of the form accepted by FONTCREATE) for the fonts to use when printing to the display and to Press and Interpress printers respectively. FONTPROFILE [Variable] This is the variable used to store the current font profile, in the form accepted by the function FONTPROFILE. Note that simply editing this value will not change the fonts used for the various font classes; it is necessary to execute (FONTPROFILE FONTPROFILE) to install the value of this variable. The process of printing with multiple fonts is affected by a large number of variables: FONTPROFILE, FILELINELENGTH, PRETTYLCOM, etc. To facilitate switching back and forth between various sets of values for the font variables, Interlisp supports the idea of named "font configurations" encapsulating the values of all relevant variables. To create a new font configuration, set all "relevant" variables to the values you want, and then call FONTNAME to save them (on the variable FONTDEFS) under a given name. To install a particular font configuration, call FONTSET giving it your name. To change the values in a saved font configuration, edit the value of the variable FONTDEFS. Note: The list of variables saved by FONTNAME is stored in the variable FONTDEFSVARS. This can be changed by the user. (FONTSET NAME) [Function] Installs font configuration for NAME. Also evaluates (FONTPROFILE FONTPROFILE) to install the font classes as specified in the new value of the variable FONTPROFILE. Generates an error if NAME not previously defined. FONTDEFSVARS [Variable] The list of variables to be packaged by a FONTNAME. Initially FONTCHANGEFLG, FILELINELENGTH, COMMENTLINELENGTH, FIRSTCOL, PRETTYLCOM, LISTFILESTR, and FONTPROFILE. FONTDEFS [Variable] An association list of font configurations. FONTDEFS is a list of elements of form (NAME . PARAMETER-PAIRS). To save a configuration on a file after performing a FONTNAME to define it, the user could either save the entire value of FONTDEFS, or use the ALISTS file package command to dump out just the one configuration. FONTESCAPECHAR [Variable] The character or string used to signal the start of a font escape sequence. FONTCHANGEFLG [Variable] If T, enables fonts when prettyprinting. If NIL, disables fonts. ALL indicates that all calls to CHANGEFONT are executed. LISTFILESTR [Variable] In Interlisp-10, passed to the operating system by LISTFILES. Can be used to specify subcommands to the LIST command, e.g. to establish correspondance between font number and font name. COMMENTLINELENGTH [Variable] Since comments are usually printed in a smaller font, COMMENTLINELENGTH is provided to offset the fact that Interlisp does not know about font widths. When FONTCHANGEFLG = T, CAR of COMMENTLINELENGTH is the linelength used to print short comments, i.e. those printed in the right margin, and CDR is the linelength used when printing full width comments. (CHANGEFONT FONT STREAM) [Function] Executes the operations on STREAM to change to the font FONT. For use in PRETTYPRINTMACROS. Image Objects 1 An Image Object is an object that includes information about an image, such as how to display it, how to print it, and how to manipulate it when it is included in a collection of images (such as a document). More generally, it enables you to include one kind of image, with its own semantics, layout rules, and editing paradigms, inside another kind of image. Image Objects provide a general-purpose interface between image users who want to manipulate arbitrary images, and image producers, who create images for use, say, in documents. Images are encapsulated inside a uniform barrier%the IMAGEOBJ data type. From the outside, you communicate to the image by calling a standard set of functions. For example, calling one function tells you how big the image is; calling another causes the image object to be displayed where you tell it, and so on. Anyone who wants to create images for general use can implement his own brand of IMAGEOBJ. IMAGEOBJs have been implemented (in library packages) for bitmaps, menus, annotations, graphs, and sketches. Image Objects were originally implemented to support inserting images into TEdit text files, but the facility is available for use by any tools that manipulate images. The Image Object interface allows objects to exist in TEdit documents and be edited with their own editor. It also provides a facility in which objects can be shift-selected (or "copy-selected") between TEdit and non-TEdit windows. For example, the Image Objects interface allows you to copy-select graphs from a Grapher window into a TEdit window. The source window (where the object comes from) does not have to know what sort of window the destination window (where the object is inserted) is, and the destination does not have to know where the insertion comes from. A new data type, IMAGEOBJ, contains the data and the procedures necessary to manipulate an object that is to be manipulated in this way. IMAGEOBJs are created with the function IMAGEOBJCREATE (below). Another new data type, IMAGEFNS, is a vector of the procedures necessary to define the behavior of a type of IMAGEOBJ. Grouping the operations in a separate data type allows multiple instances of the same type of image object to share procedure vectors. The data and procedure fields of an IMAGEOBJ have a uniform interface through the function IMAGEOBJPROP. IMAGEFNS are created with the function IMAGEFNSCREATE: (IMAGEFNSCREATE(IMAGEFNSCREATE (Function) NIL NIL ("27") 27) DISPLAYFN IMAGEBOXFN PUTFN GETFN COPYFN BUTTONEVENTINFN COPYBUTTONEVENTINFN WHENMOVEDFN WHENINSERTEDFN WHENDELETEDFN WHENCOPIEDFN WHENOPERATEDONFN PREPRINTFN %) [Function] Returns an IMAGEFNS object that contains the functions necessary to define the behavior of an IMAGEOBJ. The arguments DISPLAYFN through PREPRINTFN should all be function names to be stored as the "methods" of the IMAGEFNS. The purpose of each IMAGEFNS method is described below. Note: Image objects must be "registered" before they can be read by TEdit or HREAD. IMAGEFNSCREATE implicitly registers its GETFN argument. (IMAGEOBJCREATE(IMAGEOBJCREATE (Function) NIL NIL ("27") 27) OBJECTDATUM IMAGEFNS) [Function] Returns an IMAGEOBJ that contains the object datum OBJECTDATUM and the operations vector IMAGEFNS. OBJECTDATUM can be arbitrary data. (IMAGEOBJPROP(IMAGEOBJPROP (Function) NIL NIL ("27") 28) IMAGEOBJECT PROPERTY NEWVALUE) [NoSpread Function] Accesses and sets the properties of an IMAGEOBJ. Returns the current value of the PROPERTY property of the image object IMAGEOBJECT. If NEWVALUE is given, the property is set to it. IMAGEOBJPROP can be used on the system properties OBJECTDATUM, DISPLAYFN, IMAGEBOXFN, PUTFN, GETFN, COPYFN, BUTTONEVENTINFN, COPYBUTTONEVENTINFN, WHENOPERATEDONFN, and PREPRINTFN. Additionally, it can be used to save arbitrary properties on an IMAGEOBJ. (IMAGEFNSP(IMAGEFNSP (Function) NIL NIL ("27") 28) X) [Function] Returns X if X is an IMAGEFNS object, NIL otherwise. (IMAGEOBJP(IMAGEOBJP (Function) NIL NIL ("27") 28) X) [Function] Returns X if X is an IMAGEOBJ object, NIL otherwise. IMAGEFNS Methods Note: Many of the IMAGEFNS methods below are passed "host stream" arguments. The TEdit text editor passes the "text stream" (an object contain all of the information in the document being edited) as the "host stream" argument. Other editing programs that want to use image objects may want to pass the data structure being edited to the IMAGEFNS methods as the "host stream" argument. (DISPLAYFN IMAGEOBJ IMAGESTREAM IMAGESTREAMTYPE HOSTSTREAM) [IMAGEFNS Method] The DISPLAYFN method is called to display the object IMAGEOBJ at the current position on IMAGESTREAM. The type of IMAGESTREAM indicates whether the device is the display or some other image stream. Note: When the DISPLAYFN method is called, the offset and clipping regions for the stream are set so the object's image is at (0,0), and only that image area can be modified. (IMAGEBOXFN IMAGEOBJ IMAGESTREAM CURRENTX RIGHTMARGIN) [IMAGEFNS Method] The IMAGEBOXFN method should return the size of the object as an IMAGEBOX, which is a data structure that describes the image laid down when an IMAGEOBJ is displayed in terms of width, height, and descender height. An IMAGEBOX has four fields: XSIZE, YSIZE, YDESC, and XKERN. XSIZE and YSIZE are the width and height of the object image. YDESC and XKERN give the position of the baseline and the left edge of the image relative to where you want to position it. For characters, the YDESC is the descent (height of the descender) and the XKERN is the amount of left kerning (note: TEdit doesn't support left kerning). The IMAGEBOXFN looks at the type of the stream to determine the output device if the object's size changes from device to device. (For example, a bit-map object may specify a scale factor that is ignored when the bit map is displayed on the screen.) CURRENTX and RIGHTMARGIN allow an object to take account of its environment when deciding how big it is. If these fields are not available, they are NIL. Note: TEdit calls the IMAGEBOXFN only during line formatting, then caches the IMAGEBOX as the BOUNDBOX property of the IMAGEOBJ. This avoids the need to call the IMAGEBOXFN when incomplete position and margin information is available. (PUTFN IMAGEOBJ FILESTREAM) [IMAGEFNS Method] The PUTFN method is called to save the object on a file. It prints a description on FILESTREAM that, when read by the corresponding GETFN method (see below), regenerates the image object. (TEdit and HPRINT take care of writing out the name of the GETFN.) (GETFN FILESTREAM) [IMAGEFNS Method] The GETFN method is called when the object is encountered on the file during input. It reads the description that was written by the PUTFN method and returns an IMAGEOBJ. (COPYFN IMAGEOBJ SOURCEHOSTSTREAM TARGETHOSTSTREAM) [IMAGEFNS Method] The COPYFN method is called during a copy-select operation. It should return a copy of IMAGEOBJ. If it returns the litatom DON'T, copying is suppressed. (BUTTONEVENTINFN IMAGEOBJ WINDOWSTREAM SELECTION RELX RELY WINDOW HOSTSTREAM BUTTON) [IMAGEFNS Method] The BUTTONEVENTINFN method is called when you press a mouse button inside the object. The BUTTONEVENTINFN decides whether or not to handle the button, to track the cursor in parallel with mouse movement, and to invoke selections or edits supported by the object (but see the COPYBUTTONEVENTINFN method below). If the BUTTONEVENTINFN returns NIL, TEdit treats the button press as a selection at its level. Note that when this function is first called, a button is down. The BUTTONEVENTINFN should also support the button-down protocol to descend inside of any composite objects with in it. In most cases, the BUTTONEVENTINFN relinquishes control (i.e., returns) when the cursor leaves its object's region. When the BUTTONEVENTINFN is called, the window's clipping region and offsets have been changed so that the lower-left corner of the object's image is at (0,0), and only the object's image can be changed. The selection is available for changing to fit your needs; the mouse button went down at (RELX,RELY) within the object's image. You can affect how TEdit treats the selection by returning one of several values. If you return NIL, TEdit forgets that you selected an object; if you return the atom DON'T, TEdit doesn't permit the selection; if you return the atom CHANGED, TEdit updates the screen. Use CHANGED to signal TEdit that the object has changed size or will have side effects on other parts of the screen image. (COPYBUTTONEVENTINFN IMAGEOBJ WINDOWSTREAM) [IMAGEFNS Method] The COPYBUTTONEVENTINFN method is called when you button inside an object while holding down a copy key. Many of the comments about BUTTONEVENTINFN apply here too. Also, see the discussion below about copying image objects between windows. (WHENMOVEDFN IMAGEOBJ TARGETWINDOWSTREAM SOURCEHOSTSTREAM TARGETHOSTSTREAM) [IMAGEFNS Method] The WHENMOVEDFN method provides hooks by which the object is notified when TEdit performs an operation (MOVEing) on the whole object. It allows objects to have side effects. (WHENINSERTEDFN IMAGEOBJ TARGETWINDOWSTREAM SOURCEHOSTSTREAM TARGETHOSTSTREAM) [IMAGEFNS Method] The WHENINSERTEDFN method provides hooks by which the object is notified when TEdit performs an operation (INSERTing) on the whole object. It allows objects to have side effects. (WHENDELETEDFN IMAGEOBJ TARGETWINDOWSTREAM) [IMAGEFNS Method] The WHENDELETEDFN method provides hooks by which the object is notified when TEdit performs an operation (DELETEing) on the whole object. It allows objects to have side effects. (WHENCOPIEDFN IMAGEOBJ TARGETWINDOWSTREAM SOURCEHOSTSTREAM TARGETHOSTSTREAM) [IMAGEFNS Method] The WHENCOPIEDFN method provides hooks by which the object is notified when TEdit performs an operation (COPYing) on the whole object. The WHENCOPIEDFN method is called in addition to (and after) the COPYFN method above. It allows objects to have side effects. (WHENOPERATEDONFN IMAGEOBJ WINDOWSTREAM HOWOPERATEDON SELECTION HOSTSTREAM) [IMAGEFNS Method] The WHENOPERATEDONFN method provides a hook for edit operations. HOWOPERATEDON should be one of SELECTED, DESELECTED, HIGHLIGHTED, and UNHILIGHTED. The WHENOPERATEDONFN differs from the BUTTONEVENTINFN because it is called when you extend a selection through the object. That is, the object is treated in toto as a TEdit character. HIGHLIGHTED refers to the selection being highlighted on the screen, and UNHIGHLIGHTED means that the highlighting is being turned off. (PREPRINTFN IMAGEOBJ) [IMAGEFNS Method] The PREPRINTFN method is called to convert the object into something that can be printed for inclusion in documents. It returns an object that the receiving window can print (using either PRIN1 or PRIN2,its choice) to obtain a character representation of the object. If the PREPRINTFN method is NIL, the OBJECTDATUM field of IMAGEOBJ itself is used. TEdit uses this function when you indicate that you want to print the characters from an object rather than the object itself (presumably using PRIN1 case). Registering Image Objects Each legitimate GETFN needs to be known to the system, to prevent various Trojan-horse problems and to allow the automatic loading of the supporting code for infrequently used IMAGEOBJs. To this end, there is a global list, IMAGEOBJGETFNS, that contains an entry for each GETFN. The existence of the entry marks the GETFN as legitimate; the entry itself is a property list, which can hold information about the GETFN. No action needs to be taken for GETFNs that are currently in use: the function IMAGEFNSCREATE automatically adds its GETFN argument to the list. However, packages that support obsolete versions of objects may need to explicitly add the obsolete GETFNs. For example, TEdit supports bit-map IMAGEOBJs. Recently, a change was made in the format in which objects are stored; to retain compatibility with the old object format, there are now two GETFNs. The current GETFN is automatically on the list, courtesy of IMAGEFNSCREATE. However, the code file that supports the old bit-map objects contains the clause: (ADDVARS (IMAGEOBJGETFNS (OLDGETFNNAME))), which adds the old GETFN to IMAGEOBJGETFNS. For a given GETFN, the entry on IMAGEOBJGETFNS may be a property list of information. Currently the only recognized property is FILE. FILE is the name of the file that can be loaded if the GETFN isn't defined. This file should define the GETFN, along with all the other functions needed to support that kind of IMAGEOBJ. For example, the bit-map IMAGEOBJ implemented by TEdit use the GETFN BMOBJ.GETFN2. Its entry on IMAGEOBJGETFNS is (BMOBJ.GETFN2 FILE IMAGEOBJ), indicating that the support code for bit-map image objects resides on the file IMAGEOBJ, and that the GETFN for them is BMOBJ.GETFN2. This makes it possible to have entries for GETFNs whose supporting code isn't loaded%you might, for instance, have your init file add entries to IMAGEOBJGETFNS for the kinds of image objects you commonly use. The system's default reading method will automatically load the code when necessary. Reading and Writing Image Objects on Files Image Objects can be written out to files using HPRINT and read back using HREAD. The following functions can also be used: (WRITEIMAGEOBJ(WRITEIMAGEOBJ (Function) NIL NIL ("27") 31) IMAGEOBJ STREAM) [Function] Prints (using PRIN2) a call to READIMAGEOBJ, then calls the PUTFN for IMAGEOBJ to write it onto STREAM. During input, then, the call to READIMAGEOBJ is read and evaluated; it in turn reads back the object's description, using the appropriate GETFN. (READIMAGEOBJ(READIMAGEOBJ (Function) NIL NIL ("27") 31) STREAM GETFN NOERROR) [Function] Reads an IMAGEOBJ from STREAM, starting at the current file position. Uses the function GETFN after validating it (and loading support code, if necessary). If the GETFN can't be validated or isn't defined, READIMAGEOBJ returns an "encapsulated image object", an IMAGEOBJ that safely encapsulates all of the information in the image object. An encapsulated image object displays as a rectangle that says, "Unknown IMAGEOBJ Type" and lists the GETFN's name. Selecting an encapsulated image object with the mouse causes another attempt to read the object from the file; this is so you can load any necessary support code and then get to the object. Warning: You cannot save an encapsulated image object on a file because there isn't enough information to allow copying the description to the new file from the old one. If NOERROR is non-NIL, READIMAGEOBJ returns NIL if it can't successfully read the object. Copying Image Objects Between Windows Copying between windows is implemented as follows: If a button event occurs in a window when a copy key is down, the window's COPYBUTTONEVENTFN window property is called. If this window supports copy-selection, it should track the mouse, indicating the item to be copied. When the button is released, the COPYBUTTONEVENTFN should create an image object out of the selected information, and call COPYINSERT to insert it in the current TTY window. COPYINSERT calls the COPYINSERTFN window property of the TTY window to insert this image object. Therefore, both the source and destination windows can determine how they handle copying image objects. If the COPYBUTTONEVENTFN of a window is NIL, the BUTTONEVENTFN is called instead when a button event occurs in the window when a copy key is down, and copying from that window is not supported. If the COPYINSERTFN of the TTY window is NIL, COPYINSERT will turn the image object into a string (by calling the PREPRINTFN method of the image object) and insert it by calling BKSYSBUF. COPYBUTTONEVENTFN [Window Property] The COPYBUTTONEVENTFN of a window is called (if it exists) when a button event occurs in the window and a copy key is down. If no COPYBUTTONEVENTFN exists, the BUTTONEVENTFN is called. COPYINSERTFN [Window Property] The COPYINSERTFN of the "destination" window is called by COPYINSERT to insert something into the destination window. It is called with two arguments: the object to be inserted and the destination window. The object to be inserted can be a character string, an IMAGEOBJ, or a list of IMAGEOBJs and character strings. As a convention, the COPYINSERTFN should call BKSYSBUF if the object to be inserted insert is a character string. (COPYINSERT IMAGEOBJ) [Function] COPYINSERT inserts IMAGEOBJ into the window that currently has the TTY. If the current TTY window has a COPYINSERTFN, it is called, passing it IMAGEOBJ and the window as arguments. If no COPYINSERTFN exists and if IMAGEOBJ is an image object, BKSYSBUF is called on the result of calling its PREPRINTFN on it. If IMAGEOBJ is not an image object, it is simply passed to BKSYSBUF . In this case, BKSYSBUF will call PRIN2 with a read table taken from the process associated with the TTY window. A window that wishes to use PRIN1 or a different read table must provide its own COPYINSERTFN to do this. Implementation of Image Streams 1 Interlisp does all image creation through a set of functions and data structures for device-independent graphics, known popularly as DIG. DIG is implemented through the use of a special type of stream, known as an image stream. An image stream, by convention, is any stream that has its IMAGEOPS field (described in detail below) set to a vector of meaningful graphical operations. Using image streams, you can write programs that draw and print on an output stream without regard to the underlying device, be it a window, a disk, or a printer. To define a new image stream type, it is necessary to put information on the variable IMAGESTREAMTYPES: IMAGESTREAMTYPES [Variable] This variable describes how to create a stream for a given image stream type. The value of IMAGESTREAMTYPES is an association list, indexed by the image stream type (e.g., DISPLAY, INTERPRESS, etc.). The format of a single association list item is: (IMAGETYPE (OPENSTREAM OPENSTREAMFN) (FONTCREATE FONTCREATEFN) (FONTSAVAILABLE FONTSAVAILABLEFN)) OPENSTREAMFN, FONTCREATEFN, and FONTSAVAILABLEFN are "image stream methods," device-dependent functions used to implement generic image stream operations. For Interpress image streams, the association list entry is: (INTERPRESS (OPENSTREAM OPENIPSTREAM) (FONTCREATE \CREATEINTERPRESSFONT) (FONTSAVAILABLE \SEARCHINTERPRESSFONTS)) (OPENSTREAMFN FILE OPTIONS) [Image Stream Method] FILE is the file name as it was passed to OPENIMAGESTREAM, and OPTIONS is the OPTIONS property list passed to OPENIMAGESTREAM. The result must be a stream of the appropriate image type. (FONTCREATEFN FAMILY SIZE FACE ROTATION DEVICE) [Image Stream Method] FAMILY is the family name for the font, e.g., MODERN. SIZE is the body size of the font, in printer's points. FACE is a three-element list describing the weight, slope, and expansion of the face desired, e.g., (MEDIUM ITALIC EXPANDED). ROTATION is how much the font is to be rotated from the normal orientation, in minutes of arc. For example, to print a landscape page, fonts have the rotation 5400 (90 degrees). The function's result must be a FONTDESCRIPTOR with the fields filled in appropriately. (FONTSAVAILABLEFN FAMILY SIZE FACE ROTATION DEVICE) [Image Stream Method] This function returns a list of all fonts agreeing with the FAMILY, SIZE, FACE, and ROTATION arguments; any of them may be wild-carded (i.e., equal to *, which means any value is acceptable). Each element of the list should be a quintuple of the form (FAMILY SIZE FACE ROTATION DEVICE). Where the function looks is an implementation decision: the FONTSAVAILABLEFN for the display device looks at DISPLAYFONTDIRECTORIES, the Interpress code looks on INTERPRESSFONTDIRECTORIES, and implementors of new devices should feel free to introduce new search path variables. As indicated above, image streams use a field that no other stream uses: IMAGEOPS. IMAGEOPS is an instance of the IMAGEOPS data type and contains a vector of the stream's graphical methods. The methods contained in the IMAGEOPS object can make arbitrary use of the stream's IMAGEDATA field, which is provided for their use, and may contain any data needed. IMAGETYPE [IMAGEOPS Field] Value is the name of an image type. Monochrome display streams have an IMAGETYPE of DISPLAY; color display streams are identified as (COLOR DISPLAY). The IMAGETYPE field is informational and can be set to anything you choose. IMFONTCREATE [IMAGEOPS Field] Value is the device name to pass to FONTCREATE when fonts are created for the stream. The remaining fields are all image stream methods, whose value should be a device-dependent function that implements the generic operation. Most methods are called by a similarly-named function, e.g. the function DRAWLINE calls the IMDRAWLINE method. All coordinates that refer to points in a display device's space are measured in the device's units. (The IMSCALE method provides access to a device's scale.) For arguments that have defaults (such as the BRUSH argument of DRAWCURVE), the default is substituted for the NIL argument before it is passed to the image stream method. Therefore, image stream methods do not have to handle defaults. (IMCLOSEFN STREAM) [Image Stream Method] Called before a stream is closed with CLOSEF. This method should flush buffers, write header or trailer information, etc. (IMDRAWLINE STREAM X1 Y1 X2 Y2 WIDTH OPERATION COLOR DASHING) [Image Stream Method] Draws a line of width WIDTH from (X1, Y1) to (X2, Y2). See DRAWLINE. (IMDRAWCURVE STREAM KNOTS CLOSED BRUSH DASHING) [Image Stream Method] Draws a curve through KNOTS. See DRAWCURVE. (IMDRAWCIRCLE STREAM CENTERX CENTERY RADIUS BRUSH DASHING) [Image Stream Method] Draws a circle of radius RADIUS around (CENTERX, CENTERY). See DRAWCIRCLE. (IMDRAWELLIPSE STREAM CENTERX CENTERY SEMIMINORRADIUS SEMIMAJORRADIUS ORIENTATION BRUSH DASHING) [Image Stream Method] Draws an ellipse around (CENTERX, CENTERY). See DRAWELLIPSE. (IMFILLPOLYGON STREAM POINTS TEXTURE) [Image Stream Method] Fills in the polygon outlined by POINTS on the image stream STREAM, using the texture TEXTURE. See FILLPOLYGON. (IMFILLCIRCLE STREAM CENTERX CENTERY RADIUS TEXTURE) [Image Stream Method] Draws a circle filled with texture TEXTURE around (CENTERX, CENTERY). See FILLCIRCLE. (IMBLTSHADE TEXTURE STREAM DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT OPERATION CLIPPINGREGION) [Image Stream Method] The texture-source case of BITBLT. DESTINATIONLEFT, DESTINATIONBOTTOM, WIDTH, HEIGHT, and CLIPPINGREGION are measured in STREAM's units. This method is invoked by the functions BITBLT and BLTSHADE. (IMBITBLT SOURCEBITMAP SOURCELEFT SOURCEBOTTOM STREAM DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT SOURCETYPE OPERATION TEXTURE CLIPPINGREGION CLIPPEDSOURCELEFT CLIPPEDSOURCEBOTTOM SCALE) [Image Stream Method] Contains the bit-map-source cases of BITBLT. SOURCELEFT, SOURCEBOTTOM, CLIPPEDSOURCELEFT, CLIPPEDSOURCEBOTTOM, WIDTH, and HEIGHT are measured in pixels; DESTINATIONLEFT, DESTINATIONBOTTOM, and CLIPPINGREGION are in the units of the destination stream. (IMSCALEDBITBLT SOURCEBITMAP SOURCELEFT SOURCEBOTTOM STREAM DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT SOURCETYPE OPERATION TEXTURE CLIPPINGREGION CLIPPEDSOURCELEFT CLIPPEDSOURCEBOTTOM SCALE) [Image Stream Method] A scaled version of IMBITBLT. Each pixel in SOURCEBITMAP is replicated SCALE times in the X and Y directions; currently, SCALE must be an integer. (IMMOVETO STREAM X Y) [Image Stream Method] Moves to (X,Y). This method is invoked by the function MOVETO. If IMMOVETO is not supplied, a default method composed of calls to the IMXPOSITION and IMYPOSITION methods is used. (IMSTRINGWIDTH STREAM STR RDTBL) [Image Stream Method] Returns the width of string STR in STREAM's units, using STREAM's current font. This is envoked when STRINGWIDTH is passed a stream as its FONT argument. If IMSTRINGWIDTH is not supplied, it defaults to calling STRINGWIDTH on the default font of STREAM. (IMCHARWIDTH STREAM CHARCODE) [Image Stream Method] Returns the width of character CHARCODE in STREAM's units, using STREAM's current font. This is invoked when CHARWIDTH is passed a stream as its FONT argument. If IMCHARWIDTH is not supplied, it defaults to calling CHARWIDTH on the default font of STREAM. (IMCHARWIDTHY STREAM CHARCODE) [Image Stream Method] Returns the Y componant of the width of character CHARCODE in STREAM's units, using STREAM's current font. This is envoked when CHARWIDTHY is passed a stream as its FONT argument. If IMCHARWIDTHY is not supplied, it defaults to calling CHARWIDTHY on the default font of STREAM. (IMBITMAPSIZE STREAM BITMAP DIMENSION) [Image Stream Method] Returns the size that BITMAP will be when BITBLTed to STREAM, in STREAM's units. DIMENSION can be one of WIDTH, HEIGHT, or NIL, in which case the dotted pair (WIDTH . HEIGHT) will be returned. This is envoked by BITMAPIMAGESIZE. If IMBITMAPSIZE is not supplied, it defaults to a method that multiplies the bitmap height and width by the scale of STREAM. (IMNEWPAGE STREAM) [Image Stream Method] Causes a new page to be started. The X position is set to the left margin, and the Y position is set to the top margin plus the linefeed. If not supplied, defaults to (\OUTCHAR STREAM (CHARCODE ^L)). Envoked by DSPNEWPAGE. (IMTERPRI STREAM) [Image Stream Method] Causes a new line to be started. The X position is set to the left margin, and the Y position is set to the current Y position plus the linefeed. If not supplied, defaults to (\OUTCHAR STREAM (CHARCODE EOL)). Envoked by TERPRI. (IMRESET(IMRESET (Image Stream Method) NIL NIL ("27") 35) STREAM) [Image Stream Method] Resets the X and Y position of STREAM. The X coordinate is set to its left margin; the Y coordinate is set to the top of the clipping region minus the font ascent. Envoked by DSPRESET. The following methods all have corresponding DSPxx functions (e.g., IMYPOSITION corresponds to DSPYPOSITION) that invoke them. They also have the property of returning their previous value; when called with NIL they return the old value without changing it. (IMCLIPPINGREGION(IMCLIPPINGREGION (Image Stream Method) NIL NIL ("27") 35) STREAM REGION) [Image Stream Method] Sets a new clipping region on STREAM. (IMXPOSITION(IMXPOSITION (Image Stream Method) NIL NIL ("27") 36) STREAM XPOSITION) [Image Stream Method] Sets the X-position on STREAM. (IMYPOSITION(IMYPOSITION (Image Stream Method) NIL NIL ("27") 36) STREAM YPOSITION) [Image Stream Method] Sets a new Y-position on STREAM. (IMFONT(IMFONT (Image Stream Method) NIL NIL ("27") 36) STREAM FONT) [Image Stream Method] Sets STREAM's font to be FONT. (IMLEFTMARGIN(IMLEFTMARGIN (Image Stream Method) NIL NIL ("27") 36) STREAM LEFTMARGIN) [Image Stream Method] Sets STREAM's left margin to be LEFTMARGIN. The left margin is defined as the X-position set after the new line. (IMRIGHTMARGIN(IMRIGHTMARGIN (Image Stream Method) NIL NIL ("27") 36) STREAM RIGHTMARGIN) [Image Stream Method] Sets STREAM's right margin to be RIGHTMARGIN. The right margin is defined as the maximum X-position at which characters are printed; printing beyond it causes a new line. (IMTOPMARGIN(IMTOPMARGIN (Image Stream Method) NIL NIL ("27") 36) STREAM YPOSITION) [Image Stream Method] Sets STREAM's top margin (the Y-position of the tops of characters that is set after a new page) to be YPOSITION. (IMBOTTOMMARGIN(IMBOTTOMMARGIN (Image Stream Method) NIL NIL ("27") 36) STREAM YPOSITION) [Image Stream Method] Sets STREAM's bottom margin (the Y-position beyond which any printing causes a new page) to be YPOSITION. (IMLINEFEED(IMLINEFEED (Image Stream Method) NIL NIL ("27") 36) STREAM DELTA) [Image Stream Method] Sets STREAM's line feed distance (distance to move vertically after a new line) to be DELTA. (IMSCALE(IMSCALE (Image Stream Method) NIL NIL ("27") 36) STREAM SCALE) [Image Stream Method] Returns the number of device points per screen point (a screen point being ~1/72 inch). SCALE is ignored. (IMSPACEFACTOR(IMSPACEFACTOR (Image Stream Method) NIL NIL ("27") 36) STREAM FACTOR) [Image Stream Method] Sets the amount by which to multiply the natural width of all following space characters on STREAM; this can be used for the justification of text. The default value is 1. For example, if the natural width of a space in STREAM's current font is 12 units, and the space factor is set to two, spaces appear 24 units wide. The values returned by STRINGWIDTH and CHARWIDTH are also affected. (IMOPERATION(IMOPERATION (Image Stream Method) NIL NIL ("27") 36) STREAM OPERATION) [Image Stream Method] Sets the default BITBLT OPERATION argument. (IMBACKCOLOR(IMBACKCOLOR (Image Stream Method) NIL NIL ("27") 36) STREAM COLOR) [Image Stream Method] Sets the background color of STREAM. (IMCOLOR (IMCOLOR% (Image Stream Method) NIL NIL ("27") 36)STREAM COLOR) [Image Stream Method] Sets the default color of STREAM. In addition to the IMAGEOPS methods described above, there are two other important methods, which are contained in the stream itself. These fields can be installed using a form like (replace (STREAM OUTCHARFN) of STREAM with (FUNCTION MYOUTCHARFN)). Note: You need to have loaded the Interlisp-D system declarations to manipulate the fields of STREAMs. The declarations can be loaded by loading the Lisp Library package SYSEDIT. (STRMBOUTFN(STRMBOUTFN (Stream Method) NIL NIL ("27") 37) STREAM CHARCODE) [Stream Method] The function called by BOUT. (OUTCHARFN(OUTCHARFN (Stream Method) NIL NIL ("27") 37) STREAM CHARCODE) [Stream Method] The function that is called to output a single byte. This is like STRMBOUTFN, except for being one level higher: it is intended for text output. Hence, this function should convert (CHARCODE EOL) into the stream's actual end-of-line sequence and should adjust the stream's CHARPOSITION appropriately before invoking the stream's STRMBOUTFN (by calling BOUT) to actually put the character. Defaults to \FILEOUTCHARFN, which is probably incorrect for an image stream. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "26-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "26-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "26-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "26-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "26-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "26-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))&F PAGEHEADING RIGHTPAGE,//HH,xx,2`~~,0T,,HH,0T,002HH5xlx-T,HH +,HH306 +T-T0T-T3(T,,25l5l5l/xx/xx3HHT306 +T,ll,ll,ll@ PAGEHEADINGLEFTBACKTTERMINAL +PALATINO TITAN +TITAN TITAN TITAN TITAN TITAN +CLASSIC +CLASSIC + HELVETICA +CLASSIC +MODERN +MODERNMODERN +&  IM.CHAP.GETFNMODERN +!  HRULE.GETFNMODERN +  HRULE.GETFNMODERN +  !(O)4"  +!  +  +    j?"  +  +"  ".IM.INDEX.GETFNTITAN +  + + +L" *IM.INDEX.GETFNTITAN +  + + +y "/IM.INDEX.GETFNTITAN +   " +(IM.INDEX.GETFNTITAN +  7 " *IM.INDEX.GETFNTITAN + ,  ".IM.INDEX.GETFNTITAN + C! $    "%IM.INDEX.GETFNTITAN +    U F +*"  *  "%IM.INDEX.GETFNTITAN +  " )IM.INDEX.GETFNTITAN +    " *IM.INDEX.GETFNTITAN +  "  (" 'IM.INDEX.GETFNMODERN +   3 $</,_" +(IM.INDEX.GETFNTITAN + (2" *IM.INDEX.GETFNMODERN + +   +    " *IM.INDEX.GETFNMODERN + +" *IM.INDEX.GETFNTITAN + 1  ,   %     " )IM.INDEX.GETFNTITAN +    ) +" +(IM.INDEX.GETFNTITAN + 6 "$IM.INDEX.GETFNMODERN + X >/=@( BMOBJ.GETFN3MODERN + SZX BMOBJ.GETFN3PALATINO ;is, +g + + < 3w'L   +"5IM.INDEX.GETFNCLASSIC + :37"&IM.INDEX.GETFNCLASSIC +    %F + + Z" 'IM.INDEX.GETFNTITAN + ) S +V, BMOBJ.GETFN3MODERN +  HRULE.GETFNMODERN + +'?o cf)"-IM.INDEX.GETFNCLASSIC + *   37  }  ! $_  9  + +/ +2 <+ +v&J.N> B B; W ?W6 &(%#" *IM.INDEX.GETFNCLASSIC + H  + "-IM.INDEX.GETFNCLASSIC +   !".IM.INDEX.GETFNCLASSIC +   HRULE.GETFNMODERN +"0IM.INDEX.GETFNTITAN + "&IM.INDEX.GETFNTITAN + U)! ++%#$p" +IM.INDEX.GETFNTITAN + )o".IM.INDEX.GETFNTITAN +   4-U" ,IM.INDEX.GETFNTITAN +   *{"-IM.INDEX.GETFNCLASSIC + 3-u< +" +IM.INDEX.GETFNCLASSIC + F " *IM.INDEX.GETFNTITAN + . +b"*IM.INDEX.GETFNTITAN + / 7Z" ,IM.INDEX.GETFNMODERN +  D  %2*"'IM.INDEX.GETFNTITAN + &Z 0 # " k|v  X"   %.a"  .m$  HRULE.GETFNMODERN +a"   ' /"   ' /" + "  +," R )  - <(  HRULE.GETFNMODERN +l" + ^ /1 3"0    +  + 20  + )( +   3   + i: +? + 2   0 +    73X3WL + c  < BMOBJ.GETFN3 < BMOBJ.GETFN3  BMOBJ.GETFN3 J +   +& BMOBJ.GETFN3MODERN +  BMOBJ.GETFN3  BMOBJ.GETFN3  BMOBJ.GETFN3  BMOBJ.GETFN3MODERN +  BMOBJ.GETFN3  BMOBJ.GETFN3  BMOBJ.GETFN3  BMOBJ.GETFN3MODERN +  BMOBJ.GETFN3  BMOBJ.GETFN3  BMOBJ.GETFN3 " [ ;  ""   !  HRULE.GETFNMODERN +F- +"  + + + +$& + +  + +; &  t + +"   + +$  A'IM.INDEX.GETFN" (5! "  *52$   HRULE.GETFNMODERN + +, MSxtV>"  !<?Q%# #5$!@% BMOBJ.GETFN3MODERN +" + , <"@ 2 +  +#4^ +"  P( Y<>"    +f+ ~ +>  + +"  .  HRULE.GETFNMODERN +" /  " *IM.INDEX.GETFNMODERN +" $^76Q  ~=  BMOBJ.GETFN3TERMINAL +"  * b      +   +) +6 +,  BMOBJ.GETFN3PALATINO >%" + %#"  " + /8"   ,  HRULE.GETFNMODERN +/:<.'" - +7?  Y P"  /  + )0" +   &" " + &" .#" 9 " ;   HRULE.GETFNMODERN +'"  * Z" +   " + " + i   +  o +  o 7*a" + ?  +"  O +  "  | >   e"  # / +  7 +) %:  "     [  I   HRULE.GETFNMODERN +96%&:  +]#*$'$$$)sM +B ;1  SKIO.GETFN.2PALATINO " + 33+;K 84V +`   +l  +RF +1 +^ +. + + +9 + z "  " 1?X)3 G +#*4 + + 0 + x~"  + + + +5 K/T 9.& G" 0G +-6H` x +% 7  +, .F +n" %0w;" 7:<"  A2 =" +    K "   ' J  + + +"   @y  yH"  A "  * + *  :" ' )= +"0IM.INDEX.GETFNMODERN + $3IM.INDEX.GETFNO P3IM.INDEX.GETFNPALATINO +d  HRULE.GETFNMODERN + +E +"U"7' >"X"  HRULE.GETFNMODERN +  kye: B   +z H  N +, +I 5 ! + "  *p7 cU* >u  2 +g" b   *X  +gHj& $"  L " *   +  "- 9> >"L" ) +" 3 -N"6V];" +  HRULE.GETFNMODERN +j_q  N/ "-IM.INDEX.GETFNTITAN + ? K   +CN "-IM.INDEX.GETFNCLASSIC +      " +IM.INDEX.GETFNCLASSIC + '$ & &   + +C" (IM.INDEX.GETFNTITAN +   " (IM.INDEX.GETFNCLASSIC +    9("  /  (  I " + )  +3GC02K + ~ +.$ +?"  L +&?*" + }" * N" B H yR  D=!p"  n`"  =  YE" = YE"     YE"  =  Y 18" 8 .  +   > 4" +   +I +   + )"(Z *|(+W) T3-DM  +V +T  0-" ,IM.INDEX.GETFNCLASSIC +   # ^" +IM.INDEX.GETFNCLASSIC +  <?& ,  +& I + + +      +: +6"n   "  * +/  <" +  + (  ( + 0 >&0    HRULE.GETFNMODERN +W;V "\A +;  + + +'    + +& +,"  &>"  (5a*" <;e <![Ib/ J" H +  ?" $ +(  +u]  &{"  &O" +  + + + + + + +"  ! "  +  +"  P "  ! "  %#  +" + V 3" %G-  " + ++<  "   +'  ) "  '  ) "   %' + ) +"     ! f"  &-T +" &- ; "1IM.INDEX.GETFNMODERN +   +X-  e0":IM.INDEX.GETFNMODERN + " 5IM.INDEX.GETFNMODERN +   " 5IM.INDEX.GETFNMODERN +   "0IM.INDEX.GETFNMODERN +  +" 6IM.INDEX.GETFNMODERN +  +%"" 7IM.INDEX.GETFNMODERN +  .Q" 5IM.INDEX.GETFNMODERN + H "8IM.INDEX.GETFNMODERN + += " +4IM.INDEX.GETFNMODERN + + K"1IM.INDEX.GETFNMODERN + Y " 7IM.INDEX.GETFNMODERN + +  \|v  " 5IM.INDEX.GETFNMODERN + +  " 5IM.INDEX.GETFNMODERN + + " 3IM.INDEX.GETFN  dG" +.IM.INDEX.GETFNMODERN + " -IM.INDEX.GETFNMODERN + C +kO , + .3/s z \ No newline at end of file diff --git a/docs/turpin-irm/IRM-3/27-WINDOWS.TEDIT b/docs/turpin-irm/IRM-3/27-WINDOWS.TEDIT new file mode 100644 index 00000000..3e9589d4 Binary files /dev/null and b/docs/turpin-irm/IRM-3/27-WINDOWS.TEDIT differ diff --git a/docs/turpin-irm/IRM-3/28-HARDCOPY.TEDIT b/docs/turpin-irm/IRM-3/28-HARDCOPY.TEDIT new file mode 100644 index 00000000..06d7b7ca --- /dev/null +++ b/docs/turpin-irm/IRM-3/28-HARDCOPY.TEDIT @@ -0,0 +1,27 @@ +INTERLISP-D REFERENCE MANUAL HARDCOPY FACILITIES "29"28. HARDCOPY FACILITIES 2 Interlisp-D includes facilities for generating hardcopy in "Interpress" format and "Press" format. Interpress is a file format used for communicating documents to Xerox Network System printers such as the Xerox 8044 and Xerox 5700. Press is a file format used for communicating documents to Xerox laser Xerographic printers known by the names "Dover", "Spruce", "Penguin", and "Raven". There are also library packages available for supporting other types of printer formats (4045, FX-80, C150, etc.). The hardcopy facilities are designed to allow the user to support new types of printers with minimal changes to the user interface. Files can be in a number of formats, including Interpress files, plain text files, and formatted Tedit files. In order to print a file on a given printer, it is necessary to identify the format of the file, convert the file to a format that the printer can accept, and transmit it. Rather than require that the user explicitly determine file types and do the conversion, the Interlisp-D hardcopy functions generate Interpress or other format output depending on the appropriate choice for the designated printer. The hardcopy functions use the variables PRINTERTYPES and PRINTFILETYPES (described below) to determine the type of a file, how to convert it for a given printer, and how to send it. By changing these variables, the user can define other kinds of printers and print to them using the normal hardcopy functions. (SEND.FILE.TO.PRINTER(SEND.FILE.TO.PRINTER (Function) NIL NIL ("29") 1) FILE HOST PRINTOPTIONS) [Function] The function SEND.FILE.TO.PRINTER causes the file FILE to be sent to the printer HOST. If HOST is NIL, the first host in the list DEFAULTPRINTINGHOST which can print FILE is used. PRINTOPTIONS is a property list of the form (PROP1 VALUE1 PROP2 VALUE2 ...). The properties accepted depends on the type of printer. For Interpress printers, the following properties are accepted: DOCUMENT.NAME The document name to appear on the header page (a string). Default is the full name of the file. DOCUMENT.CREATION.DATE The creation date to appear on the header page (a Lisp integer date, such as returned by IDATE). The default value is the creation date of the file. SENDER.NAME The name of the sender to appear on the header page (a string). The default value is the name of the user. RECIPIENT.NAME The name of the recipient to appear on the header page (a string). The default is none. MESSAGE An additional message to appear on the header page (a string). The default is none. #COPIES The number of copies to be printed. The default value is 1. PAGES.TO.PRINT The pages of the document that should be printed, represented as a list (FIRSTPAGE# LASTPAGE#). For example, if this option is (3 5), this specifies that pages 3 through 5, inclusive, should be printed. Note that the page numbering used for this purpose has no connection to any page numbers that may be printed on the document. The default is to print all of the pages in the document. MEDIUM The medium on which the master is to be printed. If omitted, this defaults to the value of NSPRINT.DEFAULT.MEDIUM, as follows: NIL means to use the printer's default; T means to use the first medium reported available by the printer; any other value must be a Courier value of type MEDIUM. The format of this type is a list (PAPER (KNOWN.SIZE TYPE)) or (PAPER (OTHER.SIZE (WIDTH LENGTH))). The paper TYPE is one of US.LETTER, US.LEGAL, A0 through A10, ISO.B0 through ISO.B10, and JIS.B0 through JIS.B10. For users who use A4 paper exclusively, it should be sufficient to set NSPRINT.DEFAULT.MEDIUM to (PAPER (KNOWN.SIZE "A4")). When using different paper sizes, it may be necessary to reset the variable DEFAULTPAGEREGION, the region on the page used for printing (measured in micas from the lower-left corner). STAPLE? True if the document should be stapled. #SIDES 1 or 2 to indicate that the document should be printed on one or two sides, respectively. The default is the value of EMPRESS#SIDES. PRIORITY The priority of this print request, one of LOW, NORMAL, or HIGH. The default is the printer's default. Note: Press printers only recognize the options #COPIES, #SIDES, DOCUMENT.CREATION.DATE, and DOCUMENT.NAME. For example, (SEND.FILE.TO.PRINTER 'FOO NIL '(#COPIES 3 #SIDES 2 DOCUMENT.NAME "For John")) SEND.FILE.TO.PRINTER calls PRINTERTYPE and PRINTFILETYPE to determine the printer type of HOST and the file format of FILE. If FILE is a formatted file already in a form that the printer can print, it is transmitted directly. Otherwise, CONVERT.FILE.TO.TYPE.FOR.PRINTER is called to do the conversion. [Note: If the file is converted, PRINTOPTIONS is passed to the formatting function, so it can include properties such as HEADING, REGION, and FONTS.] All of these functions use the lists PRINTERTYPES and PRINTFILETYPES to actually determine how to do the conversion. LISTFILES (Chapter 17) calls the function LISTFILES1 to send a single file to a hardcopy printing device. Interlisp-D is initialized with LISTFILES1 defined to call SEND.FILE.TO.PRINTER. (HARDCOPYW(HARDCOPYW (Function) NIL NIL ("29") 2) WINDOW/BITMAP/REGION FILE HOST SCALEFACTOR ROTATION PRINTERTYPE HARDCOPYTITLE) [Function] Creates a hardcopy file from a bitmap and optionally sends it to a printer. Note that some printers may have limitations concerning how big or how "complicated" the bitmap may be printed. WINDOW/BITMAP/REGION can either be a WINDOW (open or closed), a BITMAP, or a REGION (interpreted as a region of the screen). If WINDOW/BITMAP/REGION is NIL, the user is prompted for a screen region using GETREGION. If FILE is non-NIL, it is used as the name of the file for output. If HOST = NIL, this file is not printed. If FILE is NIL, a temporary file is created, and sent to HOST. To save an image on a file without printing it, perform (HARDCOPYW IMAGE FILE). To print an image to the printer PRINTER without saving the file, perform (HARDCOPYW IMAGE NIL PRINTER). If both FILE and HOST are NIL, the default action is to print the image, without saving the file. The printer used is determined by the argument PRINTERTYPE and the value of the variable DEFAULTPRINTINGHOST. If PRINTERTYPE is non-NIL, the first host on DEFAULTPRINTINGHOST of the type PRINTERTYPE is used. If PRINTERTYPE is NIL, the first printer on DEFAULTPRINTINGHOST that implements the BITMAPSCALE (as determined by PRINTERTYPES) operation is used, if any. Otherwise, the first printer on DEFAULTPRINTINGHOST is used. The type of hardcopy file produced is determined by HOST if non-NIL, else by PRINTERTYPE if non-NIL, else by the value of DEFAULTPRINTINGHOST, as described above. SCALEFACTOR is a reduction factor. If not given, it is computed automatically based on the size of the bitmap and the capabilities of the printer type. This may not be supported for some printers. ROTATION specifies how the bitmap image should be rotated on the printed page. Most printers (including Interpress printers) only support a ROTATION of multiples of 90. PRINTERTYPE specifies what type of printer to use when HOST is NIL. HARDCOPYW uses this information to select which printer to use or what print file format to convert the output into, as described above. The background menu contains a "Hardcopy" command (Chapter 28) that prompts the user for a region on the screen, and sends the image to the default printer. Hardcopy output may also be obtained by writing a file on the printer device LPT, e.g. (COPYFILE 'FOO '{LPT}). When a file on this device is closed, it is converted to Interpress or some other format (if necessary) and sent to the default printer (the first host on DEFAULTPRINTINGHOST). One can include the printer name directly in the file name, e.g. (COPYFILE 'FOO {LPT}TREMOR:) will send the file to the printer TREMOR:. HARDCOPYTITLE is a string specifying a title to print on the page containing the screen image. If NIL, the string "Window Image" is used. To omit a title, specify the null string. (PRINTERSTATUS(PRINTERSTATUS (Function) NIL NIL ("29") 3) PRINTER) [Function] Returns a list describing the current status of the printer named PRINTER. The exact form of the value returned depends on the type of printer. For InterPress printers, the status describes whether the printer is available or busy or needs attention, and what type of paper is loaded in the printer. Returns NIL if the printer does not respond in a reasonable time, which can occur if the printer is very busy, or does not implement the printer status service. DEFAULTPRINTINGHOST (DEFAULTPRINTINGHOST% (Variable) NIL NIL ("29") 3) [Variable] The variable DEFAULTPRINTINGHOST is used to designate the default printer to be used as the output of printing operations. It should be a list of the known printer host names, for example, (QUAKE LISPPRINT:). If an element of DEFAULTPRINTINGHOST is a list, is interpreted as (PRINTERTYPE HOST), specifying both the host type and the host name. The type of the printer, which determines the protocol used to send to it and the file format it requires, is determined by the function PRINTERTYPE. If DEFAULTPRINTINGHOST is a single printer name, it is treated as if it were a list of one element. (PRINTFILETYPE(PRINTFILETYPE (Function) NIL NIL ("29") 4) FILE %) [Function] Returns the format of the file FILE. Possible values include INTERPRESS, TEDIT, etc. If it cannot determine the file type, it returns NIL. Uses the global variable PRINTFILETYPES. (PRINTERTYPE(PRINTERTYPE (Function) NIL NIL ("29") 4) HOST) [Function] Returns the type of the printer HOST. Currently uses the following heuristic: 1. If HOST is a list, the CAR is assumed to be the printer type and CADR the name of the printer 2. If HOST is a litatom with a non-NIL PRINTERTYPE property, the property value is returned as the printer type 3. If HOST contains a colon (e.g., PRINTER:PARC:XEROX) it is assumed to be an INTERPRESS printer 4. If HOST is the CADR of a list on DEFAULTPRINTINGHOST, the CAR is returned as the printer type 5. Otherwise, the value of DEFAULTPRINTERTYPE is returned as the printer type. Low-level Hardcopy Variables 1 The following variables are used to define how Interlisp should generate hardcopy of different types. The user should only need to change these variables when it is necessary to access a new type of printer, or define a new hardcopy document type (not often). PRINTERTYPES(PRINTERTYPES (Variable) NIL NIL ("29") 4) [Variable] The characteristics of a given printer are determined by the value of the list PRINTERTYPES. Each element is a list of the form (TYPES (PROPERTY1 VALUE1) (PROPERTY2 VALUE2) ...) TYPES is a list of the printer types that this entry addresses. The (PROPERTYn VALUEn) pairs define properties associated with each printer type. The printer properties include the following: CANPRINT Value is a list of the file types that the printer can print directly. STATUS Value is a function that knows how to find out the status of the printer, used by PRINTERSTATUS. PROPERTIES Value is a function which returns a list of known printer properties. SEND Value is a function which invokes the appropriate protocol to send a file to the printer. BITMAPSCALE Value is a function of arguments WIDTH and HEIGHT in bits which returns a scale factor for scaling a bitmap. BITMAPFILE Value is a form which, when evaluated, converts a bitmap to a file format that the printer will accept. Note: The name 8044 is defined on PRINTERTYPES as a synonym for the INTERPRESS printer type. The names SPRUCE, PENGUIN, and DOVER are defined on PRINTERTYPES as synonyms for the PRESS printer type. The printer types FULLPRESS and RAVEN are also defined the same as PRESS, except that these printer types indicate that the printer is a "Full Press" printer that is able to scale bitmap images, in addition to the normal Press printer facilities. PRINTFILETYPES(PRINTFILETYPES (Variable) NIL NIL ("29") 5) [Variable] The variable PRINTFILETYPES contains information about various file formats, such as Tedit files and Interpress files. The format is similar to PRINTERTYPES. The properties that can be specified include: TEST Value is a function which tests a file if it is of the given type. Note that this function is passed an open stream. CONVERSION Value is a property list of other file types and funcitons that convert from the specified type to the file format. EXTENSION Value is a list of possible file extensions for files of this type. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "28-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "28-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "28-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "28-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "28-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "28-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))F PAGEHEADING RIGHTPAGE;xllxx,xx2l~~2`~~,HH/HH206 +,@ PAGEHEADINGLEFTBACKT-T3(T,2 PALATINO TITAN TITAN TITAN TITAN +CLASSIC +MODERN +MODERNMODERN + +   IM.CHAP.GETFNMODERN +  HRULE.GETFNMODERN +  } - 2IM.INDEX.GETFNMODERN +  +  | b Y8  l  X  S  =  H(  \%r%@      JN[  ( x   +)0  4  "k D L) 1 ! +W + 'IM.INDEX.GETFNMODERN +? .1 5 +8Nu       > +@ +   , ME# UO +IM.INDEX.GETFNMODERN +B 3IM.INDEX.GETFN   M+IM.INDEX.GETFNMODERN +  +X )IM.INDEX.GETFNTITAN + +' > + !"   HRULE.GETFNMODERN +   *IM.INDEX.GETFNMODERN +O & @ <.FR  +FZ !< +h#  + " ,IM.INDEX.GETFNMODERN + v 1 v t D1=z \ No newline at end of file diff --git a/docs/turpin-irm/IRM-3/29-TERMINAL.TEDIT b/docs/turpin-irm/IRM-3/29-TERMINAL.TEDIT new file mode 100644 index 00000000..d8bd06bc Binary files /dev/null and b/docs/turpin-irm/IRM-3/29-TERMINAL.TEDIT differ diff --git a/doctools/NEWIRM b/doctools/NEWIRM new file mode 100644 index 00000000..70fb2805 --- /dev/null +++ b/doctools/NEWIRM @@ -0,0 +1,84 @@ +(DEFINE-FILE-INFO PACKAGE "INTERLISP" READTABLE "INTERLISP" BASE 10) + +(FILECREATED "24-Jun-2025 00:36:10" {DSK}frank>il>medley>doctools>NEWIRM.;13 4225 + + :EDIT-BY "FGH" + + :CHANGES-TO (VARS NEWIRMCOMS) + + :PREVIOUS-DATE "24-Jun-2025 00:28:20" {DSK}frank>il>medley>doctools>NEWIRM.;11) + + +(PRETTYCOMPRINT NEWIRMCOMS) + +(RPAQQ NEWIRMCOMS ((INITVARS (DOCTOOLSDIR (CONCAT (MEDLEYDIR) + "doctools>"))) + (FILES (FROM VALUEOF DOCTOOLSDIR) + IMTOOLS) + (FNS IRMTOC IRM-IMPTR IRMANALYZE IRMHC) + (VARS IRMFILES))) + +(RPAQ? DOCTOOLSDIR (CONCAT (MEDLEYDIR) + "doctools>")) + +(FILESLOAD (FROM VALUEOF DOCTOOLSDIR) + IMTOOLS) +(DEFINEQ + +(IRMTOC + [LAMBDA NIL + (TEDIT (MAKE.IM.TOC T NIL (DIRECTORY "*.IMPTR;"]) + +(IRM-IMPTR + [LAMBDA NIL + (SETQ IMPTR.TOC.LIST NIL) + (for F in (DIRECTORY "*.IMPTR;") collect (GRAB.IMPTR F)) + IMPTR.TOC.LIST]) + +(IRMANALYZE + [LAMBDA NIL + (LET* ((TS (TEXTOBJ (WHICHW))) + (OBJS (TEDIT.MAP.OBJECTS TS (FUNCTION TRUE) + NIL T))) + (for OBJ in OBJS collect (fetch DISPLAYFN of (fetch IMAGEOBJFNS of (CADR OBJ]) + +(IRMHC + [LAMBDA NIL + (for F in (DIRECTORY "*.TEDIT;") do (TEDIT.FORMAT.HARDCOPY F (PACKFILENAME.STRING 'VERSION NIL + 'EXTENSION NIL 'BODY F) + T NIL NIL NIL 'PDF]) +) + +(RPAQQ IRMFILES ("{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>01-INTRO.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>02-LITATOM.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>03-LISTS.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>04-STRINGS.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>05-ARRAY.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>06-HASHARRAYS.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>07-NUMBERS.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>08-RECORDPACKAGE.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>09-CONDITIONALS.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>10-FUNC-DEF.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>11-VAR-BINDINGS.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>12-MISC.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>13-EXECUTIVE.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>14-ERRORS.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>15-BREAKING.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>16-SEDIT.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>17-FILEPACKAGE.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>18-COMPILER.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>19-DWIM.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>20-CLISP.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>21-PERFORMANCE.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>22-PROCESSES.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>23-STREAMS.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>24-IO.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>25-USERIO-PACKAGES.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>26-GRAPHICS.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>27-WINDOWS.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>28-HARDCOPY.TEDIT;1" + "{DSK}FRANK>IL>MEDLEY>DOCS>MEDLEY-IRM>29-TERMINAL.TEDIT;1")) +(DECLARE%: DONTCOPY + (FILEMAP (NIL (810 1627 (IRMTOC 820 . 899) (IRM-IMPTR 901 . 1053) (IRMANALYZE 1055 . 1324) (IRMHC 1326 + . 1625))))) +STOP