# beowulf
## Þý liste cræfte spræc
LISP 1.5 is to all Lisp dialects as Beowulf is to English literature.

## Contents
* [What this is](#what-this-is)
+ [Status](#status)
+ [BUT WHY?!!?!](#but-why-----)
+ [Project Target](#project-target)
+ [Invoking](#invoking)
+ [Building and Invoking](#building-and-invoking)
+ [Reader macros](#reader-macros)
+ [Functions and symbols implemented](#functions-and-symbols-implemented)
+ [Architectural plan](#architectural-plan)
- [resources/lisp1.5.lsp](#resources-lisp15lsp)
- [beowulf/boostrap.clj](#beowulf-boostrapclj)
- [beowulf/host.clj](#beowulf-hostclj)
- [beowulf/read.clj](#beowulf-readclj)
+ [Commentary](#commentary)
* [Installation](#installation)
+ [Input/output](#input-output)
- [SYSOUT](#sysout)
- [SYSIN](#sysin)
* [Learning Lisp 1.5](#learning-lisp-15)
* [Other Lisp 1.5 resources](#other-lisp-15-resources)
+ [Other implmentations](#other-implementations)
+ [History resources](#history-resources)
* [License](#license)
Table of contents generated with markdown-toc
## What this is
A work-in-progress towards an implementation of Lisp 1.5 in Clojure. The
objective is to build a complete and accurate implementation of Lisp 1.5
as described in the manual, with, in so far as is possible, exactly the
same behaviour — except as documented below.
### BUT WHY?!!?!
Because.
Because Lisp is the only computer language worth learning, and if a thing
is worth learning, it's worth learning properly; which means going back to
the beginning and trying to understand that.
Because there is, so far as I know, no working implementation of Lisp 1.5
for modern machines.
Because I'm barking mad, and this is therapy.
### Status
Working Lisp interpreter, but some key features not yet implemented.
* [Project website](https://simon-brooke.github.io/beowulf/).
* [Source code documentation](https://simon-brooke.github.io/beowulf/docs/codox/index.html).
### Project Target
The project target is to be able to run the [Wang algorithm for the propositional calculus](https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=52) given in chapter 8 of the *Lisp 1.5 Programmer's Manual*. When that runs, the project is as far as I am concerned feature complete. I may keep tinkering with it after that and I'll certainly accept pull requests which are in the spirit of the project (i.e. making Beowulf more usable, and/or implementing parts of Lisp 1.5 which I have not implemented), but this isn't intended to be a new language for doing real work; it's an educational and archaeological project, not serious engineering.
Some `readline`-like functionality would be really useful, but my attempt to integrate [JLine](https://github.com/jline/jline3) has not (yet) been successful.
An in-core structure editor would be an extremely nice thing, and I may well implement one.
You are of course welcome to fork the project and do whatever you like with it!
### Invoking
Invoke with
java -jar target/uberjar/beowulf-0.3.0-standalone.jar --help
(Obviously, check your version number)
Command line arguments as follows:
```
-h, --help Print this message
-p PROMPT, --prompt PROMPT Set the REPL prompt to PROMPT
-r INITFILE, --read SYSOUTFILE Read Lisp sysout from the file SYSOUTFILE
(defaults to `resources/lisp1.5.lsp`)
-s, --strict Strictly interpret the Lisp 1.5 language,
without extensions.
```
To end a session, type `STOP` at the command prompt.
### Building and Invoking
Build with
lein uberjar
### Reader macros
Currently `SETQ` and `DEFUN` are implemented as reader macros, sort of. It would
now be possible to reimplement them as `FEXPRs` and so the reader macro functionality will probably go away.
### Functions and symbols implemented
| Function | Type | Signature | Implementation | Documentation |
|--------------|----------------|------------------|----------------|----------------------|
| NIL | Lisp variable | ? | | The canonical empty list. See manual pages 22, 69s |
| T | Lisp variable | ? | | The canonical true value. See manual pages 22, 69 |
| F | Lisp variable | ? | | The canonical false value. See manual pages 22, 69 |
| ADD1 | Host lambda function | x:number | | Add one to the number `x`. |
| AND | Host lambda function | expr* | PREDICATE | `T` if and only if none of my `args` evaluate to either `F` or `NIL`, else `F`.
In `beowulf.host` principally because I don't yet feel confident to define varargs functions in Lisp. |
| APPEND | Lisp lambda function | ? | | see manual pages 11, 61 |
| APPLY | Host lambda function | ? | | Apply this `function` to these `arguments` in this `environment` and return the result.
For bootstrapping, at least, a version of APPLY written in Clojure.
All args are assumed to be symbols or `beowulf.cons-cell/ConsCell` objects.
See page 13 of the Lisp 1.5 Programmers Manual. |
| ASSOC | Lisp lambda function, Host lambda function | a:list | ? | If a is an association list such as the one formed by PAIRLIS in the above example, then assoc will produce the first pair whose first term is x. Thus it is a table searching function. All args are assumed to be `beowulf.cons-cell/ConsCell` objects. See page 12 of the Lisp 1.5 Programmers Manual.
**NOTE THAT** this function is overridden by an implementation in Lisp, but is currently still present for bootstrapping. |
| ATOM | Host lambda function | x:expr | PREDICATE | Returns `T` if and only if the argument `x` is bound to an atom; else `F`. It is not clear to me from the documentation whether `(ATOM 7)` should return `T` or `F`. I'm going to assume `T`. |
| CAR | Host lambda function | list | | Return the item indicated by the first pointer of a pair. NIL is treated specially: the CAR of NIL is NIL. |
| CAAAAR | Lisp lambda function | list | ? | ? |
| CAAADR | Lisp lambda function | list | ? | ? |
| CAAAR | Lisp lambda function | list | ? | ? |
| CAADAR | Lisp lambda function | list | ? | ? |
| CAADDR | Lisp lambda function | list | ? | ? |
| CAADR | Lisp lambda function | list | ? | ? |
| CAAR | Lisp lambda function | list | ? | ? |
| CADAAR | Lisp lambda function | list | ? | ? |
| CADADR | Lisp lambda function | list | ? | ? |
| CADAR | Lisp lambda function | list | ? | ? |
| CADDAR | Lisp lambda function | list | ? | ? |
| CADDDR | Lisp lambda function | list | ? | ? |
| CADDR | Lisp lambda function | list | ? | ? |
| CADR | Lisp lambda function | list | ? | ? |
| CDAAAR | Lisp lambda function | list | ? | ? |
| CDAADR | Lisp lambda function | list | ? | ? |
| CDAAR | Lisp lambda function | list | ? | ? |
| CDADAR | Lisp lambda function | list | ? | ? |
| CDADDR | Lisp lambda function | list | ? | ? |
| CDADR | Lisp lambda function | list | ? | ? |
| CDAR | Lisp lambda function | list | ? | ? |
| CDDAAR | Lisp lambda function | list | ? | ? |
| CDDADR | Lisp lambda function | list | ? | ? |
| CDDAR | Lisp lambda function | list | ? | ? |
| CDDDAR | Lisp lambda function | list | ? | ? |
| CDDDDR | Lisp lambda function | list | ? | ? |
| CDDDR | Lisp lambda function | list | ? | ? |
| CDDR | Lisp lambda function | list | ? | ? |
| CDR | Host lambda function | list | | Return the item indicated by the second pointer of a pair. NIL is treated specially: the CDR of NIL is NIL. |
| CONS | Host lambda function | expr, expr | | Construct a new instance of cons cell with this `car` and `cdr`. |
| CONSP | Host lambda function | o:expr | ? | Return `T` if object `o` is a cons cell, else `F`.
**NOTE THAT** this is an extension function, not available in strict mode. I believe that Lisp 1.5 did not have any mechanism for testing whether an argument was, or was not, a cons cell. |
| COPY | Lisp lambda function | ? | | see manual pages 62 |
| DEFINE | Host lambda function | ? | PSEUDO-FUNCTION | Bootstrap-only version of `DEFINE` which, post boostrap, can be overwritten in LISP. The single argument to `DEFINE` should be an association list of symbols to lambda functions. See page 58 of the manual. |
| DIFFERENCE | Host lambda function | x:number, y:number | | Returns the result of subtracting the number `y` from the number `x` |
| DIVIDE | Lisp lambda function | x:number, y:number | | see manual pages 26, 64 |
| DOC | Host lambda function | ? | ? | Open the page for this `symbol` in the Lisp 1.5 manual, if known, in the default web browser.
**NOTE THAT** this is an extension function, not available in strct mode. |
| EFFACE | Lisp lambda function | ? | PSEUDO-FUNCTION | see manual pages 63 |
| ERROR | Host lambda function | ? | PSEUDO-FUNCTION | Throw an error |
| EQ | Host lambda function | ? | PREDICATE | Returns `T` if and only if both `x` and `y` are bound to the same atom, else `NIL`. |
| EQUAL | Host lambda function | ? | PREDICATE | This is a predicate that is true if its two arguments are identical S-expressions, and false if they are different. (The elementary predicate `EQ` is defined only for atomic arguments.) The definition of `EQUAL` is an example of a conditional expression inside a conditional expression. NOTE: returns `F` on failure, not `NIL` |
| EVAL | Host lambda function | ? | | Evaluate this `expr` and return the result. If `environment` is not passed, it defaults to the current value of the global object list. The `depth` argument is part of the tracing system and should not be set by user code. All args are assumed to be numbers, symbols or `beowulf.cons-cell/ConsCell` objects. However, if called with just a single arg, `expr`, I'll assume it's being called from the Clojure REPL and will coerce the `expr` to `ConsCell`. |
| FACTORIAL | Lisp lambda function | ? | ? | ? |
| FIXP | Host lambda function | ? | PREDICATE | ? |
| GENSYM | Host lambda function | ? | | Generate a unique symbol. |
| GET | Host lambda function | ? | | From the manual: '`get` is somewhat like `prop`; however its value is car of the rest of the list if the `indicator` is found, and NIL otherwise.'
It's clear that `GET` is expected to be defined in terms of `PROP`, but we can't implement `PROP` here because we lack `EVAL`; and we can't have `EVAL` here because both it and `APPLY` depends on `GET`.
OK, It's worse than that: the statement of the definition of `GET` (and of) `PROP` on page 59 says that the first argument to each must be a list; But the in the definition of `ASSOC` on page 70, when `GET` is called its first argument is always an atom. Since it's `ASSOC` and `EVAL` which I need to make work, I'm going to assume that page 59 is wrong. |
| GREATERP | Host lambda function | ? | PREDICATE | ? |
| INTEROP | Host lambda function | ? | ? | ? |
| INTERSECTION | Lisp lambda function | ? | ? | ? |
| LENGTH | Lisp lambda function | ? | | see manual pages 62 |
| LESSP | Host lambda function | ? | PREDICATE | ? |
| MAPLIST | Lisp lambda function | ? | FUNCTIONAL | see manual pages 20, 21, 63 |
| MEMBER | Lisp lambda function | ? | PREDICATE | see manual pages 11, 62 |
| MINUSP | Lisp lambda function | ? | PREDICATE | see manual pages 26, 64 |
| NOT | Lisp lambda function | ? | PREDICATE | see manual pages 21, 23, 58 |
| NULL | Lisp lambda function | ? | PREDICATE | see manual pages 11, 57 |
| NUMBERP | Host lambda function | ? | PREDICATE | ? |
| OBLIST | Host lambda function | ? | | Return a list of the symbols currently bound on the object list.
**NOTE THAT** in the Lisp 1.5 manual, footnote at the bottom of page 69, it implies that an argument can be passed but I'm not sure of the semantics of this. |
| ONEP | Lisp lambda function | ? | PREDICATE | see manual pages 26, 64 |
| OR | Host lambda function | ? | PREDICATE | `T` if and only if at least one of my `args` evaluates to something other than either `F` or `NIL`, else `F`.
In `beowulf.host` principally because I don't yet feel confident to define varargs functions in Lisp. |
| PAIR | Lisp lambda function | ? | | see manual pages 60 |
| PAIRLIS | Lisp lambda function, Host lambda function | ? | ? | This function gives the list of pairs of corresponding elements of the lists `x` and `y`, and APPENDs this to the list `a`. The resultant list of pairs, which is like a table with two columns, is called an association list. Essentially, it builds the environment on the stack, implementing shallow binding.
All args are assumed to be `beowulf.cons-cell/ConsCell` objects. See page 12 of the Lisp 1.5 Programmers Manual.
**NOTE THAT** this function is overridden by an implementation in Lisp, but is currently still present for bootstrapping. |
| PLUS | Host lambda function | ? | | ? |
| PRETTY | | ? | ? | ? |
| PRINT | | ? | PSEUDO-FUNCTION | see manual pages 65, 84 |
| PROG | Host nlambda function | ? | | The accursed `PROG` feature. See page 71 of the manual.
Lisp 1.5 introduced `PROG`, and most Lisps have been stuck with it ever since. It introduces imperative programming into what should be a pure functional language, and consequently it's going to be a pig to implement. Broadly, `PROG` is a variadic pseudo function called as a `FEXPR` (or possibly an `FSUBR`, although I'm not presently sure that would even work.) The arguments, which are unevaluated, are a list of forms, the first of which is expected to be a list of symbols which will be treated as names of variables within the program, and the rest of which (the 'program body') are either lists or symbols. Lists are treated as Lisp expressions which may be evaulated in turn. Symbols are treated as targets for the `GO` statement.