pure_LISP reference manual
[ Index ]
[ LISP reference ]
[ LISP tutorial ]
[ SECD collection ]
[ SECD reference ]
This document describes pure_LISP language, implemented on an advanced
SECD machine.
Use also the Tutorial - it provides some info
about installation and first steps in the package.
1. Data types, syntax, functions.
Data types in pure_LISP:
Terminal data types:
- nil - represents empty list
- integer - represents 32-bit integer value (the codes of
SECD machine are also represented by integers).
- atom - used for variable/function names. Atom names are used
also as short strings.
Composite data types:
- node - used to represent pair (a.b) where a and b are some data
structures.
List (x1 x2 ... xn) is represented as (x1.(x2.(...(xn.nil)...)).
- non-functional objects - used for I/O and communications.
They are variants of the node (subtypes of node type):
- pipe - for interprocess communications.
- stream - represents I/O streams of lexemes/characters.
- connwait - for TCP connection in process of establishing
the connection.
Atom T is used for true logical value, while F represents
false. Atom nil is the same as () and represents empty list.
Syntax is as simple as possible:
The only special symbols are ( ) . SPACE. CR and LF are treated
as SPACE.
Lexeme is a sequence of non-special symbols.
Example:
Hello 234 -128 a b nil #ld_0_1 are proper lexemes.
Expressions are:
- terminal expressions - simple lexemes
- node expressions - if e1 and e2 are expressions, (e1.e2) is an expression.
- lists - (e1 ... en) is a list of the expressions e1, e2 ... en.
Functions in pure_LISP:
Both data and programs in pure_LISP are represented by the expressions
!
The program is simply a function call. Each function call have the form:
(function arg1 arg2 ... argn)
A function may be defined by the compiler (main function) or by the
user. The main function lambda defines new user functions. The process
of program execution in LISP is called evaluation.
Evaluations in pure_LISP are lazy (non-strict or call-by-need designations
are used). Shortly in lazy evaluation model the evaluation process return
formulas for more complex expressions, instead of values. The actual evaluation
is done only if access to some details of the complex expression is needed.
This model allows programming with infinite data structures and defining
user functions, receiving semi-correct parameters, for example:
(letrec ...
(or lambda(e1 e2)
(if e1 (quote T) e2))
)
In strict evaluation model usage of above-mentioned user defined function
or will cause an error in expression (or (eq l nil) (eq (car
l) 0)) if l is an empty list, while in lazy evaluations
e2 will not be evaluated at all.
The rules for lazy evaluations are defined in the compiler.lib source.
Shortly, evaluation of the parameters of all user functions are delayed,
the parameters of main function cons are delayed as well.
First parameter of if and all parameters of all main primitives
except cons are forced. The evaluation of the body of user function
is forced. let and letrec are treated as user functions.
2. Main functions (functions, defined by the compiler).
Primitive functions:
Arithmetic functions:
- (add i j) evaluates sum i+j
- (sub i j) evaluates difference i-j
- (mul i j) evaluates product i*j
- (div i j) evaluates (i div j)
- (rem i j) evaluates remainder (i mod j)
- (inc i) evaluates i+1
- (dec i) evaluates i-1
Predicate functions:
- (eq e1 e2) returns T if e1 is the same as e2
- (leq i j) returns T if i<=j
- (lexleq a1 a2) returns T if atom a1 is lexicaly less
or equal to atom a2
- (node exp) returns T if exp is a node (a non empty list
/ pair)
- (null exp) returns T if exp is nil (an empty list)
- (integer exp) returns T if exp is an integer
- (atom exp) returns T if exp is an atom
List/atom functions:
- (cons e1 e2) returns new node (e1.e2).
If e2 is a list (E2 ... EN), the result may be treated as a new list (e1
E2 ... EN).
- (car e) returns left part of a node (head element of
a list).
- (cdr e) returns right part of a node (tail of a list).
- (chr i) returns an atom, which name is one character
simbol with ASSCI code i.
- (concat a1 a2) returns atom which name is a concatenation
of the names of atoms a1 and a2.
Other primitives:
- (fail e) kill current process.
- (stat e) returns e. Side effect: it prints statistic
about SECD machine in secd.log file.
- (fopen fmod fn) opens the file named by atom fn and returns
stream object.
If fmod is 0, file is opened for input. If fmod is 1, the file is erased
and opened for output.
If fn is nil, the stdin/stdout is associated with the resulting stream.
- (fclose f) Side effect: it closes the file, represented
by stream f.
- (connect ip p) tries to establish new TCP connection
to host with IP address ip at port p and returns connwait object.
- (listen maxcon p) starts new TCP listener at port p.
maxcon is the maximum connections allowed for this new
server. listen returns connwait object.
All primitive functions have their correspoding code in SECD architecture. They all have 1 or 2 parameters.
Except cons, all are compiled to force evaluation of their parameters. Cons
delay the evaluation of the parameters.
The other group of main functions may be called special functions (for
control, i/o, process control and other purpouses). They all have specific
semantics.
Special functions:
- (if cond eT eF)
if is the only function for conditional evaluation.
First the expression cond is evaluated. If cond is T, the expression
eT will be returned as a result of if, else eF will be returned.
- (quote e)
This function returns the parameter e without evaluating it.
It is used in all cases where you want to treate e as a constant, not
as a formula.
- (lambda args exp) returns a function definition.
args is a list of formal parameters for this function,
exp describes how to evaluate it.
The functions are evaluated in their lexical scope.
- (let exp (V1.e1) (V2.e2) ... (VN.eN))
let is a shortening for the expression ((lambda (V1 V2 ...
VN) exp) e1 e2 ... eN)
If LS is the lexical scope for let, first the expressions e1, e2 ... eN
are delayed and binded with corresponding variables V1, V2, ... VN.
At last, the expression exp is evaluated in the new scope ((V1 V2 ...
VN).LS).
- (letrec exp (V1.e1) (V2.e2) ... (VN.eN))
letrec is similar to let with the difference that e1, e2 ... eN are delayed
in the new scope ((V1 V2 ... VN).LS).
This allow definitions of groups of self-refferencing recursive functions.
Such recursion is the main control mechanism in LISP programming !
- (do V e1 e2)
In pure functional programming there is no way to determine what is
the order of evaluation of two expressions.
Input/output operations are non-functional by nature and the order of
evaluations is important for them.
do is the only function, organising sequence of actions.
It forces the evaluation of e1, then binds the returned value with variable
V and at last evaluates e2 in the scope ((V).LS) where LS is the lexical
scope of do.
- (send msg io) send message msg to input/output
object io (pipe or stream).
If io is a pipe, msg may be of any type. If it is a stream, msg must be
of terminal type (atom, integer or nil). In this case msg is converted
to lexeme and is written to output stream.
Send may change the running process. Current process is saved in some
pending queue and other process is restored (msg handler or other process).
For details see the SEC Reference.
- (recv io) is opposite to send. It receives a mesage
from io and return it as a result.
Additional allowed io type is connwait. In this case the result is an
established new connection - TCP socket, represented by node of the form
(instream.outstream). If connwait object is generated by the connect function,
just 1 socket may be received. If connwait is generated by listen, many
sockets may be received.
If there is no ready msg, current process is saved and other process is
restored.
- (sendc i stream)
While send writes lexemes to stream, sendc writes single character with
ascii code i.
- (recvc stream)
Opposite to sendc. Return ascii code of received character.
- (fork e1 e2)
New process is created. Current process will evaluate the exspression
e1. The new process will evaluate the expression e2.
- (inLib LibName exp)
This function allows compilation in previously defined lexical scopes.
LibName.lib is the file name of some library, having syntax like this:
(letrec user_program (V1.e1) ... (VN.eN))
Substitution is made in the text of the library - exp replaces atom user_program
and resulting expression is compiled.
- (env)
This function returns the node (LS.E) where LS is the lexical scope of
the function and E is the environment (closure) at the moment of evaluation.
env is used for dinamic linking in miniOS, conjucted with pure_LISP.
In current version of miniOS env is used to generate variable self,
representing RTL lexical scope and the corresponding closure.
Functions, defined by the compiler are accesible from all programs.
If your program will work alone (not in miniOS shell) it must use only
these functions and functions, defined in the program !
3. Run Time Library (RTL) functions and variables.
If you use miniOS, some additional functions and variables are accesible.
They are defined in run time library and are writen in pure_LISP.
RTL consists of 3 sources - system.lib, compiler.lib and rtl.lsp.
Variables input and output are defined in
RTL to represent standard input and output stream for current shell account.
Functions in system.lib:
- (member e l) returns T if e is a member of list l.
- (posn e l) returns position where e is occured in list
l.
- (mapcar f l) applies one-parameter function f to all
elements of list l and returns resulting list.
- (error lex) writes lex on server console and kills current
session.
- (f2name p s) p and s must be atoms. returns concatenation
p|.|s
Example: (f2name (quote test) (quote lisp)) --> atom with name
'test.lisp'
- (read f) Reads expression from stream f.
- (read_list f) Internaly used by read.
- (ReadSFrom fn) Reads expression from file named fn.
- (printf e f st) Prints expression e to
stream f with stylet st.
If st is nil, e is written without any formating.
If st is 'lisp' e is ordered to be more readable as a lisp program.
If st is 'secd' e is ordered to be more readable as a SECD code.
- (print e st) is same as (printf e output st).
- (WriteSTo e st fn) Writes expression e to file named
fn with style st.
- (force e) Forces evaluation for all parts of e.
pure_LISP evaluations are lazy and this function guarantee that expression
e will be fully evaluated.
The only place I use this function is when SECD code is generated.
It is usable only in cases where e doesn't contain infinite parts !
Functions in compiler.lib:
- (comp e n c) is the main compiler function.
e is expression source, n is lexical
scope in which e will be compiled, and c
is cumulative variable for resulting SECD code.
Returns SECD code for evaluation of the expression e.
- There are more than 15 additional functions in compiler.lib, but all
they are recursively used by comp and I do not suggest you to
use them.
Functions in rtl.lsp:
- self is variable with value (LS.E), where LS represents lexical
scope and E represent closure of the RTL itself.
- (comp_lsp fn) compiles file fn.lsp to file fn.sec in
lexical scope of the RTL.
- (comp_lex fn s) compiles file fn.lsp to file fn.sec in
lexical scope s.
- (comp_self) compiles all miniOS files in their proper scopes.
It is beter to make backup copy of all miniOS files before using this function.
- (eval e) compiles expression e in RTL scope and evaluates
the resulting SECD code.
- (exec fn) reads SECD code from file fn.sec and evaluates
it in RTL closure.
(c) Skelet, Sep 2002 in terms of GNU GPL
Mail me at home or
work