zlisp 0.1


Documentation page.

    This is the zlisp documentation page. If you want to visit the zlisp applet, please click on the above image.
This page, as zlisp itself is still under construction.

    Contents of this document:



 

Preface

    Here is my own LISP interpreter. I made it just as an exercise while I was learning Java, but it grew up and actually became a acceptable tool for all those who are interested in LISP or want to learn it, so I decided to make it accessible on the Internet. There are of course lots of high performance LISP interpreters, but they are usually big and platform-specific. zlisp can be run in any system who implements a Java VM, and can be thrown as an applet inside your Web browser. I think zlisp would be useful for all those who are interested on computational sciences, the basis of artificial intelligence, or the mathematical substrate of computers. LISP is really a very simple programming language, and it nicely shows the formal logic of algorithms, so it would be a good idea for anyone who wants to learn about computers to start with it. Specially, humanities students, interested in semiotics and formal languages, may find it helpful. In fact, for many years, LISP has been the standard language for all these studies.

    But zlisp is not only another LISP interpreter, its flexibility an extensibility make it a useful tool to do multiple tasks. Due to the way it interacts with the Java VM, it can perform eventually any operation that can be made using Java. So if you are already a Java programmer, you can add zlisp new methods even at runtime.

    There are also some differences between zlisp and the standard LISP implementation Lisp 1.5 that will be discussed later on this document. Most of them are intended to make LISP an even more regular and elegant formal language, and many others have been made to accomplish all zlisp objects to be also Java objects. zlisp does not use CONS cells to internally represent its structures as many others interpreters do, and dot pairs have been completely eliminated (it was just a confusing and even hard to handle notation).

    This document is an introduction to LISP and zlisp. Although it is not my natural language , I decided to write it down in English to make it more accessible to the Internet community. Please, send me any suggestion about gramathics, orthography or style.

    As zlisp was made and is maintained only as an exercise, feel free to download it and use it in your own Web page and please, e-mail me if you find any bug (I am sure there are many bugs). I am now correcting the source code and trying it to look prettier; as soon as I can, I will make it available. Hope you find zlisp useful,

Alejandro Luque Estepa.

1. Overview

1.1. A brief historical review of LISP.

