home *** CD-ROM | disk | FTP | other *** search
/ Education Sampler 1992 [NeXTSTEP] / Education_1992_Sampler.iso / Programming / Source / winterp-1.13 / doc / XlispImpl.doc < prev    next >
Encoding:
Text File  |  1991-10-06  |  42.4 KB  |  1,083 lines

  1. ********************************************************************************
  2. * (c) Copyright 1990, by Jeff Prothero.
  3. *
  4. * Permission to use, copy, modify, distribute, and sell this software and its
  5. * documentation for any purpose is hereby granted without fee, provided that
  6. * the above copyright notice appear in all copies and that both that
  7. * copyright notice and this permission notice appear in supporting
  8. * documentation, and that the name of Jeff Prothero and the University of
  9. * Washington not be used in advertising or publicity pertaining to
  10. * distribution of the software and documentation without specific, written
  11. * prior permission.  Jeff Prothero and the University of Washington make
  12. * no representations about the suitability of this software and documentation
  13. * for any purpose. It is provided "as is" without express or implied warranty.
  14. *
  15. * JEFF PROTHERO AND THE UNIVERSITY OF WASHINGTON DISCLAIM ALL WARRANTIES WITH
  16. * REGARD TO THIS SOFTWARE AND DOCUMENTATION, INCLUDING ALL IMPLIED WARRANTIES OF
  17. * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL JEFF PROTHERO AND THE UNIVERSITY
  18. * OF WASHINGTON BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIALDAMAGES OR
  19. * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  20. * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
  21. * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE AND DOCUMENTATION.
  22. ********************************************************************************
  23. * Document Version: $Header: XlispImpl.doc,v 1.3 91/10/06 07:04:38 mayer Exp $
  24. ********************************************************************************
  25.  
  26. ------- Forwarded Messages
  27.  
  28. Forwarded: Fri, 16 Nov 90 16:08:00 PST
  29. Forwarded: winterp@hplnpm.hpl.hp.com
  30. Path: hplabsz!hplabs!ucbvax!bloom-beacon!mintaka!ogicse!milton!milton.u.washington.edu!jsp
  31. From: jsp@glia.u.washington.edu (Jeff Prothero)
  32. Newsgroups: comp.lang.lisp.x
  33. Subject: xlisp 2.1/winterp internals (26K long)
  34. Message-ID: <JSP.90Nov15145624@glia.u.washington.edu>
  35. Date: 15 Nov 90 22:56:23 GMT
  36. Sender: news@milton.u.washington.edu
  37. Distribution: comp
  38. Organization: Biological Structure, U of Wash, Seattle
  39. Lines: 689
  40.  
  41. I've just finished reading the xlisp 2.1 source code for the first
  42. time.  The tutorial and reference material included with the winterp
  43. distribution are well done, but I would have liked an overview of the
  44. interpreter internals.  Here's a first cut at such a document.
  45. Comments welcome...
  46.  
  47. - ---------------------------cut here-------------------------------
  48.  
  49. 90Nov13 jsp@milton.u.washington.edu
  50.  
  51.                    +---------------------+
  52.                    | xlisp 2.1 internals |
  53.                    +---------------------+
  54.  
  55.  
  56.  Who should read this?
  57.  ---------------------
  58.  
  59. Anyone poking through the C implementation of xlisp for the first
  60. time.  This is intended to provide a rough roadmap of the global xlisp
  61. structures and algorithms.  If you just want to write lisp code in
  62. xlisp, you don't need to read this file -- go read xlisp.doc,
  63. XlispOOP.doc, and XlispRef.doc, in about that order.  If you want to
  64. tinker with the xlisp implementation code, you should *still* read
  65. those three before reading this.  The following isn't intended to be
  66. exhaustively precise -- that's what the source code is for!  It is
  67. intended only to give you sufficient orientation give you a fighting
  68. chance to understand the code the first time through, instead of the
  69. third time.
  70.  
  71. At the bottom of the file you'll find an example of how to add new
  72. primitive functions to xlisp.
  73.  
  74.  
  75.  
  76.  What is an LVAL?
  77.  ----------------
  78.  
  79. An "LVAL" is the C type for a generic pointer to an xlisp
  80. garbage-collectable something.  (Cons cell, object, string, closure,
  81. symbol, vector, whatever.)  Virtually every variable in the
  82. interpreter is an LVAL.  Cons cells contain two LVAL slots,
  83. symbols contains four LVAL slots, etc.
  84.  
  85.  
  86.  
  87.  What is the obarry?
  88.  ------------------
  89.  
  90. The obarray is the xlisp symbol table.  More precisely, it is is a
  91. hashtable mapping ascii strings (symbol names) to symbols.  ("obarray"
  92. is a misnomer, since it contains only xlisp SYMBOLs, and in particular
  93. contains no xlisp OBJECTs.)  It is used when converting lisp
  94. expressions from text to internal form. Since it is a root for the
  95. garbage collector, it also serves to distinguish permanent
  96. global-variable symbols from other symbols -- you can permanently
  97. protect a symbol from the garbage collector by entering it into the
  98. obarray.  This is called "interning" the symbol.  The obarray is
  99. called "obarray" in C and "*OBARRAY*" in xlisp.
  100.  
  101.  
  102.  
  103.  The Interpreter Stacks
  104.  ----------------------
  105.  
  106. xlisp uses two stacks, an "evaluation stack" and an "argument stack".
  107. Both are roots for the garbage collector.  The evaluation stack is
  108. largely private to the interpreter and protects internal values from
  109. garbage collection, while the argument stack holds the conventional
  110. user-visible stackframes.
  111.  
  112.  
  113. The evaluation stack is an EDEPTH-long array of "LVAL" allocated by
  114. xldmem.c:xlminit().  It grows zeroward.
  115.  
  116. xlstkbase points to the zero-near end of the evaluation stack.
  117.  
  118. xlstktop points to the zero-far end of the evaluation stack: the
  119. occupied part of the stack lies between xlstack and xlstktop.  NOTE
  120. that xlstktop is *NOT* the top of the stack in the conventional sense
  121. of indicating the most recent entry on the stack: xlstktop is a static
  122. bounds pointer which never changes once the stack is allocated.
  123.  
  124. xlstack starts at the zero-far end of the evaluation stack.  *xlstack
  125. is the most recent LVAL on the stack.  The garbage collector MARKs
  126. everything reachable from the evaluation stack (among other things),
  127. so we frequently push things on this stack while C code is
  128. manipulating them. (Via xlsave(), xlprotect(), xlsave1(), xlprot1().)
  129.  
  130.  
  131. The argument stack is an ADEPTH-long array of "LVAL".  It also grows
  132. zeroward.  The evaluator pushes arguments on the argument stack at the
  133. start of a function call (form evaluation).  Built-in functions
  134. usually eat them directly off the stack.  For user-lisp functions
  135. xleval.c:evfun() pops them off the stack and binds them to the
  136. appropriate symbols before beginning execution of the function body
  137. proper.
  138.  
  139. xlargstkbase is the zero-near end of argument stack.
  140.  
  141. xlargstktop is the zero-far end of argument stack.  Like xlstktop,
  142. xlargstktop is a static bounds pointer which never changes after
  143. the stack is allocated.
  144.  
  145. *xlsp ("sp"=="stack pointer") is the most recent item on the argument stack.
  146.  
  147. xlfp ("fp"=="frame pointer") is the base of the current stackframe.
  148.  
  149.  
  150.  
  151.   What is a context?
  152.   ------------------
  153.  
  154. An xlisp "context" is something like a checkpoint, recording a
  155. particular point buried in the execution history so that we can
  156. abort/return back to it.  Contexts are used to implement call/return,
  157. catch/throw, signals, gotos, and breaks.  xlcontext points to the
  158. chain of active contexts, the top one being the second-newest active
  159. context.  (The newest -- that is, current -- active context is
  160. implemented by the variables xlstack xlenv xlfenv xldenv xlcontext
  161. xlargv xlargc xlfp xlsp.)  Context records are written by
  162. xljump.c:xlbegin() and read by xljump.c:xljump().  Context records are
  163. C structures on the C program stack; They are not in the dynamic
  164. memory pool or on the lisp execution or argument stacks.
  165.  
  166.  
  167.  
  168.   What is an environment?
  169.   -----------------------
  170.  
  171. An environment is basically a store of symbol-value pairs, used to
  172. resolve variable references by the lisp program.  xlisp maintains
  173. three environments, in the global variables xlenv, xlfenv and xldenv.
  174.  
  175. xlenv and xlfenv are conceptually a single environment, although they
  176. are implemented separately.  They are linked-list stacks which are
  177. pushed when we enter a function and popped when we exit it.  We also
  178. switch xlenv+xlfenf environments entirely when we begin executing a
  179. new closure (user-fn written in lisp).
  180.  
  181. The xlenv environment is the most heavily used environment.  It is
  182. used to resolve everyday data references to local variables.  It
  183. consists of a list of frames (and objects).  Each frame is a list of
  184. sym-val pairs.  In the case of an object, we check all the instance
  185. and class variables of the object, then do the same for its
  186. superclass, until we run out of superclasses.
  187.  
  188. The xlfenv environment is maintained strictly parallel to xlenv, but
  189. is used to find function values instead of variable values.  The
  190. separation may be partly for lookup speed and partly for historical
  191. reasons.
  192.  
  193. When we send a message, we set xlenv to the value it had when the
  194. message CLOSURE was built, then push on (obj msg-class), where
  195. msg-class is the [super]class defining the method.  (We also set
  196. xlfenv to the value xlfenv had when the method was built.)  This makes
  197. the object instance variables part of the environment, and saves the
  198. information needed to correctly resolve references to class variables,
  199. and to implement SEND-SUPER.
  200.  
  201. The xldenv environment tracks the old values of global variables which
  202. we have changed but intend to restore later to their original values,
  203. particularly when we bind and unbind s_evalhook and s_applyhook
  204. (*EVALHOOK* and *APPLYHOOK*).  (This is mostly to support the debug
  205. facilities.)  It is a simple list of sym-val pairs,
  206. treated as a stack.
  207.  
  208. These environments are manipulated in C via the xlisp.h macros
  209. xlframe(e), xlbind(s,v), xlfbind(s,v), xlpbind(s,v,e), xldbind(s,v),
  210. xlunbind(e).
  211.  
  212.  
  213.  
  214.   How are xlisp entities stored and identified?
  215.   ---------------------------------------------
  216.  
  217. Conceptually, xlisp manages memory as a single array of fixed-size
  218. objects.  Keeping all objects the same size simplifies memory
  219. management enormously, since any object can be allocated anywhere, and
  220. complex compacting schemes aren't needed.  Every LVAL pointer points
  221. somewhere in this array.  Every xlisp object has the basic format
  222. (xldmem.h:typdef struct node)
  223.  
  224.  struct node {
  225.      char n_type;
  226.      char n_flags;
  227.      LVAL car;
  228.      LVAL cdr;
  229.  }
  230.  
  231. where n_type is one of:
  232.  
  233.  FREE     A node on the freelist.
  234.  SUBR     A function implemented in C. (Needs evalutated arguments.)
  235.  FSUBR    A special function implemented in C. (Needs unevaluated arguments).
  236.  CONS     A regular lisp cons cell.
  237.  SYMBOL   A symbol.
  238.  FIXNUM   An integer.
  239.  FLONUM   A floating-point number.
  240.  STRING   A string.
  241.  OBJECT   Any object, including class objects.
  242.  STREAM   An input or output file.
  243.  VECTOR      A variable-size array of LVALs.
  244.  CLOSURE  Result of DEFUN or LAMBDA -- a function written in lisp.
  245.  CHAR      An ascii character.
  246.  USTREAM  An internal stream.
  247.  STRUCT      A structure.
  248.  
  249. Messages may be sent only to nodes with n_type == OBJECT.
  250.  
  251. Obviously, several of the above types won't fit in a fixed-size
  252. two-slot node.  The escape is to have them malloc() some memory
  253. and have one of the slots point to it -- VECTOR is the archetype.  For
  254. example, see xldmem.c:newvector().  To some extent, this malloc()
  255. hack simply exports the memory- fragmentation problem to the C
  256. malloc()/free() routines.  However, it helps keep xlisp simple, and it
  257. has the happy side-effect of unpinning the body of the vector, so that
  258. vectors can easily be expanded and contracted.
  259.  
  260. The garbage collector has special-case code for each of the above node
  261. types, so it can find all LVAL slots and recycle any malloc()ed ram
  262. when a node is garbage-collected.
  263.  
  264. Xlisp pre-allocates nodes for all ascii characters, and for small
  265. integers.  These nodes are never garbage-collected.
  266.  
  267. As a practical matter, allocating all nodes in a single array is not
  268. very sensible.  Instead, nodes are allocated as needed, in segments of
  269. one or two thousand nodes, and the segments linked by a pointer chain
  270. rooted at xldmem.c:segs.
  271.  
  272.  
  273.  
  274.   How are vectors implemented?
  275.   ----------------------------
  276.  
  277. An xlisp vector is a generic array of LVAL slots.  Vectors are also
  278. the canonical illustration of xlisp's escape mechanism for node types
  279. which need more than two LVAL slots (the maximum possible in the
  280. fixed-size nodes in the dynamic memory pool).  The node CAR/CDR slots
  281. for a vector hold a size field plus a pointer to a malloc()ed ram
  282. chunk, which is automatically free()ed when the vector is
  283. garbage-collected.
  284.  
  285. xldmem.h defines macros for reading and writing vector fields and
  286. slots: getsize(), getelement() and setelement().  It also defines
  287. macros for accessing each of the other types of xlisp nodes.
  288.  
  289.  
  290.  
  291.   How are strings implemented?
  292.   ---------------------------- 
  293.  
  294. Strings work much like vectors: The node has a pointer to a malloc()ed
  295. ram chunk which is automatically free()ed when the string gets
  296. garbage-collected.
  297.  
  298.  
  299.  
  300.  How are symbols implemented?
  301.  ----------------------------
  302.  
  303. A symbol is a generic user-visible lisp variable, with separate slots
  304. for print name, value, function, and property list.  Any or all of
  305. these slots (including name) may be NIL.  You create a symbol in C by
  306. calling "xlmakesym(name)" or "xlenter(name)" (to make a symbol and
  307. enter it in the obarray). You create a symbol in xlisp by using the
  308. single-quote operator: "'name", or by calling "(gensym)", or
  309. indirectly in various ways.  Most of the symbol-specific code in the
  310. interpreter is in xlsym.c.
  311.  
  312. Physically, a symbol is implemented like a four-slot vector.
  313.  
  314. Random musing: Abstractly, the LISP symbols plus cons cells (etc)
  315. constitute a single directed graph, and the symbols mark spots where
  316. normal recursive evaluation should stop.  Normal lisp programming
  317. practice is to have a symbol in every cycle in the graph, so that
  318. recursive traversal can be done without MARK bits.
  319.  
  320.  
  321.  
  322.   How are closures implemented?
  323.   -----------------------------
  324.  
  325. A closure, the return value from a lambda, is a regular coded-in-lisp
  326. fn.  Physically, it it implemented like an eleven-slot vector, with the
  327. node n_type field hacked to contain CLOSURE instead of VECTOR. The
  328. vector slots contain:
  329.  
  330.  name   symbol -- 1st arg of DEFUN.  NIL for LAMBDA closures.
  331.  type   (s_lambda or s_macro). Must be s_lambda to be executable.
  332.  args   List of "required" formal arguments (as symbols)
  333.  oargs  List of "optional" args, each like: (name (default specified-p))
  334.  rest   Name of "&rest" formal arg, else NIL.
  335.  kargs  keyword args, each like: ((':foo 'bar default specified-p))
  336.  aargs  &aux vars, each like: (('arg default))
  337.  body   actual code (as lisp list) for fn.
  338.  env    value of xlenv when the closure was built.  NIL for macros.
  339.  fenv   value of xlfend when the closure was built. NIL for macros.
  340.  lambda The original formal args list in the DEFUN or LAMBDA.
  341.  
  342. The lambda field is for printout purposes.  The remaining fields store
  343. a predigested version of the formal args list.  This is a limited form
  344. of compilation: by processing the args list at closure-creation time,
  345. we reduce the work needed during calls to the closure.
  346.  
  347.  
  348.  
  349.   How are objects implemented?
  350.   ----------------------------
  351.  
  352. An object is implemented like a vector, with the size determined by
  353. the number of instance variables.  The first slot in the vector points
  354. to the class of the object; the remaining slots hold the instance
  355. variables for the object.  An object needs enough slots to hold all
  356. the instance variables defined by its class, *plus* all the instance
  357. variables defined by all of its superclasses.
  358.  
  359.  
  360.  
  361.   How are classes implemented?
  362.   ----------------------------
  363.  
  364. A class is a specific kind of object, hence has a class pointer plus
  365. instance variables.  All classes have the following instance variables:
  366.  
  367.  MESSAGES   A list of (interned-symbol method-closure) pairs.
  368.  IVARS        Instance variable names: A list of interned symbols.
  369.  CVARS      Class variable names:    A list of interned symbols.
  370.  CVALS      Class variable values:   A vector of values.
  371.  SUPERCLASS A pointer to the superclass.
  372.  IVARCNT    Number of class instance variables, as a fixnum.
  373.  IVARTOTAL  Total number of instance variables, as a fixnum.
  374.  
  375. IVARCNT is the count of the number of instance variables defined by
  376. our class.  IVARTOTAL is the total number of instance variables in an
  377. object of this class -- IVARCNT for this class plus the IVARCNTs from
  378. all of our superclasses.
  379.  
  380.  
  381.  
  382.  
  383.   How is the class hierarchy laid out?
  384.   ------------------------------------
  385.  
  386. The fundamental objects are the OBJECT and CLASS class objects.  (Both
  387. are instances of class CLASS, and since CLASSes are a particular kind
  388. of OBJECT, both are also objects, with n_type==OBJECT.  Bear with me!)
  389.  
  390. OBJECT is the root of the class hierarchy: everything you can send a
  391. message to is of type OBJECT.  (Vectors, chars, integers and so forth
  392. stand outside the object hierarchy -- you can't send messages to them.
  393. I'm not sure why Dave did it this way.) OBJECT defines the messages:
  394.  
  395.  :isnew -- Does nothing
  396.  :class -- Returns contents of class-pointer slot.
  397.  :show  -- Prints names of obj, obj->class and instance vars.
  398.  
  399. A CLASS is a specialized type of OBJECT (with instance variables like
  400. MESSAGES which generic OBJECTs lack), class CLASS hence has class
  401. OBJECT as its superclass.  The CLASS object defines the messages:
  402.  
  403.  :new     -- Create new object with self.IVARTOTAL LVAR slots, plus
  404.             one for the class pointer. Point class slot to self.
  405.             Set new.n_type char to OBJECT.
  406.  :isnew     -- Fill in IVARS, CVARS, CVALS, SUPERCLASS, IVARCNT and
  407.             IVARTOTAL, using parameters from :new call.  (The
  408.             :isnew msg inherits the :new msg parameters because
  409.             the  :isnew msg is generated automatically after
  410.             each :new   msg, courtesy of a special hack in
  411.             xlobj.c:sendmsg().)
  412.  :answer -- Add a (msg closure) pair to self.MESSAGES.
  413.  
  414.  
  415.  
  416. Here's a figure to summarize the above, with a generic object thrown
  417. in for good measure.  Note that all instances of CLASS will have a
  418. SUPERCLASS pointer, but no normal object will.  Note also that the
  419. messages known to an object are those which can be reached by
  420. following exactly one Class Ptr and then zero or more Superclass Ptrs.
  421. For example, the generic object can respond to :ISNEW, :CLASS and
  422. :SHOW, but not to :NEW or :ANSWER.  (The functions implementing the
  423. given messages are shown in parentheses.)
  424.  
  425.                     NIL
  426.                      ^
  427.                      |
  428.                      |Superclass Ptr
  429.                      |
  430.                 Msg+--------+
  431.  :isnew (xlobj.c:obisnew) <----|  class |Class Ptr
  432.  :class (xlobj.c:obclass) <----| OBJECT |------------+
  433.  :show    (xlobj.c:objshow) <----|        |            |
  434.                    +--------+            |
  435.        +---------+                ^  ^               |
  436.        | generic |Class Ptr       |  |               |
  437.        | object  |----------------+  |Superclass Ptr |
  438.        +---------+             |               |
  439.                 Msg+--------+            |
  440.  :isnew    (xlobj.c:clnew)      <----| class  |Class Ptr   |
  441.  :new    (xlobj.c:clisnew) <----| CLASS  |--------+   |
  442.  :answer(xlobj.c:clanswer)<----|        |        |   |
  443.                    +--------+        |   |
  444.                   ^  ^           |   |
  445.                   |  |           |   |
  446.                   |  +-----------+   |
  447.                   +------------------+
  448.  
  449.  
  450. Thus, class CLASS inherits the :CLASS and :SHOW messages from class
  451. OBJECT, overrides the default :ISNEW message, and provides new the
  452. messages :NEW and :ANSWER.
  453.  
  454. New classes are created by (send CLASS :NEW ...) messages.  Their
  455. Class Ptr will point to CLASS.  By default, they will have OBJECT as
  456. their superclass, but this can be overridden by the second optional
  457. argument to :NEW.
  458.  
  459. The above basic structure is set up by xlobj.c:xloinit().
  460.  
  461.  
  462.  
  463.   How do we look up the value of a variable?
  464.   ------------------------------------------
  465.  
  466. When we're cruising along evaluating an expression and encounter a
  467. symbol, the symbol might refer to a global variable, an instance
  468. variable, or a class variable in any of our superclasses.  Figuring
  469. out which means digging throught the environment.  The canonical place
  470. this happens is in xleval.c:xleval(), which simply passes the buck to
  471. xlsym.c:xlgetvalue(), which in turn passes the buck to
  472. xlxsym.c:xlxgetvalue(), where the fun of scanning down xlenv begins.
  473. The xlenv environment looks something like
  474.  
  475.      Backbone    Environment frame contents
  476.      --------    --------------------------
  477. xlenv --> frame      ((sym val) (sym val) (sym val) ... )
  478.       frame      ...
  479.       object     (obj msg-class)
  480.       frame      ...
  481.       object     ...
  482.       frame      ...
  483.       ...
  484.  
  485. The "frame" lines are due to everyday nested constructs like LET
  486. expressions, while the "object" lines represent an object environment
  487. entered via a message send.  xlxgetvalue scans the enviroment left to
  488. right, and then top to bottom.  It scans down the regular environment
  489. frames itself, and calls xlobj.c:xlobjgetvalue() to search the object
  490. environment frames.
  491.  
  492. xlobjgetvalue() first searches for the symbol in the msg-class, then
  493. in all the successive superclasses of msg-class.  In each class, it
  494. first checks the list of instance-variable names in the IVARS slot,
  495. then the list of class-variables name in the CVARS slot.
  496.  
  497.   
  498.  
  499.   How are function calls implemented?
  500.   -----------------------------------
  501.  
  502. xleval.c contains the central expression-evaluation code.
  503. xleval.c:xleval() is the standard top-level entrypoint.  The two
  504. central functions are xleval.c:xlevform() and xleval.c:evfun().
  505. xlevform() can evaluate four kinds of expression nodes:
  506.  
  507. SUBR: A normal primitive fn coded in C.  We call evpushargs() to
  508. evaluate and push the arguments, then call the primitive.
  509.  
  510. FSUBR: A special primitive fn coded in C, which (like IF) wants its
  511. arguments unevaluated.  We call pushargs() (instead of evpushargs())
  512. and then the C fn.
  513.  
  514. CLOSURE: A preprocessed written-in-lisp fn from a DEFUN or LAMBDA.  We
  515. call evpushargs() and then evfun().
  516.  
  517. CONS: We issue an error if it isn't a LAMBDA, otherwise we call
  518. xleval.c:xlclose() to build a CLOSURE from the LAMBDA, and fall into
  519. the CLOSURE code.
  520.  
  521. The common thread in all the above cases is that we call evpushargs()
  522. or pushargs() to push all the arguments on the evaluation stack,
  523. leaving the number and location of the arguments in the global
  524. variables xlargc and xlargv.  The primitive C functions consume
  525. their arguments directly from the argument stack.
  526.  
  527. xleval.c:evfun() evaluates a CLOSURE by:
  528.  
  529. (1) Switching xlenv and xlfenv to the values they had when
  530. the CLOSURE was built. (These values are recorded in the CLOSURE.)
  531.  
  532. (2) Binding the arguments to the environment.  This involves scanning
  533. through the section of the argument stack indicated by xlargc/xlargv,
  534. using information from the CLOSURE to resolve keyword arguments
  535. correctly and assign appropriate default values to optional arguments,
  536. among other things.
  537.  
  538. (3) Evaluating the body of the function via xleval.c:xleval().
  539.  
  540. (4) Cleaning up and restoring the original environment.
  541.  
  542.  
  543.  
  544.   How are message-sends implemented?
  545.   ----------------------------------
  546.  
  547. We scan the MESSAGES list in the CLASS object of the recipient,
  548. looking for a (message-symbol method) pair that matches our message
  549. symbol.  If necessary, we scan the MESSAGES lists of the recipients
  550. superclasses too.  (xlobj.c:sendmsg().)  Once we find it, we basically
  551. do a normal function evaluation. (xlobjl.c:evmethod().)  Two oddities:
  552. We need to replace the message-symbol by the recipient on the argument
  553. stack to make things look normal, and we need to push an 'object'
  554. stack entry on the xlenv environment so we remember which class is
  555. handling the message.
  556.  
  557.  
  558.  
  559.   How is garbage collection implemented?
  560.   --------------------------------------
  561.  
  562. The dynamic memory pool managed by xlisp consists of a chain of memory
  563. *segments* rooted at global C variable "segs".  Each segment contains
  564. an array of "struct node"s plus a pointer to the next segment.  Each
  565. node contains a n_type field and a MARK bit, which is zero except
  566. during garbage collection.
  567.  
  568. Xlisp uses a simple, classical mark-and-sweep garbage collector.  When
  569. it runs out of memory (fnodes==NIL), it does a recursive traversal
  570. setting the MARK flag on all nodes reachable from the obarray, the
  571. three environments xlenv/xlfenv/xldenv, and the evaluation and
  572. argument stacks.  (A "switch" on the n_type field tells us how to find
  573. all the LVAL slots in the node (plus associated storage), and a
  574. pointer-reversal trick lets us avoid using too much stack space during
  575. the traversal.)  sweep() then adds all un-MARKed LVALs to fnodes, and
  576. clears the MARK bit on the remaining nodes.  If this fails to produce
  577. enough free nodes, a new segment is malloc()ed.
  578.  
  579. The code to do this stuff is mostly in xldmem.c.
  580.  
  581.  
  582.  
  583.  How do I add a new primitive fn to xlisp?
  584.  -----------------------------------------
  585.  
  586. Add a line to the end of xlftab.c:funtab[].  This table contains a
  587. list of triples:
  588.  
  589. The first element of each triple is the function name as it will
  590. appear to the programmer. Make it all upper case.
  591.  
  592. The second element is S (for SUBR) if (like most fns) your function
  593. wants its arguments pre-evaluated, else F (for FSUBR).
  594.  
  595. The third element is the name of the C function to call.
  596.  
  597. Remember that your arguments arrive on the xlisp argument rather
  598. than via the usual C parameter mechanism.
  599.  
  600. CAUTION: Try to keep your files separate from generic xlisp files, and
  601. to minimize the number of changes you make in the generic xlisp files.
  602. This way, you'll have an easier time re-installing your changes when
  603. new versions of xlisp come out.  It's a good idea to put a marker
  604. (like a comment with your initials) on each line you change or insert
  605. in the generic xlisp fileset.  For example, if you are going to add
  606. many primitive functions to your xlisp, use an #include file rather
  607. than putting them all in xlftab.c.
  608.  
  609. CAUTION: Remember that you usually need to protect the LVAL variables
  610. in your function from the garbage-collector.  It never hurts to do
  611. this, and often produces obscure bugs if you dont.  Generic code for
  612. a new primitive fn:
  613.  
  614. /* xlsamplefun - do useless stuff. */
  615. /* Called like (samplefun '(a c b) 1 2.0) */
  616. LVAL xlsamplefun()
  617. {
  618.     /* Variables to hold the arguments: */
  619.     LVAL    list_arg, integer_arg, float_arg;
  620.  
  621.     /* Get the arguments, with appropriate errors */
  622.     /* if any are of the wrong type.  Look in     */
  623.     /* xlisp.h for macros to read other types of  */
  624.     /* arguments.  Look in xlmath.c for examples  */
  625.     /* of functions which can handle an argument  */
  626.     /* which may be either int or float:          */
  627.     list_arg    = xlgalist()  ;  /* "XLisp Get A LIST"   */
  628.     integer_arg = xlgafixnum();  /* "XLisp Get A FIXNUM" */
  629.     float_arg   = xlgaflonum();  /* "XLisp Get A FLONUM" */
  630.  
  631.     /* Issue an error message if there are any extra arguments: */
  632.     xllastarg();
  633.  
  634.  
  635.  
  636.     /* Call a separate C function to do the actual  */
  637.     /* work.  This way, the main function can       */
  638.     /* be called from both xlisp code and C code.   */
  639.     /* By convention, the name of the xlisp wrapper */
  640.     /* starts with "xl", and the native C function  */
  641.     /* has the same name minus the "xl" prefix:     */
  642.     return samplefun( list_arg, integer_arg, float_arg );
  643. }
  644. LVAL samplefun( list_arg, integer_arg, float_arg )
  645. LVAL            list_arg, integer_arg, float_arg;
  646. {
  647.     FIXTYPE val_of_integer_arg;
  648.     FLOTYPE val_of_float_arg;
  649.  
  650.     /* Variables which will point to LISP objects: */
  651.     LVAL result;
  652.  
  653.     LVAL list_ptr;
  654.     LVAL float_ptr;
  655.     LVAL int_ptr;
  656.  
  657.     /* Protect our internal pointers by */
  658.     /* pushing them on the evaluation   */
  659.     /* stack so the garbage collector   */
  660.     /* can't recycle them in the middle */
  661.     /* of the routine:                  */
  662.     xlstkcheck(3);    /* Make sure following xlsave */
  663.                       /* calls won't overrun stack. */
  664.     xlsave(list_ptr); /* Use xlsave1() if you don't */
  665.     xlsave(float_ptr);/* do an xlstkcheck().        */
  666.     xlsave(int_ptr);
  667.  
  668.     /* Create an internal list structure, protected */
  669.     /* against garbage collection until we exit fn: */
  670.     list_ptr = cons(list_arg,list_arg);
  671.  
  672.     /* Get the actual values of our fixnum and flonum: */
  673.     val_of_integer_arg = getfixnum( integer_arg );
  674.     val_of_float_arg   = getflonum( float_arg   );
  675.  
  676.  
  677.     /*******************************************/
  678.     /* You can have any amount of intermediate */
  679.     /* computations at this point in the fn... */
  680.     /*******************************************/
  681.  
  682.  
  683.     /* Make new numeric values to return: */
  684.     integer_ptr = cvflonum( val_of_integer_arg * 3   );
  685.     float_ptr   = cvflonum( val_of_float_arg   * 3.0 );
  686.  
  687.     /* Cons it all together to produce a return value: */
  688.     result = cons(
  689.         list_ptr,
  690.         cons(
  691.             integer_ptr,
  692.             cons(
  693.                 float_ptr,
  694.                 NIL
  695.             )
  696.         )
  697.     );
  698.  
  699.     /* Restore the stack, cancelling the xlsave()s: */
  700.     xlpopn(3); /* Use xlpop() for a single argument.*/
  701.  
  702.     return result;
  703. }
  704.  
  705.  
  706.  
  707.  
  708.  Minor Observations:
  709.  -------------------
  710.  
  711. xlapply, xlevform and sendmsg will issue an error if they encounter a
  712. s_macro CLOSURE.  This is presumably because all macros are expanded
  713. by xleval.c:xlclose when it builds a closure.
  714.  
  715. Neither xlapply nor sendmsg will handle FSUBRs.  This is presumably
  716. a minor bug, left due to the difficulty of keeping arguments
  717. unevaluated to that point. ?
  718.  
  719.  
  720.  Minor Mysteries:
  721.  ----------------
  722.  
  723. Why doesn't xlevform trace FSUBRs?  Is this a speed hack?
  724.  
  725. Why do both xlobj.c:xloinit() and xlobj.c:obsymvols() initialize the
  726. "object" and "class" variables?
  727. - --
  728.  
  729. - - Jeff   (S)
  730.  
  731. ------- Message 2
  732.  
  733. Path: hplabsz!hplabs!ucbvax!ucdavis!csus.edu!wuarchive!zaphod.mps.ohio-state.edu!uakari.primate.wisc.edu!dali.cs.montana.edu!milton!uw-beaver!zephyr.ens.tek.com!tekchips!tekgvs!toma
  734. From: toma@tekgvs.LABS.TEK.COM (Tom Almy)
  735. Newsgroups: comp.lang.lisp.x
  736. Subject: Re: xlisp 2.1/winterp internals (26K long)
  737. Message-ID: <8440@tekgvs.LABS.TEK.COM>
  738. Date: 16 Nov 90 21:13:29 GMT
  739. References: <JSP.90Nov15145624@glia.u.washington.edu>
  740. Reply-To: toma@tekgvs.LABS.TEK.COM (Tom Almy)
  741. Distribution: comp
  742. Organization: Tektronix, Inc., Beaverton,  OR.
  743. Lines: 206
  744.  
  745. >I've just finished reading the xlisp 2.1 source code for the first
  746. >time.  The tutorial and reference material included with the winterp
  747. >distribution are well done, but I would have liked an overview of the
  748. >interpreter internals.  Here's a first cut at such a document.
  749. >Comments welcome...
  750.  
  751. I have spend many hours going over the listings, fixing bugs, and making
  752. extensions. I wish I had this when I started. But I do have a few comments.
  753.  
  754.  
  755. >xlenv and xlfenf are conceptually a single environment, although they
  756. >are implemented separately. [...]
  757.  
  758. >The xlfenv environment is maintained strictly parallel to xlenv, but
  759. >is used to find function values instead of variable values.  The
  760. >separation may be partly for lookup speed and partly for historical
  761. >reasons.
  762.  
  763. They have to be maintained separately because let lexically binds values and
  764. flet, labels, and macrolet lexically bind only functions. 
  765. For instance consider:
  766. (defun x () x)
  767. (setq x 10)
  768. (let ((x 3)) (print x) (print (x)))
  769.  
  770. will print 3 and 10.
  771.  
  772. while
  773.  
  774. (flet ((x () (+ 1 x))) (print x) (print (x)))
  775.  
  776. will print 10 and 11.
  777.  
  778. and 
  779.  
  780. (let ((x 3)) (flet ((x () (+ 1 x))) (print x) (print (x))))
  781.  
  782. will print 3 and 4.
  783.  
  784. You couldn't do this with a combined binding list.
  785.  
  786.  
  787. >The xldenv environment tracks the old values of global variables which
  788. >we have changed but intend to restore later to their original values,
  789. >particularly when we bind and unbind s_evalhook and s_applyhook
  790. >(*EVALHOOK* and *APPLYHOOK*).  (This is mostly to support the debug
  791. >facilities.)  It is a simple list of sym-val pairs,
  792. >treated as a stack.
  793.  
  794. xldenv tracks the dynamic binding (as opposed to lexical binding). A "flaw"
  795. in xlisp is that there is no mechanism for declaring special variables
  796. (which would be always dynamically bound). You can dynamically bind
  797. variables with PROGV. If my memory serves, only PROGV, EVALHOOK and 
  798. (as I implemented it) APPLYHOOK dynamically bind variables.  For instance,
  799. consider the following variation of the LET example above:
  800.  
  801. (defun x () x)
  802. (setq x 10)
  803. (progv '(x) '(3) (print x) (print (x)))
  804.  
  805. will print 3 and 3. (When execution falls out of progv, the global x is
  806. rebound to 10).
  807.  
  808.  
  809. This is the best way to override global variable settings in an application,
  810. since the variables will be restored automatically on termination.
  811.  
  812.  
  813. >Obviously, several of the above types won't fit in a fixed-size
  814. >two-slot node.  The escape is to have them malloc() some memory
  815. >and have one of the slots point to it -- VECTOR is the archetype.  For
  816. >example, see xldmem.c:newvector().  To some extent, this malloc()
  817. >hack simply exports the memory- fragmentation problem to the C
  818. >malloc()/free() routines.  However, it helps keep xlisp simple, and it
  819. >has the happy side-effect of unpinning the body of the vector, so that
  820. >vectors can easily be expanded and contracted.
  821.  
  822. XSCHEME which relies more heavily on arrays, maintains a pool of storage
  823. to allocate arrays and strings, for which it does garbage collection
  824. and (I believe) compaction as well. At any rate, my modified xlisp can
  825. optionally use the xcheme approach which has decided advantages in
  826. programs that use many arrays and strings since the memory does not
  827. get fragmented. Enough said.
  828.  
  829.  
  830. >Xlisp pre-allocates nodes for all ascii characters, and for small
  831. >integers.  These nodes are never garbage-collected.
  832.  
  833. This also speeds up READ, and vastly reduces the number of nodes since
  834. all identical characters and small integers are unique. The range of
  835. small integers treated in this way is compilation settable.
  836.  
  837.  
  838. >As a practical matter, allocating all nodes in a single array is not
  839. >very sensible.  Instead, nodes are allocated as needed, in segments of
  840. >one or two thousand nodes, and the segments linked by a pointer chain
  841. >rooted at xldmem.c:segs.
  842.  
  843. The size of the segment is settable using the ALLOC function.
  844.  
  845. >You create a symbol in xlisp by using the
  846. >single-quote operator: "'name", or by calling "(gensym)", or
  847. >indirectly in various ways.
  848.  
  849. I would say that 'name is an indirect way to create a symbol. The direct
  850. ways are using MAKE-SYMBOL (for uninterned symbols) or INTERN (for interned
  851. symbols), or as you mentioned GENSYM (also uninterned). You can make READ
  852. create an uninterned symbol by preceeding it with #:, otherwise all symbols
  853. read by READ are interned.
  854.  
  855. In addition, when you make a symbol that starts with the colon character,
  856. the symbol is given itself as the value, otherwise the new symbol has no
  857. value.
  858.  
  859.  
  860. >OBJECT is the root of the class hierarchy: everything you can send a
  861. >message to is of type OBJECT.  (Vectors, chars, integers and so forth
  862. >stand outside the object hierarchy -- you can't send messages to them.
  863. >I'm not sure why Dave did it this way.)
  864.  
  865. Probably because the object facility is an extension of lisp. You can
  866. create classes of these things. There is also efficiency considerations.
  867. The only object oriented programming language I know of where everything
  868. is an object is Smalltalk, but if you look at the implementation, it does
  869. cheat at the low level to speed things up.
  870.  
  871. > :isnew -- Does nothing
  872.  
  873. It does return the object!
  874.  
  875.  
  876. >FSUBR: A special primitive fn coded in C, which (like IF) wants its
  877. >arguments unevaluated.  
  878.  
  879. These are the "special forms"
  880.  
  881. >We scan the MESSAGES list in the CLASS object of the recipient,
  882. >looking for a (message-symbol method) pair that matches our message
  883. >symbol.  If necessary, we scan the MESSAGES lists of the recipients
  884. >superclasses too.  (xlobj.c:sendmsg().)  Once we find it, we basically
  885. >do a normal function evaluation. (xlobjl.c:evmethod().)  Two oddities:
  886. >We need to replace the message-symbol by the recipient on the argument
  887. >stack to make things look normal, and we need to push an 'object'
  888. >stack entry on the xlenv environment so we remember which class is
  889. >handling the message.
  890.  
  891.  
  892. The first "oddity" has an important side effect, when :answer was
  893. used to build the method closure, an additional argument, "self", was
  894. added so that the method could access itself with the symbol self.
  895. This argument stack fix supplies the needed argument. 
  896.  
  897. The reason for the second "oddity" is that the method's class is
  898. needed for SEND-SUPER. When one uses SEND-SUPER, the message lookup
  899. begins in the superclass of the method rather than the class of the
  900. object (as with SEND).
  901.  
  902. >    xlstkcheck(3);    /* Make sure following xlsave */
  903. >                      /* calls won't overrun stack. */
  904. >    xlsave(list_ptr); /* Use xlsave1() if you don't */
  905. >    xlsave(float_ptr);/* do an xlstkcheck().        */
  906. >    xlsave(int_ptr);
  907.  
  908. xlsave also set the variable to nil. If you don't need to do that you
  909. can use xlprot instead of xlsave, or xlprot1 instead of xlsave1
  910.  
  911. >xlapply, xlevform and sendmsg will issue an error if they encounter a
  912. >s_macro CLOSURE.  This is presumably because all macros are expanded
  913. >by xleval.c:xlclose when it builds a closure.
  914.  
  915. You are not allowed to use APPLY or FUNCALL with macros in Common
  916. Lisp. There is no way provided to declare macro methods, nor do they
  917. make much sense (at least in my mind).
  918.  
  919. >Neither xlapply nor sendmsg will handle FSUBRs.  This is presumably
  920. >a minor bug, left due to the difficulty of keeping arguments
  921. >unevaluated to that point. ?
  922.  
  923. You are not allowed to use APPLY or FUNCALL with special forms. There is
  924. no way to declare methods using SUBRs or FSUBRs (the existing SUBR
  925. methods are initialized at load time).
  926.  
  927. >
  928. > Minor Mysteries:
  929. > ----------------
  930.  
  931. >Why doesn't xlevform trace FSUBRs?  Is this a speed hack?
  932. Good question. Probably not a speed hack. You can't trace macros either.
  933.  
  934. >Why do both xlobj.c:xloinit() and xlobj.c:obsymvols() initialize the
  935. >"object" and "class" variables?
  936.  
  937. xloinit creates the classes class and object, as well as the symbols, but
  938. sets the C variables class and object to point to the class and object.
  939.  
  940. obsymbols just set the C variables by looking up the symbols. It is needed
  941. because when you restore a workspace you don't create new objects but still
  942. need to know where the existing objects are (they might be in a different
  943. location in the saved workspace). Notice that obsymbols is called by xlsymbols
  944. which is called both when initializing a new workspace or restoring an old
  945. workspace.
  946.  
  947.  
  948. Tom Almy
  949. toma@tekgvs.labs.tek.com
  950. Standard Disclaimers Apply
  951.  
  952. ------- Message 3
  953.  
  954. Path: hplabsz!hplabs!sdd.hp.com!wuarchive!emory!att!cbnewsc!lgm
  955. From: lgm@cbnewsc.att.com (lawrence.g.mayka)
  956. Newsgroups: comp.lang.lisp.x
  957. Subject: Re: xlisp 2.1/winterp internals (26K long)
  958. Summary: CLOS treats everything as an object
  959. Message-ID: <1990Nov17.155836.23781@cbnewsc.att.com>
  960. Date: 17 Nov 90 15:58:36 GMT
  961. References: <JSP.90Nov15145624@glia.u.washington.edu> <8440@tekgvs.LABS.TEK.COM>
  962. Organization: AT&T Bell Laboratories
  963. Lines: 20
  964.  
  965. In article <8440@tekgvs.LABS.TEK.COM>, toma@tekgvs.LABS.TEK.COM (Tom Almy) writes:
  966. > The only object oriented programming language I know of where everything
  967. > is an object is Smalltalk, but if you look at the implementation, it does
  968. > cheat at the low level to speed things up.
  969.  
  970. Correction: the Common Lisp Object System (CLOS) indeed considers
  971. every entity to be an object - i.e, an instance of some class on which
  972. methods can specialize.  Thus, one can specialize a method for
  973. integers, or symbols, or sequences.  It is also true, however, that
  974. CLOS classes differ in their inheritance behavior according to
  975. metaclass (STANDARD-CLASS, STRUCTURE-CLASS, or BUILT-IN-CLASS); and it
  976. is also true that functionality may reside not only in methods but
  977. also in (unspecializable) functions and macros.
  978.  
  979.  
  980.     Lawrence G. Mayka
  981.     AT&T Bell Laboratories
  982.     lgm@iexist.att.com
  983.  
  984. Standard disclaimer.
  985.  
  986. ------- Message 4
  987.  
  988. Path: hplabsz!hplabs!sdd.hp.com!zaphod.mps.ohio-state.edu!van-bc!ubc-cs!uw-beaver!zephyr.ens.tek.com!tekchips!tekgvs!toma
  989. From: toma@tekgvs.LABS.TEK.COM (Tom Almy)
  990. Newsgroups: comp.lang.lisp.x
  991. Subject: Re: xlisp 2.1/winterp internals (26K long)
  992. Message-ID: <8444@tekgvs.LABS.TEK.COM>
  993. Date: 17 Nov 90 17:47:38 GMT
  994. References: <JSP.90Nov15145624@glia.u.washington.edu> <8440@tekgvs.LABS.TEK.COM>
  995. Reply-To: toma@tekgvs.LABS.TEK.COM (Tom Almy)
  996. Distribution: comp
  997. Organization: Tektronix, Inc., Beaverton,  OR.
  998. Lines: 22
  999.  
  1000. I hate to followup to my own posting, but:
  1001.  
  1002. 1. I appologise for all the CR characters at the end of each line.
  1003. 2. I realized I made a mistake. 
  1004. The original posting:
  1005. >>xlapply, xlevform and sendmsg will issue an error if they encounter a
  1006. >>s_macro CLOSURE.  This is presumably because all macros are expanded
  1007. >>by xleval.c:xlclose when it builds a closure.
  1008.  
  1009. >>Neither xlapply nor sendmsg will handle FSUBRs.  This is presumably
  1010. >>a minor bug, left due to the difficulty of keeping arguments
  1011. >>unevaluated to that point. ?
  1012.  
  1013. Corrected reply:
  1014. Common Lisp does not allow APPLYing a macro or special form (FSUBR).
  1015. This is based on the evaluation model.
  1016. Since SEND is a subr, all of its arguments are already evaluated so it
  1017. is already too late to have macro or fsubr methods.
  1018.  
  1019. Tom Almy
  1020. toma@tekgvs.labs.tek.com
  1021. Standard Disclaimers Apply
  1022.  
  1023. ------- Message 5
  1024.  
  1025. Path: hplabsz!hplabs!sdd.hp.com!uakari.primate.wisc.edu!dali.cs.montana.edu!milton!milton.u.washington.edu!jsp
  1026. From: jsp@glia.biostr.washington.edu. (Jeff Prothero)
  1027. Newsgroups: comp.lang.lisp.x
  1028. Subject: Re: xlisp 2.1/winterp internals (26K long)
  1029. Message-ID: <JSP.90Nov17111311@glia.biostr.washington.edu.>
  1030. Date: 17 Nov 90 19:13:11 GMT
  1031. References: <JSP.90Nov15145624@glia.u.washington.edu>
  1032.     <8440@tekgvs.LABS.TEK.COM>
  1033. Sender: news@milton.u.washington.edu
  1034. Distribution: comp
  1035. Organization: Biological Structure, U of Wash, Seattle
  1036. Lines: 42
  1037. In-reply-to: toma@tekgvs.LABS.TEK.COM's message of 16 Nov 90 21:13:29 GMT
  1038.  
  1039. In article <8440@tekgvs.LABS.TEK.COM> toma@tekgvs.LABS.TEK.COM (Tom Almy) writes:
  1040.  
  1041. >[lots of good clarifications of stuff I was fuzzy on]
  1042.  
  1043. Thanks!  I'm going to merge your comments into internals.doc
  1044. (with a global acknowledgement) if you don't object...
  1045.  
  1046. >[good examples of lexical binding of fns vs variables]
  1047. >You couldn't do this with a combined binding list.
  1048.  
  1049. Um.  Clearly, the lexical binding mechanism needs to distiguish
  1050. between binding of functions and variables.  Maybe I'm a little slow,
  1051. but it is still not obvious to me that you need separate xlenv and
  1052. xlfenv base pointers.  For example, I don't see simultaneous pushing
  1053. of one stack and popping of the other.
  1054.  
  1055. >>Vectors, chars, integers and so forth
  1056. >>stand outside the object hierarchy -- you can't send messages to them.
  1057. >>I'm not sure why Dave did it this way.
  1058.  
  1059. >Probably because the object facility is an extension of lisp. You can
  1060. >create classes of these things. There is also efficiency considerations.
  1061. >The only object oriented programming language I know of where everything
  1062. >is an object is Smalltalk, but if you look at the implementation, it does
  1063. >cheat at the low level to speed things up.
  1064.  
  1065. Doing CAR and CDR via messages (say), would clearly have a big
  1066. efficiency impact (barring very smart compilation).  But defining a
  1067. CLASS string and supporting messages to strings (say), doesn't present
  1068. any serious (to me) efficiency issues.  It means that xlobj.c:xsend(),
  1069. instead of using xlgaobject(), needs to check for n_type != OBJECT,
  1070. and if so, get the apppropriate class object by using n_type to index
  1071. into a C array.  Added cost to a normal xlisp message-send: about two
  1072. machine instructions.  The win would be that you could safely send
  1073. (say) a :SHOW message to any LVAL, instead of needing special-case
  1074. checking in the xlisp code. No?
  1075.  
  1076.  
  1077. - --
  1078.      jsp@glia.biostr.washington.edu (Jeff Prothero)
  1079.      jsp@u.washington.edu (If above bounces.)
  1080.      Biological Structure Graphics Lab, U Washington
  1081.  
  1082. ------- End of Forwarded Messages
  1083.