File r38/lisp/csl/cslbase/0notes artifact 6506921182 part of check-in 58a25bf8df


                      RANDOM NOTES ON CSL/CCL
                      =======================


The code tries to be in fairly conservative C, but in quite a lot
of places it depends on "the spirit of C" rather than the letter of
the language standards.

"machine.h" contains a catalogue of systems and compilers that the
code has AT SOME STAGE been at least tested once on. At present the
most tested configurations are
   Windows, Watcom C 11.5
   Linux x86, gcc
and other cases may well have glitches remaining. But I hope that the long
list of tested cases will give encouragement that the code is respectably
portable. Only the Windows version (using The Microsoft Foundation Classes)
has a currently-supported windowed interface: other versions use a command-
line style. Both Mac and X have at times experimenented with the "StdWin"
application framework but the code here does not guarantee to work with it
still or well.

I have adopted an indentation and code layout style that I like. I am aware
that it is not the default one supported my emacs etc, but I have been using
it for a long while and feel very set in my ways.

This Lisp uses an agressive garbage collector that MUST be able to identify
all root pointers reliably. C local variables are not marked by the GC, and
because the GC can relocate things a reference held in a simple C variable
is NOT SAFE across anything that could cause a GC. The macros push() and
pop() place data on a Lisp-safe stack. The coding conventions needed to make
GC safe are ones that take a significant time to get used to. They are not
coder-friendly. If you breach them then the code may work most of the time
but fail later in a messy way. I can only say "you have been warned"!

This Lisp needs to control error recovery and general unwinding. Thus any
function call that could possibly fail MUST be followed by a test to
see if it did. In some cases this will need to do pop() operations (which are
NOT automatically balanced across procedure calls: the programmer has to
take full responsibility for matchiing push and pops). See the code for
examples of what is needed. Again this is not coder-friendly and errors in
applying the conventions can have delayed and unpleasant effect. Again "you
have been warned"

Common Lisp supports functions that return multiple values. Thus the normal
simple case MUST use the onevalue() macro to indicate that what is returned
is a singleton. Failure to do this can mess up error recovery or the
garbage collector.

The above three issues lead to significantly ugly and delicate code. They
are easy for somebody whos is not neurotic enough to overlook. The
trouble that they can lead to will often be deferred, only showing up
occasionally on garbage collection or when recovering from an unrelated
exception. This situation where errors can go undetected for a long while
makes the need for care over these issues especially high priority!

For several platforms it seems to be a really good thing to be able to
respond well to asynchronous events. These may be "^C", or mouse clicks.
Especially with windowed systems some form of threading would be very very
helpful. This is hard wrt portability. Thus for essentially all systems I
use the idea of a "tick stream" of more or less regular events that can
be used to activate polling etc. Having tried genuine timer-based systems and
having had a LOT of trouble with them I mostly use a software count-down
scheme under the guard SOFTWARE_TICKS. If you feel tempted to discard this
in favour of real threads or of nothing then consider the issues of back-
porting to (eg) the Mac, or DOS where you must poll before even ^C is
noticed. I do not want this code to lose portability, please!

The basic data representation here is that small integers (and characters)
are represented as value types, with some of the low 3 bits used as tags.
Cons cells, vectors, big-numbers etc are handled by reference. By arranging
that all items are stored aligne dto an 8-byte boundary the low 3 bits
in a pointer are available for use as tags. More details can be seen in
"tags.h".  The most significant bit in a pointer is used for garbage
collection, so heap memory must all be allocated in one half of the
machine'e address space (it does not matter which!).

The Lisp system here was designed for delivery of code that had already
been developed and was basically working. It was not designed as a
development environment. This means that in some respects it is
inflexible! For instance there is a Lisp to C compiler, but it is
configured so that the generated C is statically linked into the Lisp.
The Lisp does not have very clever windowed interfaces, browsers etc.

The Lisp compiler has its master version coded in RLISP (an Algol-like
surface syntax for Lisp, popularised by the REDUCE algebra system). A
yacc-based parser can translate this into raw Lisp, but enhancements would
be better made to the RLISP version.