1.2. What is zlisp and what is it intended for.
    zlisp is a small, plataform-independent LISP interpreter, mostly designed for educational purposes and as an exercise of Java programming. However, zlisp is also a strong, reliable interpreter that you can use to make your own programs, although I suggest you look for another one if you are to make a large, complex project (probably, GNU Emacs-LISP will fit your needs)

    zlisp is completely designed and compiled in Java, so it is plataform-independient and can be run in almost any machine. zlisp inherits many powerful capabilities from Java: safety (is eventually impossible for zlisp to cause any damage to your machine), smart and intensive memory handling, run time link (it allows you to extend zlisp while you are running it), etc. As you may know, Java is a completely object-oriented language, but that's something  zlisp haven't inherited. However, Lisp objects are also Java objects and every object in Lisp has its own Java class; this allows you to treat zlisp as something like an object-oriented language while you are extending it with your own Java code. The extension capability of zlisp was one of the most important items at its design and is one the most relevant differences between it and other interpreters. In fact, you can say that zlisp is not only a LISP interpreter, but a complete tool to design new Java methods and invoke them within a LISP program. However, you don't need to do this and you can look at zlisp as a complete LISP interpreter. Indeed, as it was proved that any computable algorithm can be implemented in LISP, zlisp can be noted as a proof that this also applies to Java (of course we must prove that zlisp is indeed a LISP interpreter first).

    Perhaps, one of the most interesting (from my point of view) features of zlisp is that to use it, you have only to point your web browser to its home page (and you can do this just by clicking on the image on the top of this document). There you will see the very simple -and quite obvious- instructions to interact with the interpreter. As an example, you can write (plus 2 2) and tell the interpreter to evaluate that expression. Of course, if all's going well, you will note that the answer is 4. But probably, if you have done that, you have noticed the long time your computer takes to perform such a simple calculation: don't worry, it was because the program had to load all the Java classes before you use them the first time, but when the classes are in memory, very few time it will take to calculate a sum (but remember: Java is a semi-interpreted language and zlisp is a interpreter of LISP that runs upon the Java VM so it won't be expected to run too fast)
 


2. Data types in zlisp. List and atoms.

2.1. Standard LISP data types.
    Now that we have introduced LISP and zlisp, we can take a look a little deeper and see how do they run. First of all, we will review the basis of LISP for all those who are not familiarized with them. In the last chapter, when we wanted zlisp to perform a simple operation, such as 2+2, we wrote (plus 2 2). Notice that: (1) the operator goes before the parameters it takes (this is called Polish notation) and (2) both the operators and its parameters appear between parenthesis and form what we call a list. Really, what any LISP interpreter does is to evaluate lists, indeed, "LISP" stands for "LISt Processing". To understand what a list is and what is used for, first we shall study the components of lists, i.e. atoms, by introducing some types zlisp supports.

2.1.1. Numbers.
    Everybody is supposed to know what a number is (or at least what it is used for) so we won't take a long time talking about numbers. We will only notice that there are two number types zlisp allows you to use: integer and float numbers: integers are 32-bits signed and floats are 64-bits IEEE754 floating-point. The interpreter treats a number as a float when you introduce it using a separating point ".", or comma ",", depending on your language settings. When the interpreter performs any operation between numbers, it returns a number with the same type that the one with the greatest precision you gave him, i.e. if you evaluate the expression (quotient 5 2) that computes 5 / 2, you will get the integer quotient 2, not the exact quotient 2.5 you may have expected. To get a more accurate result, you should use at least one floating-point number at the operator parameters, v.g. (quotient 5.0 2). Note that numbers are always evaluated to themselves, so if you write 3.14159265 and evaluate this expression, it will return just the same number you introduced.

2.1.2. Strings.
    A string is a set of characters delimited by two double quotes (") at the end and at the beginning. Examples of strings are "to be or not to be" , "words, words, words"  , "3.141592" , "(plus 3 4)". As numbers, strings are evaluated to themselves, so it will be a foolish thing to evaluate "(plus 3 4)" because you will get "(plus 3 4)" again.

2.1.3. Symbols.
    Symbols are probably one of the most important data type in LISP, in fact LISP was designed to put stress not on numerical, but symbolic calculus. A symbol is just a set of alphanumeric characters, starting with an alphabetical one, not containing any spaces and not delimited by any special character. Thus, examples of symbols are pi, me,  you
number_25 or even el_perro_de_San_Roque_no_tiene_rabo_porque_Ramon_Rodriguez_se_lo_ha_cortado (as can be noted, there's no limitation for the length of symbols). We will see later that a symbol can be linked to any LISP object of any data type, but you can use them anyway on your expressions: the problem appears only if you tell the interpreter to evaluate an unlinked symbol; if you try to do this (e.g. by writing rose_of_Castille without having given any value to that symbol) the interpreter will return an error i.e. an unlinked symbol is evaluated to an error. A linked symbol is evaluated, of course, to the object it is linked to. A list of all linked symbols zlisp have at any moment, can be obtained by evaluating the expression (lbound). If you do that, you'll see that there are a lot of pre-linked symbols in your machine: that's because, as we will see later, in zlisp, all operators (as plus, quotient, etc.) are symbols linked to objects of the type method (vid. inf., 2.2.2).

    Now, you may ask, how can I link a symbol?. Well, most LISP interpreters use a operator called setq to do this. setq takes two parameters: first, the symbol you want to link and then the object you want that symbol to be linked to. For example, to link the symbol this_year to the value 1999 you have only to write (setq this_year 1999) and tell the interpreter to evaluate. Then evaluate the symbol this_year and the interpreter will gently remember you which year you live in. We must take care when using setq because the interpreter evaluates the second parameter before it is assigned to the symbol. In the last example, we had no problems because, as we have noted, numbers are evaluated to themselves, neither we will have problems with strings. But what if we want to link a symbol to another symbol? How can we avoid the interpreter to evaluate something? Well, really you can't avoid it because the interpreter always evaluate this second parameter, but you can build an expression that will be evaluated to whatever you want: the way to do this is the operator quote. This operator takes one parameters and just returns it without evaluating, so if you write (quote what_I_want) or, which is more common, by using the simple quote ('), 'what_I_want you will get just what_I_want, Therefore, if you want to link the symbol my_symbol to the value other_symbol, just write (setq my_symbol 'other_symbol) and when the interpreter evaluates my_symbol , it returns other_symbol, as we wanted.

    There's an interesting thing about that: you can link a symbol to itself. Just write (setq the_snake_that_bites_its_tail 'the_snake_that_bites_its_tail) and whenever you evaluate such a descriptive symbol, you get it again and again. In LISP there are two important symbols that get linked at the moment you start using the interpreter: t and nil. Though they are used on logical expressions as equivalents of true and false, we will note here that zlisp treats them as the other symbols, and that you can (although it is not recommended) assign them the value you want. Initially, nil is linked to the empty list () and t is linked to itself (like the former snake). The reason you shouldn't change their linkages is that many zlisp methods are implemented to return these particular symbols and it will get a little confusing if you manipulate them.

