All tests pass, documentation regenerated, going for release.

This commit is contained in:
Simon Brooke 2023-04-10 13:30:04 +01:00
parent e5677a8300
commit 8c5727f5df
No known key found for this signature in database
GPG key ID: A7A4F18D1D4DF987
18 changed files with 752 additions and 500 deletions

View file

@ -1,6 +1,16 @@
# Change Log # Change Log
All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/).
## [0.3.0] - 2023-04-10
### Changed
- Added property lists in the exact format used by Lisp 1.5;
- Added `ASSOC`, `EFFACE`, `MAPLIST`, `PROG`, and other functions;
- Where there are both interpreted (`EXPR`) and Clojure (`SUBR`) implementations of the same function, the `EXPR` is preferred (it is planned to make this configurable if/when there is a working compiler);
- More error messages/diagnostics are now printed in Old English (it is planned to implement internationalisation so that you can switch to messages you actually understand);
- Documentation improvements.
## [0.2.1] - 2023-03-30 ## [0.2.1] - 2023-03-30
### Changed ### Changed

294
README.md
View file

@ -4,7 +4,34 @@
LISP 1.5 is to all Lisp dialects as Beowulf is to English literature. LISP 1.5 is to all Lisp dialects as Beowulf is to English literature.
![Beowulf logo](img/beowulf_logo.png) ![Beowulf logo](https://simon-brooke.github.io/beowulf/docs/img/beowulf_logo_med.png)
## Contents
* [What this is](#what-this-is)
+ [Status](#status)
+ [BUT WHY?!!?!](#but-why-----)
+ [Project Target](#project-target)
+ [Invoking](#invoking)
+ [Building and Invoking](#building-and-invoking)
+ [Reader macros](#reader-macros)
+ [Functions and symbols implemented](#functions-and-symbols-implemented)
+ [Architectural plan](#architectural-plan)
- [resources/lisp1.5.lsp](#resources-lisp15lsp)
- [beowulf/boostrap.clj](#beowulf-boostrapclj)
- [beowulf/host.clj](#beowulf-hostclj)
- [beowulf/read.clj](#beowulf-readclj)
+ [Commentary](#commentary)
* [Installation](#installation)
+ [Input/output](#input-output)
- [SYSOUT](#sysout)
- [SYSIN](#sysin)
* [Learning Lisp 1.5](#learning-lisp-15)
* [Other Lisp 1.5 resources](#other-lisp-15-resources)
+ [Other implmentations](#other-implementations)
+ [History resources](#history-resources)
* [License](#license)
<small><i><a href='http://ecotrust-canada.github.io/markdown-toc/'>Table of contents generated with markdown-toc</a></i></small>
## What this is ## What this is
@ -13,6 +40,19 @@ 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 as described in the manual, with, in so far as is possible, exactly the
same bahaviour - except as documented below. same bahaviour - except as documented below.
### BUT WHY?!!?!
Because.
Because Lisp is the only computer language worth learning, and if a thing
is worth learning, it's worth learning properly; which means going back to
the beginning and trying to understand that.
Because there is, so far as I know, no working implementation of Lisp 1.5
for modern machines.
Because I'm barking mad, and this is therapy.
### Status ### Status
Working Lisp interpreter, but some key features not yet implemented. Working Lisp interpreter, but some key features not yet implemented.
@ -20,11 +60,20 @@ Working Lisp interpreter, but some key features not yet implemented.
* [Project website](https://simon-brooke.github.io/beowulf/). * [Project website](https://simon-brooke.github.io/beowulf/).
* [Source code documentation](https://simon-brooke.github.io/beowulf/docs/codox/index.html). * [Source code documentation](https://simon-brooke.github.io/beowulf/docs/codox/index.html).
### Building and Invoking ### Project Target
Build with 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.
lein uberjar Some `readline`-like functionality would be really useful, but my attempt to
integrate [JLine](https://github.com/jline/jline3) has not (yet) been successful.
An in-core structure editor would be an extremely nice thing, and I may well
implement one.
You are of course welcome to fork the project and do whatever you like with it!
### Invoking
Invoke with Invoke with
@ -37,107 +86,128 @@ Command line arguments as follows:
``` ```
-h, --help Print this message -h, --help Print this message
-p PROMPT, --prompt PROMPT Set the REPL prompt to PROMPT -p PROMPT, --prompt PROMPT Set the REPL prompt to PROMPT
-r INITFILE, --read INITFILE Read Lisp functions from the file INITFILE -r INITFILE, --read SYSOUTFILE Read Lisp sysout from the file SYSOUTFILE
-s, --strict Strictly interpret the Lisp 1.5 language, without extensions. (defaults to `resources/lisp1.5.lsp`)
-s, --strict Strictly interpret the Lisp 1.5 language,
without extensions.
``` ```
To end a session, type `STOP` at the command prompt. To end a session, type `STOP` at the command prompt.
### Building and Invoking
Build with
lein uberjar
### Reader macros ### Reader macros
Currently I don't have 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
The following functions and symbols are implemented:
| Function | Type | Signature | Implementation | Documentation | | Function | Type | Signature | Implementation | Documentation |
|--------------|----------------|------------------|----------------|----------------------| |--------------|----------------|------------------|----------------|----------------------|
| NIL | Lisp variable | | | ? | | NIL | Lisp variable | ? | | see manual pages <a href='#page22'>22</a>, <a href='#page69'>69</a> |
| T | Lisp variable | | | ? | | T | Lisp variable | ? | | see manual pages <a href='#page22'>22</a>, <a href='#page69'>69</a> |
| F | Lisp variable | | | ? | | F | Lisp variable | ? | | see manual pages <a href='#page22'>22</a>, <a href='#page69'>69</a> |
| ADD1 | Host function | (ADD1 X) | | ? | | ADD1 | Host lambda function | ? | | ? |
| AND | Host function | (AND & ARGS) | 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. | | 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 function | (APPEND X Y) | LAMBDA-fn | see manual pages <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=19'>11</a>, <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=69'>61</a> | | APPEND | Lisp lambda function | ? | | see manual pages <a href='#page11'>11</a>, <a href='#page61'>61</a> |
| APPLY | Host function | (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 | 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. |
| ATOM | Host function | (ATOM X) | 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`. | | 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. |
| CAR | Host function | (CAR X) | | Return the item indicated by the first pointer of a pair. NIL is treated specially: the CAR of NIL is NIL. | | 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`. |
| CAAAAR | Lisp function | (CAAAAR X) | LAMBDA-fn | ? | | 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. |
| CAAADR | Lisp function | (CAAADR X) | LAMBDA-fn | ? | | CAAAAR | Lisp lambda function | ? | ? | ? |
| CAAAR | Lisp function | (CAAAR X) | LAMBDA-fn | ? | | CAAADR | Lisp lambda function | ? | ? | ? |
| CAADAR | Lisp function | (CAADAR X) | LAMBDA-fn | ? | | CAAAR | Lisp lambda function | ? | ? | ? |
| CAADDR | Lisp function | (CAADDR X) | LAMBDA-fn | ? | | CAADAR | Lisp lambda function | ? | ? | ? |
| CAADR | Lisp function | (CAADR X) | LAMBDA-fn | ? | | CAADDR | Lisp lambda function | ? | ? | ? |
| CAAR | Lisp function | (CAAR X) | LAMBDA-fn | ? | | CAADR | Lisp lambda function | ? | ? | ? |
| CADAAR | Lisp function | (CADAAR X) | LAMBDA-fn | ? | | CAAR | Lisp lambda function | ? | ? | ? |
| CADADR | Lisp function | (CADADR X) | LAMBDA-fn | ? | | CADAAR | Lisp lambda function | ? | ? | ? |
| CADAR | Lisp function | (CADAR X) | LAMBDA-fn | ? | | CADADR | Lisp lambda function | ? | ? | ? |
| CADDAR | Lisp function | (CADDAR X) | LAMBDA-fn | ? | | CADAR | Lisp lambda function | ? | ? | ? |
| CADDDR | Lisp function | (CADDDR X) | LAMBDA-fn | ? | | CADDAR | Lisp lambda function | ? | ? | ? |
| CADDR | Lisp function | (CADDR X) | LAMBDA-fn | ? | | CADDDR | Lisp lambda function | ? | ? | ? |
| CADR | Lisp function | (CADR X) | LAMBDA-fn | ? | | CADDR | Lisp lambda function | ? | ? | ? |
| CDAAAR | Lisp function | (CDAAAR X) | LAMBDA-fn | ? | | CADR | Lisp lambda function | ? | ? | ? |
| CDAADR | Lisp function | (CDAADR X) | LAMBDA-fn | ? | | CDAAAR | Lisp lambda function | ? | ? | ? |
| CDAAR | Lisp function | (CDAAR X) | LAMBDA-fn | ? | | CDAADR | Lisp lambda function | ? | ? | ? |
| CDADAR | Lisp function | (CDADAR X) | LAMBDA-fn | ? | | CDAAR | Lisp lambda function | ? | ? | ? |
| CDADDR | Lisp function | (CDADDR X) | LAMBDA-fn | ? | | CDADAR | Lisp lambda function | ? | ? | ? |
| CDADR | Lisp function | (CDADR X) | LAMBDA-fn | ? | | CDADDR | Lisp lambda function | ? | ? | ? |
| CDAR | Lisp function | (CDAR X) | LAMBDA-fn | ? | | CDADR | Lisp lambda function | ? | ? | ? |
| CDDAAR | Lisp function | (CDDAAR X) | LAMBDA-fn | ? | | CDAR | Lisp lambda function | ? | ? | ? |
| CDDADR | Lisp function | (CDDADR X) | LAMBDA-fn | ? | | CDDAAR | Lisp lambda function | ? | ? | ? |
| CDDAR | Lisp function | (CDDAR X) | LAMBDA-fn | ? | | CDDADR | Lisp lambda function | ? | ? | ? |
| CDDDAR | Lisp function | (CDDDAR X) | LAMBDA-fn | ? | | CDDAR | Lisp lambda function | ? | ? | ? |
| CDDDDR | Lisp function | (CDDDDR X) | LAMBDA-fn | ? | | CDDDAR | Lisp lambda function | ? | ? | ? |
| CDDDR | Lisp function | (CDDDR X) | LAMBDA-fn | ? | | CDDDDR | Lisp lambda function | ? | ? | ? |
| CDDR | Lisp function | (CDDR X) | LAMBDA-fn | ? | | CDDDR | Lisp lambda function | ? | ? | ? |
| CDR | Host function | (CDR X) | | Return the item indicated by the second pointer of a pair. NIL is treated specially: the CDR of NIL is NIL. | | CDDR | Lisp lambda function | ? | ? | ? |
| CONS | Host function | (CONS CAR CDR) | | Construct a new instance of cons cell with this `car` and `cdr`. | | 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. |
| COPY | Lisp function | (COPY X) | LAMBDA-fn | see manual pages <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=70'>62</a> | | CONS | Host lambda function | ? | | Construct a new instance of cons cell with this `car` and `cdr`. |
| DEFINE | Host function | (DEFINE ARGS) | PSEUDO-FUNCTION | Bootstrap-only version of `DEFINE` which, post boostrap, can be overwritten in LISP. The single argument to `DEFINE` should be an assoc list which should be nconc'ed onto the front of the oblist. Broadly, (SETQ OBLIST (NCONC ARG1 OBLIST)) | | 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. |
| DIFFERENCE | Host function | (DIFFERENCE X Y) | | ? | | COPY | Lisp lambda function | ? | | see manual pages <a href='#page62'>62</a> |
| DIVIDE | Lisp function | (DIVIDE X Y) | LAMBDA-fn | see manual pages <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=34'>26</a>, <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=72'>64</a> | | 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. |
| ERROR | Host function | (ERROR & ARGS) | PSEUDO-FUNCTION | Throw an error | | DIFFERENCE | Host lambda function | ? | | ? |
| EQ | Host function | (EQ X Y) | PREDICATE | Returns `T` if and only if both `x` and `y` are bound to the same atom, else `NIL`. | | DIVIDE | Lisp lambda function | ? | | see manual pages <a href='#page26'>26</a>, <a href='#page64'>64</a> |
| EQUAL | Host function | (EQUAL X Y) | 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` | | 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. |
| EVAL | Host function | (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. | | EFFACE | Lisp lambda function | ? | PSEUDO-FUNCTION | see manual pages <a href='#page63'>63</a> |
| FACTORIAL | Lisp function | (FACTORIAL N) | LAMBDA-fn | ? | | ERROR | Host lambda function | ? | PSEUDO-FUNCTION | Throw an error |
| FIXP | Host function | (FIXP X) | PREDICATE | ? | | EQ | Host lambda function | ? | PREDICATE | Returns `T` if and only if both `x` and `y` are bound to the same atom, else `NIL`. |
| GENSYM | Host function | (GENSYM ) | | Generate a unique symbol. | | 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` |
| GET | Lisp function | (GET X Y) | LAMBDA-fn | see manual pages <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=49'>41</a>, <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=67'>59</a> | | 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`. |
| GREATERP | Host function | (GREATERP X Y) | PREDICATE | ? | | FACTORIAL | Lisp lambda function | ? | ? | ? |
| INTEROP | Host function | (INTEROP FN-SYMBOL ARGS) | (INTEROP) | 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. 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. | | FIXP | Host lambda function | ? | PREDICATE | ? |
| INTERSECTION | Lisp function | (INTERSECTION X Y) | LAMBDA-fn | ? | | GENSYM | Host lambda function | ? | | Generate a unique symbol. |
| LENGTH | Lisp function | (LENGTH L) | LAMBDA-fn | see manual pages <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=70'>62</a> | | 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. |
| LESSP | Host function | (LESSP X Y) | PREDICATE | ? | | GREATERP | Host lambda function | ? | PREDICATE | ? |
| MEMBER | Lisp function | (MEMBER A X) | LAMBDA-fn | see manual pages <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=19'>11</a>, <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=70'>62</a> | | INTEROP | Host lambda function | ? | ? | ? |
| MINUSP | Lisp function | (MINUSP X) | LAMBDA-fn | see manual pages <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=34'>26</a>, <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=72'>64</a> | | INTERSECTION | Lisp lambda function | ? | ? | ? |
| NOT | Lisp function | (NOT X) | LAMBDA-fn | see manual pages <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=29'>21</a>, <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=31'>23</a>, <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=66'>58</a> | | LENGTH | Lisp lambda function | ? | | see manual pages <a href='#page62'>62</a> |
| NULL | Lisp function | (NULL X) | LAMBDA-fn | see manual pages <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=19'>11</a>, <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=65'>57</a> | | LESSP | Host lambda function | ? | PREDICATE | ? |
| NUMBERP | Host function | (NUMBERP X) | PREDICATE | ? | | MAPLIST | Lisp lambda function | ? | FUNCTIONAL | see manual pages <a href='#page20'>20</a>, <a href='#page21'>21</a>, <a href='#page63'>63</a> |
| OBLIST | Host function | (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. | | MEMBER | Lisp lambda function | ? | PREDICATE | see manual pages <a href='#page11'>11</a>, <a href='#page62'>62</a> |
| ONEP | Lisp function | (ONEP X) | LAMBDA-fn | see manual pages <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=34'>26</a>, <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=72'>64</a> | | MINUSP | Lisp lambda function | ? | PREDICATE | see manual pages <a href='#page26'>26</a>, <a href='#page64'>64</a> |
| PAIR | Lisp function | (PAIR X Y) | LAMBDA-fn | see manual pages <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=68'>60</a> | | NOT | Lisp lambda function | ? | PREDICATE | see manual pages <a href='#page21'>21</a>, <a href='#page23'>23</a>, <a href='#page58'>58</a> |
| PLUS | Host function | (PLUS & ARGS) | | ? | | NULL | Lisp lambda function | ? | PREDICATE | see manual pages <a href='#page11'>11</a>, <a href='#page57'>57</a> |
| PRETTY | Lisp variable | | (PRETTY) | ? | | NUMBERP | Host lambda function | ? | PREDICATE | ? |
| PRINT | Lisp variable | | PSEUDO-FUNCTION | ? | | 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. |
| PROP | Lisp function | (PROP X Y U) | LAMBDA-fn | see manual pages <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=67'>59</a> | | ONEP | Lisp lambda function | ? | PREDICATE | see manual pages <a href='#page26'>26</a>, <a href='#page64'>64</a> |
| QUOTIENT | Host function | (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. | | 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. |
| RANGE | Lisp variable | ? | (RANGE (LAMBDA (N M) (COND ((LESSP M N) (QUOTE NIL)) ((QUOTE T) (CONS N (RANGE (ADD1 N) M)))))) | ? | | PAIR | Lisp lambda function | ? | | see manual pages <a href='#page60'>60</a> |
| READ | Host function | (READ ); (READ INPUT) | 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. | | 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. |
| REMAINDER | Host function | (REMAINDER X Y) | | ? | | PLUS | Host lambda function | ? | | ? |
| REPEAT | Lisp function | (REPEAT N X) | LAMBDA-fn | ? | | PRETTY | | ? | ? | ? |
| RPLACA | Host function | (RPLACA CELL VALUE) | 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) | | PRINT | | ? | PSEUDO-FUNCTION | see manual pages <a href='#page65'>65</a>, <a href='#page84'>84</a> |
| RPLACD | Host function | (RPLACD CELL VALUE) | 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) | | 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. |
| SET | Host function | (SET SYMBOL VAL) | 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! | | PROP | Lisp lambda function | ? | FUNCTIONAL | see manual pages <a href='#page59'>59</a> |
| SUB1 | Lisp function | (SUB1 N) | LAMBDA-fn | see manual pages <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=34'>26</a>, <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=72'>64</a> | | QUOTE | Lisp lambda function | ? | | see manual pages <a href='#page10'>10</a>, <a href='#page22'>22</a>, <a href='#page71'>71</a> |
| SYSIN | Host function | (SYSIN ); (SYSIN FILENAME) | (SYSIN) | 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. | | 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. |
| SYSOUT | Host function | (SYSOUT ); (SYSOUT FILEPATH) | (SYSOUT) | 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. | | RANGE | Lisp lambda function | ? | ? | ? |
| TERPRI | Lisp variable | | PSEUDO-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. |
| TIMES | Host function | (TIMES & ARGS) | | ? | | REMAINDER | Host lambda function | ? | | ? |
| TRACE | Host function | (TRACE S) | PSEUDO-FUNCTION | Add this symbol `s` to the set of symbols currently being traced. If `s` is not a symbol, does nothing. | | REPEAT | Lisp lambda function | ? | ? | ? |
| UNTRACE | Host function | (UNTRACE S) | PSEUDO-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) |
| ZEROP | Lisp function | (ZEROP N) | LAMBDA-fn | see manual pages <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=34'>26</a>, <a href='https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=72'>64</a> | | 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 <a href='#page63'>63</a> |
| 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 <a href='#page12'>12</a>, <a href='#page61'>61</a> |
| SUBST | Lisp lambda function | ? | | see manual pages <a href='#page11'>11</a>, <a href='#page61'>61</a> |
| 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 <a href='#page65'>65</a>, <a href='#page84'>84</a> |
| 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 <a href='#page26'>26</a>, <a href='#page64'>64</a> |
Functions described as 'Lisp function' above are defined in the default Functions described as 'Lisp function' above are defined in the default
sysout file, `resources/lisp1.5.lsp`, which will be loaded by default unless sysout file, `resources/lisp1.5.lsp`, which will be loaded by default unless
@ -199,19 +269,6 @@ Intended deviations from the behaviour of the real Lisp reader are as follows:
a comment, as most modern Lisps do; but I do not believe Lisp 1.5 had a comment, as most modern Lisps do; but I do not believe Lisp 1.5 had
this feature. this feature.
### 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.
### Commentary ### Commentary
What's surprised me in working on this is how much more polished Lisp 1.5 is What's surprised me in working on this is how much more polished Lisp 1.5 is
@ -229,11 +286,19 @@ but this is software which is almost sixty years old).
## Installation ## Installation
At present, clone the source and build it using Download the latest [release 'uberjar'](https://github.com/simon-brooke/beowulf/releases) and run it using:
`lein uberjar`. ```bash
java -jar <path name of uberjar>
```
You will require to have [Leiningen](https://leiningen.org/) installed. Or clone the source and build it using:
```bash
lein uberjar`
```
To build it you will require to have [Leiningen](https://leiningen.org/) installed.
### Input/output ### Input/output
@ -266,7 +331,14 @@ 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 file, which is why `resources/lisp1.5.lsp` is largely copytyped and
reconstructed from the manual. reconstructed from the manual.
I'm not at this time aware of any other working Lisp 1.5 implementations. ### Other implementations
There's an online (browser native) Lisp 1.5 implementation [here](https://pages.zick.run/ichigo/) (source code [here](https://github.com/zick/IchigoLisp)). It
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/further_reading.html).
## License ## License

File diff suppressed because one or more lines are too long

View file

@ -5,9 +5,9 @@
<p>See Appendix E, <code>OVERLORD - THE MONITOR</code>, and Appendix F, <code>LISP INPUT <p>See Appendix E, <code>OVERLORD - THE MONITOR</code>, and Appendix F, <code>LISP INPUT
AND OUTPUT</code>.</p> AND OUTPUT</code>.</p>
<p>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.</p> <p>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.</p>
<p>Hence functions SYSOUT and SYSIN, which do just that.</p></div></div><div class="public anchor" id="var-default-sysout"><h3>default-sysout</h3><div class="usage"></div><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/io.clj#L49">view source</a></div></div><div class="public anchor" id="var-resolve-subr"><h3>resolve-subr</h3><div class="usage"><code>(resolve-subr entry)</code></div><div class="doc"><div class="markdown"><p>If this oblist <code>entry</code> references a subroutine, attempt to fix up that reference.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/io.clj#L108">view source</a></div></div><div class="public anchor" id="var-safely-wrap-subr"><h3>safely-wrap-subr</h3><div class="usage"><code>(safely-wrap-subr entry)</code></div><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/io.clj#L69">view source</a></div></div><div class="public anchor" id="var-safely-wrap-subrs"><h3>safely-wrap-subrs</h3><div class="usage"><code>(safely-wrap-subrs objects)</code></div><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/io.clj#L80">view source</a></div></div><div class="public anchor" id="var-SYSIN"><h3>SYSIN</h3><div class="usage"><code>(SYSIN)</code><code>(SYSIN filename)</code></div><div class="doc"><div class="markdown"><p>Read the contents of the file at this <code>filename</code> into the object list. </p> <p>Hence functions SYSOUT and SYSIN, which do just that.</p></div></div><div class="public anchor" id="var-default-sysout"><h3>default-sysout</h3><div class="usage"></div><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/io.clj#L49">view source</a></div></div><div class="public anchor" id="var-resolve-subr"><h3>resolve-subr</h3><div class="usage"><code>(resolve-subr entry)</code><code>(resolve-subr entry prop)</code></div><div class="doc"><div class="markdown"><p>If this oblist <code>entry</code> references a subroutine, attempt to fix up that reference.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/io.clj#L108">view source</a></div></div><div class="public anchor" id="var-safely-wrap-subr"><h3>safely-wrap-subr</h3><div class="usage"><code>(safely-wrap-subr entry)</code></div><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/io.clj#L69">view source</a></div></div><div class="public anchor" id="var-safely-wrap-subrs"><h3>safely-wrap-subrs</h3><div class="usage"><code>(safely-wrap-subrs objects)</code></div><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/io.clj#L80">view source</a></div></div><div class="public anchor" id="var-SYSIN"><h3>SYSIN</h3><div class="usage"><code>(SYSIN)</code><code>(SYSIN filename)</code></div><div class="doc"><div class="markdown"><p>Read the contents of the file at this <code>filename</code> into the object list. </p>
<p>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.</p> <p>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.</p>
<p>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.</p> <p>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.</p>
<p><strong>NOTE THAT</strong> if the provided <code>filename</code> does not end with <code>.lsp</code> (which, if youre writing it from the Lisp REPL, it wont), the extension <code>.lsp</code> will be appended.</p> <p><strong>NOTE THAT</strong> if the provided <code>filename</code> does not end with <code>.lsp</code> (which, if youre writing it from the Lisp REPL, it wont), the extension <code>.lsp</code> will be appended.</p>
<p><strong>NOTE THAT</strong> this is an extension function, not available in strct mode.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/io.clj#L137">view source</a></div></div><div class="public anchor" id="var-SYSOUT"><h3>SYSOUT</h3><div class="usage"><code>(SYSOUT)</code><code>(SYSOUT filepath)</code></div><div class="doc"><div class="markdown"><p>Dump the current content of the object list to file. If no <code>filepath</code> is specified, a file name will be constructed of the symbol <code>Sysout</code> and the current date. File paths will be considered relative to the filepath set when starting Lisp.</p> <p><strong>NOTE THAT</strong> this is an extension function, not available in strct mode.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/io.clj#L140">view source</a></div></div><div class="public anchor" id="var-SYSOUT"><h3>SYSOUT</h3><div class="usage"><code>(SYSOUT)</code><code>(SYSOUT filepath)</code></div><div class="doc"><div class="markdown"><p>Dump the current content of the object list to file. If no <code>filepath</code> is specified, a file name will be constructed of the symbol <code>Sysout</code> and the current date. File paths will be considered relative to the filepath set when starting Lisp.</p>
<p><strong>NOTE THAT</strong> this is an extension function, not available in strct mode.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/io.clj#L84">view source</a></div></div></div></body></html> <p><strong>NOTE THAT</strong> this is an extension function, not available in strct mode.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/io.clj#L84">view source</a></div></div></div></body></html>

View file

@ -21,4 +21,4 @@
T-&gt;ff[car[x]]]]] (COND ((ATOM X) X) T-&gt;ff[car[x]]]]] (COND ((ATOM X) X)
((QUOTE T)(FF (CAR X)))))) ((QUOTE T)(FF (CAR X))))))
</code></pre> </code></pre>
<p><em>quote ends</em></p></div></div><div class="public anchor" id="var-gen-cond"><h3>gen-cond</h3><div class="usage"><code>(gen-cond p context)</code></div><div class="doc"><div class="markdown"><p>Generate a cond statement from this simplified parse tree fragment <code>p</code>; returns <code>nil</code> if <code>p</code> does not represent a (MEXPR) cond statement.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L99">view source</a></div></div><div class="public anchor" id="var-gen-cond-clause"><h3>gen-cond-clause</h3><div class="usage"><code>(gen-cond-clause p context)</code></div><div class="doc"><div class="markdown"><p>Generate a cond clause from this simplified parse tree fragment <code>p</code>; returns <code>nil</code> if <code>p</code> does not represent a cond clause.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L87">view source</a></div></div><div class="public anchor" id="var-gen-dot-terminated-list"><h3>gen-dot-terminated-list</h3><div class="usage"><code>(gen-dot-terminated-list p)</code></div><div class="doc"><div class="markdown"><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.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L123">view source</a></div></div><div class="public anchor" id="var-gen-fn-call"><h3>gen-fn-call</h3><div class="usage"><code>(gen-fn-call p context)</code></div><div class="doc"><div class="markdown"><p>Generate a function call from this simplified parse tree fragment <code>p</code>; returns <code>nil</code> if <code>p</code> does not represent a (MEXPR) function call.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L112">view source</a></div></div><div class="public anchor" id="var-gen-iexpr"><h3>gen-iexpr</h3><div class="usage"><code>(gen-iexpr tree)</code></div><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L161">view source</a></div></div><div class="public anchor" id="var-generate"><h3>generate</h3><div class="usage"><code>(generate p)</code><code>(generate p context)</code></div><div class="doc"><div class="markdown"><p>Generate lisp structure from this parse tree <code>p</code>. It is assumed that <code>p</code> has been simplified.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L199">view source</a></div></div><div class="public anchor" id="var-generate-assign"><h3>generate-assign</h3><div class="usage"><code>(generate-assign tree context)</code></div><div class="doc"><div class="markdown"><p>Generate an assignment statement based on this <code>tree</code>. If the thing being assigned to is a function signature, then we have to do something different to if its an atom.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L175">view source</a></div></div><div class="public anchor" id="var-generate-defn"><h3>generate-defn</h3><div class="usage"><code>(generate-defn tree context)</code></div><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L149">view source</a></div></div><div class="public anchor" id="var-generate-set"><h3>generate-set</h3><div class="usage"><code>(generate-set tree context)</code></div><div class="doc"><div class="markdown"><p>Actually not sure what the mexpr representation of set looks like</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L170">view source</a></div></div><div class="public anchor" id="var-strip-leading-zeros"><h3>strip-leading-zeros</h3><div class="usage"><code>(strip-leading-zeros s)</code><code>(strip-leading-zeros s prefix)</code></div><div class="doc"><div class="markdown"><p><code>read-string</code> interprets strings with leading zeros as octal; strip any from this string <code>s</code>. If whats left is empty (i.e. there were only zeros, return <code>"0"</code>.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L185">view source</a></div></div></div></body></html> <p><em>quote ends</em></p></div></div><div class="public anchor" id="var-gen-cond"><h3>gen-cond</h3><div class="usage"><code>(gen-cond p context)</code></div><div class="doc"><div class="markdown"><p>Generate a cond statement from this simplified parse tree fragment <code>p</code>; returns <code>nil</code> if <code>p</code> does not represent a (MEXPR) cond statement.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L99">view source</a></div></div><div class="public anchor" id="var-gen-cond-clause"><h3>gen-cond-clause</h3><div class="usage"><code>(gen-cond-clause p context)</code></div><div class="doc"><div class="markdown"><p>Generate a cond clause from this simplified parse tree fragment <code>p</code>; returns <code>nil</code> if <code>p</code> does not represent a cond clause.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L87">view source</a></div></div><div class="public anchor" id="var-gen-dot-terminated-list"><h3>gen-dot-terminated-list</h3><div class="usage"><code>(gen-dot-terminated-list p)</code></div><div class="doc"><div class="markdown"><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.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L123">view source</a></div></div><div class="public anchor" id="var-gen-fn-call"><h3>gen-fn-call</h3><div class="usage"><code>(gen-fn-call p context)</code></div><div class="doc"><div class="markdown"><p>Generate a function call from this simplified parse tree fragment <code>p</code>; returns <code>nil</code> if <code>p</code> does not represent a (MEXPR) function call.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L112">view source</a></div></div><div class="public anchor" id="var-gen-iexpr"><h3>gen-iexpr</h3><div class="usage"><code>(gen-iexpr tree context)</code></div><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L162">view source</a></div></div><div class="public anchor" id="var-generate"><h3>generate</h3><div class="usage"><code>(generate p)</code><code>(generate p context)</code></div><div class="doc"><div class="markdown"><p>Generate lisp structure from this parse tree <code>p</code>. It is assumed that <code>p</code> has been simplified.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L200">view source</a></div></div><div class="public anchor" id="var-generate-assign"><h3>generate-assign</h3><div class="usage"><code>(generate-assign tree context)</code></div><div class="doc"><div class="markdown"><p>Generate an assignment statement based on this <code>tree</code>. If the thing being assigned to is a function signature, then we have to do something different to if its an atom.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L176">view source</a></div></div><div class="public anchor" id="var-generate-defn"><h3>generate-defn</h3><div class="usage"><code>(generate-defn tree context)</code></div><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L149">view source</a></div></div><div class="public anchor" id="var-generate-set"><h3>generate-set</h3><div class="usage"><code>(generate-set tree context)</code></div><div class="doc"><div class="markdown"><p>Actually not sure what the mexpr representation of set looks like</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L171">view source</a></div></div><div class="public anchor" id="var-strip-leading-zeros"><h3>strip-leading-zeros</h3><div class="usage"><code>(strip-leading-zeros s)</code><code>(strip-leading-zeros s prefix)</code></div><div class="doc"><div class="markdown"><p><code>read-string</code> interprets strings with leading zeros as octal; strip any from this string <code>s</code>. If whats left is empty (i.e. there were only zeros, return <code>"0"</code>.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/beowulf/blob/master/src/beowulf/reader/generate.clj#L186">view source</a></div></div></div></body></html>

View file

@ -37,6 +37,10 @@ h2 {
font-size: 25px; font-size: 25px;
} }
th, td {
vertical-align: top;
}
h5.license { h5.license {
margin: 9px 0 22px 0; margin: 9px 0 22px 0;
color: lime; color: lime;

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,7 @@
:source-uri "https://github.com/simon-brooke/beowulf/blob/master/{filepath}#L{line}" :source-uri "https://github.com/simon-brooke/beowulf/blob/master/{filepath}#L{line}"
;; :themes [:journeyman] ;; :themes [:journeyman]
} }
:description "An implementation of LISP 1.5 in Clojure" :description "LISP 1.5 is to all Lisp dialects as Beowulf is to English literature."
:license {:name "GPL-2.0-or-later" :license {:name "GPL-2.0-or-later"
:url "https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html"} :url "https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html"}
:dependencies [[org.clojure/clojure "1.11.1"] :dependencies [[org.clojure/clojure "1.11.1"]

View file

@ -37,6 +37,10 @@ h2 {
font-size: 25px; font-size: 25px;
} }
th, td {
vertical-align: top;
}
h5.license { h5.license {
margin: 9px 0 22px 0; margin: 9px 0 22px 0;
color: lime; color: lime;

View file

@ -161,6 +161,7 @@
(PLUS 32767 SUBR (BEOWULF HOST PLUS)) (PLUS 32767 SUBR (BEOWULF HOST PLUS))
(PRETTY 32767) (PRETTY 32767)
(PRINT 32767) (PRINT 32767)
(PROG 32767 FSUBR (BEOWULF HOST PROG))
(PROP (PROP
32767 32767
EXPR EXPR

View file

@ -0,0 +1 @@
not[x] = [x = F -> T; x = NIL -> T; T -> F]

View file

@ -46,12 +46,12 @@
["-h" "--help"] ["-h" "--help"]
["-p PROMPT" "--prompt PROMPT" "Set the REPL prompt to PROMPT" ["-p PROMPT" "--prompt PROMPT" "Set the REPL prompt to PROMPT"
:default "Sprecan::"] :default "Sprecan::"]
["-r INITFILE" "--read INITFILE" "Read Lisp system from file INITFILE" ["-r SYSOUTFILE" "--read SYSOUTFILE" "Read Lisp system from file SYSOUTFILE"
:default default-sysout :default default-sysout
:validate [#(and :validate [#(and
(.exists (io/file %)) (.exists (io/file %))
(.canRead (io/file %))) (.canRead (io/file %)))
"Could not find initfile"]] "Could not find sysout file"]]
["-s" "--strict" "Strictly interpret the Lisp 1.5 language, without extensions."] ["-s" "--strict" "Strictly interpret the Lisp 1.5 language, without extensions."]
["-t" "--time" "Time evaluations."]]) ["-t" "--time" "Time evaluations."]])

View file

@ -8,7 +8,7 @@
*manual-url* page-url]] *manual-url* page-url]]
[beowulf.oblist :refer [NIL oblist]] [beowulf.oblist :refer [NIL oblist]]
[clojure.java.browse :refer [browse-url]] [clojure.java.browse :refer [browse-url]]
[clojure.string :as s ])) [clojure.string :as s]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; ;;;
@ -60,7 +60,6 @@
(when (keyword? key) (when (keyword? key)
(key (get-metadata-for-function function))))) (key (get-metadata-for-function function)))))
(defn- get-metadata-for-entry [entry key] (defn- get-metadata-for-entry [entry key]
(let [fn ((host-functions) (symbol (first entry)))] (let [fn ((host-functions) (symbol (first entry)))]
(get-metadata-for-function fn key))) (get-metadata-for-function fn key)))
@ -69,13 +68,25 @@
"Try to work out what this `entry` from the oblist actually "Try to work out what this `entry` from the oblist actually
represents." represents."
[entry] [entry]
(cond (let [interpretation {'APVAL "Lisp variable"
(= (second entry) 'LAMBDA) "Lisp function" 'EXPR "Lisp lambda function"
(= (second entry) 'LABEL) "Labeled form" 'FEXPR "Lisp nlambda function"
((host-functions) (first entry)) (if (fn? (eval (symbol ((host-functions) (first entry))))) 'SUBR "Host lambda function"
"Host function" 'FSUBR "Host nlambda function"}]
"Host variable") (s/join ", "
:else "Lisp variable")) (remove nil?
(map
#(when (some #{%} entry) (interpretation %))
(keys interpretation))))))
;; (cond
;; (= (nth entry 2) 'EXPR) "Lisp function"
;; (= (nth entry 2) 'FEXPR) "Labeled form"
;; ((host-functions) (first entry)) (try (if (fn? (eval (symbol ((host-functions) (first entry)))))
;; "Host function"
;; "Host variable")
;; (catch Exception _
;; "?Host macro?"))
;; :else "Lisp variable"))
(defn- format-clj-signature (defn- format-clj-signature
"Format the signature of the Clojure function represented by `symbol` for "Format the signature of the Clojure function represented by `symbol` for
@ -102,17 +113,18 @@
(defn infer-implementation (defn infer-implementation
[entry] [entry]
(case (second entry) (or (:implementation (index (keyword (first entry)))) "?"))
LAMBDA (format "%s-fn" (second entry)) ;; (case (second entry)
LABEL (format "%s-fn" (second entry)) ;; LAMBDA (format "%s-fn" (second entry))
(or (:implementation (index (keyword (first entry)))) (str entry)))) ;; LABEL (format "%s-fn" (second entry))
;; (or (:implementation (index (keyword (first entry)))) (str entry))))
(defn find-documentation (defn find-documentation
"Find appropriate documentation for this `entry` from the oblist." "Find appropriate documentation for this `entry` from the oblist."
[entry] [entry]
(let [k (keyword (first entry))] (let [k (keyword (first entry))]
(cond (cond
(= (count entry) 1) (if-let [doc (get-metadata-for-entry entry :doc)] (some #{'SUBR 'FSUBR} entry) (if-let [doc (get-metadata-for-entry entry :doc)]
(s/replace doc "\n" " ") (s/replace doc "\n" " ")
"?") "?")
(k index) (str "see manual pages " (format-page-references k)) (k index) (str "see manual pages " (format-page-references k))

View file

@ -108,9 +108,12 @@
(defn resolve-subr (defn resolve-subr
"If this oblist `entry` references a subroutine, attempt to fix up that "If this oblist `entry` references a subroutine, attempt to fix up that
reference." reference."
[entry] ([entry]
(or (resolve-subr entry 'SUBR)
(resolve-subr entry 'FSUBR)))
([entry prop]
(cond (= entry NIL) NIL (cond (= entry NIL) NIL
(= (CAR entry) 'SUBR) (try (= (CAR entry) prop) (try
(make-cons-cell (make-cons-cell
(CAR entry) (CAR entry)
(make-cons-cell (make-cons-cell
@ -122,7 +125,7 @@
(CADR entry)) (CADR entry))
(CDDR entry))) (CDDR entry)))
:else (make-cons-cell :else (make-cons-cell
(CAR entry) (resolve-subr (CDR entry))))) (CAR entry) (resolve-subr (CDR entry))))))
(defn- resolve-subroutines (defn- resolve-subroutines

View file

@ -148,24 +148,25 @@
(defn generate-defn (defn generate-defn
[tree context] [tree context]
(if (= :mexpr (first tree))
(generate-defn (second tree) context)
(make-beowulf-list (make-beowulf-list
(list 'PUT (list 'PUT
(list 'QUOTE (generate (-> tree second second) context)) (list 'QUOTE (generate (-> tree second second second) context))
(list 'QUOTE 'EXPR) (list 'QUOTE 'EXPR)
(list 'QUOTE (list 'QUOTE
(cons 'LAMBDA (cons 'LAMBDA
(cons (generate (nth (second tree) 2) context) (list (generate (nth (-> tree second second) 2) context)
(map #(generate % context) (generate (nth tree 3) context))))))))
(-> tree rest rest rest))))))))
(defn gen-iexpr (defn gen-iexpr
[tree] [tree context]
(let [bundle (reduce #(assoc %1 (first %2) %2) (let [bundle (reduce #(assoc %1 (first %2) %2)
{} {}
(rest tree))] (rest tree))]
(list (generate (:iop bundle)) (list (generate (:iop bundle) context)
(generate (:lhs bundle)) (generate (:lhs bundle) context)
(generate (:rhs bundle))))) (generate (:rhs bundle) context))))
(defn generate-set (defn generate-set
"Actually not sure what the mexpr representation of set looks like" "Actually not sure what the mexpr representation of set looks like"
@ -213,28 +214,19 @@
(make-cons-cell (generate (nth p 2) context) (make-cons-cell (generate (nth p 2) context)
(generate (nth p 3) context))) (generate (nth p 3) context)))
:args (make-beowulf-list (map #(generate % context) (rest p))) :args (make-beowulf-list (map #(generate % context) (rest p)))
:atom (case context :atom (symbol (second p))
:mexpr (if (some #(Character/isUpperCase %) (second p))
(list 'QUOTE (symbol (second p)))
(symbol (second p)))
:cond-mexpr (case (second p)
(T F NIL) (symbol (second p))
;; else
(symbol (second p)))
;; else
(symbol (second p)))
:bindings (generate (second p) context) :bindings (generate (second p) context)
:body (make-beowulf-list (map #(generate % context) (rest p))) :body (make-beowulf-list (map #(generate % context) (rest p)))
(:coefficient :exponent) (generate (second p) context) (:coefficient :exponent) (generate (second p) context)
:cond (gen-cond p (if (= context :mexpr) :cond-mexpr context)) :cond (gen-cond p (if (= context :mexpr) :cond-mexpr context))
:cond-clause (gen-cond-clause p context) :cond-clause (gen-cond-clause p context)
:decimal (read-string (apply str (map second (rest p)))) :decimal (read-string (apply str (map second (rest p))))
:defn (generate-assign p context) :defn (generate-defn p context)
:dotted-pair (make-cons-cell :dotted-pair (make-cons-cell
(generate (nth p 1) context) (generate (nth p 1) context)
(generate (nth p 2) context)) (generate (nth p 2) context))
:fncall (gen-fn-call p context) :fncall (gen-fn-call p context)
:iexpr (gen-iexpr p) :iexpr (gen-iexpr p context)
:integer (read-string (strip-leading-zeros (second p))) :integer (read-string (strip-leading-zeros (second p)))
:iop (case (second p) :iop (case (second p)
"/" 'DIFFERENCE "/" 'DIFFERENCE
@ -249,9 +241,14 @@
:fragment p}))) :fragment p})))
:list (gen-dot-terminated-list (rest p)) :list (gen-dot-terminated-list (rest p))
(:lhs :rhs) (generate (second p) context) (:lhs :rhs) (generate (second p) context)
:mexpr (generate (second p) :mexpr) :mexpr (generate (second p) (if (= context :cond-mexpr) context :mexpr))
:mconst (make-beowulf-list :mconst (if (= context :cond-mexpr)
(list 'QUOTE (symbol (upper-case (second p))))) (case (second p)
("T" "F" "NIL") (symbol (second p))
;; else
(list 'QUOTE (symbol (second p))))
;; else
(list 'QUOTE (symbol (second p))))
:mvar (symbol (upper-case (second p))) :mvar (symbol (upper-case (second p)))
:number (generate (second p) context) :number (generate (second p) context)
:octal (let [n (read-string (strip-leading-zeros (second p) "0")) :octal (let [n (read-string (strip-leading-zeros (second p) "0"))

View file

@ -65,7 +65,7 @@
cond-clause := mexpr opt-space arrow opt-space mexpr opt-space; cond-clause := mexpr opt-space arrow opt-space mexpr opt-space;
arrow := '->'; arrow := '->';
args := arg | (opt-space arg semi-colon opt-space)* opt-space arg opt-space; args := arg | (opt-space arg semi-colon opt-space)* opt-space arg opt-space;
arg := mexpr | sexpr; arg := mexpr;
fn-name := mvar; fn-name := mvar;
mvar := #'[a-z][a-z0-9]*'; mvar := #'[a-z][a-z0-9]*';
mconst := #'[A-Z][A-Z0-9]*'; mconst := #'[A-Z][A-Z0-9]*';

View file

@ -68,10 +68,10 @@
(deftest conditional-tests (deftest conditional-tests
(testing "Conditional expressions" (testing "Conditional expressions"
(let [expected "(COND ((ATOM X) X) ((QUOTE T) (FF (CAR X))))" (let [expected "(COND ((ATOM X) X) (T (FF (CAR X))))"
actual (print-str (gsp "[atom[x]->x; T->ff[car[x]]]"))] actual (print-str (gsp "[atom[x]->x; T->ff[car[x]]]"))]
(is (= actual expected))) (is (= actual expected)))
(let [expected "(LABEL FF (LAMBDA (X) (COND ((ATOM X) X) ((QUOTE T) (FF (CAR X))))))" (let [expected "(LABEL FF (LAMBDA (X) (COND ((ATOM X) X) (T (FF (CAR X))))))"
actual (print-str actual (print-str
(generate (generate
(simplify-tree (simplify-tree
@ -88,6 +88,6 @@
(deftest assignment-tests (deftest assignment-tests
(testing "Function assignment" (testing "Function assignment"
(let [expected "(PUT (QUOTE FF) (QUOTE EXPR) (QUOTE (LAMBDA (X) (COND ((ATOM X) X) ((QUOTE T) (FF (CAR X)))))))" (let [expected "(PUT (QUOTE FF) (QUOTE EXPR) (QUOTE (LAMBDA (X) (COND ((ATOM X) X) (T (FF (CAR X)))))))"
actual (print-str (gsp "ff[x]=[atom[x] -> x; T -> ff[car[x]]]"))] actual (print-str (gsp "ff[x]=[atom[x] -> x; T -> ff[car[x]]]"))]
(is (= actual expected))))) (is (= actual expected)))))