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. **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  
diff --git a/docs/cloverage/beowulf/cons_cell.clj.html b/docs/cloverage/beowulf/cons_cell.clj.html index aaf1230..a229691 100644 --- a/docs/cloverage/beowulf/cons_cell.clj.html +++ b/docs/cloverage/beowulf/cons_cell.clj.html @@ -259,7 +259,7 @@ 085      (if
- + 086       (or
@@ -445,22 +445,22 @@ 147    clojure.lang.Counted
- + 148    (count [this] (loop [cell this
149                         result 1]
- + 150                    (if
- + 151                     (and (coll? (.getCdr cell)) (not= NIL (.getCdr cell)))
- + 152                      (recur (.getCdr cell) (inc result))
- + 153                      result)))
diff --git a/docs/cloverage/beowulf/core.clj.html b/docs/cloverage/beowulf/core.clj.html index adf35d5..209aa59 100644 --- a/docs/cloverage/beowulf/core.clj.html +++ b/docs/cloverage/beowulf/core.clj.html @@ -154,10 +154,10 @@ 050      :default default-sysout
- + 051      :validate [#(and
- + 052                   (.exists (io/file %))
@@ -170,40 +170,40 @@ 055     ["-s" "--strict" "Strictly interpret the Lisp 1.5 language, without extensions."]
- 056     ["-t" "--time" "Time evaluations."] -
- - 057     ["-x" "--testing" "Disable the jline reader - useful when piping input."]]) + 056     ["-t" "--time" "Time evaluations."]])
- 058   + 057  
- 059  (defn- re  + 058  (defn- re 
- 060    "Like REPL, but it isn't a loop and doesn't print." + 059    "Like REPL, but it isn't a loop and doesn't print."
- 061    [input] + 060    [input]
- 062    (EVAL (READ input) NIL 0)) + 061    (EVAL (READ input) NIL 0))
- 063   + 062  
- 064  (defn repl + 063  (defn repl
- 065    "Read/eval/print loop." + 064    "Read/eval/print loop."
- 066    [prompt] + 065    [prompt]
- 067    (loop []  + 066    (loop [] +
+ + 067      (print prompt)
068      (flush) @@ -211,8 +211,8 @@ 069      (try
- - 070        (if-let [input (trim (read-from-console prompt))] + + 070        (if-let [input (trim (read-from-console))]
071          (if (= input stop-word) @@ -259,7 +259,7 @@ 085             data
- + 086              (case (:cause data)
@@ -316,13 +316,13 @@ 104          (str "Síðe " (System/getProperty "beowulf.version") "\n"))
- + 105        (when
106         (:help (:options args))
- + 107          (:summary args))
@@ -358,8 +358,8 @@ 118        (try
- - 119          (repl (:prompt (:options args))) + + 119          (repl (str (:prompt (:options args)) " "))
120          (catch diff --git a/docs/cloverage/beowulf/host.clj.html b/docs/cloverage/beowulf/host.clj.html index 69b2501..5a4bbed 100644 --- a/docs/cloverage/beowulf/host.clj.html +++ b/docs/cloverage/beowulf/host.clj.html @@ -130,19 +130,19 @@ 042    [symbol]
- + 043    (when (:strict *options*)
- + 044      (throw (ex-info (format "%s ne āfand innan Lisp 1.5" symbol)
- + 045                      {:type :strict
046                       :phase :host
- + 047                       :function symbol})))
@@ -274,7 +274,7 @@ 090    [l path]
- + 091    (cond
@@ -283,1547 +283,1439 @@ 093      (empty? path) l
- - 094      (not (instance? ConsCell l)) (throw (ex-info (str "Ne liste: " -
- - 095                                                        l "; " (type l)) -
- - 096                                                   {:phase :eval -
- 097                                                    :function "universal access function" + 094      :else
- - 098                                                    :args [l path] -
- - 099                                                    :type :beowulf})) + + 095      (try
- 100      :else (case (last path) + 096        (case (last path)
- 101              \a (uaf (.first l) (butlast path)) + 097          \a (uaf (.first l) (butlast path))
- 102              \d (uaf (.getCdr l) (butlast path)) + 098          \d (uaf (.getCdr l) (butlast path))
- - 103              (throw (ex-info (str "uaf: unexpected letter in path (only `a` and `d` permitted): " + + 099          (throw (ex-info (str "uaf: unexpected letter in path (only `a` and `d` permitted): " (last path))
- - 104                                   (last path)) -
- - 105                              {:phase :eval + + 100                          {:cause  :uaf
- 106                               :function "universal access function" + 101                           :detail :unexpected-letter
- - 107                               :args [l path] + + 102                           :expr   (last path)})))
- 108                               :type :beowulf}))))) + 103        (catch ClassCastException e +
+ + 104          (throw (ex-info +
+ + 105                  (str "uaf: Not a LISP list? " (type l)) +
+ + 106                  {:cause  :uaf +
+ + 107                   :detail :not-a-lisp-list +
+ + 108                   :expr   l} +
+ + 109                  e))))))
- 109   + 110  
- 110  (defmacro CAAR [x] `(uaf ~x '(\a \a))) + 111  (defmacro CAAR [x] `(uaf ~x '(\a \a)))
- 111  (defmacro CADR [x] `(uaf ~x '(\a \d))) + 112  (defmacro CADR [x] `(uaf ~x '(\a \d)))
- - 112  (defmacro CDDR [x] `(uaf ~x '(\d \d))) + + 113  (defmacro CDDR [x] `(uaf ~x '(\d \d)))
- 113  (defmacro CDAR [x] `(uaf ~x '(\d \a))) + 114  (defmacro CDAR [x] `(uaf ~x '(\d \a)))
- 114   + 115  
- 115  (defmacro CAAAR [x] `(uaf ~x '(\a \a \a))) + 116  (defmacro CAAAR [x] `(uaf ~x '(\a \a \a)))
- 116  (defmacro CAADR [x] `(uaf ~x '(\a \a \d))) + 117  (defmacro CAADR [x] `(uaf ~x '(\a \a \d)))
- 117  (defmacro CADAR [x] `(uaf ~x '(\a \d \a))) + 118  (defmacro CADAR [x] `(uaf ~x '(\a \d \a)))
- 118  (defmacro CADDR [x] `(uaf ~x '(\a \d \d))) + 119  (defmacro CADDR [x] `(uaf ~x '(\a \d \d)))
- 119  (defmacro CDDAR [x] `(uaf ~x '(\d \d \a))) + 120  (defmacro CDDAR [x] `(uaf ~x '(\d \d \a)))
- 120  (defmacro CDDDR [x] `(uaf ~x '(\d \d \d))) + 121  (defmacro CDDDR [x] `(uaf ~x '(\d \d \d)))
- 121  (defmacro CDAAR [x] `(uaf ~x '(\d \a \a))) + 122  (defmacro CDAAR [x] `(uaf ~x '(\d \a \a)))
- 122  (defmacro CDADR [x] `(uaf ~x '(\d \a \d))) + 123  (defmacro CDADR [x] `(uaf ~x '(\d \a \d)))
- 123   + 124  
- 124  (defmacro CAAAAR [x] `(uaf ~x '(\a \a \a \a))) + 125  (defmacro CAAAAR [x] `(uaf ~x '(\a \a \a \a)))
- 125  (defmacro CAADAR [x] `(uaf ~x '(\a \a \d \a))) + 126  (defmacro CAADAR [x] `(uaf ~x '(\a \a \d \a)))
- 126  (defmacro CADAAR [x] `(uaf ~x '(\a \d \a \a))) + 127  (defmacro CADAAR [x] `(uaf ~x '(\a \d \a \a)))
- 127  (defmacro CADDAR [x] `(uaf ~x '(\a \d \d \a))) + 128  (defmacro CADDAR [x] `(uaf ~x '(\a \d \d \a)))
- 128  (defmacro CDDAAR [x] `(uaf ~x '(\d \d \a \a))) + 129  (defmacro CDDAAR [x] `(uaf ~x '(\d \d \a \a)))
- 129  (defmacro CDDDAR [x] `(uaf ~x '(\d \d \d \a))) + 130  (defmacro CDDDAR [x] `(uaf ~x '(\d \d \d \a)))
- 130  (defmacro CDAAAR [x] `(uaf ~x '(\d \a \a \a))) + 131  (defmacro CDAAAR [x] `(uaf ~x '(\d \a \a \a)))
- 131  (defmacro CDADAR [x] `(uaf ~x '(\d \a \d \a))) + 132  (defmacro CDADAR [x] `(uaf ~x '(\d \a \d \a)))
- 132  (defmacro CAAADR [x] `(uaf ~x '(\a \a \a \d))) + 133  (defmacro CAAADR [x] `(uaf ~x '(\a \a \a \d)))
- 133  (defmacro CAADDR [x] `(uaf ~x '(\a \a \d \d))) + 134  (defmacro CAADDR [x] `(uaf ~x '(\a \a \d \d)))
- 134  (defmacro CADADR [x] `(uaf ~x '(\a \d \a \d))) + 135  (defmacro CADADR [x] `(uaf ~x '(\a \d \a \d)))
- 135  (defmacro CADDDR [x] `(uaf ~x '(\a \d \d \d))) + 136  (defmacro CADDDR [x] `(uaf ~x '(\a \d \d \d)))
- 136  (defmacro CDDADR [x] `(uaf ~x '(\d \d \a \d))) + 137  (defmacro CDDADR [x] `(uaf ~x '(\d \d \a \d)))
- 137  (defmacro CDDDDR [x] `(uaf ~x '(\d \d \d \d))) + 138  (defmacro CDDDDR [x] `(uaf ~x '(\d \d \d \d)))
- 138  (defmacro CDAADR [x] `(uaf ~x '(\d \a \a \d))) + 139  (defmacro CDAADR [x] `(uaf ~x '(\d \a \a \d)))
- 139  (defmacro CDADDR [x] `(uaf ~x '(\d \a \d \d))) + 140  (defmacro CDADDR [x] `(uaf ~x '(\d \a \d \d)))
- 140   + 141  
- 141  (defn RPLACA + 142  (defn RPLACA
- 142    "Replace the CAR pointer of this `cell` with this `value`. Dangerous, should + 143    "Replace the CAR pointer of this `cell` with this `value`. Dangerous, should
- 143    really not exist, but does in Lisp 1.5 (and was important for some + 144    really not exist, but does in Lisp 1.5 (and was important for some
- 144    performance hacks in early Lisps)" + 145    performance hacks in early Lisps)"
- 145    [^ConsCell cell value] + 146    [^ConsCell cell value]
- 146    (if + 147    (if
- 147     (instance? ConsCell cell) + 148     (instance? ConsCell cell)
- 148      (if + 149      (if
- 149       (or + 150       (or
- 150        (instance? ConsCell value) + 151        (instance? ConsCell value)
- 151        (number? value) + 152        (number? value)
- 152        (symbol? value) + 153        (symbol? value)
- 153        (= value NIL)) + 154        (= value NIL))
- 154        (try + 155        (try
- 155          (.rplaca cell value) + 156          (.rplaca cell value)
- 156          cell + 157          cell
- 157          (catch Throwable any + 158          (catch Throwable any
- 158            (throw (ex-info + 159            (throw (ex-info
- 159                    (str (.getMessage any) " in RPLACA: `") + 160                    (str (.getMessage any) " in RPLACA: `")
- 160                    {:cause :upstream-error + 161                    {:cause :upstream-error
- 161                     :phase :host + 162                     :phase :host
- 162                     :function :rplaca + 163                     :function :rplaca
- 163                     :args (list cell value) + 164                     :args (list cell value)
- 164                     :type :beowulf} + 165                     :type :beowulf}
- 165                    any)))) + 166                    any))))
- 166        (throw (ex-info + 167        (throw (ex-info
- 167                (str "Un-ġefōg þing in RPLACA: `" value "` (" (type value) ")") + 168                (str "Un-ġefōg þing in RPLACA: `" value "` (" (type value) ")")
- 168                {:cause :bad-value + 169                {:cause :bad-value
- 169                 :phase :host + 170                 :phase :host
- 170                 :function :rplaca + 171                 :function :rplaca
- 171                 :args (list cell value) + 172                 :args (list cell value)
- 172                 :type :beowulf}))) + 173                 :type :beowulf})))
- 173      (throw (ex-info + 174      (throw (ex-info
- 174              (str "Uncynlic miercels in RPLACA: `" cell "` (" (type cell) ")") + 175              (str "Uncynlic miercels in RPLACA: `" cell "` (" (type cell) ")")
- 175              {:cause :bad-cell + 176              {:cause :bad-cell
- 176               :phase :host + 177               :phase :host
- 177               :function :rplaca + 178               :function :rplaca
- 178               :args (list cell value) + 179               :args (list cell value)
- 179               :type :beowulf})))) + 180               :type :beowulf}))))
- 180   + 181  
- 181  (defn RPLACD + 182  (defn RPLACD
- 182    "Replace the CDR pointer of this `cell` with this `value`. Dangerous, should + 183    "Replace the CDR pointer of this `cell` with this `value`. Dangerous, should
- 183    really not exist, but does in Lisp 1.5 (and was important for some + 184    really not exist, but does in Lisp 1.5 (and was important for some
- 184    performance hacks in early Lisps)" + 185    performance hacks in early Lisps)"
- 185    [^ConsCell cell value] + 186    [^ConsCell cell value]
- 186    (if + 187    (if
- 187     (instance? ConsCell cell) + 188     (instance? ConsCell cell)
- 188      (if + 189      (if
- - 189       (or + + 190       (or
- 190        (instance? ConsCell value) + 191        (instance? ConsCell value)
- 191        (number? value) + 192        (number? value)
- 192        (symbol? value) + 193        (symbol? value)
- - 193        (= value NIL)) + + 194        (= value NIL))
- 194        (try + 195        (try
- 195          (.rplacd cell value) + 196          (.rplacd cell value)
- 196          cell + 197          cell
- 197          (catch Throwable any + 198          (catch Throwable any
- 198            (throw (ex-info + 199            (throw (ex-info
- 199                    (str (.getMessage any) " in RPLACD: `") + 200                    (str (.getMessage any) " in RPLACD: `")
- 200                    {:cause :upstream-error + 201                    {:cause :upstream-error
- 201                     :phase :host + 202                     :phase :host
- 202                     :function :rplacd + 203                     :function :rplacd
- 203                     :args (list cell value) + 204                     :args (list cell value)
- 204                     :type :beowulf} + 205                     :type :beowulf}
- 205                    any)))) + 206                    any)))) +
+ + 207        (throw (ex-info +
+ + 208                (str "Un-ġefōg þing in RPLACD: `" value "` (" (type value) ")") +
+ + 209                {:cause :bad-value +
+ + 210                 :phase :host +
+ + 211                 :function :rplacd +
+ + 212                 :args (list cell value) +
+ + 213                 :type :beowulf}))) +
+ + 214      (throw (ex-info +
+ + 215              (str "Uncynlic miercels in RPLACD: `" cell "` (" (type cell) ")") +
+ + 216              {:cause :bad-cell +
+ + 217               :phase :host +
+ + 218               :detail :rplacd +
+ + 219               :args (list cell value) +
+ + 220               :type :beowulf}))));; PLUS +
+ + 221   +
+ + 222  (defn LIST +
+ + 223    [& args]
- 206        (throw (ex-info -
- - 207                (str "Un-ġefōg þing in RPLACD: `" value "` (" (type value) ")") -
- - 208                {:cause :bad-value -
- - 209                 :phase :host -
- - 210                 :function :rplacd -
- - 211                 :args (list cell value) -
- - 212                 :type :beowulf}))) -
- - 213      (throw (ex-info -
- - 214              (str "Uncynlic miercels in RPLACD: `" cell "` (" (type cell) ")") -
- - 215              {:cause :bad-cell -
- - 216               :phase :host -
- - 217               :detail :rplacd -
- - 218               :args (list cell value) -
- - 219               :type :beowulf})))) + 224    (make-beowulf-list args))
- 220   + 225   +
+ + 226  ;;;; Basic predicates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +
+ + 227   +
+ + 228  (defmacro NULL +
+ + 229    "Returns `T` if and only if the argument `x` is bound to `NIL`; else `F`." +
+ + 230    [x] +
+ + 231    `(if (= ~x NIL) T F)) +
+ + 232   +
+ + 233  (defmacro NILP +
+ + 234    "Not part of LISP 1.5: `T` if `o` is `NIL`, else `NIL`." +
+ + 235    [x] +
+ + 236    `(if (= ~x NIL) T NIL)) +
+ + 237  
- 221  (defn LIST + 238  (defn ATOM
- 222    [& args] -
- - 223    (make-beowulf-list args)) -
- - 224   + 239    "Returns `T` if and only if the argument `x` is bound to an atom; else `F`.
- 225  ;;;; Basic predicates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -
- - 226   -
- - 227  (defmacro NULL + 240    It is not clear to me from the documentation whether `(ATOM 7)` should return
- 228    "Returns `T` if and only if the argument `x` is bound to `NIL`; else `F`." + 241    `T` or `F`. I'm going to assume `T`."
- 229    [x] -
- - 230    `(if (= ~x NIL) T F)) -
- - 231   -
- - 232  (defmacro NILP -
- - 233    "Not part of LISP 1.5: `T` if `o` is `NIL`, else `NIL`." -
- - 234    [x] -
- - 235    `(if (= ~x NIL) T NIL)) -
- - 236   -
- - 237  (defn ATOM -
- - 238    "Returns `T` if and only if the argument `x` is bound to an atom; else `F`. -
- - 239    It is not clear to me from the documentation whether `(ATOM 7)` should return -
- - 240    `T` or `F`. I'm going to assume `T`." -
- - 241    [x] + 242    [x]
- 242    (if (or (symbol? x) (number? x)) T F)) + 243    (if (or (symbol? x) (number? x)) T F))
- 243   + 244  
- 244  (defmacro ATOM? + 245  (defmacro ATOM?
- 245    "The convention of returning `F` from predicates, rather than `NIL`, is going + 246    "The convention of returning `F` from predicates, rather than `NIL`, is going
- 246    to tie me in knots. This is a variant of `ATOM` which returns `NIL` + 247    to tie me in knots. This is a variant of `ATOM` which returns `NIL`
- 247    on failure." + 248    on failure."
- 248    [x] + 249    [x]
- 249    `(if (or (symbol? ~x) (number? ~x)) T NIL)) + 250    `(if (or (symbol? ~x) (number? ~x)) T NIL))
- 250   + 251  
- 251  (defn EQ + 252  (defn EQ
- 252    "Returns `T` if and only if both `x` and `y` are bound to the same atom, + 253    "Returns `T` if and only if both `x` and `y` are bound to the same atom,
- 253    else `NIL`." + 254    else `NIL`."
- 254    [x y] + 255    [x y]
- 255    (cond (and (instance? ConsCell x) + 256    (cond (and (instance? ConsCell x)
- 256               (.equals x y)) T + 257               (.equals x y)) T
- 257          (and (= (ATOM x) T) (= x y)) T + 258          (and (= (ATOM x) T) (= x y)) T
- 258          :else NIL)) + 259          :else NIL))
- 259   + 260  
- 260  (defn EQUAL + 261  (defn EQUAL
- 261    "This is a predicate that is true if its two arguments are identical + 262    "This is a predicate that is true if its two arguments are identical
- 262    S-expressions, and false if they are different. (The elementary predicate + 263    S-expressions, and false if they are different. (The elementary predicate
- 263    `EQ` is defined only for atomic arguments.) The definition of `EQUAL` is + 264    `EQ` is defined only for atomic arguments.) The definition of `EQUAL` is
- 264    an example of a conditional expression inside a conditional expression. + 265    an example of a conditional expression inside a conditional expression.
- 265   + 266  
- 266    NOTE: returns `F` on failure, not `NIL`" + 267    NOTE: returns `F` on failure, not `NIL`"
- 267    [x y] + 268    [x y]
- 268    (cond + 269    (cond
- 269      (= (ATOM x) T) (if (= x y) T F) + 270      (= (ATOM x) T) (if (= x y) T F)
- 270      (= (EQUAL (CAR x) (CAR y)) T) (EQUAL (CDR x) (CDR y)) + 271      (= (EQUAL (CAR x) (CAR y)) T) (EQUAL (CDR x) (CDR y))
- 271      :else F)) + 272      :else F))
- 272   + 273  
- 273  (defn AND + 274  (defn AND
- 274    "`T` if and only if none of my `args` evaluate to either `F` or `NIL`, + 275    "`T` if and only if none of my `args` evaluate to either `F` or `NIL`,
- 275     else `F`. + 276     else `F`.
- 276      + 277     
- 277     In `beowulf.host` principally because I don't yet feel confident to define + 278     In `beowulf.host` principally because I don't yet feel confident to define
- 278     varargs functions in Lisp." + 279     varargs functions in Lisp."
- 279    [& args] + 280    [& args]
- 280    ;; (println "AND: " args " type: " (type args) " seq? " (seq? args)) + 281    ;; (println "AND: " args " type: " (type args) " seq? " (seq? args))
- 281    ;; (println "  filtered: " (seq (filter #{F NIL} args))) + 282    ;; (println "  filtered: " (seq (filter #{F NIL} args)))
- - 282    (cond (= NIL args) T + + 283    (cond (= NIL args) T
- 283          (seq? args) (if (seq (filter #{F NIL} args)) F T) + 284          (seq? args) (if (seq (filter #{F NIL} args)) F T)
- - 284          :else T)) -
- - 285   + + 285          :else T))
286  
- - 287  (defn OR -
- - 288    "`T` if and only if at least one of my `args` evaluates to something other -
- - 289    than either `F` or `NIL`, else `F`. -
- - 290      -
- - 291     In `beowulf.host` principally because I don't yet feel confident to define -
- - 292     varargs functions in Lisp." -
- - 293    [& args] -
- - 294    ;; (println "OR: " args " type: " (type args) " seq? " (seq? args)) -
- - 295    ;; (println "  filtered: " (seq (remove #{F NIL} args))) -
- - 296    (cond (= NIL args) F -
- - 297          (seq? args) (if (seq (remove #{F NIL} args)) T F) -
- - 298          :else F)) -
- 299   + 287   +
+ + 288  (defn OR +
+ + 289    "`T` if and only if at least one of my `args` evaluates to something other +
+ + 290    than either `F` or `NIL`, else `F`. +
+ + 291      +
+ + 292     In `beowulf.host` principally because I don't yet feel confident to define +
+ + 293     varargs functions in Lisp." +
+ + 294    [& args] +
+ + 295    ;; (println "OR: " args " type: " (type args) " seq? " (seq? args)) +
+ + 296    ;; (println "  filtered: " (seq (remove #{F NIL} args))) +
+ + 297    (cond (= NIL args) F +
+ + 298          (seq? args) (if (seq (remove #{F NIL} args)) T F) +
+ + 299          :else F))
300  
- - 301  ;;;; Operations on lists ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + 301  
- 302  ;; + 302  ;;;; Operations on lists ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- 303  ;; TODO: These are candidates for moving to Lisp urgently! + 303  ;; +
+ + 304  ;; TODO: These are candidates for moving to Lisp urgently!
- 304   + 305  
- 305  (defn ASSOC + 306  (defn ASSOC
- 306    "If a is an association list such as the one formed by PAIRLIS in the above + 307    "If a is an association list such as the one formed by PAIRLIS in the above
- 307    example, then assoc will produce the first pair whose first term is x. Thus + 308    example, then assoc will produce the first pair whose first term is x. Thus
- 308    it is a table searching function. + 309    it is a table searching function.
- 309   + 310  
- 310    All args are assumed to be `beowulf.cons-cell/ConsCell` objects. + 311    All args are assumed to be `beowulf.cons-cell/ConsCell` objects.
- 311    See page 12 of the Lisp 1.5 Programmers Manual. + 312    See page 12 of the Lisp 1.5 Programmers Manual.
- 312      + 313     
- 313     **NOTE THAT** this function is overridden by an implementation in Lisp, + 314     **NOTE THAT** this function is overridden by an implementation in Lisp,
- 314     but is currently still present for bootstrapping." + 315     but is currently still present for bootstrapping."
- 315    [x a] + 316    [x a]
- 316    (cond + 317    (cond
- 317      (= NIL a) NIL ;; this clause is not present in the original but is added for + 318      (= NIL a) NIL ;; this clause is not present in the original but is added for
- 318      ;; robustness. + 319      ;; robustness.
- 319      (= (EQUAL (CAAR a) x) T) (CAR a) + 320      (= (EQUAL (CAAR a) x) T) (CAR a)
- 320      :else + 321      :else
- 321      (ASSOC x (CDR a)))) + 322      (ASSOC x (CDR a))))
- 322   + 323  
- 323  (defn PAIRLIS + 324  (defn PAIRLIS
- 324    "This function gives the list of pairs of corresponding elements of the + 325    "This function gives the list of pairs of corresponding elements of the
- 325    lists `x` and `y`, and APPENDs this to the list `a`. The resultant list + 326    lists `x` and `y`, and APPENDs this to the list `a`. The resultant list
- 326    of pairs, which is like a table with two columns, is called an + 327    of pairs, which is like a table with two columns, is called an
- 327    association list. + 328    association list.
- 328   + 329  
- 329    Eessentially, it builds the environment on the stack, implementing shallow + 330    Eessentially, it builds the environment on the stack, implementing shallow
- 330    binding. + 331    binding.
- 331   + 332  
- 332    All args are assumed to be `beowulf.cons-cell/ConsCell` objects. + 333    All args are assumed to be `beowulf.cons-cell/ConsCell` objects.
- 333    See page 12 of the Lisp 1.5 Programmers Manual. + 334    See page 12 of the Lisp 1.5 Programmers Manual.
- 334      + 335     
- 335     **NOTE THAT** this function is overridden by an implementation in Lisp, + 336     **NOTE THAT** this function is overridden by an implementation in Lisp,
- 336     but is currently still present for bootstrapping." + 337     but is currently still present for bootstrapping."
- 337    [x y a] + 338    [x y a]
- 338    (cond + 339    (cond
- 339      ;; the original tests only x; testing y as well will be a little more + 340      ;; the original tests only x; testing y as well will be a little more
- 340      ;; robust if `x` and `y` are not the same length. + 341      ;; robust if `x` and `y` are not the same length.
- 341      (or (= NIL x) (= NIL y)) a + 342      (or (= NIL x) (= NIL y)) a
- 342      :else (make-cons-cell + 343      :else (make-cons-cell
- 343             (make-cons-cell (CAR x) (CAR y)) + 344             (make-cons-cell (CAR x) (CAR y))
- 344             (PAIRLIS (CDR x) (CDR y) a)))) + 345             (PAIRLIS (CDR x) (CDR y) a))))
- 345   + 346  
- 346  ;;;; Arithmetic ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + 347  ;;;; Arithmetic ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- 347  ;; + 348  ;;
- 348  ;; TODO: When in strict mode, should we limit arithmetic precision to that + 349  ;; TODO: When in strict mode, should we limit arithmetic precision to that
- 349  ;; supported by Lisp 1.5? + 350  ;; supported by Lisp 1.5?
- 350   + 351  
- 351  (defn PLUS + 352  (defn PLUS
- 352    [& args] + 353    [& args]
- 353    (let [s (apply + args)] + 354    (let [s (apply + args)]
- 354      (if (integer? s) s (float s)))) + 355      (if (integer? s) s (float s))))
- 355   + 356  
- 356  (defn TIMES + 357  (defn TIMES
- 357    [& args] + 358    [& args]
- 358    (let [p (apply * args)] + 359    (let [p (apply * args)]
- - 359      (if (integer? p) p (float p)))) + + 360      (if (integer? p) p (float p))))
- 360   + 361  
- 361  (defn DIFFERENCE + 362  (defn DIFFERENCE
- 362    [x y] + 363    [x y]
- 363    (let [d (- x y)] + 364    (let [d (- x y)]
- - 364      (if (integer? d) d (float d)))) + + 365      (if (integer? d) d (float d))))
- 365   + 366  
- 366  (defn QUOTIENT + 367  (defn QUOTIENT
- 367    "I'm not certain from the documentation whether Lisp 1.5 `QUOTIENT` returned + 368    "I'm not certain from the documentation whether Lisp 1.5 `QUOTIENT` returned
- 368    the integer part of the quotient, or a realnum representing the whole + 369    the integer part of the quotient, or a realnum representing the whole
- 369    quotient. I am for now implementing the latter." + 370    quotient. I am for now implementing the latter."
- 370    [x y] + 371    [x y]
- 371    (let [q (/ x y)] + 372    (let [q (/ x y)]
- 372      (if (integer? q) q (float q)))) + 373      (if (integer? q) q (float q))))
- 373   + 374  
- 374  (defn REMAINDER + 375  (defn REMAINDER
- 375    [x y] + 376    [x y]
- 376    (rem x y)) + 377    (rem x y))
- 377   + 378  
- 378  (defn ADD1 + 379  (defn ADD1
- 379    [x] + 380    [x]
- 380    (inc x)) + 381    (inc x))
- 381   + 382  
- 382  (defn SUB1 + 383  (defn SUB1
- 383    [x] -
- - 384    (dec x)) -
- - 385   -
- - 386  (defn FIXP -
- - 387    [x] -
- - 388    (if (integer? x) T F)) -
- - 389   -
- - 390  (defn NUMBERP -
- - 391    [x] -
- - 392    (if (number? x) T F)) -
- - 393   -
- - 394  (defn LESSP -
- - 395    [x y] -
- - 396    (if (< x y) T F)) -
- - 397   -
- - 398  (defn GREATERP -
- - 399    [x y] -
- - 400    (if (> x y) T F)) -
- - 401   -
- - 402  ;;;; Miscellaneous ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -
- - 403   -
- - 404  (defn GENSYM -
- - 405    "Generate a unique symbol." -
- - 406    [] -
- - 407    (symbol (upper-case (str (gensym "SYM"))))) -
- - 408   -
- - 409  (defn ERROR -
- - 410    "Throw an error" -
- - 411    [& args] -
- - 412    (throw (ex-info "LISP STÆFLEAHTER" {:args args -
- - 413                                        :phase :eval -
- - 414                                        :function 'ERROR -
- - 415                                        :type :lisp -
- - 416                                        :code (or (first args) 'A1)}))) -
- - 417   -
- - 418  ;;;; Assignment and the object list ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -
- - 419   -
- - 420  (defn OBLIST -
- - 421    "Return a list of the symbols currently bound on the object list. -
- - 422      -
- - 423     **NOTE THAT** in the Lisp 1.5 manual, footnote at the bottom of page 69, it implies  -
- - 424     that an argument can be passed but I'm not sure of the semantics of -
- - 425     this." -
- - 426    [] -
- - 427    (if (instance? ConsCell @oblist) -
- - 428      (make-beowulf-list (map CAR @oblist)) -
- - 429      NIL)) -
- - 430   -
- - 431  (def magic-marker -
- - 432    "The unexplained magic number which marks the start of a property list." -
- - 433    (Integer/parseInt "77777" 8)) -
- - 434   -
- - 435  (defn hit-or-miss-assoc -
- - 436    "Find the position of the binding of this `target` in a Lisp 1.5  -
- - 437     property list `plist`. -
- - 438      -
- - 439     Lisp 1.5 property lists are not assoc lists, but lists of the form -
- - 440     `(name value name value name value...)`. It's therefore necessary to -
- - 441     recurse down the list two entries at a time to avoid confusing names -
- - 442     with values." -
- - 443    [target plist] -
- - 444    (if (and (instance? ConsCell plist) (even? (count plist))) -
- - 445      (cond (= plist NIL) NIL -
- - 446            (= (first plist) target) plist -
- - 447            :else (hit-or-miss-assoc target (CDDR plist))) -
- - 448      NIL)) -
- - 449   -
- - 450  (defn ATTRIB -
- - 451    "Destructive append. From page 59 of the manual: -
- - 452      -
- - 453     The function `attrib` concatenates its two arguments by changing the last  -
- - 454     element of its first argument to point to the second argument. Thus it -
- - 455     is commonly used to tack something onto the end of a property list.  -
- - 456     The value of `attrib` is the second argument. -
- - 457   -
- - 458     For example -
- - 459     ``` -
- - 460     attrib[FF; (EXPR (LAMBDA (X) (COND ((ATOM X) X) (T (FF (CAR x))))))] -
- - 461     ``` -
- - 462     would put EXPR followed by the LAMBDA expression for FF onto the end of  -
- - 463     the property list for FF." -
- - 464    [x e] -
- - 465    (loop [l x] -
- - 466      (cond -
- - 467        (instance? ConsCell (CDR l)) (recur (CDR l)) -
- - 468        :else (when (RPLACD l e) e)))) -
- - 469   -
- - 470  (defn PUT -
- - 471    "Put this `value` as the value of the property indicated by this `indicator`  -
- - 472     of this `symbol`. Return `value` on success. -
- - 473      -
- - 474     NOTE THAT there is no `PUT` defined in the manual, but it would have been  -
- - 475     easy to have defined it so I don't think this fully counts as an extension." -
- - 476    [symbol indicator value] -
- - 477    (let [binding (ASSOC symbol @oblist)] -
- - 478      (if (instance? ConsCell binding) -
- - 479        (let [prop (hit-or-miss-assoc indicator (CDDR binding))] -
- - 480          (if (instance? ConsCell prop) -
- - 481            (RPLACA (CDR prop) value) -
- - 482            ;; The implication is ATTRIB was used here, but I have not made that -
- - 483            ;; work and this does work, so if it ain't broke don't fix it. -
- - 484            (RPLACD binding -
- - 485                    (make-cons-cell -
- - 486                     magic-marker -
- - 487                     (make-cons-cell -
- - 488                      indicator -
- - 489                      (make-cons-cell value (CDDR binding))))))) -
- - 490        (swap! -
- - 491         oblist -
- - 492         (fn [ob s p v] -
- - 493           (make-cons-cell -
- - 494            (make-beowulf-list (list s magic-marker p v)) -
- - 495            ob)) -
- - 496         symbol indicator value))) -
- - 497    value) -
- - 498   -
- - 499  (defn GET -
- - 500    "From the manual: -
- - 501      -
- - 502     '`get` is somewhat like `prop`; however its value is car of the rest of -
- - 503     the list if the `indicator` is found, and NIL otherwise.' -
- - 504      -
- - 505     It's clear that `GET` is expected to be defined in terms of `PROP`, but -
- - 506     we can't implement `PROP` here because we lack `EVAL`; and we can't have -
- - 507     `EVAL` here because both it and `APPLY` depends on `GET`. -
- - 508      -
- - 509     OK, It's worse than that: the statement of the definition of `GET` (and  -
- - 510     of) `PROP` on page 59 says that the first argument to each must be a list; -
- - 511     But the in the definition of `ASSOC` on page 70, when `GET` is called its -
- - 512     first argument is always an atom. Since it's `ASSOC` and `EVAL` which I  -
- - 513     need to make work, I'm going to assume that page 59 is wrong." -
- - 514    [symbol indicator] -
- - 515    (let [binding (ASSOC symbol @oblist) -
- - 516          val (cond -
- - 517                (= binding NIL) NIL -
- - 518                (= magic-marker -
- - 519                   (CADR binding)) (let [p (hit-or-miss-assoc indicator binding)] -
- - 520                                     (if-not (= NIL p) -
- - 521                                       (CADR p) -
- - 522                                       NIL)) -
- - 523                :else (throw -
- - 524                       (ex-info "Misformatted property list (missing magic marker)" -
- - 525                                {:phase :host -
- - 526                                 :function :get -
- - 527                                 :args (list symbol indicator) -
- - 528                                 :type :beowulf})))] -
- - 529      ;; (println "<< GET returning: " val) -
- - 530      val)) -
- - 531   -
- - 532  (defn DEFLIST -
- - 533    "For each pair in this association list `a-list`, set the property with this -
- - 534     `indicator` of the symbol which is the first element of the pair to the  -
- - 535     value which is the second element of the pair. See page 58 of the manual." -
- - 536    [a-list indicator] + 384    [x]
- 537    (doall + 385    (dec x))
- - 538     (map + + 386   +
+ + 387  (defn FIXP +
+ + 388    [x] +
+ + 389    (if (integer? x) T F)) +
+ + 390   +
+ + 391  (defn NUMBERP +
+ + 392    [x] +
+ + 393    (if (number? x) T F)) +
+ + 394   +
+ + 395  (defn LESSP +
+ + 396    [x y] +
+ + 397    (if (< x y) T F)) +
+ + 398   +
+ + 399  (defn GREATERP +
+ + 400    [x y] +
+ + 401    (if (> x y) T F)) +
+ + 402   +
+ + 403  ;;;; Miscellaneous ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +
+ + 404   +
+ + 405  (defn GENSYM +
+ + 406    "Generate a unique symbol." +
+ + 407    [] +
+ + 408    (symbol (upper-case (str (gensym "SYM"))))) +
+ + 409   +
+ + 410  (defn ERROR +
+ + 411    "Throw an error" +
+ + 412    [& args]
- 539      #(when (PUT (CAR %) indicator (CDR %)) (CAR %)) + 413    (throw (ex-info "LISP STÆFLEAHTER" {:args args +
+ + 414                                        :phase :eval +
+ + 415                                        :function 'ERROR +
+ + 416                                        :type :lisp +
+ + 417                                        :code (or (first args) 'A1)}))) +
+ + 418   +
+ + 419  ;;;; Assignment and the object list ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +
+ + 420   +
+ + 421  (defn OBLIST +
+ + 422    "Return a list of the symbols currently bound on the object list. +
+ + 423      +
+ + 424     **NOTE THAT** in the Lisp 1.5 manual, footnote at the bottom of page 69, it implies  +
+ + 425     that an argument can be passed but I'm not sure of the semantics of +
+ + 426     this." +
+ + 427    [] +
+ + 428    (if (instance? ConsCell @oblist) +
+ + 429      (make-beowulf-list (map CAR @oblist))
- 540      a-list))) + 430      NIL))
- 541   + 431  
- 542  (defn DEFINE + 432  (def magic-marker
- 543    "Bootstrap-only version of `DEFINE` which, post boostrap, can be overwritten  -
- - 544    in LISP.  -
- - 545   -
- - 546    The single argument to `DEFINE` should be an association list of symbols to -
- - 547     lambda functions. See page 58 of the manual." -
- - 548    [a-list] -
- - 549    (DEFLIST a-list 'EXPR)) -
- - 550   -
- - 551  (defn SET -
- - 552    "Implementation of SET in Clojure. Add to the `oblist` a binding of the -
- - 553     value of `var` to the value of `val`. NOTE WELL: this is not SETQ!" -
- - 554    [symbol val] -
- - 555    (PUT symbol 'APVAL val)) -
- - 556   -
- - 557  ;;;; TRACE and friends ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -
- - 558   -
- - 559  (def traced-symbols -
- - 560    "Symbols currently being traced." + 433    "The unexplained magic number which marks the start of a property list."
- 561    (atom #{})) + 434    (Integer/parseInt "77777" 8)) +
+ + 435   +
+ + 436  (defn PUT +
+ + 437    "Put this `value` as the value of the property indicated by this `indicator`  +
+ + 438     of this `symbol`. Return `value` on success. +
+ + 439      +
+ + 440     NOTE THAT there is no `PUT` defined in the manual, but it would have been  +
+ + 441     easy to have defined it so I don't think this fully counts as an extension." +
+ + 442    [symbol indicator value] +
+ + 443    (if-let [binding (ASSOC symbol @oblist)] +
+ + 444      (if-let [prop (ASSOC indicator (CDDR binding))] +
+ + 445        (RPLACD prop value) +
+ + 446        (RPLACD binding +
+ + 447                (make-cons-cell +
+ + 448                 magic-marker +
+ + 449                 (make-cons-cell +
+ + 450                  indicator +
+ + 451                  (make-cons-cell value (CDDR binding)))))) +
+ + 452      (swap! +
+ + 453       oblist +
+ + 454       (fn [ob s p v] +
+ + 455         (make-cons-cell +
+ + 456          (make-beowulf-list (list s magic-marker p v)) +
+ + 457          ob)) +
+ + 458       symbol indicator value))) +
+ + 459   +
+ + 460  (defn GET +
+ + 461    "From the manual: +
+ + 462      +
+ + 463     '`get` is somewhat like `prop`; however its value is car of the rest of +
+ + 464     the list if the `indicator` is found, and NIL otherwise.' +
+ + 465      +
+ + 466     It's clear that `GET` is expected to be defined in terms of `PROP`, but +
+ + 467     we can't implement `PROP` here because we lack `EVAL`; and we can't have +
+ + 468     `EVAL` here because both it and `APPLY` depends on `GET`. +
+ + 469      +
+ + 470     OK, It's worse than that: the statement of the definition of `GET` (and  +
+ + 471     of) `PROP` on page 59 says that the first argument to each must be a list; +
+ + 472     But the in the definition of `ASSOC` on page 70, when `GET` is called its +
+ + 473     first argument is always an atom. Since it's `ASSOC` and `EVAL` which I  +
+ + 474     need to make work, I'm going to assume that page 59 is wrong." +
+ + 475    [symbol indicator] +
+ + 476    (let [binding (ASSOC symbol @oblist) +
+ + 477          val (cond +
+ + 478                (= binding NIL) NIL +
+ + 479                (= magic-marker +
+ + 480                   (CADR binding)) (loop [b binding] +
+ + 481                                    ;;  (println "GET loop, seeking " indicator ":") +
+ + 482                                    ;;  (pretty-print b) +
+ + 483                                     (if (instance? ConsCell b) +
+ + 484                                       (if (= (CAR b) indicator) +
+ + 485                                         (CADR b) ;; <- this is what we should actually be returning +
+ + 486                                         (recur (CDR b))) +
+ + 487                                       NIL)) +
+ + 488                :else (throw +
+ + 489                       (ex-info "Misformatted property list (missing magic marker)" +
+ + 490                                {:phase :host +
+ + 491                                 :function :get +
+ + 492                                 :args (list symbol indicator) +
+ + 493                                 :type :beowulf})))] +
+ + 494      ;; (println "<< GET returning: " val) +
+ + 495      val)) +
+ + 496   +
+ + 497  (defn DEFLIST +
+ + 498    "For each pair in this association list `a-list`, set the property with this +
+ + 499     `indicator` of the symbol which is the first element of the pair to the  +
+ + 500     value which is the second element of the pair. See page 58 of the manual." +
+ + 501    [a-list indicator] +
+ + 502    (map +
+ + 503     #(PUT (CAR %) indicator (CDR %)) +
+ + 504     a-list)) +
+ + 505   +
+ + 506  (defn DEFINE +
+ + 507    "Bootstrap-only version of `DEFINE` which, post boostrap, can be overwritten  +
+ + 508    in LISP.  +
+ + 509   +
+ + 510    The single argument to `DEFINE` should be an association list of symbols to +
+ + 511     lambda functions. See page 58 of the manual." +
+ + 512    [a-list] +
+ + 513    (DEFLIST a-list 'EXPR)) +
+ + 514   +
+ + 515  (defn SET +
+ + 516    "Implementation of SET in Clojure. Add to the `oblist` a binding of the +
+ + 517     value of `var` to the value of `val`. NOTE WELL: this is not SETQ!" +
+ + 518    [symbol val] +
+ + 519    (PUT symbol 'APVAL val)) +
+ + 520   +
+ + 521  ;;;; TRACE and friends ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +
+ + 522   +
+ + 523  (def traced-symbols +
+ + 524    "Symbols currently being traced." +
+ + 525    (atom #{})) +
+ + 526   +
+ + 527  (defn traced? +
+ + 528    "Return `true` iff `s` is a symbol currently being traced, else `nil`." +
+ + 529    [s] +
+ + 530    (try (contains? @traced-symbols s) +
+ + 531         (catch Throwable _ nil))) +
+ + 532   +
+ + 533  (defn TRACE +
+ + 534    "Add this `s` to the set of symbols currently being traced. If `s` +
+ + 535     is not a symbol or sequence of symbols, does nothing." +
+ + 536    [s] +
+ + 537    (swap! traced-symbols +
+ + 538           #(cond +
+ + 539              (symbol? s) (conj % s) +
+ + 540              (and (seq? s) (every? symbol? s)) (union % (set s)) +
+ + 541              :else %))) +
+ + 542   +
+ + 543  (defn UNTRACE +
+ + 544    "Remove this `s` from the set of symbols currently being traced. If `s` +
+ + 545     is not a symbol or sequence of symbols, does nothing." +
+ + 546    [s] +
+ + 547    (cond +
+ + 548      (symbol? s) (swap! traced-symbols #(set (remove (fn [x] (= s x)) %))) +
+ + 549      (and (seq? s) (every? symbol? s)) (map UNTRACE s)) +
+ + 550    @traced-symbols) +
+ + 551   +
+ + 552  ;;;; Extensions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +
+ + 553   +
+ + 554  (defn DOC +
+ + 555    "Open the page for this `symbol` in the Lisp 1.5 manual, if known, in the  +
+ + 556      default web browser. +
+ + 557      +
+ + 558     **NOTE THAT** this is an extension function, not available in strct mode." +
+ + 559    [symbol] +
+ + 560    (when (lax? 'DOC) +
+ + 561      (open-doc symbol)))
562  
- 563  (defn traced? + 563  (defn CONSP
- 564    "Return `true` iff `s` is a symbol currently being traced, else `nil`." + 564    "Return `T` if object `o` is a cons cell, else `F`.
- 565    [s] -
- - 566    (try (contains? @traced-symbols s) + 565     
- 567         (catch Throwable _ nil))) -
- - 568   -
- - 569  (defn TRACE + 566     **NOTE THAT** this is an extension function, not available in strct mode. 
- 570    "Add this `s` to the set of symbols currently being traced. If `s` + 567     I believe that Lisp 1.5 did not have any mechanism for testing whether an
- 571     is not a symbol or sequence of symbols, does nothing." + 568     argument was, or was not, a cons cell."
- 572    [s] -
- - 573    (swap! traced-symbols -
- - 574           #(cond -
- - 575              (symbol? s) (conj % s) -
- - 576              (and (seq? s) (every? symbol? s)) (union % (set s)) -
- - 577              :else %))) -
- - 578   -
- - 579  (defn UNTRACE -
- - 580    "Remove this `s` from the set of symbols currently being traced. If `s` -
- - 581     is not a symbol or sequence of symbols, does nothing." -
- - 582    [s] -
- - 583    (cond -
- - 584      (symbol? s) (swap! traced-symbols #(set (remove (fn [x] (= s x)) %))) -
- - 585      (and (seq? s) (every? symbol? s)) (map UNTRACE s)) -
- - 586    @traced-symbols) -
- - 587   -
- - 588  ;;;; Extensions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -
- - 589   -
- - 590  (defn DOC -
- - 591    "Open the page for this `symbol` in the Lisp 1.5 manual, if known, in the  -
- - 592      default web browser. -
- - 593      -
- - 594     **NOTE THAT** this is an extension function, not available in strct mode." -
- - 595    [symbol] -
- - 596    (when (lax? 'DOC) -
- - 597      (open-doc symbol))) -
- - 598   -
- - 599  (defn CONSP -
- - 600    "Return `T` if object `o` is a cons cell, else `F`. -
- - 601      -
- - 602     **NOTE THAT** this is an extension function, not available in strct mode.  -
- - 603     I believe that Lisp 1.5 did not have any mechanism for testing whether an -
- - 604     argument was, or was not, a cons cell." -
- - 605    [o] + 569    [o]
- 606    (when (lax? 'CONSP) + 570    (when (lax? 'CONSP)
- 607      (if (instance? ConsCell o) 'T 'F))) + 571      (if (instance? ConsCell o) 'T 'F)))
diff --git a/docs/cloverage/beowulf/io.clj.html b/docs/cloverage/beowulf/io.clj.html index aaf97e7..2ef3c37 100644 --- a/docs/cloverage/beowulf/io.clj.html +++ b/docs/cloverage/beowulf/io.clj.html @@ -283,248 +283,239 @@ 093    ([filepath]
- - 094     (let [destination (full-path (str filepath))] -
- - 095      (spit destination + + 094     (spit (full-path (str filepath))
- 096           (with-out-str + 095           (with-out-str
- 097             (println (apply str (repeat 79 ";"))) + 096             (println (apply str (repeat 79 ";")))
- 098             (println (format ";; Beowulf %s Sysout file generated at %s" + 097             (println (format ";; Beowulf %s Sysout file generated at %s"
- 099                              (or (System/getProperty "beowulf.version") "") + 098                              (or (System/getProperty "beowulf.version") "")
- 100                              (local-date-time))) + 099                              (local-date-time)))
- 101             (when (System/getenv "USER") + 100             (when (System/getenv "USER")
- 102               (println (format ";; generated by %s" (System/getenv "USER")))) + 101               (println (format ";; generated by %s" (System/getenv "USER"))))
- 103             (println (apply str (repeat 79 ";"))) + 102             (println (apply str (repeat 79 ";")))
- 104             (println) + 103             (println)
- 105             (let [output (safely-wrap-subrs @oblist)] + 104             (let [output (safely-wrap-subrs @oblist)]
- 106               (pretty-print output) + 105               (pretty-print output)
- 107               ))) -
- - 108       (println "Saved sysout to " destination) -
- - 109       NIL))) + 106               )))))
- 110   + 107  
- - 111  (defn resolve-subr -
- - 112    "If this oblist `entry` references a subroutine, attempt to fix up that + + 108  (defn resolve-subr
- 113     reference." + 109    "If this oblist `entry` references a subroutine, attempt to fix up that
- 114    ([entry] -
- - 115     (or (resolve-subr entry 'SUBR) -
- - 116         (resolve-subr entry 'FSUBR))) + 110     reference."
- 117    ([entry prop] + 111    ([entry] +
+ + 112     (or (resolve-subr entry 'SUBR) +
+ + 113         (resolve-subr entry 'FSUBR))) +
+ + 114    ([entry prop]
- 118     (cond (= entry NIL) NIL + 115     (cond (= entry NIL) NIL
- 119          (= (CAR entry) prop) (try + 116          (= (CAR entry) prop) (try
- 120                                  (make-cons-cell + 117                                  (make-cons-cell
- 121                                   (CAR entry) + 118                                   (CAR entry)
- 122                                   (make-cons-cell + 119                                   (make-cons-cell
- 123                                    (interpret-qualified-name + 120                                    (interpret-qualified-name
- 124                                           (CADR entry)) + 121                                           (CADR entry))
- 125                                    (CDDR entry))) + 122                                    (CDDR entry)))
- 126                                  (catch Exception _ + 123                                  (catch Exception _
- 127                                    (print "Warnung: ne can āfinde " + 124                                    (print "Warnung: ne can āfinde "
- 128                                           (CADR entry)) + 125                                           (CADR entry))
- 129                                    (CDDR entry))) + 126                                    (CDDR entry)))
- 130          :else (make-cons-cell + 127          :else (make-cons-cell
- 131                 (CAR entry) (resolve-subr (CDR entry)))))) + 128                 (CAR entry) (resolve-subr (CDR entry))))))
- 132   + 129  
- 133   + 130  
- 134  (defn- resolve-subroutines + 131  (defn- resolve-subroutines
- 135    "Attempt to fix up the references to subroutines (Clojure functions) among + 132    "Attempt to fix up the references to subroutines (Clojure functions) among
- 136     these `objects`, being new content for the object list." + 133     these `objects`, being new content for the object list."
- 137    [objects] + 134    [objects]
- 138    (make-beowulf-list + 135    (make-beowulf-list
- 139     (map + 136     (map
- 140      resolve-subr + 137      resolve-subr
- 141      objects))) + 138      objects)))
- 142   + 139  
- 143  (defn SYSIN + 140  (defn SYSIN
- 144    "Read the contents of the file at this `filename` into the object list.  + 141    "Read the contents of the file at this `filename` into the object list. 
- 145      + 142     
- 146     If the file is not a valid Beowulf sysout file, this will probably  + 143     If the file is not a valid Beowulf sysout file, this will probably 
- 147     corrupt the system, you have been warned. File paths will be considered  + 144     corrupt the system, you have been warned. File paths will be considered 
- 148     relative to the filepath set when starting Lisp. + 145     relative to the filepath set when starting Lisp.
- 149   + 146  
- 150     It is intended that sysout files can be read both from resources within + 147     It is intended that sysout files can be read both from resources within
- 151     the jar file, and from the file system. If a named file exists in both the + 148     the jar file, and from the file system. If a named file exists in both the
- 152     file system and the resources, the file system will be preferred. + 149     file system and the resources, the file system will be preferred.
- 153      + 150     
- 154     **NOTE THAT** if the provided `filename` does not end with `.lsp` (which, + 151     **NOTE THAT** if the provided `filename` does not end with `.lsp` (which,
- 155     if you're writing it from the Lisp REPL, it won't), the extension `.lsp` + 152     if you're writing it from the Lisp REPL, it won't), the extension `.lsp`
- 156     will be appended. + 153     will be appended.
- 157      + 154     
- 158     **NOTE THAT** this is an extension function, not available in strct mode." + 155     **NOTE THAT** this is an extension function, not available in strct mode."
- 159    ([] + 156    ([]
- 160     (SYSIN (or (:read *options*) (str "resources/" default-sysout)))) + 157     (SYSIN (or (:read *options*) (str "resources/" default-sysout))))
- 161    ([filename] + 158    ([filename]
- 162     (let [fp (file (full-path (str filename))) + 159     (let [fp (file (full-path (str filename)))
- 163           file (when (and (.exists fp) (.canRead fp)) fp) + 160           file (when (and (.exists fp) (.canRead fp)) fp)
- 164           res (try (resource filename) + 161           res (try (resource filename)
- 165                    (catch Throwable _ nil)) + 162                    (catch Throwable _ nil))
- 166           content (try (READ (slurp (or file res))) + 163           content (try (READ (slurp (or file res)))
- 167                        (catch Throwable _ + 164                        (catch Throwable _
- 168                          (throw (ex-info "Ne can ārǣde" + 165                          (throw (ex-info "Ne can ārǣde"
- 169                                          {:context "SYSIN" + 166                                          {:context "SYSIN"
- 170                                           :filename filename + 167                                           :filename filename
- 171                                           :filepath fp}))))] + 168                                           :filepath fp}))))]
- 172       (swap! oblist + 169       (swap! oblist
- 173              #(when (or % (seq content)) + 170              #(when (or % (seq content))
- 174                 (resolve-subroutines content)))))) + 171                 (resolve-subroutines content))))))
diff --git a/docs/cloverage/beowulf/oblist.clj.html b/docs/cloverage/beowulf/oblist.clj.html index 4ca6768..f96cc9c 100644 --- a/docs/cloverage/beowulf/oblist.clj.html +++ b/docs/cloverage/beowulf/oblist.clj.html @@ -133,8 +133,8 @@ 043    "Command line options from invocation."
- - 044    {:testing true}) + + 044    {})
045   diff --git a/docs/cloverage/beowulf/read.clj.html b/docs/cloverage/beowulf/read.clj.html index 74998a3..ba3a47f 100644 --- a/docs/cloverage/beowulf/read.clj.html +++ b/docs/cloverage/beowulf/read.clj.html @@ -50,319 +50,283 @@ 015    switch."
- 016    (:require [beowulf.oblist :refer [*options*]] + 016    (:require ;; [beowulf.reader.char-reader :refer [read-chars]]
- 017              [beowulf.reader.char-reader :refer [read-chars]] + 017              [beowulf.reader.generate :refer [generate]]
- 018              [beowulf.reader.generate :refer [generate]] + 018              [beowulf.reader.parser :refer [parse]]
- 019              [beowulf.reader.parser :refer [parse]] + 019              [beowulf.reader.simplify :refer [simplify]]
- 020              [beowulf.reader.simplify :refer [simplify]] + 020              [clojure.string :refer [join split starts-with? trim]])
- 021              [clojure.string :refer [join split starts-with? trim]]) + 021    (:import [java.io InputStream]
- 022    (:import [instaparse.gll Failure] -
- - 023             [java.io InputStream])) + 022             [instaparse.gll Failure]))
- 024   + 023  
- 025  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + 024  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- 026  ;;; + 025  ;;;
- 027  ;;; This file provides the reader required for boostrapping. It's not a bad + 026  ;;; This file provides the reader required for boostrapping. It's not a bad
- 028  ;;; reader - it provides feedback on errors found in the input - but it isn't + 027  ;;; reader - it provides feedback on errors found in the input - but it isn't
- 029  ;;; the real Lisp reader. + 028  ;;; the real Lisp reader.
- 030  ;;; + 029  ;;;
- 031  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + 030  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- 032  ;;; + 031  ;;;
- 033  ;;; Copyright (C) 2022-2023 Simon Brooke + 032  ;;; Copyright (C) 2022-2023 Simon Brooke
- 034  ;;; + 033  ;;;
- 035  ;;; This program is free software; you can redistribute it and/or + 034  ;;; This program is free software; you can redistribute it and/or
- 036  ;;; modify it under the terms of the GNU General Public License + 035  ;;; modify it under the terms of the GNU General Public License
- 037  ;;; as published by the Free Software Foundation; either version 2 + 036  ;;; as published by the Free Software Foundation; either version 2
- 038  ;;; of the License, or (at your option) any later version. + 037  ;;; of the License, or (at your option) any later version.
- 039  ;;;  + 038  ;;; 
- 040  ;;; This program is distributed in the hope that it will be useful, + 039  ;;; This program is distributed in the hope that it will be useful,
- 041  ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of + 040  ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- 042  ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + 041  ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- 043  ;;; GNU General Public License for more details. + 042  ;;; GNU General Public License for more details.
- 044  ;;;  + 043  ;;; 
- 045  ;;; You should have received a copy of the GNU General Public License + 044  ;;; You should have received a copy of the GNU General Public License
- 046  ;;; along with this program; if not, write to the Free Software + 045  ;;; along with this program; if not, write to the Free Software
- 047  ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. + 046  ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- 048  ;;; + 047  ;;;
- 049  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + 048  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- 050   + 049  
- 051  (defn strip-line-comments + 050  (defn strip-line-comments
- 052    "Strip blank lines and comment lines from this string `s`, expected to + 051    "Strip blank lines and comment lines from this string `s`, expected to
- 053     be Lisp source." + 052     be Lisp source."
- 054    [^String s] + 053    [^String s]
- 055    (join "\n" + 054    (join "\n"
- 056          (remove + 055          (remove
- 057           #(or (empty? %) + 056           #(or (empty? %)
- 058                (starts-with? (trim %) ";;")) + 057                (starts-with? (trim %) ";;"))
- 059           (split s #"\n")))) + 058           (split s #"\n"))))
- 060   + 059  
- 061  (defn number-lines + 060  (defn number-lines
- 062    ([^String s] + 061    ([^String s]
- 063     (number-lines s nil)) + 062     (number-lines s nil))
- 064    ([^String s ^Failure e] + 063    ([^String s ^Failure e]
- 065     (let [l (-> e :line) + 064     (let [l (-> e :line)
- 066           c (-> e :column)] + 065           c (-> e :column)]
- 067       (join "\n" + 066       (join "\n"
- 068             (map #(str (format "%5d %s" (inc %1) %2) + 067             (map #(str (format "%5d %s" (inc %1) %2)
- 069                        (when (= l (inc %1)) + 068                        (when (= l (inc %1))
- 070                          (str "\n" (apply str (repeat c " ")) "^"))) + 069                          (str "\n" (apply str (repeat c " ")) "^")))
- 071                  (range) + 070                  (range)
- 072                  (split s #"\n")))))) + 071                  (split s #"\n"))))))
- 073   + 072  
- 074  (defn gsp + 073  (defn gsp
- 075    "Shortcut macro - the internals of read; or, if you like, read-string. + 074    "Shortcut macro - the internals of read; or, if you like, read-string.
- 076    Argument `s` should be a string representation of a valid Lisp + 075    Argument `s` should be a string representation of a valid Lisp
- 077    expression." + 076    expression."
- 078    [s] + 077    [s]
- 079    (let [source (strip-line-comments s) + 078    (let [source (strip-line-comments s)
- 080          parse-tree (parse source)] + 079          parse-tree (parse source)]
- 081      (if (instance? Failure parse-tree) + 080      (if (instance? Failure parse-tree)
- 082        (doall (println (number-lines source parse-tree)) + 081        (doall (println (number-lines source parse-tree))
- 083               (throw (ex-info "Ne can forstande " (assoc parse-tree :source source)))) + 082               (throw (ex-info "Ne can forstande " (assoc parse-tree :source source))))
- 084        (generate (simplify parse-tree))))) + 083        (generate (simplify parse-tree)))))
- 085   + 084  
- 086  (defn- dummy-read-chars [prompt] + 085  (defn read-from-console +
+ + 086    "Attempt to read a complete lisp expression from the console. NOTE that this +
+ + 087     will only really work for S-Expressions, not M-Expressions." +
+ + 088    []
- 087    (loop [r "" p prompt] + 089    (loop [r (read-line)]
- - 088      (if (and (seq r) -
- - 089               (= (count (re-seq #"\(" r)) + + 090      (if (and (= (count (re-seq #"\(" r))
- 090                  (count (re-seq #"\)" r))) + 091             (count (re-seq #"\)" r)))
- 091               (= (count (re-seq #"\[" r)) + 092               (= (count (re-seq #"\[" r))
- 092                  (count (re-seq #"\]" r)))) + 093                  (count (re-seq #"\]" r))))
- 093        r + 094        r
- - 094        (do -
- - 095          (print (str p " ")) -
- - 096          (flush) -
- - 097          (recur (str r "\n" (read-line)) "::"))))) + + 095        (recur (str r "\n" (read-line))))))
- 098   + 096  
- 099  (defn read-from-console + 097  (defn READ
- 100    "Attempt to read a complete lisp expression from the console. + 098    "An implementation of a Lisp reader sufficient for bootstrapping; not necessarily
- 101      + 099    the final Lisp reader. `input` should be either a string representation of a LISP
- 102     There's a major problem here that the read-chars reader messes up testing. + 100    expression, or else an input stream. A single form will be read."
- 103     We need to be able to disable it while testing!" + 101    ([] +
+ + 102     (gsp (read-from-console)))
- 104    [prompt] -
- - 105    (if (:testing *options*) -
- - 106      (dummy-read-chars prompt) -
- - 107      (read-chars prompt))) -
- - 108   -
- - 109  (defn READ -
- - 110    "An implementation of a Lisp reader sufficient for bootstrapping; not necessarily -
- - 111    the final Lisp reader. `input` should be either a string representation of a LISP -
- - 112    expression, or else an input stream. A single form will be read." -
- - 113    ([] -
- - 114     (gsp (read-from-console (:prompt *options*)))) -
- - 115    ([input] + 103    ([input]
- 116     (cond + 104     (cond
- 117       (empty? input) (READ) + 105       (empty? input) (READ)
- 118       (string? input) (gsp input) + 106       (string? input) (gsp input)
- 119       (instance? InputStream input) (READ (slurp input)) + 107       (instance? InputStream input) (READ (slurp input))
- 120       :else    (throw (ex-info "READ: `input` should be a string or an input stream" {}))))) + 108       :else    (throw (ex-info "READ: `input` should be a string or an input stream" {})))))
diff --git a/docs/cloverage/beowulf/reader/char_reader.clj.html b/docs/cloverage/beowulf/reader/char_reader.clj.html index a0ac0f9..f198c42 100644 --- a/docs/cloverage/beowulf/reader/char_reader.clj.html +++ b/docs/cloverage/beowulf/reader/char_reader.clj.html @@ -71,259 +71,163 @@ 022     https://stackoverflow.com/questions/7931988/how-to-manipulate-control-characters"

- 023    (:require [beowulf.oblist :refer [*options* oblist]]) + 023    ;; (:import [org.jline.reader LineReader LineReaderBuilder]
- 024    (:import [org.jline.reader.impl.completer StringsCompleter] + 024    ;;          [org.jline.terminal TerminalBuilder])
- 025             [org.jline.reader.impl DefaultParser DefaultParser$Bracket] -
- - 026             [org.jline.reader LineReaderBuilder] -
- - 027             [org.jline.terminal TerminalBuilder] -
- - 028             [org.jline.widget AutopairWidgets AutosuggestionWidgets])) + 025    )
- 029   + 026  
- 030  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + 027  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- 031  ;;; + 028  ;;;
- 032  ;;; Copyright (C) 2022-2023 Simon Brooke + 029  ;;; Copyright (C) 2022-2023 Simon Brooke
- 033  ;;; + 030  ;;;
- 034  ;;; This program is free software; you can redistribute it and/or + 031  ;;; This program is free software; you can redistribute it and/or
- 035  ;;; modify it under the terms of the GNU General Public License + 032  ;;; modify it under the terms of the GNU General Public License
- 036  ;;; as published by the Free Software Foundation; either version 2 + 033  ;;; as published by the Free Software Foundation; either version 2
- 037  ;;; of the License, or (at your option) any later version. + 034  ;;; of the License, or (at your option) any later version.
- 038  ;;;  + 035  ;;; 
- 039  ;;; This program is distributed in the hope that it will be useful, + 036  ;;; This program is distributed in the hope that it will be useful,
- 040  ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of + 037  ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- 041  ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + 038  ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- 042  ;;; GNU General Public License for more details. + 039  ;;; GNU General Public License for more details.
- 043  ;;;  + 040  ;;; 
- 044  ;;; You should have received a copy of the GNU General Public License + 041  ;;; You should have received a copy of the GNU General Public License
- 045  ;;; along with this program; if not, write to the Free Software + 042  ;;; along with this program; if not, write to the Free Software
- 046  ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. + 043  ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- 047  ;;; + 044  ;;;
- 048  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + 045  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- 049   + 046  
- 050  ;; It looks from the example given [here](https://github.com/jline/jline3/blob/master/demo/src/main/java/org/jline/demo/Repl.java) + 047  ;; It looks from the example given [here](https://github.com/jline/jline3/blob/master/demo/src/main/java/org/jline/demo/Repl.java)
- 051  ;; as though JLine could be used to build a perfect line-reader for Beowulf; but it also + 048  ;; as though JLine could be used to build a perfect line-reader for Beowulf; but it also
- 052  ;; looks as though you'd need a DPhil in JLine to write it, and I don't have + 049  ;; looks as though you'd need a DPhil in JLine to write it, and I don't have
- 053  ;; the time. + 050  ;; the time.
- 054   -
- - 055  (defn build-completer + 051  
- 056    "Build a completer which takes tokens from the oblist. + 052  ;; (def get-reader
- 057      + 053  ;;   "Return a reader, first constructing it if necessary.
- 058     This is sort-of working, in as much as hitting <TAB> on a blank line will  + 054     
- 059     show a table of values from the oblist, but hitting <TAB> after you've  + 055  ;;    **NOTE THAT** this is not settled API. The existence and call signature of
- 060     started input does not show potential completions for tokens you've started." + 056  ;;    this function is not guaranteed in future versions."
- 061    [] + 057  ;;   (memoize (fn []
- - 062    (StringsCompleter. (map #(str (first %)) @oblist))) + + 058  ;;   (let [term (.build (.system (TerminalBuilder/builder) true))] +
+ + 059  ;;     (.build (.terminal (LineReaderBuilder/builder) term))))))
- 063   + 060  
- 064  ;; This breaks; it is not correctly resolving the Enum, although I can't work out + 061  ;; (defn read-chars
- 065  ;; why not. + 062  ;;   "A drop-in replacement for `clojure.core/read-line`, except that line editing
- 066  ;; (defn build-parser + 063  ;;    and history should be enabled.
- 067  ;;   [] + 064     
- 068  ;;   (println "Building parser") + 065  ;;    **NOTE THAT** this does not work yet, but it is in the API because I hope 
- 069  ;;   (let [parser (DefaultParser.)] + 066  ;;    that it will work later!"
- 070  ;;     (doall + 067  ;;   [] 
- 071  ;;      (.setEofOnUnclosedBracket  + 068  ;;     (let [eddie (get-reader)]
- 072  ;;       parser DefaultParser$Bracket/ROUND)))) -
- - 073   -
- - 074  (def get-reader + 069  ;;       (loop [s (.readLine eddie)]
- 075    "Return a reader, first constructing it if necessary. + 070  ;;       (if (and (= (count (re-seq #"\(" s))
- 076      + 071  ;;            (count (re-seq #"\)" s)))
- 077     **NOTE THAT** this is not settled API. The existence and call signature of + 072  ;;                (= (count (re-seq #"\[]" s))
- 078     this function is not guaranteed in future versions." -
- - 079    (memoize (fn [] -
- - 080              (let [term (.build (.system (TerminalBuilder/builder) true)) -
- - 081                    reader (-> (LineReaderBuilder/builder) -
- - 082                                (.terminal  term) -
- - 083                                (.completer  (build-completer)) + 073  ;;                   (count (re-seq #"\]" s))))
- 084   ;;       #(.parser % (build-parser)) + 074  ;;         s
- 085                                (.build)) -
- - 086                    ;; apw (AutopairWidgets. reader false) -
- - 087                    ;; asw (AutosuggestionWidgets. reader) -
- - 088                    ] -
- - 089                ;; (.enable apw) -
- - 090                ;; (.enable asw) -
- - 091                reader)))) -
- - 092   -
- - 093  (defn read-chars -
- - 094    "A drop-in replacement for `clojure.core/read-line`, except that line editing -
- - 095     and history should be enabled. -
- - 096      -
- - 097     **NOTE THAT** this does not fully work yet, but it is in the API because I  -
- - 098     hope that it will work later!" -
- - 099    [prompt] -
- - 100    (let [eddie (get-reader)] -
- - 101      (loop [s (.readLine eddie (str prompt " "))] -
- - 102        (if (and (= (count (re-seq #"\(" s)) -
- - 103                    (count (re-seq #"\)" s))) -
- - 104                 (= (count (re-seq #"\[]" s)) -
- - 105                    (count (re-seq #"\]" s)))) -
- - 106          s -
- - 107          (recur (str s " " (.readLine eddie ":: "))))))) + 075  ;;         (recur (str s " " (.readLine eddie)))))))
diff --git a/docs/cloverage/index.html b/docs/cloverage/index.html index bb572ba..8f8236c 100644 --- a/docs/cloverage/index.html +++ b/docs/cloverage/index.html @@ -16,71 +16,71 @@ beowulf.bootstrap
746
347
-68.25 % + style="width:63.9344262295082%; + float:left;"> 624
352
+63.93 %
164
21
70
-72.55 % -49843255 + style="width:59.48275862068966%; + float:left;"> 138
19
75
+67.67 % +42233232 beowulf.cons-cell
366
115
-76.09 % + style="width:72.34927234927235%; + float:left;"> 348
133
+72.35 %
114
109
9
22
-84.83 % + style="width:18.620689655172413%; + float:left;"> 27 +81.38 % 27423145 beowulf.core
210
74
-73.94 % + style="width:69.47368421052632%; + float:left;"> 198
87
+69.47 %
54
2
13
-81.16 % + style="width:72.46376811594203%; + float:left;"> 50
4
15
+78.26 % 132669 beowulf.host
1410
1067
-56.92 % + style="width:42.77384423157018%; + float:left;"> 1027
1374
+42.77 %
203
32
32
-88.01 % -60769267 + style="width:53.72549019607843%; + float:left;"> 137
37
81
+68.24 % +57166255 beowulf.interop
beowulf.io
142
188
-43.03 % + style="width:56.037151702786375%; + float:left;"> 181
+43.96 %
32
7
36
-52.00 % -1741275 + style="width:46.478873239436616%; + float:left;"> 33
6
32
+54.93 % +1711271 beowulf.manual
beowulf.oblist
11
+ float:left;"> 9
100.00 %
beowulf.read
112
94
-54.37 % + style="width:49.43181818181818%; + float:left;"> 87
89
+49.43 %
29
21
3
15
-68.09 % -1201047 +61.54 % +108939 beowulf.reader.char-reader
7
63
-10.00 % + style="width:100.0%; + float:left;"> 1 +100.00 %
5
14
-26.32 % -107619 + style="width:100.0%; + float:left;"> 1 +100.00 % +7541 beowulf.reader.generate
Totals: -69.16 % +64.63 % -78.39 % +74.41 % diff --git a/docs/codox/beowulf.bootstrap.html b/docs/codox/beowulf.bootstrap.html index c9ba900..8301c99 100644 --- a/docs/codox/beowulf.bootstrap.html +++ b/docs/codox/beowulf.bootstrap.html @@ -1,30 +1,17 @@ -beowulf.bootstrap documentation

beowulf.bootstrap

Lisp as defined in Chapter 1 (pages 1-14) of the Lisp 1.5 Programmer's Manual; that is to say, a very simple Lisp language, which should, I believe, be sufficient in conjunction with the functions provided by beowulf.host, be sufficient to bootstrap the full Lisp 1.5 interpreter..

-

The convention is adopted that functions in this file with names in ALLUPPERCASE are Lisp 1.5 functions (although written in Clojure) and that therefore all arguments must be numbers, symbols or beowulf.cons_cell.ConsCell objects.

-

*depth*

dynamic

Stack depth. Unfortunately we need to be able to pass round depth for functions which call EVAL/APPLY but do not know about depth.

-

APPLY

(APPLY function args environment)(APPLY function args environment depth)

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.

-

apply-label

(apply-label function args environment depth)

Apply in the special case that the first element in the function is LABEL.

-

EVAL

(EVAL expr)(EVAL expr env depth)

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.

-

find-target

TODO: write docs

-

PROG

(PROG program env depth)

The accursed PROG feature. See page 71 of the manual.

+beowulf.bootstrap documentation

beowulf.bootstrap

Lisp as defined in Chapter 1 (pages 1-14) of the Lisp 1.5 Programmer's Manual; that is to say, a very simple Lisp language, which should, I believe, be sufficient in conjunction with the functions provided by beowulf.host, be sufficient to bootstrap the full Lisp 1.5 interpreter..

+

The convention is adopted that functions in this file with names in ALLUPPERCASE are Lisp 1.5 functions (although written in Clojure) and that therefore all arguments must be numbers, symbols or beowulf.cons_cell.ConsCell objects.

APPLY

(APPLY function args environment depth)

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.

EVAL

(EVAL expr)(EVAL expr env depth)

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.

find-target

TODO: write docs

PROG

(PROG program env depth)

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.

+

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 PROGs 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.

+

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 A3except 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.

-

prog-eval

(prog-eval expr vars env depth)

Like EVAL, q.v., except handling symbols, and expressions starting GO, RETURN, SET and SETQ specially.

-

SASSOC

(SASSOC x y u)

Like ASSOC, but with an action to take if no value is found.

-

From the manual, page 60:

-

‘The function sassoc searches y, which is a list of dotted pairs, for a pair whose first element that is x. If such a pair is found, the value of sassoc is this pair. Otherwise the function u of no arguments is taken as the value of sassoc.’

-

try-resolve-subroutine

(try-resolve-subroutine subr args)

Attempt to resolve this subr with these args.

-

value

(value s)(value s indicators)

Seek a value for this symbol s by checking each of these indicators in turn.

-
\ No newline at end of file +

Good.

prog-eval

(prog-eval expr vars env depth)

Like EVAL, q.v., except handling symbols, and expressions starting GO, RETURN, SET and SETQ specially.

try-resolve-subroutine

(try-resolve-subroutine subr args)

Attempt to resolve this subr with these args.

\ No newline at end of file diff --git a/docs/codox/beowulf.cons-cell.html b/docs/codox/beowulf.cons-cell.html index 035aeb5..6e2b315 100644 --- a/docs/codox/beowulf.cons-cell.html +++ b/docs/codox/beowulf.cons-cell.html @@ -1,16 +1,3 @@ -beowulf.cons-cell documentation

beowulf.cons-cell

The fundamental cons cell on which all Lisp structures are built. Lisp 1.5 lists do not necessarily have a sequence as their CDR, and must have both CAR and CDR mutable, so cannot be implemented on top of Clojure lists.

-

cons-cell?

(cons-cell? o)

Is this object o a beowulf cons-cell?

-

F

The canonical false value - different from NIL, which is not canonically false in Lisp 1.5.

-

make-beowulf-list

(make-beowulf-list x)

Construct a linked list of cons cells with the same content as the sequence x.

-

make-cons-cell

(make-cons-cell car cdr)

Construct a new instance of cons cell with this car and cdr.

-

MutableSequence

protocol

Like a sequence, but mutable.

-

members

getCar

(getCar this)

Return the first element of this sequence.

-

getCdr

(getCdr this)

like more, q.v., but returns List NIL not Clojure nil when empty.

-

getUid

(getUid this)

Returns a unique identifier for this object

-

rplaca

(rplaca this value)

replace the first element of this sequence with this value

-

rplacd

(rplacd this value)

replace the rest (but-first; cdr) of this sequence with this value

-

pretty-print

(pretty-print cell)(pretty-print cell width level)

This isn’t the world’s best pretty printer but it sort of works.

-

T

The canonical true value.

-
\ No newline at end of file +beowulf.cons-cell documentation

beowulf.cons-cell

The fundamental cons cell on which all Lisp structures are built. Lisp 1.5 lists do not necessarily have a sequence as their CDR, and must have both CAR and CDR mutable, so cannot be implemented on top of Clojure lists.

cons-cell?

(cons-cell? o)

Is this object o a beowulf cons-cell?

F

The canonical false value - different from NIL, which is not canonically false in Lisp 1.5.

make-beowulf-list

(make-beowulf-list x)

Construct a linked list of cons cells with the same content as the sequence x.

make-cons-cell

(make-cons-cell car cdr)

Construct a new instance of cons cell with this car and cdr.

MutableSequence

protocol

Like a sequence, but mutable.

members

getCar

(getCar this)

Return the first element of this sequence.

getCdr

(getCdr this)

like more, q.v., but returns List NIL not Clojure nil when empty.

getUid

(getUid this)

Returns a unique identifier for this object

rplaca

(rplaca this value)

replace the first element of this sequence with this value

rplacd

(rplacd this value)

replace the rest (but-first; cdr) of this sequence with this value

pretty-print

(pretty-print cell)(pretty-print cell width level)

This isn’t the world’s best pretty printer but it sort of works.

T

The canonical true value.

\ No newline at end of file diff --git a/docs/codox/beowulf.core.html b/docs/codox/beowulf.core.html index fe738c1..8b8b2ef 100644 --- a/docs/codox/beowulf.core.html +++ b/docs/codox/beowulf.core.html @@ -1,8 +1,3 @@ -beowulf.core documentation

beowulf.core

Essentially, the -main function and the bootstrap read-eval-print loop.

-

-main

(-main & opts)

Parse options, print the banner, read the init file if any, and enter the read/eval/print loop.

-

cli-options

TODO: write docs

-

repl

(repl prompt)

Read/eval/print loop.

-

stop-word

The word which, if submitted an an input line, will cause Beowulf to quit. Question: should this be forlǣte?

-
\ No newline at end of file +beowulf.core documentation

beowulf.core

Essentially, the -main function and the bootstrap read-eval-print loop.

-main

(-main & opts)

Parse options, print the banner, read the init file if any, and enter the read/eval/print loop.

cli-options

TODO: write docs

repl

(repl prompt)

Read/eval/print loop.

stop-word

The word which, if submitted an an input line, will cause Beowulf to quit. Question: should this be forlǣte?

\ No newline at end of file diff --git a/docs/codox/beowulf.gendoc.html b/docs/codox/beowulf.gendoc.html index 2414d81..763e8ab 100644 --- a/docs/codox/beowulf.gendoc.html +++ b/docs/codox/beowulf.gendoc.html @@ -1,13 +1,4 @@ -beowulf.gendoc documentation

beowulf.gendoc

Generate table of documentation of Lisp symbols and functions.

-

NOTE: this is very hacky. You almost certainly do not want to use this!

-

find-documentation

(find-documentation entry)

Find appropriate documentation for this entry from the oblist.

-

gen-doc-table

(gen-doc-table)

TODO: write docs

-

gen-index

(gen-index)(gen-index url destination)

TODO: write docs

-

host-functions

Functions which we can infer are written in Clojure. We need to collect these at run-time, not compile time, hence memoised function, not variable.

-

infer-implementation

(infer-implementation entry)

TODO: write docs

-

infer-signature

(infer-signature entry)

Infer the signature of the function value of this oblist entry, if any.

-

infer-type

(infer-type entry)

Try to work out what this entry from the oblist actually represents.

-

open-doc

(open-doc symbol)

Open the documentation page for this symbol, if known, in the default web browser.

-
\ No newline at end of file +beowulf.gendoc documentation

beowulf.gendoc

Generate table of documentation of Lisp symbols and functions.

+

NOTE: this is very hacky. You almost certainly do not want to use this!

find-documentation

(find-documentation entry)

Find appropriate documentation for this entry from the oblist.

gen-doc-table

(gen-doc-table)

TODO: write docs

gen-index

(gen-index)(gen-index url destination)

TODO: write docs

host-functions

Functions which we can infer are written in Clojure. We need to collect these at run-time, not compile time, hence memoised function, not variable.

infer-implementation

(infer-implementation entry)

TODO: write docs

infer-signature

(infer-signature entry)

Infer the signature of the function value of this oblist entry, if any.

infer-type

(infer-type entry)

Try to work out what this entry from the oblist actually represents.

open-doc

(open-doc symbol)

Open the documentation page for this symbol, if known, in the default web browser.

\ No newline at end of file diff --git a/docs/codox/beowulf.host.html b/docs/codox/beowulf.host.html index 1b803cc..d6c6f6f 100644 --- a/docs/codox/beowulf.host.html +++ b/docs/codox/beowulf.host.html @@ -1,100 +1,19 @@ -beowulf.host documentation

beowulf.host

provides Lisp 1.5 functions which can’t be (or can’t efficiently be) implemented in Lisp 1.5, which therefore need to be implemented in the host language, in this case Clojure.

-

ADD1

(ADD1 x)

TODO: write docs

-

AND

(AND & args)

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.

-

ASSOC

(ASSOC x a)

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.

+beowulf.host documentation

beowulf.host

provides Lisp 1.5 functions which can’t be (or can’t efficiently be) implemented in Lisp 1.5, which therefore need to be implemented in the host language, in this case Clojure.

ADD1

(ADD1 x)

TODO: write docs

AND

(AND & args)

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.

ASSOC

(ASSOC x a)

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

(ATOM x)

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.

-

ATOM?

macro

(ATOM? x)

The convention of returning F from predicates, rather than NIL, is going to tie me in knots. This is a variant of ATOM which returns NIL on failure.

-

ATTRIB

(ATTRIB x e)

Destructive append. From page 59 of the manual:

-

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))))))]
-
-

would put EXPR followed by the LAMBDA expression for FF onto the end of the property list for FF.

-

CAAAAR

macro

(CAAAAR x)

TODO: write docs

-

CAAADR

macro

(CAAADR x)

TODO: write docs

-

CAAAR

macro

(CAAAR x)

TODO: write docs

-

CAADAR

macro

(CAADAR x)

TODO: write docs

-

CAADDR

macro

(CAADDR x)

TODO: write docs

-

CAADR

macro

(CAADR x)

TODO: write docs

-

CAAR

macro

(CAAR x)

TODO: write docs

-

CADAAR

macro

(CADAAR x)

TODO: write docs

-

CADADR

macro

(CADADR x)

TODO: write docs

-

CADAR

macro

(CADAR x)

TODO: write docs

-

CADDAR

macro

(CADDAR x)

TODO: write docs

-

CADDDR

macro

(CADDDR x)

TODO: write docs

-

CADDR

macro

(CADDR x)

TODO: write docs

-

CADR

macro

(CADR x)

TODO: write docs

-

CAR

(CAR x)

Return the item indicated by the first pointer of a pair. NIL is treated specially: the CAR of NIL is NIL.

-

CDAAAR

macro

(CDAAAR x)

TODO: write docs

-

CDAADR

macro

(CDAADR x)

TODO: write docs

-

CDAAR

macro

(CDAAR x)

TODO: write docs

-

CDADAR

macro

(CDADAR x)

TODO: write docs

-

CDADDR

macro

(CDADDR x)

TODO: write docs

-

CDADR

macro

(CDADR x)

TODO: write docs

-

CDAR

macro

(CDAR x)

TODO: write docs

-

CDDAAR

macro

(CDDAAR x)

TODO: write docs

-

CDDADR

macro

(CDDADR x)

TODO: write docs

-

CDDAR

macro

(CDDAR x)

TODO: write docs

-

CDDDAR

macro

(CDDDAR x)

TODO: write docs

-

CDDDDR

macro

(CDDDDR x)

TODO: write docs

-

CDDDR

macro

(CDDDR x)

TODO: write docs

-

CDDR

macro

(CDDR x)

TODO: write docs

-

CDR

(CDR x)

Return the item indicated by the second pointer of a pair. NIL is treated specially: the CDR of NIL is NIL.

-

CONS

(CONS car cdr)

Construct a new instance of cons cell with this car and cdr.

-

CONSP

(CONSP o)

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.

-

DEFINE

(DEFINE a-list)

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.

-

DEFLIST

(DEFLIST a-list indicator)

For each pair in this association list a-list, set the property with this indicator of the symbol which is the first element of the pair to the value which is the second element of the pair. See page 58 of the manual.

-

DIFFERENCE

(DIFFERENCE x y)

TODO: write docs

-

DOC

(DOC symbol)

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.

-

EQ

(EQ x y)

Returns T if and only if both x and y are bound to the same atom, else NIL.

-

EQUAL

(EQUAL x y)

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

-

ERROR

(ERROR & args)

Throw an error

-

FIXP

(FIXP x)

TODO: write docs

-

GENSYM

(GENSYM)

Generate a unique symbol.

-

GET

(GET symbol indicator)

From the manual:

+

NOTE THAT this function is overridden by an implementation in Lisp, but is currently still present for bootstrapping.

ATOM

(ATOM x)

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.

ATOM?

macro

(ATOM? x)

The convention of returning F from predicates, rather than NIL, is going to tie me in knots. This is a variant of ATOM which returns NIL on failure.

CAAAAR

macro

(CAAAAR x)

TODO: write docs

CAAADR

macro

(CAAADR x)

TODO: write docs

CAAAR

macro

(CAAAR x)

TODO: write docs

CAADAR

macro

(CAADAR x)

TODO: write docs

CAADDR

macro

(CAADDR x)

TODO: write docs

CAADR

macro

(CAADR x)

TODO: write docs

CAAR

macro

(CAAR x)

TODO: write docs

CADAAR

macro

(CADAAR x)

TODO: write docs

CADADR

macro

(CADADR x)

TODO: write docs

CADAR

macro

(CADAR x)

TODO: write docs

CADDAR

macro

(CADDAR x)

TODO: write docs

CADDDR

macro

(CADDDR x)

TODO: write docs

CADDR

macro

(CADDR x)

TODO: write docs

CADR

macro

(CADR x)

TODO: write docs

CAR

(CAR x)

Return the item indicated by the first pointer of a pair. NIL is treated specially: the CAR of NIL is NIL.

CDAAAR

macro

(CDAAAR x)

TODO: write docs

CDAADR

macro

(CDAADR x)

TODO: write docs

CDAAR

macro

(CDAAR x)

TODO: write docs

CDADAR

macro

(CDADAR x)

TODO: write docs

CDADDR

macro

(CDADDR x)

TODO: write docs

CDADR

macro

(CDADR x)

TODO: write docs

CDAR

macro

(CDAR x)

TODO: write docs

CDDAAR

macro

(CDDAAR x)

TODO: write docs

CDDADR

macro

(CDDADR x)

TODO: write docs

CDDAR

macro

(CDDAR x)

TODO: write docs

CDDDAR

macro

(CDDDAR x)

TODO: write docs

CDDDDR

macro

(CDDDDR x)

TODO: write docs

CDDDR

macro

(CDDDR x)

TODO: write docs

CDDR

macro

(CDDR x)

TODO: write docs

CDR

(CDR x)

Return the item indicated by the second pointer of a pair. NIL is treated specially: the CDR of NIL is NIL.

CONS

(CONS car cdr)

Construct a new instance of cons cell with this car and cdr.

CONSP

(CONSP o)

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.

DEFINE

(DEFINE a-list)

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.

DEFLIST

(DEFLIST a-list indicator)

For each pair in this association list a-list, set the property with this indicator of the symbol which is the first element of the pair to the value which is the second element of the pair. See page 58 of the manual.

DIFFERENCE

(DIFFERENCE x y)

TODO: write docs

DOC

(DOC symbol)

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.

EQ

(EQ x y)

Returns T if and only if both x and y are bound to the same atom, else NIL.

EQUAL

(EQUAL x y)

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

ERROR

(ERROR & args)

Throw an error

FIXP

(FIXP x)

TODO: write docs

GENSYM

(GENSYM)

Generate a unique symbol.

GET

(GET symbol indicator)

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

(GREATERP x y)

TODO: write docs

-

hit-or-miss-assoc

(hit-or-miss-assoc target plist)

Find the position of the binding of this target in a Lisp 1.5 property list plist.

-

Lisp 1.5 property lists are not assoc lists, but lists of the form (name value name value name value...). It’s therefore necessary to recurse down the list two entries at a time to avoid confusing names with values.

-

lax?

(lax? symbol)

Are we in lax mode? If so. return true; is not, throw an exception with this symbol.

-

LESSP

(LESSP x y)

TODO: write docs

-

LIST

(LIST & args)

TODO: write docs

-

magic-marker

The unexplained magic number which marks the start of a property list.

-

NILP

macro

(NILP x)

Not part of LISP 1.5: T if o is NIL, else NIL.

-

NULL

macro

(NULL x)

Returns T if and only if the argument x is bound to NIL; else F.

-

NUMBERP

(NUMBERP x)

TODO: write docs

-

OBLIST

(OBLIST)

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.

-

OR

(OR & args)

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.

-

PAIRLIS

(PAIRLIS x y a)

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.

+

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

(GREATERP x y)

TODO: write docs

lax?

(lax? symbol)

Are we in lax mode? If so. return true; is not, throw an exception with this symbol.

LESSP

(LESSP x y)

TODO: write docs

LIST

(LIST & args)

TODO: write docs

magic-marker

The unexplained magic number which marks the start of a property list.

NILP

macro

(NILP x)

Not part of LISP 1.5: T if o is NIL, else NIL.

NULL

macro

(NULL x)

Returns T if and only if the argument x is bound to NIL; else F.

NUMBERP

(NUMBERP x)

TODO: write docs

OBLIST

(OBLIST)

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.

OR

(OR & args)

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.

PAIRLIS

(PAIRLIS x y a)

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.

-

PLUS

(PLUS & args)

TODO: write docs

-

PUT

(PUT symbol indicator value)

Put this value as the value of the property indicated by this indicator of this symbol. Return value on success.

-

NOTE THAT there is no PUT defined in the manual, but it would have been easy to have defined it so I don’t think this fully counts as an extension.

-

QUOTIENT

(QUOTIENT x y)

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.

-

REMAINDER

(REMAINDER x y)

TODO: write docs

-

RPLACA

(RPLACA cell value)

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

(RPLACD cell value)

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)

-

SET

(SET symbol val)

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

(SUB1 x)

TODO: write docs

-

TIMES

(TIMES & args)

TODO: write docs

-

TRACE

(TRACE s)

Add this s to the set of symbols currently being traced. If s is not a symbol or sequence of symbols, does nothing.

-

traced-symbols

Symbols currently being traced.

-

traced?

(traced? s)

Return true iff s is a symbol currently being traced, else nil.

-

uaf

(uaf l path)

Universal access function; l is expected to be an arbitrary LISP list, path a (clojure) list of the characters a and d. Intended to make declaring all those fiddly #'c[ad]+r' functions a bit easier

-

UNTRACE

(UNTRACE s)

Remove this s from the set of symbols currently being traced. If s is not a symbol or sequence of symbols, does nothing.

-
\ No newline at end of file +

NOTE THAT this function is overridden by an implementation in Lisp, but is currently still present for bootstrapping.

PLUS

(PLUS & args)

TODO: write docs

PUT

(PUT symbol indicator value)

Put this value as the value of the property indicated by this indicator of this symbol. Return value on success.

+

NOTE THAT there is no PUT defined in the manual, but it would have been easy to have defined it so I don’t think this fully counts as an extension.

QUOTIENT

(QUOTIENT x y)

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.

REMAINDER

(REMAINDER x y)

TODO: write docs

RPLACA

(RPLACA cell value)

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

(RPLACD cell value)

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)

SET

(SET symbol val)

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

(SUB1 x)

TODO: write docs

TIMES

(TIMES & args)

TODO: write docs

TRACE

(TRACE s)

Add this s to the set of symbols currently being traced. If s is not a symbol or sequence of symbols, does nothing.

traced-symbols

Symbols currently being traced.

traced?

(traced? s)

Return true iff s is a symbol currently being traced, else nil.

uaf

(uaf l path)

Universal access function; l is expected to be an arbitrary LISP list, path a (clojure) list of the characters a and d. Intended to make declaring all those fiddly #'c[ad]+r' functions a bit easier

UNTRACE

(UNTRACE s)

Remove this s from the set of symbols currently being traced. If s is not a symbol or sequence of symbols, does nothing.

\ No newline at end of file diff --git a/docs/codox/beowulf.interop.html b/docs/codox/beowulf.interop.html index 8d4ec99..cd46169 100644 --- a/docs/codox/beowulf.interop.html +++ b/docs/codox/beowulf.interop.html @@ -1,17 +1,11 @@ -beowulf.interop documentation

beowulf.interop

TODO: write docs

-

INTEROP

(INTEROP fn-symbol args)

Clojure (or other host environment) interoperation API. fn-symbol is expected to be either

+beowulf.interop documentation

beowulf.interop

TODO: write docs

INTEROP

(INTEROP fn-symbol args)

Clojure (or other host environment) interoperation API. fn-symbol is expected to be either

    -
  1. a symbol bound in the host environment to a function; or
  2. -
  3. a sequence (list) of symbols forming a qualified path name bound to a function.
  4. +
  5. a symbol bound in the host environment to a function; or
  6. +
  7. a sequence (list) of symbols forming a qualified path name bound to a function.

Lower case characters cannot normally be represented in Lisp 1.5, so both the upper case and lower case variants of fn-symbol will be tried. If the function you’re looking for has a mixed case name, that is not currently accessible.

args is expected to be a Lisp 1.5 list of arguments to be passed to that function. Return value must be something acceptable to Lisp 1.5, so either a symbol, a number, or a Lisp 1.5 list.

-

If fn-symbol is not found (even when cast to lower case), or is not a function, or the value returned cannot be represented in Lisp 1.5, an exception is thrown with :cause bound to :interop and :detail set to a value representing the actual problem.

-

interpret-qualified-name

(interpret-qualified-name l)

For interoperation with Clojure, it will often be necessary to pass qualified names that are not representable in Lisp 1.5. This function takes a sequence in the form (PART PART PART... NAME) and returns a symbol in the form part.part.part/NAME. This symbol will then be tried in both that form and lower-cased. Names with hyphens or underscores cannot be represented with this scheme.

-

listify-qualified-name

(listify-qualified-name subr)

We need to be able to print something we can link to the particular Clojure function subr in a form in which Lisp 1.5 is able to read it back in and relink it.

-

This assumes subr is either 1. a string in the format #'beowulf.io/SYSIN or beowulf.io/SYSIN; or 2. something which, when coerced to a string with str, will have such a format.

-

to-beowulf

(to-beowulf o)

Return a beowulf-native representation of the Clojure object o. Numbers and symbols are unaffected. Collections have to be converted; strings must be converted to symbols.

-

to-clojure

(to-clojure l)

If l is a beowulf.cons_cell.ConsCell, return a Clojure list having the same members in the same order.

-
\ No newline at end of file +

If fn-symbol is not found (even when cast to lower case), or is not a function, or the value returned cannot be represented in Lisp 1.5, an exception is thrown with :cause bound to :interop and :detail set to a value representing the actual problem.

interpret-qualified-name

(interpret-qualified-name l)

For interoperation with Clojure, it will often be necessary to pass qualified names that are not representable in Lisp 1.5. This function takes a sequence in the form (PART PART PART... NAME) and returns a symbol in the form part.part.part/NAME. This symbol will then be tried in both that form and lower-cased. Names with hyphens or underscores cannot be represented with this scheme.

listify-qualified-name

(listify-qualified-name subr)

We need to be able to print something we can link to the particular Clojure function subr in a form in which Lisp 1.5 is able to read it back in and relink it.

+

This assumes subr is either 1. a string in the format #'beowulf.io/SYSIN or beowulf.io/SYSIN; or 2. something which, when coerced to a string with str, will have such a format.

to-beowulf

(to-beowulf o)

Return a beowulf-native representation of the Clojure object o. Numbers and symbols are unaffected. Collections have to be converted; strings must be converted to symbols.

to-clojure

(to-clojure l)

If l is a beowulf.cons_cell.ConsCell, return a Clojure list having the same members in the same order.

\ No newline at end of file diff --git a/docs/codox/beowulf.io.html b/docs/codox/beowulf.io.html index b0aa1ea..d5bae54 100644 --- a/docs/codox/beowulf.io.html +++ b/docs/codox/beowulf.io.html @@ -1,19 +1,13 @@ -beowulf.io documentation

beowulf.io

Non-standard extensions to Lisp 1.5 to read and write to the filesystem.

+beowulf.io documentation

beowulf.io

Non-standard extensions to Lisp 1.5 to read and write to the filesystem.

Lisp 1.5 had only READ, which read one S-Expression at a time, and various forms of PRIN* functions, which printed to the line printer. There was also PUNCH, which wrote to a card punch. It does not seem that there was any concept of an interactive terminal.

-

See Appendix E, OVERLORD - THE MONITOR, and Appendix F, LISP INPUT AND OUTPUT.

+

See Appendix E, OVERLORD - THE MONITOR, and Appendix F, LISP INPUT +AND OUTPUT.

For our purposes, to save the current state of the Lisp system it should be sufficient to print the current contents of the oblist to file; and to restore a previous state from file, to overwrite the contents of the oblist with data from that file.

-

Hence functions SYSOUT and SYSIN, which do just that.

-

default-sysout

TODO: write docs

-

resolve-subr

(resolve-subr entry)(resolve-subr entry prop)

If this oblist entry references a subroutine, attempt to fix up that reference.

-

safely-wrap-subr

(safely-wrap-subr entry)

TODO: write docs

-

safely-wrap-subrs

(safely-wrap-subrs objects)

TODO: write docs

-

SYSIN

(SYSIN)(SYSIN filename)

Read the contents of the file at this filename into the object list.

+

Hence functions SYSOUT and SYSIN, which do just that.

default-sysout

TODO: write docs

resolve-subr

(resolve-subr entry)(resolve-subr entry prop)

If this oblist entry references a subroutine, attempt to fix up that reference.

safely-wrap-subr

(safely-wrap-subr entry)

TODO: write docs

safely-wrap-subrs

(safely-wrap-subrs objects)

TODO: write docs

SYSIN

(SYSIN)(SYSIN filename)

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

(SYSOUT)(SYSOUT filepath)

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.

-
\ No newline at end of file +

NOTE THAT this is an extension function, not available in strct mode.

SYSOUT

(SYSOUT)(SYSOUT filepath)

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.

\ No newline at end of file diff --git a/docs/codox/beowulf.manual.html b/docs/codox/beowulf.manual.html index b38f14c..cd01906 100644 --- a/docs/codox/beowulf.manual.html +++ b/docs/codox/beowulf.manual.html @@ -1,8 +1,3 @@ -beowulf.manual documentation

beowulf.manual

Experimental code for accessing the manual online.

-

*manual-url*

dynamic

TODO: write docs

-

format-page-references

(format-page-references fn-symbol)

Format page references from the manual index for the function whose name is fn-symbol.

-

index

This is data extracted from the index pages of Lisp 1.5 Programmer's Manual. It’s here in the hope that we can automatically link to an online PDF link to the manual when the user invokes a function probably called DOC or HELP.

-

page-url

(page-url page-no)

Format the URL for the page in the manual with this page-no.

-
\ No newline at end of file +beowulf.manual documentation

beowulf.manual

Experimental code for accessing the manual online.

*manual-url*

dynamic

TODO: write docs

format-page-references

(format-page-references fn-symbol)

Format page references from the manual index for the function whose name is fn-symbol.

index

This is data extracted from the index pages of Lisp 1.5 Programmer's Manual. It’s here in the hope that we can automatically link to an online PDF link to the manual when the user invokes a function probably called DOC or HELP.

page-url

(page-url page-no)

Format the URL for the page in the manual with this page-no.

\ No newline at end of file diff --git a/docs/codox/beowulf.oblist.html b/docs/codox/beowulf.oblist.html index a468d75..74f48e7 100644 --- a/docs/codox/beowulf.oblist.html +++ b/docs/codox/beowulf.oblist.html @@ -1,9 +1,5 @@ -beowulf.oblist documentation

beowulf.oblist

A namespace mainly devoted to the object list and other top level global variables.

-

Yes, this makes little sense, but if you put them anywhere else you end up in cyclic dependency hell.

-

*options*

dynamic

Command line options from invocation.

-

NIL

The canonical empty list symbol.

-

TODO: this doesn’t really work, because (from Clojure) (empty? NIL) throws an exception. It might be better to subclass beowulf.cons_cell.ConsCell to create a new singleton class Nil which overrides the empty method of IPersistentCollection?

-

oblist

The default environment.

-
\ No newline at end of file +beowulf.oblist documentation

beowulf.oblist

A namespace mainly devoted to the object list and other top level global variables.

+

Yes, this makes little sense, but if you put them anywhere else you end up in cyclic dependency hell.

*options*

dynamic

Command line options from invocation.

NIL

The canonical empty list symbol.

+

TODO: this doesn’t really work, because (from Clojure) (empty? NIL) throws an exception. It might be better to subclass beowulf.cons_cell.ConsCell to create a new singleton class Nil which overrides the empty method of IPersistentCollection?

oblist

The default environment.

\ No newline at end of file diff --git a/docs/codox/beowulf.read.html b/docs/codox/beowulf.read.html index 0e9c56b..cfa7e94 100644 --- a/docs/codox/beowulf.read.html +++ b/docs/codox/beowulf.read.html @@ -1,16 +1,9 @@ -beowulf.read documentation

beowulf.read

This provides the reader required for boostrapping. It’s not a bad reader - it provides feedback on errors found in the input - but it isn’t the real Lisp reader.

+beowulf.read documentation

beowulf.read

This provides the reader required for boostrapping. It’s not a bad reader - it provides feedback on errors found in the input - but it isn’t the real Lisp reader.

Intended deviations from the behaviour of the real Lisp reader are as follows:

    -
  1. It reads the meta-expression language MEXPR in addition to the symbolic expression language SEXPR, which I do not believe the Lisp 1.5 reader ever did;
  2. -
  3. It treats everything between a double semi-colon and an end of line as a comment, as most modern Lisps do; but I do not believe Lisp 1.5 had this feature.
  4. +
  5. It reads the meta-expression language MEXPR in addition to the symbolic expression language SEXPR, which I do not believe the Lisp 1.5 reader ever did;
  6. +
  7. It treats everything between a double semi-colon and an end of line as a comment, as most modern Lisps do; but I do not believe Lisp 1.5 had this feature.
-

Both these extensions can be disabled by using the --strict command line switch.

-

gsp

(gsp s)

Shortcut macro - the internals of read; or, if you like, read-string. Argument s should be a string representation of a valid Lisp expression.

-

number-lines

(number-lines s)(number-lines s e)

TODO: write docs

-

READ

(READ)(READ input)

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.

-

read-from-console

(read-from-console prompt)

Attempt to read a complete lisp expression from the console.

-

There’s a major problem here that the read-chars reader messes up testing. We need to be able to disable it while testing!

-

strip-line-comments

(strip-line-comments s)

Strip blank lines and comment lines from this string s, expected to be Lisp source.

-
\ No newline at end of file +

Both these extensions can be disabled by using the --strict command line switch.

gsp

(gsp s)

Shortcut macro - the internals of read; or, if you like, read-string. Argument s should be a string representation of a valid Lisp expression.

number-lines

(number-lines s)(number-lines s e)

TODO: write docs

READ

(READ)(READ input)

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.

read-from-console

(read-from-console)

Attempt to read a complete lisp expression from the console. NOTE that this will only really work for S-Expressions, not M-Expressions.

strip-line-comments

(strip-line-comments s)

Strip blank lines and comment lines from this string s, expected to be Lisp source.

\ No newline at end of file diff --git a/docs/codox/beowulf.reader.char-reader.html b/docs/codox/beowulf.reader.char-reader.html index d5a0f22..a014787 100644 --- a/docs/codox/beowulf.reader.char-reader.html +++ b/docs/codox/beowulf.reader.char-reader.html @@ -1,20 +1,13 @@ -beowulf.reader.char-reader documentation

beowulf.reader.char-reader

Provide sensible line editing, auto completion, and history recall.

+beowulf.reader.char-reader documentation

beowulf.reader.char-reader

Provide sensible line editing, auto completion, and history recall.

None of what’s needed here is really working yet, and a pull request with a working implementation would be greatly welcomed.

-

What’s needed (rough specification)

+

What’s needed (rough specification)

    -
  1. Carriage return does not cause input to be returned, unless a. the number of open brackets ( and closing brackets ) match; and b. the number of open square brackets [ and closing square brackets ] also match;
  2. -
  3. aborts editing and returns the string STOP;
  4. -
  5. and scroll back and forward through history, but ideally I’d like this to be the Lisp history (i.e. the history of S-Expressions actually read by READ, rather than the strings which were supplied to READ);
  6. -
  7. offers potential auto-completions taken from the value of (OBLIST), ideally the current value, not the value at the time the session started;
  8. -
  9. and offer movement and editing within the line.
  10. +
  11. Carriage return does not cause input to be returned, unless a. the number of open brackets ( and closing brackets ) match; and b. the number of open square brackets [ and closing square brackets ] also match;
  12. +
  13. aborts editing and returns the string STOP;
  14. +
  15. and scroll back and forward through history, but ideally I’d like this to be the Lisp history (i.e. the history of S-Expressions actually read by READ, rather than the strings which were supplied to READ);
  16. +
  17. offers potential auto-completions taken from the value of (OBLIST), ideally the current value, not the value at the time the session started;
  18. +
  19. and offer movement and editing within the line.
-

TODO: There are multiple problems with JLine; a better solution might be to start from here: https://stackoverflow.com/questions/7931988/how-to-manipulate-control-characters

-

build-completer

(build-completer)

Build a completer which takes tokens from the oblist.

-

This is sort-of working, in as much as hitting on a blank line will show a table of values from the oblist, but hitting after you’ve started input does not show potential completions for tokens you’ve started.

-

get-reader

Return a reader, first constructing it if necessary.

-

NOTE THAT this is not settled API. The existence and call signature of this function is not guaranteed in future versions.

-

read-chars

(read-chars prompt)

A drop-in replacement for clojure.core/read-line, except that line editing and history should be enabled.

-

NOTE THAT this does not fully work yet, but it is in the API because I hope that it will work later!

-
\ No newline at end of file +

TODO: There are multiple problems with JLine; a better solution might be to start from here: https://stackoverflow.com/questions/7931988/how-to-manipulate-control-characters

\ No newline at end of file diff --git a/docs/codox/beowulf.reader.generate.html b/docs/codox/beowulf.reader.generate.html index 0325184..58d4e15 100644 --- a/docs/codox/beowulf.reader.generate.html +++ b/docs/codox/beowulf.reader.generate.html @@ -1,13 +1,13 @@ -beowulf.reader.generate documentation

beowulf.reader.generate

Generating S-Expressions from parse trees.

-

From Lisp 1.5 Programmers Manual, page 10

+beowulf.reader.generate documentation

beowulf.reader.generate

Generating S-Expressions from parse trees.

+

From Lisp 1.5 Programmers Manual, page 10

Note that I’ve retyped much of this, since copy/pasting out of PDF is less than reliable. Any typos are mine.

Quote starts:

We are now in a position to define the universal LISP function evalquote[fn;args], When evalquote is given a function and a list of arguments for that function, it computes the value of the function applied to the arguments. LISP functions have S-expressions as arguments. In particular, the argument fn of the function evalquote must be an S-expression. Since we have been writing functions as M-expressions, it is necessary to translate them into S-expressions.

-

The following rules define a method of translating functions written in the meta-language into S-expressions. 1. If the function is represented by its name, it is translated by changing all of the letters to upper case, making it an atomic symbol. Thus car is translated to CAR. 2. If the function uses the lambda notation, then the expression λ[[x ..;xn]; ε] is translated into (LAMBDA (X1 ...XN) ε*), where ε* is the translation of ε. 3. If the function begins with label, then the translation of label[α;ε] is (LABEL α* ε*).

-

Forms are translated as follows: 1. A variable, like a function name, is translated by using uppercase letters. Thus the translation of var1 is VAR1. 2. The obvious translation of letting a constant translate into itself will not work. Since the translation of x is X, the translation of X must be something else to avoid ambiguity. The solution is to quote it. Thus X is translated into (QUOTE X). 3. The form fn[argl;. ..;argn] is translated into (fn* argl* ...argn*) 4. The conditional expression [pl-el;...;pn-en] is translated into (COND (p1* e1*)...(pn* en*))

-

Examples

+

The following rules define a method of translating functions written in the meta-language into S-expressions. 1. If the function is represented by its name, it is translated by changing all of the letters to upper case, making it an atomic symbol. Thus car is translated to CAR. 2. If the function uses the lambda notation, then the expression λ[[x ..;xn]; ε] is translated into (LAMBDA (X1 ...XN) ε*), where ε* is the translation of ε. 3. If the function begins with label, then the translation of label[α;ε] is (LABEL α* ε*).

+

Forms are translated as follows: 1. A variable, like a function name, is translated by using uppercase letters. Thus the translation of var1 is VAR1. 2. The obvious translation of letting a constant translate into itself will not work. Since the translation of x is X, the translation of X must be something else to avoid ambiguity. The solution is to quote it. Thus X is translated into (QUOTE X). 3. The form fn[argl;. ..;argn] is translated into (fn* argl* ...argn*) 4. The conditional expression [pl-el;...;pn-en] is translated into (COND (p1* e1*)...(pn* en*))

+

Examples

  M-expressions                                  S-expressions             
 
   x                                              X                         
@@ -21,15 +21,4 @@
        T->ff[car[x]]]]]                              (COND ((ATOM X) X) 
                                                          ((QUOTE T)(FF (CAR X))))))
 
-

quote ends

-

gen-cond

(gen-cond p context)

Generate a cond statement from this simplified parse tree fragment p; returns nil if p does not represent a (MEXPR) cond statement.

-

gen-cond-clause

(gen-cond-clause p context)

Generate a cond clause from this simplified parse tree fragment p; returns nil if p does not represent a cond clause.

-

gen-dot-terminated-list

(gen-dot-terminated-list p)

Generate a list, which may be dot-terminated, from this partial parse tree ‘p’. Note that the function acts recursively and progressively decapitates its argument, so that the argument will not always be a valid parse tree.

-

gen-fn-call

(gen-fn-call p context)

Generate a function call from this simplified parse tree fragment p; returns nil if p does not represent a (MEXPR) function call.

-

gen-iexpr

(gen-iexpr tree context)

TODO: write docs

-

generate

(generate p)(generate p context)

Generate lisp structure from this parse tree p. It is assumed that p has been simplified.

-

generate-assign

(generate-assign tree context)

Generate an assignment statement based on this tree. If the thing being assigned to is a function signature, then we have to do something different to if it’s an atom.

-

generate-defn

(generate-defn tree context)

TODO: write docs

-

generate-set

(generate-set tree context)

Actually not sure what the mexpr representation of set looks like

-

strip-leading-zeros

(strip-leading-zeros s)(strip-leading-zeros s prefix)

read-string interprets strings with leading zeros as octal; strip any from this string s. If what’s left is empty (i.e. there were only zeros, return "0".

-
\ No newline at end of file +

quote ends

gen-cond

(gen-cond p context)

Generate a cond statement from this simplified parse tree fragment p; returns nil if p does not represent a (MEXPR) cond statement.

gen-cond-clause

(gen-cond-clause p context)

Generate a cond clause from this simplified parse tree fragment p; returns nil if p does not represent a cond clause.

gen-dot-terminated-list

(gen-dot-terminated-list p)

Generate a list, which may be dot-terminated, from this partial parse tree ‘p’. Note that the function acts recursively and progressively decapitates its argument, so that the argument will not always be a valid parse tree.

gen-fn-call

(gen-fn-call p context)

Generate a function call from this simplified parse tree fragment p; returns nil if p does not represent a (MEXPR) function call.

gen-iexpr

(gen-iexpr tree context)

TODO: write docs

generate

(generate p)(generate p context)

Generate lisp structure from this parse tree p. It is assumed that p has been simplified.

generate-assign

(generate-assign tree context)

Generate an assignment statement based on this tree. If the thing being assigned to is a function signature, then we have to do something different to if it’s an atom.

generate-defn

(generate-defn tree context)

TODO: write docs

generate-set

(generate-set tree context)

Actually not sure what the mexpr representation of set looks like

strip-leading-zeros

(strip-leading-zeros s)(strip-leading-zeros s prefix)

read-string interprets strings with leading zeros as octal; strip any from this string s. If what’s left is empty (i.e. there were only zeros, return "0".

\ No newline at end of file diff --git a/docs/codox/beowulf.reader.macros.html b/docs/codox/beowulf.reader.macros.html index 4cd6085..19c4982 100644 --- a/docs/codox/beowulf.reader.macros.html +++ b/docs/codox/beowulf.reader.macros.html @@ -1,8 +1,5 @@ -beowulf.reader.macros documentation

beowulf.reader.macros

Can I implement reader macros? let’s see!

+beowulf.reader.macros documentation

beowulf.reader.macros

Can I implement reader macros? let’s see!

We don’t need (at least, in the Clojure reader) to rewrite forms like 'FOO, because that’s handled by the parser. But we do need to rewrite things which don’t evaluate their arguments, like SETQ, because (unless LABEL does it, which I’m not yet sure of) we’re not yet able to implement things which don’t evaluate arguments.

-

TODO: at this stage, the following should probably also be read macros: DEFINE

-

*readmacros*

dynamic

TODO: write docs

-

expand-macros

(expand-macros form)

TODO: write docs

-
\ No newline at end of file +

TODO: at this stage, the following should probably also be read macros: DEFINE

*readmacros*

dynamic

TODO: write docs

expand-macros

(expand-macros form)

TODO: write docs

\ No newline at end of file diff --git a/docs/codox/beowulf.reader.parser.html b/docs/codox/beowulf.reader.parser.html index 128d2ee..7499c5e 100644 --- a/docs/codox/beowulf.reader.parser.html +++ b/docs/codox/beowulf.reader.parser.html @@ -1,5 +1,3 @@ -beowulf.reader.parser documentation

beowulf.reader.parser

The actual parser, supporting both S-expression and M-expression syntax.

-

parse

Parse a string presented as argument into a parse tree which can then be operated upon further.

-
\ No newline at end of file +beowulf.reader.parser documentation

beowulf.reader.parser

The actual parser, supporting both S-expression and M-expression syntax.

parse

Parse a string presented as argument into a parse tree which can then be operated upon further.

\ No newline at end of file diff --git a/docs/codox/beowulf.reader.simplify.html b/docs/codox/beowulf.reader.simplify.html index f2176ef..52fa75b 100644 --- a/docs/codox/beowulf.reader.simplify.html +++ b/docs/codox/beowulf.reader.simplify.html @@ -1,9 +1,4 @@ -beowulf.reader.simplify documentation

beowulf.reader.simplify

Simplify parse trees. Be aware that this is very tightly coupled with the parser.

-

remove-nesting

(remove-nesting tree context)

TODO: write docs

-

remove-optional-space

(remove-optional-space tree)

TODO: write docs

-

simplify

(simplify p)

Simplify this parse tree p. If p is an instaparse failure object, throw an ex-info, with p as the value of its :failure key. Calls remove-optional-space before processing.

-

simplify-tree

(simplify-tree p)(simplify-tree p context)

Simplify this parse tree p. If p is an instaparse failure object, throw an ex-info, with p as the value of its :failure key.

-

NOTE THAT it is assumed that remove-optional-space has been run on the parse tree BEFORE it is passed to simplify-tree.

-
\ No newline at end of file +beowulf.reader.simplify documentation

beowulf.reader.simplify

Simplify parse trees. Be aware that this is very tightly coupled with the parser.

remove-nesting

(remove-nesting tree context)

TODO: write docs

remove-optional-space

(remove-optional-space tree)

TODO: write docs

simplify

(simplify p)

Simplify this parse tree p. If p is an instaparse failure object, throw an ex-info, with p as the value of its :failure key. Calls remove-optional-space before processing.

simplify-tree

(simplify-tree p)(simplify-tree p context)

Simplify this parse tree p. If p is an instaparse failure object, throw an ex-info, with p as the value of its :failure key.

+

NOTE THAT it is assumed that remove-optional-space has been run on the parse tree BEFORE it is passed to simplify-tree.

\ No newline at end of file diff --git a/docs/codox/beowulf.scratch.html b/docs/codox/beowulf.scratch.html index 3209599..bdd1149 100644 --- a/docs/codox/beowulf.scratch.html +++ b/docs/codox/beowulf.scratch.html @@ -1,8 +1,3 @@ -beowulf.scratch documentation

beowulf.scratch

This namespace is for temporary functions and is intentionally excluded from Git.

-

fns

TODO: write docs

-

interop-listify-q-name

(interop-listify-q-name subr)

We need to be able to print something we can link to the particular Clojure function subr in a form in which Lisp 1.5 is able to read it back in and relink it.

-

This assumes subr is either 1. a string in the format #'beowulf.io/SYSIN or beowulf.io/SYSIN; or 2. something which, when coerced to a string with str, will have such a format.

-

mogrify-plist

(mogrify-plist entry fns)

TODO: write docs

-
\ No newline at end of file +beowulf.scratch documentation

beowulf.scratch

This namespace is for temporary functions and is intentionally excluded from Git.

accessor-body

(accessor-body l v)

TODO: write docs

accessor-symbol

(accessor-symbol l)

Generate a symbol by prepending C and appending A to this list of string fragments l.

accessors-generator

(accessors-generator n)

TODO: write docs

manual-index

TODO: write docs

mogrify-plist

(mogrify-plist entry fns)

TODO: write docs

\ No newline at end of file diff --git a/docs/codox/further_reading.html b/docs/codox/further_reading.html index 236e98a..4ff9617 100644 --- a/docs/codox/further_reading.html +++ b/docs/codox/further_reading.html @@ -1,19 +1,17 @@ -Further Reading

Further Reading

+Further Reading

Further Reading

    -
  1. CODING for the MIT-IBM 704 COMPUTER, October 1957 This paper is not about Lisp. But it is about the particular individual computer on which Lisp was first implemented, and it is written in part by members of the Lisp team. I have found it useful in understanding the software environment in which, and the constraints under which, Lisp was written.
  2. -
  3. MIT AI Memo 1, John McCarthy, September 1958 This is, as far as I can find, the earliest specification document of the Lisp project.
  4. -
  5. Lisp 1 Programmer’s Manual, Phyllis Fox, March 1960
  6. -
  7. Lisp 1.5 Programmer’s Manual, Michael I. Levin, August 1962 This book is essential reading: it documents in some detail the first fully realised Lisp language system.
  8. -
  9. Early LISP History (1956 - 1959), Herbert Stoyan, August 1984
  10. -
  11. -

    The Roots of Lisp, Paul Graham, 2001

    -
  12. -
  13. The Revenge of the Nerds, Paul Graham, 2002 This is mainly about why to use Lisp as a language for modern commercial software, but has useful insights into where it comes from. -
    -

    So the short explanation of why this 1950s language is not obsolete is that it was not technology but math, and math doesn’t get stale.

    -
    -
  14. -
-
\ No newline at end of file +
  • CODING for the MIT-IBM 704 COMPUTER, October 1957 This paper is not about Lisp. But it is about the particular individual computer on which Lisp was first implemented, and it is written in part by members of the Lisp team. I have found it useful in understanding the software environment in which, and the constraints under which, Lisp was written.
  • +
  • MIT AI Memo 1, John McCarthy, September 1958 This is, as far as I can find, the earliest specification document of the Lisp project.
  • +
  • Lisp 1 Programmer’s Manual, Phyllis Fox, March 1960
  • +
  • Lisp 1.5 Programmer’s Manual, Michael I. Levin, August 1962 This book is essential reading: it documents in some detail the first fully realised Lisp language system.
  • +
  • Early LISP History (1956 - 1959), Herbert Stoyan, August 1984
  • +
  • +

    The Roots of Lisp, Paul Graham, 2001

  • +
  • +

    The Revenge of the Nerds, Paul Graham, 2002 This is mainly about why to use Lisp as a language for modern commercial software, but has useful insights into where it comes from.

    +
    +

    So the short explanation of why this 1950s language is not obsolete is that it was not technology but math, and math doesn’t get stale.

    +
  • +
    \ No newline at end of file diff --git a/docs/codox/index.html b/docs/codox/index.html index bd07442..5d09c60 100644 --- a/docs/codox/index.html +++ b/docs/codox/index.html @@ -1,18 +1,3 @@ -Beowulf 0.3.1-SNAPSHOT

    Beowulf 0.3.1-SNAPSHOT

    Released under the GPL-2.0-or-later

    LISP 1.5 is to all Lisp dialects as Beowulf is to English literature.

    Installation

    To install, add the following dependency to your project or build file:

    [beowulf "0.3.1-SNAPSHOT"]

    Topics

    Namespaces

    beowulf.bootstrap

    Lisp as defined in Chapter 1 (pages 1-14) of the Lisp 1.5 Programmer's Manual; that is to say, a very simple Lisp language, which should, I believe, be sufficient in conjunction with the functions provided by beowulf.host, be sufficient to bootstrap the full Lisp 1.5 interpreter..

    -

    beowulf.cons-cell

    The fundamental cons cell on which all Lisp structures are built. Lisp 1.5 lists do not necessarily have a sequence as their CDR, and must have both CAR and CDR mutable, so cannot be implemented on top of Clojure lists.

    -

    beowulf.core

    Essentially, the -main function and the bootstrap read-eval-print loop.

    -

    Public variables and functions:

    beowulf.gendoc

    Generate table of documentation of Lisp symbols and functions.

    -

    beowulf.host

    provides Lisp 1.5 functions which can’t be (or can’t efficiently be) implemented in Lisp 1.5, which therefore need to be implemented in the host language, in this case Clojure.

    -

    beowulf.io

    Non-standard extensions to Lisp 1.5 to read and write to the filesystem.

    -

    beowulf.manual

    Experimental code for accessing the manual online.

    -

    Public variables and functions:

    beowulf.oblist

    A namespace mainly devoted to the object list and other top level global variables.

    -

    Public variables and functions:

    beowulf.read

    This provides the reader required for boostrapping. It’s not a bad reader - it provides feedback on errors found in the input - but it isn’t the real Lisp reader.

    -

    Public variables and functions:

    beowulf.reader.char-reader

    Provide sensible line editing, auto completion, and history recall.

    -

    Public variables and functions:

    beowulf.reader.macros

    Can I implement reader macros? let’s see!

    -

    Public variables and functions:

    beowulf.reader.parser

    The actual parser, supporting both S-expression and M-expression syntax.

    -

    Public variables and functions:

    beowulf.reader.simplify

    Simplify parse trees. Be aware that this is very tightly coupled with the parser.

    -
    \ No newline at end of file +Beowulf 0.3.0

    Beowulf 0.3.0

    Released under the GPL-2.0-or-later

    LISP 1.5 is to all Lisp dialects as Beowulf is to English literature.

    Installation

    To install, add the following dependency to your project or build file:

    [beowulf "0.3.0"]

    Topics

    Namespaces

    beowulf.bootstrap

    Lisp as defined in Chapter 1 (pages 1-14) of the Lisp 1.5 Programmer's Manual; that is to say, a very simple Lisp language, which should, I believe, be sufficient in conjunction with the functions provided by beowulf.host, be sufficient to bootstrap the full Lisp 1.5 interpreter..

    Public variables and functions:

    beowulf.cons-cell

    The fundamental cons cell on which all Lisp structures are built. Lisp 1.5 lists do not necessarily have a sequence as their CDR, and must have both CAR and CDR mutable, so cannot be implemented on top of Clojure lists.

    beowulf.core

    Essentially, the -main function and the bootstrap read-eval-print loop.

    Public variables and functions:

    beowulf.gendoc

    Generate table of documentation of Lisp symbols and functions.

    beowulf.host

    provides Lisp 1.5 functions which can’t be (or can’t efficiently be) implemented in Lisp 1.5, which therefore need to be implemented in the host language, in this case Clojure.

    beowulf.io

    Non-standard extensions to Lisp 1.5 to read and write to the filesystem.

    beowulf.manual

    Experimental code for accessing the manual online.

    Public variables and functions:

    beowulf.oblist

    A namespace mainly devoted to the object list and other top level global variables.

    Public variables and functions:

    beowulf.read

    This provides the reader required for boostrapping. It’s not a bad reader - it provides feedback on errors found in the input - but it isn’t the real Lisp reader.

    Public variables and functions:

    beowulf.reader.char-reader

    Provide sensible line editing, auto completion, and history recall.

    Public variables and functions:

      beowulf.reader.macros

      Can I implement reader macros? let’s see!

      Public variables and functions:

      beowulf.reader.parser

      The actual parser, supporting both S-expression and M-expression syntax.

      Public variables and functions:

      beowulf.reader.simplify

      Simplify parse trees. Be aware that this is very tightly coupled with the parser.

      \ No newline at end of file diff --git a/docs/codox/intro.html b/docs/codox/intro.html index 35c0e33..0875f1d 100644 --- a/docs/codox/intro.html +++ b/docs/codox/intro.html @@ -1,74 +1,66 @@ -beowulf

      beowulf

      -

      Þý liste cræfte spræc

      +beowulf

      beowulf

      +

      Þý liste cræfte spræc

      LISP 1.5 is to all Lisp dialects as Beowulf is to English literature.

      Beowulf logo

      -

      Contents

      +

      Contents

      -

      Table of contents generated with markdown-toc

      -

      What this is

      +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 bahaviour - except as documented below.

      -

      BUT WHY?!!?!

      +

      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.

      -

      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;
      2. -
      3. Ichigo Lisp, in JavaScript, which compiles to Web Assembly;
      4. -
      5. Geert Bosch’s implementation in ADA;
      6. -
      -

      There are probably others.

      -

      In addition, this 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 on which it might be possible to actually run this.

      -

      Status

      +

      Status

      Working Lisp interpreter, but some key features not yet implemented.

      -

      Project Target

      +

      Project Target

      The project target is to be able to run the Wang algorithm for the propositional calculus 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 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

      +

      Invoking

      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)

      Command line arguments as follows:

      @@ -80,171 +72,764 @@ without extensions.

      To end a session, type STOP at the command prompt.

      -

      Building and Invoking

      +

      Building and Invoking

      Build with

      lein uberjar
       
      -

      Reader macros

      +

      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

      +

      Functions and symbols implemented

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      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
      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. 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.
      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 PROGs 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 A3except 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!
      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
      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
      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
      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. 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.
      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 PROGs 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 A3except 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!
      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
      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

      Functions described as ‘Lisp function’ above are defined in the default sysout file, resources/lisp1.5.lsp, which will be loaded by default unless you specify another initfile on the command line.

      Functions described as ‘Host function’ are implemented in Clojure, but if you’re brave you can redefine them in Lisp and the Lisp definitions will take precedence over the Clojure implementations.

      -

      Architectural plan

      +

      Architectural plan

      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

      +

      resources/lisp1.5.lsp

      The objective is to have within resources/lisp1.5.lsp, all those functions defined in the Lisp 1.5 Programmer’s Manual which can be implemented in Lisp.

      This means that, while Beowulf is hosted on Clojure, all that would be required to rehost Lisp 1.5 on a different platform would be to reimplement

        -
      • bootstrap.clj
      • -
      • host.clj
      • -
      • read.clj
      • +
      • bootstrap.clj
      • +
      • host.clj
      • +
      • read.clj

      The objective this is to make it fairly easy to implement Lisp 1.5 on top of any of the many Make A Lisp implementations.

      -

      beowulf/boostrap.clj

      +

      beowulf/boostrap.clj

      This file is essentially Lisp as defined in Chapter 1 (pages 1-14) of the Lisp 1.5 Programmer’s Manual; that is to say, a very simple Lisp language, which should, I believe, be sufficient in conjunction with the functions provided by beowulf.host, to bootstrap the full Lisp 1.5 interpreter.

      In addition it contains the function INTEROP, which allows host language functions to be called from Lisp.

      -

      beowulf/host.clj

      +

      beowulf/host.clj

      This file provides Lisp 1.5 functions which can’t be (or can’t efficiently be) implemented in Lisp 1.5, which therefore need to be implemented in the host language, in this case Clojure.

      -

      beowulf/read.clj

      +

      beowulf/read.clj

      This file provides the reader required for boostrapping. It’s not a bad reader - it provides feedback on errors found in the input - but it isn’t the real Lisp reader.

      Intended deviations from the behaviour of the real Lisp reader are as follows:

        -
      1. It reads the meta-expression language MEXPR in addition to the symbolic expression language SEXPR, which I do not believe the Lisp 1.5 reader ever did;
      2. -
      3. It treats everything between a double semi-colon and an end of line as a comment, as most modern Lisps do; but I do not believe Lisp 1.5 had this feature.
      4. +
      5. It reads the meta-expression language MEXPR in addition to the symbolic expression language SEXPR, which I do not believe the Lisp 1.5 reader ever did;
      6. +
      7. It treats everything between a double semi-colon and an end of line as a comment, as most modern Lisps do; but I do not believe Lisp 1.5 had this feature.
      -

      Commentary

      -

      What’s surprised me in working on this is how much more polished Lisp 1.5 is than legend had led me to believe. The language is remarkably close to Portable Standard Lisp which is in my opinion one of the best and most usable early Lisp implementations.

      +

      Commentary

      +

      What’s surprised me in working on this is how much more polished Lisp 1.5 is than legend had led me to believe. The language is remarkably close to Portable Standard Lisp which is in my opinion one of the best and most usable early Lisp implementations.

      What’s even more surprising is how faithful a reimplementation of Lisp 1.5 the first Lisp dialect I learned, Acornsoft Lisp, turns out to have been.

      I’m convinced you could still use Lisp 1.5 for interesting and useful software (which isn’t to say that modern Lisps aren’t better, but this is software which is almost sixty years old).

      -

      Installation

      +

      Installation

      Download the latest release ‘uberjar’ and run it using:

      -
          java -jar <path name of uberjar>
      +
          java -jar <path name of uberjar>
       

      Or clone the source and build it using:

      -
          lein uberjar`
      +
          lein uberjar`
       

      To build it you will require to have Leiningen installed.

      -

      Input/output

      +

      Input/output

      Lisp 1.5 greatly predates modern computers. It had a facility to print to a line printer, or to punch cards on a punch-card machine, and it had a facility to read system images in from tape; but there’s no file I/O as we would currently understand it, and, because there are no character strings and the valid characters within an atom are limited, it isn’t easy to compose a sensible filename.

      I’ve provided two functions to work around this problem.

      -

      SYSOUT

      +

      SYSOUT

      SYSOUT dumps the global object list to disk as a single S Expression (specifically: an association list). This allows you to persist your session, with all your current work, to disk. The function takes one argument, expected to be a symbol, and, if that argument is provided, writes a file whose name is that symbol with .lsp appended. If no argument is provided, it will construct a filename comprising the token Sysout, followed by the current date, followed by .lsp. In either case the file will be written to the directory given in the FILEPATH argument at startup time, or by default the current directory.

      Obviously, SYSOUT may be called interactively (and this is the expected practice).

      -

      SYSIN

      -

      SYSIN reads a file from disk and overwrites the global object list with its contents. The expected practice is that this will be a file created by SYSOUT. A command line flag --read is provided so that you can specify

      -

      Learning Lisp 1.5

      +

      SYSIN

      +

      SYSIN reads a file from disk and overwrites the global object list with its contents. The expected practice is that this will be a file created by SYSOUT. A command line flag --read is provided so that you can specify

      +

      Learning Lisp 1.5

      The Lisp 1.5 Programmer's Manual is still in print, ISBN 13 978-0-262-13011-0; but it’s also available online.

      -

      Other Lisp 1.5 resources

      +

      Other Lisp 1.5 resources

      The main resource I’m aware of is the Software Preservation Society’s site, here. It has lots of fascinating stuff including full assembler listings for various obsolete processors, but I failed to find the Lisp source of Lisp functions as a text file, which is why resources/lisp1.5.lsp is largely copytyped and reconstructed from the manual.

      -

      Other implementations

      +

      Other implementations

      There’s an online (browser native) Lisp 1.5 implementation here (source code here). It even has a working compiler!

      -

      History resources

      -

      I’m compiling a list of links to historical documents on Lisp 1.5.

      -

      License

      -

      Copyright © 2019 Simon Brooke. Licensed under the GNU General Public License, version 2.0 or (at your option) any later version.

      -
      \ No newline at end of file +

      History resources

      +

      I’m compiling a list of links to historical documents on Lisp 1.5.

      +

      License

      +

      Copyright © 2019 Simon Brooke. Licensed under the GNU General Public License, version 2.0 or (at your option) any later version.

      \ No newline at end of file diff --git a/docs/codox/mexpr.html b/docs/codox/mexpr.html index 376a85c..7692fe9 100644 --- a/docs/codox/mexpr.html +++ b/docs/codox/mexpr.html @@ -1,16 +1,16 @@ -Interpreting M-Expressions

      Interpreting M-Expressions

      +Interpreting M-Expressions

      Interpreting M-Expressions

      M-Expressions (‘mexprs’) are the grammar which John McCarthy origininally used to write Lisp, and the grammar in which many of the function definitions in the Lisp 1.5 Programmer’s Manual are stated. However, I have not seen anywhere a claim that Lisp 1.5 could read M-Expressions, and it is not clear to me whether it was even planned that it should do so, although the discussion on page 10 suggests that it was.

      Rather, it seems to me possible that M-Expressions were only ever a grammar intended to be written on paper, like Backus Naur Form, to describe and to reason about algorithms. I think at the point at which the M-Expression grammar was written, the idea of the universal Lisp function

      I set out to make Beowulf read M-Expressions essentially out of curiousity, to see whether it could be done. I had this idea that if it could be done, I could implement most of Lisp 1.5 simply by copying in the M-Expression definitions out of the manual.

      Consequently, the Beowulf parser can parse the M-Expression grammar as stated in the manual, and generate S-Expressions from it according to the table specified on page 10 of the manual.

      There are two problems with this.

      -

      Problems with interpreting M-Expressions

      -

      Generating idiomatic Lisp

      +

      Problems with interpreting M-Expressions

      +

      Generating idiomatic Lisp

      In the M-Expression notation, a lower case character or sequence of characters represents a variable; an upper case character represents a constant. As the manual says,

      -

      2 . The obvious translation of letting a constant translate into itself will not work. Since the translation of x is X, the translation of X must be something else to avoid ambiguity. The solution is to quote it. Thus X is translated into (QUOTE X).

      +

      2 . The obvious translation of letting a constant translate into itself will not work. Since the translation of x is X, the translation of X must be something else to avoid ambiguity. The solution is to quote it. Thus X is translated into (QUOTE X).

      Thus, necessarily, the translation of a constant must always be quoted. In practice, key constants in Lisp such as T are bound to themselves, so it is idiomatic in Lisp, certainly in the way we have learned to use it, to write, for example,

      (SET (QUOTE NULL) 
      @@ -32,12 +32,11 @@
       

      Is the value of NIL the atom NIL, or is it the empty list ()? If the former, then the translation from the M-Expression above is correct. However, that means that recursive functions which recurse down a list seeking the end will fail. So the latter must be the case.

      NULL is described thus (Ibid, p11):

      -

      This is a predicate useful for deciding when a list is exhausted. It is true if and only if its argument is NIL.

      +

      This is a predicate useful for deciding when a list is exhausted. It is true if and only if its argument is NIL.

      NIL is used explicitly in an M-Expression for example in the definition of intersection (Ibid, p15).

      I think there is an ambiguity in referencing constants which are not bound to themselves in the M-Expression notation as given in the manual. This is particularly problematic with regards to NIL and F, but there may be others instances.

      However, so long as F is bound to NIL, and NIL is also bound to NIL (both of which are true by default, although changeable by the user), and NIL is the special marker used in the CDR of the last cons cell of a flat list, this is a difference which in practice does not make a difference. I still find it worrying, though, that rebinding variables could lead to disaster.

      -

      Curly braces

      +

      Curly braces

      The use of curly braces is not defined in the grammar as stated on page 10. They are not used in the initial definition of APPLY on page 13, but they are used in the more developed restatement on page 70. I believe they are to be read as indicating a section of assembly code to be assembled by the Lisp Assembly Program – but I don’t find the exposition here particularly clear and I’m not sure of this.

      -

      Consequently, the M-Expression interpreter in Beowulf does not interpret curly braces.

      -
      \ No newline at end of file +

      Consequently, the M-Expression interpreter in Beowulf does not interpret curly braces.

      \ No newline at end of file diff --git a/docs/codox/values.html b/docs/codox/values.html index 70fc1ef..1bfdc12 100644 --- a/docs/codox/values.html +++ b/docs/codox/values.html @@ -1,29 +1,27 @@ -The properties of the system, and their values

      The properties of the system, and their values

      -

      here be dragons

      +The properties of the system, and their values

      The properties of the system, and their values

      +

      here be dragons

      Lisp is the list processing language; that is what its name means. It processes data structures built of lists - which may be lists of lists, or lists of numbers, or lists of any other sort of data item provided for by the designers of the system.

      But how is a list, in a computer, actually implemented?

      They’re implemented as pairs, or, as the manual sometimes rather delightfully called them, ‘doublets’. Pairs of what? Pairs of pointers. Of the two pointers of a pair, the first points to the current entry of the list, and the second, by default, points to the remainder of the list, or, if the end of the list has been reached, to a special datum known as NIL which among other things indicates that the end of the list has been reached. The pair itself is normally referred to as a ‘cons cell’ for reasons which are nerdy and not important just now (all right, because they are constructed using a function called cons, which is in itself believed to be simply an abbreviation of ‘construct’).

      Two functions are used to access the two pointers of the cell. In modern Lisps these functions are called first and rest, because a lot of people who aren’t greybeards find these names easier. But they aren’t the original names. The original names were CAR and CDR.

      Why?

      -

      History

      -

      Lisp was originally written on an IBM 704 computer at Massachusetts Institute of Technology, almost seventy years ago.

      +

      History

      +

      Lisp was originally written on an IBM 704 computer at Massachusetts Institute of Technology, almost seventy years ago.

      The machine had registers which were not eight, or sixteen, or thirty two, or sixty four, bits wide, or any other number which would seem rational to modern computer scientists, but thirty six. Myth - folk memory - tells us that the machine’s memory was arranged in pages. As I understand it (but this truly is folk memory) the offset within the page of the word to be fetched was known as the ‘decrement’, while the serial number of the page in the addressing sequence was known as the ‘address’. To fetch a word from memory, you first had to select the page using the ‘address’, and secondly the word itself using the ‘decrement’. So there were specific instructions for selecting the address, and the decrement, from the register separately.

      There were two mnemonics for the machine instructions used to access the content of these registers, respectively:

      -
      CAR
      -
      -

      Contents of the Address part of Register; and

      -
      -
      CDR
      -
      -

      Contents of the Decrement part of Register.

      -
      +
      CAR
      +
      +

      Contents of the Address part of Register; and

      +
      CDR
      +
      +

      Contents of the Decrement part of Register.

      Is this actually true?

      I think so. If you look at page 80 of the Lisp 1 Programmer’s Manual, you will see this:

      -
      TEN                       (the TEN-Mode is entered)
      +
      TEN                       (the TEN-Mode is entered)
       
       O CAR   (((A,B),C)) () \
                               |
      @@ -40,23 +38,23 @@ O14 (read lines O and 1)
       

      Edited 3rd April to add: I’ve found a document, not related to Lisp (although John McCarthy is credited as one of the authors), which does confirm – or strictly, amend – the story. This is the CODING for the MIT-IBM 704 COMPUTER, dating from October 1957. The registers of the 704 were divided into four parts, named respectively the prefix part, the address part, the tag part, and the decrement part, of 3, 15, 3, and 15 bits respectively. The decrement part was not used in addressing; that part of the folklore I was taught isn’t right. But the names are correct. Consider this excerpt :

      -

      The address, tag and decrement parts of symbolic instructions are given in that order. In some cases the decrement, tag or address parts are not necessary; therefore the following combinations where OP represents the instruction abbreviation are permissible.

      +

      The address, tag and decrement parts of symbolic instructions are given in that order. In some cases the decrement, tag or address parts are not necessary; therefore the following combinations where OP represents the instruction abbreviation are permissible.

      This doesn’t prove there were individual machine instructions with the mnemonics CAR and CDR; in fact, I’m going to say with some confidence that there were not, by reference to the table of instructions appended to the same document. The instructions do have three letter mnemonics, and they do use ‘A’ and ‘D’ as abbreviations for ‘address’ and ‘decrement’ respectively, but CAR and CDR are not included.

      So it seems probable that CAR and CDR were labels for subroutines, as I hypothesised above. But they were quite likely pre-existing subroutines, in use before the instantiation of the Lisp project, because they would be generally useful; and the suggestion that they are contractions of ‘contents of the address part’ and ‘contents of the decrement part’, respectively, seem confirmed.

      -

      And, going further down the rabbit hole, there’s this. In 1957, before work on the Lisp project started, McCarthy was writing functions to add list processing to the then-new FORTRAN language, on the very same IBM 704 machine.

      +

      And, going further down the rabbit hole, there’s this. In 1957, before work on the Lisp project started, McCarthy was writing functions to add list processing to the then-new FORTRAN language, on the very same IBM 704 machine.

      -

      in this time any function that delivered integer values had to have a first letter X. Any function (as opposited to subroutines) had to have a last letter F in its name. Therefore the functions selecting parts of the IBM704 memory register (word) were introduced to be XCSRF, XCPRF, XCDRF, XCTRF and XCARF

      +

      in this time any function that delivered integer values had to have a first letter X. Any function (as opposited to subroutines) had to have a last letter F in its name. Therefore the functions selecting parts of the IBM704 memory register (word) were introduced to be XCSRF, XCPRF, XCDRF, XCTRF and XCARF


      I think that the answer has to be that if CAR and CDR had been named by the early Lisp team – John McCarthy and his immediate colleagues – they would not have been named as they were. If not FRST and REST, as in more modern Lisps, then something like P1 and P2. CAR and CDR are distinctive and memorable (and therefore in my opinion worth preserving) because they very specifically name the parts of a cons cell and of nothing else.

      Let’s be clear, here: when CAR and CDR are used in Lisp, they are returning pointers, certainly – but not in the sense that one points to a page and the other to a word. Each is an offset into a cell array, which is almost certainly an array of single 36 bit words held on a single page. So both are in effect being used as decrements. Their use in Lisp is an overload onto their original semantic meaning; they are no longer being used for the purpose for which they are named.

      As far as I can tell, these names first appear in print in 1960, both in the Lisp 1 Programmer’s Manual referenced above, and in McCarthy’s paper Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I. The paper was published in April so was presumably written in 1959

      -

      Grey Anatomy

      -

      The Object List

      +

      Grey Anatomy

      +

      The Object List

      Lisp keeps track of values by associating them with names. It does so by having what is in effect a global registry of all the names it knows to which values are attached. This being a list processing language, that was of course, in early Lisps, a list: a single specialised first class list known as the ‘object list’, or oblist for short.

      -

      Of course, a list need not just be a list of single items, it can be a list of pairs: it can be a list of pairs of the form (name . value). Hold onto that, because I want to talk about another fundamental part of a working Lisp system, the stack.

      -

      The Stack

      +

      Of course, a list need not just be a list of single items, it can be a list of pairs: it can be a list of pairs of the form (name . value). Hold onto that, because I want to talk about another fundamental part of a working Lisp system, the stack.

      +

      The Stack

      Considering the case of pure interpreter first, let’s think about how a function keeps track of the data it’s working on. In order to do its work, it probably calls other functions, to which it passes off data, and they in turn probably call further functions. So, when control returns to our first function, how does it know where its data is? The answer is that each function pushes its argument bindings onto the stack when it starts work, and pops them off again when it exits. So when control returns to a function, its own data is still on the top of the stack. Or, to be precise, actually it doesn’t; in practice the function EVAL does it for each function in turn. But it doesn’t matter: it gets done.

      What is this stack? Well, it’s a list of (name . value) pairs. At least, it is in pure Lisps; Clojure, because it runs on the Java Virtual Machine and interoperates with other software running on the JVM, uses the JVM stack which is a permanently reserved vector of memory never used for anything else. Consequently it cannot be very large; and the consequence of that is that it’s very easy to crash JVM programs because they’ve run out of stack space.

      The advantage of organising your stack as a vector is that on average it’s usually slightly more memory efficient, and that it’s somewhat faster to access. The disadvantage is you need a contiguous block of memory for it, and once you’ve run out, you’ve at best lost both those advantages but in the normal case your program just crashes. Also, the memory you’ve reserved for the stack isn’t available for any other use, even during the most of the time that the stack isn’t using most of it. So of course there’s a temptation to keep the amount reserved for the stack as small as possible.

      @@ -64,7 +62,7 @@ O14 (read lines O and 1)

      The advantage of organising your stack as a list is that, while there is any memory left on the machine at all, you cannot run out of stack.

      ### The Spine

      So, there’s an object list where you associate names and values, and there’s a stack where you associate names and values. But, why do they have to be different? And why do you have to search in two places to find the value of a name?

      -

      The answer is – or it was, and arguably it should be – that you don’t. The stack can simply be pushed onto the front of the object list. This has multiple advantages. The first and most obvious is that you only have to search in one place for the value associated with a name.

      +

      The answer is – or it was, and arguably it should be – that you don’t. The stack can simply be pushed onto the front of the object list. This has multiple advantages. The first and most obvious is that you only have to search in one place for the value associated with a name.

      The second is more subtle: a function can mask a variable in the object list by binding the same name to a new value, and the functions to which it then calls will only see that new value. This is useful if, for example, printed output is usually sent to the user’s terminal, but for a particular operation you want to send it to a line printer or to a file on disk. You simply rebind the name of the standard output stream to your chosen output stream, and call the function whose output you want to redirect.

      So, in summary, there’s a lot of merit in making the stack and the object list into a single central structure on which the architecture of our Lisp system is built. But there’s more we need to record, and it’s important.

      ### Fields and Properties

      @@ -74,35 +72,35 @@ O14 (read lines O and 1)

      But if instead of storing a table for each sort of thing on which we hold data, and a row in that table for each item of that sort on which we store data, we simply tagged each thing on which we hold data with those things which are interesting about them? We could tag my friend Lucy with the fact she’s on pilgrimage, and what her pilgrimage route is. Those aren’t things we need to know about most people, it would be absurdly wasteful to add a column to a person table to record pilgrimage route. So in a conventional data system we would lose that data.

      Lisp has had, right back from the days of Lisp 1.5 – so, for sixty-five years – a different solution. We can give every symbol arbitrarily many, arbitrarily different, properties. A property is a (name . value) pair. We don’t have to store the same properties for every object. The values of the properties don’t have to have a fixed size, and they don’t have to take up space they don’t need. It’s like having a table with as many fields as we choose, and being able to add more fields at any time.

      So, in summary, I knew, in building Beowulf, that I’d have to implement property lists. I just didn’t know how I was going to do it.

      -

      Archaeology

      +

      Archaeology

      What I’m doing with Beowulf is trying to better understand the history of Lisp by reconstructing a very early example; in this case, Lisp 1.5, from about 1962, or sixty one years ago.

      I had had the naive assumption that entries on the object list in early Lisps had their CAR pointing to the symbol and their CDR pointing to the related value. Consequently, in building beowulf, I could not work out where the property list went. More careful reading of the text implies, but does not explicitly state, that my naive assumption is wrong.

      Instead, it appears that the CAR points to the symbol, as expected, but the CDR points to the property list; and that on the property list there are privileged properties at least as follows:

      -
      APVAL
      -
      the simple straightforward ordinary value of the symbol, considered as a variable;
      -
      EXPR
      -
      the definition of the function considered as a normal lambda expression (arguments to be evaluated before applying);
      -
      FEXPR
      -
      the definition of a function which should be applied to unevaluated arguments (what InterLisp and Portable Standard Lisp would call nlambda);
      -
      SUBR
      -
      the definition of a compiled subroutine which should be applied to evaluated arguments;
      -
      FSUBR
      -
      the definition of a compiled subroutine which should be applied to unevaluated arguments.
      +
      APVAL
      +
      the simple straightforward ordinary value of the symbol, considered as a variable;
      +
      EXPR
      +
      the definition of the function considered as a normal lambda expression (arguments to be evaluated before applying);
      +
      FEXPR
      +
      the definition of a function which should be applied to unevaluated arguments (what InterLisp and Portable Standard Lisp would call nlambda);
      +
      SUBR
      +
      the definition of a compiled subroutine which should be applied to evaluated arguments;
      +
      FSUBR
      +
      the definition of a compiled subroutine which should be applied to unevaluated arguments.

      I think there was also another privileged property value which contained the property considered as a constant, but I haven’t yet confirmed that.

      From this it would seem that Lisp 1.5 was not merely a ‘Lisp 2’ but in fact a ‘Lisp 6’, with six effectively first class namespaces. In fact it’s not as bad as that, because of the way EVAL is evaluated.

      Essentially the properties are tried in turn, and only the first value found is used. Thus the heirarchy is

        -
      1. APVAL
      2. -
      3. EXPR
      4. -
      5. FEXPR
      6. -
      7. SUBR
      8. -
      9. FSUBR
      10. +
      11. APVAL
      12. +
      13. EXPR
      14. +
      15. FEXPR
      16. +
      17. SUBR
      18. +
      19. FSUBR

      This means that, while the other potential values can be retrieved from the property list, interpreted definitions (if present) will always be preferred to uninterpreted definitions, and lambda function definitions (which evaluate their arguments), where present, will always be preferred to non-lamda definitions, which don’t.

      BUT NOTE THAT the APVAL value is sought only when seeking a variable value for the symbol, while the others are only when seeking a function value, so Lisp 1.5 is a ‘Lisp 2’, not a ‘Lisp 1’. I strongly believe that this is wrong: a function is a value, and should be treated as such. But at the same time I do acknowledge the benefit of being able to store both source and compiled forms of the function as properties of the same symbol.

      -

      The persistent problem

      +

      The persistent problem

      There’s a view in modern software theory – with which I strongly hold – that data should be immutable. Data that changes under you is the source of all sorts of bugs. And in modern multi threaded systems, the act of reading a datum whilst some other process is writing it, or worse, two processes attempting simultaneously to write the same datum, is a source of data corruption and even crashes. So I’m very wary of mutable data; and, in modern systems where we normally have a great deal of space and a lot of processor power, making fresh copies of data structures containing the change we wanted to make is a reasonable price to pay for avoiding a whole class of bugs.

      But early software was not like that. It was always constrained by the limits of the hardware on which it ran, to a degree that we are not. And the experience that we now have of the problems caused by mutable data, they did not have. So it’s core to the design of Lisp 1.5 that its lists are mutable; and, indeed, one of the biggest challenges in writing Beowulf has been implementing mutable lists in Clojure, a language carefully designed to prevent them.

      But, just because Lisp 1.5 lists can be mutable, should they be? And when should they be?

      @@ -110,50 +108,50 @@ O14 (read lines O and 1)

      What I do at present, and what I think may be good enough, is that each time execution returns to the read-eval-print loop, the REPL, the user’s command line, I rebuild a new execution stack on the top of the oblist as it exists now. So, if the last operation modified the oblist, the next operation will see the new, modified version. But if someone tried to run some persistent program which was writing stuff to property values and hoping to read them back in the same computation, that wouldn’t work, and it would be a very hard bug to trace down.

      So my options are:

        -
      1. To implement PUT and GET in Clojure, so that they can operate on the current copy of the object list, not the one at the base of the stack. I’m slightly unwilling to do that, because my objective is to make Beowulf ultimately as self-hosting as possible.
      2. -
      3. To implement PUT and GET in Lisp, and have them destructively modify the working copy of the object list.
      4. +
      5. To implement PUT and GET in Clojure, so that they can operate on the current copy of the object list, not the one at the base of the stack. I’m slightly unwilling to do that, because my objective is to make Beowulf ultimately as self-hosting as possible.
      6. +
      7. To implement PUT and GET in Lisp, and have them destructively modify the working copy of the object list.

      Neither of these particularly appeal.

      -

      How property lists should work

      -

      I’m still not fully understanding how property lists in Lisp 1.5 are supposed to work.

      -

      List format

      -

      Firstly, are they association lists comprising dotted pairs of (property-name . value), i.e.:

      +

      How property lists should work

      +

      I’m still not fully understanding how property lists in Lisp 1.5 are supposed to work.

      +

      List format

      +

      Firstly, are they association lists comprising dotted pairs of (property-name . value), i.e.:

      -

      ((property-name1 . value1) (property-name2 . value2) … (property-namen . valuen))

      +

      ((property-name1 . value1) (property-name2 . value2) … (property-namen . valuen))

      I have assumed so, and that is what I presently intend to implement, but the diagrams on pages 59 and 60 seem rather to show a flat list of interleaved names and values:

      -

      (property-name1 value1 property-name2 value2 … property-namen valuen)

      +

      (property-name1 value1 property-name2 value2 … property-namen valuen)

      I cannot see what the benefit of this latter arrangement is, and I’m unwilling to do it, although I think it may be what was done. But if it was done that way, why was it done that way? These were bright people, and they certainly knew about association lists. So… I’m puzzled.

      -

      Function signatures

      +

      Function signatures

      To associate the value of a property with a symbol, we need three things: we need the symbol, we need the property name, and we need the value. For this reason, Portable Standard Lisp and others has a function put with three arguments:

      -

      (Put U:id IND:id PROP:any): any The indicator IND with the property PROP is placed on the property list of the id U. If the action of Put occurs, the value of PROP is returned. If either of U and IND are not ids the type mismatch error occurs and no property is placed. (Put 'Jim 'Height 68) The above returns 68 and places (Height . 68) on the property list of the id Jim

      +

      (Put U:id IND:id PROP:any): any The indicator IND with the property PROP is placed on the property list of the id U. If the action of Put occurs, the value of PROP is returned. If either of U and IND are not ids the type mismatch error occurs and no property is placed. (Put 'Jim 'Height 68) The above returns 68 and places (Height . 68) on the property list of the id Jim

      Cambridge Lisp is identical to this except in lower case. InterLisp and several others have putprop:

      -

      (PUTPROP ATM PROP VAL) [Function] Puts the property PROP with value VAL on the property list of ATM. VAL replaces any previous value for the property PROP on this property list. Returns VAL.

      +

      (PUTPROP ATM PROP VAL) [Function] Puts the property PROP with value VAL on the property list of ATM. VAL replaces any previous value for the property PROP on this property list. Returns VAL.

      The execrable Common Lisp uses its execrable macro setf but really the less said about that the better.

      -

      So I was looking for a function of three arguments to set properties, and I didn’t find one.

      +

      So I was looking for a function of three arguments to set properties, and I didn’t find one.

      There’s a function DEFINE which takes one argument, an association list of pairs:

      -
      	(function-name . function-definition)`
      +
      	(function-name . function-definition)`
       

      So how does that work, if what it’s doing is setting properties? If all you’re passing is pairs of name and definition, where does the property name come from?

      The answer is as follows, taken from the manual:

      -

      define x : EXPR pseudo-function

      -

      The argument of define, x, is a list of pairs

      -
      -

      ((ul vl) (u2 v2) … (un vn))

      -
      -

      where each u is a name and each v is a λ-expression for a function . For each pair, define puts an EXPR on the property list for u pointing to v. The function of define puts things on at the front of the property list. The value of define is the list of us.

      +

      define [x] : EXPR pseudo-function

      +

      The argument of define, x, is a list of pairs

      +
      +

      ((ul vl) (u2 v2) … (un vn))

      +
      +

      where each u is a name and each v is a λ-expression for a function . For each pair, define puts an EXPR on the property list for u pointing to v. The function of define puts things on at the front of the property list. The value of define is the list of us.

      So, in fact, the value of the property being set by define is fixed: hard wired, not parameterised. That seems an astonishing decision, until you realise that Lisp 1.5’s creators weren’t creating their functions one by one, in a playful exploration with their system, but entering them in a batch.

      -

      Learning by doing

      +

      Learning by doing

      In fact, when I got over my surprise, I realised that that (name . function-definition) list is actually very much like this, which is an excerpt from a sysout from a Beowulf prototype:

      -
      (...
      +
      (...
       	(MAPLIST LAMBDA (L F) 
                  (COND ((NULL L) NIL) 
                        ((QUOTE T) (CONS (F (CAR L)) (MAPLIST (CDR L) F)))))
      @@ -174,45 +172,81 @@ O14 (read lines O and 1)
       

      Because the point of a sysout is you don’t write it. The point about the REPL – the Read Eval Print Loop which is the heart of the interactive Lisp development cycle, where you sit playing with things and fiddling with them interactively, and where when one thing works you get onto the next without bothering to make some special effort to record it.

      The point of a sysout is that, at the end of the working day, you invoke one function

      - - - - - - + + + + + + + + + + + + + + + + + +
      Function Type Signature Implementation Documentation
      SYSOUT Host function (SYSOUT); (SYSOUT FILEPATH) SUBR 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.
      Function Type Signature Implementation Documentation
      SYSOUT Host function (SYSOUT); (SYSOUT FILEPATH) SUBR 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.

      At the start of the next working day, you load that sysout in and continue your session.

      The sysout captures the entire working state of the machine. No-one types it in, as an operation in itself. Instead, data structures – corpuses of functions among them – simply build up on the object list almost casually, as a side effect of the fact that you’re enjoying exploring your problem and finding elegant ways of solving it. So SYSOUT and SYSIN seem to me, as someone who all his adult life has worked with Lisp interactively, as just an automatic part of the cycle of the day.

      -

      The process of discovery

      -

      The thing is, I don’t think anyone is ever going to use Beowulf the way Lisp 1.5 was used. I mean, probably, no one is ever going to use Beowulf at all; but if they did they wouldn’t use Beowulf the way Lisp 1.5 was used.

      +

      The process of discovery

      +

      The thing is, I don’t think anyone is ever going to use Beowulf the way Lisp 1.5 was used. I mean, probably, no one is ever going to use Beowulf at all; but if they did they wouldn’t use Beowulf the way Lisp 1.5 was used.

      I’m a second generation software person. I have worked, in my career, with two people who personally knew and had worked with Alan Turing. I have worked with, and to an extent been mentored by, Chris Burton, who in his apprenticeship was part of the team that built the Manchester Mark One, and who in his retirement led the team who restored it. But I never knew the working conditions they were accustomed to. In my first year at university we used card punches, and, later, when we had a bit of seniority, teletypewriters (yes, that’s what TTY stands for), but by the time I’d completed my undergraduate degree and become a research associate I had a Xerox 1108 workstation with a huge bitmapped graphic screen, and an optical mouse, goddamit, running InterLisp, all to myself.

      People in the heroic age did not have computers all to themselves. They did not have terminals all to themselves. They didn’t sit at a terminal experimenting in the REPL. They wrote their algorithms in pencil on paper. When they were certain they’d got it right, they’d use a card punch to punch a deck of cards carrying the text of the program, and then they were certain they’d got that right, they’d drop it into the input hopper. Some time later their batch would run, and the operator would put the consequent printout into their pigeon hole for them to collect.

      (They wrote amazingly clean code, those old masters. I could tell you a story about Chris Burton, the train, and the printer driver, that software people of today simply would not believe. But it’s true. And I think that what taught them that discipline was the high cost of even small errors.)

      Lisp 1.5 doesn’t have PUT, PUTPROP or DEFUN because setting properties individually, defining functions individually one at a time, was not something they ever thought about doing. And in learning that, I’ve learned more than I ever expected to about the real nature of Lisp 1.5, and the (great) people who wrote it.


      -

      Deeper delving

      -

      After writing, and publishing, this essay, I went on procrastinating, which is what I do when I’m sure I’m missing something; and to procrastinate, I went on reading the earliest design documents of Lisp I could find. And so I came across the MIT AI team’s first ever memo, written by John McCarthy in September 1958. And in that, I find this:

      +

      Deeper delving

      +

      After writing, and publishing, this essay, I went on procrastinating, which is what I do when I’m sure I’m missing something; and to procrastinate, I went on reading the earliest design documents of Lisp I could find. And so I came across the MIT AI team’s first ever memo, written by John McCarthy in September 1958. And in that, I find this:

      -

      3.2.1. First we have those that extract parts of a 704 word and form a word from parts. We shall distinguish the following parts of a word and indicate each of them by a characteristic letter.

      - - - - - - - - - - - - - -
      Letter Description
      w the whole word
      p the prefix (bits s, 1, 2)
      i the indicator (bits 1 and 2)
      s the sign bit
      d the decrement (bits 3-17)
      t the tag (bits 18-20)
      a the address (bits 21-35)
      +

      3.2.1. First we have those that extract parts of a 704 word and form a word from parts. We shall distinguish the following parts of a word and indicate each of them by a characteristic letter.

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Letter Description
      w the whole word
      p the prefix (bits s, 1, 2)
      i the indicator (bits 1 and 2)
      s the sign bit
      d the decrement (bits 3-17)
      t the tag (bits 18-20)
      a the address (bits 21-35)

      In the discussion of functions which access properties on page 58 of the Lisp 1.5 programmer’s manual, the word ‘indicator’ is used in preference to ‘symbol’ for the name of a property: for example

      -

      The function deflist is a more general defining function. Its first argument is a list of pairs as for define. Its second argument is the indicator that is to be used. After deflist has been executed with (ui vi) among its first argument, the property list of ui will begin:

      -

      If deflist or define is used twice on the same object with the same indicator, the old value will be replaced by the new one.

      +

      The function deflist is a more general defining function. Its first argument is a list of pairs as for define. Its second argument is the indicator that is to be used. After deflist has been executed with (ui vi) among its first argument, the property list of ui will begin:

      +

      If deflist or define is used twice on the same object with the same indicator, the old value will be replaced by the new one.

      (my emphasis).

      That use of ‘indicator’ has been nagging at me for a week. It looks like a term of art. If it’s just an ordinary atomic symbol, why isn’t it called a symbol?

      @@ -220,11 +254,10 @@ O14 (read lines O and 1)

      The reason I don’t think so is that there are only two bits in the indicator part of the word, so only four distinct values; whereas we know that Lisp 1.5 has (at least) five distinct indicator values, APVAL, EXPR, FEXPR, SUBR and FSUBR.

      Furthermore, on page 39, we have:

      -

      A property list is characterized by having the special constant 777778 (i. e., minus 1) as the first element of the list. The rest of the list contains various properties of the atomic symbol. Each property is preceded by an atomic symbol which is called its indicator.

      +

      A property list is characterized by having the special constant 777778 (i. e., minus 1) as the first element of the list. The rest of the list contains various properties of the atomic symbol. Each property is preceded by an atomic symbol which is called its indicator.

      (again, my emphasis)

      But I’m going to hypothesise that the properties were originally intended to be discriminated by the indicator bits in the cons cell, that they were originally coded that way, and that there was some code which depended on property lists being flat lists; and that, when it was discovered that four indicators were not enough and that something else was going to have to be used, the new format of the property list using atomic symbols as indicators was bodged in.


      So what this is about is I’ve spent most of a whole day procrastinating, because I’m not exactly sure how I’m going to make the change I’ve got to make. Versions of Beowulf up to and including 0.2.1 used the naive understanding of the architecture; version 0.3.0 should use the corrected version. But before it can, I need to be reasonably confident that I understand what the correct solution is.

      -

      I shall implement PUT, even though it isn’t in the spec, because it’s a useful building block on which to build DEFINE and DEFLIS, both of which are. And also, because PUT would have been very easy for the Lisp 1.5 implementers to implement, if it had been relevant to their working environment. And I shall implement property list as flat lists of interleaved ‘indicator’ symbols and values, even with that nonsense 777778 as a prefix, because now I know (or think I know) that it was a bodge, it seems right in the spirit of historical reconstruction to reconstruct the bodge.

      -
      \ No newline at end of file +

      I shall implement PUT, even though it isn’t in the spec, because it’s a useful building block on which to build DEFINE and DEFLIS, both of which are. And also, because PUT would have been very easy for the Lisp 1.5 implementers to implement, if it had been relevant to their working environment. And I shall implement property list as flat lists of interleaved ‘indicator’ symbols and values, even with that nonsense 777778 as a prefix, because now I know (or think I know) that it was a bodge, it seems right in the spirit of historical reconstruction to reconstruct the bodge.

      \ No newline at end of file diff --git a/docs/index.html b/docs/index.html deleted file mode 120000 index 2eb3014..0000000 --- a/docs/index.html +++ /dev/null @@ -1 +0,0 @@ -codox/intro.html \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..e54cf99 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,14 @@ + + + + Beowulf: Documentation + + + +

      Beowulf: Documentation

      + + + diff --git a/project.clj b/project.clj index 64742f2..b57eeb4 100644 --- a/project.clj +++ b/project.clj @@ -1,12 +1,17 @@ -(defproject beowulf "0.3.1-SNAPSHOT" +(defproject beowulf "0.3.0" :aot :all :cloverage {:output "docs/cloverage" :ns-exclude-regex [#"beowulf\.gendoc" #"beowulf\.scratch"]} - :codox {:metadata {:doc "**TODO**: write docs" + :codox {:html {:transforms [[:head] [:append + [:link {:rel "icon" + :type "image/x-icon" + :href "../img/beowulf_logo_favicon.png"}]]]} + :metadata {:doc "**TODO**: write docs" :doc/format :markdown} :output-path "docs/codox" :source-uri "https://github.com/simon-brooke/beowulf/blob/master/{filepath}#L{line}" - :themes [:journeyman]} + ;; :themes [:journeyman] + } :description "LISP 1.5 is to all Lisp dialects as Beowulf is to English literature." :license {:name "GPL-2.0-or-later" :url "https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html"} @@ -18,17 +23,15 @@ [clojure.java-time "1.2.0"] [environ "1.2.0"] [instaparse "1.4.12"] - [org.jline/jline "3.23.0"] - [com.github.seancorfield/expectations "2.0.165"] - ;; [rhizome "0.2.9"] ;; not needed in production builds +;; [org.jline/jline "3.23.0"] + [rhizome "0.2.9"] ;; not needed in production builds ] :main beowulf.core :plugins [[lein-cloverage "1.2.2"] - [lein-codox "0.10.8"] + [lein-codox "0.10.7"] [lein-environ "1.1.0"]] :profiles {:jar {:aot :all} - :uberjar {:aot :all} - :dev {:resource-paths ["resources"]}} + :uberjar {:aot :all}} :release-tasks [["vcs" "assert-committed"] ["change" "version" "leiningen.release/bump-version" "release"] ["vcs" "commit"] @@ -39,4 +42,4 @@ ["change" "version" "leiningen.release/bump-version"] ["vcs" "commit"]] :target-path "target/%s" - :url "https://github.com/simon-brooke/beowulf") + :url "https://github.com/simon-brooke/the-great-game") diff --git a/resources/codox/theme/journeyman/css/default.css b/resources/codox/theme/journeyman/css/default.css deleted file mode 100644 index a445e91..0000000 --- a/resources/codox/theme/journeyman/css/default.css +++ /dev/null @@ -1,563 +0,0 @@ -body { - font-family: Helvetica, Arial, sans-serif; - font-size: 15px; - color: limegreen; - background-color: black; -} - -a { - color: lime; -} - -a:active, a:hover { - color: yellowgreen; -} - -a:visited { - color: green; -} - -pre, code { - font-family: Monaco, DejaVu Sans Mono, Consolas, monospace; - font-size: 9pt; - margin: 15px 0; - color: limegreen; - background-color: #111; -} - -h1 { - font-weight: normal; - font-size: 29px; - margin: 10px 0 2px 0; - padding: 0; -} - -h2 { - font-weight: normal; - font-size: 25px; -} - -th, td { - vertical-align: top; -} - -h5.license { - margin: 9px 0 22px 0; - color: lime; - font-weight: normal; - font-size: 12px; - font-style: italic; -} - -.document h1, .namespace-index h1 { - font-size: 32px; - margin-top: 12px; -} - -#header, #content, .sidebar { - position: fixed; -} - -#header { - top: 0; - left: 0; - right: 0; - height: 22px; - color: limegreen; - padding: 5px 7px; -} - -#content { - top: 32px; - right: 0; - bottom: 0; - overflow: auto; - background: black; - color: green; - padding: 0 18px; -} - -.sidebar { - position: fixed; - top: 32px; - bottom: 0; - overflow: auto; -} - -.sidebar.primary { - background: #080808; - border-right: solid 1px forestgreen; - left: 0; - width: 250px; -} - -.sidebar.secondary { - background: #111; - border-right: solid 1px darkgreen; - left: 251px; - width: 200px; -} - -#content.namespace-index, #content.document { - left: 251px; -} - -#content.namespace-docs { - left: 452px; -} - -#content.document { - padding-bottom: 10%; -} - -#header { - background: #080808; - box-shadow: 0 0 8px rgba(192, 255, 192, 0.4); - z-index: 100; -} - -#header h1 { - margin: 0; - padding: 0; - font-size: 18px; - font-weight: lighter; - text-shadow: -1px -1px 0px #333; -} - -#header h1 .project-version { - font-weight: normal; -} - -.project-version { - padding-left: 0.15em; -} - -#header a, .sidebar a { - display: block; - text-decoration: none; -} - -#header h2 { - float: right; - font-size: 9pt; - font-weight: normal; - margin: 4px 3px; - padding: 0; - color: #5f5; -} - -#header h2 a { - display: inline; -} - -.sidebar h3 { - margin: 0; - padding: 10px 13px 0 13px; - font-size: 19px; - font-weight: lighter; -} - -.sidebar h3 a { - color: #4f4; -} - -.sidebar h3.no-link { - color: green; -} - -.sidebar ul { - padding: 7px 0 6px 0; - margin: 0; -} - -.sidebar ul.index-link { - padding-bottom: 4px; -} - -.sidebar li { - display: block; - vertical-align: middle; -} - -.sidebar li a, .sidebar li .no-link { - border-left: 3px solid transparent; - padding: 0 10px; - white-space: nowrap; -} - -.sidebar li .no-link { - display: block; - color: #7F7; - font-style: italic; -} - -.sidebar li .inner { - display: inline-block; - padding-top: 7px; - height: 24px; -} - -.sidebar li a, .sidebar li .tree { - height: 31px; -} - -.depth-1 .inner { padding-left: 2px; } -.depth-2 .inner { padding-left: 6px; } -.depth-3 .inner { padding-left: 20px; } -.depth-4 .inner { padding-left: 34px; } -.depth-5 .inner { padding-left: 48px; } -.depth-6 .inner { padding-left: 62px; } - -.sidebar li .tree { - display: block; - float: left; - position: relative; - top: -10px; - margin: 0 4px 0 0; - padding: 0; -} - -.sidebar li.depth-1 .tree { - display: none; -} - -.sidebar li .tree .top, .sidebar li .tree .bottom { - display: block; - margin: 0; - padding: 0; - width: 7px; -} - -.sidebar li .tree .top { - border-left: 1px solid yellowgreen; - border-bottom: 1px solid yellowgreen; - height: 19px; -} - -.sidebar li .tree .bottom { - height: 22px; -} - -.sidebar li.branch .tree .bottom { - border-left: 1px solid yellowgreen; -} - -.sidebar.primary li.current a { - border-left: 3px solid goldenrod; - color: goldenrod; -} - -.sidebar.secondary li.current a { - border-left: 3px solid yellow; - color: yellow; -} - -.namespace-index h2 { - margin: 30px 0 0 0; -} - -.namespace-index h3 { - font-size: 16px; - font-weight: bold; - margin-bottom: 0; -} - -.namespace-index .topics { - padding-left: 30px; - margin: 11px 0 0 0; -} - -.namespace-index .topics li { - padding: 5px 0; -} - -.namespace-docs h3 { - font-size: 18px; - font-weight: bold; -} - -.public h3 { - margin: 0; - float: left; -} - -.usage { - clear: both; -} - -.public { - margin: 0; - border-top: 1px solid lime; - padding-top: 14px; - padding-bottom: 6px; -} - -.public:last-child { - margin-bottom: 20%; -} - -.members .public:last-child { - margin-bottom: 0; -} - -.members { - margin: 15px 0; -} - -.members h4 { - color: lime; - font-weight: normal; - font-variant: small-caps; - margin: 0 0 5px 0; -} - -.members .inner { - padding-top: 5px; - padding-left: 12px; - margin-top: 2px; - margin-left: 7px; - border-left: 1px solid #5f5; -} - -#content .members .inner h3 { - font-size: 12pt; -} - -.members .public { - border-top: none; - margin-top: 0; - padding-top: 6px; - padding-bottom: 0; -} - -.members .public:first-child { - padding-top: 0; -} - -h4.type, -h4.dynamic, -h4.added, -h4.deprecated { - float: left; - margin: 3px 10px 15px 0; - font-size: 15px; - font-weight: bold; - font-variant: small-caps; -} - -.public h4.type, -.public h4.dynamic, -.public h4.added, -.public h4.deprecated { - font-size: 13px; - font-weight: bold; - margin: 3px 0 0 10px; -} - -.members h4.type, -.members h4.added, -.members h4.deprecated { - margin-top: 1px; -} - -h4.type { - color: #717171; -} - -h4.dynamic { - color: #9933aa; -} - -h4.added { - color: #7acc32; -} - -h4.deprecated { - color: #880000; -} - -.namespace { - margin-bottom: 30px; -} - -.namespace:last-child { - margin-bottom: 10%; -} - -.index { - padding: 0; - font-size: 80%; - margin: 15px 0; - line-height: 16px; -} - -.index * { - display: inline; -} - -.index p { - padding-right: 3px; -} - -.index li { - padding-right: 5px; -} - -.index ul { - padding-left: 0; -} - -.type-sig { - clear: both; - color: goldenrod; -} - -.type-sig pre { - padding-top: 10px; - margin: 0; -} - -.usage code { - display: block; - margin: 2px 0; - color: limegreen; -} - -.usage code:first-child { - padding-top: 10px; -} - -p { - margin: 15px 0; -} - -.public p:first-child, .public pre.plaintext { - margin-top: 12px; -} - -.doc { - margin: 0 0 26px 0; - clear: both; -} - -.public .doc { - margin: 0; -} - -.namespace-index .doc { - margin-bottom: 20px; -} - -.namespace-index .namespace .doc { - margin-bottom: 10px; -} - -.markdown p, .markdown li, .markdown dt, .markdown dd, .markdown td { - line-height: 22px; -} - -.markdown li { - padding: 2px 0; -} - -.markdown h2 { - font-weight: normal; - font-size: 25px; - margin: 30px 0 10px 0; -} - -.markdown h3 { - font-weight: normal; - font-size: 20px; - margin: 30px 0 0 0; -} - -.markdown h4 { - font-size: 15px; - margin: 22px 0 -4px 0; -} - -.doc, .public, .namespace .index { - max-width: 680px; - overflow-x: visible; -} - -.markdown pre > code { - display: block; - padding: 10px; -} - -.markdown pre > code, .src-link a { - border: 1px solid lime; - border-radius: 2px; -} - -.markdown code:not(.hljs), .src-link a { - background: #111; -} - -pre.deps { - display: inline-block; - margin: 0 10px; - border: 1px solid lime; - border-radius: 2px; - padding: 10px; - background-color: #111; -} - -.markdown hr { - border-style: solid; - border-top: none; - color: goldenrod; -} - -.doc ul, .doc ol { - padding-left: 30px; -} - -.doc table { - border-collapse: collapse; - margin: 0 10px; -} - -.doc table td, .doc table th { - border: 1px solid goldenrod; - padding: 4px 6px; -} - -.doc table th { - background: #111; -} - -.doc dl { - margin: 0 10px 20px 10px; -} - -.doc dl dt { - font-weight: bold; - margin: 0; - padding: 3px 0; - border-bottom: 1px solid goldenrod; -} - -.doc dl dd { - padding: 5px 0; - margin: 0 0 5px 10px; -} - -.doc abbr { - border-bottom: 1px dotted goldenrod; - font-variant: none; - cursor: help; -} - -.src-link { - margin-bottom: 15px; -} - -.src-link a { - font-size: 70%; - padding: 1px 4px; - text-decoration: none; - color: lime5bb; -} diff --git a/resources/codox/theme/journeyman/css/highlight.css b/resources/codox/theme/journeyman/css/highlight.css deleted file mode 100644 index d0cdaa3..0000000 --- a/resources/codox/theme/journeyman/css/highlight.css +++ /dev/null @@ -1,97 +0,0 @@ -/* -github.com style (c) Vasily Polovnyov -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - color: #333; - background: #f8f8f8; -} - -.hljs-comment, -.hljs-quote { - color: #998; - font-style: italic; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-subst { - color: #333; - font-weight: bold; -} - -.hljs-number, -.hljs-literal, -.hljs-variable, -.hljs-template-variable, -.hljs-tag .hljs-attr { - color: #008080; -} - -.hljs-string, -.hljs-doctag { - color: #d14; -} - -.hljs-title, -.hljs-section, -.hljs-selector-id { - color: #900; - font-weight: bold; -} - -.hljs-subst { - font-weight: normal; -} - -.hljs-type, -.hljs-class .hljs-title { - color: #458; - font-weight: bold; -} - -.hljs-tag, -.hljs-name, -.hljs-attribute { - color: #000080; - font-weight: normal; -} - -.hljs-regexp, -.hljs-link { - color: #009926; -} - -.hljs-symbol, -.hljs-bullet { - color: #990073; -} - -.hljs-built_in, -.hljs-builtin-name { - color: #0086b3; -} - -.hljs-meta { - color: #999; - font-weight: bold; -} - -.hljs-deletion { - background: #fdd; -} - -.hljs-addition { - background: #dfd; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/resources/codox/theme/journeyman/theme.edn b/resources/codox/theme/journeyman/theme.edn deleted file mode 100644 index 0875f7f..0000000 --- a/resources/codox/theme/journeyman/theme.edn +++ /dev/null @@ -1,11 +0,0 @@ -{:resources ["css/default.css" "css/highlight.css"] - :transforms [[:head] [:append - [:link {:rel "stylesheet" - :type "text/css" - :href "css/default.css"}] - [:link {:rel "stylesheet" - :type "text/css" - :href "css/highlight.css"}] - [:link {:rel "icon" - :type "image/x-icon" - :href "../img/beowulf_logo_favicon.png"}]]]} \ No newline at end of file diff --git a/docs/codox/default.css b/resources/codox/themes/journeyman/css/default.css similarity index 100% rename from docs/codox/default.css rename to resources/codox/themes/journeyman/css/default.css diff --git a/docs/codox/highlight.css b/resources/codox/themes/journeyman/css/highlight.css similarity index 100% rename from docs/codox/highlight.css rename to resources/codox/themes/journeyman/css/highlight.css diff --git a/resources/codox/themes/journeyman/theme.edn b/resources/codox/themes/journeyman/theme.edn new file mode 100644 index 0000000..e1fdd5e --- /dev/null +++ b/resources/codox/themes/journeyman/theme.edn @@ -0,0 +1 @@ +{:resources ["css/default.css" "css/highlight.css"]} \ No newline at end of file diff --git a/resources/lisp1.5.lsp b/resources/lisp1.5.lsp index ad537a5..e56bc7d 100644 --- a/resources/lisp1.5.lsp +++ b/resources/lisp1.5.lsp @@ -25,7 +25,6 @@ (T (ASSOC X (CDR L))))) SUBR (BEOWULF HOST ASSOC)) (ATOM 32767 SUBR (BEOWULF HOST ATOM)) - (ATTRIB 32767 SUBR (BEOWULF HOST ATTRIB)) (CAR 32767 SUBR (BEOWULF HOST CAR)) (CAAAAR 32767 EXPR (LAMBDA (X) (CAR (CAR (CAR (CAR X)))))) (CAAADR 32767 EXPR (LAMBDA (X) (CAR (CAR (CAR (CDR X)))))) @@ -56,16 +55,6 @@ (CDDDR 32767 EXPR (LAMBDA (X) (CDR (CDR (CDR X))))) (CDDR 32767 EXPR (LAMBDA (X) (CDR (CDR X)))) (CDR 32767 SUBR (BEOWULF HOST CDR)) - (CONC - 32767 - FEXPR - (LABEL - ARGS - (COND - ((COND - ((ONEP (LENGTH ARGS)) ARGS) - (T (ATTRIB (CAR ARGS) (APPLY CONC (CDR ARGS) NIL)))) - ARGS)))) (CONS 32767 SUBR (BEOWULF HOST CONS)) (CONSP 32767 SUBR (BEOWULF HOST CONSP)) (COPY @@ -181,7 +170,6 @@ (COND ((NULL X) (U)) ((EQ (CAR X) Y) (CDR X)) (T (PROP (CDR X) Y U))))) - (PUT 32767 SUBR (BEOWULF HOST PUT)) (QUOTE 32767 EXPR (LAMBDA (X) X)) (QUOTIENT 32767 SUBR (BEOWULF HOST QUOTIENT)) (RANGE @@ -199,7 +187,6 @@ (LAMBDA (N X) (COND ((EQ N 0) NIL) (T (CONS X (REPEAT (SUB1 N) X)))))) (RPLACA 32767 SUBR (BEOWULF HOST RPLACA)) (RPLACD 32767 SUBR (BEOWULF HOST RPLACD)) - (SASSOC 32767 SUBR (BEOWULF BOOTSTRAP SASSOC)) (SEARCH 32767 EXPR (LAMBDA (X P F U) (COND ((NULL X) (U X)) diff --git a/resources/sexpr/conc.lsp b/resources/sexpr/conc.lsp index ebd03b1..2738a45 100644 --- a/resources/sexpr/conc.lsp +++ b/resources/sexpr/conc.lsp @@ -1,7 +1 @@ -;; This isn't working but it's really not far off. - -(PUT 'CONC 'FEXPR - ;; possibly ARGS should be (ARGS)... - '(LABEL ARGS - (COND ((COND ((ONEP (LENGTH ARGS)) ARGS) - (T (ATTRIB (CAR ARGS) (APPLY CONC (CDR ARGS) NIL)))) ARGS)))) \ No newline at end of file +;; TODO \ No newline at end of file diff --git a/resources/sexpr/fact.lsp b/resources/sexpr/fact.lsp deleted file mode 100644 index 86b2a97..0000000 --- a/resources/sexpr/fact.lsp +++ /dev/null @@ -1,22 +0,0 @@ -;; Common Lisp - -(defun range (max &key (min 0) (step 1)) - (loop for n from min below max by step - collect n)) - -(mapcar #'(lambda (x) (+ x 1)) (range 10)) - -(defun factoriali (n) - (reduce #'* (range (+ n 1) :min 1 :step 1))) - -(defun factorialr (n) - (cond ((= n 1) 1) - (t (* n (factorialr (- n 1)))))) - - -;; Clojure -(defn factorial [n] - (reduce *' (range 1 (+ n 1)))) - -(defn expt [n x] - (reduce *' (repeat x n))) diff --git a/resources/sexpr/select.lsp b/resources/sexpr/select.lsp deleted file mode 100644 index 4b09439..0000000 --- a/resources/sexpr/select.lsp +++ /dev/null @@ -1,13 +0,0 @@ -;; Bottom of page 66 - -(PUT 'SELECT 'FEXPR - '(LABEL FORM - (PROG (Q BODY) - (SETQ Q (EVAL (CAR FORM))) ;; not sure that Q should be evaled. - (SETQ BODY (CDR FORM)) - LOOP - (COND - ((EQ NIL (CDR BODY)) (RETURN (CAR BODY))) - ((EQ Q (EVAL (CAAR BODY))) (RETURN (CDAR BODY)))) - (SETQ BODY (CDR BODY)) - (GO LOOP)))) \ No newline at end of file diff --git a/src/beowulf/bootstrap.clj b/src/beowulf/bootstrap.clj index 7f4ed1a..d530f62 100644 --- a/src/beowulf/bootstrap.clj +++ b/src/beowulf/bootstrap.clj @@ -11,11 +11,9 @@ objects." (:require [beowulf.cons-cell :refer [F make-beowulf-list make-cons-cell pretty-print T]] - [beowulf.host :refer [ASSOC ATOM CAAR CAADR CADAR CADDR CADR CAR CDR - CONS ERROR GET LIST NUMBERP PAIRLIS traced?]] - [beowulf.oblist :refer [*options* NIL]] - [clojure.string :as s] - [clojure.tools.trace :refer [deftrace]]) + [beowulf.host :refer [ASSOC ATOM CAAR CADAR CADDR CADR CAR CDR GET + LIST NUMBERP PAIRLIS traced?]] + [beowulf.oblist :refer [*options* NIL oblist]]) (:import [beowulf.cons_cell ConsCell] [clojure.lang Symbol])) @@ -39,20 +37,10 @@ ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(declare APPLY EVAL EVCON prog-eval) +(declare APPLY EVAL prog-eval) ;;;; The PROGram feature ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(def ^:dynamic - *depth* - "Stack depth. Unfortunately we need to be able to pass round depth for - functions which call EVAL/APPLY but do not know about depth." - 0) - -(defn- trace-indent - ([] (trace-indent *depth*)) - ([d] (s/join (repeat d " ")))) - (def find-target (memoize (fn [target body] @@ -94,45 +82,30 @@ (cond (number? expr) expr (symbol? expr) (@vars expr) - (instance? ConsCell expr) (case (CAR expr) - COND (prog-cond (CDR expr) + (instance? ConsCell expr) (case (.getCar expr) + COND (prog-cond (.getCdr expr) vars env depth) - GO (let [target (CADR expr)] - (when (traced? 'PROG) - (println " PROG:GO: Goto " target)) - (make-cons-cell - '*PROGGO* target)) - RETURN (let [val (prog-eval - (CADR expr) - vars env depth)] - (when (traced? 'PROG) - (println " PROG:RETURN: Returning " - val)) - (make-cons-cell - '*PROGRETURN* - val)) - SET (let [var (prog-eval (CADR expr) - vars env depth) - val (prog-eval (CADDR expr) - vars env depth)] - (when (traced? 'PROG) - (println " PROG:SET: Setting " - var " to " val)) + GO (make-cons-cell + '*PROGGO* (.getCar (.getCdr expr))) + RETURN (make-cons-cell + '*PROGRETURN* + (prog-eval (.getCar (.getCdr expr)) + vars env depth)) + SET (let [v (CADDR expr)] (swap! vars assoc - var - val) - val) - SETQ (let [var (CADDR expr) - val (prog-eval var - vars env depth)] - (when (traced? 'PROG) - (println " PROG:SETQ: Setting " var " to " val)) + (prog-eval (CADR expr) + vars env depth) + (prog-eval (CADDR expr) + vars env depth)) + v) + SETQ (let [v (CADDR expr)] (swap! vars assoc (CADR expr) - val) - val) + (prog-eval v + vars env depth)) + v) ;; else (beowulf.bootstrap/EVAL expr (merge-vars vars env) @@ -200,7 +173,7 @@ (println "Program:") (pretty-print program))) ;; for debugging (loop [cursor body] - (let [step (if (= NIL cursor) NIL (.getCar cursor))] + (let [step (.getCar cursor)] (when trace (do (println "Executing step: " step) (println " with vars: " @vars))) (cond (= cursor NIL) NIL @@ -233,7 +206,7 @@ with these `args` at this depth." [function-symbol args depth] (when (traced? function-symbol) - (let [indent (trace-indent depth)] + (let [indent (apply str (repeat depth "-"))] (println (str indent "> " function-symbol " " args))))) (defn- trace-response @@ -241,13 +214,11 @@ `function-symbol` at this depth." [function-symbol response depth] (when (traced? function-symbol) - (let [indent (apply str (trace-indent depth))] + (let [indent (apply str (repeat depth "-"))] (println (str "<" indent " " function-symbol " " response)))) response) -;;;; Support functions for interpreter ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn value +(defn- value "Seek a value for this symbol `s` by checking each of these indicators in turn." ([s] @@ -257,21 +228,6 @@ (first (remove #(= % NIL) (map #(GET s %) indicators)))))) -(defn SASSOC - "Like `ASSOC`, but with an action to take if no value is found. - - From the manual, page 60: - - 'The function `sassoc` searches `y`, which is a list of dotted pairs, for - a pair whose first element that is `x`. If such a pair is found, the value - of `sassoc` is this pair. Otherwise the function `u` of no arguments is - taken as the value of `sassoc`.'" - [x y u] - (let [v (ASSOC x y)] - (if-not (= v NIL) v - (APPLY u NIL NIL)))) - - ;;;; APPLY ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn try-resolve-subroutine @@ -292,7 +248,7 @@ return the result." [^Symbol function-symbol args ^ConsCell environment depth] (trace-call function-symbol args depth) - (let [lisp-fn (value function-symbol '(EXPR FEXPR)) ;; <-- should these be handled differently? I think so! + (let [lisp-fn (value function-symbol '(EXPR FEXPR)) args' (cond (= NIL args) args (empty? args) NIL (instance? ConsCell args) args @@ -319,43 +275,6 @@ (trace-response function-symbol result depth) result)) -;; (LABEL ARGS -;; (COND ((COND ((ONEP (LENGTH ARGS)) ARGS) -;; (T (ATTRIB (CAR ARGS) (APPLY CONC (CDR ARGS) NIL)))) -;; ARGS))) -;; ((1 2 3 4) (5 6 7 8) (9 10 11 12)) -;; NIL -;; (def function (make-beowulf-list '(LABEL ARGS (COND -;; ((COND ((ONEP (LENGTH ARGS)) ARGS) -;; (T (ATTRIB (CAR ARGS) -;; (APPLY CONC (CDR ARGS) NIL)))) -;; ARGS))))) -;; (def args (make-beowulf-list '((1 2 3 4) (5 6 7 8) (9 10 11 12)))) - -;; function -;; (CADR function) -;; (CADDR function) - -(defn apply-label - "Apply in the special case that the first element in the function is `LABEL`." - [function args environment depth] - (EVAL - (CADDR function) - (CONS - (CONS (CADR function) args) - environment) - depth)) - -;; (apply-label function args NIL 1) -;; (APPLY function args NIL 1) - -(defn- apply-lambda - "Apply in the special case that the first element in the function is `LAMBDA`." - [function args environment depth] - (EVAL - (CADDR function) - (PAIRLIS (CADR function) args environment) depth)) - (defn APPLY "Apply this `function` to these `arguments` in this `environment` and return the result. @@ -363,34 +282,38 @@ 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." - ([function args environment] - (APPLY function args environment *depth*)) - ([function args environment depth] - (binding [*depth* (inc depth)] - (trace-call 'APPLY (list function args environment) depth) - (let [result (cond - (= NIL function) (if (:strict *options*) - NIL - (throw (ex-info "NIL sí ne þegnung" - {:phase :apply - :function "NIL" - :args args - :type :beowulf}))) - (= (ATOM function) T) (apply-symbolic function args environment (inc depth)) - :else (case (first function) - LABEL (apply-label function args environment depth) - FUNARG (APPLY (CADR function) args (CADDR function) depth) - LAMBDA (apply-lambda function args environment depth) - ;; else - ;; OK, this is *not* what is says in the manual... - ;; COND (EVCON ???) - (throw (ex-info "Ungecnáwen wyrþan sí þegnung-weard" - {:phase :apply - :function function - :args args - :type :beowulf}))))] - (trace-response 'APPLY result depth) - result)))) + [function args environment depth] + (trace-call 'APPLY (list function args environment) depth) + (let [result (cond + (= NIL function) (if (:strict *options*) + NIL + (throw (ex-info "NIL sí ne þegnung" + {:phase :apply + :function "NIL" + :args args + :type :beowulf}))) + (= (ATOM function) T) (apply-symbolic function args environment (inc depth)) + :else (case (first function) + LABEL (APPLY + (CADDR function) + args + (make-cons-cell + (make-cons-cell + (CADR function) + (CADDR function)) + environment) + depth) + FUNARG (APPLY (CADR function) args (CADDR function) depth) + LAMBDA (EVAL + (CADDR function) + (PAIRLIS (CADR function) args environment) depth) + (throw (ex-info "Ungecnáwen wyrþan sí þegnung-weard" + {:phase :apply + :function function + :args args + :type :beowulf}))))] + (trace-response 'APPLY result depth) + result)) ;;;; EVAL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -438,12 +361,12 @@ (println (str indent ": EVAL: sceald bindele: " (or v "nil")))) (if (instance? ConsCell v) (.getCdr v) - (let [v' (value expr)] + (let [v' (value expr (list 'APVAL))] (when (traced? 'EVAL) (println (str indent ": EVAL: deóp bindele: (" expr " . " (or v' "nil") ")"))) (if v' v' - (throw (ex-info (format "Ne tácen-bindele āfand: `%s`" expr) + (throw (ex-info "Ne tácen-bindele āfand" {:phase :eval :function 'EVAL :args (list expr env depth) @@ -489,10 +412,11 @@ (EVLIS (CDR expr) env depth) env depth)) - :else (EVAL (CONS (CDR (SASSOC (CAR expr) env (fn [] (ERROR 'A9)))) - (CDR expr)) - env - (inc depth)))] + :else (APPLY + (CAR expr) + (EVLIS (CDR expr) env depth) + env + depth))] (trace-response 'EVAL result depth) result))) diff --git a/src/beowulf/core.clj b/src/beowulf/core.clj index 74131ff..d20339d 100644 --- a/src/beowulf/core.clj +++ b/src/beowulf/core.clj @@ -53,8 +53,7 @@ (.canRead (io/file %))) "Could not find sysout file"]] ["-s" "--strict" "Strictly interpret the Lisp 1.5 language, without extensions."] - ["-t" "--time" "Time evaluations."] - ["-x" "--testing" "Disable the jline reader - useful when piping input."]]) + ["-t" "--time" "Time evaluations."]]) (defn- re "Like REPL, but it isn't a loop and doesn't print." @@ -64,10 +63,11 @@ (defn repl "Read/eval/print loop." [prompt] - (loop [] + (loop [] + (print prompt) (flush) (try - (if-let [input (trim (read-from-console prompt))] + (if-let [input (trim (read-from-console))] (if (= input stop-word) (throw (ex-info "\nFærwell!" {:cause :quit})) (println @@ -116,7 +116,7 @@ (catch Throwable any (println any)))) (try - (repl (:prompt (:options args))) + (repl (str (:prompt (:options args)) " ")) (catch Exception e diff --git a/src/beowulf/host.clj b/src/beowulf/host.clj index 9c0bdb5..48f622d 100644 --- a/src/beowulf/host.clj +++ b/src/beowulf/host.clj @@ -91,21 +91,22 @@ (cond (= l NIL) NIL (empty? path) l - (not (instance? ConsCell l)) (throw (ex-info (str "Ne liste: " - l "; " (type l)) - {:phase :eval - :function "universal access function" - :args [l path] - :type :beowulf})) - :else (case (last path) - \a (uaf (.first l) (butlast path)) - \d (uaf (.getCdr l) (butlast path)) - (throw (ex-info (str "uaf: unexpected letter in path (only `a` and `d` permitted): " - (last path)) - {:phase :eval - :function "universal access function" - :args [l path] - :type :beowulf}))))) + :else + (try + (case (last path) + \a (uaf (.first l) (butlast path)) + \d (uaf (.getCdr l) (butlast path)) + (throw (ex-info (str "uaf: unexpected letter in path (only `a` and `d` permitted): " (last path)) + {:cause :uaf + :detail :unexpected-letter + :expr (last path)}))) + (catch ClassCastException e + (throw (ex-info + (str "uaf: Not a LISP list? " (type l)) + {:cause :uaf + :detail :not-a-lisp-list + :expr l} + e)))))) (defmacro CAAR [x] `(uaf ~x '(\a \a))) (defmacro CADR [x] `(uaf ~x '(\a \d))) @@ -216,7 +217,7 @@ :phase :host :detail :rplacd :args (list cell value) - :type :beowulf})))) + :type :beowulf}))));; PLUS (defn LIST [& args] @@ -432,41 +433,6 @@ "The unexplained magic number which marks the start of a property list." (Integer/parseInt "77777" 8)) -(defn hit-or-miss-assoc - "Find the position of the binding of this `target` in a Lisp 1.5 - property list `plist`. - - Lisp 1.5 property lists are not assoc lists, but lists of the form - `(name value name value name value...)`. It's therefore necessary to - recurse down the list two entries at a time to avoid confusing names - with values." - [target plist] - (if (and (instance? ConsCell plist) (even? (count plist))) - (cond (= plist NIL) NIL - (= (first plist) target) plist - :else (hit-or-miss-assoc target (CDDR plist))) - NIL)) - -(defn ATTRIB - "Destructive append. From page 59 of the manual: - - 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))))))] - ``` - would put EXPR followed by the LAMBDA expression for FF onto the end of - the property list for FF." - [x e] - (loop [l x] - (cond - (instance? ConsCell (CDR l)) (recur (CDR l)) - :else (when (RPLACD l e) e)))) - (defn PUT "Put this `value` as the value of the property indicated by this `indicator` of this `symbol`. Return `value` on success. @@ -474,27 +440,22 @@ NOTE THAT there is no `PUT` defined in the manual, but it would have been easy to have defined it so I don't think this fully counts as an extension." [symbol indicator value] - (let [binding (ASSOC symbol @oblist)] - (if (instance? ConsCell binding) - (let [prop (hit-or-miss-assoc indicator (CDDR binding))] - (if (instance? ConsCell prop) - (RPLACA (CDR prop) value) - ;; The implication is ATTRIB was used here, but I have not made that - ;; work and this does work, so if it ain't broke don't fix it. - (RPLACD binding - (make-cons-cell - magic-marker - (make-cons-cell - indicator - (make-cons-cell value (CDDR binding))))))) - (swap! - oblist - (fn [ob s p v] - (make-cons-cell - (make-beowulf-list (list s magic-marker p v)) - ob)) - symbol indicator value))) - value) + (if-let [binding (ASSOC symbol @oblist)] + (if-let [prop (ASSOC indicator (CDDR binding))] + (RPLACD prop value) + (RPLACD binding + (make-cons-cell + magic-marker + (make-cons-cell + indicator + (make-cons-cell value (CDDR binding)))))) + (swap! + oblist + (fn [ob s p v] + (make-cons-cell + (make-beowulf-list (list s magic-marker p v)) + ob)) + symbol indicator value))) (defn GET "From the manual: @@ -516,9 +477,13 @@ val (cond (= binding NIL) NIL (= magic-marker - (CADR binding)) (let [p (hit-or-miss-assoc indicator binding)] - (if-not (= NIL p) - (CADR p) + (CADR binding)) (loop [b binding] + ;; (println "GET loop, seeking " indicator ":") + ;; (pretty-print b) + (if (instance? ConsCell b) + (if (= (CAR b) indicator) + (CADR b) ;; <- this is what we should actually be returning + (recur (CDR b))) NIL)) :else (throw (ex-info "Misformatted property list (missing magic marker)" @@ -534,10 +499,9 @@ `indicator` of the symbol which is the first element of the pair to the value which is the second element of the pair. See page 58 of the manual." [a-list indicator] - (doall - (map - #(when (PUT (CAR %) indicator (CDR %)) (CAR %)) - a-list))) + (map + #(PUT (CAR %) indicator (CDR %)) + a-list)) (defn DEFINE "Bootstrap-only version of `DEFINE` which, post boostrap, can be overwritten diff --git a/src/beowulf/io.clj b/src/beowulf/io.clj index d798762..62ead4c 100644 --- a/src/beowulf/io.clj +++ b/src/beowulf/io.clj @@ -91,8 +91,7 @@ ([] (SYSOUT nil)) ([filepath] - (let [destination (full-path (str filepath))] - (spit destination + (spit (full-path (str filepath)) (with-out-str (println (apply str (repeat 79 ";"))) (println (format ";; Beowulf %s Sysout file generated at %s" @@ -104,9 +103,7 @@ (println) (let [output (safely-wrap-subrs @oblist)] (pretty-print output) - ))) - (println "Saved sysout to " destination) - NIL))) + ))))) (defn resolve-subr "If this oblist `entry` references a subroutine, attempt to fix up that diff --git a/src/beowulf/oblist.clj b/src/beowulf/oblist.clj index 8769b7f..38aa999 100644 --- a/src/beowulf/oblist.clj +++ b/src/beowulf/oblist.clj @@ -41,5 +41,5 @@ (def ^:dynamic *options* "Command line options from invocation." - {:testing true}) + {}) diff --git a/src/beowulf/read.clj b/src/beowulf/read.clj index 8ed736a..54fcfe4 100644 --- a/src/beowulf/read.clj +++ b/src/beowulf/read.clj @@ -13,14 +13,13 @@ Both these extensions can be disabled by using the `--strict` command line switch." - (:require [beowulf.oblist :refer [*options*]] - [beowulf.reader.char-reader :refer [read-chars]] + (:require ;; [beowulf.reader.char-reader :refer [read-chars]] [beowulf.reader.generate :refer [generate]] [beowulf.reader.parser :refer [parse]] [beowulf.reader.simplify :refer [simplify]] [clojure.string :refer [join split starts-with? trim]]) - (:import [instaparse.gll Failure] - [java.io InputStream])) + (:import [java.io InputStream] + [instaparse.gll Failure])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; @@ -83,35 +82,24 @@ (throw (ex-info "Ne can forstande " (assoc parse-tree :source source)))) (generate (simplify parse-tree))))) -(defn- dummy-read-chars [prompt] - (loop [r "" p prompt] - (if (and (seq r) - (= (count (re-seq #"\(" r)) - (count (re-seq #"\)" r))) +(defn read-from-console + "Attempt to read a complete lisp expression from the console. NOTE that this + will only really work for S-Expressions, not M-Expressions." + [] + (loop [r (read-line)] + (if (and (= (count (re-seq #"\(" r)) + (count (re-seq #"\)" r))) (= (count (re-seq #"\[" r)) (count (re-seq #"\]" r)))) r - (do - (print (str p " ")) - (flush) - (recur (str r "\n" (read-line)) "::"))))) - -(defn read-from-console - "Attempt to read a complete lisp expression from the console. - - There's a major problem here that the read-chars reader messes up testing. - We need to be able to disable it while testing!" - [prompt] - (if (:testing *options*) - (dummy-read-chars prompt) - (read-chars prompt))) + (recur (str r "\n" (read-line)))))) (defn READ "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." ([] - (gsp (read-from-console (:prompt *options*)))) + (gsp (read-from-console))) ([input] (cond (empty? input) (READ) diff --git a/src/beowulf/reader/char_reader.clj b/src/beowulf/reader/char_reader.clj index a38f301..883f8fa 100644 --- a/src/beowulf/reader/char_reader.clj +++ b/src/beowulf/reader/char_reader.clj @@ -20,12 +20,9 @@ TODO: There are multiple problems with JLine; a better solution might be to start from here: https://stackoverflow.com/questions/7931988/how-to-manipulate-control-characters" - (:require [beowulf.oblist :refer [*options* oblist]]) - (:import [org.jline.reader.impl.completer StringsCompleter] - [org.jline.reader.impl DefaultParser DefaultParser$Bracket] - [org.jline.reader LineReaderBuilder] - [org.jline.terminal TerminalBuilder] - [org.jline.widget AutopairWidgets AutosuggestionWidgets])) + ;; (:import [org.jline.reader LineReader LineReaderBuilder] + ;; [org.jline.terminal TerminalBuilder]) + ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; @@ -52,56 +49,27 @@ ;; looks as though you'd need a DPhil in JLine to write it, and I don't have ;; the time. -(defn build-completer - "Build a completer which takes tokens from the oblist. +;; (def get-reader +;; "Return a reader, first constructing it if necessary. - This is sort-of working, in as much as hitting on a blank line will - show a table of values from the oblist, but hitting after you've - started input does not show potential completions for tokens you've started." - [] - (StringsCompleter. (map #(str (first %)) @oblist))) +;; **NOTE THAT** this is not settled API. The existence and call signature of +;; this function is not guaranteed in future versions." +;; (memoize (fn [] +;; (let [term (.build (.system (TerminalBuilder/builder) true))] +;; (.build (.terminal (LineReaderBuilder/builder) term)))))) -;; This breaks; it is not correctly resolving the Enum, although I can't work out -;; why not. -;; (defn build-parser -;; [] -;; (println "Building parser") -;; (let [parser (DefaultParser.)] -;; (doall -;; (.setEofOnUnclosedBracket -;; parser DefaultParser$Bracket/ROUND)))) - -(def get-reader - "Return a reader, first constructing it if necessary. +;; (defn read-chars +;; "A drop-in replacement for `clojure.core/read-line`, except that line editing +;; and history should be enabled. - **NOTE THAT** this is not settled API. The existence and call signature of - this function is not guaranteed in future versions." - (memoize (fn [] - (let [term (.build (.system (TerminalBuilder/builder) true)) - reader (-> (LineReaderBuilder/builder) - (.terminal term) - (.completer (build-completer)) - ;; #(.parser % (build-parser)) - (.build)) - ;; apw (AutopairWidgets. reader false) - ;; asw (AutosuggestionWidgets. reader) - ] - ;; (.enable apw) - ;; (.enable asw) - reader)))) - -(defn read-chars - "A drop-in replacement for `clojure.core/read-line`, except that line editing - and history should be enabled. - - **NOTE THAT** this does not fully work yet, but it is in the API because I - hope that it will work later!" - [prompt] - (let [eddie (get-reader)] - (loop [s (.readLine eddie (str prompt " "))] - (if (and (= (count (re-seq #"\(" s)) - (count (re-seq #"\)" s))) - (= (count (re-seq #"\[]" s)) - (count (re-seq #"\]" s)))) - s - (recur (str s " " (.readLine eddie ":: "))))))) +;; **NOTE THAT** this does not work yet, but it is in the API because I hope +;; that it will work later!" +;; [] +;; (let [eddie (get-reader)] +;; (loop [s (.readLine eddie)] +;; (if (and (= (count (re-seq #"\(" s)) +;; (count (re-seq #"\)" s))) +;; (= (count (re-seq #"\[]" s)) +;; (count (re-seq #"\]" s)))) +;; s +;; (recur (str s " " (.readLine eddie))))))) \ No newline at end of file diff --git a/test/beowulf/core_test.clj b/test/beowulf/core_test.clj index 9c2029f..81cb86b 100644 --- a/test/beowulf/core_test.clj +++ b/test/beowulf/core_test.clj @@ -1,9 +1,8 @@ (ns beowulf.core-test - (:require [beowulf.core :refer [-main repl stop-word]] - [beowulf.oblist :refer [*options*]] - [clojure.java.io :refer [reader]] - [clojure.string :refer [split starts-with?]] - [clojure.test :refer [deftest is testing]])) + (:require [clojure.java.io :refer [reader]] + [clojure.string :refer [split]] + [clojure.test :refer [deftest is testing]] + [beowulf.core :refer [-main repl stop-word]])) ;; (deftest a-test ;; (testing "FIXME, I fail." @@ -21,43 +20,45 @@ (deftest repl-tests (testing "quit functionality" (with-open [r (reader (string->stream stop-word))] - (binding [clojure.core/*in* r - *options* (assoc *options* :testing true)] + (binding [*in* r] (is (thrown-with-msg? Exception #"\nFærwell!" (repl ""))))) + (let [expected nil actual (with-open [r (reader (string->stream stop-word))] (binding [*in* r] - (-main "--testing")))] + (-main)))] (is (= actual expected))))) -;; The new read-chars interface is really messing with this. Need to sort out! -;; OK, binding `:testing` doesn't work because `*options*` gets rebound in main. -;; Need to be able to pass in a testing flag as argument to -main +;; TODO: not working because STOP is not being recognised, but I haven't +;; worked out why not yet. It *did* work. + (deftest flag-tests - (testing "Only testing flag" + (testing "No flags" (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." expected-quit-message (str "Sprecan '" stop-word "' tó laéfan") expected-result #".*\(3 \. 4\)" expected-prompt "Sprecan:: " expected-signoff "Færwell!" ;; anticipated output (note blank lines): - - ;; Hider wilcuman. Béowulf is mín nama. - - ;; Sprecan 'STOP' tó laéfan - - ;; Sprecan:: > (3 . 4) - ;; Sprecan:: - ;; Færwell! + + ; Hider wilcuman. Béowulf is mín nama. + + ; Sprecan 'STOP' tó laéfan + + ; Sprecan:: > (3 . 4) + ; Sprecan:: + ; Færwell! [_ greeting _ _ quit-message _ result prompt signoff] (with-open [r (reader (string->stream (str "cons[3; 4]\n" stop-word)))] (binding [*in* r] - (split (with-out-str (-main "--testing")) #"\n")))] + (split (with-out-str (-main)) #"\n")))] (is (= greeting expected-greeting)) + ; (is (= error expected-error)) (is (re-matches expected-result result)) (is (= quit-message expected-quit-message)) (is (= prompt expected-prompt)) - (is (= signoff expected-signoff)))) + (is (= signoff expected-signoff)) + )) (testing "unknown flag" (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." expected-quit-message (str "Sprecan '" stop-word "' tó laéfan") @@ -68,63 +69,115 @@ [_ greeting _ error quit-message _ result prompt signoff] (with-open [r (reader (string->stream (str "cons[5; 6]\n" stop-word)))] (binding [*in* r] - (split (with-out-str (-main "--unknown" "--testing")) #"\n")))] + (split (with-out-str (-main "--unknown")) #"\n")))] (is (= greeting expected-greeting)) (is (re-matches expected-error error)) (is (re-matches expected-result result)) (is (= quit-message expected-quit-message)) (is (= prompt expected-prompt)) - (is (= signoff expected-signoff)))) -;; ;; TODO: not working because STOP is not being recognised, but I haven't -;; ;; worked out why not yet. It *did* work. - -;; Hider wilcuman. Béowulf is mín nama. -;; -f FILEPATH, --file-path FILEPATH Set the path to the directory for reading and writing Lisp files. -;; -h, --help -;; -p PROMPT, --prompt PROMPT Sprecan:: Set the REPL prompt to PROMPT -;; -r SYSOUTFILE, --read SYSOUTFILE lisp1.5.lsp Read Lisp system from file SYSOUTFILE -;; -s, --strict Strictly interpret the Lisp 1.5 language, without extensions. -;; -t, --time Time evaluations. -;; -x, --testing Disable the jline reader - useful when piping input. -;; Sprecan 'STOP' tó laéfan - -;; Sprecan:: - - (testing "help" - (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." - expected-h1 " -h, --help" - expected-quit-message (str "Sprecan '" stop-word "' tó laéfan") - expected-result #".*\(A \. B\)" - expected-prompt "Sprecan:: " - expected-signoff "Færwell!" - [_ greeting _ _ h1 _ _ _ _ _ quit-message _ result prompt signoff] - (with-open [r (reader (string->stream (str "cons[A; B]\n" stop-word)))] - (binding [*in* r] - (split (with-out-str (-main "--help" "--testing")) #"\n")))] - (is (= greeting expected-greeting)) - (is (= h1 expected-h1)) - (is (re-matches expected-result result)) - (is (= quit-message expected-quit-message)) - (is (starts-with? prompt expected-prompt)) - (is (= signoff expected-signoff)))) - (testing "prompt" - (let [expected-prompt "? " - [_ _ _ _ _ _ prompt _] - (with-open [r (reader (string->stream stop-word))] - (binding [*in* r] - (split (with-out-str (-main "--prompt" "?" "--testing")) #"\n")))] - (is (= prompt expected-prompt)))) - (testing "read - file not found" - (let [expected-error #"Failed to validate.*" - [_ _ _ error _ _ _ _ _] - (with-open [r (reader (string->stream (str "cons[A; B]\n" stop-word)))] - (binding [*in* r] - (split (with-out-str (-main "--testing" "--read" "froboz")) #"\n")))] - (is (re-matches expected-error error)))) - (testing "strict" - (let [expected-result #".*Cannot parse meta expressions in strict mode.*" - [_ _ _ _ _ _ result _ _] - (with-open [r (reader (string->stream (str "cons[A; B]\n" stop-word)))] - (binding [*in* r] - (split (with-out-str (-main "--strict" "--testing")) #"\n")))] - (is (re-matches expected-result result ))))) + (is (= signoff expected-signoff)) + )) + ; (testing "help" + ; (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." + ; expected-h1 " -h, --help" + ; expected-quit-message (str "Sprecan '" stop-word "' tó laéfan") + ; expected-result #".*\(A \. B\)" + ; expected-prompt "Sprecan:: " + ; expected-signoff "Færwell!" + ; [_ greeting _ h1 _ _ _ _ quit-message _ result prompt signoff] + ; (with-open [r (reader (string->stream (str "cons[A; B]\n" stop-word)))] + ; (binding [*in* r] + ; (split (with-out-str (-main "--help")) #"\n")))] + ; (is (= greeting expected-greeting)) + ; (is (= h1 expected-h1)) + ; (is (re-matches expected-result result)) + ; (is (= quit-message expected-quit-message)) + ; (is (= prompt expected-prompt)) + ; (is (= signoff expected-signoff)) + ; )) + ; (testing "prompt" + ; (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." + ; expected-quit-message (str "Sprecan '" stop-word "' tó laéfan") + ; expected-error "" + ; expected-result #".*\(A \. B\).*" + ; expected-prompt "? " + ; expected-signoff "Færwell!" + ; [_ greeting _ error quit-message _ result prompt signoff] + ; (with-open [r (reader (string->stream (str stop-word)))] + ; (binding [*in* r] + ; (split (with-out-str (-main "--prompt" "?")) #"\n")))] + ; (is (= greeting expected-greeting)) + ; (is (= error expected-error)) + ; (is (re-matches expected-result result )) + ; (is (= quit-message expected-quit-message)) + ; (is (= prompt expected-prompt)) + ; (is (= signoff expected-signoff)) + ; )) + ; (testing "read - file not found" + ; (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." + ; expected-quit-message (str "Sprecan '" stop-word "' tó laéfan") + ; expected-error #"Failed to validate.*" + ; expected-result #".*\(A \. B\)" + ; expected-prompt "Sprecan:: " + ; expected-signoff "Færwell!" + ; [_ greeting _ error quit-message _ result prompt signoff] + ; (with-open [r (reader (string->stream (str "cons[A; B]\n" stop-word)))] + ; (binding [*in* r] + ; (split (with-out-str (-main "--read" "froboz")) #"\n")))] + ; (is (= greeting expected-greeting)) + ; (is (re-matches expected-error error)) + ; (is (re-matches expected-result result)) + ; (is (= quit-message expected-quit-message)) + ; (is (= prompt expected-prompt)) + ; (is (= signoff expected-signoff)) + ; )) + ; (testing "read - file found" + ; ;; TODO: there's no feedback from this because the initfile + ; ;; is not yet read. This will change + ; (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." + ; expected-quit-message (str "Sprecan '" stop-word "' tó laéfan") + ; expected-error "" + ; expected-result #".*\(A \. B\)" + ; expected-prompt "Sprecan:: " + ; expected-signoff "Færwell!" + ; [_ greeting error quit-message _ _ result prompt signoff] + ; (with-open [r (reader (string->stream (str "cons[A; B]\n" stop-word)))] + ; (binding [*in* r] + ; (split (with-out-str (-main "--read" "README.md")) #"\n")))] + ; (is (= greeting expected-greeting)) + ; (is (= error expected-error)) + ; (is (re-matches expected-result result)) + ; (is (= quit-message expected-quit-message)) + ; (is (= prompt expected-prompt)) + ; (is (= signoff expected-signoff)) + ; )) + ; (testing "strict" + ; (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." + ; expected-quit-message (str "Sprecan '" stop-word "' tó laéfan") + ; expected-error "" + ; expected-result #".*Cannot parse meta expressions in strict mode.*" + ; expected-prompt "Sprecan:: " + ; expected-signoff "Færwell!" + ; [_ greeting _ error quit-message _ result prompt signoff] + ; (with-open [r (reader (string->stream (str "cons[A; B]\n" stop-word)))] + ; (binding [*in* r] + ; (split (with-out-str (-main "--strict")) #"\n")))] + ; (is (= greeting expected-greeting)) + ; (is (= error expected-error)) + ; (is (re-matches expected-result result )) + ; (is (= quit-message expected-quit-message)) + ; (is (= prompt expected-prompt)) + ; (is (= signoff expected-signoff)) + ; )) + ; ; (testing "trace" + ; (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." + ; expected-error "" + ; expected-trace #".*traced-eval.*" + ; [_ greeting _ error _ _ trace & _] + ; (with-open [r (reader (string->stream (str "cons[A; B]\n" stop-word)))] + ; (binding [*in* r] + ; (split (with-out-str (-main "--trace")) #"\n")))] + ; (is (= greeting expected-greeting)) + ; (is (= error expected-error)) + ; (is (re-matches expected-trace trace)) +) \ No newline at end of file diff --git a/test/beowulf/host_test.clj b/test/beowulf/host_test.clj index 4d43b1e..7e5e1ff 100644 --- a/test/beowulf/host_test.clj +++ b/test/beowulf/host_test.clj @@ -1,66 +1,38 @@ (ns beowulf.host-test - (:require [beowulf.cons-cell :refer [F make-beowulf-list make-cons-cell T]] - [beowulf.host :refer [ADD1 AND CADDDR CAR CDR DEFINE DIFFERENCE - ERROR FIXP GREATERP lax? LESSP NILP NULL - NUMBERP OR PLUS RPLACA RPLACD SUB1 TIMES uaf]] - [beowulf.io :refer [SYSIN]] - [beowulf.oblist :refer [*options* NIL]] - [beowulf.read :refer [gsp]] - [clojure.test :refer [deftest is testing use-fixtures]] - [expectations.clojure.test - :refer [defexpect expect more-> more-of]])) - -(use-fixtures :once (fn [f] - (try (when (SYSIN "resources/lisp1.5.lsp") - (f)) - (catch Throwable any - (throw (ex-info "Failed to load Lisp sysout" - {:phase test - :function 'SYSIN - :file "resources/lisp1.5.lsp"} - any)))))) + (:require [clojure.test :refer [deftest is testing]] + [beowulf.cons-cell :refer [F make-beowulf-list T]] + [beowulf.host :refer [CDR DIFFERENCE NUMBERP PLUS RPLACA RPLACD TIMES]] + [beowulf.oblist :refer [NIL]] + [beowulf.read :refer [gsp]])) (deftest destructive-change-test (testing "RPLACA" (let - [l (make-beowulf-list '(A B C D E)) - target (CDR l) - expected "(A F C D E)" - actual (do (RPLACA target 'F) (print-str l))] + [l (make-beowulf-list '(A B C D E)) + target (CDR l) + expected "(A F C D E)" + actual (do (RPLACA target 'F) (print-str l))] (is (= actual expected))) (is (thrown-with-msg? - Exception - #"Un-ġefōg þing in RPLACA.*" - (RPLACA (make-beowulf-list '(A B C D E)) "F")) + Exception + #"Un-ġefōg þing in RPLACA.*" + (RPLACA (make-beowulf-list '(A B C D E)) "F")) "You can't represent a string in Lisp 1.5") (is (thrown-with-msg? - Exception - #"Uncynlic miercels in RPLACA.*" - (RPLACA '(A B C D E) 'F)) - "You can't RPLACA into anything which isn't a MutableSequence.")) - (testing "RPLACD" + Exception + #"Uncynlic miercels in RPLACA.*" + (RPLACA '(A B C D E) 'F)) + "You can't RPLACA into anything which isn't a MutableSequence.") + ) + (testing "RPLACA" (let - [l (make-beowulf-list '(A B C D E)) - target (CDR l) - expected "(A B . F)" - actual (do (RPLACD target 'F) (print-str l))] + [l (make-beowulf-list '(A B C D E)) + target (CDR l) + expected "(A B . F)" + actual (do (RPLACD target 'F) (print-str l))] (is (= actual expected))) - (let - [l (make-beowulf-list '(A B C D E)) - target (CDR l) - expected "(A B)" - actual (do (RPLACD target NIL) (print-str l))] - (is (= actual expected))) - (is (thrown-with-msg? - Exception - #"Un-ġefōg þing in RPLACD.*" - (RPLACD (make-beowulf-list '(A B C D E)) :a)) - "You can't represent a keyword in Lisp 1.5") - (is (thrown-with-msg? - Exception - #"Uncynlic miercels in RPLACD.*" - (RPLACD "ABCDE" 'F)) - "You can't RPLACD into anything which isn't a MutableSequence."))) + ) + ) (deftest numberp-tests (testing "NUMBERP" @@ -87,171 +59,12 @@ (let [expected 3.5 actual (PLUS 1.25 9/4)] (is (= actual expected)) - (is (float? actual))) - (let [expected 3.5 - actual (PLUS -2.5 6)] - (is (= actual expected) "Negative numbers are cool.") (is (float? actual)))) (testing "TIMES" (let [expected 6 actual (TIMES 2 3)] - (is (= actual expected))) - (let [expected 2.5 - actual (TIMES 5 0.5)] (is (= actual expected)))) - (testing "DIFFERENCE" + (testing DIFFERENCE (let [expected -1 actual (DIFFERENCE 1 2)] - (is (= actual expected))) - (let [expected (float 0.1) - actual (DIFFERENCE -0.1 -0.2)] - (is (= actual expected)))) - (testing "ADD1" - (let [expected -1 - actual (ADD1 -2)] - (is (= actual expected))) - (let [expected (float 3.5) - actual (ADD1 2.5)] - (is (= actual expected)))) - (testing "SUB1" - (let [expected -3 - actual (SUB1 -2)] - (is (= actual expected))) - (let [expected (float 1.5) - actual (SUB1 2.5)] (is (= actual expected))))) - -(deftest laxness - (testing "lax" - (let [expected true - actual (lax? 'Test)] - (is (= actual expected) "Pass, the Queen's Cat, and all's well"))) - (testing "strict" - (binding [*options* (assoc *options* :strict true)] - (is (thrown-with-msg? Exception #".*ne āfand innan Lisp 1.5" (lax? 'Test)))))) - -(deftest access-tests - (testing "primitive access" - (let [cell (make-cons-cell 1 7)] - (let [expected 1 - actual (CAR cell)] - (is (= actual expected))) - (let [expected 7 - actual (CDR cell)] - (is (= actual expected)))) - (is (thrown-with-msg? Exception #".*Ne can tace CAR of.*" (CAR 7))) - (is (thrown-with-msg? Exception #".*Ne can tace CDR of.*" (CDR 'A))) - (is (thrown-with-msg? Exception #".*Ne liste.*" (CADDDR "Foo"))) - (is (thrown-with-msg? Exception #".*uaf: unexpected letter in path.*" - (uaf (make-beowulf-list '(A B C D)) - '(\d \a \z \e \d)))))) - -(deftest misc-predicate-tests - (testing "NULL" - (let [expected T - actual (NULL NIL)] - (is (= actual expected))) - (let [expected F - actual (NULL (make-beowulf-list '(A B C)))] - (is (= actual expected)))) - (testing "NILP" - (let [expected T - actual (NILP NIL)] - (is (= actual expected))) - (let [expected NIL - actual (NILP (make-beowulf-list '(A B C)))] - (is (= actual expected)))) - (testing "AND" - (let [expected T - actual (AND)] - (is (= actual expected))) - (let [expected T - actual (AND T T)] - (is (= actual expected))) - (let [expected T - actual (AND T T T)] - (is (= actual expected))) - (let [expected T - actual (AND 1 'A (make-beowulf-list '(A B C)))] - (is (= actual expected))) - (let [expected F - actual (AND NIL)] - (is (= actual expected))) - (let [expected F - actual (AND T T F T)] - (is (= actual expected)))) - (testing "OR" - (let [expected F - actual (OR)] - (is (= actual expected))) - (let [expected T - actual (OR NIL T)] - (is (= actual expected))) - (let [expected T - actual (OR T F T)] - (is (= actual expected))) - (let [expected T - actual (OR 1 F (make-beowulf-list '(A B C)))] - (is (= actual expected))) - (let [expected F - actual (OR NIL)] - (is (= actual expected))) - (let [expected F - actual (OR NIL F)] - (is (= actual expected)))) - (testing "FIXP" - (let [expected F - actual (FIXP NIL)] - (is (= actual expected))) - (let [expected F - actual (FIXP 'A)] - (is (= actual expected))) - (let [expected F - actual (FIXP 3.2)] - (is (= actual expected))) - (let [expected T - actual (FIXP 7)] - (is (= actual expected)))) - (testing "LESSP" - (let [expected F - actual (LESSP 7 3)] - (is (= actual expected))) - (let [expected T - actual (LESSP -7 3.5)] - (is (= actual expected))) - (let [expected F - actual (LESSP 3.14 3.14)] - (is (= actual expected)))) - (testing "GREATERP" - (let [expected T - actual (GREATERP 7 3)] - (is (= actual expected))) - (let [expected F - actual (GREATERP -7 3.5)] - (is (= actual expected))) - (let [expected F - actual (GREATERP 3.14 3.14)] - (is (= actual expected))))) - -;; Really tricky to get DEFINE set up for testing here. It works OK in the -;; REPL, but there's nonsense going on with lazy sequences. Better to -;; reimplement in Lisp. -;; (deftest define-tests -;; (testing "DEFINE" -;; (let [expected "(FF)" -;; actual (str (doall (DEFINE -;; (gsp "((FF LAMBDA (X) (COND ((ATOM X) X) (T (FF (CAR X))))))"))))] -;; (is (= actual expected))))) - -(defexpect error-without-code - (expect (more-> clojure.lang.ExceptionInfo type - (more-of {:keys [:phase :function :args :type :code]} - 'A1 code) ex-data) - (ERROR))) - -(defexpect error-with-code - (let [x 'X1] - (expect (more-> clojure.lang.ExceptionInfo type - (more-of {:keys [:phase :function :args :type :code]} - x code) ex-data) - (ERROR x)))) diff --git a/test/beowulf/lisp_test.clj b/test/beowulf/lisp_test.clj index 98bcc39..7d9fa64 100644 --- a/test/beowulf/lisp_test.clj +++ b/test/beowulf/lisp_test.clj @@ -1,13 +1,13 @@ (ns beowulf.lisp-test "The idea here is to test actual Lisp functions" - (:require [beowulf.bootstrap :refer [EVAL]] + (:require [clojure.test :refer [deftest testing is use-fixtures]] + [beowulf.bootstrap :refer [EVAL]] [beowulf.cons-cell :refer [make-beowulf-list]] - [beowulf.io :refer [SYSIN]] ;; [beowulf.oblist :refer [NIL]] - [beowulf.oblist :refer [NIL]] - [beowulf.read :refer [READ]] - [clojure.test :refer [deftest is testing use-fixtures]])) + [beowulf.io :refer [SYSIN]] + ;; [beowulf.oblist :refer [NIL]] + [beowulf.read :refer [READ]])) -(defn reps +(defn- reps "'Read eval print string', or 'read eval print single'. Reads and evaluates one input string, and returns the output string." @@ -130,6 +130,7 @@ actual (reps input)] (is (= actual expected)))))) + (deftest MEMBER-tests (testing "member" (let [expected "T" @@ -146,18 +147,17 @@ (is (= actual expected))))) ;; This is failing, and although yes, it does matter, I have not yet tracked the reason. -(deftest sublis-tests - (testing "sublis" - (let [expected "(SHAKESPEARE WROTE (THE TEMPEST))" - actual (reps - "(SUBLIS - '((X . SHAKESPEARE) (Y . (THE TEMPEST))) - '(X WROTE Y))")] - (is (= actual expected))))) +;; (deftest sublis-tests +;; (testing "sublis" +;; (let [expected "(SHAKESPEARE WROTE (THE TEMPEST))" +;; actual (reps +;; "(SUBLIS +;; '((X . SHAKESPEARE) (Y . (THE TEMPEST))) +;; '(X WROTE Y))")] +;; (is (= actual expected))))) (deftest prog-tests (testing "PROG" - ;; (reps "(TRACE 'PROG)") (let [expected "5" actual (reps "(PROG (X) (SETQ X 1) @@ -165,61 +165,4 @@ (SETQ X (ADD1 X)) (COND ((EQ X 5) (RETURN X)) (T (GO START))))")] - (is (= actual expected))))) - -(deftest put-get-tests - (let [symbol 'TESTSYMBOL - p1 'TESTPROPONE - p2 'TESTPROPTWO] - (testing "GET - property should be missing" - (let [expected "NIL" - actual (reps "(GET 'TESTSYMBOL 'TESTPROPONE)")] - (is (= actual expected)))) - (testing "PUT and GET: value of new property; change value of property" - (let [prop (reps "(GENSYM)") - val1 (reps "(GENSYM)") - val2 (reps "(GENSYM)") - expected1 val1 - actual1 (when (reps (str "(PUT '" symbol " '" prop " '" val1 ")")) - (reps (str "(GET '" symbol " '" prop ")"))) - expected2 val2 - actual2 (when (reps (str "(PUT '" symbol " '" prop " '" val2 ")")) - (reps (str "(GET '" symbol " '" prop ")")))] - (is (not= val1 val2)) - (is (= actual1 expected1) "The value set can be retrieved.") - (is (= actual2 expected2) "The value is changed."))) - (testing "PUT and GET: different properties have independent values" - (let [val1 (reps "(GENSYM)") - val2 (reps "(GENSYM)") - expected1 val1 - actual1 (when (reps (str "(PUT '" symbol " '" p1 " '" val1 ")")) - (reps (str "(GET '" symbol " '" p1 ")"))) - expected2 val2 - actual2 (when (reps (str "(PUT '" symbol " '" p2 " '" val2 ")")) - (reps (str "(GET '" symbol " '" p2 ")"))) - expected3 val1 - actual3 (reps (str "(GET '" symbol " '" p1 ")"))] - (is (not= val1 val2)) - (is (= actual1 expected1) "The value set can be retrieved.") - (is (= actual2 expected2) "Values are independent.") - (is (= actual3 expected3) "Setting a second property does not obliterate the first."))))) - -(deftest fsubr-tests - (testing "FSUBR/CONC" - (reps "(SETQ P (RANGE 1 4))") - (reps "(SETQ Q (RANGE 5 8))") - (reps "(SETQ R (RANGE 9 12))") - (let [expected "(1 2 3 4 5 6 7 8 9 10 11 12)" - actual (reps "(CONC P Q R)")] - (is (= actual expected))))) - -(deftest attrib-tests - (testing "ATTRIB" - (reps "(SETQ X '(A B C))") - (reps "(SETQ Y '(D E F))") - (let [expected "(D E F)" - actual (reps "(ATTRIB X Y)")] - (is (= actual expected))) - (let [expected "(A B C D E F)" - actual (reps "X")] (is (= actual expected))))) \ No newline at end of file