Evaluation

Evaluators

In order to specify the behaviors upon an error and an interrupt(signal), set an appropriate function to each of the special variables error-handler* and signal-handler* in advance. There is no correctable or continue-able error. After analyzing errors you must abort the current execution by reset or appropriate throw to upper level catchers. reset is equivalent to (throw 0 NIL), since EusLisp’s top-level creates catch frame named 0.

Error handlers should be programmed as functions with three or four arguments: code msg1 form &optional (msg2). Code is the error code which identifies system defined errors, such as 14 for ’mismatch argument’ or 13 for ’undefined function’. These mappings are described in “c/eus.h”. msg1 and msg1 are messages displayed to the user. form is the S-expression which caused the error.

Signal handlers should be programmed as functions receiving two arguments: sig and code. Sig is the signal number ranging from 1 to 31, and code is the minor signal code defined in signal-number dependent manners.

^D (end-of-file) at the top-level terminates eus session. This is useful when eus is programmed as a filter.

Eval-dynamic is the function to find the dynamic value bound to a symbol used as a let or lambda variable. This is useful for debugging.

identity obj

returns obj itself. Note the difference between identity and quote. identity is a function whereas quote is a special form. Therefore, (identity ’abc) is evaluated to abc and (quote ’abc) == (quote (quote abc)) is evaluated to ’abc. Identity is often used as the default value for :key parameters of many generic sequence functions.

eval form &optional environment

evaluates form and returns its value. Hook function can be called before entering the evaluation, if evalhook* is set to some function that accept form and environment.

apply func &rest args

