New Lisp functions ASSOC, EFFACE, MAPLIST

This commit is contained in:
Simon Brooke 2023-04-01 17:56:49 +01:00
parent 41cecdc522
commit 5ee9531e6b
10 changed files with 109 additions and 34 deletions

View file

@ -1,6 +1,6 @@
# beowulf # beowulf
LISP 1.5 is to all Lisp dialects as Beowulf is to Emglish literature. LISP 1.5 is to all Lisp dialects as Beowulf is to English literature.
## What this is ## What this is

View file

@ -11,6 +11,11 @@
(APPEND LAMBDA (APPEND LAMBDA
(X Y) (COND ((NULL X) Y) ((QUOTE T) (CONS (CAR X) (APPEND (CDR X) Y))))) (X Y) (COND ((NULL X) Y) ((QUOTE T) (CONS (CAR X) (APPEND (CDR X) Y)))))
(APPLY) (APPLY)
(ASSOC LAMBDA (X L)
(COND
((NULL L) (QUOTE NIL))
((AND (CONSP (CAR L)) (EQ (CAAR L) X)) (CDAR L))
((QUOTE T) (ASSOC X (CDR L)))))
(ATOM) (ATOM)
(CAR) (CAR)
(CAAAAR LAMBDA (X) (CAR (CAR (CAR (CAR X))))) (CAAAAR LAMBDA (X) (CAR (CAR (CAR (CAR X)))))
@ -43,6 +48,7 @@
(CDDR LAMBDA (X) (CDR (CDR X))) (CDDR LAMBDA (X) (CDR (CDR X)))
(CDR) (CDR)
(CONS) (CONS)
(CONSP)
(COPY (COPY
LAMBDA LAMBDA
(X) (X)
@ -53,6 +59,11 @@
(DIFFERENCE) (DIFFERENCE)
(DIVIDE (DIVIDE
LAMBDA (X Y) (CONS (QUOTIENT X Y) (CONS (REMAINDER X Y) (QUOTE NIL)))) LAMBDA (X Y) (CONS (QUOTIENT X Y) (CONS (REMAINDER X Y) (QUOTE NIL))))
(DOC)
(EFFACE
LAMBDA (X L) (COND ((NULL L) (QUOTE NIL))
((EQUAL X (CAR L)) (CDR L))
((QUOTE T) (RPLACD L (EFFACE X (CDR L))))))
(ERROR) (ERROR)
(EQ) (EQ)
(EQUAL) (EQUAL)
@ -78,6 +89,7 @@
((QUOTE T) (INTERSECTION (CDR X) Y)))) ((QUOTE T) (INTERSECTION (CDR X) Y))))
(LENGTH LAMBDA (L) (COND ((EQ NIL L) 0) (T (ADD1 (LENGTH (CDR L)))))) (LENGTH LAMBDA (L) (COND ((EQ NIL L) 0) (T (ADD1 (LENGTH (CDR L))))))
(LESSP) (LESSP)
(MAPLIST LAMBDA (L F) (COND ((NULL L) NIL) ((QUOTE T) (CONS (F (CAR L)) (MAPLIST (CDR L) F)))))
(MEMBER (MEMBER
LAMBDA LAMBDA
(A X) (A X)

View file

@ -0,0 +1,7 @@
;; Not present in Lisp 1.5(!)
assoc[x; l] = [null[l] -> NIL;
and[consp[car[l]]; eq[caar[l]; x]] -> cdar[l];
T -> assoc[x; cdr[l]]]
;; (ASSOC 'C (PAIR '(A B C D E F) (RANGE 1 6)))

View file

@ -0,0 +1,6 @@
;; page 63. I'm not at all sure why an implementation using RPLACD is preferred
;; over a pure functional implementation here.
efface[x; l] = [null[l] -> NIL;
equal[x; car[l]] -> cdr[l];
T -> rplacd[l; efface[x; cdr[l]]]]

View file

@ -0,0 +1,4 @@
;; page 63
maplist[l; f] = [null[l] -> nil;
T -> cons[f[car[l]]; maplist[cdr[l]; f]]]

View file

@ -13,7 +13,7 @@
[beowulf.cons-cell :refer [make-beowulf-list make-cons-cell [beowulf.cons-cell :refer [make-beowulf-list make-cons-cell
pretty-print T F]] pretty-print T F]]
[beowulf.host :refer [ADD1 AND ASSOC ATOM ATOM? CAR CDR CONS DEFINE [beowulf.host :refer [ADD1 AND ASSOC ATOM ATOM? CAR CDR CONS DEFINE
DIFFERENCE EQ EQUAL ERROR FIXP GENSYM DIFFERENCE DOC EQ EQUAL ERROR FIXP GENSYM
GREATERP lax? LESSP LIST NUMBERP OBLIST GREATERP lax? LESSP LIST NUMBERP OBLIST
PAIRLIS PLUS QUOTIENT REMAINDER RPLACA RPLACD SET PAIRLIS PLUS QUOTIENT REMAINDER RPLACA RPLACD SET
TIMES TRACE traced? UNTRACE]] TIMES TRACE traced? UNTRACE]]
@ -256,6 +256,7 @@
CONS (safe-apply CONS args) CONS (safe-apply CONS args)
DEFINE (DEFINE (CAR args)) DEFINE (DEFINE (CAR args))
DIFFERENCE (DIFFERENCE (CAR args) (CADR args)) DIFFERENCE (DIFFERENCE (CAR args) (CADR args))
DOC (DOC (first args))
EQ (safe-apply EQ args) EQ (safe-apply EQ args)
EQUAL (safe-apply EQUAL args) EQUAL (safe-apply EQUAL args)
ERROR (safe-apply ERROR args) ERROR (safe-apply ERROR args)

View file

@ -118,5 +118,7 @@
(case (:cause data) (case (:cause data)
:quit nil :quit nil
;; default ;; default
(pprint data)) (do
(println "ERROR: " (.getMessage e))
(pprint data)))
(println e)))))))) (println e))))))))