I leave a notation that sort-of looks like an attempt to nest comments
in places in the code where I know there are issues that have not been
fully resolved. Eg comments such as
   /* /* This is not yet fully up to Common Lisp specification */
etc etc. I do this because I can then make (some) C compilers generate
warnings that remind me of these worries. Ideally I would fix them all! Or
ideally C would have been specified with a #warn as well as and #error
preprocessor directive!

The notation "/* Signature ... */" present towards the top of each file
is maintained using a utility called "filesign". It provides a checksum
on the file, which I find much more reliable as a way of confirming
versions than date-stamps, especially when files are copied between
a number of different machines, perhaps in different time-zones.

[more random comments follow...]

double-execute() and undouble-execute() are like trace() and untrace() but
cause the nominated functions to double their costs. Only at first actually
does anything for functions with fixed numbers of args.


PRESERVE optional args.  First specifies a restart function
to be called, the second is a banner to display when the image is reloaded.
Reduce now uses this mechanism to gets its banner displayed. Third arg can be
stuff to pass to the restart function - passing is via a text-string interface
so it is a bit delicate!

Lexical closures supported in the bytecode compiler (for variable reference
but not GO or RETURN-FROM) and 3 new opcodes (closure, loadlex, storelex)
introduced in bytes1.c to support same.

By default the MSB of pointers is now determined at run time (rather than
being a manifest value compiled into the code). 

Support for calls to functions with large numbers of arguments is
improved. In particular in Common Lisp mode it is possible to have
more than 50 args.

Line-length overflow checking for printing of big numbers implemented.

binary printing for numbers, and case fold up as well as down, supported.

-------------------------------------------------------------------------------

DATA REPRESENTATION
===================

All objects in CSL are represented by 32-bit words(*), and each object in
memory is aligned at an 8-byte boundary.  The bottom 3 bits of a word
aare used as tags, and indicate how the rest of the word should be
interpreted.  In some cases the rest of the word is just direct information
(eg small integers are handled this way) and then the 0x8-bit is used by
the garbage collecter.  In other cases the 32-bit word contains the address
in memory of some object, and in that case its most significant bit is
reserved for use by the garbage collector.  Thus all valid addresses must have
the same sign (but this can be either + or -).  Many objects in memory have
a header word at their start that indicates their length and gives more
details about their type.  The encoding of object lengths means that the
largest possible single object in CSL would be 4 Mbytes long. Also CSL
memory is allocated in chunks (typically 256 Kbytes) and that puts another,
usually more severe, limit on maximum object size. This may matter for
arrays and bignums.

(*) well on some machines they may be 64-bits but not much changes!

The coding in the low bits of a word is as follows:

    x x x | x 0 0 0      cons cell (and NIL in Common Lisp mode)
    x x x | G 0 0 1      28-bit integer (G = garbage collector bit)
    x x x | G 0 1 0      characters, vec-hdrs, other oddities (immediate)
    x x x | G 0 1 1      28-bit short float if Common Lisp mode
    x x x | x 1 0 0      a symbol
    x x x | x 1 0 1      bignum. {rational number, complex number}
    x x x | x 1 1 0      Lisp vector (includes strings etc)
    x x x | x 1 1 1      pointer to boxed floating point number

Pointers reserve the 0x80000000L bit for the garbage collector,
assuming that all addresses are in the same half of the space.  This
of course is a slightly dodgy assumption that may be false on some
computers. On 64-bit machines it is again the top bit that is used by
the GC.



Header words in the vector heap

If the bottom 3 bits of a word are 010 (TAG_ODDS) the word is
treated as immediate data.  In that case the 1000 bit (GC_BIT_I)
is kept free for use as a mark bit.  The next bits up classify the
object:
          | x x 0 0 | * 0 1 0       valid immediate date in heap
                                    but can also be used as vector headers
          | x x 0 1 | * 0 1 0       symbol headers
          | x x 1 0 | * 0 1 0       number headers
          | x x 1 1 | * 0 1 0       vector headers

