My monster, it lives!

I'm not confident this is yet tidy, so I'm not yet closing the feature branch - but it's working.
This commit is contained in:
Simon Brooke 2023-04-06 14:25:12 +01:00
parent 5b5ddb9444
commit 20b8f45db1
3 changed files with 176 additions and 122 deletions

View file

@ -9,10 +9,9 @@
ALLUPPERCASE are Lisp 1.5 functions (although written in Clojure) and that ALLUPPERCASE are Lisp 1.5 functions (although written in Clojure) and that
therefore all arguments must be numbers, symbols or `beowulf.cons_cell.ConsCell` therefore all arguments must be numbers, symbols or `beowulf.cons_cell.ConsCell`
objects." objects."
(:require [beowulf.cons-cell :refer [make-cons-cell T]] (:require [beowulf.cons-cell :refer [make-beowulf-list make-cons-cell T]]
[beowulf.host :refer [ATOM CAAR CADAR CADDR CADR CAR CDR GET LIST [beowulf.host :refer [ASSOC ATOM CAAR CADAR CADDR CADR CAR CDR GET
NUMBERP PAIRLIS traced?]] LIST NUMBERP PAIRLIS traced?]]
[beowulf.interop :refer [to-clojure]]
[beowulf.oblist :refer [*options* NIL oblist]]) [beowulf.oblist :refer [*options* NIL oblist]])
(:import [beowulf.cons_cell ConsCell] (:import [beowulf.cons_cell ConsCell]
[clojure.lang Symbol])) [clojure.lang Symbol]))
@ -39,51 +38,67 @@
(declare APPLY EVAL) (declare APPLY EVAL)
(defmacro QUOTE (defn try-resolve-subroutine
"Quote, but in upper case for LISP 1.5" "Attempt to resolve this `subr` with these `arg`."
[f] [subr args]
`(quote ~f)) (when (and subr (not= subr NIL))
(try @(resolve subr)
(catch Throwable any
(throw (ex-info "Failed to resolve subroutine"
{:phase :apply
:function subr
:args args
:type :beowulf}
any))))))
(defn- traced-apply (defn- trace-call
"Like `APPLY`, but with trace output to console." "Show a trace of a call to the function named by this `function-symbol`
[function-symbol args lisp-fn environment depth] with these `args` at this depth."
[function-symbol args depth]
(when (traced? function-symbol)
(let [indent (apply str (repeat depth "-"))] (let [indent (apply str (repeat depth "-"))]
(println (str indent "> " function-symbol " " args)) (println (str indent "> " function-symbol " " args)))))
(let [r (APPLY lisp-fn args environment depth)]
(println (str "<" indent " " r))
r)))
(defn- safe-apply (defn- trace-response
"We've a real problem with varargs functions when `args` is `NIL`, because "Show a trace of this `response` from the function named by this
Clojure does not see `NIL` as an empty sequence." `function-symbol` at this depth."
[clj-fn args] [function-symbol response depth]
(let [args' (when (instance? ConsCell args) args)] (when (traced? function-symbol)
(apply clj-fn args'))) (let [indent (apply str (repeat depth "-"))]
(println (str "<" indent " " function-symbol " " response))))
response)
(defn- value
"Seek a value for this symbol `s` by checking each of these indicators in
turn."
([s]
(value s (list 'APVAL 'EXPR 'FEXPR 'SUBR 'FSUBR)))
([s indicators]
(when (symbol? s)
(first (remove #(= % NIL) (map #(GET s %)
indicators))))))
(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 args ^ConsCell environment depth] [^Symbol function-symbol args ^ConsCell environment depth]
(let [lisp-fn (try (EVAL function-symbol environment depth) (trace-call function-symbol args depth)
(catch Throwable any (when (:trace *options*) (let [lisp-fn ;; (try
(println any))))] (value function-symbol '(EXPR FEXPR))
(if (and lisp-fn ;; (catch Exception any (when (traced? function-symbol)
(not= lisp-fn NIL)) (if (traced? function-symbol) ;; (println any))))
(traced-apply function-symbol subr (value function-symbol '(SUBR FSUBR))
args host-fn (try-resolve-subroutine subr args)
lisp-fn result (cond (and lisp-fn
environment (not= lisp-fn NIL)) (APPLY lisp-fn args environment depth)
depth) host-fn (apply host-fn (when (instance? ConsCell args) args))
(APPLY lisp-fn args environment depth)) :else (ex-info "No function found"
(if function-symbol {:phase :apply
(let [f (GET function-symbol 'SUBR)]
(when f
(apply @(resolve f) (to-clojure args))))
;; else
(ex-info "No function found"
{:context "APPLY"
:function function-symbol :function function-symbol
:args args}))))) :args args
:type :beowulf}))]
(trace-response function-symbol result depth)
result))
(defn APPLY (defn APPLY
"Apply this `function` to these `arguments` in this `environment` and return "Apply this `function` to these `arguments` in this `environment` and return
@ -93,13 +108,15 @@
All args are assumed to be symbols or `beowulf.cons-cell/ConsCell` objects. All args are assumed to be symbols or `beowulf.cons-cell/ConsCell` objects.
See page 13 of the Lisp 1.5 Programmers Manual." See page 13 of the Lisp 1.5 Programmers Manual."
[function args environment depth] [function args environment depth]
(cond (trace-call 'APPLY (list function args environment) depth)
(let [result (cond
(= NIL function) (if (:strict *options*) (= NIL function) (if (:strict *options*)
NIL NIL
(throw (ex-info "NIL is not a function" (throw (ex-info "NIL is not a function"
{:context "APPLY" {:phase :apply
:function "NIL" :function "NIL"
:args args}))) :args args
:type :beowulf})))
(= (ATOM function) T) (apply-symbolic function args environment (inc depth)) (= (ATOM function) T) (apply-symbolic function args environment (inc depth))
:else (case (first function) :else (case (first function)
LABEL (APPLY LABEL (APPLY
@ -119,7 +136,9 @@
{:phase :apply {:phase :apply
:function function :function function
:args args :args args
:type :beowulf}))))) :type :beowulf}))))]
(trace-response 'APPLY result depth)
result))
(defn- EVCON (defn- EVCON
"Inner guts of primitive COND. All `clauses` are assumed to be "Inner guts of primitive COND. All `clauses` are assumed to be
@ -146,13 +165,25 @@
(EVAL (CAR args) env depth) (EVAL (CAR args) env depth)
(EVLIS (CDR args) env depth)))) (EVLIS (CDR args) env depth))))
;; (defn- eval-symbolic [^Symbol s env] (defn- eval-symbolic
;; (let [binding (ASSOC s env)] [expr env depth]
;; (if (= binding NIL) (let [v (value expr (list 'APVAL))
;; (throw (ex-info (format "No binding for symbol `%s`" s) indent (apply str (repeat depth "-"))]
;; {:phase :eval (when (traced? 'EVAL)
;; :symbol s})) (println (str indent ": EVAL: deep binding (" expr " . " (or v "nil") ")")))
;; (CDR binding)))) (if (and v (not= v NIL))
v
(let [v' (ASSOC expr env)]
(when (traced? 'EVAL)
(println (str indent ": EVAL: shallow binding: " (or v' "nil"))))
(if (and v' (not= v' NIL))
(.getCdr v')
(throw (ex-info "No binding for symbol found"
{:phase :eval
:function 'EVAL
:args (list expr env depth)
:type :lisp
:code :A8})))))))
(defn EVAL (defn EVAL
"Evaluate this `expr` and return the result. If `environment` is not passed, "Evaluate this `expr` and return the result. If `environment` is not passed,
@ -160,13 +191,18 @@
argument is part of the tracing system and should not be set by user code. argument is part of the tracing system and should not be set by user code.
All args are assumed to be numbers, symbols or `beowulf.cons-cell/ConsCell` All args are assumed to be numbers, symbols or `beowulf.cons-cell/ConsCell`
objects." objects. However, if called with just a single arg, `expr`, I'll assume it's
being called from the Clojure REPL and will coerce the `expr` to `ConsCell`."
([expr] ([expr]
(EVAL expr @oblist 0)) (let [expr' (if (and (coll? expr) (not (instance? ConsCell expr)))
(make-beowulf-list expr)
expr)]
(EVAL expr' @oblist 0)))
([expr env depth] ([expr env depth]
(cond (trace-call 'EVAL (list expr env depth) depth)
(let [result (cond
(= (NUMBERP expr) T) expr (= (NUMBERP expr) T) expr
(symbol? expr) (GET expr 'APVAL) (symbol? expr) (eval-symbolic expr env depth)
(string? expr) (if (:strict *options*) (string? expr) (if (:strict *options*)
(throw (throw
(ex-info (ex-info
@ -189,5 +225,7 @@
(CAR expr) (CAR expr)
(EVLIS (CDR expr) env depth) (EVLIS (CDR expr) env depth)
env env
depth)))) depth))]
(trace-response 'EVAL result depth)
result)))

View file

@ -2,8 +2,8 @@
"Essentially, the `-main` function and the bootstrap read-eval-print loop." "Essentially, the `-main` function and the bootstrap read-eval-print loop."
(:require [beowulf.bootstrap :refer [EVAL]] (:require [beowulf.bootstrap :refer [EVAL]]
[beowulf.io :refer [default-sysout SYSIN]] [beowulf.io :refer [default-sysout SYSIN]]
[beowulf.oblist :refer [*options* NIL]]
[beowulf.read :refer [READ read-from-console]] [beowulf.read :refer [READ read-from-console]]
[beowulf.oblist :refer [*options* oblist]]
[clojure.java.io :as io] [clojure.java.io :as io]
[clojure.pprint :refer [pprint]] [clojure.pprint :refer [pprint]]
[clojure.string :refer [trim]] [clojure.string :refer [trim]]
@ -55,7 +55,7 @@
(defn- re (defn- re
"Like REPL, but it isn't a loop and doesn't print." "Like REPL, but it isn't a loop and doesn't print."
[input] [input]
(EVAL (READ input) @oblist 0)) (EVAL (READ input) NIL 0))
(defn repl (defn repl
"Read/eval/print loop." "Read/eval/print loop."

View file

@ -2,14 +2,12 @@
"provides Lisp 1.5 functions which can't be (or can't efficiently "provides Lisp 1.5 functions which can't be (or can't efficiently
be) implemented in Lisp 1.5, which therefore need to be implemented in the be) implemented in Lisp 1.5, which therefore need to be implemented in the
host language, in this case Clojure." host language, in this case Clojure."
(:require [clojure.string :refer [upper-case]] (:require [beowulf.cons-cell :refer [F make-beowulf-list make-cons-cell T]] ;; note hyphen - this is Clojure...
[beowulf.cons-cell :refer [F make-cons-cell make-beowulf-list
pretty-print T]]
;; note hyphen - this is Clojure...
[beowulf.gendoc :refer [open-doc]] [beowulf.gendoc :refer [open-doc]]
[beowulf.oblist :refer [*options* oblist NIL]]) [beowulf.oblist :refer [*options* NIL oblist]]
(:import [beowulf.cons_cell ConsCell] [clojure.set :refer [union]]
;; note underscore - same namespace, but Java. [clojure.string :refer [upper-case]])
(:import [beowulf.cons_cell ConsCell] ;; note underscore - same namespace, but Java.
)) ))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -290,9 +288,20 @@
In `beowulf.host` principally because I don't yet feel confident to define In `beowulf.host` principally because I don't yet feel confident to define
varargs functions in Lisp." varargs functions in Lisp."
[& args] [& args]
(if (empty? (filter #(or (= 'F %) (= NIL %) (nil? %)) args)) (cond (= NIL args) T
'T (not (#{NIL F} (.getCar args))) (AND (.getCdr args))
'F)) :else T))
(defn OR
"`T` if and only if at least one of my `args` evaluates to something other
than either `F` or `NIL`, else `F`.
In `beowulf.host` principally because I don't yet feel confident to define
varargs functions in Lisp."
[& args]
(cond (= NIL args) F
(not (#{NIL F} (.getCar args))) T
:else (OR (.getCdr args))))
;;;; Operations on lists ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Operations on lists ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
@ -516,19 +525,26 @@
"Return `true` iff `s` is a symbol currently being traced, else `nil`." "Return `true` iff `s` is a symbol currently being traced, else `nil`."
[s] [s]
(try (contains? @traced-symbols s) (try (contains? @traced-symbols s)
(catch Throwable _))) (catch Throwable _ nil)))
(defn TRACE (defn TRACE
"Add this symbol `s` to the set of symbols currently being traced. If `s` "Add this `s` to the set of symbols currently being traced. If `s`
is not a symbol, does nothing." is not a symbol or sequence of symbols, does nothing."
[s] [s]
(when (symbol? s) (swap! traced-symbols
(swap! traced-symbols #(conj % s)))) #(cond
(symbol? s) (conj % s)
(and (seq? s) (every? symbol? s)) (union % (set s))
:else %)))
(defn UNTRACE (defn UNTRACE
"Remove this `s` from the set of symbols currently being traced. If `s`
is not a symbol or sequence of symbols, does nothing."
[s] [s]
(when (symbol? s) (cond
(swap! traced-symbols #(set (remove (fn [x] (= s x)) %))))) (symbol? s) (swap! traced-symbols #(set (remove (fn [x] (= s x)) %)))
(and (seq? s) (every? symbol? s)) (map UNTRACE s))
@traced-symbols)
;;;; Extensions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Extensions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;