2.1.4. Lists.
    These former three data types are referred as atoms, because they are all individual things and not sets. In LISP, sets of ordered objects are called lists, and they are, as we noted above, the basis of the language. A list may be composed of atoms of any type or other lists, that we may call sublists, all of them mixed and combined in anyway. The format widely used to represent lists is to put all their elements between brackets and separated by spaces. Thus, examples of lists are (spain portugal france) , (1 2 3 4 5 6 7) , (1 (2 3) (4 5) (6 7)) , (me) , ((3 apples) (2 strawberries)) , (((life) (is not)) (but (a tale))) and any other as complex as you want. Remember that elements in lists are ordered, so (1 2 3) and (3 2 1) are different lists (some people call this kind of objects vectors, but LISP-speaking, they are always called lists).

    Now that we understand exactly what a list is, we can also understand what have we been doing when we wrote things such (plus 3 4): we were evaluating lists. We will get a little deeper on this subject in the next chapter, but note that when you want a list to be evaluated, its first element must be something to tell the interpreter what to do (an operator or a defined function), and the rest of the elements must be the parameters of the procedure (operation) you want to perform.

    Of course, as you may have realized, a symbol can be also linked to a list, and if you don't want a list to be evaluated, you can also use the quote: (setq andalucia '(almeria cadiz cordoba granada huelva jaen malaga sevilla)). But if you want a list to be evaluated before its value its assigned to a symbol, you can also write (setq five (plus 2 3)), and then, whenever you write five it will be evaluated to the numeric value 5 so you can also write (setq six (plus five 1)) and henceforth.

2.2. Other data allowed by zlisp.
    These last four data types are common to most LISP interpreters and you will be using them most of the time you write your LISP code, but zlisp extends those types with two others: errors and methods, which are very important to understand how does zlisp work and what the differences are with other interpreters.

2.2.1. Errors.
    To allow programs running in zlisp to handle their own errors, a chance was taken: the interpreter does not break the program execution when an error occurs, instead of that, it just evaluates the problematic expression to be an object of error type and it's the programmer task to face and solve the problem. An error is just another data type (an atom) that contains the information needed to know which error has occurred. Although this will probably will be improved in later versions of zlisp, by now, this information is codified into a single number that refers to the error type. There are few types of errors:

    Error #0: Unknown error type.                                                             This type is a trash where all errors not defined yet go.
    Error #1: Not enought arguments.                                                   An operator or function has been called with too few arguments.
    Error #2: Incompatible type.                                                               An operator or function has been called with an argument of a improper type.
    Error #3: Undefined symbol .                                                              An unlinked symbol has been evaluated.
    Error #4: Function not defined as list .                                             A function incorrectly defined has been called (vid. 3.2)
    Error #5: Incorrect format of function.                                                ""                                                ""
    Error #6: Can only run lists and methods.                                       Something different of a list or method has been tried to run.
    Error #7: goto and return must be called inside a prog method.   (vid 4.5)
    Error #8: Class not found.                                                                     (vid 2.2.2)
    Error #9: Method not found in class.                                                  (vid 2.2.2)
    Error #10: File not found.                                                                      An attempt has been done to read a file that does not exist.

    You can always obtain an error object by using the error operator which takes an error number as its only parameter: (error 0) will return an error of the type 0 listed above (undefined error).