Data that could be in the heap or on the stack decodes further:

      x x | 0 0 0 0 | * 0 1 0       character
      x x | 0 1 0 0 | * 0 1 0       handle/offset to BPS page
      x x | 1 0 0 0 | * 0 1 0       [handle/offset to literal vector]
      x x | 1 1 0 0 | * 0 1 0       special marker (SPID) left on stack
                                    to help interpreter

The remaining 24 bits of the word are available for data.  The BPS and
literal vector handles in this case are for a segmented memory
implementation, and the 24 bit address has its top 10 bits identifying
a page number, then 14 bits giving a WORD offset into the associated
page, which can thus be up to 64 Kbytes large. Or more depending on
PAGE_BITS.  Literal vectors done this way are not used at present.
For Standard Lisp only 8 bits of character information is needed - the
remaining bits are there to support font etc information in a Common
Lisp world.


  . . . . | g f 0 1 | * 0 1 0       symbol header
        g   global variable
        f   fluid (special) variable
The remaining bits in the word are used as follows
    00000100  symbol names a special form
    00000200  symbol has a definition as a macro
    00000400  an unprinted gensym (so print-name is not complete yet)
    00000800  has a definition in C from the C-coded kernel
    00001000  just carries a code-pointer (codep test)
    00002000  any gensym, printed or not
    000fc000  fastget code in this is used as a p-list indicator
    00100000  traced
    00200000  is external in its home package
    ffc00000  reachability in first 10 packages!!!


The remaining header words hold the length (in bytes) of the object
(including the length of the header word) in the upper 22 bits of the
word.  This puts a limit of 4 Mbytes on the largest possible single
object in this Lisp.

      0 0 | 0 0 1 0 | * 0 1 0       bignum header word
      0 0 | 0 1 1 0 | * 0 1 0       ratnum
      0 0 | 1 0 1 0 | * 0 1 0       complex number
      0 0 | 1 1 1 0 | * 0 1 0       NOT USED (non-float number)
      0 1 | 0 0 1 0 | * 0 1 0       single float
      0 1 | 0 1 1 0 | * 0 1 0       double float
      0 1 | 1 0 1 0 | * 0 1 0       long float
      0 1 | 1 1 1 0 | * 0 1 0       NOT USED (floating number)


Note that headers for numbers are 0x|xx10|x010 and the 0x100 bit flags
floating point cases


      n n | n 0 1 1 | * 0 1 0       bitvector with nnn bits in last byte

      0 0 | 0 1 1 1 | * 0 1 0       string
      0 0 | 1 0 1 1 | * 0 1 0       (bitvector)
      0 0 | 1 1 1 1 | * 0 1 0       simple vector
      0 1 | 0 0 1 1 | * 0 1 0       (bitvector)
      0 1 | 0 1 1 1 | * 0 1 0       BPS
      0 1 | 1 0 1 1 | * 0 1 0       (bitvector)
      0 1 | 1 1 1 1 | * 0 1 0       hash table
      1 0 | 0 0 1 1 | * 0 1 0       (bitvector)
      1 0 | 0 1 1 1 | * 0 1 0       SPARE contains binary data
      1 0 | 1 0 1 1 | * 0 1 0       (bitvector)
      1 0 | 1 1 1 1 | * 0 1 0       Common Lisp array (header block)
      1 1 | 0 0 1 1 | * 0 1 0       (bitvector)
      1 1 | 0 1 1 1 | * 0 1 0       encapsulated stack pointer
      1 1 | 1 0 1 1 | * 0 1 0       (bitvector)
      1 1 | 1 1 1 1 | * 0 1 0       Common Lisp structure

                                    (use BPS for vector of 8-bit ints)
      1 0 | 0 0 1 0 | * 0 1 0       vector of 16-bit integers
      1 0 | 0 1 1 0 | * 0 1 0       vector of 32-bit integers
      1 0 | 1 0 1 0 | * 0 1 0       MIXED1 [3 words of pointers, then binary]
      1 0 | 1 1 1 0 | * 0 1 0       MIXED2
      1 1 | 0 0 1 0 | * 0 1 0       vector of single-precision floats
      1 1 | 0 1 1 0 | * 0 1 0       vector of double-precision floats
      1 1 | 1 0 1 0 | * 0 1 0       MIXED3
      1 1 | 1 1 1 0 | * 0 1 0       Stream handle (aka MIXED4)