View file

@ -3,12 +3,12 @@
NOTE: this is *very* hacky. You almost certainly do not want to NOTE: this is *very* hacky. You almost certainly do not want to
use this!" use this!"
(:require [beowulf.io :refer [default-sysout SYSIN]] (:require ;; [beowulf.io :refer [default-sysout SYSIN]]
[beowulf.host :refer [ASSOC]] [beowulf.manual :refer [format-page-references index
[beowulf.manual :refer [format-page-references index *manual-url*]] *manual-url* page-url]]
[beowulf.oblist :refer [NIL oblist]] [beowulf.oblist :refer [NIL oblist]]
[clojure.java.browse :refer [browse-url]] [clojure.java.browse :refer [browse-url]]
[clojure.string :refer [join replace upper-case]])) [clojure.string :refer [join replace upper-case]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; ;;;
@ -31,16 +31,18 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def host-functions (def host-functions
"Functions which we can infer are written in Clojure." "Functions which we can infer are written in Clojure. We need to collect these
(reduce at run-time, not compile time, hence memoised function, not variable."
merge (memoize
{} (fn [] (reduce
(map merge
ns-publics {}
['beowulf.bootstrap (map
'beowulf.host ns-publics
'beowulf.io ['beowulf.bootstrap
'beowulf.read]))) 'beowulf.host
'beowulf.io
'beowulf.read])))))
;; OK, this, improbably, works. There's probably a better way... ;; OK, this, improbably, works. There's probably a better way...
;; (:doc (meta (eval (read-string (str "#'" "beowulf.read" "/" "READ"))))) ;; (:doc (meta (eval (read-string (str "#'" "beowulf.read" "/" "READ")))))
@ -60,7 +62,7 @@
(defn- get-metadata-for-entry [entry key] (defn- get-metadata-for-entry [entry key]
(let [fn (host-functions (symbol (first entry)))] (let [fn ((host-functions) (symbol (first entry)))]
(get-metadata-for-function fn key))) (get-metadata-for-function fn key)))
(defn infer-type (defn infer-type
@ -70,7 +72,7 @@
(cond (cond
(= (second entry) 'LAMBDA) "Lisp function" (= (second entry) 'LAMBDA) "Lisp function"
(= (second entry) 'LABEL) "Labeled form" (= (second entry) 'LABEL) "Labeled form"
(host-functions (first entry)) (if (fn? (eval (symbol (host-functions (first entry))))) ((host-functions) (first entry)) (if (fn? (eval (symbol ((host-functions) (first entry)))))
"Host function" "Host function"
"Host variable") "Host variable")
:else "Lisp variable")) :else "Lisp variable"))
@ -118,12 +120,12 @@
(defn gen-doc-table (defn gen-doc-table
([] ([]
(gen-doc-table default-sysout)) ;; (gen-doc-table default-sysout))
([sysfile] ;; ([sysfile]
(when (= NIL @oblist) ;; (when (= NIL @oblist)
(try (SYSIN sysfile) ;; (try (SYSIN sysfile)
(catch Throwable any ;; (catch Throwable any
(println (.getMessage any) " while reading " sysfile)))) ;; (println (.getMessage any) " while reading " sysfile))))
(join (join
"\n" "\n"
(doall (doall
@ -151,3 +153,18 @@
(list "## Index" (list "## Index"
"" ""
(gen-doc-table))))))))) (gen-doc-table)))))))))
(defn open-doc
"Open the documentation page for this `symbol`, if known, in the default
web browser."
[symbol]
(let [doc (get-metadata-for-function symbol :doc)]
(if-let [pages (:page-nos (index (keyword symbol)))]
(browse-url (page-url (first pages)))
(if doc
(println doc)
(throw (ex-info "No documentation found"
{:phase :host
:function 'DOC
:args (list symbol)
:type :beowulf}))))))

View file

@ -6,6 +6,7 @@
[beowulf.cons-cell :refer [F make-cons-cell make-beowulf-list [beowulf.cons-cell :refer [F make-cons-cell make-beowulf-list
pretty-print T]] pretty-print T]]
;; note hyphen - this is Clojure... ;; note hyphen - this is Clojure...
[beowulf.gendoc :refer [open-doc]]
[beowulf.oblist :refer [*options* oblist NIL]]) [beowulf.oblist :refer [*options* oblist NIL]])
(:import [beowulf.cons_cell ConsCell] (:import [beowulf.cons_cell ConsCell]
;; note underscore - same namespace, but Java. ;; note underscore - same namespace, but Java.
@ -268,8 +269,8 @@
;; TODO: These are candidates for moving to Lisp urgently! ;; TODO: These are candidates for moving to Lisp urgently!
(defn ASSOC (defn ASSOC
"If a is an association list such as the one formed by PAIRLIS in the above "If `a` is an association list such as the one formed by PAIRLIS in the above
example, then assoc will produce the first pair whose first term is x. Thus example, then assoc will produce the first pair whose first term is `x`. Thus
it is a table searching function. it is a table searching function.
All args are assumed to be `beowulf.cons-cell/ConsCell` objects. All args are assumed to be `beowulf.cons-cell/ConsCell` objects.
@ -478,3 +479,24 @@
[s] [s]
(when (symbol? s) (when (symbol? s)
(swap! traced-symbols #(set (remove (fn [x] (= s x)) %))))) (swap! traced-symbols #(set (remove (fn [x] (= s x)) %)))))
;;;; Extensions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn DOC
"Open the page for this `symbol` in the Lisp 1.5 manual, if known, in the
default web browser.
**NOTE THAT** this is an extension function, not available in strct mode."
[symbol]
(when (lax? 'DOC)
(open-doc symbol)))
(defn CONSP
"Return `T` if object `o` is a cons cell, else `F`.
**NOTE THAT** this is an extension function, not available in strct mode.
I believe that Lisp 1.5 did not have any mechanism for testing whether an
argument was, or was not, a cons cell."
[o]
(when (lax? 'CONSP)
(if (instance? o ConsCell) 'T 'F)))

View file

@ -63,7 +63,9 @@
"Dump the current content of the object list to file. If no `filepath` is "Dump the current content of the object list to file. If no `filepath` is
specified, a file name will be constructed of the symbol `Sysout` and specified, a file name will be constructed of the symbol `Sysout` and
the current date. File paths will be considered relative to the filepath the current date. File paths will be considered relative to the filepath
set when starting Lisp." set when starting Lisp.
**NOTE THAT** this is an extension function, not available in strct mode."
([] ([]
(SYSOUT nil)) (SYSOUT nil))
([filepath] ([filepath]
@ -92,7 +94,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.
**NOTE THAT** this is an extension function, not available in strct mode."
([] ([]
(SYSIN (or (:read *options*) default-sysout))) (SYSIN (or (:read *options*) default-sysout)))
([filename] ([filename]