Struggling to get Lisp tests working; total fail, but improvements.

This commit is contained in:
Simon Brooke 2023-03-31 00:49:33 +01:00
parent b6f52cd775
commit 03ed76f34d
9 changed files with 132 additions and 77 deletions

1
.gitignore vendored
View file

@ -16,3 +16,4 @@ pom.xml.asc
.clj-kondo/ .clj-kondo/
.lsp/ .lsp/
resources/scratch.lsp resources/scratch.lsp
Sysout*.lsp

View file

@ -1,7 +1,7 @@
# Change Log # Change Log
All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/).
## [0.2.1] - 2023-03-?? ## [0.2.1] - 2023-03-30
### Changed ### Changed
- this is fundamentally a working Lisp. The reader reads S-Expressions fully and M-Expressions at least partially. It is not (yet) a feature complete Lisp 1.5. - this is fundamentally a working Lisp. The reader reads S-Expressions fully and M-Expressions at least partially. It is not (yet) a feature complete Lisp 1.5.

View file

@ -15,8 +15,6 @@ Boots to REPL, but few functions yet available.
* [Project website](https://simon-brooke.github.io/beowulf/). * [Project website](https://simon-brooke.github.io/beowulf/).
* [Source code documentation](https://simon-brooke.github.io/beowulf/docs/codox/index.html). * [Source code documentation](https://simon-brooke.github.io/beowulf/docs/codox/index.html).
* [Test Coverage Report](https://simon-brooke.github.io/beowulf/docs/cloverage/index.html)
### Building and Invoking ### Building and Invoking
@ -101,6 +99,14 @@ The following functions and symbols are implemented:
| UNTRACE | ? | null | ? | | UNTRACE | ? | null | ? |
| ZEROP | Lisp function | (N) | ? | | ZEROP | Lisp function | (N) | ? |
Functions described as 'Lisp function' above are defined in the default
sysout file, `resources/lisp1.5.lsp`, which will be loaded by default unless
you specify another initfile on the command line.
Functions described as 'Host function' are implemented in Clojure, but if you're
brave you can redefine them in Lisp and the Lisp definitions will take precedence
over the Clojure implementations.
### Architectural plan ### Architectural plan
Not everything documented in this section is yet built. It indicates the Not everything documented in this section is yet built. It indicates the

View file

@ -360,10 +360,9 @@
that an argument can be passed but I'm not sure of the semantics of that an argument can be passed but I'm not sure of the semantics of
this." this."
[] []
(when (lax? 'OBLIST)
(if (instance? ConsCell @oblist) (if (instance? ConsCell @oblist)
(make-beowulf-list (map CAR @oblist)) (make-beowulf-list (map CAR @oblist))
NIL))) NIL))
(defn DEFINE (defn DEFINE
"Bootstrap-only version of `DEFINE` which, post boostrap, can be overwritten "Bootstrap-only version of `DEFINE` which, post boostrap, can be overwritten
@ -396,65 +395,78 @@
symbol val) symbol val)
NIL)) NIL))
(defn- traced-apply
"Like `APPLY`, but with trace output to console."
[function-symbol args lisp-fn environment depth]
(let [indent (apply str (repeat depth "-"))]
(println (str indent "> " function-symbol " " args))
(let [r (APPLY lisp-fn args environment depth)]
(println (str "<" indent " " r))
r)))
(defn- safe-apply
"We've a real problem with varargs functions when `args` is `NIL`, because
Clojure does not see `NIL` as an empty sequence."
[clj-fn args]
(let [args' (when (instance? ConsCell args) args)]
(apply clj-fn args')))
(defn- apply-symbolic (defn- apply-symbolic
"Apply this `funtion-symbol` to these `args` in this `environment` and "Apply this `funtion-symbol` to these `args` in this `environment` and
return the result." return the result."
[^Symbol function-symbol ^ConsCell args ^ConsCell environment depth] [^Symbol function-symbol args ^ConsCell environment depth]
(let [fn (try (EVAL function-symbol environment depth) (let [lisp-fn (try (EVAL function-symbol environment depth)
(catch Throwable any (when (:trace *options*) (catch Throwable any (when (:trace *options*)
(println any)))) (println any))))]
indent (apply str (repeat depth "-"))] (if (and lisp-fn
(if (and fn (not= fn NIL)) (not= lisp-fn NIL)) (if (traced? function-symbol)
(if (traced? function-symbol) (traced-apply function-symbol
(do args
(println (str indent "> " function-symbol " " args)) lisp-fn
(let [r (APPLY fn args environment depth)] environment
(println (str "<" indent " " r)) depth)
r)) (APPLY lisp-fn args environment depth))
(APPLY fn args environment depth))
(case function-symbol ;; there must be a better way of doing this! (case function-symbol ;; there must be a better way of doing this!
ADD1 (apply ADD1 args) ADD1 (safe-apply ADD1 args)
AND (apply AND args) AND (safe-apply AND args)
APPEND (apply APPEND args) APPEND (safe-apply APPEND args)
APPLY (apply APPLY args) APPLY (safe-apply APPLY args) ;; TODO: need to pass the environment and depth
ATOM (ATOM? (CAR args)) ATOM (ATOM? (CAR args))
CAR (CAAR args) CAR (CAAR args)
CDR (CDAR args) CDR (CDAR args)
CONS (make-cons-cell (CAR args) (CADR args)) CONS (make-cons-cell (CAR args) (CADR args))
DEFINE (DEFINE (CAR args)) DEFINE (DEFINE (CAR args))
DIFFERENCE (DIFFERENCE (CAR args) (CADR args)) DIFFERENCE (DIFFERENCE (CAR args) (CADR args))
EQ (apply EQ args) EQ (safe-apply EQ args)
EQUAL (apply EQUAL args) EQUAL (safe-apply EQUAL args)
ERROR (apply ERROR args) ERROR (safe-apply ERROR args)
;; think about EVAL. Getting the environment right is subtle ;; think about EVAL. Getting the environment right is subtle
FIXP (apply FIXP args) FIXP (safe-apply FIXP args)
GENSYM (GENSYM) GENSYM (GENSYM)
GREATERP (apply GREATERP args) GREATERP (safe-apply GREATERP args)
INTEROP (when (lax? INTEROP) (apply INTEROP args)) INTEROP (when (lax? INTEROP) (safe-apply INTEROP args))
LESSP (apply LESSP args) LESSP (safe-apply LESSP args)
LIST (apply LIST args) LIST (safe-apply LIST args)
NUMBERP (apply NUMBERP args) NUMBERP (safe-apply NUMBERP args)
OBLIST (OBLIST) OBLIST (OBLIST)
PLUS (apply PLUS args) PLUS (safe-apply PLUS args)
PRETTY (when (lax? 'PRETTY) PRETTY (when (lax? 'PRETTY)
(apply pretty-print args)) (safe-apply pretty-print args))
PRINT (apply print args) PRINT (safe-apply print args)
QUOTIENT (apply QUOTIENT args) QUOTIENT (safe-apply QUOTIENT args)
READ (READ) READ (READ)
REMAINDER (apply REMAINDER args) REMAINDER (safe-apply REMAINDER args)
RPLACA (apply RPLACA args) RPLACA (safe-apply RPLACA args)
RPLACD (apply RPLACD args) RPLACD (safe-apply RPLACD args)
SET (apply SET args) SET (safe-apply SET args)
SYSIN (when (lax? 'SYSIN) SYSIN (when (lax? 'SYSIN)
(apply SYSIN args)) (safe-apply SYSIN args))
SYSOUT (when (lax? 'SYSOUT) SYSOUT (when (lax? 'SYSOUT)
(if (empty? args) (safe-apply SYSOUT args))
(SYSOUT)
(apply SYSOUT args)))
TERPRI (println) TERPRI (println)
TIMES (apply TIMES args) TIMES (safe-apply TIMES args)
TRACE (apply TRACE args) TRACE (safe-apply TRACE args)
UNTRACE (apply UNTRACE args) UNTRACE (safe-apply UNTRACE args)
;; else ;; else
(ex-info "No function found" (ex-info "No function found"
{:context "APPLY" {:context "APPLY"

View file

@ -138,7 +138,7 @@
(cond (cond
(instance? ConsCell (. this CDR)) (str " " (subs (.toString (. this CDR)) 1)) (instance? ConsCell (. this CDR)) (str " " (subs (.toString (. this CDR)) 1))
(= NIL (. this CDR)) ")" (= NIL (. this CDR)) ")"
:else (str " . " (. this CDR)))))) :else (str " . " (. this CDR) ")")))))
(defn- to-string (defn- to-string
"Printing ConsCells gave me a *lot* of trouble. This is an internal function "Printing ConsCells gave me a *lot* of trouble. This is an internal function
@ -161,12 +161,9 @@
s s
(to-string car) (to-string car)
(cond (cond
(or (nil? cdr) (= cdr NIL)) (or (nil? cdr) (= cdr NIL)) ")"
")" cons? " "
cons? :else (str " . " (to-string cdr) ")")))]
" "
:else
(str " . " (to-string cdr) ")")))]
(if (if
cons? cons?
(recur cdr (inc n) ss) (recur cdr (inc n) ss)

View file

@ -71,7 +71,9 @@
**NOTE THAT** if the provided `filename` does not end with `.lsp` (which, **NOTE THAT** if the provided `filename` does not end with `.lsp` (which,
if you're writing it from the Lisp REPL, it won't), the extension `.lsp` if you're writing it from the Lisp REPL, it won't), the extension `.lsp`
will be appended." will be appended."
[filename] ([]
(SYSIN (or (:read *options*) "resources/lisp1.5.lsp")))
([filename]
(let [fp (file (full-path (str filename))) (let [fp (file (full-path (str filename)))
file (when (and (.exists fp) (.canRead fp)) fp) file (when (and (.exists fp) (.canRead fp)) fp)
res (try (resource filename) res (try (resource filename)
@ -82,4 +84,4 @@
{:context "SYSIN" {:context "SYSIN"
:filepath fp} :filepath fp}
any))))] any))))]
(swap! oblist #(when (or % (seq content)) content)))) (swap! oblist #(when (or % (seq content)) content)))))

View file

@ -6,7 +6,12 @@
) )
(def NIL (def NIL
"The canonical empty list symbol." "The canonical empty list symbol.
TODO: this doesn't really work, because (from Clojure) `(empty? NIL)` throws
an exception. It might be better to subclass beowulf.cons_cell.ConsCell to create
a new singleton class Nil which overrides the `empty` method of
IPersistentCollection?"
'NIL) 'NIL)
(def oblist (def oblist

View file

@ -0,0 +1,32 @@
(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]]
[beowulf.cons-cell :refer [make-beowulf-list]]
[beowulf.io :refer [SYSIN]]
[beowulf.read :refer [READ]]))
;; (use-fixtures :once (fn [f]
;; (try (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"}))))))
;; (deftest "COPY test"
;; ;; (testing "copy NIL"
;; ;; (println "in-test: " (SYSIN "resources/lisp1.5.lsp"))
;; ;; (let [expected "NIL"
;; ;; actual (with-out-str (println (EVAL (READ "(COPY NIL)"))))]
;; ;; (is (= actual expected))))
;; (testing "copy straight list"
;; (println "in-test: " (SYSIN "resources/lisp1.5.lsp"))
;; (let [expected (make-beowulf-list '(A B C))
;; actual (with-out-str (print (EVAL (READ "(COPY '(A B C))"))))]
;; (is (= actual expected))))
;; (testing "copy assoc list"
;; (let [expected "((A . 1) (B . 2) (C . 3))"
;; actual (with-out-str (println (EVAL (READ "(COPY '((A . 1) (B . 2) (C . 3)))"))))]
;; (is (= actual expected)))))

View file

@ -53,7 +53,7 @@
;; I suspect as (CAR (LIST A B C)). ;; I suspect as (CAR (LIST A B C)).
(let [expected "(CAR (LIST A B C))" (let [expected "(CAR (LIST A B C))"
actual (print-str (gsp "car[(A B C)]"))] actual (print-str (gsp "car[ list[a; b; c]]"))]
(is (= actual expected))) (is (= actual expected)))
)) ))