rtLisp

Realtime Lisp
Reference Manual

Version 0.3
2016 Nov 12

Glenn Takanishi

These pages are a modification of the original documention from RefLisp-2.67 by Bill Birch



Introduction

The "rtLisp" interpreter is a small C-program for self study of realtime systems programming. rtLisp uses the Scheme dialect of Lisp described in the scheme standard guide R3RS.

Run the interpreter by entering:

        > rtlisp
        ; rtlisp v-0.1
        >

Lexical and Syntactic Conventions

The main routine for rtLisp runs the read-evaluation-and-print loop, RELP, to execute each lisp expression in a program. A program's expression is embedded with parenthesis. For example, the expression

        (print "hello" "world")
is a list. This list is composed of atoms called symbols. The atom "print" is a function operator, and the elements "hello" "world" are atomic identifiers or symbols which are literally data elements which are not evaluated. These atoms are quoted expressions. A quoted expression can be turned from data to program operators whenever the programmer decides to perform a transformation or substitution of data atoms or quoted expressions into function operators.

The lisp reader may parse a lisp program a line at a time. Each line is composed of a list or lists embedded in parenthesis or a line maybe contain just an atom. The lisp reader then passes the list consisting of symbols to the lisp evaluator. The primary purpose of the lisp evaluator is to break down a lisp expression into a form it can compute [1].






The list evaluator takes the first element of the lisp above to be a function operator "print". The first element by convention is call the "car" of the list. The rest of the list, the "cdr", are the data elements hello and world.

Finally, the result of the evaluation of an expression is printed.

Lisp Expressions and Lambda

John McCarthy separated a lisp expression into its form and its arguments. The expression form and expression arguments can exist independently of each other. For example, the lambda list for the function which increments an integer by one is "(n) (+ n 1)". The first element, (n), is the argument list and the second element, (+ n 1), is the form list. John McCarthy noted this often in his published works [1]. He used the formalism of lambda calculus to explicitly separate the functional form from its arguments.

The lambda expression for the list (print "hello" "world") is

     (args) (print args)  where args is the list ["hello" "world"].

Currently, rtlisp uses dynamic scoping. It is not lexically scoped like Scheme. Lisp scoping is determined by how the arguments in symbolic expressions are kept track of. This is an important part of building the rtlisp interpreter.

Lisp Syntax

The special characters recognised are:


	( [		-- beginning of a list, or cons cell.
	) ]		-- end of a list, or cons cell
	.		-- separates the car and cdr halves of a cell.
	'		-- quote character
	`		-- backquote character
	,		-- comma function
	,@		-- comma-at function
	#'		-- function character
	#!		-- first user reader macro.
	#?		-- second user reader macro.
	"		-- start and end of strings
	\		-- escape char for within strings (not Common Lisp)
	;		-- starts a comment
	\		-- escape for the next character in symbols.
	-0123456789 	-- start an integer

Whitespace characters are recognised as the separator between lexical
tokens. 

A more formal description of the s-expression syntax might be:

	An <s-exp> can be an <atom> or a <list>

	<atom>s can be:

		nil	; nil is an atom
	
		positive or negative integers eg:

                              545, -23

		floating point numbers eg:

                              13.4, -33.7, 12.4E8, 23.4e-2

		unlimited length character strings delimited by "s
		with special characters in strings:
				"\n" = newline
				"\e" = escape
				"\t" = tab
				"\r" = carriage return
				"\f" = form feed
				"\\" = \
				"\"" = "
				"\c" = c where c is any other charater.
	symbols
		- symbol names may include any character preceded
		  by the \ character.
			eg. A\(strange\.symbol

		- alphabetic characters are all converted to
		  lower case in the interpreter. Thus FOOBAR
		  and foobar are the same.

		- constants
		
		- special symbols whose value cannot be changed
		  with set, and which are never garbage collected.
		
	<list>s can be either:
		nil	; the null list
		() 	; the null list which equals nil
		( <s-exp> . <s-exp> ) ; a cons cell
		( <s-exp> <s-exp> etc.... ) ; which is the same as
		( <s-exp> . ( <s-exp> . 
			      (  <s-exp> etc.. . nil ))))

	An s-expression preceded by the ' single quote character
	is expanded by (read) to be quoted:

		'<s-exp>  ==>	(quote <s-exp>)

	Other characters have this effect also:

		`<s-exp> ==>	(backquote <s-exp>)
		,<s-exp> ==>	(comma <s-exp>)
		,@<s-exp> ==>	(comma-at <s-exp>)
		#'<s-exp> ==>	(function <s-exp>)

	[The notation "==>" means "evaluates to".]

	These functions are defined in the initialisation file and are
	intended to provide compatibility with Common Lisp. 

		#!<s-exp> ==>	(quser1 <s-exp>)
		#?<s-exp> ==>	(quser2 <s-exp>)

	These quote characters intended to be user-definable.