2.2.2. Methods. Direct links to Java.
    As we noted early  zlisp is completely Java-based so you can add new operators by writing java methods. The object type created to allow the user to do this extensions is method. Briefly, a method object is a LISP object (an atom indeed) that points to a java.lang.reflect.Method object (if you are a Java programmer, you probably know what are we talking about, if you are not, you will never have to use that kind of object). This object tells the Java VM how to manage with the parameters passed. To invoke a method object, you have only to write it at the beginning of a list, as we have been doing all the time. In fact, you can realize that things such plus, quote, difference etc. actually are symbols linked to objects of the type method, so if you tell the interpreter to evaluate an expression like plus you will see it is evaluated to something that sounds like public static zlisp.LispObject zlisp.Methods.plusMethod(zlisp.LispObject[]) that is the format used to print methods objects. Java methods are always contained in a class (again, you must have some Java knowledge to understand this). In the example above, there were a method called plusMethod contained in the class zlisp.Methods, which is the class where all the standard methods are contained.
    You can at any time create a new zlisp-method object by specifying the names of a class and a method within it and using the method operator, For example (method "zlisp.Methods" "plusMethod") will be evaluated to an object that points to the zlisp standard method to add numbers and if you evaluate

you can henceforth use an operator named sum instead of plus although the later is still valid. Note that the convention used to name the Java methods contained in the the class zlisp.Methods is to add the word "Method" to the name of the LISP operator. Note also that you can do operations like this: and thus change the meaning a some LISP operators. You can experiment with this, but probably it will get a little messy in a few minutes and you'll need to restart the interpreter to restore the primitive meanings. Soon, a document specifying how to write new Java methods for zlisp will be available, by now, just think of them as objects that take some parameters, do something with them and then return another object.
 


3. How does zlisp work.

3.1 Defining new functions. The lambda operator. Local variables.
    Now that we know all the data types used when programming in zlisp, let's talk a little about what can we do with them. First of all, you may take a look at chapter 4, where all predefined methods are listed and then you would be able to start writing a little more complex programs, but in this chapter we will introduce some important concepts to understand the internals of the interpreter and so allow you to take advantage of the most interesting features of LISP and this interpreter.

    The first thing we will introduce is the defun operator, that will allow you to define new functions. We have already seen arithmetic operators such as plus, difference, quotient, times and so on, but suppose you are implementing a program on which you have to evaluate many times a function like f(x)=5x+3. Of course, you don't want to write the complete expression (involving plus and times operators) every time you need to use the function. Almost all programming languages have some way to avoid you doing this by defining a function that is the stored on memory and used whenever yo need. The exact name of the LISP operator used to define functions varies a little on different dialects, but the most common one is defun. We will see how this operator works by defining the function f(x):