is applied to args. Func must be evaluated to be a function symbol (a symbol which has a function definition), a lambda form, or a closure. Macros and special forms cannot be applied. The last element of args must be a list of arguments while other args should be bare arguments. Thus, if the last args is NIL, then apply is almost equivalent to funcall, except that apply has one more arguments than funcall. (apply #’max 2 5 3 ’(8 2)) –> 8.

funcall func &rest args

applies func to args. The number of args must coincide to the number of arguments the func requests.

quote obj

evaluates to obj itself.

function func

makes a function closure. If func is a symbol, its function definition is retrieved.

evalhook hookfunc form &optional env

evaluates form once after binding hookfunc to evalhook*.

eval-dynamic variable

finds the value of variable (symbol) on the stack.

macroexpand form

expands form if it is a macro call. If form is expanded to a macro call again, expansion is repeated until non macro call results.

eval-when situation &rest forms

Situation is a list of compile, load and eval. Forms are evaluated when the current execution mode matches with situation. eval-when is important to control the behavior and environment of the compiler. If compile is specified, forms are evaluated by the compiler so that the result will affect the consequent compilation. For example, defmacro should be evaluated by the compiler in order to let the compiler expand macro calls at compile time. If load is given in the situation list, forms are compiled to be loaded (evaluated) at load time, i.e., compiled functions are defined at load time. This is the normal effect that we expect to the compiler. load situation is used to control the compiler’s environment. If eval is included in situation list, forms are evaluated when their source code is loaded.

the type form

Declares form is of type. type is either a class object, :integer, :fixnum, or :float.

declare &rest declarations

Each declaration is a list of a declaration specifier and an integer or target symbols. Declarations are important to let the compiler produce faster code.

special declares special variables

type declares the type of variables; (type integer count); valid type specifiers are , , and . The type keyword may be omitted if type specifier is either one listed here. So (integer count) is a correct declaration. Other types (classes) such as float-vector, integer-vector, etc. need to be preceded by type, as (type float-vector vec1).

ftype declares the result type of functions

optimize set *optimize* parameter (0–3) of the compiler

safety set *safety* parameter (0–3) of the compiler

space set *space* parameter (0–3) of the compiler

inline not recognized

not-inline not recognized

proclaim proclamation

globally declares the types of variables and compiler options. The same declarations are accepted as described for declare special form. However, proclaim is a function of one argument and proclamation is evaluated.

warn format-string &rest args

prints warning-message given as format-string and args to *error-output*.

error format-string &rest args

calls the current error-handler function bound to error-handler*. The default error-handler ’euserror’ first prints arguments to error-output* using format, then enters a new top level session. The prompt shows you the depth of your error session. Throwing to the number, you can go back to the lower level error session.

In the multithread EusLisp, special variables are shared among threads and the same error-handler* is referenced by different threads. To avoid this inconvenience, multithread EusLisp provides the install-error-handler function which installs different error handler for each thread.

lisp::install-error-handler handler

installs the handler as the error handler of the current thread.

Top-level Interaction

EusLisp’s standard top-level read-eval-print loop is controlled by eustop. When EusLisp is invoked, eustop tries to load the file named “.eusrc” in your home directory or the file specified by the EUSRC environment variable. It also tries to load a file named “.eusrc” in the current working directory. So, if you are in your home directory, note that .eusrc is loaded twice. Then EusLisp loads files specified in its argument list. After these loading, eustop enters normal interactive session.

When standard-input* is connected to user’s tty, eustop prints prompt generated by the toplevel-prompt function. The default toplevel-prompt prints “eus$ ”. The effect of changing the definition of toplevel-prompt appears when eustop is invoked next time. One way to change the prompt from the first time is to define toplevel-prompt in your .eusrc file.

Inputs are read from terminal-io* stream. If the input is parenthesized, it is taken as a lisp form and is evaluated by eval. Else if the first symbol of the input line has function definition, the line is automatically parenthesized and evaluated. If no function definition is found, then its special value is examined and the value is printed. If the symbol is unbound, the line is regarded as UNIX command and passed to sh (Bourn’s shell). If sh cannot find corresponding unix command, “command unrecognized” message is printed. Thus, eustop works both as a lisp interpreter and as a unix shell. If you do not wish to execute the input as UNIX command, you may escape the form by preceeding a comma ’,’ at the begining of the line. This is also useful to see the dynamic value binding when an error occurred in the interpretive execution. Since EusLisp adopts lexical scope, we cannot examine the value of local variables outside of the scope unless they are declared special.

If the environment variable, USE_TOP_SELECTOR, is defined, the toplevel input is read in an asynchronous manner using the select library call. The input stream (standard-input*) is registered to the top-selector*, which is an instance of the port-selector class, together with the read-eval-print function (repsel) Therefore arrival of key inputs invokes the evaluation of the repsel. This feature is particularly useful when EusLisp is to handle multiple events, i.e., key inputs, X window events, and socket connection requests, at the same time. In order to exploit this asynchronous toplevel interaction, users should never write a code that blocks at the read operation. Instead, the input stream should be registered to the top-selector* with its handler function by using the :add-port method. The handler function is expected to read from the stream, which is already known ready to return the input without blocking.

Note that Xwindow event handlers are defined to use the top-selector* implicitly when USE_TOP_SELECTOR is defined, and user programs do not have to call x:window-main-loop at all to catch X events.

Using the time-out of the select call, users may define a timer handler. Each time the select call times out, the function bound to timer-job* is invoked with no argument. The timer interval is defined by *top-selector-interval*, which is defaulted to 10.0 second. Note that the timer function invokation is not precisely periodic when there are inputs to the *top-selector*.

In the toplevel interaction, each line input is remembered in history* vector with a sequence number. You can recall a specific input by ! function as if you were in csh. The difference from csh’s history is, you need at least one white space between the exclamation mark and the sequence number since ! is a function. ^D (EOF) terminates EusLisp normally. To return abnormal termination code to upper level (usually a csh), use exit with an appropriate condition code.

eustop sets a signal handler only for SIGINT and SIGPIPE, and other signals are not caught. Thus, signals such as SIGTERM or SIGQUIT cause EusLisp to terminate. In order to catch these signals to avoid termination, use unix:signal function to set user-defined signal handlers.

- **

current input.

+ **

previous input.

++ **

old input.

+++ **

ancient input.

* **

previous result.

** **

old result.

*** **

ancient result.

*prompt-string* **

prompt string used by eustop.

*program-name* **

the command that invoked this EusLisp, possibly eus, eusx, eusxview or user-saved euslisp.

eustop &rest argv

is the default toplevel loop.

eussig sig code

is the default signal hander for SIGPIPE. eussig prints signal number upon its arrival and enters another toplevel loop.

sigint-handler sig code

is the default signal handler for SIGINT (control-C). It enters a new top level session.

euserror code message &rest arg

the default error handler that prints message and enters a new error session.

reset **

quits error loop and goes back to the outermost eustop session.

exit &optional termination-code

terminates EusLisp process and returns termination-code (0..255) as the process status code (0..255).

*top-selector* **

The port-selector object to handle asynchronous function invocation according to inputs from multiple streams.

h **

prints all the inputs remembered in history* vector with associated sequence numbers.

! &optional (seq 0)

recalls the input line associated with the sequence number seq. When seq is 0, the most recent command is recalled, and if seq is negative, the line is specified relatively to the current input. The recalled line is printed and the cursor is located at the end of the line. You can go backward by control-H (backspace) or control-B, go forward by control-F or control-K, go to the beginning of line by control-A, to the end of line by control-L. control-C cancels the line editing. control-M (carriage-return) or control-J (line-feed) finishes editing the line and starts evaluation of the edited line. If seq is not a number and is a symbol or a string, the history list is searched toward old input, and a command line which include the symbol or a string as a substring is returned.

new-history depth

initializes history* vector to have depth length. Depth input lines are remembered. All the input lines recorded in the current history* are discarded.

Compilation

EusLisp compiler is used to speed the execution of Lisp programs. You can expect 5 to 30 times faster execution and notable reduction of garbage collection time elapsed by macro expansion.

Euscomp does optimization for arithmetic operation and vector access. Sometimes proper type declarations are needed to inform the compiler applicability of optimization.

Compile-function compiles functions one by one. Compile-file compiles an entire source file. During the execution of Compile-file, each form in a file is read and evaluated. This may change the current EusLisp environment. For examples, defparameter may set a new value to a symbol and defun may substitute the existing compiled function with its non-compiled version. To avoid these unexpected effects, use the eval-when special form without compile time situation, or use euscomp command to run the compiler as a separate process.

Euscomp is a unix command, which is usually a symbolic link to eus. It recognizes several options. -O flag indicates optimization of the C compiler. Each of -O1,-O2, -O3 indicates optimization level of EusLisp compiler, which is equivalent to proclaiming (optimize 1 or 2 or 3). Each of -S0, -S1, -S2, -S3 set 0,1,2 and 3 to compiler:*safety*. If *safety* is less than 2, no code for checking interrupt is emitted, and you will lose control if the program enters an infinite loop. If *safety* is zero, the number of required arguments is not checked. -V flag is used to print function names when they are compiled (verbose). -c flag prevents from forking and exec’ing cc. -D pushes next argument to the features* list, which can be used for conditional compilation in conjunction with #- and #+ read-macro.

The compiler translates EusLisp source program named as “xxx.l” into the intermediate C program file named “xxx.c” and the header file named “xxx.h”. Then the C compiler is run and “xxx.o” is generated. Intermediate files “xxx.c” and “xxx.h” are left for the purpose of cross compilation: usually you only need to compile “xxx.c” files by cc unix command when you wish to use the code on machines of different architecture. Compiled code is loaded to EusLisp by ’(load “xxx”)’.

Each intermediate file refers to the “eus.h” header file, which is supposed to be located in the eusdir*/c directory. eusdir* is copied from the EUSDIR environment variable. If none is set, /usr/local/eus/ is taken as the default directory.

When compiled, intermediate C programs are usually much bigger than the original source code. For example, 1,161 lines of “l/common.l” lisp source expands to 8,194 lines of “l/common.c” and 544 lines of “l/common.h”. Compiling 1,000 lines of lisp source is not a hard task, but optimized compililation of nearly 10,000 lines of C program not only takes long time (several minutes), but also consumes much disk space. So if you are compiling relatively big programs, be sure your machine has sufficient /var/tmp disk, otherwise CC may die. Setting the TEMPDIR environment variable to a bigger disk slice may help.

As the linkage is performed at load-time or at run-time, no recompilation is required even the eus kernel is updated. On the other hand, run-time linkage may impose you another inconvenience. Suppose you have two functions A and B in a file “x.l” and A calls B. After compiling “x.l”, you load “x.o” and try to call A which internally calles B. Then you find a bug in B, and probably you would redefine B. Here, you have compiled A and non-compiled B. You may call A again, but nothing will change, since A still calls old compiled B which is rigidly linked when A first called B. To avoid this problem, A must be redefined again, or B must be redefined just after “x.o” is loaded and before A is called.

When a compiled-code is loaded, its top level code, which is normally a series of defun, defmethod, etc., is executed. This top level code is defined as the entry function of the load module. The compiler names the entry function, and the loader has to know the exact name of this function. To make the situation simple, both the compiler and the loader assume the entry function name is identical to the basename of the object file. For example, if you compile and load “fib.l”, the compiler produces “fib(…)” as the entry function of “fib.c”, and the loader looks for “fib” in the “fib.o” object file. Since the final object file is produced by “cc” and “ld” of unix, this entry function name has to satisfy the naming rule of C functions. Therefore, you have to avoid C’s reserved keywords such as “int”, “struct”, “union”, “register”, “extern”, etc., or the private identifiers defined in “c/eus.h” such as “pointer”, “cons”, “makeint”, etc., to be used as the name of the file. If you have to use one of these reserved words as the name of the source file, you specify it as :entry arguments of the compiler and the loader.

A restriction exists for the usage of closure: return-from special form in closures and clean-up forms in unwind-protect is not always correctly compiled.

Disassemble is not implemented. In order to analyze compiled code, see the intermediate C program or use adb.

euscomp &rest filename

Invokes EusLisp compiler.

compile-file = `[function]
srcfile &key = (verbose nil) = (optimize 2) (c-optimize 1) (safety 1) (pic t) (cc t) (entry (pathname-name file))

compiles a file. “.l” is assumed for the suffix of the srcfile. If :verbose is T, names of functions and methods being compiled are printed to make it easy to find the expressions where errors occurred. :Optimize, :c-optimize and :safety specifies the optimization levels. :Pic should be set T, unless the module is hard-linked in the EusLisp core during the make stage.

compile funcname

compiles a function. Compile first prints the function definition into a temporary file. The file is compiled by compile-file and then is loaded by load. Temporary files are deleted.

compile-file-if-src-newer srcfile &key compiler-options

compiles the srcfile if it is newer (more recently modified) than its corresponding object file. The object file is supposed to have the “.o” suffix.

compiler:*optimize* **

controls optimization level.

compiler:*verbose* **

When set to non-nil, the name of a function or a method being compiled, and the time required for the compilation are displayed.

compiler:*safety* **

controls safety level.

Program Loading

load = `[function]
fname &key = (verbose *load-verbose*) (package *package*) (entry (pathname-name fname)) (symbol-input nil) (symbol-output “”) (print nil) (ld-option “”)

Load is the function to read either a source file or a compiled object file into the EusLisp process. If the file specified by fname exists, it is loaded. Whether the file is source or binary is automatically checked by seeing its magic number. If the file does not exist but a file with the file type ’.o’ exists, the file is loaded as an object file. Else if a file with the ’.l’ suffix is found, it is loaded as a source program. Therefore, there is a case where you specified “foo.so” expecting “foo.l” is already compiled, but “foo.l” is actually loaded, since it has not yet been compiled in reality. In other words, if you just specify a base-name of a file, its compiled version is first tried to be loaded, and the source file suffixed by “.l” is tried later. If the file name is not specified in the absolute path by prefixing the name with a slash “/”, load searches for the file in the directories specified by the load-path* global variable.

For example, if load-path* is (“/user/eus/” “/usr/lisp/”), and “llib/math” is given as fname, load tries to find “/user/eus/llib/math.o”, “/usr/lisp/llib/math.o”, “/user/eus/llib/math.l” and “/usr/lisp/llib/math.l” in this order. If no appropriate file could be found, an error is reported.

:entry option specifies the entry address to initialize the load module. For example, :entry “_myfunc” option means that the execution begins at _myfunc. Default entry is the basename of the file loaded as described in the section [compiler]. Library module names can be specified in :ld-option option string. For example, in order to link a module which uses suncore libraries, :ld-option “-lsuncore -lsunwindow -lpixrect -lm -lc” should be given. On non Solaris systems, ld runs twice when libraries are included; once to determine the size of the linked module, and again to link them actually with a proper memory allocation.

:symbol-input and :symbol-output options are used to solve references from one object module to another or to avoid duplicated loading of libraries. Suppose you have two object modules A and B which has reference to symbols defined in A. You first load the module A specifying :symbol-output = “a.out”. Symbol information generated by this linking is written to a.out. In order to load the module B, you have to specify :symbol-input = “a.out” to solve the references from B to A.

On Solaris2 OS, the loading of compiled code is done by calling dlopen in the dynamic loader library. Application of dlopen is restricted to shared objects which are compiled position independently with “-K pic” option. Also, since dlopen cannot open the same file twice, load first does dlclose on the file already loaded.

:print option decides whether load should produce output to *standard-output* for each input expression. This option is provided to find which expression (usually defun, defmethod, etc.) results error in loading.

load-files &rest files

loads files successively with setting :verbose to T.

*modules* **

holds a list of names of the modules that have been loaded so far.

provide module-name &rest version-info

adds the concatenation of module-name and version-info to modules* as the name of the module being loaded. module-name should be a symbol or a string. Calls to require should appear at the beginning of files that compose a complete modules.

require module-name &rest load-arg

loads file given by module-name or the first argument or load-arg unless module-name is found in modules*. provide and require control dependency among modules and are used to avoid duplicated loading of basic modules. Suppose you have one basic module named “A” and two application modules named “B” and “C” which are independent from each other but rely on “A” module. At the beginning of each file, module name is declared by provide. Since “A” module does not depend on any other modules it does not require anything. (require “A” “a.o”) follows calls to provide in “B” and “C”. If you load “B” (more precisely, “b.o”), “a.o” is also loaded since it is found in modules* and two module names “A” and “B” are added to modules*. Then if you load “C”, “A” module is not loaded and “C” is added to modules*.

system:binload = `[function]
*opath qpath &optional = (entry (pathname-name opath)) (symfile “/usr/local/bin/eus”) (symout “a.out”) (ldopt “”) *

link-load a binary file.

system::txtload fname

Debugging Aid

describe obj &optional (stream *standard-output*)

prints the contents of an object slot by slot.

describe-list list &optional (stream *standard-output*)

s each element in list.

inspect obj

is the interactive version of describe. It accepts subcommands to print each slot of an object, to go deeper into a slot, or set a new value to a slot, etc. Use ’?’ command to see the subcommand menu.

more &rest forms

After evaluating forms with the binding of *standard-output* to a temporary file, the temporary file is output to *standard-output* with Unix’s ’more’ command. More is useful to see a long output generated by functions like describe.

break &optional (prompt “:: ”)

Enters a break loop. Since the current binding context is in effect, local variables can be seen by prefixing “,” to an input. To end break, type control-D.

help topic

prints the brief description on the topic which is usually a function symbol. The help description has been created from the reference manual (this document). The environment variable LANG is referrenced to determine one of two reference manuals, Japanese or English. If LANG is constituted either with “ja”, “JA”, “jp”, or “JP”, Japanese is selected. Otherwise, English. This determination is made when EusLisp start up. The actual reading of the help document is made at the first time when the ’help’ is invoked to save memory if unnecessary.

apropos strng &optional pack

is useful when you forget the exact name of a function or a variable and you only know its partial or ambiguous name. It prints all the symbols whose symbol-names include the strng as a substring. If pack is provided, only prints symbols that belong to this package instead. Case insensitive.

apropos-list strng &optional pack

is similar to apropos but does no printing and returns the result as a list.

constants &optional (string “”) (pkg *package*)

lists every symbol in pkg which has defined constant and matches with string.

variables &optional (string “”) (pkg *package*)

lists every symbol in pkg which has global value assigned and matches with string.

functions &optional (string “”) (pkg *package*)

lists every symbol in pkg which has global function defined and matches with string.

btrace &optional (depth 10)

prints call history of depth levels.

step-hook form env

step form

and trace work correctly only for functions, and not for macro or special forms.

trace &rest functions

begins tracing of functions. Each time functions are called, their arguments and results are prited.

untrace &rest functions

stops tracing.

timing count &rest forms

executes forms count times, and calculates time required for one execution of forms.

time function

begins measurement of time elapsed by function.

sys:list-all-catchers **

returns a list of all catch tags.

sys:list-all-instances aclass &optional scan-sub

scans in the overall heap, and collects all the instances of the specified class. If scan-sub is NIL, then instances of exactly the aclass are listed, otherwise, instances of aclass or its subclasses are collected.

sys:list-all-bindings **

scans bind stack, and returns a list of all the accessible value bindings.

sys:list-all-special-bindings **

scans the stack and list up all value bindings.

Dump Objects

EusLisp’s reader and printer are designed so that they can write any objects out to files in the forms that are rereadable. The objects may have mutual or recursive references. This feature is enabled when print-circle* and print-object* are set to T. Following functions set these variables to T, open a file, and print objects. The most important application of these functions is to dump the structures of 3D models that have mutual references.

dump-object file &rest objects

dump-structure file &rest objects

dumps objects to file in a format as they can be read back again.

dump-loadable-structure file &rest symbols

dumps objects bound to symbols to file. The file can be read back again by simply loading it.

(setq a (make-cube 1 2 3))

;; sample for dump-object
(dump-object "a-cube.l" a)
(with-open-file
  (f "a-cube.l" :direction :input)
  (setq a (read f)))
(print a)

;; sample for dump-structure
(dump-structure "a-cube.l" a)
(with-open-file
  (f "a-cube.l" :direction :input)
  (setq a (read f)))
(print a)

;; sample for dump-loadable-structure
(dump-loadable-structure "a-cube.l" a)
(load "a-cube.l")
(print a)

Process Image Saving

This process image saving is no longer supported on Solaris2 based EusLisp, since it heavily depends on Solaris’s dynamic loading facility which loads shared objects position-independently above the sbrk point.

sys:save path &optional (symbol-file “”) starter

dumps the current EusLisp process environment to a file which can be invoked as a Unix command later. If a function name is specified for starter, the function is evaluated when the command begins execution. Each command line argument is coerced to string in EusLisp and they are passed to starter as its arguments, so that it can parse the command line. Be sure that you have closed all the streams except standard-input* and standard-output*. File open states cannot be saved. Also, be sure you have not attempted mmap, which unnoticeably happens when you make internetwork socket-stream. Sun’s network library always memory-maps NIS information such as host-by-name table and locates them at the uppermost available location of a process that cannot be saved. When the saved image is run later, any access to the network library fails and causes core dump. Note that Xwindow also uses this library, thus you cannot save your process image once you opened connection to Xserver.

Customization of Toplevel

When EusLisp is invoked from Unix, execution is initiated by the toplevel function bound to toplevel*. This function is eustop in eus and xtop in eusx. You can change this toplevel function by specifying your own function to the third argument to save.

The toplevel function should be programmed to accept arbitrary number of arguments. Each argument on the command line is coerced to a string and transfered to the toplevel function. The program below repeatedly reads expressions from the file given by the first argument and pretty-prints them to the second argument file.

(defun pprint-copy (infile outfile)
  (with-open-file (in infile)
     (with-open-file (out outfile :direction :output)
        (let ((eof (cons nil nil)) (exp))
           (while (not (eq (setq exp (read in nil eof)) eof))
              (pprint exp out))))))
(defun pprint-copy-top (&rest argv)
   (when (= (length argv) 2)
      (pprint-copy (first argv) (second argv))))

Once you defined these functions in EusLisp, (save “ppcopy” “” ’pprint-copy-top) creates a unix executable command named ppcopy.

In Solaris based EusLisp, the toplevel evaluator cannot change in this manner, since save is not available. Instead, edit lib/eusrt.l to define the custom toplevel evaluator and set it to toplevel*. lib/eusrt.l defines initialization procedures evaluated at every invocation of the EusLisp.

Miscelaneous Functions

lisp-implementation-type **

returns “EusLisp”.

lisp-implementation-version **

returns the name, the version and the make-date of this EusLisp. This string is also printed at the opening of a session. “MT-EusLisp 7.50 X 1.2 for Solaris Sat Jan 7 11:13:28 1995”