The Top Level (Read/Eval/Print, REPL, Loop)

Installing rtLisp

Set up the environment variable LISPINIT to point to the initialisation
file.  For example use:

	export LISPINIT=/usr/local/share/rtlisp/init.l

To run the demonstrations, change into the rtlisp directory and type
the following after the prompt:

	rtlisp demo.s 


Starting rtLisp
On your operating system, there will be a command or mouse-clicking operation which will get the program started. rtlisp [arg ...] Once started, the program accumulates the command line parameters in a list in symbol "command-line". The program then reads and executes the initialisation file file. This filename is taken from the environment variable "LISPINIT". If the environment variable is missing rtlisp attempts to read "init.l" in the current directory. You should set up your environment variables to point to your initialisation file, eg: export LISPINIT=/usr/local/share/rtlisp/init.l The shipped initialisation file then processes the command line parameters. Each is taken to be the name of a file to be read and executed. It processes each file in turn, skipping files it cannot open. You may prefer to use the command line arguments in a different way, if so you must alter your LISPINIT file. rtLisp Execution This section describes what happens after you invoke rtlisp. After the initialisation messages you will be presented with a > prompt: > rtlisp ; rtlisp v-0.1 > To halt execution type (exit). Also the interpreter returns control to the operating system when it sees end-of-file (or the *eof* symbol). The character used to signify this varies from one system to another, on Unix systems it is Control-D. The operation of the interpreter is a read-eval-print loop. Its operation is the same as (eval <s-exp>) and is as follows: Atoms which are not symbols such as: integer, float, string, subrs, fsubr, stream. evaluate to themselves. eg > 12 12 Symbols are tested to see if they have a value, in which case that is returned. e.g. > (set a 1) 1 > a 1 If a symbol has no value, it is unbounded. An error is reported and processing stops. Some symbols are constant and should not be changed. They evaluate to themselves. Important constants are: nil, #t, unbound, *eof*. Following an Expression through the REPL The lisp interpreter processes a symbolic lisp expression by running it through three basic phases: read, evaluation and print. The lisp interpreter first reads the symbolic expression from the command line or input file a line at a time. For example, you can type "(cons 2 3)" on the command line. This lisp expression is parsed in the read phase. The first element of the list is parsed into an operator token "cons" which constructs a list pair. The parser continues processing the list for more items until the last one. After the parser classifies each item or token, this list of items is passed on to the next phase called the evaluation phase. In the evaluation phase, for example, the cons procedure evaluates the arguments 2 and 3 forming a cons cell (2 . 3). Inside the internals of the interpreter this expression is passed to the "SUBR" section of code which had evaluated the arguments. (cons 2 3) ==> (<SUBR:2783823> 2 3) ==> (2 . 3) If the expression being evaluated was "(cons 'a 'b)", the arguments would not be evaluated. For example, (cons 'a 'b) ==> (<FSUBR:989977> (quote a) (quote b)) ==> (a . b) "a" and "b" are symbols with no specifically assigned values. For our interpreter, inside the rtlisp internals, the evaluation of the arguments for the lisp expression falls into three categories: SUBR, MACRO and FSUBR. The corresponding lisp function operation is classified into "lambda", "macro" or "lambda*". The application of the evalution, or the "apply" process breaks down into these three types. Finally, the results of the evaluation are passed to the print function. Examples of Lambda Evaluation If "lambda" is found, the actual arguments are evaluated (SUBR). The function's argument symbols are then set to these values. > ((lambda (x) (car x)) (list 1 2 3 )) 1 If "lambda*" is found, the actual arguments are not evaluated. > ((lambda* (x) (car x)) (list 1 2 3 4)) list > (set f `(lambda (n) (eval (+ n 1)))) (lambda (n) (eval (+ n 1))) > (f 1) 2 > (set g (lambda (n) (+ n 1))) (lambda (n) (eval (+ n 1))) > (g 2) 3 > ((lambda (x) (car x)) (list 1 2 3 )) 1 If "macro" is found, the actual arguments are not evaluated, like lambda*, however the result of the "apply" is evaluated again. e.g. > ((macro (x) `(car ,x)) '(1 2 3 4)) 1 If a formal argument is the special symbol &rest, then all remaining actual arguments are set to the formal argument following the &rest. > ((lambda (a &rest b) (list a b)) 1 2 3 4 5 6) (1 (2 3 4 5 6)) If a formal argument is the symbol &optional, then any missing actual arguments are set to NIL. > ((lambda (a &optional b) (list a b)) 99) (99 nil) If an optional argument is expressed as a list of the form (<symbol> <initform>) then the <initform> is evaluated and used when no actual argument is supplied. > ((lambda (a &optional (b 27)) (list a b)) 99) (99 27) The symbols &rest and &optional may be used in combination. > ((lambda (a &optional b &rest c) (list a b c)) 99) (99 nil nil) > (set F (lambda (a &optional b &rest c) (list a b c))) (lambda (a &optional b &rest c) (list a b c)) > (F 99) (99 nil nil) In all cases the body of the function is then evaluated with the new bindings. The value of this evaluation is then returned. After execution of the function, the previous values of the symbols used as arguments are restored. Scope and Extent rtLisp is a "shallow" binding interpreter. It has no environments. The function arguments therefore have dynamic extent and global scope. This means that the formal arguments of a lambda-list are the same symbols available at the top level, and are effectively globals. It also means that when a function is applied, the formal arguments are shadowed, and are removed when the function finishes. In it's basic form then, the interpreter suffers from the "funarg" problem. The #' reader macro is provided to allow (function) to be defined in rtLisp. Lexical scope may be added to programs by re-defining DEF with a version that re-writes the formal arguments of defined functions. This facility is available in the file "lexical.s". Functions loaded after "lexical.s" will have lexical scope and dynamic extent. Adding both lexical scope and indefinite extent can be achieved by deferring the re-writing of functions until evaluation time. This facility is implemented in the file "extent.s" which provides an another alernative version of DEF. Programs will run significantly slower if they use indefinite extent. Some Common Lisp functions require indefinite extent scope to run correctly, usually those that implement objects with local state. The file "scheme.s" provides "define" conforming to Scheme syntax which also has indefinite extent. List Processing Functions The following sections describe the inbuilt functions provided in the interpreter. (car <expr>) Returns the first element of the list <expr>, or more strictly, the A side of the cons cell. (car '(1 2 3 4)) ==> 1 (car '()) ==> nil (cdr <expr>) Returns everything except the first element of <expr> (cdr '(1 2 3 4)) ==> (2 3 4) (cdr '()) ==> nil (cadr <expr>) Returns the second element of <expr> (cadr '(1 2 3 4)) ==> 2 (caddr <expr>) Returns the third element of <expr> (caddr '(1 2 3 4)) ==> 3 (nth <integer> <expr>) Returns the nth element of <expr>, numbering from zero. It returns NIL if <integer> is greater than the length of the list. (nth 3 '(0 1 2 3 4)) ==> 3 (cons <expr1> <expr2>) Returns a new cons cell with <expr1> and <expr2> as its car and cdr. (cons 1 2) ==> (1 . 2) (cons 1 nil) ==> (1) ==> (1 . nil) (cons 1 '(8 9)) ==> (1 8 9) (length <expr>) Returns the number of elements in the list, or the number of characters in a string or symbol name. (length nil) ==> 0 (length '(1 2 3 4)) ==> 4 (length "foobar") ==> 6 (length (symbol-name 'fred)) ==> 4 (member <expr> <list>) Returns a sublist if <expr> is a member of the list, nil otherwise. (equal) is used to compare the elements. Definition: (def member (expr list) (cond ((null list) nil) ((equal? expr (car list)) list) (t (member expr (cdr list))))) (member '23 '(a s d f)) ==> nil (member '23 '(a b c 23 d)) ==> (23 d) (append <a> &rest <b>) Appends one or more lists together. (append '(1 3) '(3 4) ==> (1 2 3 4) (append '(1 2) '(3 4) '(5 6) ==> (1 2 3 4 5 6) (reverse <x>) Reverses the top level of a list. (reverse '(1 2 3 4)) ==> (4 3 2 1) (move <to-cell> <from-cell>) This function destructively modifies the to-cell by copying the contents of from-cell into it. It attempts to discard the cells pointed to by to-cell before the operation if it was a cons cell. It checks to see if a circular structure would result from the operation. For now, it is an error to create circular structures, because the reference counting garbage collector cannot handle them. > (def set-car! (to from) ; define SET-CAR! (move to (cons from (cdr to))) > (set A '(1 . 2)) ; initialise a cell > (set B A) > (set-car! A 99) ; overwrite it > B (99 . 2) Symbol Functions The value of a symbol is *unbound* if none of these functions below have given it a value. If you (set*) or (set!) a symbol to *unbound*, the cells used in the previous value are reclaimed if possible. (set* <symbol> <value>) Assigns the free <symbol> the <value> specified. Returns the value. <symbol> must be an identifier. (set* 'x 21) ==> 21 x ==> 21 (symbol-value <symbol>) Returns the current value of the symbol. An error occurs if the symbol has no value. (set* 'foobar 21) (symbol-value 'foobar) ==> 21 (set! <symbol> <value>) Performs the same as (set) however <symbol> is not evaluated. This the usual assignment function in rtlisp. (set! x 89) ==> 89 x ==> 89 (constant <symbol> <value) The same as set!, however the type of the <symbol> is changed into a constant, ie., the symbol is unchangeable. (intern <string>) Creates a new symbol, with a printname of <string>. The symbol is added to the symbol table, and can be found by (read). If the symbol was already present it is returned. This function is equivalent to (set-symbol). > (set* (intern "foobar") 23) 23 foobar 23 (gensym) Creates a unique new symbol, with a printname of G<n>, where <n> is a unique number. The symbol is NOT added to the symbol table, it is inaccessible to (read). eg: >(gensym) G123 (output-symbol <s-exp> &optional <proforma>) This creates and interns a symbol with a print name of <s-exp> even if a symbol with that name is already present. The optional <proforma> is used to derive the number of the hash bucket where the symbol is stored. If <proforma> is absent, <s-exp> is used which is the default. The symbol is returned. Note: rtlisp is different from Common Lisp which only allows symbols to have string print names. (set-symbol <s-exp> &optional <proforma>) This looks for a symbol with a name equal to <s-exp>, using (read-symbol). If none is found it adds it. The optional <proforma> is used to derive the number of the hash bucket where the symbol is looked for and stored. If <proforma> is absent, <s-exp> is used which is the default. (read) uses (set-symbol) with no proforma. The symbol is returned. If this operation duplicates an existing symbol, (read) will use the newest one. (read-symbol <s-exp> &optional <proforma>) This looks for a symbol with a name equal to <s-exp>, if none is found it returns nil. The optional <proforma> is used to derive the number of the hash bucket which is searched. If <proforma> is absent, <s-exp> is used by default. In addition, if the proforma contains the wildcard * symbol, then (read-symbol) matches on the symbol name. The symbol found is returned, otherwise nil. Definition of the matching used: (def match (mask y) (cond ((equal mask '*) t) ((equal mask y) t) (else (cond ((and (consp mask) (consp y)) (and (match (car mask) (car y)) (match (cdr mask) (cdr y)))) (else nil))))) > (or > (set person '(name * age *)) ; create a proforma (output-symbol '(name (john doe) age 23) person) ; and an instance (read-symbol * person)) ; look for any person (name (john doe) age 23) ; found John Doe! This function can be used in conjunction with one of the user defined quote charaters to achieve a usable syntax: > (def quser2 (name) (eval (read-symbol name))) quser2 (set w 2) 2 > w ; (read-symbol "w") ==> 'w 2 ;; make a new symbol (set* (set-symbol '(matrix 12 34)) 123.3) 123.3 > (matrix 12 34) ; access it 123.3 ;; or if (quser1) is defined like (backquote): #!(matrix ,x-cood ,y-coord ) ; x-coord is a symbol (input-symbol <s-exp> &optional <proforma>) This looks for a symbol using (read-symbol), if one is found, it is removed from the symbol table, and an attempt is made to garbage collect it. If a symbol is found, its print name is returned. (def <function> <arglist> <expr> .... ) This macro is equivalent to: (set! <function> '(lambda <arglist> <expr> .....)) and is used to declare new functions. Functions declared with (def) will have all their arguments evaluated prior to being executed. (def) is a macro. <arglist> can be either an atomic symbol or a list of symbols. In the first case all arguments are passed as a list in the single argument, otherwise the actual arguments are placed in the corresponding symbol values using (set). The &optional and &rest flags can also be used. The <function> symbol is returned. (def foo (&rest args) (length args)) ==> foo (foo 1 2 3 4) ==> 4 (def bar (arg1 arg2) (cons arg1 arg2))==>bar (bar 'a 'b) ==> (a . b) (df <function> <arglist> <expr> .... ) This macro is equivalent to: (set* <function> '(lambdaq <arglist> <expr> .....)) and is used to declare new functions. Functions declared with (df) do not have their arguments evaluated prior to being executed.(df) is a macro. <arglist> can be either an atomic symbol or a list of symbols. In the first case all arguments are put into <arglist> with (set), otherwise the actual arguments are placed in the corresponding symbol values using (set). The <function> symbol is returned. (df foo (&rest args) args) ==> foo (foo a b c d) ==> (a b c d) (df if (c true false) (cond ((eval c) true) (t false))) ==> if (if (zerop 9) (cons 1 2) (cons 3 4)) ==> (cons 3 4) (def-macro <function> <arglist> <expr> .... ) This macro is equivalent to: (set! <function> '(macro <arglist> <expr> .....)) and is used to declare macros. Functions declared with (def-macro) do not have their arguments evaluated prior to being executed. A macro is evaluated like a (df) function, with the result receiving an additional evaluation. The <function> symbol is returned. (def-macro if (c true false) (cond ((eval c) true) (t false))) ==> if (if (zerop 9) (cons 1 2) (cons 3 4)) ==> (3 . 4) (def-macro amacro (&rest args) args) (amacro cons 1 2 ) ==> (1 . 2) (oblist) Returns a list of all the symbols in the symbol table, in hashed order, whether bound or not. Control Functions (cond (<predicate> <expr> .....) (<predicate> <expr> .....) ....) This is the main conditional control structure in rtlisp. The predicates are evaluated in turn until one is found that returns a non-nil value. Then each of the expressions <expr> following are evaluated in turn. The value returned from the (cond) is the value of the final expression. If no <predicate> evaluates to non-nil then the value of the (cond) is nil. Likewise a null <expr> list renders nil. (while <condition> <expr> .....) The <condition> is evaluated, if it is not nil, the <expr> list is evaluated, each in turn. This process is repeated until the <condition> becomes nil. The last <expr> evaluated is returned, or nil. (until (<condition> <result> ... <lastresult>) <expr> .....) The <condition> is evaluated, if it is nil, the <expr> list is evaluated, each in turn. This process is repeated until the <condition> becomes non-nil. Then the <result> forms are evaluated, the value of the <lastresult> is returned. If no <results> are present NIL is returned. (def test1 (n) (do-until ((equal n 0) "finished") (print n) (set! n (- n 1)))) (begin <expr> <expr> .......) This function evaluates each of the expressions in its arguments. The value of the last argument is returned. (error <expr>) [function] The expression is printed, and the interpreter jumps to the top level. This function can be used to jump out of a user program if a severe error is detected. Note that cells will be permanently lost from the free pool in the process. (catch <tag-symbol> <expr> ..... <expr-n>) First half of no-local goto. The <tag-symbol> is (set) to the current stack frame. The expressions following are executed and the value of the last expression is returned. (catch) is equivalent to: (set* <tag-symbol> current stack frame) (begin <expr> ..... <expr-n) The special tag 'error-tag is used by the interpreter. If it finds an error it calls (throw 'error-tag <>). You can therfore catch errors by enclosing functions in (catch 'error-tag ...). (throw <tag-symbol> <return-expr>) Second half of a non-local goto. The <tag-symbol> is evaluated, and must result in a stack-frame object set up by a previous, current (throw). The <return-exp> is evaluated, and execution then jumps to the last (throw) with the <tag-symbol>. The (throw) returns with <return-exp>. (throw 'error-tag <>) is a way of signalling a user error. Implementation note: catch and throw are implemented with the C setjmp() and longjmp() functions. References to cells on the stack cannot be garbage-collected, and (throw) will "leak" memory. Use (catch) and (throw) to catch user errors or for circumstances that are infrequent. ; function to calculate Square-root safely. (def my-sqrt (x) (cond ((< x 0) (throw 'math-error "whoops!")) (t (exp (/ (log x) 2))))) (catch 'math-error (print 1) (my-sqrt -22) (print 2)) ; When executed this prints: 1 "whoops!" Evaluation Functions (eval <expr>) The <expr> is evaluated, given that <expr> may have been evaluated before (eval) was called, this would make the second evaluation of <expr>. Since shallow binding is used in the interpreter, the current bindings are used. (eval '(cons 1 2)) ==> (1 . 2) (eval ''x) ==> x (set ex '(* 2 3)) (eval ex) ==> 6 (apply <func> <actuals>) The <func> is applied to the argument list <actuals>. <func> must be a SUBR or FSUBRP or a lambda expression, and <actuals> must be a list of arguments as required by the <func> being invoked. (apply 'cons '(1 2)) ==> (1 . 2) (apply '(lambda x (cons x x) '(3)) ==> ((3) 3) (quote <expr>) The <expr> is returned un-evaluated. (quote) could be written as: (df quote (x) x) The parser in (read) turns '<expr> into (quote <expr>) when it reads expressions. (quote w) ==> w 'w ==> w (backquote <expr>) The back-quote function provides an easy way to write macros. The (comma) and (comma-at) markers are also supported. The description of the backquote mechanism is rather complex, suffice it to say that it behaves like (quote) with the addition that any expressions enclosed by the (comma) are evaluated, and those enclosed by (comma-at) are evaluated and spliced into the result. Note that (read) expands ` , and ,@ to (backquote) (comma) and (comma-at). `(foo ,(* 2 3)) ; evaluates to (foo 6) `(bar ,@(list 3 4)) ; ==> (bar 3 4) ;; example of backquote used to define a macro: ;; (def-macro if (condition true-exp false-exp) `(cond (,condition ,true-exp) (#t ,false-exp))) (if (equal 1 2) 'equal 'not-equal) ; ==> 'not-equal ;; And used to define a macro with variable arguments. (def-macro toto (&rest x) `(cons ,@x)) (toto 1 2) ; ==> (1 . 2) Predicate Functions (atom? <expr>) Returns nil if <expr> is a list, else t. (cons? <expr>) Returns #t if <expr> is a list, else nil. (null? <expr>) Returns #t if <expr> is nil, else it returns nil. (symbol? <expr>) Returns #t if <expr> is an symbol, else nil. (symbol? 'car) ==> #t (symbol? car) ==> nil ; because car evaluates to a SUBRP (string? <expr>) Returns #t if <expr> is a string, else nil. (string? "car") ==> t (string? 'car) ==> nil (equal? <expr1> <expr2>) Returns #t if the two expressions point to the same object in memory. (equal? 'foo 'foo) ==> #t (equal? "foo" "foo") ==> nil (equal? 1 1) ==> nil (equal? '(1) '(1)) => nil (eq? <expr1> <expr2>) Returns #t if the two expressions are equivalent, irrespective of their location in memory. This is safer than (equal?). (eq? 'foo 'foo) ==> #t (eq? "foo" "foo") ==> #t (eq? 1 1) ==> #t (eq? '(1) '(1)) => #t (bound? <expr>) Returns nil if <expr> is an symbol with no assigned value. Useful for testing a symbol's value without provoking an error. (bound? x) ==> nil (set! x 1) (bound? x) ==> #t (integer? <expr>) Returns t if <expr> is an integer, else nil. (fix? 32) ==> #t (fix? 32.8) ==> nil (float? <expr>) Returns t if <expr> is a floating point number , else nil. (float? 32.3) ==> #t (float? 2) ==> nil (number? <expr>) Returns t if <expr> is a number (float or integer), else nil. (number? 32) ==> #t (number? 3.2) ==> #t (number? "32") ==> nil Mathematical Functions Mathematical functions may convert their arguments from integer types to floats before evaluation. (+ <num1> <num2> ..... <numN>) Returns the sum of a list of integer arguments. Non- integer arguments provoke an error message. (- <num1> &optional <num2>) Returns <num1> - <num2>. If <num2> is absent it returns -1 * <num1>. Non-numeric arguments provoke an error message. (* <num1> ..... ) Returns the product of a list of numeric arguments. May float if necesary. Non-integer arguments provoke an error message. (/ <num1> &optional <num2>) Returns <num1> divided by <num2>. If <num2> is absent it returns 1/<num1>. Non-numeric arguments provoke an error message. A float will be returned if <num1> is not divisible by <num2>. (rem <num1> <num2>) Returns the remainder when <num1> is divided by <num2>. Non-integer arguments provoke an error message. (floor <number>) Converts its argument by truncating towards negative infinity. i.e. the result is is the largest integer that is not larger than the <number>. A integer data type is always returned. (abs <num>) Returns the absolute value of <num>. (random <range>) Returns a pseudo-random number between 0 and <range>. (seed <num>) Sets the starting point of the random number generator sequence. The (rand) called directly after (seed) will always generate the same sequence for the same seed. (Not Common Lisp). (hash <sexp>) Computes an hash index on the <sexp> input, returning an integer. This is used by (set-symbol) etc to calculate the hash bucket to use for symbols. [The function may change from one version to the next.] (float <integer>) Returns float equivalent of its argument. (log <num>) Returns natural logarithm of argument. (log10 <num>) Returns log to base 10 of argument. (exp <num>) Returns e raised to the power of argument. (sin <num>) Returns trigonometric sine of <num> which is in radians.. (cos <num>) Returns trigonometric cosine of <num> which is in radians.. (arc-sin <num>) Returns trigonometric inverse sine of <num> which is between 0 and 1, returns radians. (arc-cos <num>) Returns trigonometric inverse cosine of <num> which is between 0 and 1, returns radians. Relational Functions (and <expr1> <expr2> .... <exprN>) [Macro] Evaluates each expression in turn until one is found that has a value of nil, in which case nil is returned. The remaining expressions are not evaluated. If none are nil then t is returned. (or <expr1> <expr2> .... <exprN>) Evaluates each expression in turn until one is found that has a value of t, in which case t is returned. The remainder are not evaluated. If none are t then nil is returned. (> <num1> <num2>) Returns t if <num1> is greater than <num2>, else nil is returned. Numeric arguments only. (< <num1> <num2>) Returns t if <num1> is less than <num2>, else nil is returned. Numeric arguments only. (string< <left> <right>) Returns t if <left> is lexically less than <right>, else nil is returned. A mixture of symbol and string arguments is accepted. (string< 'a 'b) ==> t (string< "b" 'a) == nil Property Lists Each symbol, with or without a value has a property list. The properties are stored as an "alist". ie ((<prop1> . <value1>) (<prop2> . <valu2>) .........) All symbols have a property which is the text used when the symbol is printed, PNAME. (put <symbol> <property> <value>) Sets the symbol's property to the value given. Returns the <value>. (put 'mary 'age 21) ==> 21 (put 'foobar 'what "first") (get <symbol> <property> <value>) Returns the value of the property attached to the symbol. If this property is not attached nil is returned. (get 'john 'age) ==> 21 (remprop U IND) Removes the property with indicator IND from the property list of U. Returns the removed property or NIL if there was no such indicator. (symbol-name <symbol>) Returns a expression (string) containing the name of the symbol. (symbol-name 'fred) ==> "fred" (symbol-plist <symbol>) Returns the entire property list of the symbol. > (symbol-plist 'john) (PNAME john age 21) > (symbol-plist (set-symbol '(plan 9)) (PNAME (plan 9)) Input/Output Functions File I/O (open <filename> &key :direction <direction>) Opens the file specified, using the mode specified. <filename> can be an symbol or string. <direction> is has the following meanings: :input open for reading :output truncate or create for writing If no mode is specified, :input is assumed. (open) returns a stream, otherwise NIL. (open "C:\\BILL\\LISP\\TEST.LSP" :direction :input) ==> <file:9823891> (set! fd (open 'data :direction :output)) ==> <file:2829849> (open "test.lsp") (close <fd>) Closes a file opened by (open). <fd> must be a stream. (set! fd (open 'data 'input) ==> <file:2829849> (close fd) (force-output <fd>) Forces all buffered data to be output on the file specified. <fd> must be a file descriptor returned by (open). (set! fd (open 'data 'output) ==> <file:2829849> (flush fd) (read &optional <fd>) Read parse the next s-expression from the file specified. <fd> must have been returned from (open). If <fd> is absent, then the standard input is read. *eof* is returned at the end of file. (read)(hello world) ==> (hello world) (read (open 'file 'input)) ==> eof (load <filename-string> &key :verbose :print) This function opens, reads, parses and evaluates the input file specified, <infilename> can be a symbol, string or bigstring. If :verbose is t (default), then a comment is printed to standard output. If :print is true, then the results of evaluations are printed to standard output. :print nil is the default. (load "demos.s" :verbose t :print nil) ; The default (load "/usr/mcarthy/eliza" :print t) ; print results. (display <expr>) Prints the <expr> on the standard output. Strings are not quoted, nor are special characters. Returns <expr>. (princ "string") string (princ ''foo) (quote foo) (write <expr> &optional <fd>) Prints the <expr> on the standard output. Strings are quoted, and some quote forms are reduced. Returns <expr>. This function is designed to output in a form suitable to be (read) again. (prin1 "string") "string" (prin1 weird\\symbol) weird\\symbol (prin1 ''foo) 'foo (prin1 ',@bar) ,@bar (write* <fd> <expr> .....) Prints the expressions to the file specified, using (princ). If the first argument is not an <fd> returned from (open) an error is reported. (read-char <fd>) Reads the next character from the file specified. The value is returned as an integer less than 256. <fd> must have been returned from (open), if absent the standard input is read. *eof* is returned at the end of file. (write-char <char> <fd> ) Outputs one character from the file specified. <char> is an integer which will be output.<fd> must have been returned from (open), if absent the standard output is used. (string->integer <string>) ASCII to integer transformation. Returns an integer corresponding to the value of the numeric string. (string->integer "143") ==> 143 (integer->string <number>) Integer to ASCII convertion. Returns a string with the ASCII representation of the integer. (integer->string 678) ==> "678" (real->string <number> <width> <precision> <leading 0s>) Float to ASCII convertion. Returns a string with the ASCII representation of the float. <width> is the number of characters in the output string <precision> if the number of decimal places. if <leading 0s> is eq to t then the number is padded out with leading zeros. > (real->string 50.0 3 0 #t) 050 > (string? (real->string 678)) #t (system <program> <arg> <arg> ....) This function allows you to call another program from within rtlisp. The result depends upon the base operating system. In general it calls execv(). (system "emacs" "/usr/bill/lisp/man.txt") (getenv <string>) This function returns the value of the environment variable named by <string>, or NIL if it is absent. (get-decoded-time) This routine reads the computer's clock and returns the time and date. The return value is a list of integers in the following format: (second ; 0-59 minute ; 0-59 hour ; 0-23 date ; 1-31 month ; 1-12 year ; two digits day-of-week ; 0-6 where 0 = Monday daylight-saving ; ) NB The ATARI-ST clock only ticks every 2 seconds! (get-decoded-time) ==> (11 20 21 1 7 92 3) (get-universal-time) This routine gets the number of seconds since the epoch (Jan 1 00:00:00 GMT 1970). It returns a float because integers are too small to contain the value. Therefore the value doesn't change very often! (command-line) Get the command line. The global variable *argv* also lists the command line. Debugging Functions (room) Prints out a message about this rtlisp including the version number and memory usage. Memory is allocated from a fixed-size pool, when that becomes full, memory is acquired from the operating system heap via malloc(). The behaviour of the operating system determines what happens when the heap is all used up. ATARI-TOS aborts the program! The program uses a lot of stack (16,384 bytes on the ST, 64K on MS-DOS). (trace <arg>) (trace 1) instructs the interpreter to print every argument to the main evaluation functions. The return values are not printed. (The return values from most executions of "eval" and "apply" will usually appear as arguments to subsequent calls to these functions.) (trace 0) switches tracing off. (trace <symbol-function>) This is defined in init.l, and prints the call and return of lambda functions. See init.l for details. > (trace our-function) (untrace <symbol-function>) Removes the effects of (trace). init.l for details. Extensions to rtLisp (extensions <boolean>) This function switches on Bill Birch's extensions to rtlisp if <boolean> is T, and switches them off if NIL. The extensions activated are: (apply): Before apply attempts to apply its functional argument to the actual argument list, it checks to see if there exists a symbol whose PNAME is of the form (<func> <actuals). If there is, (apply) evaluates the symbol instead of normal application. The symbol value is returned. This extension permits memoisation of functions, and allows a more functional programming style. for example: (extensions #t) ; activate extensions ; handy function to create symbols which look like ; function applications (def assert (form result) (set (set-symbol form) result)) ; Fibonnacci Series (def fib (n) ; Note total lack of COND statements !! (+ (fib (- n 1)) (fib (- n 2))) ) ; Declare the terminating conditions of fibonacci series. ; These are found by APPLY in preference to an application. (assert `(,fib 1) 1) (assert `(,fib 0) 1) ;; Here is a memoised version of the fibonnacci series ;; which is very much faster than (fib) (def fib-fast (n) ; The assert statement saves the result for ; future executions of APPLY (assert (list fib-fast n) (+ (fib-fast (- n 1)) (fib-fast (- n 2))) )) (assert `(,fib-fast 1) 1) (assert `(,fib-fast 0) 1) History & Design Notes This interpreter started out conforming to Standard Lisp. The family of lisp dialects can be viewed to be close since the fundamental lisp functions can be formulated in list processing functions using car, cdr and cons. So rtlisp is moving to use the Scheme dialect of lisp. The fundamental design rules in force during the development of this interpreter has been the use of reference counting instead of mark and sweep garbage collection. The future version of this interpreter will use a concurrent for of incremental mark and sweep garbage collection. Reference counting will be replaced. Here is a list of the design goals: 1 The cell manipulation code, see the lisp.h code, is made as simple and efficient as possible for self-study. 2 In the spirit of lisp, only one data type is permitted, the Cell structure. All data is stored in the subfields of the "cons" cell. This prevents list manipulation code from being repeated for different data types. 3 The 'C' source code of rtlisp should function as much as possible like lisp (in spirit) itself which primarily manipulates linked list structures. 4 Garbage collection will be done using concurrent mark & sweep to do real-time list processing. Reference [1] Symbol Manupulating Language--~Memo 4--Revlsions of the Language by John McCarthy,
[http://www.softwarepreservation.org/projects/LISP/MIT/AIM-004.pdf]
[2] Timothy P. Hart and Thomas G. Evans, "Notes on Implementing Lisp for the M-460 Computer", in "THE PROGRAMMING LANGUAGE Lisp: Its Operation and Applications", Information International Inc. March 1964.
[http://www.softwarepreservation.org/projects/LISP/book/III_LispBook_Apr66.pdf]
The above reference, [2], is on page 191 of the LispBook_Apr66.
[3] Arthur Norman and Gillian Cattell, "Lisp on the BBC Microcomputer", Acornsoft, 1983.
[2] was used in the development of eval and read. Examples are also taken from [3]. Appendix I'm incrementally rewriting this code to make it much more efficient. Then I'm hoping to implement the ρτ-realtime concurrent mark and sweep garbage collector.