defun takes three parameters: the first one is the name of the function you want to define, the second is a list of the parameters your function will use, and the third is the body of the function, i.e. the expression that will be evaluated whenever you call the function. Now that you have defined the function f(x), you can use it as if it were another LISP operator, making no difference, so evaluate (f 3) and you will get 18. Note that:     But what sort of thing is a defined function? Well, we have seen all data types of zlisp, so a function must be one of the types listed on the last chapter. By the way we write it, without double-quotes, nor brackets, you may realize that f is, indeed, a symbol. But it have to be linked to something to tell the interpreter how to handle when it finds it in an expression like (f 3). Ok, let's see if we can evaluate the symbol f alone: write it and evaluate. You probably got something like this: Just a list!! That's right: a function is nothing but a list, the first of whose elements is the list of function parameters, and the second the function body. Now this question arises: Had we obtained the same results if had we written (setq f '((x) (plus 3 (times 5 x))), instead of using the defun operator? The answer is yes; it would had been exactly the same, so the defun operator is not unavoidable if you want to define functions. Furthermore, you can use lists directly as if they were functions, without defining them before: will have the same effect as (f 3). That sort of things, however, are not true on most LISP interpreters and the way to  do things like the above one is to use the lambda operator. Using it, the latter expression will sound like As you may realize, in zlisp, the lambda operator is very simple: it just return a list composed by its parameters, not evaluating them. It's recommendable to use the lambda operator instead of quoted lists, because is the widely used convention on most LISP interpreters. The usefulness of using functions without assigning a name will be noted latter, when we discuss the applicative operators.

    Finally, we will talk a little about LISP variables. On chapter 3, we saw the setq operator that allowed us to link symbols to other objects. We will call symbols linked on this way global variables because they have an unlimited field of visibility, that is, their linkages can be seen anywhere at anytime . But on this chapter we have seen how function parameters are linked before the function body is evaluated and then unlinked. These are called local variables because they can only be seen on a limited space of the program (the function body). On chapter 4 we will see that there are different places where local variables can be defined. This problem arises: What happens if there are global and local variables with the same name? Does a collision occur?. Let's see if there is any problem by writing this code:

The latter expression will be evaluated to 20. But if you ask the interpreter about the value of the x symbol, it will return 5, not 10. That is because when two variables have the same name, the local one prevails over the global, so when the body of the function add_ten is evaluated, the global variable is hidden and x is assigned to 10, but as soon as the local variable is released, x gets again its original value 5.

    If two local variables with the same name are defined at the same time, the lower level variable prevails, so if you evaluate the expressions

you will get 17, as you would have expected. Thus, once you have defined a function, it doesn't matter what names did you give to the variables, the behavior will be always the same, and you don't have to care. Function variables are so called mute variables.

3.2 Evaluating and running objects.
    On this section, we will go further about some concepts introduced on the latter one and actually know the behavior of the interpreter. We have been using the word evaluate since the very beginning of this document and probably you already know what does it mean for the interpreter: it takes an expression, it looks trough it and then returns something in response. On chapter one, we saw how atoms are evaluated: numbers, strings, errors an methods are evaluated to themselves, symbols are evaluated to the object they are linked to, if there is one, or to an error if there is not, but we don't know what does the interpreter do when it evaluates a list.

    To exemplify it we will use the following few lists that can be evaluated:

a)    (plus 2 2)
b)    (plus_clone 2 2)
c)    ((lambda (x y) (plus x y)) 2 2)

where plus_clone is a function previously defined as (defun plus_clone (x y) (plus x y)). The procedure to evaluate a list is:

Now we have understood all things we have been doing all the time. We understand how zlisp works and have arrived to the top of the mountain. Next chapters will be the way down, to make use of all things we have learned. First we should take a look over all the standard methods of zlisp.

 

4. zlisp standard methods.

    This chapter contains a list of all symbol linked to methods zlisp starts with. On naming them, I tried to use the most common notations and sometimes I have used several names for a single method so you can use the one you like most; in particular, there are some methods that have a symbolic abreviation, as plus, times, etc. In these cases, I list the abreviation in parenthesis after the full name. I know there are some useful methods zlisp lacks and I will try to implement them as soon as possible. In the meanwhile, you can try to define new zlisp functions to fill the gap. Remember that you can use setq to link methods to new symbols; it will be useful if you do not like the names I use.

4.1 General purpose methods.

and (&):

atom: boundp: concat: cond: defun:  equal (=): eval: if:  import: lambda: lbound: let: load: method: msg: not (!):
null:
nullp: or (|): quote: set: setq: stringp: symbolp: tostring: 4.2 Arithmetic operators.

add1:

difference (-): greaterp (>): lessp (<): numberp: plus (+): quotient (-): sub1: times (*): zerop: 4.3 List manipulating methods.

append:

