diff --git a/.gitignore b/.gitignore
index 795f8a4..a0db7e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,7 +21,3 @@ Sysout*.lsp
*.pdf
src/beowulf/scratch.clj
-
-.portal/vs-code.edn
-
-.portal/
diff --git a/README.md b/README.md
index 47aee7e..430ce62 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,7 @@ LISP 1.5 is to all Lisp dialects as Beowulf is to English literature.
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 bahaviour - except as documented below.
+same behaviour — except as documented below.
### BUT WHY?!!?!
@@ -48,20 +48,11 @@ 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.
-#### There are other barking mad people out there
-
-Since I wrote Beowulf, I've become aware of other modern reimplementations of Lisp 1.5:
-
-1. [Kenichi Sasagawa's in C](https://github.com/sasagawa888/lisp1.5);
-2. [Ichigo Lisp, in JavaScript, which compiles to Web Assembly](https://github.com/zick/IchigoLisp);
-3. [Geert Bosch's implementation in ADA](https://github.com/GeertBosch/lisp);
-
-There are probably others.
-
-In addition, [this](https://github.com/informatimago/lisp-1-5) appears to be a transcription of the original IBM 709 card deck for the Lisp 1.5 system. There's an IBM 709 emulator [here](https://github.com/Bertoid1311/B7094) on which it might be possible to actually run this.
-
### Status
Working Lisp interpreter, but some key features not yet implemented.
@@ -71,14 +62,11 @@ Working Lisp interpreter, but some key features not yet implemented.
### 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.
+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.
+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.
+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!
@@ -86,7 +74,7 @@ You are of course welcome to fork the project and do whatever you like with it!
Invoke with
- java -jar target/uberjar/beowulf-0.3.1-standalone.jar --help
+ java -jar target/uberjar/beowulf-0.3.0-standalone.jar --help
(Obviously, check your version number)
@@ -119,53 +107,53 @@ now be possible to reimplement them as `FEXPRs` and so the reader macro function
| Function | Type | Signature | Implementation | Documentation |
|--------------|----------------|------------------|----------------|----------------------|
-| NIL | Lisp variable | ? | | see manual pages 22, 69 |
-| T | Lisp variable | ? | | see manual pages 22, 69 |
-| F | Lisp variable | ? | | see manual pages 22, 69 |
-| ADD1 | Host lambda function | ? | | ? |
-| AND | Host lambda function | ? | 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 | ? | ? | 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 | ? | 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 | ? | | 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 | ? | ? | ? |
-| CAAADR | Lisp lambda function | ? | ? | ? |
-| CAAAR | Lisp lambda function | ? | ? | ? |
-| CAADAR | Lisp lambda function | ? | ? | ? |
-| CAADDR | Lisp lambda function | ? | ? | ? |
-| CAADR | Lisp lambda function | ? | ? | ? |
-| CAAR | Lisp lambda function | ? | ? | ? |
-| CADAAR | Lisp lambda function | ? | ? | ? |
-| CADADR | Lisp lambda function | ? | ? | ? |
-| CADAR | Lisp lambda function | ? | ? | ? |
-| CADDAR | Lisp lambda function | ? | ? | ? |
-| CADDDR | Lisp lambda function | ? | ? | ? |
-| CADDR | Lisp lambda function | ? | ? | ? |
-| CADR | Lisp lambda function | ? | ? | ? |
-| CDAAAR | Lisp lambda function | ? | ? | ? |
-| CDAADR | Lisp lambda function | ? | ? | ? |
-| CDAAR | Lisp lambda function | ? | ? | ? |
-| CDADAR | Lisp lambda function | ? | ? | ? |
-| CDADDR | Lisp lambda function | ? | ? | ? |
-| CDADR | Lisp lambda function | ? | ? | ? |
-| CDAR | Lisp lambda function | ? | ? | ? |
-| CDDAAR | Lisp lambda function | ? | ? | ? |
-| CDDADR | Lisp lambda function | ? | ? | ? |
-| CDDAR | Lisp lambda function | ? | ? | ? |
-| CDDDAR | Lisp lambda function | ? | ? | ? |
-| CDDDDR | Lisp lambda function | ? | ? | ? |
-| CDDDR | Lisp lambda function | ? | ? | ? |
-| CDDR | Lisp lambda function | ? | ? | ? |
-| CDR | Host lambda function | ? | | 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 | ? | | Construct a new instance of cons cell with this `car` and `cdr`. |
-| CONSP | Host lambda function | ? | ? | Return `T` if object `o` is a cons cell, else `F`. **NOTE THAT** this is an extension function, not available in strct 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 | ? | | ? |
-| DIVIDE | Lisp lambda function | ? | | 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 |
+| 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` |
@@ -173,50 +161,50 @@ now be possible to reimplement them as `FEXPRs` and so the reader macro function
| 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. |
+| 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 |
+| 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 |
+| 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. Eessentially, 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. |
+| 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. **GO:** A `GO` statement takes the form of `(GO target)`, where `target` should be one of the symbols which occur at top level among that particular invocation of `PROG`s arguments. A `GO` statement may occur at top level in a PROG, or in a clause of a `COND` statement in a `PROG`, but not in a function called from the `PROG` statement. When a `GO` statement is evaluated, execution should transfer immediately to the expression which is the argument list immediately following the symbol which is its target. If the target is not found, an error with the code `A6` should be thrown. **RETURN:** A `RETURN` statement takes the form `(RETURN value)`, where `value` is any value. Following the evaluation of a `RETURN` statement, the `PROG` should immediately exit without executing any further expressions, returning the value. **SET and SETQ:** In addition to the above, if a `SET` or `SETQ` expression is encountered in any expression within the `PROG` body, it should affect not the global object list but instead only the local variables of the program. **COND:** In **strict** mode, when in normal execution, a `COND` statement none of whose clauses match should not return `NIL` but should throw an error with the code `A3`... *except* that inside a `PROG` body, it should not do so. *sigh*. **Flow of control:** Apart from the exceptions specified above, expressions in the program body are evaluated sequentially. If execution reaches the end of the program body, `NIL` is returned. Got all that? Good. |
-| PROP | Lisp lambda function | ? | FUNCTIONAL | see manual pages 59 |
-| QUOTE | Lisp lambda function | ? | | see manual pages 10, 22, 71 |
-| QUOTIENT | Host lambda function | ? | | I'm not certain from the documentation whether Lisp 1.5 `QUOTIENT` returned the integer part of the quotient, or a realnum representing the whole quotient. I am for now implementing the latter. |
+| 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.
- **GO:** A `GO` statement takes the form of `(GO target)`, where `target` should be one of the symbols which occur at top level among that particular invocation of `PROG`s arguments. A `GO` statement may occur at top level in a PROG, or in a clause of a `COND` statement in a `PROG`, but not in a function called from the `PROG` statement. When a `GO` statement is evaluated, execution should transfer immediately to the expression which is the argument list immediately following the symbol which is its target. If the target is not found, an error with the code `A6` should be thrown.
- **RETURN:** A `RETURN` statement takes the form `(RETURN value)`, where `value` is any value. Following the evaluation of a `RETURN` statement, the `PROG` should immediately exit without executing any further expressions, returning the value.
- **SET and SETQ:** In addition to the above, if a `SET` or `SETQ` expression is encountered in any expression within the `PROG` body, it should affect not the global object list but instead only the local variables of the program.
- **COND:** In **strict** mode, when in normal execution, a `COND` statement none of whose clauses match should not return `NIL` but should throw an error with the code `A3`... *except* that inside a `PROG` body, it should not do so. *sigh*.
**Flow of control:** Apart from the exceptions specified above, expressions in the program body are evaluated sequentially. If execution reaches the end of the program body, `NIL` is returned.
Got all that? Good. |
+| PROP | Lisp lambda function | ? | FUNCTIONAL | see manual pages 59 |
+| QUOTE | Lisp lambda function | ? | | see manual pages 10, 22, 71 |
+| QUOTIENT | Host lambda function | ? | | I'm not certain from the documentation whether Lisp 1.5 `QUOTIENT` returned the integer part of the quotient, or a realnum representing the whole quotient. I am for now implementing the latter. |
| RANGE | Lisp lambda function | ? | ? | ? |
| READ | Host lambda function | ? | PSEUDO-FUNCTION | An implementation of a Lisp reader sufficient for bootstrapping; not necessarily the final Lisp reader. `input` should be either a string representation of a LISP expression, or else an input stream. A single form will be read. |
| REMAINDER | Host lambda function | ? | | ? |
| REPEAT | Lisp lambda function | ? | ? | ? |
-| RPLACA | Host lambda function | ? | PSEUDO-FUNCTION | Replace the CAR pointer of this `cell` with this `value`. Dangerous, should really not exist, but does in Lisp 1.5 (and was important for some performance hacks in early Lisps) |
-| RPLACD | Host lambda function | ? | PSEUDO-FUNCTION | Replace the CDR pointer of this `cell` with this `value`. Dangerous, should really not exist, but does in Lisp 1.5 (and was important for some performance hacks in early Lisps) |
-| SEARCH | Lisp lambda function | ? | FUNCTIONAL | see manual pages 63 |
-| SET | Host lambda function | ? | PSEUDO-FUNCTION | Implementation of SET in Clojure. Add to the `oblist` a binding of the value of `var` to the value of `val`. NOTE WELL: this is not SETQ! |
+| RPLACA | Host lambda function | ? | PSEUDO-FUNCTION | Replace the `CAR` pointer of this `cell` with this `value`. Dangerous, should really not exist, but does in Lisp 1.5 (and was important for some performance hacks in early Lisps) |
+| RPLACD | Host lambda function | ? | PSEUDO-FUNCTION | Replace the `CDR` pointer of this `cell` with this `value`. Dangerous, should really not exist, but does in Lisp 1.5 (and was important for some performance hacks in early Lisps) |
+| SEARCH | Lisp lambda function | ? | FUNCTIONAL | see manual pages 63 |
+| SET | Host lambda function | ? | PSEUDO-FUNCTION | Implementation of SET in Clojure. Add to the `oblist` a binding of the value of `var` to the value of `val`.
**NOTE WELL**: this is not SETQ! |
| SUB1 | Lisp lambda function, Host lambda function | ? | | ? |
| SUB2 | Lisp lambda function | ? | ? | ? |
-| SUBLIS | Lisp lambda function | ? | | see manual pages 12, 61 |
-| SUBST | Lisp lambda function | ? | | see manual pages 11, 61 |
-| SYSIN | Host lambda function | ? | ? | Read the contents of the file at this `filename` into the object list. If the file is not a valid Beowulf sysout file, this will probably corrupt the system, you have been warned. File paths will be considered relative to the filepath set when starting Lisp. It is intended that sysout files can be read both from resources within the jar file, and from the file system. If a named file exists in both the file system and the resources, the file system will be preferred. **NOTE THAT** if the provided `filename` does not end with `.lsp` (which, if you're writing it from the Lisp REPL, it won't), the extension `.lsp` will be appended. **NOTE THAT** this is an extension function, not available in strct mode. |
-| SYSOUT | Host lambda function | ? | ? | Dump the current content of the object list to file. If no `filepath` is specified, a file name will be constructed of the symbol `Sysout` and the current date. File paths will be considered relative to the filepath set when starting Lisp. **NOTE THAT** this is an extension function, not available in strct mode. |
-| TERPRI | | ? | PSEUDO-FUNCTION | see manual pages 65, 84 |
+| SUBLIS | Lisp lambda function | ? | | see manual pages 12, 61 |
+| SUBST | Lisp lambda function | ? | | see manual pages 11, 61 |
+| SYSIN | Host lambda function | ? | ? | Read the contents of the file at this `filename` into the object list. If the file is not a valid Beowulf sysout file, this will probably corrupt the system, you have been warned. File paths will be considered relative to the filepath set when starting Lisp. It is intended that sysout files can be read both from resources within the jar file, and from the file system. If a named file exists in both the file system and the resources, the file system will be preferred.
**NOTE THAT** if the provided `filename` does not end with `.lsp` (which, if you're writing it from the Lisp REPL, it won't), the extension `.lsp` will be appended.
**NOTE THAT** this is an extension function, not available in strct mode. |
+| SYSOUT | Host lambda function | ? | ? | Dump the current content of the object list to file. If no `filepath` is specified, a file name will be constructed of the symbol `Sysout` and the current date. File paths will be considered relative to the filepath set when starting Lisp.
**NOTE THAT** this is an extension function, not available in strict mode. |
+| TERPRI | | ? | PSEUDO-FUNCTION | see manual pages 65, 84 |
| TIMES | Host lambda function | ? | | ? |
| TRACE | Host lambda function | ? | PSEUDO-FUNCTION | Add this `s` to the set of symbols currently being traced. If `s` is not a symbol or sequence of symbols, does nothing. |
| UNION | Lisp lambda function | ? | ? | ? |
-| UNTRACE | Host lambda function | ? | PSEUDO-FUNCTION | Remove this `s` from the set of symbols currently being traced. If `s` is not a symbol or sequence of symbols, does nothing. |
-| ZEROP | Lisp lambda function | ? | PREDICATE | see manual pages 26, 64 |
+| UNTRACE | Host lambda function | ? | PSEUDO-FUNCTION | Remove this `s` from the set of symbols currently being traced. If `s` is not a symbol or sequence of symbols, does nothing. |
+| ZEROP | Lisp lambda function | ? | PREDICATE | see manual pages 26, 64 |
Functions described as 'Lisp function' above are defined in the default
sysout file, `resources/lisp1.5.lsp`, which will be loaded by default unless
@@ -228,8 +216,7 @@ over the Clojure implementations.
### Architectural plan
-Not everything documented in this section is yet built. It indicates the
-direction of travel and intended destination, not the current state.
+Not everything documented in this section is yet built. It indicates the direction of travel and intended destination, not the current state.
#### resources/lisp1.5.lsp
@@ -347,7 +334,7 @@ even has a working compiler!
### History resources
-I'm compiling a [list of links to historical documents on Lisp 1.5](https://simon-brooke.github.io/beowulf/docs/codox/further_reading.html).
+I'm compiling a [list of links to historical documents on Lisp 1.5](https://simon-brooke.github.io/beowulf/docs/further_reading.html).
## License
diff --git a/doc/lisp1.5.md b/doc/lisp1.5.md
index 4a566ed..6042cc8 100644
--- a/doc/lisp1.5.md
+++ b/doc/lisp1.5.md
@@ -961,15 +961,19 @@ But if eval is given (QUOTE X), X should not be evaluated. QUOTE is a special fo
that prevents its argument from being evaluated.
A special form differs from a function in two ways. Its arguments are not evaluated
before the special form sees them. COND, for example, has a very special way of
+```
+
evaluating its arguments by using evcon. The second way which special forms differ
-from functions is that they may have an indefinite number of arguments. Special forms
-have indicators on their property lists called FEXPR and FSUBR for LISP-defined forms
+from functions is that they may have an indefinite number of arguments. Special forrrls
+have indicators on their property lists called FEXPR and FSUBR for LISP -defined forms
and machine language coded forms, respectively.
-### 2.6 Programming for the Interpreter
+```
+2.6 Programming for the Interpreter
+```
The purpose of this section is to help the programmer avoid certain common errors.
-Example 1: CAR
+Example 1
fn: CAR
args: ((A B))
The value is A. Note that the interpreter expects a list of arguments. The one argu-
@@ -977,18 +981,20 @@ ment for car is (A B). The extra pair of parentheses is necessary.
One could write (LAMBDA (X) (CAR X)) instead of just CAR. This is correct but
unnecessary.
-Example 2: CONS
+```
+Example 2
fn: CONS
-args: (A (B . C))
-The value is cons[a; cons[b; c]] = (A . (B . C)).
-The print program will write this as (A B . C).
+args: (A (B. C))
+The value is cons[^;(^. c)] = (A. (B. C)).
+The print program will write this as (A B. C).
+```
Example (^3) -
fn: CONS
-args: ((CAR (QUOTE (A . B))) (CDR (QUOTE (C . D))))
-The value of this computation will be ((CAR (QUOTE (A . B))) . (CDR (QUOTE (C . D)))).
-This is not what the programmer expected. He expected (CAR (QUOTE (A . B))) to
-evaluate to A, and expected (A . D) as the value of cons.
+args: ((CAR (QUOTE (A. B))) (CDR (QUOTE (C. D))))
+The value of this computation will be ((CAR (QUOTE (A. B))). (CDR (QUOTE (C. D)))).
+This is not what the programmer expected. He expected (CAR (QUOTE (A. B))) to
+evaluate to A, and expected (A. D) as the value of cons.
* The interpreter expects a ---- list of arguments. ------- It does not expect a list of expressions
-- that will evaluate to the arguments. Tworcorrect ways of writing this function are listed
@@ -1715,7 +1721,6 @@ represented in storage only once,
The following simple example has been included to illustrate the exact construction
of list structures. Two types of list structures are shown, and a function for deriving
one from the other is given in LISP.
-
We assume that we have a list of the form
n, = ((A B C) (D E F),... , (X Y z)),
@@ -2704,9 +2709,7 @@ If `deflist` or `define` is used twice on the same object with the same indicato
The function attrib concatenates its two arguments by changing the last element of its first argument to point to the second argument. Thus it is commonly used to tack something onto the end of a property list. The value of attrib is the second argument.
For example
-```
-attrib[FF; (EXPR (LAMBDA (X) (COND ((ATOM X) X) (T (FF (CAR x))))))]
-```
+attrib[~~; (EXPR (LAMBDA (X) (COND ((ATOM X) X) (T (FF (CAR x))))))]
would put EXPR followed by the LAMBDA expression for FF onto the end of the prop-
erty list for FF.
diff --git a/docs/cloverage/beowulf/bootstrap.clj.html b/docs/cloverage/beowulf/bootstrap.clj.html
index a9efdd4..c45387d 100644
--- a/docs/cloverage/beowulf/bootstrap.clj.html
+++ b/docs/cloverage/beowulf/bootstrap.clj.html
@@ -44,1459 +44,1231 @@
013 pretty-print T]]
- 014 [beowulf.host :refer [ASSOC ATOM CAAR CAADR CADAR CADDR CADR CAR CDR
+ 014 [beowulf.host :refer [ASSOC ATOM CAAR CADAR CADDR CADR CAR CDR GET
- 015 CONS ERROR GET LIST NUMBERP PAIRLIS traced?]]
+ 015 LIST NUMBERP PAIRLIS traced?]]
- 016 [beowulf.oblist :refer [*options* NIL]]
+ 016 [beowulf.oblist :refer [*options* NIL oblist]])
- 017 [clojure.string :as s]
+ 017 (:import [beowulf.cons_cell ConsCell]
- 018 [clojure.tools.trace :refer [deftrace]])
-
-
- 019 (:import [beowulf.cons_cell ConsCell]
-
-
- 020 [clojure.lang Symbol]))
+ 018 [clojure.lang Symbol]))
- 021
+ 019
- 022 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ 020 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+ 021 ;;;
+
+
+ 022 ;;; Copyright (C) 2022-2023 Simon Brooke
023 ;;;
- 024 ;;; Copyright (C) 2022-2023 Simon Brooke
+ 024 ;;; This program is free software; you can redistribute it and/or
- 025 ;;;
+ 025 ;;; modify it under the terms of the GNU General Public License
- 026 ;;; This program is free software; you can redistribute it and/or
+ 026 ;;; as published by the Free Software Foundation; either version 2
- 027 ;;; modify it under the terms of the GNU General Public License
+ 027 ;;; of the License, or (at your option) any later version.
- 028 ;;; as published by the Free Software Foundation; either version 2
+ 028 ;;;
- 029 ;;; of the License, or (at your option) any later version.
+ 029 ;;; This program is distributed in the hope that it will be useful,
- 030 ;;;
+ 030 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- 031 ;;; This program is distributed in the hope that it will be useful,
+ 031 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- 032 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+ 032 ;;; GNU General Public License for more details.
- 033 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 033 ;;;
- 034 ;;; GNU General Public License for more details.
+ 034 ;;; You should have received a copy of the GNU General Public License
- 035 ;;;
+ 035 ;;; along with this program; if not, write to the Free Software
- 036 ;;; You should have received a copy of the GNU General Public License
+ 036 ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- 037 ;;; along with this program; if not, write to the Free Software
+ 037 ;;;
- 038 ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ 038 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- 039 ;;;
+
+ 039
-
- 040 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+ 040 (declare APPLY EVAL prog-eval)
041
-
- 042 (declare APPLY EVAL EVCON prog-eval)
+
+ 042 ;;;; The PROGram feature ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
043
-
- 044 ;;;; The PROGram feature ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-
- 045
+
+ 044 (def find-target
- 046 (def ^:dynamic
-
-
- 047 *depth*
-
-
- 048 "Stack depth. Unfortunately we need to be able to pass round depth for
-
-
- 049 functions which call EVAL/APPLY but do not know about depth."
-
-
- 050 0)
-
-
- 051
+ 045 (memoize
- 052 (defn- trace-indent
-
-
- 053 ([] (trace-indent *depth*))
-
-
- 054 ([d] (s/join (repeat d " "))))
-
-
- 055
-
-
- 056 (def find-target
+ 046 (fn [target body]
- 057 (memoize
-
-
- 058 (fn [target body]
-
-
- 059 (loop [body' body]
+ 047 (loop [body' body]
- 060 (cond
+ 048 (cond
- 061 (= body' NIL) (throw (ex-info (str "Mislar GO miercels: `" target "`")
+ 049 (= body' NIL) (throw (ex-info (str "Mislar GO miercels: `" target "`")
- 062 {:phase :lisp
+ 050 {:phase :lisp
- 063 :function 'PROG
+ 051 :function 'PROG
- 064 :type :lisp
+ 052 :type :lisp
- 065 :code :A6
+ 053 :code :A6
- 066 :target target}))
+ 054 :target target}))
- 067 (= (.getCar body') target) body'
+ 055 (= (.getCar body') target) body'
- 068 :else (recur (.getCdr body')))))))
+ 056 :else (recur (.getCdr body')))))))
+
+
+ 057
+
+
+ 058 (defn- prog-cond
+
+
+ 059 "Like `EVCON`, q.v. except using `prog-eval` instead of `EVAL` and not
+
+
+ 060 throwing an error if no clause matches."
+
+
+ 061 [clauses vars env depth]
+
+
+ 062 (loop [clauses' clauses]
+
+
+ 063 (if-not (= clauses' NIL)
+
+
+ 064 (let [test (prog-eval (CAAR clauses') vars env depth)]
+
+
+ 065 (if (not (#{NIL F} test))
+
+
+ 066 (prog-eval (CADAR clauses') vars env depth)
+
+
+ 067 (recur (.getCdr clauses'))))
+
+
+ 068 NIL)))
069
- 070 (defn- prog-cond
-
-
- 071 "Like `EVCON`, q.v. except using `prog-eval` instead of `EVAL` and not
-
-
- 072 throwing an error if no clause matches."
-
-
- 073 [clauses vars env depth]
-
-
- 074 (loop [clauses' clauses]
-
-
- 075 (if-not (= clauses' NIL)
-
-
- 076 (let [test (prog-eval (CAAR clauses') vars env depth)]
-
-
- 077 (if (not (#{NIL F} test))
-
-
- 078 (prog-eval (CADAR clauses') vars env depth)
-
-
- 079 (recur (.getCdr clauses'))))
-
-
- 080 NIL)))
-
-
- 081
-
-
- 082 (defn- merge-vars [vars env]
+ 070 (defn- merge-vars [vars env]
- 083 (reduce
+ 071 (reduce
- 084 #(make-cons-cell
+ 072 #(make-cons-cell
- 085 (make-cons-cell %2 (@vars %2))
+ 073 (make-cons-cell %2 (@vars %2))
- 086 env)
+ 074 env)
- 087 env
+ 075 env
- 088 (keys @vars)))
+ 076 (keys @vars)))
- 089
+ 077
- 090 (defn prog-eval
+ 078 (defn prog-eval
- 091 "Like `EVAL`, q.v., except handling symbols, and expressions starting
+ 079 "Like `EVAL`, q.v., except handling symbols, and expressions starting
- 092 `GO`, `RETURN`, `SET` and `SETQ` specially."
+ 080 `GO`, `RETURN`, `SET` and `SETQ` specially."
- 093 [expr vars env depth]
+ 081 [expr vars env depth]
- 094 (cond
+ 082 (cond
- 095 (number? expr) expr
+ 083 (number? expr) expr
- 096 (symbol? expr) (@vars expr)
-
-
- 097 (instance? ConsCell expr) (case (CAR expr)
-
-
- 098 COND (prog-cond (CDR expr)
-
-
- 099 vars env depth)
-
-
- 100 GO (let [target (CADR expr)]
-
-
- 101 (when (traced? 'PROG)
-
-
- 102 (println " PROG:GO: Goto " target))
-
-
- 103 (make-cons-cell
-
-
- 104 '*PROGGO* target))
-
-
- 105 RETURN (let [val (prog-eval
-
-
- 106 (CADR expr)
-
-
- 107 vars env depth)]
-
-
- 108 (when (traced? 'PROG)
-
-
- 109 (println " PROG:RETURN: Returning "
-
-
- 110 val))
-
-
- 111 (make-cons-cell
-
-
- 112 '*PROGRETURN*
-
-
- 113 val))
-
-
- 114 SET (let [var (prog-eval (CADR expr)
-
-
- 115 vars env depth)
-
-
- 116 val (prog-eval (CADDR expr)
-
-
- 117 vars env depth)]
-
-
- 118 (when (traced? 'PROG)
-
-
- 119 (println " PROG:SET: Setting "
-
-
- 120 var " to " val))
-
-
- 121 (swap! vars
-
-
- 122 assoc
-
-
- 123 var
-
-
- 124 val)
-
-
- 125 val)
-
-
- 126 SETQ (let [var (CADDR expr)
-
-
- 127 val (prog-eval var
-
-
- 128 vars env depth)]
-
-
- 129 (when (traced? 'PROG)
-
-
- 130 (println " PROG:SETQ: Setting " var " to " val))
-
-
- 131 (swap! vars
-
-
- 132 assoc
-
-
- 133 (CADR expr)
-
-
- 134 val)
-
-
- 135 val)
-
-
- 136 ;; else
-
-
- 137 (beowulf.bootstrap/EVAL expr
-
-
- 138 (merge-vars vars env)
-
-
- 139 depth))))
-
-
- 140
-
-
- 141 (defn PROG
-
-
- 142 "The accursed `PROG` feature. See page 71 of the manual.
-
-
- 143
-
-
- 144 Lisp 1.5 introduced `PROG`, and most Lisps have been stuck with it ever
-
-
- 145 since. It introduces imperative programming into what should be a pure
-
-
- 146 functional language, and consequently it's going to be a pig to implement.
-
-
- 147
-
-
- 148 Broadly, `PROG` is a variadic pseudo function called as a `FEXPR` (or
-
-
- 149 possibly an `FSUBR`, although I'm not presently sure that would even work.)
-
-
- 150
-
-
- 151 The arguments, which are unevaluated, are a list of forms, the first of
-
-
- 152 which is expected to be a list of symbols which will be treated as names
-
-
- 153 of variables within the program, and the rest of which (the 'program body')
-
-
- 154 are either lists or symbols. Lists are treated as Lisp expressions which
-
-
- 155 may be evaulated in turn. Symbols are treated as targets for the `GO`
-
-
- 156 statement.
-
-
- 157
-
-
- 158 **GO:**
-
-
- 159 A `GO` statement takes the form of `(GO target)`, where
-
-
- 160 `target` should be one of the symbols which occur at top level among that
-
-
- 161 particular invocation of `PROG`s arguments. A `GO` statement may occur at
-
-
- 162 top level in a PROG, or in a clause of a `COND` statement in a `PROG`, but
-
-
- 163 not in a function called from the `PROG` statement. When a `GO` statement
-
-
- 164 is evaluated, execution should transfer immediately to the expression which
-
-
- 165 is the argument list immediately following the symbol which is its target.
-
-
- 166
-
-
- 167 If the target is not found, an error with the code `A6` should be thrown.
-
-
- 168
-
-
- 169 **RETURN:**
-
-
- 170 A `RETURN` statement takes the form `(RETURN value)`, where
-
-
- 171 `value` is any value. Following the evaluation of a `RETURN` statement,
-
-
- 172 the `PROG` should immediately exit without executing any further
-
-
- 173 expressions, returning the value.
-
-
- 174
-
-
- 175 **SET and SETQ:**
-
-
- 176 In addition to the above, if a `SET` or `SETQ` expression is encountered
-
-
- 177 in any expression within the `PROG` body, it should affect not the global
-
-
- 178 object list but instead only the local variables of the program.
-
-
- 179
-
-
- 180 **COND:**
-
-
- 181 In **strict** mode, when in normal execution, a `COND` statement none of
-
-
- 182 whose clauses match should not return `NIL` but should throw an error with
-
-
- 183 the code `A3`... *except* that inside a `PROG` body, it should not do so.
-
-
- 184 *sigh*.
-
-
- 185
-
-
- 186 **Flow of control:**
-
-
- 187 Apart from the exceptions specified above, expressions in the program body
-
-
- 188 are evaluated sequentially. If execution reaches the end of the program
-
-
- 189 body, `NIL` is returned.
-
-
- 190
-
-
- 191 Got all that?
-
-
- 192
-
-
- 193 Good."
-
-
- 194 [program env depth]
-
-
- 195 (let [trace (traced? 'PROG)
-
-
- 196 vars (atom (reduce merge (map #(assoc {} % NIL) (.getCar program))))
-
-
- 197 body (.getCdr program)
-
-
- 198 targets (set (filter symbol? body))]
-
-
- 199 (when trace (do
-
-
- 200 (println "Program:")
-
-
- 201 (pretty-print program))) ;; for debugging
-
-
- 202 (loop [cursor body]
-
-
- 203 (let [step (if (= NIL cursor) NIL (.getCar cursor))]
-
-
- 204 (when trace (do (println "Executing step: " step)
-
-
- 205 (println " with vars: " @vars)))
-
-
- 206 (cond (= cursor NIL) NIL
-
-
- 207 (symbol? step) (recur (.getCdr cursor))
+ 084 (symbol? expr) (@vars expr)
- 208 :else (let [v (prog-eval (.getCar cursor) vars env depth)]
-
-
- 209 (when trace (println " --> " v))
-
-
- 210 (if (instance? ConsCell v)
+ 085 (instance? ConsCell expr) (case (.getCar expr)
- 211 (case (.getCar v)
+ 086 COND (prog-cond (.getCdr expr)
+
+
+ 087 vars env depth)
+
+
+ 088 GO (make-cons-cell
- 212 *PROGGO* (let [target (.getCdr v)]
+ 089 '*PROGGO* (.getCar (.getCdr expr)))
+
+
+ 090 RETURN (make-cons-cell
+
+
+ 091 '*PROGRETURN*
- 213 (if (targets target)
+ 092 (prog-eval (.getCar (.getCdr expr))
+
+
+ 093 vars env depth))
+
+
+ 094 SET (let [v (CADDR expr)]
+
+
+ 095 (swap! vars
- 214 (recur (find-target target body))
+ 096 assoc
-
- 215 (throw (ex-info (str "Uncynlic GO miercels `"
+
+ 097 (prog-eval (CADR expr)
-
- 216 target "`")
+
+ 098 vars env depth)
-
- 217 {:phase :lisp
+
+ 099 (prog-eval (CADDR expr)
-
- 218 :function 'PROG
-
-
- 219 :args program
-
-
- 220 :type :lisp
-
-
- 221 :code :A6
-
-
- 222 :target target
-
-
- 223 :targets targets}))))
+
+ 100 vars env depth))
- 224 *PROGRETURN* (.getCdr v)
+ 101 v)
-
- 225 ;; else
+
+ 102 SETQ (let [v (CADDR expr)]
+
+
+ 103 (swap! vars
- 226 (recur (.getCdr cursor)))
+ 104 assoc
-
- 227 (recur (.getCdr cursor)))))))))
+
+ 105 (CADR expr)
+
+
+ 106 (prog-eval v
+
+
+ 107 vars env depth))
+
+
+ 108 v)
+
+
+ 109 ;; else
+
+
+ 110 (beowulf.bootstrap/EVAL expr
+
+
+ 111 (merge-vars vars env)
+
+
+ 112 depth))))
- 228
+ 113
+
+
+ 114 (defn PROG
- 229 ;;;; Tracing execution ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ 115 "The accursed `PROG` feature. See page 71 of the manual.
+
+
+ 116
+
+
+ 117 Lisp 1.5 introduced `PROG`, and most Lisps have been stuck with it ever
+
+
+ 118 since. It introduces imperative programming into what should be a pure
+
+
+ 119 functional language, and consequently it's going to be a pig to implement.
+
+
+ 120
+
+
+ 121 Broadly, `PROG` is a variadic pseudo function called as a `FEXPR` (or
+
+
+ 122 possibly an `FSUBR`, although I'm not presently sure that would even work.)
+
+
+ 123
+
+
+ 124 The arguments, which are unevaluated, are a list of forms, the first of
+
+
+ 125 which is expected to be a list of symbols which will be treated as names
+
+
+ 126 of variables within the program, and the rest of which (the 'program body')
+
+
+ 127 are either lists or symbols. Lists are treated as Lisp expressions which
+
+
+ 128 may be evaulated in turn. Symbols are treated as targets for the `GO`
+
+
+ 129 statement.
+
+
+ 130
+
+
+ 131 **GO:**
+
+
+ 132 A `GO` statement takes the form of `(GO target)`, where
+
+
+ 133 `target` should be one of the symbols which occur at top level among that
+
+
+ 134 particular invocation of `PROG`s arguments. A `GO` statement may occur at
+
+
+ 135 top level in a PROG, or in a clause of a `COND` statement in a `PROG`, but
+
+
+ 136 not in a function called from the `PROG` statement. When a `GO` statement
+
+
+ 137 is evaluated, execution should transfer immediately to the expression which
+
+
+ 138 is the argument list immediately following the symbol which is its target.
+
+
+ 139
+
+
+ 140 If the target is not found, an error with the code `A6` should be thrown.
+
+
+ 141
+
+
+ 142 **RETURN:**
+
+
+ 143 A `RETURN` statement takes the form `(RETURN value)`, where
+
+
+ 144 `value` is any value. Following the evaluation of a `RETURN` statement,
+
+
+ 145 the `PROG` should immediately exit without executing any further
+
+
+ 146 expressions, returning the value.
+
+
+ 147
+
+
+ 148 **SET and SETQ:**
+
+
+ 149 In addition to the above, if a `SET` or `SETQ` expression is encountered
+
+
+ 150 in any expression within the `PROG` body, it should affect not the global
+
+
+ 151 object list but instead only the local variables of the program.
+
+
+ 152
+
+
+ 153 **COND:**
+
+
+ 154 In **strict** mode, when in normal execution, a `COND` statement none of
+
+
+ 155 whose clauses match should not return `NIL` but should throw an error with
+
+
+ 156 the code `A3`... *except* that inside a `PROG` body, it should not do so.
+
+
+ 157 *sigh*.
+
+
+ 158
+
+
+ 159 **Flow of control:**
+
+
+ 160 Apart from the exceptions specified above, expressions in the program body
+
+
+ 161 are evaluated sequentially. If execution reaches the end of the program
+
+
+ 162 body, `NIL` is returned.
+
+
+ 163
+
+
+ 164 Got all that?
+
+
+ 165
+
+
+ 166 Good."
+
+
+ 167 [program env depth]
+
+
+ 168 (let [trace (traced? 'PROG)
+
+
+ 169 vars (atom (reduce merge (map #(assoc {} % NIL) (.getCar program))))
+
+
+ 170 body (.getCdr program)
+
+
+ 171 targets (set (filter symbol? body))]
+
+
+ 172 (when trace (do
+
+
+ 173 (println "Program:")
+
+
+ 174 (pretty-print program))) ;; for debugging
+
+
+ 175 (loop [cursor body]
+
+
+ 176 (let [step (.getCar cursor)]
+
+
+ 177 (when trace (do (println "Executing step: " step)
+
+
+ 178 (println " with vars: " @vars)))
+
+
+ 179 (cond (= cursor NIL) NIL
+
+
+ 180 (symbol? step) (recur (.getCdr cursor))
+
+
+ 181 :else (let [v (prog-eval (.getCar cursor) vars env depth)]
+
+
+ 182 (when trace (println " --> " v))
+
+
+ 183 (if (instance? ConsCell v)
+
+
+ 184 (case (.getCar v)
+
+
+ 185 *PROGGO* (let [target (.getCdr v)]
+
+
+ 186 (if (targets target)
+
+
+ 187 (recur (find-target target body))
+
+
+ 188 (throw (ex-info (str "Uncynlic GO miercels `"
+
+
+ 189 target "`")
+
+
+ 190 {:phase :lisp
+
+
+ 191 :function 'PROG
+
+
+ 192 :args program
+
+
+ 193 :type :lisp
+
+
+ 194 :code :A6
+
+
+ 195 :target target
+
+
+ 196 :targets targets}))))
+
+
+ 197 *PROGRETURN* (.getCdr v)
+
+
+ 198 ;; else
+
+
+ 199 (recur (.getCdr cursor)))
+
+
+ 200 (recur (.getCdr cursor)))))))))
+
+
+ 201
+
+
+ 202 ;;;; Tracing execution ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+ 203
+
+
+ 204 (defn- trace-call
+
+
+ 205 "Show a trace of a call to the function named by this `function-symbol`
+
+
+ 206 with these `args` at this depth."
+
+
+ 207 [function-symbol args depth]
+
+
+ 208 (when (traced? function-symbol)
+
+
+ 209 (let [indent (apply str (repeat depth "-"))]
+
+
+ 210 (println (str indent "> " function-symbol " " args)))))
+
+
+ 211
+
+
+ 212 (defn- trace-response
+
+
+ 213 "Show a trace of this `response` from the function named by this
+
+
+ 214 `function-symbol` at this depth."
+
+
+ 215 [function-symbol response depth]
+
+
+ 216 (when (traced? function-symbol)
+
+
+ 217 (let [indent (apply str (repeat depth "-"))]
+
+
+ 218 (println (str "<" indent " " function-symbol " " response))))
+
+
+ 219 response)
+
+
+ 220
+
+
+ 221 (defn- value
+
+
+ 222 "Seek a value for this symbol `s` by checking each of these indicators in
+
+
+ 223 turn."
+
+
+ 224 ([s]
+
+
+ 225 (value s (list 'APVAL 'EXPR 'FEXPR 'SUBR 'FSUBR)))
+
+
+ 226 ([s indicators]
+
+
+ 227 (when (symbol? s)
+
+
+ 228 (first (remove #(= % NIL) (map #(GET s %)
+
+
+ 229 indicators))))))
230
-
- 231 (defn- trace-call
-
- 232 "Show a trace of a call to the function named by this `function-symbol`
-
-
- 233 with these `args` at this depth."
-
-
- 234 [function-symbol args depth]
-
-
- 235 (when (traced? function-symbol)
-
-
- 236 (let [indent (trace-indent depth)]
-
-
- 237 (println (str indent "> " function-symbol " " args)))))
+ 231 ;;;; APPLY ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- 238
+ 232
- 239 (defn- trace-response
+ 233 (defn try-resolve-subroutine
- 240 "Show a trace of this `response` from the function named by this
+ 234 "Attempt to resolve this `subr` with these `args`."
- 241 `function-symbol` at this depth."
-
-
- 242 [function-symbol response depth]
-
-
- 243 (when (traced? function-symbol)
-
-
- 244 (let [indent (apply str (trace-indent depth))]
-
-
- 245 (println (str "<" indent " " function-symbol " " response))))
-
-
- 246 response)
-
-
- 247
-
-
- 248 ;;;; Support functions for interpreter ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-
- 249
-
-
- 250 (defn value
-
-
- 251 "Seek a value for this symbol `s` by checking each of these indicators in
-
-
- 252 turn."
-
-
- 253 ([s]
-
-
- 254 (value s (list 'APVAL 'EXPR 'FEXPR 'SUBR 'FSUBR)))
-
-
- 255 ([s indicators]
-
-
- 256 (when (symbol? s)
-
-
- 257 (first (remove #(= % NIL) (map #(GET s %)
-
-
- 258 indicators))))))
-
-
- 259
-
-
- 260 (defn SASSOC
-
-
- 261 "Like `ASSOC`, but with an action to take if no value is found.
-
-
- 262
-
-
- 263 From the manual, page 60:
-
-
- 264
-
-
- 265 'The function `sassoc` searches `y`, which is a list of dotted pairs, for
-
-
- 266 a pair whose first element that is `x`. If such a pair is found, the value
-
-
- 267 of `sassoc` is this pair. Otherwise the function `u` of no arguments is
-
-
- 268 taken as the value of `sassoc`.'"
-
-
- 269 [x y u]
-
-
- 270 (let [v (ASSOC x y)]
-
-
- 271 (if-not (= v NIL) v
-
-
- 272 (APPLY u NIL NIL))))
-
-
- 273
-
-
- 274
-
-
- 275 ;;;; APPLY ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-
- 276
-
-
- 277 (defn try-resolve-subroutine
-
-
- 278 "Attempt to resolve this `subr` with these `args`."
-
-
- 279 [subr args]
+ 235 [subr args]
- 280 (when (and subr (not= subr NIL))
+ 236 (when (and subr (not= subr NIL))
- 281 (try @(resolve subr)
+ 237 (try @(resolve subr)
- 282 (catch Throwable any
+ 238 (catch Throwable any
- 283 (throw (ex-info "þegnung (SUBR) ne āfand"
+ 239 (throw (ex-info "þegnung (SUBR) ne āfand"
- 284 {:phase :apply
+ 240 {:phase :apply
- 285 :function subr
+ 241 :function subr
- 286 :args args
+ 242 :args args
- 287 :type :beowulf}
+ 243 :type :beowulf}
- 288 any))))))
+ 244 any))))))
- 289
+ 245
- 290 (defn- apply-symbolic
+ 246 (defn- apply-symbolic
- 291 "Apply this `funtion-symbol` to these `args` in this `environment` and
+ 247 "Apply this `funtion-symbol` to these `args` in this `environment` and
- 292 return the result."
+ 248 return the result."
- 293 [^Symbol function-symbol args ^ConsCell environment depth]
+ 249 [^Symbol function-symbol args ^ConsCell environment depth]
- 294 (trace-call function-symbol args depth)
+ 250 (trace-call function-symbol args depth)
- 295 (let [lisp-fn (value function-symbol '(EXPR FEXPR)) ;; <-- should these be handled differently? I think so!
+ 251 (let [lisp-fn (value function-symbol '(EXPR FEXPR))
-
- 296 args' (cond (= NIL args) args
+
+ 252 args' (cond (= NIL args) args
- 297 (empty? args) NIL
+ 253 (empty? args) NIL
- 298 (instance? ConsCell args) args
+ 254 (instance? ConsCell args) args
- 299 :else (make-beowulf-list args))
+ 255 :else (make-beowulf-list args))
- 300 subr (value function-symbol '(SUBR FSUBR))
+ 256 subr (value function-symbol '(SUBR FSUBR))
- 301 host-fn (try-resolve-subroutine subr args')
+ 257 host-fn (try-resolve-subroutine subr args')
+
+
+ 258 result (cond (and lisp-fn
- 302 result (cond (and lisp-fn
-
-
- 303 (not= lisp-fn NIL)) (APPLY lisp-fn args' environment depth)
+ 259 (not= lisp-fn NIL)) (APPLY lisp-fn args' environment depth)
- 304 host-fn (try
+ 260 host-fn (try
- 305 (apply host-fn (when (instance? ConsCell args') args'))
+ 261 (apply host-fn (when (instance? ConsCell args') args'))
- 306 (catch Exception any
+ 262 (catch Exception any
- 307 (throw (ex-info (str "Uncynlic þegnung: "
+ 263 (throw (ex-info (str "Uncynlic þegnung: "
- 308 (.getMessage any))
+ 264 (.getMessage any))
- 309 {:phase :apply
+ 265 {:phase :apply
- 310 :function function-symbol
+ 266 :function function-symbol
- 311 :args args
+ 267 :args args
- 312 :type :beowulf}
+ 268 :type :beowulf}
- 313 any))))
+ 269 any))))
-
- 314 :else (ex-info "þegnung ne āfand"
-
-
- 315 {:phase :apply
-
-
- 316 :function function-symbol
-
-
- 317 :args args
-
-
- 318 :type :beowulf}))]
-
-
- 319 (trace-response function-symbol result depth)
-
-
- 320 result))
-
-
- 321
-
-
- 322 ;; (LABEL ARGS
-
-
- 323 ;; (COND ((COND ((ONEP (LENGTH ARGS)) ARGS)
-
-
- 324 ;; (T (ATTRIB (CAR ARGS) (APPLY CONC (CDR ARGS) NIL))))
-
-
- 325 ;; ARGS)))
-
-
- 326 ;; ((1 2 3 4) (5 6 7 8) (9 10 11 12))
-
-
- 327 ;; NIL
-
-
- 328 ;; (def function (make-beowulf-list '(LABEL ARGS (COND
-
-
- 329 ;; ((COND ((ONEP (LENGTH ARGS)) ARGS)
-
-
- 330 ;; (T (ATTRIB (CAR ARGS)
-
-
- 331 ;; (APPLY CONC (CDR ARGS) NIL))))
-
-
- 332 ;; ARGS)))))
-
-
- 333 ;; (def args (make-beowulf-list '((1 2 3 4) (5 6 7 8) (9 10 11 12))))
-
-
- 334
-
-
- 335 ;; function
-
-
- 336 ;; (CADR function)
-
-
- 337 ;; (CADDR function)
-
-
- 338
-
-
- 339 (defn apply-label
-
-
- 340 "Apply in the special case that the first element in the function is `LABEL`."
-
-
- 341 [function args environment depth]
-
-
- 342 (EVAL
-
-
- 343 (CADDR function)
-
-
- 344 (CONS
-
-
- 345 (CONS (CADR function) args)
-
-
- 346 environment)
-
-
- 347 depth))
-
-
- 348
-
-
- 349 ;; (apply-label function args NIL 1)
-
-
- 350 ;; (APPLY function args NIL 1)
-
-
- 351
-
-
- 352 (defn- apply-lambda
-
-
- 353 "Apply in the special case that the first element in the function is `LAMBDA`."
-
-
- 354 [function args environment depth]
-
-
- 355 (EVAL
-
-
- 356 (CADDR function)
-
-
- 357 (PAIRLIS (CADR function) args environment) depth))
-
-
- 358
-
-
- 359 (defn APPLY
-
-
- 360 "Apply this `function` to these `arguments` in this `environment` and return
-
-
- 361 the result.
-
-
- 362
-
-
- 363 For bootstrapping, at least, a version of APPLY written in Clojure.
-
-
- 364 All args are assumed to be symbols or `beowulf.cons-cell/ConsCell` objects.
-
-
- 365 See page 13 of the Lisp 1.5 Programmers Manual."
-
-
- 366 ([function args environment]
-
-
- 367 (APPLY function args environment *depth*))
-
-
- 368 ([function args environment depth]
-
-
- 369 (binding [*depth* (inc depth)]
-
-
- 370 (trace-call 'APPLY (list function args environment) depth)
-
-
- 371 (let [result (cond
-
-
- 372 (= NIL function) (if (:strict *options*)
-
-
- 373 NIL
-
-
- 374 (throw (ex-info "NIL sí ne þegnung"
-
-
- 375 {:phase :apply
-
-
- 376 :function "NIL"
-
-
- 377 :args args
-
-
- 378 :type :beowulf})))
-
-
- 379 (= (ATOM function) T) (apply-symbolic function args environment (inc depth))
-
-
- 380 :else (case (first function)
-
-
- 381 LABEL (apply-label function args environment depth)
-
-
- 382 FUNARG (APPLY (CADR function) args (CADDR function) depth)
-
-
- 383 LAMBDA (apply-lambda function args environment depth)
-
-
- 384 ;; else
-
-
- 385 ;; OK, this is *not* what is says in the manual...
-
-
- 386 ;; COND (EVCON ???)
-
-
- 387 (throw (ex-info "Ungecnáwen wyrþan sí þegnung-weard"
+
+ 270 :else (ex-info "þegnung ne āfand"
- 388 {:phase :apply
+ 271 {:phase :apply
- 389 :function function
+ 272 :function function-symbol
- 390 :args args
+ 273 :args args
- 391 :type :beowulf}))))]
+ 274 :type :beowulf}))]
- 392 (trace-response 'APPLY result depth)
+ 275 (trace-response function-symbol result depth)
- 393 result))))
+ 276 result))
- 394
-
-
- 395 ;;;; EVAL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-
- 396
+ 277
- 397 (defn- EVCON
+ 278 (defn APPLY
- 398 "Inner guts of primitive COND. All `clauses` are assumed to be
+ 279 "Apply this `function` to these `arguments` in this `environment` and return
- 399 `beowulf.cons-cell/ConsCell` objects. Note that tests in Lisp 1.5
+ 280 the result.
- 400 often return `F`, not `NIL`, on failure. If no clause matches,
+ 281
- 401 then, strictly, we throw an error with code `:A3`.
-
-
- 402
+ 282 For bootstrapping, at least, a version of APPLY written in Clojure.
- 403 See pages 13 and 71 of the Lisp 1.5 Programmers Manual."
+ 283 All args are assumed to be symbols or `beowulf.cons-cell/ConsCell` objects.
- 404 [clauses env depth]
-
-
- 405 (loop [clauses' clauses]
-
-
- 406 (if-not (= clauses' NIL)
-
-
- 407 (let [test (EVAL (CAAR clauses') env depth)]
-
-
- 408 (if (not (#{NIL F} test))
+ 284 See page 13 of the Lisp 1.5 Programmers Manual."
- 409 ;; (and (not= test NIL) (not= test F))
-
-
- 410 (EVAL (CADAR clauses') env depth)
-
-
- 411 (recur (.getCdr clauses'))))
-
-
- 412 (if (:strict *options*)
-
-
- 413 (throw (ex-info "Ne ġefōg dǣl in COND"
-
-
- 414 {:phase :eval
-
-
- 415 :function 'COND
-
-
- 416 :args (list clauses)
-
-
- 417 :type :lisp
-
-
- 418 :code :A3}))
-
-
- 419 NIL))))
-
-
- 420
-
-
- 421 (defn- EVLIS
-
-
- 422 "Map `EVAL` across this list of `args` in the context of this
-
-
- 423 `env`ironment.All args are assumed to be `beowulf.cons-cell/ConsCell` objects.
-
-
- 424 See page 13 of the Lisp 1.5 Programmers Manual."
-
-
- 425 [args env depth]
-
-
- 426 (cond
-
-
- 427 (= NIL args) NIL
-
-
- 428 :else
-
-
- 429 (make-cons-cell
-
-
- 430 (EVAL (CAR args) env depth)
-
-
- 431 (EVLIS (CDR args) env depth))))
-
-
- 432
-
-
- 433 (defn- eval-symbolic
-
-
- 434 [expr env depth]
-
-
- 435 (let [v (ASSOC expr env)
-
-
- 436 indent (apply str (repeat depth "-"))]
-
-
- 437 (when (traced? 'EVAL)
-
-
- 438 (println (str indent ": EVAL: sceald bindele: " (or v "nil"))))
-
-
- 439 (if (instance? ConsCell v)
-
-
- 440 (.getCdr v)
-
-
- 441 (let [v' (value expr)]
-
-
- 442 (when (traced? 'EVAL)
-
-
- 443 (println (str indent ": EVAL: deóp bindele: (" expr " . " (or v' "nil") ")")))
-
-
- 444 (if v'
-
-
- 445 v'
-
-
- 446 (throw (ex-info (format "Ne tácen-bindele āfand: `%s`" expr)
-
-
- 447 {:phase :eval
-
-
- 448 :function 'EVAL
-
-
- 449 :args (list expr env depth)
-
-
- 450 :type :lisp
-
-
- 451 :code :A8})))))))
-
-
- 452
-
-
- 453 (defn EVAL
-
-
- 454 "Evaluate this `expr` and return the result. If `environment` is not passed,
-
-
- 455 it defaults to the current value of the global object list. The `depth`
-
-
- 456 argument is part of the tracing system and should not be set by user code.
-
-
- 457
-
-
- 458 All args are assumed to be numbers, symbols or `beowulf.cons-cell/ConsCell`
-
-
- 459 objects. However, if called with just a single arg, `expr`, I'll assume it's
-
-
- 460 being called from the Clojure REPL and will coerce the `expr` to `ConsCell`."
-
-
- 461 ([expr]
-
-
- 462 (let [expr' (if (and (coll? expr) (not (instance? ConsCell expr)))
-
-
- 463 (make-beowulf-list expr)
-
-
- 464 expr)]
-
-
- 465 (EVAL expr' NIL 0)))
-
-
- 466 ([expr env depth]
+ 285 [function args environment depth]
- 467 (trace-call 'EVAL (list expr env depth) depth)
-
-
- 468 (let [result (cond
+ 286 (trace-call 'APPLY (list function args environment) depth)
- 469 (= NIL expr) NIL ;; it was probably a mistake to make Lisp
-
-
- 470 ;; NIL distinct from Clojure nil
-
-
- 471 (= (NUMBERP expr) T) expr
-
-
- 472 (symbol? expr) (eval-symbolic expr env depth)
+ 287 (let [result (cond
- 473 (string? expr) (if (:strict *options*)
+ 288 (= NIL function) (if (:strict *options*)
- 474 (throw
+ 289 NIL
-
- 475 (ex-info
+
+ 290 (throw (ex-info "NIL sí ne þegnung"
-
- 476 (str "EVAL: strings not allowed in strict mode: \"" expr "\"")
-
-
- 477 {:phase :eval
+
+ 291 {:phase :apply
- 478 :detail :strict
+ 292 :function "NIL"
- 479 :expr expr}))
+ 293 :args args
-
- 480 (symbol expr))
+
+ 294 :type :beowulf})))
- 481 (= (ATOM (CAR expr)) T) (case (CAR expr)
-
-
- 482 COND (EVCON (CDR expr) env depth)
-
-
- 483 FUNCTION (LIST 'FUNARG (CADR expr))
-
-
- 484 PROG (PROG (CDR expr) env depth)
-
-
- 485 QUOTE (CADR expr)
-
-
- 486 ;; else
-
-
- 487 (APPLY
-
-
- 488 (CAR expr)
-
-
- 489 (EVLIS (CDR expr) env depth)
-
-
- 490 env
-
-
- 491 depth))
-
-
- 492 :else (EVAL (CONS (CDR (SASSOC (CAR expr) env (fn [] (ERROR 'A9))))
-
-
- 493 (CDR expr))
-
-
- 494 env
-
-
- 495 (inc depth)))]
+ 295 (= (ATOM function) T) (apply-symbolic function args environment (inc depth))
- 496 (trace-response 'EVAL result depth)
+ 296 :else (case (first function)
+
+
+ 297 LABEL (APPLY
+
+
+ 298 (CADDR function)
+
+
+ 299 args
+
+
+ 300 (make-cons-cell
+
+
+ 301 (make-cons-cell
+
+
+ 302 (CADR function)
+
+
+ 303 (CADDR function))
+
+
+ 304 environment)
+
+
+ 305 depth)
+
+
+ 306 FUNARG (APPLY (CADR function) args (CADDR function) depth)
+
+
+ 307 LAMBDA (EVAL
+
+
+ 308 (CADDR function)
+
+
+ 309 (PAIRLIS (CADR function) args environment) depth)
+
+
+ 310 (throw (ex-info "Ungecnáwen wyrþan sí þegnung-weard"
+
+
+ 311 {:phase :apply
+
+
+ 312 :function function
+
+
+ 313 :args args
+
+
+ 314 :type :beowulf}))))]
+
+
+ 315 (trace-response 'APPLY result depth)
- 497 result)))
+ 316 result))
- 498
+ 317
+
+
+ 318 ;;;; EVAL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+ 319
+
+
+ 320 (defn- EVCON
+
+
+ 321 "Inner guts of primitive COND. All `clauses` are assumed to be
+
+
+ 322 `beowulf.cons-cell/ConsCell` objects. Note that tests in Lisp 1.5
+
+
+ 323 often return `F`, not `NIL`, on failure. If no clause matches,
+
+
+ 324 then, strictly, we throw an error with code `:A3`.
+
+
+ 325
+
+
+ 326 See pages 13 and 71 of the Lisp 1.5 Programmers Manual."
+
+
+ 327 [clauses env depth]
+
+
+ 328 (loop [clauses' clauses]
+
+
+ 329 (if-not (= clauses' NIL)
+
+
+ 330 (let [test (EVAL (CAAR clauses') env depth)]
+
+
+ 331 (if (not (#{NIL F} test))
+
+
+ 332 ;; (and (not= test NIL) (not= test F))
+
+
+ 333 (EVAL (CADAR clauses') env depth)
+
+
+ 334 (recur (.getCdr clauses'))))
+
+
+ 335 (if (:strict *options*)
+
+
+ 336 (throw (ex-info "Ne ġefōg dǣl in COND"
+
+
+ 337 {:phase :eval
+
+
+ 338 :function 'COND
+
+
+ 339 :args (list clauses)
+
+
+ 340 :type :lisp
+
+
+ 341 :code :A3}))
+
+
+ 342 NIL))))
+
+
+ 343
+
+
+ 344 (defn- EVLIS
+
+
+ 345 "Map `EVAL` across this list of `args` in the context of this
+
+
+ 346 `env`ironment.All args are assumed to be `beowulf.cons-cell/ConsCell` objects.
+
+
+ 347 See page 13 of the Lisp 1.5 Programmers Manual."
+
+
+ 348 [args env depth]
+
+
+ 349 (cond
+
+
+ 350 (= NIL args) NIL
+
+
+ 351 :else
+
+
+ 352 (make-cons-cell
+
+
+ 353 (EVAL (CAR args) env depth)
+
+
+ 354 (EVLIS (CDR args) env depth))))
+
+
+ 355
+
+
+ 356 (defn- eval-symbolic
+
+
+ 357 [expr env depth]
+
+
+ 358 (let [v (ASSOC expr env)
+
+
+ 359 indent (apply str (repeat depth "-"))]
+
+
+ 360 (when (traced? 'EVAL)
+
+
+ 361 (println (str indent ": EVAL: sceald bindele: " (or v "nil"))))
+
+
+ 362 (if (instance? ConsCell v)
+
+
+ 363 (.getCdr v)
+
+
+ 364 (let [v' (value expr (list 'APVAL))]
+
+
+ 365 (when (traced? 'EVAL)
+
+
+ 366 (println (str indent ": EVAL: deóp bindele: (" expr " . " (or v' "nil") ")")))
+
+
+ 367 (if v'
+
+
+ 368 v'
+
+
+ 369 (throw (ex-info "Ne tácen-bindele āfand"
+
+
+ 370 {:phase :eval
+
+
+ 371 :function 'EVAL
+
+
+ 372 :args (list expr env depth)
+
+
+ 373 :type :lisp
+
+
+ 374 :code :A8})))))))
+
+
+ 375
+
+
+ 376 (defn EVAL
+
+
+ 377 "Evaluate this `expr` and return the result. If `environment` is not passed,
+
+
+ 378 it defaults to the current value of the global object list. The `depth`
+
+
+ 379 argument is part of the tracing system and should not be set by user code.
+
+
+ 380
+
+
+ 381 All args are assumed to be numbers, symbols or `beowulf.cons-cell/ConsCell`
+
+
+ 382 objects. However, if called with just a single arg, `expr`, I'll assume it's
+
+
+ 383 being called from the Clojure REPL and will coerce the `expr` to `ConsCell`."
+
+
+ 384 ([expr]
+
+
+ 385 (let [expr' (if (and (coll? expr) (not (instance? ConsCell expr)))
+
+
+ 386 (make-beowulf-list expr)
+
+
+ 387 expr)]
+
+
+ 388 (EVAL expr' NIL 0)))
+
+
+ 389 ([expr env depth]
+
+
+ 390 (trace-call 'EVAL (list expr env depth) depth)
+
+
+ 391 (let [result (cond
+
+
+ 392 (= NIL expr) NIL ;; it was probably a mistake to make Lisp
+
+
+ 393 ;; NIL distinct from Clojure nil
+
+
+ 394 (= (NUMBERP expr) T) expr
+
+
+ 395 (symbol? expr) (eval-symbolic expr env depth)
+
+
+ 396 (string? expr) (if (:strict *options*)
+
+
+ 397 (throw
+
+
+ 398 (ex-info
+
+
+ 399 (str "EVAL: strings not allowed in strict mode: \"" expr "\"")
+
+
+ 400 {:phase :eval
+
+
+ 401 :detail :strict
+
+
+ 402 :expr expr}))
+
+
+ 403 (symbol expr))
+
+
+ 404 (= (ATOM (CAR expr)) T) (case (CAR expr)
+
+
+ 405 COND (EVCON (CDR expr) env depth)
+
+
+ 406 FUNCTION (LIST 'FUNARG (CADR expr))
+
+
+ 407 PROG (PROG (CDR expr) env depth)
+
+
+ 408 QUOTE (CADR expr)
+
+
+ 409 ;; else
+
+
+ 410 (APPLY
+
+
+ 411 (CAR expr)
+
+
+ 412 (EVLIS (CDR expr) env depth)
+
+
+ 413 env
+
+
+ 414 depth))
+
+
+ 415 :else (APPLY
+
+
+ 416 (CAR expr)
+
+
+ 417 (EVLIS (CDR expr) env depth)
+
+
+ 418 env
+
+
+ 419 depth))]
+
+
+ 420 (trace-response 'EVAL result depth)
+
+
+ 421 result)))
+
+
+ 422