-------------------------------------------------------------------------------

                  FAST PROPERTY ACCESS IN CSL/CCL
                  ===============================

The "fast-get" facility in CSL arranges that for a number of user-specified
property names (used with both GET and FLAGP) the access has constant cost.
Normally access to a property involves searching a property list, and if one
symbol has many properties this can be tedious - with a worst case in the
fairly common circumstance that the property is not found.

To speed up CSL I allocate 6 bits in a symbol header that influence how that
symbol is treated when used as a property name.  One code is reserved to
mean "use a normal property list", leaving 63 codes for fast access. The
first of these is reserved for a property called 'NONCOM because the built-in
ORDERP function wants to use this for compatibility with REDUCE(!!).

A tag must be marked as "fast" before any properties with that indicated are
set up.  If this constraint is not adhered to then properties can end up
stored in inconsistent or redundant forms.  The protocol is to select an
integer in the range 0 to 63 for each tag to be handled specially, and then
to make a call such as

    (symbol-make-fastget 'noncom           0)
    (symbol-make-fastget 'my_property_name 1)

    (flag '(a b c) 'noncom)
    (put 'x 'my_property_name 'y)
 or (setf (get 'x 'my_property_name) 'y)

The fast-get status of a symbol can be inspected using

    (symbol-fast-get 'x nil)

or reset to have no special treatment (please do not do this!)

    (symbol-fast-get 'x -1)

When a symbol is given a "fast" property a vector of length 63 is created
and a word in the symbol's header is made to point to it. The vector contains
either property values ir a marker that indicates that no property is
present.  A call (symbol-fastgets 'x) can retrieve the vector and can be
useful while debugging, maybe.

If you inspect the property list of an object (using the PLIST function) then
any fast properties are extracted from the vector and built onto the front
of the property list as returned. Of course this means that altering the
property list using RPLACx may not be useful!

The function GET retrieves a property. In Common Lisp mode the three-argument
version (GET a b c) allows c to be returned as a default value if the
property sought is not present. The two-argument version can not distinguish
between a property whose value is NIL and not having a property present
at all.  The function FLAGP returns T if a property is present (whatever
value is associated, including the case where the stored value is NIL). The
function FLAG just sets properties, giving them the value T in a somewhat
arbitrary way.

Clearly use of fast gets consumes memory, but because many of the extra
vectors contain many NIL elements they compress well in image files, which
therefore do not grow too badly.  To decide which properties are most
critical you can run tests using the profiled version of the bytecode
interpreter (bytes.c rather than bytes1.c), and after a run call the
function (BYTECOUNTS).   You need to adjust your build sequence so that
the file bytes.c is compiled with RECORD_GET defined, e.g. by putting
                   -DRECORD_GET
among the flags passed to your C compiler.  Note that this option will slow
things down noticeably.  The output from BYTECOUNTS indicates which property
tags were used, and how many unsuccessful searches for each tag occurred. This
latter information is collected because unsuccessful searches are the worst
case for a traditional property list search.

Note that when a property is handled "fast" it is not stored on the regular
property list (the only information about it is in the vector). Thus if the
most commonly used properties have been handled this way the average length
of property lists will shrink and so access to all other properties is
slightly speeded up too.

By editing FASTGET_SIZE in the source file "tags.h" if would be possible to
make the symbol extension vectors smaller than 63 items long, so in
cases where a much smaller set of tags is important the system can
be configured to save some memory.

-----------------------------------------------------------------------

[This issue was responded to during the Axiom customisation process]
Subject: Interface for dynamic opening and closing of libraries

Arthur:

I've discussed the specification of the "dynamic libraries" interface with
Barry and this is what we think we need.  For a user's own code, we will
always know the full pathname of the library it is in, so we can load the
module explicitly.

1. A function that loads a particular module from a particular library, e.g.
    (load-module <module> <pathname>)
   The library need not be open for input.  You might wish to have separate
   open-library, load-module, and close-library operations if that is more
   efficient.  

2. A function that opens a library for output, e.g.
    (open-library <pathname>)
   The function need not be responsible for checking if the library is open 
   already.

3. A function that reads a lisp source file, translates it into byte codes, 
   and writes the results into a named library, e.g.
    (fasl-out <filename> <library>)
   This could either:
     (a) Overwrite an existing module with the same name (our preferred option),
         or
     (b) replace the entire library if it already exists.

4. A function that closes an open library and, if option 3a above is 
   implemented, does all the necessary tidying-up, compaction etc, e.g.
    (close-library <pathname>)

5. Facilities for setting the library search path from within Lisp:
    (a) deleting a library from the search path;
    (b) adding a new library to the front of the search path;
    (c) adding a new library to the end of the search path.


------------------------------------------------------------------------

This shows the proportionate use of the various byte opcodes
on one particular test run. The information was collected while I was
trying to optimise the bytecode design. If any elaborate bytecode or
combination of bytecodes was getting very heavy use I might provide
special-case variants etc. Of course with different test programs the
profile may end up somewhat different.

    7.3400  LOADLOC6  
    3.8160  LOADFREE  
    3.6256  CARLOC6   
    2.8646  LOADFREE2 
    2.7618  EXIT      
    2.6747  PUSH      
    2.6054  LOADLOC1  
    2.2990  LITGET    
    2.2175  LOADLOC3  
    2.0495  STOREFREE 
    2.0072  JUMPNIL_L 
    2.0046  CONS      
    1.8176  BUILTIN2  
    1.7415  JUMPNIL   
    1.7210  LOADLOC0  
    1.5494  JUMPST1NI 
    1.5250  BUILTIN1  
    1.4593  JUMPNIL_B 
    1.4564  CALL1     
    1.4206  STORELOC1 
    1.3783  PUSHNIL2  
    1.3783  LOSE2     
    1.3624  FREERSTR  
    1.3624  FREEBIND  
    1.2805  JUMPT     
    1.2565  JUMP_B    
    1.1196  CDR       
    1.0389  LOADLOC5  
    1.0384  VNIL      
    0.9265  STORELOC2 
    0.9265  STORELOC0 
    0.9200  JUMPNEQCA 
    0.8966  CAR       
    0.8812  LOADLOC10 
    0.8712  JUMP_BL   
    0.8607  LOADLOC2  
    0.8244  STOREFREE 
    0.7828  LOADLOC4  
    0.7735  GREATERP  
    0.7659  JUMPNFLAG 
    0.7408  JUMPLITNE 
    0.7264  CALL2_1   
    0.7089  LOADLIT   
    0.6966  ADD1      
    0.6903  CARLOC2   
    0.6749  LOADLOC7  
    0.6739  JUMPT_BL  
    0.6556  CADR      
    0.6501  JUMPNATOM 
    0.6448  JUMPFLAGP 
    0.6395  CALL2     
    0.6369  JUMPLITEQ 
    0.6357  LOSE      
    0.6287  STORELOC  
    0.6274  SWOP      
    0.6003  LOADLIT4  
    0.5577  STORELOC5 
    0.5397  APPLY1    
    0.5004  LOADLOC9  
    0.4910  CDRLOC2   
    0.4772  JUMPL2T   
    0.4630  CARLOC10  
    0.4404  LOADLIT1  
    0.4196  CARLOC0   
    0.4178  CDRLOC0   
    0.4173  JUMPNIL_B 
    0.4029  JUMPFREE3 
    0.3861  STORELOC4 
    0.3767  STORELOC3 
    0.3659  NCONS     
    0.3613  LOADLOC8  
    0.3589  PUSHNIL   
    0.3164  JUMPB2NIL 
    0.3108  JUMPFREEN 
    0.3030  JUMPATOM  
    0.2955  JUMPFREET 
    0.2879  PUSHNILS  
    0.2823  LOADLOC   
    0.2672  CALL1_3   
    0.2587  CARLOC1   
    0.2554  CDDR      
    0.2515  CARLOC5   
    0.2469  CALL1_1   
    0.2457  LOADLOC11 
    0.2298  LOSES     
    0.2275  PUSHNIL3  
    0.2109  JUMPLIT2N 
    0.2024  NUMBERP   
    0.2013  CALL3     
    0.2004  JUMPT_L   
    0.1952  JUMPL0T   
    0.1930  CALL2R    
    0.1910  JUMPLIT1N 
    0.1840  JUMPL1T   
    0.1835  LOSE3     
    0.1789  STORELOC7 
    0.1785  CALL2_4   
    0.1706  CALL1_2   
    0.1696  CAARLOC2  
    0.1673  POP       
    0.1645  JUMPL1NIL 
    0.1582  LOADFREE1 
    0.1533  JCALL     
    0.1516  JUMPNE    
    0.1512  CARLOC4   
    0.1463  LOADLIT2  
    0.1418  JUMPL1NAT 
    0.1414  CARLOC11  
    0.1382  CDRLOC1   
    0.1373  CDRLOC5   
    0.1352  JUMPL0NIL 
    0.1350  JUMPB1NIL 
    0.1251  LOADLIT3  
    0.1238  LOADLIT5  
    0.1180  LOADFREE3 
    0.1173  CDAR      
    0.1163  CARLOC7   
    0.1127  CAAR      
    0.1120  JUMPST0NI 
    0.1108  ACONS     
    0.1080  LOADLIT6  
    0.1077  XCONS     
    0.1061  CALLN     
    0.0986  JUMPT_B   
    0.0973  CALL1_5   
    0.0959  CARLOC9   
    0.0890  LOADFREE4 

----------------------------------------------------------------------
[some comments from when NAG were getting Axiom support fully up to
scratch: these are now probably only of historical interest!]

15 May [PAB]	sys*.c: get_truename should preserve '/' at end of string
		print.c: fix memory leak in Ltruename
21 May [PAB]	syscwin.c: use STARTF_USESTDHANDLES
17 Jun [MCD]    scandir.c: Changed the NT version of scan_files to check
                  for a trailing '\' before adding '\*.*'.
12 Aug [MCD]    all files: merged with latest version from ACN.
14 Aug [MCD]    fns3.c: Added C-coded version of subseq and auxilliaries.
22 Aug [TTT]    arith03.c arith11.c: changed quotib,Cremainder to treat 
                properly the case  (DIVIDE  -134217728 134217728)
23 Aug [TTT]    fns1.c: changed a comment to refer to *break-loop* not *break-function*
30 Aug [TTT]    machine.h: SYS_TIMES macro name changed because of collision
		with system-defined macro in HP/UX
		also affected :sys.h restart.c sysunix.c sysvms.c sysxwin.c
20 Sep [TTT]    fns3.c: added hashtable-flavour thingies
9  Oct [TTT]    sockets.c: added glnag calls for all platforms
		added  NANQ support	
28 Oct [TTT]    scandir.c: use realloc_wnull instead of realloc 
		to cure SunOS4.1.2 problems with realloc(0,...)
20 Nov [TTT]	read.c: typo Lrdfn Lddfn fixed              
20 Nov [TTT]    read.c: Lrdf4 : changed the Lopen to honour :if-does-not-exist
                 keyword
21 Nov [MCD]    All files: merged latest changes from Arthur.
 2 Dec [TTT]    gc.c :  added timnag_ license management call in reclaim
 2 Dec [TTT]    csl.c:  added init_lm call in ENTRYPOINT
 3 Dec [TTT]    sockets.c: changed product codes to AX???23NA
                added nullary init_lm function 
------1997
24 Jan [TTT]    sysunix.c: changed mkdir(filename,0770) to
                mkdir(filename,0777) to respect world setting of umask 
                in create_directory.

23 April [MCD]  externs.h restart.c csl.c fasl.c fns1.c fns2.c eval2.c
                Introduced mechanism (using symbol_protect_flag) to toggle
                protection of symbols in kernel.
10 July [MCD] csl.c Added initialisation of symbol_protect_flag to 1
              syscwin.c: Fixed bug in truename which stuck extra trailing 
                slashes on directory names, causing directoryp to fail.
19 Sep [MCD] csl.c : Disabled CCL break messages on interrupt
3 Dec [PAB] read.c, gc.c: Added call-counting mode to mapstore 
 (yes, it is a hack).

-------------------------------------------------------------------------
I have just manufactured a version of CSL that is (perhaps) easy to call
from other C code.  What follows is the main chunk of code that shows
what the interface is.  What this means wrt REDUCE is that I will want to
manufacture an alternative to (BEGIN) that reads-simplifies-prints just
one REDUCE expression/command at a time rather than the current single
function that gobbles things for ever....

/*
 * The next fragment of code is to help with the use of CSL (and hence
 * packages written in Lisp and supported under CSL) as OEM products
 * embedded within larger C-coded packages.  There is (of course) a
 * significant issue about clashes between the names of external symbols
 * if CSL is to be linked with anything else, but I will not worry about that
 * just yet.
 * The protocol for calling Lisp code from C is as follows:
 *
 *     cslstart(argc, argv, writer);allocate memory and Lisp heap etc. Args
 *                                  should be "as if" CSL was being called
 *                                  directly and this was the main entrypoint.
 *                                  The extra arg accepts output from this
 *                                  stage.  Use NULL to get standard I/O.
 *     execute_lisp_function(fname, reader, writer);
 *                                  fname is a (C) string that names a Lisp
 *                                  function of 0 args.  This is called with
 *                                  stdin/stdout access redirected to use the
 *                                  two character-at-a-time functions passed
 *                                  down.  [Value returned indicates if
 *                                  the evaluation succeeded?]
 *     cslfinish(writer);           Tidies up ready to stop.
 */
 
int execute_lisp_function(char *fname,
    character_reader *r, character_writer *w)
{
    Lisp_Object nil;
    Lisp_Object ff = make_undefined_symbol(fname);
    nil = C_nil;
    if (exception_pending()) return 1;  /* Failed to make the symbol */
    procedural_input = r;
    procedural_output = w;
    Lapply0(nil, ff);
    procedural_input = NULL;
    procedural_output = NULL;
    nil = C_nil;
    if (exception_pending()) return 2;  /* Failure during evaluation */
    return 0;
}

#ifdef SAMPLE_OF_PROCEDURAL_INTERFACE

static char ibuff[100], obuff[100];
static int ibufp = 0, obufp = 0;
static int iget()
{
    int c = ibuff[ibufp++];
    if (c == 0) return EOF;
    else return c;
}

static void iput(int c)
{
    if (obufp < sizeof(obuff)-1)
    {   obuff[obufp++] = c;
        obuff[obufp] = 0;
    }
}

#endif

int MS_CDECL main(int argc, char *argv[])
{
    cslstart(argc, argv, NULL);
#ifdef SAMPLE_OF_PROCEDURAL_INTERFACE
    strcpy(ibuff, "(print '(a b c d))");
    execute_lisp_function("oem-supervisor", iget, iput);
    printf("Buffered output is <%s>\n", obuff);
#else
    cslaction();
#endif
    my_exit(cslfinish(NULL));
    return 0;
}

...............................end


Arthur Norman.    2002


REDUCE Historical
REDUCE Sourceforge Project | Historical SVN Repository | GitHub Mirror | SourceHut Mirror | NotABug Mirror | Chisel Mirror | Chisel RSS ]