home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / files / language / xlisp2_1 / xlimpl.doc < prev    next >
Encoding:
Text File  |  1993-10-23  |  26.9 KB  |  703 lines

  1. Forwarded: Fri, 16 Nov 90 16:08:00 PST
  2. Forwarded: winterp@hplnpm.hpl.hp.com
  3. Path: hplabsz!hplabs!ucbvax!bloom-beacon!mintaka!ogicse!milton!milton.u.washington.edu!jsp
  4. From: jsp@glia.u.washington.edu (Jeff Prothero)
  5. Newsgroups: comp.lang.lisp.x
  6. Subject: xlisp 2.1/winterp internals (26K long)
  7. Message-ID: <JSP.90Nov15145624@glia.u.washington.edu>
  8. Date: 15 Nov 90 22:56:23 GMT
  9. Sender: news@milton.u.washington.edu
  10. Distribution: comp
  11. Organization: Biological Structure, U of Wash, Seattle
  12. Lines: 689
  13.  
  14. I've just finished reading the xlisp 2.1 source code for the first
  15. time.  The tutorial and reference material included with the winterp
  16. distribution are well done, but I would have liked an overview of the
  17. interpreter internals.  Here's a first cut at such a document.
  18. Comments welcome...
  19.  
  20. ---------------------------cut here-------------------------------
  21.  
  22. 90Nov13 jsp@milton.u.washington.edu       Public Domain.
  23.  
  24.                    +---------------------+
  25.                    | xlisp 2.1 internals |
  26.                    +---------------------+
  27.  
  28.  
  29.  Who should read this?
  30.  ---------------------
  31.  
  32. Anyone poking through the C implementation of xlisp for the first
  33. time.  This is intended to provide a rough roadmap of the global xlisp
  34. structures and algorithms.  If you just want to write lisp code in
  35. xlisp, you don't need to read this file -- go read xlisp.doc,
  36. XlispOOP.doc, and XlispRef.doc, in about that order.  If you want to
  37. tinker with the xlisp implementation code, you should *still* read
  38. those three before reading this.  The following isn't intended to be
  39. exhaustively precise -- that's what the source code is for!  It is
  40. intended only to give you sufficient orientation give you a fighting
  41. chance to understand the code the first time through, instead of the
  42. third time.
  43.  
  44. At the bottom of the file you'll find an example of how to add new
  45. primitive functions to xlisp.
  46.  
  47.  
  48.  
  49.  What is an LVAL?
  50.  ----------------
  51.  
  52. An "LVAL" is the C type for a generic pointer to an xlisp
  53. garbage-collectable something.  (Cons cell, object, string, closure,
  54. symbol, vector, whatever.)  Virtually every variable in the
  55. interpreter is an LVAL.  Cons cells contain two LVAL slots,
  56. symbols contains four LVAL slots, etc.
  57.  
  58.  
  59.  
  60.  What is the obarry?
  61.  ------------------
  62.  
  63. The obarray is the xlisp symbol table.  More precisely, it is is a
  64. hashtable mapping ascii strings (symbol names) to symbols.  ("obarray"
  65. is a misnomer, since it contains only xlisp SYMBOLs, and in particular
  66. contains no xlisp OBJECTs.)  It is used when converting lisp
  67. expressions from text to internal form. Since it is a root for the
  68. garbage collector, it also serves to distinguish permanent
  69. global-variable symbols from other symbols -- you can permanently
  70. protect a symbol from the garbage collector by entering it into the
  71. obarray.  This is called "interning" the symbol.  The obarray is
  72. called "obarray" in C and "*OBARRAY*" in xlisp.
  73.  
  74.  
  75.  
  76.  The Interpreter Stacks
  77.  ----------------------
  78.  
  79. xlisp uses two stacks, an "evaluation stack" and an "argument stack".
  80. Both are roots for the garbage collector.  The evaluation stack is
  81. largely private to the interpreter and protects internal values from
  82. garbage collection, while the argument stack holds the conventional
  83. user-visible stackframes.
  84.  
  85.  
  86. The evaluation stack is an EDEPTH-long array of "LVAL" allocated by
  87. xldmem.c:xlminit().  It grows zeroward.
  88.  
  89. xlstkbase points to the zero-near end of the evaluation stack.
  90.  
  91. xlstktop points to the zero-far end of the evaluation stack: the
  92. occupied part of the stack lies between xlstack and xlstktop.  NOTE
  93. that xlstktop is *NOT* the top of the stack in the conventional sense
  94. of indicating the most recent entry on the stack: xlstktop is a static
  95. bounds pointer which never changes once the stack is allocated.
  96.  
  97. xlstack starts at the zero-far end of the evaluation stack.  *xlstack
  98. is the most recent LVAL on the stack.  The garbage collector MARKs
  99. everything reachable from the evaluation stack (among other things),
  100. so we frequently push things on this stack while C code is
  101. manipulating them. (Via xlsave(), xlprotect(), xlsave1(), xlprot1().)
  102.  
  103.  
  104. The argument stack is an ADEPTH-long array of "LVAL".  It also grows
  105. zeroward.  The evaluator pushes arguments on the argument stack at the
  106. start of a function call (form evaluation).  Built-in functions
  107. usually eat them directly off the stack.  For user-lisp functions
  108. xleval.c:evfun() pops them off the stack and binds them to the
  109. appropriate symbols before beginning execution of the function body
  110. proper.
  111.  
  112. xlargstkbase is the zero-near end of argument stack.
  113.  
  114. xlargstktop is the zero-far end of argument stack.  Like xlstktop,
  115. xlargstktop is a static bounds pointer which never changes after
  116. the stack is allocated.
  117.  
  118. *xlsp ("sp"=="stack pointer") is the most recent item on the argument stack.
  119.  
  120. xlfp ("fp"=="frame pointer") is the base of the current stackframe.
  121.  
  122.  
  123.  
  124.   What is a context?
  125.   ------------------
  126.  
  127. An xlisp "context" is something like a checkpoint, recording a
  128. particular point buried in the execution history so that we can
  129. abort/return back to it.  Contexts are used to implement call/return,
  130. catch/throw, signals, gotos, and breaks.  xlcontext points to the
  131. chain of active contexts, the top one being the second-newest active
  132. context.  (The newest -- that is, current -- active context is
  133. implemented by the variables xlstack xlenv xlfenv xldenv xlcontext
  134. xlargv xlargc xlfp xlsp.)  Context records are written by
  135. xljump.c:xlbegin() and read by xljump.c:xljump().  Context records are
  136. C structures on the C program stack; They are not in the dynamic
  137. memory pool or on the lisp execution or argument stacks.
  138.  
  139.  
  140.  
  141.   What is an environment?
  142.   -----------------------
  143.  
  144. An environment is basically a store of symbol-value pairs, used to
  145. resolve variable references by the lisp program.  xlisp maintains
  146. three environments, in the global variables xlenv, xlfenv and xldenv.
  147.  
  148. xlenv and xlfenf are conceptually a single environment, although they
  149. are implemented separately.  They are linked-list stacks which are
  150. pushed when we enter a function and popped when we exit it.  We also
  151. switch xlenv+xlfenf environments entirely when we begin executing a
  152. new closure (user-fn written in lisp).
  153.  
  154. The xlenv environment is the most heavily used environment.  It is
  155. used to resolve everyday data references to local variables.  It
  156. consists of a list of frames (and objects).  Each frame is a list of
  157. sym-val pairs.  In the case of an object, we check all the instance
  158. and class variables of the object, then do the same for its
  159. superclass, until we run out of superclasses.
  160.  
  161. The xlfenv environment is maintained strictly parallel to xlenv, but
  162. is used to find function values instead of variable values.  The
  163. separation may be partly for lookup speed and partly for historical
  164. reasons.
  165.  
  166. When we send a message, we set xlenv to the value it had when the
  167. message CLOSURE was built, then push on (obj msg-class), where
  168. msg-class is the [super]class defining the method.  (We also set
  169. xlfenv to the value xlfenv had when the method was built.)  This makes
  170. the object instance variables part of the environment, and saves the
  171. information needed to correctly resolve references to class variables,
  172. and to implement SEND-SUPER.
  173.  
  174. The xldenv environment tracks the old values of global variables which
  175. we have changed but intend to restore later to their original values,
  176. particularly when we bind and unbind s_evalhook and s_applyhook
  177. (*EVALHOOK* and *APPLYHOOK*).  (This is mostly to support the debug
  178. facilities.)  It is a simple list of sym-val pairs,
  179. treated as a stack.
  180.  
  181. These environments are manipulated in C via the xlisp.h macros
  182. xlframe(e), xlbind(s,v), xlfbind(s,v), xlpbind(s,v,e), xldbind(s,v),
  183. xlunbind(e).
  184.  
  185.  
  186.  
  187.   How are xlisp entities stored and identified?
  188.   ---------------------------------------------
  189.  
  190. Conceptually, xlisp manages memory as a single array of fixed-size
  191. objects.  Keeping all objects the same size simplifies memory
  192. management enormously, since any object can be allocated anywhere, and
  193. complex compacting schemes aren't needed.  Every LVAL pointer points
  194. somewhere in this array.  Every xlisp object has the basic format
  195. (xldmem.h:typdef struct node)
  196.  
  197.  struct node {
  198.      char n_type;
  199.      char n_flags;
  200.      LVAL car;
  201.      LVAL cdr;
  202.  }
  203.  
  204. where n_type is one of:
  205.  
  206.  FREE     A node on the freelist.
  207.  SUBR     A function implemented in C. (Needs evalutated arguments.)
  208.  FSUBR    A special function implemented in C. (Needs unevaluated arguments).
  209.  CONS     A regular lisp cons cell.
  210.  SYMBOL   A symbol.
  211.  FIXNUM   An integer.
  212.  FLONUM   A floating-point number.
  213.  STRING   A string.
  214.  OBJECT   Any object, including class objects.
  215.  STREAM   An input or output file.
  216.  VECTOR      A variable-size array of LVALs.
  217.  CLOSURE  Result of DEFUN or LAMBDA -- a function written in lisp.
  218.  CHAR      An ascii character.
  219.  USTREAM  An internal stream.
  220.  STRUCT      A structure.
  221.  
  222. Messages may be sent only to nodes with n_type == OBJECT.
  223.  
  224. Obviously, several of the above types won't fit in a fixed-size
  225. two-slot node.  The escape is to have them malloc() some memory
  226. and have one of the slots point to it -- VECTOR is the archetype.  For
  227. example, see xldmem.c:newvector().  To some extent, this malloc()
  228. hack simply exports the memory- fragmentation problem to the C
  229. malloc()/free() routines.  However, it helps keep xlisp simple, and it
  230. has the happy side-effect of unpinning the body of the vector, so that
  231. vectors can easily be expanded and contracted.
  232.  
  233. The garbage collector has special-case code for each of the above node
  234. types, so it can find all LVAL slots and recycle any malloc()ed ram
  235. when a node is garbage-collected.
  236.  
  237. Xlisp pre-allocates nodes for all ascii characters, and for small
  238. integers.  These nodes are never garbage-collected.
  239.  
  240. As a practical matter, allocating all nodes in a single array is not
  241. very sensible.  Instead, nodes are allocated as needed, in segments of
  242. one or two thousand nodes, and the segments linked by a pointer chain
  243. rooted at xldmem.c:segs.
  244.  
  245.  
  246.  
  247.   How are vectors implemented?
  248.   ----------------------------
  249.  
  250. An xlisp vector is a generic array of LVAL slots.  Vectors are also
  251. the canonical illustration of xlisp's escape mechanism for node types
  252. which need more than two LVAL slots (the maximum possible in the
  253. fixed-size nodes in the dynamic memory pool).  The node CAR/CDR slots
  254. for a vector hold a size field plus a pointer to a malloc()ed ram
  255. chunk, which is automatically free()ed when the vector is
  256. garbage-collected.
  257.  
  258. xldmem.h defines macros for reading and writing vector fields and
  259. slots: getsize(), getelement() and setelement().  It also defines
  260. macros for accessing each of the other types of xlisp nodes.
  261.  
  262.  
  263.  
  264.   How are strings implemented?
  265.   ---------------------------- 
  266.  
  267. Strings work much like vectors: The node has a pointer to a malloc()ed
  268. ram chunk which is automatically free()ed when the string gets
  269. garbage-collected.
  270.  
  271.  
  272.  
  273.  How are symbols implemented?
  274.  ----------------------------
  275.  
  276. A symbol is a generic user-visible lisp variable, with separate slots
  277. for print name, value, function, and property list.  Any or all of
  278. these slots (including name) may be NIL.  You create a symbol in C by
  279. calling "xlmakesym(name)" or "xlenter(name)" (to make a symbol and
  280. enter it in the obarray). You create a symbol in xlisp by using the
  281. single-quote operator: "'name", or by calling "(gensym)", or
  282. indirectly in various ways.  Most of the symbol-specific code in the
  283. interpreter is in xlsym.c.
  284.  
  285. Physically, a symbol is implemented like a four-slot vector.
  286.  
  287. Random musing: Abstractly, the LISP symbols plus cons cells (etc)
  288. constitute a single directed graph, and the symbols mark spots where
  289. normal recursive evaluation should stop.  Normal lisp programming
  290. practice is to have a symbol in every cycle in the graph, so that
  291. recursive traversal can be done without MARK bits.
  292.  
  293.  
  294.  
  295.   How are closures implemented?
  296.   -----------------------------
  297.  
  298. A closure, the return value from a lambda, is a regular coded-in-lisp
  299. fn.  Physically, it it implemented like an eleven-slot vector, with the
  300. node n_type field hacked to contain CLOSURE instead of VECTOR. The
  301. vector slots contain:
  302.  
  303.  name   symbol -- 1st arg of DEFUN.  NIL for LAMBDA closures.
  304.  type   (s_lambda or s_macro). Must be s_lambda to be executable.
  305.  args   List of "required" formal arguments (as symbols)
  306.  oargs  List of "optional" args, each like: (name (default specified-p))
  307.  rest   Name of "&rest" formal arg, else NIL.
  308.  kargs  keyword args, each like: ((':foo 'bar default specified-p))
  309.  aargs  &aux vars, each like: (('arg default))
  310.  body   actual code (as lisp list) for fn.
  311.  env    value of xlenv when the closure was built.  NIL for macros.
  312.  fenv   value of xlfend when the closure was built. NIL for macros.
  313.  lambda The original formal args list in the DEFUN or LAMBDA.
  314.  
  315. The lambda field is for printout purposes.  The remaining fields store
  316. a predigested version of the formal args list.  This is a limited form
  317. of compilation: by processing the args list at closure-creation time,
  318. we reduce the work needed during calls to the closure.
  319.  
  320.  
  321.  
  322.   How are objects implemented?
  323.   ----------------------------
  324.  
  325. An object is implemented like a vector, with the size determined by
  326. the number of instance variables.  The first slot in the vector points
  327. to the class of the object; the remaining slots hold the instance
  328. variables for the object.  An object needs enough slots to hold all
  329. the instance variables defined by its class, *plus* all the instance
  330. variables defined by all of its superclasses.
  331.  
  332.  
  333.  
  334.   How are classes implemented?
  335.   ----------------------------
  336.  
  337. A class is a specific kind of object, hence has a class pointer plus
  338. instance variables.  All classes have the following instance variables:
  339.  
  340.  MESSAGES   A list of (interned-symbol method-closure) pairs.
  341.  IVARS        Instance variable names: A list of interned symbols.
  342.  CVARS      Class variable names:    A list of interned symbols.
  343.  CVALS      Class variable values:   A vector of values.
  344.  SUPERCLASS A pointer to the superclass.
  345.  IVARCNT    Number of class instance variables, as a fixnum.
  346.  IVARTOTAL  Total number of instance variables, as a fixnum.
  347.  
  348. IVARCNT is the count of the number of instance variables defined by
  349. our class.  IVARTOTAL is the total number of instance variables in an
  350. object of this class -- IVARCNT for this class plus the IVARCNTs from
  351. all of our superclasses.
  352.  
  353.  
  354.  
  355.  
  356.   How is the class hierarchy laid out?
  357.   ------------------------------------
  358.  
  359. The fundamental objects are the OBJECT and CLASS class objects.  (Both
  360. are instances of class CLASS, and since CLASSes are a particular kind
  361. of OBJECT, both are also objects, with n_type==OBJECT.  Bear with me!)
  362.  
  363. OBJECT is the root of the class hierarchy: everything you can send a
  364. message to is of type OBJECT.  (Vectors, chars, integers and so forth
  365. stand outside the object hierarchy -- you can't send messages to them.
  366. I'm not sure why Dave did it this way.) OBJECT defines the messages:
  367.  
  368.  :isnew -- Does nothing
  369.  :class -- Returns contents of class-pointer slot.
  370.  :show  -- Prints names of obj, obj->class and instance vars.
  371.  
  372. A CLASS is a specialized type of OBJECT (with instance variables like
  373. MESSAGES which generic OBJECTs lack), class CLASS hence has class
  374. OBJECT as its superclass.  The CLASS object defines the messages:
  375.  
  376.  :new     -- Create new object with self.IVARTOTAL LVAR slots, plus
  377.             one for the class pointer. Point class slot to self.
  378.             Set new.n_type char to OBJECT.
  379.  :isnew     -- Fill in IVARS, CVARS, CVALS, SUPERCLASS, IVARCNT and
  380.             IVARTOTAL, using parameters from :new call.  (The
  381.             :isnew msg inherits the :new msg parameters because
  382.             the  :isnew msg is generated automatically after
  383.             each :new   msg, courtesy of a special hack in
  384.             xlobj.c:sendmsg().)
  385.  :answer -- Add a (msg closure) pair to self.MESSAGES.
  386.  
  387.  
  388.  
  389. Here's a figure to summarize the above, with a generic object thrown
  390. in for good measure.  Note that all instances of CLASS will have a
  391. SUPERCLASS pointer, but no normal object will.  Note also that the
  392. messages known to an object are those which can be reached by
  393. following exactly one Class Ptr and then zero or more Superclass Ptrs.
  394. For example, the generic object can respond to :ISNEW, :CLASS and
  395. :SHOW, but not to :NEW or :ANSWER.  (The functions implementing the
  396. given messages are shown in parentheses.)
  397.  
  398.                     NIL
  399.                      ^
  400.                      |
  401.                      |Superclass Ptr
  402.                      |
  403.                 Msg+--------+
  404.  :isnew (xlobj.c:obisnew) <----|  class |Class Ptr
  405.  :class (xlobj.c:obclass) <----| OBJECT |------------+
  406.  :show    (xlobj.c:objshow) <----|        |            |
  407.                    +--------+            |
  408.        +---------+                ^  ^               |
  409.        | generic |Class Ptr       |  |               |
  410.        | object  |----------------+  |Superclass Ptr |
  411.        +---------+             |               |
  412.                 Msg+--------+            |
  413.  :isnew    (xlobj.c:clnew)      <----| class  |Class Ptr   |
  414.  :new    (xlobj.c:clisnew) <----| CLASS  |--------+   |
  415.  :answer(xlobj.c:clanswer)<----|        |        |   |
  416.                    +--------+        |   |
  417.                   ^  ^           |   |
  418.                   |  |           |   |
  419.                   |  +-----------+   |
  420.                   +------------------+
  421.  
  422.  
  423. Thus, class CLASS inherits the :CLASS and :SHOW messages from class
  424. OBJECT, overrides the default :ISNEW message, and provides new the
  425. messages :NEW and :ANSWER.
  426.  
  427. New classes are created by (send CLASS :NEW ...) messages.  Their
  428. Class Ptr will point to CLASS.  By default, they will have OBJECT as
  429. their superclass, but this can be overridden by the second optional
  430. argument to :NEW.
  431.  
  432. The above basic structure is set up by xlobj.c:xloinit().
  433.  
  434.  
  435.  
  436.   How do we look up the value of a variable?
  437.   ------------------------------------------
  438.  
  439. When we're cruising along evaluating an expression and encounter a
  440. symbol, the symbol might refer to a global variable, an instance
  441. variable, or a class variable in any of our superclasses.  Figuring
  442. out which means digging throught the environment.  The canonical place
  443. this happens is in xleval.c:xleval(), which simply passes the buck to
  444. xlsym.c:xlgetvalue(), which in turn passes the buck to
  445. xlxsym.c:xlxgetvalue(), where the fun of scanning down xlenv begins.
  446. The xlenv environment looks something like
  447.  
  448.      Backbone    Environment frame contents
  449.      --------    --------------------------
  450. xlenv --> frame      ((sym val) (sym val) (sym val) ... )
  451.       frame      ...
  452.       object     (obj msg-class)
  453.       frame      ...
  454.       object     ...
  455.       frame      ...
  456.       ...
  457.  
  458. The "frame" lines are due to everyday nested constructs like LET
  459. expressions, while the "object" lines represent an object environment
  460. entered via a message send.  xlxgetvalue scans the enviroment left to
  461. right, and then top to bottom.  It scans down the regular environment
  462. frames itself, and calls xlobj.c:xlobjgetvalue() to search the object
  463. environment frames.
  464.  
  465. xlobjgetvalue() first searches for the symbol in the msg-class, then
  466. in all the successive superclasses of msg-class.  In each class, it
  467. first checks the list of instance-variable names in the IVARS slot,
  468. then the list of class-variables name in the CVARS slot.
  469.  
  470.   
  471.  
  472.   How are function calls implemented?
  473.   -----------------------------------
  474.  
  475. xleval.c contains the central expression-evaluation code.
  476. xleval.c:xleval() is the standard top-level entrypoint.  The two
  477. central functions are xleval.c:xlevform() and xleval.c:evfun().
  478. xlevform() can evaluate four kinds of expression nodes:
  479.  
  480. SUBR: A normal primitive fn coded in C.  We call evpushargs() to
  481. evaluate and push the arguments, then call the primitive.
  482.  
  483. FSUBR: A special primitive fn coded in C, which (like IF) wants its
  484. arguments unevaluated.  We call pushargs() (instead of evpushargs())
  485. and then the C fn.
  486.  
  487. CLOSURE: A preprocessed written-in-lisp fn from a DEFUN or LAMBDA.  We
  488. call evpushargs() and then evfun().
  489.  
  490. CONS: We issue an error if it isn't a LAMBDA, otherwise we call
  491. xleval.c:xlclose() to build a CLOSURE from the LAMBDA, and fall into
  492. the CLOSURE code.
  493.  
  494. The common thread in all the above cases is that we call evpushargs()
  495. or pushargs() to push all the arguments on the evaluation stack,
  496. leaving the number and location of the arguments in the global
  497. variables xlargc and xlargv.  The primitive C functions consume
  498. their arguments directly from the argument stack.
  499.  
  500. xleval.c:evfun() evaluates a CLOSURE by:
  501.  
  502. (1) Switching xlenv and xlfenv to the values they had when
  503. the CLOSURE was built. (These values are recorded in the CLOSURE.)
  504.  
  505. (2) Binding the arguments to the environment.  This involves scanning
  506. through the section of the argument stack indicated by xlargc/xlargv,
  507. using information from the CLOSURE to resolve keyword arguments
  508. correctly and assign appropriate default values to optional arguments,
  509. among other things.
  510.  
  511. (3) Evaluating the body of the function via xleval.c:xleval().
  512.  
  513. (4) Cleaning up and restoring the original environment.
  514.  
  515.  
  516.  
  517.   How are message-sends implemented?
  518.   ----------------------------------
  519.  
  520. We scan the MESSAGES list in the CLASS object of the recipient,
  521. looking for a (message-symbol method) pair that matches our message
  522. symbol.  If necessary, we scan the MESSAGES lists of the recipients
  523. superclasses too.  (xlobj.c:sendmsg().)  Once we find it, we basically
  524. do a normal function evaluation. (xlobjl.c:evmethod().)  Two oddities:
  525. We need to replace the message-symbol by the recipient on the argument
  526. stack to make things look normal, and we need to push an 'object'
  527. stack entry on the xlenv environment so we remember which class is
  528. handling the message.
  529.  
  530.  
  531.  
  532.   How is garbage collection implemented?
  533.   --------------------------------------
  534.  
  535. The dynamic memory pool managed by xlisp consists of a chain of memory
  536. *segments* rooted at global C variable "segs".  Each segment contains
  537. an array of "struct node"s plus a pointer to the next segment.  Each
  538. node contains a n_type field and a MARK bit, which is zero except
  539. during garbage collection.
  540.  
  541. Xlisp uses a simple, classical mark-and-sweep garbage collector.  When
  542. it runs out of memory (fnodes==NIL), it does a recursive traversal
  543. setting the MARK flag on all nodes reachable from the obarray, the
  544. three environments xlenv/xlfenv/xldenv, and the evaluation and
  545. argument stacks.  (A "switch" on the n_type field tells us how to find
  546. all the LVAL slots in the node (plus associated storage), and a
  547. pointer-reversal trick lets us avoid using too much stack space during
  548. the traversal.)  sweep() then adds all un-MARKed LVALs to fnodes, and
  549. clears the MARK bit on the remaining nodes.  If this fails to produce
  550. enough free nodes, a new segment is malloc()ed.
  551.  
  552. The code to do this stuff is mostly in xldmem.c.
  553.  
  554.  
  555.  
  556.  How do I add a new primitive fn to xlisp?
  557.  -----------------------------------------
  558.  
  559. Add a line to the end of xlftab.c:funtab[].  This table contains a
  560. list of triples:
  561.  
  562. The first element of each triple is the function name as it will
  563. appear to the programmer. Make it all upper case.
  564.  
  565. The second element is S (for SUBR) if (like most fns) your function
  566. wants its arguments pre-evaluated, else F (for FSUBR).
  567.  
  568. The third element is the name of the C function to call.
  569.  
  570. Remember that your arguments arrive on the xlisp argument rather
  571. than via the usual C parameter mechanism.
  572.  
  573. CAUTION: Try to keep your files separate from generic xlisp files, and
  574. to minimize the number of changes you make in the generic xlisp files.
  575. This way, you'll have an easier time re-installing your changes when
  576. new versions of xlisp come out.  It's a good idea to put a marker
  577. (like a comment with your initials) on each line you change or insert
  578. in the generic xlisp fileset.  For example, if you are going to add
  579. many primitive functions to your xlisp, use an #include file rather
  580. than putting them all in xlftab.c.
  581.  
  582. CAUTION: Remember that you usually need to protect the LVAL variables
  583. in your function from the garbage-collector.  It never hurts to do
  584. this, and often produces obscure bugs if you dont.  Generic code for
  585. a new primitive fn:
  586.  
  587. /* xlsamplefun - do useless stuff. */
  588. /* Called like (samplefun '(a c b) 1 2.0) */
  589. LVAL xlsamplefun()
  590. {
  591.     /* Variables to hold the arguments: */
  592.     LVAL    list_arg, integer_arg, float_arg;
  593.  
  594.     /* Get the arguments, with appropriate errors */
  595.     /* if any are of the wrong type.  Look in     */
  596.     /* xlisp.h for macros to read other types of  */
  597.     /* arguments.  Look in xlmath.c for examples  */
  598.     /* of functions which can handle an argument  */
  599.     /* which may be either int or float:          */
  600.     list_arg    = xlgalist()  ;  /* "XLisp Get A LIST"   */
  601.     integer_arg = xlgafixnum();  /* "XLisp Get A FIXNUM" */
  602.     float_arg   = xlgaflonum();  /* "XLisp Get A FLONUM" */
  603.  
  604.     /* Issue an error message if there are any extra arguments: */
  605.     xllastarg();
  606.  
  607.  
  608.  
  609.     /* Call a separate C function to do the actual  */
  610.     /* work.  This way, the main function can       */
  611.     /* be called from both xlisp code and C code.   */
  612.     /* By convention, the name of the xlisp wrapper */
  613.     /* starts with "xl", and the native C function  */
  614.     /* has the same name minus the "xl" prefix:     */
  615.     return samplefun( list_arg, integer_arg, float_arg );
  616. }
  617. LVAL samplefun( list_arg, integer_arg, float_arg )
  618. LVAL            list_arg, integer_arg, float_arg;
  619. {
  620.     FIXTYPE val_of_integer_arg;
  621.     FLOTYPE val_of_float_arg;
  622.  
  623.     /* Variables which will point to LISP objects: */
  624.     LVAL result;
  625.  
  626.     LVAL list_ptr;
  627.     LVAL float_ptr;
  628.     LVAL int_ptr;
  629.  
  630.     /* Protect our internal pointers by */
  631.     /* pushing them on the evaluation   */
  632.     /* stack so the garbage collector   */
  633.     /* can't recycle them in the middle */
  634.     /* of the routine:                  */
  635.     xlstkcheck(3);    /* Make sure following xlsave */
  636.                       /* calls won't overrun stack. */
  637.     xlsave(list_ptr); /* Use xlsave1() if you don't */
  638.     xlsave(float_ptr);/* do an xlstkcheck().        */
  639.     xlsave(int_ptr);
  640.  
  641.     /* Create an internal list structure, protected */
  642.     /* against garbage collection until we exit fn: */
  643.     list_ptr = cons(list_arg,list_arg);
  644.  
  645.     /* Get the actual values of our fixnum and flonum: */
  646.     val_of_integer_arg = getfixnum( integer_arg );
  647.     val_of_float_arg   = getflonum( float_arg   );
  648.  
  649.  
  650.     /*******************************************/
  651.     /* You can have any amount of intermediate */
  652.     /* computations at this point in the fn... */
  653.     /*******************************************/
  654.  
  655.  
  656.     /* Make new numeric values to return: */
  657.     integer_ptr = cvflonum( val_of_integer_arg * 3   );
  658.     float_ptr   = cvflonum( val_of_float_arg   * 3.0 );
  659.  
  660.     /* Cons it all together to produce a return value: */
  661.     result = cons(
  662.         list_ptr,
  663.         cons(
  664.             integer_ptr,
  665.             cons(
  666.                 float_ptr,
  667.                 NIL
  668.             )
  669.         )
  670.     );
  671.  
  672.     /* Restore the stack, cancelling the xlsave()s: */
  673.     xlpopn(3); /* Use xlpop() for a single argument.*/
  674.  
  675.     return result;
  676. }
  677.  
  678.  
  679.  
  680.  
  681.  Minor Observations:
  682.  -------------------
  683.  
  684. xlapply, xlevform and sendmsg will issue an error if they encounter a
  685. s_macro CLOSURE.  This is presumably because all macros are expanded
  686. by xleval.c:xlclose when it builds a closure.
  687.  
  688. Neither xlapply nor sendmsg will handle FSUBRs.  This is presumably
  689. a minor bug, left due to the difficulty of keeping arguments
  690. unevaluated to that point. ?
  691.  
  692.  
  693.  Minor Mysteries:
  694.  ----------------
  695.  
  696. Why doesn't xlevform trace FSUBRs?  Is this a speed hack?
  697.  
  698. Why do both xlobj.c:xloinit() and xlobj.c:obsymvols() initialize the
  699. "object" and "class" variables?
  700. --
  701.  
  702. - Jeff   (S)
  703.