assoc: car: cdr: cons: intersection: last: length: list: listp: member: nth: nthcdr: reverse: setdifference: sublis: subst: union: 4.4 Applicative operators.

applytoall:

findif: subset: 4.5 Iterators.
do: go:
goto: prog: return:


5. Packages.

5.1. What is a zlisp package.
    Those described on the above chapter are the native operators of zlisp, all of them are loaded and ready to be used at the very time you tell the interpreter to evaluate your first expression and you do not have to do anything before. However, we have seen how zlisp is designed to be easily extended via Java code, and the most practical way to do this is creating a package.  If you are interested in writing new packages, you will have to wait for the document the author is preparing on which you will read as much as you want about extending zlisp. By now, this chapter treats only about how can you use some precompiled packages included with zlisp.

    Let us be clear from the beginning: a zlisp package is a Java class. This class may contain some Methods and initialization code and is dynamically loaded when you use the zlisp operator import. Note that when you specify the class name you want to load, you should write the whole Java name of the class (i.e. you should write zlisp.LispMath instead of LispMath) so that the Java machine would be able to fetch the class.

5.2. Available packages for zlisp.
    Two predefined packages are included, by now, with zlisp:  LispMath extends zlisp ability for numerical calculus and LispGraph allows the user, when running zlisp in a graphic environment (as an applet in your browser, for example), to draw graphics on the screen.

5.2.1. The LispMath package.
    When you tell the interpreter to evaluate the expression (import "zlisp.LispMath") these new symbols get linked:

sin, cos, tan, asin, acos, atan:

log, exp: sqrt: random: round: e, pi: 5.2.2. The LispGraph package.
     When you run zlisp inside a graphic environment (e.g. as an applet), you can take advantages of its graphics capabilities. At any time you can switch between the text input/output and the graphics output by clicking on the Text/Graph button. However, you must remember that when you you start zlisp, graphics are not initialized, so you will get nothing when you switch to graph mode. To initialize graphics, you have just to load the LispGraph package by evaluating (import "zlisp.LispGraph"). The graphics canvas is then set up to a white rectangle and you can start drawing. When you need to specify a pixel of the rectangle, you have to give its cartesian coordinates; remember that they are referred to the upper left corner and that they should always be two integers. On the other hand, when you want to specify a color, you must give its three RGB components (red, green and blue) in a range from 0 to 1; they must be, therefore, float numbers. The symbols contained in the LispGraph package are:

circle:

clear: cls: color:  drawmsg: line: rect, fillrect: point: setfont:  pendown: penup: forward: left: right: turtle: height, width:


6. Examples on using zlisp.

    Once we have introduced all the elements of zlisp, we are able to show how to program some toys to give you some examples on the LISP language and on zlisp. Some of them (namely the Hanoi towers example) are classic illustrations on the LISP language that you will find on many treatises on this language, but others, as the one of the Koch curves, take advantage of the graphics capabilities of zlisp that are absent on many other LISP dialects. The purpose of this examples is giving you some code to get started about, it will be a good idea for you to modify and rewrite some parts of the code to familiarize yourself with the language. If you have some suggestion about these programs, please, e-mail me.

6.1. The sieve of Eratosthenes.
    As the 1979 edition of the Enciclopaedia Britannica defines it, the sieve of Eratosthenes is a

Of course, it would be impossible for any computer to perform such a procedure over all the natural numbers, so we will limit ourselves to a finite set of numbers ranging from 1 to 99. Thus, the first thing we have to do is to create a list containing all these numbers. This list, which we will call numbers can be created by many ways. We will use the following one: Now we will define a function to now if a number n is divisible by m: Thus we can create a function that takes a list and returns its cdr except those elements that are factorized by the car: So if we call (cribe '(2 3 4 5 6 7 8 9)) we will get (3 5 7 9). We are close to our target; to finish our task, we have only to create a recursive function that calls cribe and goes through the list avoiding the numbers we know they are not primes: Finally, to make a list containing all the prime numbers from 2 to 100, we have only to evaluate the expression and it will give us the list (2 3 5 7 11...) that we were looking for. 1