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

- 023    ;; (:import [org.jline.reader LineReader LineReaderBuilder] + 023    (:require [beowulf.oblist :refer [*options* oblist]])
- 024    ;;          [org.jline.terminal TerminalBuilder]) + 024    (:import [org.jline.reader.impl.completer StringsCompleter]
- 025    ) + 025             [org.jline.reader.impl DefaultParser DefaultParser$Bracket] +
+ + 026             [org.jline.reader LineReaderBuilder] +
+ + 027             [org.jline.terminal TerminalBuilder] +
+ + 028             [org.jline.widget AutopairWidgets AutosuggestionWidgets]))
- 026   + 029  
- 027  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + 030  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- 028  ;;; + 031  ;;;
- 029  ;;; Copyright (C) 2022-2023 Simon Brooke + 032  ;;; Copyright (C) 2022-2023 Simon Brooke
- 030  ;;; + 033  ;;;
- 031  ;;; This program is free software; you can redistribute it and/or + 034  ;;; This program is free software; you can redistribute it and/or
- 032  ;;; modify it under the terms of the GNU General Public License + 035  ;;; modify it under the terms of the GNU General Public License
- 033  ;;; as published by the Free Software Foundation; either version 2 + 036  ;;; as published by the Free Software Foundation; either version 2
- 034  ;;; of the License, or (at your option) any later version. + 037  ;;; of the License, or (at your option) any later version.
- 035  ;;;  + 038  ;;; 
- 036  ;;; This program is distributed in the hope that it will be useful, + 039  ;;; This program is distributed in the hope that it will be useful,
- 037  ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of + 040  ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- 038  ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + 041  ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- 039  ;;; GNU General Public License for more details. + 042  ;;; GNU General Public License for more details.
- 040  ;;;  + 043  ;;; 
- 041  ;;; You should have received a copy of the GNU General Public License + 044  ;;; You should have received a copy of the GNU General Public License
- 042  ;;; along with this program; if not, write to the Free Software + 045  ;;; along with this program; if not, write to the Free Software
- 043  ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. + 046  ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- 044  ;;; + 047  ;;;
- 045  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + 048  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- 046   + 049  
- 047  ;; It looks from the example given [here](https://github.com/jline/jline3/blob/master/demo/src/main/java/org/jline/demo/Repl.java) + 050  ;; It looks from the example given [here](https://github.com/jline/jline3/blob/master/demo/src/main/java/org/jline/demo/Repl.java)
- 048  ;; as though JLine could be used to build a perfect line-reader for Beowulf; but it also + 051  ;; as though JLine could be used to build a perfect line-reader for Beowulf; but it also
- 049  ;; looks as though you'd need a DPhil in JLine to write it, and I don't have + 052  ;; looks as though you'd need a DPhil in JLine to write it, and I don't have
- 050  ;; the time. + 053  ;; the time.
- 051   + 054   +
+ + 055  (defn build-completer
- 052  ;; (def get-reader + 056    "Build a completer which takes tokens from the oblist.
- 053  ;;   "Return a reader, first constructing it if necessary. + 057     
- 054      + 058     This is sort-of working, in as much as hitting <TAB> on a blank line will 
- 055  ;;    **NOTE THAT** this is not settled API. The existence and call signature of + 059     show a table of values from the oblist, but hitting <TAB> after you've 
- 056  ;;    this function is not guaranteed in future versions." + 060     started input does not show potential completions for tokens you've started."
- 057  ;;   (memoize (fn [] + 061    []
- - 058  ;;   (let [term (.build (.system (TerminalBuilder/builder) true))] -
- - 059  ;;     (.build (.terminal (LineReaderBuilder/builder) term)))))) + + 062    (StringsCompleter. (map #(str (first %)) @oblist)))
- 060   + 063  
- 061  ;; (defn read-chars + 064  ;; This breaks; it is not correctly resolving the Enum, although I can't work out
- 062  ;;   "A drop-in replacement for `clojure.core/read-line`, except that line editing + 065  ;; why not.
- 063  ;;    and history should be enabled. + 066  ;; (defn build-parser
- 064      + 067  ;;   []
- 065  ;;    **NOTE THAT** this does not work yet, but it is in the API because I hope  + 068  ;;   (println "Building parser")
- 066  ;;    that it will work later!" + 069  ;;   (let [parser (DefaultParser.)]
- 067  ;;   []  + 070  ;;     (doall
- 068  ;;     (let [eddie (get-reader)] + 071  ;;      (.setEofOnUnclosedBracket 
- 069  ;;       (loop [s (.readLine eddie)] + 072  ;;       parser DefaultParser$Bracket/ROUND)))) +
+ + 073   +
+ + 074  (def get-reader
- 070  ;;       (if (and (= (count (re-seq #"\(" s)) + 075    "Return a reader, first constructing it if necessary.
- 071  ;;            (count (re-seq #"\)" s))) + 076     
- 072  ;;                (= (count (re-seq #"\[]" s)) + 077     **NOTE THAT** this is not settled API. The existence and call signature of
- 073  ;;                   (count (re-seq #"\]" s)))) + 078     this function is not guaranteed in future versions." +
+ + 079    (memoize (fn [] +
+ + 080              (let [term (.build (.system (TerminalBuilder/builder) true)) +
+ + 081                    reader (-> (LineReaderBuilder/builder) +
+ + 082                                (.terminal  term) +
+ + 083                                (.completer  (build-completer))
- 074  ;;         s + 084   ;;       #(.parser % (build-parser))
- 075  ;;         (recur (str s " " (.readLine eddie))))))) + 085                                (.build)) +
+ + 086                    ;; apw (AutopairWidgets. reader false) +
+ + 087                    ;; asw (AutosuggestionWidgets. reader) +
+ + 088                    ] +
+ + 089                ;; (.enable apw) +
+ + 090                ;; (.enable asw) +
+ + 091                reader)))) +
+ + 092   +
+ + 093  (defn read-chars +
+ + 094    "A drop-in replacement for `clojure.core/read-line`, except that line editing +
+ + 095     and history should be enabled. +
+ + 096      +
+ + 097     **NOTE THAT** this does not fully work yet, but it is in the API because I  +
+ + 098     hope that it will work later!" +
+ + 099    [prompt] +
+ + 100    (let [eddie (get-reader)] +
+ + 101      (loop [s (.readLine eddie (str prompt " "))] +
+ + 102        (if (and (= (count (re-seq #"\(" s)) +
+ + 103                    (count (re-seq #"\)" s))) +
+ + 104                 (= (count (re-seq #"\[]" s)) +
+ + 105                    (count (re-seq #"\]" s)))) +
+ + 106          s +
+ + 107          (recur (str s " " (.readLine eddie ":: ")))))))
diff --git a/docs/cloverage/index.html b/docs/cloverage/index.html index 8f8236c..69296cf 100644 --- a/docs/cloverage/index.html +++ b/docs/cloverage/index.html @@ -16,11 +16,11 @@ beowulf.bootstrap
624
352
-63.93 % + style="width:64.03688524590164%; + float:left;"> 625
351
+64.04 %
138
beowulf.cons-cell
348
133
-72.35 % + style="width:76.0914760914761%; + float:left;"> 366
115
+76.09 %
109
114
9
27
-81.38 % + style="width:15.172413793103448%; + float:left;"> 22 +84.83 % 27423145 beowulf.core
198
87
-69.47 % + style="width:73.94366197183099%; + float:left;"> 210
74
+73.94 %
50
4
15
-78.26 % + style="width:78.26086956521739%; + float:left;"> 54
2
13
+81.16 % 132669 beowulf.host
1027
1374
-42.77 % + style="width:46.61776691116545%; + float:left;"> 1144
1310
+46.62 %
137
163
37
81
-68.24 % -57166255 + style="width:23.954372623574145%; + float:left;"> 63 +76.05 % +58867263 beowulf.interop
181
43.96 %
33
6
32
-54.93 % -1711271 + style="width:45.833333333333336%; + float:left;"> 33 +54.17 % +1711272 beowulf.manual
beowulf.oblist
9
+ float:left;"> 11
100.00 %
beowulf.read
87
89
-49.43 % + style="width:54.36893203883495%; + float:left;"> 112
94
+54.37 %
21
29
3
15
-61.54 % -108939 +68.09 % +1201047 beowulf.reader.char-reader
1
-100.00 % + style="width:10.0%; + float:left;"> 7
63
+10.00 %
1
-100.00 % -7541 + style="width:26.31578947368421%; + float:left;"> 5
14
+26.32 % +107619 beowulf.reader.generate
Totals: -64.63 % +65.64 % -74.41 % +75.70 % diff --git a/resources/lisp1.5.lsp b/resources/lisp1.5.lsp index e56bc7d..09bf054 100644 --- a/resources/lisp1.5.lsp +++ b/resources/lisp1.5.lsp @@ -170,6 +170,7 @@ (COND ((NULL X) (U)) ((EQ (CAR X) Y) (CDR X)) (T (PROP (CDR X) Y U))))) + (PUT 32767 SUBR (BEOWULF HOST PUT)) (QUOTE 32767 EXPR (LAMBDA (X) X)) (QUOTIENT 32767 SUBR (BEOWULF HOST QUOTIENT)) (RANGE diff --git a/src/beowulf/core.clj b/src/beowulf/core.clj index dacedef..74131ff 100644 --- a/src/beowulf/core.clj +++ b/src/beowulf/core.clj @@ -53,7 +53,8 @@ (.canRead (io/file %))) "Could not find sysout file"]] ["-s" "--strict" "Strictly interpret the Lisp 1.5 language, without extensions."] - ["-t" "--time" "Time evaluations."]]) + ["-t" "--time" "Time evaluations."] + ["-x" "--testing" "Disable the jline reader - useful when piping input."]]) (defn- re "Like REPL, but it isn't a loop and doesn't print." diff --git a/src/beowulf/host.clj b/src/beowulf/host.clj index 48f622d..56fae9a 100644 --- a/src/beowulf/host.clj +++ b/src/beowulf/host.clj @@ -91,22 +91,21 @@ (cond (= l NIL) NIL (empty? path) l - :else - (try - (case (last path) - \a (uaf (.first l) (butlast path)) - \d (uaf (.getCdr l) (butlast path)) - (throw (ex-info (str "uaf: unexpected letter in path (only `a` and `d` permitted): " (last path)) - {:cause :uaf - :detail :unexpected-letter - :expr (last path)}))) - (catch ClassCastException e - (throw (ex-info - (str "uaf: Not a LISP list? " (type l)) - {:cause :uaf - :detail :not-a-lisp-list - :expr l} - e)))))) + (not (instance? ConsCell l)) (throw (ex-info (str "Ne liste: " + l "; " (type l)) + {:phase :eval + :function "universal access function" + :args [l path] + :type :beowulf})) + :else (case (last path) + \a (uaf (.first l) (butlast path)) + \d (uaf (.getCdr l) (butlast path)) + (throw (ex-info (str "uaf: unexpected letter in path (only `a` and `d` permitted): " + (last path)) + {:phase :eval + :function "universal access function" + :args [l path] + :type :beowulf}))))) (defmacro CAAR [x] `(uaf ~x '(\a \a))) (defmacro CADR [x] `(uaf ~x '(\a \d))) @@ -433,6 +432,21 @@ "The unexplained magic number which marks the start of a property list." (Integer/parseInt "77777" 8)) +(defn hit-or-miss-assoc + "Find the position of the binding of this `target` in a Lisp 1.5 + property list `plist`. + + Lisp 1.5 property lists are not assoc lists, but lists of the form + `(name value name value name value...)`. It's therefore necessary to + recurse down the list two entries at a time to avoid confusing names + with values." + [target plist] + (if (and (instance? ConsCell plist)(even? (count plist))) + (cond (= plist NIL) NIL + (= (first plist) target) plist + :else (hit-or-miss-assoc target (CDDR plist))) + NIL)) + (defn PUT "Put this `value` as the value of the property indicated by this `indicator` of this `symbol`. Return `value` on success. @@ -440,22 +454,25 @@ NOTE THAT there is no `PUT` defined in the manual, but it would have been easy to have defined it so I don't think this fully counts as an extension." [symbol indicator value] - (if-let [binding (ASSOC symbol @oblist)] - (if-let [prop (ASSOC indicator (CDDR binding))] - (RPLACD prop value) - (RPLACD binding - (make-cons-cell - magic-marker - (make-cons-cell - indicator - (make-cons-cell value (CDDR binding)))))) - (swap! - oblist - (fn [ob s p v] - (make-cons-cell - (make-beowulf-list (list s magic-marker p v)) - ob)) - symbol indicator value))) + (let [binding (ASSOC symbol @oblist)] + (if (instance? ConsCell binding) + (let [prop (hit-or-miss-assoc indicator (CDDR binding))] + (if (instance? ConsCell prop) + (RPLACA (CDR prop) value) + (RPLACD binding + (make-cons-cell + magic-marker + (make-cons-cell + indicator + (make-cons-cell value (CDDR binding))))))) + (swap! + oblist + (fn [ob s p v] + (make-cons-cell + (make-beowulf-list (list s magic-marker p v)) + ob)) + symbol indicator value))) + value) (defn GET "From the manual: diff --git a/src/beowulf/oblist.clj b/src/beowulf/oblist.clj index 38aa999..8769b7f 100644 --- a/src/beowulf/oblist.clj +++ b/src/beowulf/oblist.clj @@ -41,5 +41,5 @@ (def ^:dynamic *options* "Command line options from invocation." - {}) + {:testing true}) diff --git a/src/beowulf/read.clj b/src/beowulf/read.clj index 38860dc..8ed736a 100644 --- a/src/beowulf/read.clj +++ b/src/beowulf/read.clj @@ -83,16 +83,28 @@ (throw (ex-info "Ne can forstande " (assoc parse-tree :source source)))) (generate (simplify parse-tree))))) -(defn read-from-console - "Attempt to read a complete lisp expression from the console." - [prompt] - (loop [r (read-chars prompt)] - (if (and (= (count (re-seq #"\(" r)) - (count (re-seq #"\)" r))) +(defn- dummy-read-chars [prompt] + (loop [r "" p prompt] + (if (and (seq r) + (= (count (re-seq #"\(" r)) + (count (re-seq #"\)" r))) (= (count (re-seq #"\[" r)) (count (re-seq #"\]" r)))) r - (recur (str r "\n" (read-chars "")))))) + (do + (print (str p " ")) + (flush) + (recur (str r "\n" (read-line)) "::"))))) + +(defn read-from-console + "Attempt to read a complete lisp expression from the console. + + There's a major problem here that the read-chars reader messes up testing. + We need to be able to disable it while testing!" + [prompt] + (if (:testing *options*) + (dummy-read-chars prompt) + (read-chars prompt))) (defn READ "An implementation of a Lisp reader sufficient for bootstrapping; not necessarily diff --git a/test/beowulf/core_test.clj b/test/beowulf/core_test.clj index 81cb86b..9c2029f 100644 --- a/test/beowulf/core_test.clj +++ b/test/beowulf/core_test.clj @@ -1,8 +1,9 @@ (ns beowulf.core-test - (:require [clojure.java.io :refer [reader]] - [clojure.string :refer [split]] - [clojure.test :refer [deftest is testing]] - [beowulf.core :refer [-main repl stop-word]])) + (:require [beowulf.core :refer [-main repl stop-word]] + [beowulf.oblist :refer [*options*]] + [clojure.java.io :refer [reader]] + [clojure.string :refer [split starts-with?]] + [clojure.test :refer [deftest is testing]])) ;; (deftest a-test ;; (testing "FIXME, I fail." @@ -20,45 +21,43 @@ (deftest repl-tests (testing "quit functionality" (with-open [r (reader (string->stream stop-word))] - (binding [*in* r] + (binding [clojure.core/*in* r + *options* (assoc *options* :testing true)] (is (thrown-with-msg? Exception #"\nFærwell!" (repl ""))))) - (let [expected nil actual (with-open [r (reader (string->stream stop-word))] (binding [*in* r] - (-main)))] + (-main "--testing")))] (is (= actual expected))))) -;; TODO: not working because STOP is not being recognised, but I haven't -;; worked out why not yet. It *did* work. - +;; The new read-chars interface is really messing with this. Need to sort out! +;; OK, binding `:testing` doesn't work because `*options*` gets rebound in main. +;; Need to be able to pass in a testing flag as argument to -main (deftest flag-tests - (testing "No flags" + (testing "Only testing flag" (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." expected-quit-message (str "Sprecan '" stop-word "' tó laéfan") expected-result #".*\(3 \. 4\)" expected-prompt "Sprecan:: " expected-signoff "Færwell!" ;; anticipated output (note blank lines): - - ; Hider wilcuman. Béowulf is mín nama. - - ; Sprecan 'STOP' tó laéfan - - ; Sprecan:: > (3 . 4) - ; Sprecan:: - ; Færwell! + + ;; Hider wilcuman. Béowulf is mín nama. + + ;; Sprecan 'STOP' tó laéfan + + ;; Sprecan:: > (3 . 4) + ;; Sprecan:: + ;; Færwell! [_ greeting _ _ quit-message _ result prompt signoff] (with-open [r (reader (string->stream (str "cons[3; 4]\n" stop-word)))] (binding [*in* r] - (split (with-out-str (-main)) #"\n")))] + (split (with-out-str (-main "--testing")) #"\n")))] (is (= greeting expected-greeting)) - ; (is (= error expected-error)) (is (re-matches expected-result result)) (is (= quit-message expected-quit-message)) (is (= prompt expected-prompt)) - (is (= signoff expected-signoff)) - )) + (is (= signoff expected-signoff)))) (testing "unknown flag" (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." expected-quit-message (str "Sprecan '" stop-word "' tó laéfan") @@ -69,115 +68,63 @@ [_ greeting _ error quit-message _ result prompt signoff] (with-open [r (reader (string->stream (str "cons[5; 6]\n" stop-word)))] (binding [*in* r] - (split (with-out-str (-main "--unknown")) #"\n")))] + (split (with-out-str (-main "--unknown" "--testing")) #"\n")))] (is (= greeting expected-greeting)) (is (re-matches expected-error error)) (is (re-matches expected-result result)) (is (= quit-message expected-quit-message)) (is (= prompt expected-prompt)) - (is (= signoff expected-signoff)) - )) - ; (testing "help" - ; (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." - ; expected-h1 " -h, --help" - ; expected-quit-message (str "Sprecan '" stop-word "' tó laéfan") - ; expected-result #".*\(A \. B\)" - ; expected-prompt "Sprecan:: " - ; expected-signoff "Færwell!" - ; [_ greeting _ h1 _ _ _ _ quit-message _ result prompt signoff] - ; (with-open [r (reader (string->stream (str "cons[A; B]\n" stop-word)))] - ; (binding [*in* r] - ; (split (with-out-str (-main "--help")) #"\n")))] - ; (is (= greeting expected-greeting)) - ; (is (= h1 expected-h1)) - ; (is (re-matches expected-result result)) - ; (is (= quit-message expected-quit-message)) - ; (is (= prompt expected-prompt)) - ; (is (= signoff expected-signoff)) - ; )) - ; (testing "prompt" - ; (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." - ; expected-quit-message (str "Sprecan '" stop-word "' tó laéfan") - ; expected-error "" - ; expected-result #".*\(A \. B\).*" - ; expected-prompt "? " - ; expected-signoff "Færwell!" - ; [_ greeting _ error quit-message _ result prompt signoff] - ; (with-open [r (reader (string->stream (str stop-word)))] - ; (binding [*in* r] - ; (split (with-out-str (-main "--prompt" "?")) #"\n")))] - ; (is (= greeting expected-greeting)) - ; (is (= error expected-error)) - ; (is (re-matches expected-result result )) - ; (is (= quit-message expected-quit-message)) - ; (is (= prompt expected-prompt)) - ; (is (= signoff expected-signoff)) - ; )) - ; (testing "read - file not found" - ; (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." - ; expected-quit-message (str "Sprecan '" stop-word "' tó laéfan") - ; expected-error #"Failed to validate.*" - ; expected-result #".*\(A \. B\)" - ; expected-prompt "Sprecan:: " - ; expected-signoff "Færwell!" - ; [_ greeting _ error quit-message _ result prompt signoff] - ; (with-open [r (reader (string->stream (str "cons[A; B]\n" stop-word)))] - ; (binding [*in* r] - ; (split (with-out-str (-main "--read" "froboz")) #"\n")))] - ; (is (= greeting expected-greeting)) - ; (is (re-matches expected-error error)) - ; (is (re-matches expected-result result)) - ; (is (= quit-message expected-quit-message)) - ; (is (= prompt expected-prompt)) - ; (is (= signoff expected-signoff)) - ; )) - ; (testing "read - file found" - ; ;; TODO: there's no feedback from this because the initfile - ; ;; is not yet read. This will change - ; (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." - ; expected-quit-message (str "Sprecan '" stop-word "' tó laéfan") - ; expected-error "" - ; expected-result #".*\(A \. B\)" - ; expected-prompt "Sprecan:: " - ; expected-signoff "Færwell!" - ; [_ greeting error quit-message _ _ result prompt signoff] - ; (with-open [r (reader (string->stream (str "cons[A; B]\n" stop-word)))] - ; (binding [*in* r] - ; (split (with-out-str (-main "--read" "README.md")) #"\n")))] - ; (is (= greeting expected-greeting)) - ; (is (= error expected-error)) - ; (is (re-matches expected-result result)) - ; (is (= quit-message expected-quit-message)) - ; (is (= prompt expected-prompt)) - ; (is (= signoff expected-signoff)) - ; )) - ; (testing "strict" - ; (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." - ; expected-quit-message (str "Sprecan '" stop-word "' tó laéfan") - ; expected-error "" - ; expected-result #".*Cannot parse meta expressions in strict mode.*" - ; expected-prompt "Sprecan:: " - ; expected-signoff "Færwell!" - ; [_ greeting _ error quit-message _ result prompt signoff] - ; (with-open [r (reader (string->stream (str "cons[A; B]\n" stop-word)))] - ; (binding [*in* r] - ; (split (with-out-str (-main "--strict")) #"\n")))] - ; (is (= greeting expected-greeting)) - ; (is (= error expected-error)) - ; (is (re-matches expected-result result )) - ; (is (= quit-message expected-quit-message)) - ; (is (= prompt expected-prompt)) - ; (is (= signoff expected-signoff)) - ; )) - ; ; (testing "trace" - ; (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." - ; expected-error "" - ; expected-trace #".*traced-eval.*" - ; [_ greeting _ error _ _ trace & _] - ; (with-open [r (reader (string->stream (str "cons[A; B]\n" stop-word)))] - ; (binding [*in* r] - ; (split (with-out-str (-main "--trace")) #"\n")))] - ; (is (= greeting expected-greeting)) - ; (is (= error expected-error)) - ; (is (re-matches expected-trace trace)) -) \ No newline at end of file + (is (= signoff expected-signoff)))) +;; ;; TODO: not working because STOP is not being recognised, but I haven't +;; ;; worked out why not yet. It *did* work. + +;; Hider wilcuman. Béowulf is mín nama. +;; -f FILEPATH, --file-path FILEPATH Set the path to the directory for reading and writing Lisp files. +;; -h, --help +;; -p PROMPT, --prompt PROMPT Sprecan:: Set the REPL prompt to PROMPT +;; -r SYSOUTFILE, --read SYSOUTFILE lisp1.5.lsp Read Lisp system from file SYSOUTFILE +;; -s, --strict Strictly interpret the Lisp 1.5 language, without extensions. +;; -t, --time Time evaluations. +;; -x, --testing Disable the jline reader - useful when piping input. +;; Sprecan 'STOP' tó laéfan + +;; Sprecan:: + + (testing "help" + (let [expected-greeting "Hider wilcuman. Béowulf is mín nama." + expected-h1 " -h, --help" + expected-quit-message (str "Sprecan '" stop-word "' tó laéfan") + expected-result #".*\(A \. B\)" + expected-prompt "Sprecan:: " + expected-signoff "Færwell!" + [_ greeting _ _ h1 _ _ _ _ _ quit-message _ result prompt signoff] + (with-open [r (reader (string->stream (str "cons[A; B]\n" stop-word)))] + (binding [*in* r] + (split (with-out-str (-main "--help" "--testing")) #"\n")))] + (is (= greeting expected-greeting)) + (is (= h1 expected-h1)) + (is (re-matches expected-result result)) + (is (= quit-message expected-quit-message)) + (is (starts-with? prompt expected-prompt)) + (is (= signoff expected-signoff)))) + (testing "prompt" + (let [expected-prompt "? " + [_ _ _ _ _ _ prompt _] + (with-open [r (reader (string->stream stop-word))] + (binding [*in* r] + (split (with-out-str (-main "--prompt" "?" "--testing")) #"\n")))] + (is (= prompt expected-prompt)))) + (testing "read - file not found" + (let [expected-error #"Failed to validate.*" + [_ _ _ error _ _ _ _ _] + (with-open [r (reader (string->stream (str "cons[A; B]\n" stop-word)))] + (binding [*in* r] + (split (with-out-str (-main "--testing" "--read" "froboz")) #"\n")))] + (is (re-matches expected-error error)))) + (testing "strict" + (let [expected-result #".*Cannot parse meta expressions in strict mode.*" + [_ _ _ _ _ _ result _ _] + (with-open [r (reader (string->stream (str "cons[A; B]\n" stop-word)))] + (binding [*in* r] + (split (with-out-str (-main "--strict" "--testing")) #"\n")))] + (is (re-matches expected-result result ))))) diff --git a/test/beowulf/host_test.clj b/test/beowulf/host_test.clj index 7e5e1ff..5f73ea8 100644 --- a/test/beowulf/host_test.clj +++ b/test/beowulf/host_test.clj @@ -1,9 +1,21 @@ (ns beowulf.host-test - (:require [clojure.test :refer [deftest is testing]] - [beowulf.cons-cell :refer [F make-beowulf-list T]] - [beowulf.host :refer [CDR DIFFERENCE NUMBERP PLUS RPLACA RPLACD TIMES]] + (:require [beowulf.cons-cell :refer [F make-beowulf-list T]] + [beowulf.host :refer [CDR DIFFERENCE GENSYM GET NUMBERP PLUS PUT + RPLACA RPLACD TIMES]] + [beowulf.io :refer [SYSIN]] [beowulf.oblist :refer [NIL]] - [beowulf.read :refer [gsp]])) + [beowulf.read :refer [gsp]] + [clojure.test :refer [deftest is testing use-fixtures]])) + +(use-fixtures :once (fn [f] + (try (when (SYSIN "resources/lisp1.5.lsp") + (f)) + (catch Throwable any + (throw (ex-info "Failed to load Lisp sysout" + {:phase test + :function 'SYSIN + :file "resources/lisp1.5.lsp"} + any)))))) (deftest destructive-change-test (testing "RPLACA" @@ -64,7 +76,8 @@ (let [expected 6 actual (TIMES 2 3)] (is (= actual expected)))) - (testing DIFFERENCE + (testing "DIFFERENCE" (let [expected -1 actual (DIFFERENCE 1 2)] (is (= actual expected))))) + diff --git a/test/beowulf/lisp_test.clj b/test/beowulf/lisp_test.clj index 7d9fa64..5095999 100644 --- a/test/beowulf/lisp_test.clj +++ b/test/beowulf/lisp_test.clj @@ -1,11 +1,11 @@ (ns beowulf.lisp-test "The idea here is to test actual Lisp functions" - (:require [clojure.test :refer [deftest testing is use-fixtures]] - [beowulf.bootstrap :refer [EVAL]] + (:require [beowulf.bootstrap :refer [EVAL]] [beowulf.cons-cell :refer [make-beowulf-list]] - [beowulf.io :refer [SYSIN]] - ;; [beowulf.oblist :refer [NIL]] - [beowulf.read :refer [READ]])) + [beowulf.io :refer [SYSIN]] ;; [beowulf.oblist :refer [NIL]] + [beowulf.oblist :refer [NIL]] + [beowulf.read :refer [READ]] + [clojure.test :refer [deftest is testing use-fixtures]])) (defn- reps "'Read eval print string', or 'read eval print single'. @@ -165,4 +165,42 @@ (SETQ X (ADD1 X)) (COND ((EQ X 5) (RETURN X)) (T (GO START))))")] - (is (= actual expected))))) \ No newline at end of file + (is (= actual expected))))) + + +(deftest put-get-tests + (let [symbol 'TESTSYMBOL + p1 'TESTPROPONE + p2 'TESTPROPTWO] + (testing "GET - property should be missing" + (let [expected "NIL" + actual (reps "(GET 'TESTSYMBOL 'TESTPROPONE)")] + (is (= actual expected)))) + (testing "PUT and GET: value of new property; change value of property" + (let [prop (reps "(GENSYM)") + val1 (reps "(GENSYM)") + val2 (reps "(GENSYM)") + expected1 val1 + actual1 (when (reps (str "(PUT '" symbol " '" prop " '" val1 ")")) + (reps (str "(GET '" symbol " '" prop ")"))) + expected2 val2 + actual2 (when (reps (str "(PUT '" symbol " '" prop " '" val2 ")")) + (reps (str "(GET '" symbol " '" prop ")")))] + (is (not= val1 val2)) + (is (= actual1 expected1) "The value set can be retrieved.") + (is (= actual2 expected2) "The value is changed."))) + (testing "PUT and GET: different properties have independent values" + (let [val1 (reps "(GENSYM)") + val2 (reps "(GENSYM)") + expected1 val1 + actual1 (when (reps (str "(PUT '" symbol " '" p1 " '" val1 ")")) + (reps (str "(GET '" symbol " '" p1 ")"))) + expected2 val2 + actual2 (when (reps (str "(PUT '" symbol " '" p2 " '" val2 ")")) + (reps (str "(GET '" symbol " '" p2 ")"))) + expected3 val1 + actual3 (reps (str "(GET '" symbol " '" p1 ")"))] + (is (not= val1 val2)) + (is (= actual1 expected1) "The value set can be retrieved.") + (is (= actual2 expected2) "Values are independent.") + (is (= actual3 expected3) "Setting a second property does not obliterate the first."))))) \ No newline at end of file