diff --git a/.gitignore b/.gitignore
index a0db7e2..795f8a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,7 @@ Sysout*.lsp
*.pdf
src/beowulf/scratch.clj
+
+.portal/vs-code.edn
+
+.portal/
diff --git a/README.md b/README.md
index 364cfe3..47aee7e 100644
--- a/README.md
+++ b/README.md
@@ -48,11 +48,20 @@ 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.
@@ -77,7 +86,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.0-standalone.jar --help
+ java -jar target/uberjar/beowulf-0.3.1-standalone.jar --help
(Obviously, check your version number)
@@ -338,7 +347,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/further_reading.html).
+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).
## License
diff --git a/doc/lisp1.5.md b/doc/lisp1.5.md
index 6042cc8..4a566ed 100644
--- a/doc/lisp1.5.md
+++ b/doc/lisp1.5.md
@@ -961,19 +961,15 @@ 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 forrrls
-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 forms
+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
+Example 1: CAR
fn: CAR
args: ((A B))
The value is A. Note that the interpreter expects a list of arguments. The one argu-
@@ -981,20 +977,18 @@ 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
+Example 2: CONS
fn: CONS
-args: (A (B. C))
-The value is cons[^;(^. c)] = (A. (B. C)).
-The print program will write this as (A B. C).
-```
+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).
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
@@ -1721,6 +1715,7 @@ 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)),
@@ -2709,7 +2704,9 @@ 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[~~; (EXPR (LAMBDA (X) (COND ((ATOM X) X) (T (FF (CAR x))))))]
+```
+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 prop-
erty list for FF.
diff --git a/docs/cloverage/beowulf/bootstrap.clj.html b/docs/cloverage/beowulf/bootstrap.clj.html
index 20afabb..a9efdd4 100644
--- a/docs/cloverage/beowulf/bootstrap.clj.html
+++ b/docs/cloverage/beowulf/bootstrap.clj.html
@@ -38,1213 +38,1465 @@
011 objects."
- 012 (:require [clojure.string :as s]
+ 012 (:require [beowulf.cons-cell :refer [F make-beowulf-list make-cons-cell
- 013 [clojure.tools.trace :refer :all]
+ 013 pretty-print T]]
- 014 [beowulf.cons-cell :refer [make-beowulf-list make-cons-cell NIL T F]]))
+ 014 [beowulf.host :refer [ASSOC ATOM CAAR CAADR CADAR CADDR CADR CAR CDR
+
+
+ 015 CONS ERROR GET LIST NUMBERP PAIRLIS traced?]]
+
+
+ 016 [beowulf.oblist :refer [*options* NIL]]
+
+
+ 017 [clojure.string :as s]
+
+
+ 018 [clojure.tools.trace :refer [deftrace]])
+
+
+ 019 (:import [beowulf.cons_cell ConsCell]
+
+
+ 020 [clojure.lang Symbol]))
- 015
+ 021
- 016 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-
- 017 ;;;
-
-
- 018 ;;; This file is essentially Lisp as defined in Chapter 1 (pages 1-14) of the
-
-
- 019 ;;; Lisp 1.5 Programmer's Manual; that is to say, a very simple Lisp language,
-
-
- 020 ;;; which should, I believe, be sufficient in conjunction with the functions
-
-
- 021 ;;; provided by `beowulf.host`, be sufficient to bootstrap the full Lisp 1.5
-
-
- 022 ;;; interpreter.
+ 022 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
023 ;;;
- 024 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ 024 ;;; Copyright (C) 2022-2023 Simon Brooke
+
+
+ 025 ;;;
+
+
+ 026 ;;; This program is free software; you can redistribute it and/or
+
+
+ 027 ;;; modify it under the terms of the GNU General Public License
+
+
+ 028 ;;; as published by the Free Software Foundation; either version 2
+
+
+ 029 ;;; of the License, or (at your option) any later version.
+
+
+ 030 ;;;
+
+
+ 031 ;;; This program is distributed in the hope that it will be useful,
+
+
+ 032 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+
+
+ 033 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+
+ 034 ;;; GNU General Public License for more details.
+
+
+ 035 ;;;
+
+
+ 036 ;;; You should have received a copy of the GNU General Public License
+
+
+ 037 ;;; along with this program; if not, write to the Free Software
+
+
+ 038 ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+ 039 ;;;
+
+
+ 040 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- 025
+ 041
+
+
+ 042 (declare APPLY EVAL EVCON prog-eval)
+
+
+ 043
+
+
+ 044 ;;;; The PROGram feature ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+ 045
- 026 (declare EVAL)
-
-
- 027
-
-
- 028 (def oblist
+ 046 (def ^:dynamic
- 029 "The default environment."
+ 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
+
+
+ 052 (defn- trace-indent
+
+
+ 053 ([] (trace-indent *depth*))
+
+
+ 054 ([d] (s/join (repeat d " "))))
+
+
+ 055
+
+
+ 056 (def find-target
+
+
+ 057 (memoize
+
+
+ 058 (fn [target body]
+
+
+ 059 (loop [body' body]
- 030 (atom NIL))
+ 060 (cond
-
- 031
+
+ 061 (= body' NIL) (throw (ex-info (str "Mislar GO miercels: `" target "`")
-
- 032 (def ^:dynamic *options*
+
+ 062 {:phase :lisp
- 033 "Command line options from invocation."
-
-
- 034 {})
-
-
- 035
-
-
- 036 (defmacro NULL
+ 063 :function 'PROG
- 037 "Returns `T` if and only if the argument `x` is bound to `NIL`; else `F`."
+ 064 :type :lisp
- 038 [x]
+ 065 :code :A6
- 039 `(if (= ~x NIL) T F))
-
-
- 040
-
-
- 041 (defmacro ATOM
-
-
- 042 "Returns `T` if and only is the argument `x` is bound to and atom; else `F`.
-
-
- 043 It is not clear to me from the documentation whether `(ATOM 7)` should return
-
-
- 044 `T` or `F`. I'm going to assume `T`."
-
-
- 045 [x]
-
-
- 046 `(if (or (symbol? ~x) (number? ~x)) T F))
-
-
- 047
-
-
- 048 (defmacro ATOM?
-
-
- 049 "The convention of returning `F` from predicates, rather than `NIL`, is going
-
-
- 050 to tie me in knots. This is a variant of `ATOM` which returns `NIL`
-
-
- 051 on failure."
-
-
- 052 [x]
-
-
- 053 `(if (or (symbol? ~x) (number? ~x)) T NIL))
-
-
- 054
-
-
- 055 (defn CAR
-
-
- 056 "Return the item indicated by the first pointer of a pair. NIL is treated
-
-
- 057 specially: the CAR of NIL is NIL."
-
-
- 058 [x]
-
-
- 059 (cond
-
-
- 060 (= x NIL) NIL
-
-
- 061 (instance? beowulf.cons_cell.ConsCell x) (.CAR x)
-
-
- 062 :else
-
-
- 063 (throw
-
-
- 064 (Exception.
-
-
- 065 (str "Cannot take CAR of `" x "` (" (.getName (.getClass x)) ")")))))
-
-
- 066
-
-
- 067 (defn CDR
-
-
- 068 "Return the item indicated by the second pointer of a pair. NIL is treated
-
-
- 069 specially: the CDR of NIL is NIL."
-
-
- 070 [x]
-
-
- 071 (cond
-
-
- 072 (= x NIL) NIL
-
-
- 073 (instance? beowulf.cons_cell.ConsCell x) (.CDR x)
-
-
- 074 :else
-
-
- 075 (throw
-
-
- 076 (Exception.
-
-
- 077 (str "Cannot take CDR of `" x "` (" (.getName (.getClass x)) ")")))))
-
-
- 078
-
-
- 079 (defn uaf
-
-
- 080 "Universal access function; `l` is expected to be an arbitrary list, `path`
-
-
- 081 a (clojure) list of the characters `a` and `d`. Intended to make declaring
-
-
- 082 all those fiddly `#'c[ad]+r'` functions a bit easier"
-
-
- 083 [l path]
-
-
- 084 (cond
-
-
- 085 (= l NIL) NIL
+ 066 :target target}))
- 086 (empty? path) l
-
-
- 087 :else (case (last path)
-
-
- 088 \a (uaf (CAR l) (butlast path))
-
-
- 089 \d (uaf (CDR l) (butlast path)))))
-
-
- 090
-
-
- 091 (defn CAAR [x] (uaf x (seq "aa")))
-
-
- 092 (defn CADR [x] (uaf x (seq "ad")))
-
-
- 093 (defn CDDR [x] (uaf x (seq "dd")))
-
-
- 094 (defn CDAR [x] (uaf x (seq "da")))
-
-
- 095
-
-
- 096 (defn CAAAR [x] (uaf x (seq "aaa")))
-
-
- 097 (defn CAADR [x] (uaf x (seq "aad")))
-
-
- 098 (defn CADAR [x] (uaf x (seq "ada")))
-
-
- 099 (defn CADDR [x] (uaf x (seq "add")))
-
-
- 100 (defn CDDAR [x] (uaf x (seq "dda")))
-
-
- 101 (defn CDDDR [x] (uaf x (seq "ddd")))
-
-
- 102 (defn CDAAR [x] (uaf x (seq "daa")))
-
-
- 103 (defn CDADR [x] (uaf x (seq "dad")))
-
-
- 104
-
-
- 105 (defn CAAAAR [x] (uaf x (seq "aaaa")))
-
-
- 106 (defn CAADAR [x] (uaf x (seq "aada")))
-
-
- 107 (defn CADAAR [x] (uaf x (seq "adaa")))
-
-
- 108 (defn CADDAR [x] (uaf x (seq "adda")))
-
-
- 109 (defn CDDAAR [x] (uaf x (seq "ddaa")))
-
-
- 110 (defn CDDDAR [x] (uaf x (seq "ddda")))
-
-
- 111 (defn CDAAAR [x] (uaf x (seq "daaa")))
-
-
- 112 (defn CDADAR [x] (uaf x (seq "dada")))
-
-
- 113 (defn CAAADR [x] (uaf x (seq "aaad")))
-
-
- 114 (defn CAADDR [x] (uaf x (seq "aadd")))
-
-
- 115 (defn CADADR [x] (uaf x (seq "adad")))
-
-
- 116 (defn CADDDR [x] (uaf x (seq "addd")))
-
-
- 117 (defn CDDADR [x] (uaf x (seq "ddad")))
-
-
- 118 (defn CDDDDR [x] (uaf x (seq "dddd")))
-
-
- 119 (defn CDAADR [x] (uaf x (seq "daad")))
-
-
- 120 (defn CDADDR [x] (uaf x (seq "dadd")))
-
-
- 121
+ 067 (= (.getCar body') target) body'
- 122 (defn EQ
-
-
- 123 "Returns `T` if and only if both `x` and `y` are bound to the same atom,
-
-
- 124 else `F`."
-
-
- 125 [x y]
-
-
- 126 (if (and (= (ATOM x) T) (= x y)) T F))
+ 068 :else (recur (.getCdr body')))))))
- 127
+ 069
- 128 (defn EQUAL
+ 070 (defn- prog-cond
- 129 "This is a predicate that is true if its two arguments are identical
+ 071 "Like `EVCON`, q.v. except using `prog-eval` instead of `EVAL` and not
- 130 S-expressions, and false if they are different. (The elementary predicate
+ 072 throwing an error if no clause matches."
- 131 `EQ` is defined only for atomic arguments.) The definition of `EQUAL` is
+ 073 [clauses vars env depth]
-
- 132 an example of a conditional expression inside a conditional expression.
+
+ 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)))
- 133
+ 081
-
- 134 NOTE: returns `F` on failure, not `NIL`"
-
-
- 135 [x y]
+
+ 082 (defn- merge-vars [vars env]
- 136 (cond
+ 083 (reduce
-
- 137 (= (ATOM x) T) (EQ x y)
+
+ 084 #(make-cons-cell
-
- 138 (= (EQUAL (CAR x) (CAR y)) T) (EQUAL (CDR x) (CDR y))
+
+ 085 (make-cons-cell %2 (@vars %2))
- 139 :else F))
+ 086 env)
+
+
+ 087 env
+
+
+ 088 (keys @vars)))
+
+
+ 089
+
+
+ 090 (defn prog-eval
+
+
+ 091 "Like `EVAL`, q.v., except handling symbols, and expressions starting
+
+
+ 092 `GO`, `RETURN`, `SET` and `SETQ` specially."
+
+
+ 093 [expr vars env depth]
+
+
+ 094 (cond
+
+
+ 095 (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 SUBST
+ 141 (defn PROG
- 142 "This function gives the result of substituting the S-expression `x` for
+ 142 "The accursed `PROG` feature. See page 71 of the manual.
- 143 all occurrences of the atomic symbol `y` in the S-expression `z`."
+ 143
- 144 [x y z]
-
-
- 145 (cond
-
-
- 146 (= (EQUAL y z) T) x
-
-
- 147 (= (ATOM? z) T) z ;; NIL is a symbol
+ 144 Lisp 1.5 introduced `PROG`, and most Lisps have been stuck with it ever
- 148 :else
+ 145 since. It introduces imperative programming into what should be a pure
-
- 149 (make-cons-cell (SUBST x y (CAR z)) (SUBST x y (CDR z)))))
+
+ 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 (defn APPEND
+
+ 151 The arguments, which are unevaluated, are a list of forms, the first of
- 152 "Append the the elements of `y` to the elements of `x`.
-
-
- 153
+ 152 which is expected to be a list of symbols which will be treated as names
- 154 All args are assumed to be `beowulf.cons-cell/ConsCell` objects.
+ 153 of variables within the program, and the rest of which (the 'program body')
- 155 See page 11 of the Lisp 1.5 Programmers Manual."
+ 154 are either lists or symbols. Lists are treated as Lisp expressions which
- 156 [x y]
-
-
- 157 (cond
-
-
- 158 (= x NIL) y
+ 155 may be evaulated in turn. Symbols are treated as targets for the `GO`
- 159 :else
-
-
- 160 (make-cons-cell (CAR x) (APPEND (CDR x) y))))
-
-
- 161
-
-
- 162
-
-
- 163 (defn MEMBER
+ 156 statement.
- 164 "This predicate is true if the S-expression `x` occurs among the elements
+ 157
- 165 of the list `y`.
+ 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 All args are assumed to be symbols or `beowulf.cons-cell/ConsCell` objects.
+ 167 If the target is not found, an error with the code `A6` should be thrown.
+
+
+ 168
- 168 See page 11 of the Lisp 1.5 Programmers Manual."
+ 169 **RETURN:**
- 169 [x y]
+ 170 A `RETURN` statement takes the form `(RETURN value)`, where
-
- 170 (cond
+
+ 171 `value` is any value. Following the evaluation of a `RETURN` statement,
-
- 171 (= y NIL) F ;; NOTE: returns F on falsity, not NIL
+
+ 172 the `PROG` should immediately exit without executing any further
-
- 172 (= (EQUAL x (CAR y)) T) T
-
-
- 173 :else (MEMBER x (CDR y))))
+
+ 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))))
+
- 175 (defn PAIRLIS
+ 197 body (.getCdr program)
-
- 176 "This function gives the list of pairs of corresponding elements of the
+
+ 198 targets (set (filter symbol? body))]
-
- 177 lists `x` and `y`, and APPENDs this to the list `a`. The resultant list
+
+ 199 (when trace (do
-
- 178 of pairs, which is like a table with two columns, is called an
+
+ 200 (println "Program:")
-
- 179 association list.
-
-
- 180
-
-
- 181 Eessentially, it builds the environment on the stack, implementing shallow
-
-
- 182 binding.
-
-
- 183
-
-
- 184 All args are assumed to be `beowulf.cons-cell/ConsCell` objects.
-
-
- 185 See page 12 of the Lisp 1.5 Programmers Manual."
-
-
- 186 [x y a]
+
+ 201 (pretty-print program))) ;; for debugging
- 187 (cond
+ 202 (loop [cursor body]
-
- 188 ;; the original tests only x; testing y as well will be a little more
+
+ 203 (let [step (if (= NIL cursor) NIL (.getCar cursor))]
-
- 189 ;; robust if `x` and `y` are not the same length.
+
+ 204 (when trace (do (println "Executing step: " step)
-
- 190 (or (= NIL x) (= NIL y)) a
+
+ 205 (println " with vars: " @vars)))
-
- 191 :else (make-cons-cell
+
+ 206 (cond (= cursor NIL) NIL
+
+
+ 207 (symbol? step) (recur (.getCdr cursor))
- 192 (make-cons-cell (CAR x) (CAR y))
+ 208 :else (let [v (prog-eval (.getCar cursor) vars env depth)]
-
- 193 (PAIRLIS (CDR x) (CDR y) a))))
-
-
- 194
-
-
- 195 (defn ASSOC
-
-
- 196 "If a is an association list such as the one formed by PAIRLIS in the above
-
-
- 197 example, then assoc will produce the first pair whose first term is x. Thus
-
-
- 198 it is a table searching function.
-
-
- 199
-
-
- 200 All args are assumed to be `beowulf.cons-cell/ConsCell` objects.
-
-
- 201 See page 12 of the Lisp 1.5 Programmers Manual."
-
-
- 202 [x a]
-
-
- 203 (cond
+
+ 209 (when trace (println " --> " v))
- 204 (= NIL a) NIL ;; this clause is not present in the original but is added for
+ 210 (if (instance? ConsCell v)
-
- 205 ;; robustness.
-
-
- 206 (= (EQUAL (CAAR a) x) T) (CAR a)
-
-
- 207 :else
-
-
- 208 (ASSOC x (CDR a))))
-
-
- 209
+
+ 211 (case (.getCar v)
- 210 (defn- SUB2
+ 212 *PROGGO* (let [target (.getCdr v)]
-
- 211 "Internal to `SUBLIS`, q.v., which SUBSTitutes into a list from a store.
-
-
- 212 ? I think this is doing variable binding in the stack frame?"
-
-
- 213 [a z]
-
-
- 214 (cond
-
-
- 215 (= NIL a) z
-
-
- 216 (= (CAAR a) z) (CDAR a) ;; TODO: this looks definitely wrong
-
-
- 217 :else
-
-
- 218 (SUB2 (CDR a) z)))
-
-
- 219
+
+ 213 (if (targets target)
- 220 (defn SUBLIS
+ 214 (recur (find-target target body))
+
+
+ 215 (throw (ex-info (str "Uncynlic GO miercels `"
+
+
+ 216 target "`")
+
+
+ 217 {:phase :lisp
- 221 "Here `a` is assumed to be an association list of the form
+ 218 :function 'PROG
+
+
+ 219 :args program
- 222 `((ul . vl)...(un . vn))`, where the `u`s are atomic, and `y` is any
+ 220 :type :lisp
- 223 S-expression. What `SUBLIS` does, is to treat the `u`s as variables when
+ 221 :code :A6
+
+
+ 222 :target target
+
+
+ 223 :targets targets}))))
+
+
+ 224 *PROGRETURN* (.getCdr v)
- 224 they occur in `y`, and to SUBSTitute the corresponding `v`s from the pair
+ 225 ;; else
-
- 225 list.
+
+ 226 (recur (.getCdr cursor)))
-
- 226
-
-
- 227 My interpretation is that this is variable binding in the stack frame.
+
+ 227 (recur (.getCdr cursor)))))))))
228
- 229 All args are assumed to be `beowulf.cons-cell/ConsCell` objects.
-
-
- 230 See page 12 of the Lisp 1.5 Programmers Manual."
-
-
- 231 [a y]
-
-
- 232 (cond
-
-
- 233 (= (ATOM? y) T) (SUB2 a y)
-
-
- 234 :else
-
-
- 235 (make-cons-cell (SUBLIS a (CAR y)) (SUBLIS a (CDR y)))))
+ 229 ;;;; Tracing execution ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- 236
+ 230
- 237 (defn interop-interpret-q-name
+ 231 (defn- trace-call
- 238 "For interoperation with Clojure, it will often be necessary to pass
+ 232 "Show a trace of a call to the function named by this `function-symbol`
- 239 qualified names that are not representable in Lisp 1.5. This function
+ 233 with these `args` at this depth."
- 240 takes a sequence in the form `(PART PART PART... NAME)` and returns
+ 234 [function-symbol args depth]
-
- 241 a symbol in the form `PART.PART.PART/NAME`. This symbol will then be
-
-
- 242 tried in both that form and lower-cased. Names with hyphens or
-
-
- 243 underscores cannot be represented with this scheme."
-
-
- 244 [l]
-
-
- 245 (if
-
-
- 246 (seq? l)
-
-
- 247 (symbol
-
-
- 248 (s/reverse
+
+ 235 (when (traced? function-symbol)
- 249 (s/replace-first
+ 236 (let [indent (trace-indent depth)]
-
- 250 (s/reverse
-
-
- 251 (s/join "." (map str l)))
-
-
- 252 "."
-
-
- 253 "/")))
-
-
- 254 l))
+
+ 237 (println (str indent "> " function-symbol " " args)))))
- 255
+ 238
-
- 256 (deftrace INTEROP
+
+ 239 (defn- trace-response
- 257 "Clojure (or other host environment) interoperation API. `fn-symbol` is expected
+ 240 "Show a trace of this `response` from the function named by this
- 258 to be either
+ 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 1. a symbol bound in the host environment to a function; or
+
+ 260 (defn SASSOC
- 261 2. a sequence (list) of symbols forming a qualified path name bound to a
+ 261 "Like `ASSOC`, but with an action to take if no value is found.
- 262 function.
-
-
- 263
+ 262
- 264 Lower case characters cannot normally be represented in Lisp 1.5, so both the
+ 263 From the manual, page 60:
- 265 upper case and lower case variants of `fn-symbol` will be tried. If the
+ 264
- 266 function you're looking for has a mixed case name, that is not currently
+ 265 'The function `sassoc` searches `y`, which is a list of dotted pairs, for
- 267 accessible.
-
-
- 268
+ 266 a pair whose first element that is `x`. If such a pair is found, the value
- 269 `args` is expected to be a Lisp 1.5 list of arguments to be passed to that
+ 267 of `sassoc` is this pair. Otherwise the function `u` of no arguments is
- 270 function. Return value must be something acceptable to Lisp 1.5, so either
+ 268 taken as the value of `sassoc`.'"
- 271 a symbol, a number, or a Lisp 1.5 list.
-
-
- 272
-
-
- 273 If `fn-symbol` is not found (even when cast to lower case), or is not a function,
-
-
- 274 or the value returned cannot be represented in Lisp 1.5, an exception is thrown
-
-
- 275 with `:cause` bound to `:interop` and `:detail` set to a value representing the
-
-
- 276 actual problem."
-
-
- 277 [fn-symbol args]
-
-
- 278 (let
-
-
- 279 [q-name (if
-
-
- 280 (seq? fn-symbol)
-
-
- 281 (interop-interpret-q-name fn-symbol)
-
-
- 282 fn-symbol)
+ 269 [x y u]
- 283 l-name (symbol (s/lower-case q-name))
-
-
- 284 f (cond
-
-
- 285 (try
-
-
- 286 (fn? (eval l-name))
-
-
- 287 (catch java.lang.ClassNotFoundException e nil)) (eval l-name)
-
-
- 288 (try
-
-
- 289 (fn? (eval q-name))
-
-
- 290 (catch java.lang.ClassNotFoundException e nil)) (eval q-name)
-
-
- 291 :else (throw
-
-
- 292 (ex-info
-
-
- 293 (str "INTEROP: unknown function `" fn-symbol "`")
+ 270 (let [v (ASSOC x y)]
- 294 {:cause :interop
-
-
- 295 :detail :not-found
-
-
- 296 :name fn-symbol
-
-
- 297 :also-tried l-name})))
-
-
- 298 result (eval (cons f args))]
-
-
- 299 (cond
+ 271 (if-not (= v NIL) v
- 300 (instance? beowulf.cons_cell.ConsCell result) result
-
-
- 301 (seq? result) (make-beowulf-list result)
-
-
- 302 (symbol? result) result
-
-
- 303 (string? result) (symbol result)
-
-
- 304 (number? result) result
-
-
- 305 :else (throw
-
-
- 306 (ex-info
-
-
- 307 (str "INTEROP: Cannot return `" result "` to Lisp 1.5.")
-
-
- 308 {:cause :interop
-
-
- 309 :detail :not-representable
-
-
- 310 :result result})))))
+ 272 (APPLY u NIL NIL))))
- 311
+ 273
+
+
+ 274
+
+
+ 275 ;;;; APPLY ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+ 276
- 312 (defn APPLY
+ 277 (defn try-resolve-subroutine
- 313 "For bootstrapping, at least, a version of APPLY written in Clojure.
+ 278 "Attempt to resolve this `subr` with these `args`."
- 314 All args are assumed to be symbols or `beowulf.cons-cell/ConsCell` objects.
-
-
- 315 See page 13 of the Lisp 1.5 Programmers Manual."
-
-
- 316 [function args environment]
-
-
- 317 (cond
-
-
- 318 (=
-
-
- 319 (ATOM? function)
-
-
- 320 T)(cond
-
-
- 321 ;; TODO: doesn't check whether `function` is bound in the environment;
-
-
- 322 ;; we'll need that before we can bootstrap.
-
-
- 323 (= function 'CAR) (CAAR args)
-
-
- 324 (= function 'CDR) (CDAR args)
+ 279 [subr args]
- 325 (= function 'CONS) (make-cons-cell (CAR args) (CADR args))
+ 280 (when (and subr (not= subr NIL))
-
- 326 (= function 'ATOM) (if (ATOM? (CAR args)) T NIL)
-
-
- 327 (= function 'EQ) (if (= (CAR args) (CADR args)) T NIL)
+
+ 281 (try @(resolve subr)
- 328 :else
-
-
- 329 (APPLY
+ 282 (catch Throwable any
- 330 (EVAL function environment)
-
-
- 331 args
-
-
- 332 environment))
-
-
- 333 (= (first function) 'LAMBDA) (EVAL
-
-
- 334 (CADDR function)
+ 283 (throw (ex-info "þegnung (SUBR) ne āfand"
- 335 (PAIRLIS (CADR function) args environment))
-
-
- 336 (= (first function) 'LABEL) (APPLY
-
-
- 337 (CADDR function)
+ 284 {:phase :apply
- 338 args
+ 285 :function subr
- 339 (make-cons-cell
+ 286 :args args
+
+
+ 287 :type :beowulf}
- 340 (make-cons-cell
-
-
- 341 (CADR function)
-
-
- 342 (CADDR function))
-
-
- 343 environment))))
+ 288 any))))))
- 344
+ 289
-
- 345 (defn- EVCON
+
+ 290 (defn- apply-symbolic
- 346 "Inner guts of primitive COND. All args are assumed to be
+ 291 "Apply this `funtion-symbol` to these `args` in this `environment` and
- 347 `beowulf.cons-cell/ConsCell` objects.
+ 292 return the result."
- 348 See page 13 of the Lisp 1.5 Programmers Manual."
-
-
- 349 [clauses env]
-
-
- 350 (if
-
-
- 351 (not= (EVAL (CAAR clauses) env) NIL)
-
-
- 352 (EVAL (CADAR clauses) env)
-
-
- 353 (EVCON (CDR clauses) env)))
-
-
- 354
-
-
- 355 (defn- EVLIS
-
-
- 356 "Map `EVAL` across this list of `args` in the context of this
-
-
- 357 `env`ironment.All args are assumed to be `beowulf.cons-cell/ConsCell` objects.
-
-
- 358 See page 13 of the Lisp 1.5 Programmers Manual."
-
-
- 359 [args env]
-
-
- 360 (cond
+ 293 [^Symbol function-symbol args ^ConsCell environment depth]
- 361 (= NIL args) NIL
+ 294 (trace-call function-symbol args depth)
-
- 362 :else
+
+ 295 (let [lisp-fn (value function-symbol '(EXPR FEXPR)) ;; <-- should these be handled differently? I think so!
-
- 363 (make-cons-cell
-
-
- 364 (EVAL (CAR args) env)
-
-
- 365 (EVLIS (CDR args) env))))
-
-
- 366
-
-
- 367 (deftrace traced-eval
-
-
- 368 "Essentially, identical to EVAL except traced."
-
-
- 369 [expr env]
-
-
- 370 (cond
-
-
- 371 (=
-
-
- 372 (ATOM? expr) T)
-
-
- 373 (CDR (ASSOC expr env))
-
-
- 374 (=
-
-
- 375 (ATOM? (CAR expr))
-
-
- 376 T)(cond
-
-
- 377 (= (CAR expr) 'QUOTE) (CADR expr)
-
-
- 378 (= (CAR expr) 'COND) (EVCON (CDR expr) env)
-
-
- 379 :else (APPLY
-
-
- 380 (CAR expr)
-
-
- 381 (EVLIS (CDR expr) env)
-
-
- 382 env))
-
-
- 383 :else (APPLY
-
-
- 384 (CAR expr)
-
-
- 385 (EVLIS (CDR expr) env)
-
-
- 386 env)))
-
-
- 387
-
-
- 388 (defn EVAL
-
-
- 389 "For bootstrapping, at least, a version of EVAL written in Clojure.
-
-
- 390 All args are assumed to be symbols or `beowulf.cons-cell/ConsCell` objects.
-
-
- 391 See page 13 of the Lisp 1.5 Programmers Manual."
-
-
- 392 [expr env]
+
+ 296 args' (cond (= NIL args) args
- 393 (cond
+ 297 (empty? args) NIL
- 394 (true? (:trace *options*))
-
-
- 395 (traced-eval expr env)
-
-
- 396 (=
-
-
- 397 (ATOM? expr) T)
-
-
- 398 (CDR (ASSOC expr env))
-
-
- 399 (=
-
-
- 400 (ATOM? (CAR expr))
-
-
- 401 T)(cond
-
-
- 402 (= (CAR expr) 'QUOTE) (CADR expr)
-
-
- 403 (= (CAR expr) 'COND) (EVCON (CDR expr) env)
-
-
- 404 :else (APPLY
-
-
- 405 (CAR expr)
-
-
- 406 (EVLIS (CDR expr) env)
-
-
- 407 env))
-
-
- 408 :else (APPLY
+ 298 (instance? ConsCell args) args
- 409 (CAR expr)
+ 299 :else (make-beowulf-list args))
-
- 410 (EVLIS (CDR expr) env)
+
+ 300 subr (value function-symbol '(SUBR FSUBR))
+
+
+ 301 host-fn (try-resolve-subroutine subr args')
+
+
+ 302 result (cond (and lisp-fn
+
+
+ 303 (not= lisp-fn NIL)) (APPLY lisp-fn args' environment depth)
+
+
+ 304 host-fn (try
+
+
+ 305 (apply host-fn (when (instance? ConsCell args') args'))
+
+
+ 306 (catch Exception any
+
+
+ 307 (throw (ex-info (str "Uncynlic þegnung: "
+
+
+ 308 (.getMessage any))
+
+
+ 309 {:phase :apply
+
+
+ 310 :function function-symbol
+
+
+ 311 :args args
+
+
+ 312 :type :beowulf}
+
+
+ 313 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*)
- 411 env)))
+ 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"
+
+
+ 388 {:phase :apply
+
+
+ 389 :function function
+
+
+ 390 :args args
+
+
+ 391 :type :beowulf}))))]
+
+
+ 392 (trace-response 'APPLY result depth)
+
+
+ 393 result))))
- 412
+ 394
+
+
+ 395 ;;;; EVAL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- 413
+ 396
+
+
+ 397 (defn- EVCON
+
+
+ 398 "Inner guts of primitive COND. All `clauses` are assumed to be
+
+
+ 399 `beowulf.cons-cell/ConsCell` objects. Note that tests in Lisp 1.5
+
+
+ 400 often return `F`, not `NIL`, on failure. If no clause matches,
+
+
+ 401 then, strictly, we throw an error with code `:A3`.
- 414
+ 402
+
+
+ 403 See pages 13 and 71 of the Lisp 1.5 Programmers Manual."
+
+
+ 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))
+
+
+ 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]
+
+
+ 467 (trace-call 'EVAL (list expr env depth) depth)
+
+
+ 468 (let [result (cond
+
+
+ 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)
+
+
+ 473 (string? expr) (if (:strict *options*)
+
+
+ 474 (throw
+
+
+ 475 (ex-info
+
+
+ 476 (str "EVAL: strings not allowed in strict mode: \"" expr "\"")
+
+
+ 477 {:phase :eval
+
+
+ 478 :detail :strict
+
+
+ 479 :expr expr}))
+
+
+ 480 (symbol expr))
+
+
+ 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)))]
+
+
+ 496 (trace-response 'EVAL result depth)
+
+
+ 497 result)))
+
+
+ 498