This isn't working, but it's very close.

This commit is contained in:
Simon Brooke 2023-04-05 23:35:41 +01:00
parent b5afb1ad44
commit 5b5ddb9444
7 changed files with 447 additions and 402 deletions

View file

@ -4,4 +4,4 @@
2. [MIT AI Memo 1, John McCarthy, September 1958](https://www.softwarepreservation.org/projects/LISP/MIT/AIM-001.pdf)
3. [Lisp 1 Programmer's Manual, Phyllis Fox, March 1960](https://bitsavers.org/pdf/mit/rle_lisp/LISP_I_Programmers_Manual_Mar60.pdf)
4. [Lisp 1.5 Programmer's Manual, Michael I. Levin, August 1962](https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf#page=81)
4. [Early LISP History (1956 - 1959)](https://dl.acm.org/doi/pdf/10.1145/800055.802047#page=3)
5. [Early LISP History (1956 - 1959), Herbert Stoyan, August 1984](https://dl.acm.org/doi/pdf/10.1145/800055.802047#page=3)

View file

@ -1,157 +1,223 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Beowulf Sysout file generated at 2023-03-31T02:24:08.808
;; Beowulf 0.3.0-SNAPSHOT Sysout file generated at 2023-04-05T23:30:32.954
;; generated by simon
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
((NIL)
(T . T)
(F)
(ADD1)
(AND)
(APPEND LAMBDA
(X Y) (COND ((NULL X) Y) ((QUOTE T) (CONS (CAR X) (APPEND (CDR X) Y)))))
(APPLY)
(ASSOC LAMBDA (X L)
(COND
((NULL L) (QUOTE NIL))
((AND (CONSP (CAR L)) (EQ (CAAR L) X)) (CAR L))
((QUOTE T) (ASSOC X (CDR L)))))
(ATOM)
(CAR)
(CAAAAR LAMBDA (X) (CAR (CAR (CAR (CAR X)))))
(CAAADR LAMBDA (X) (CAR (CAR (CAR (CDR X)))))
(CAAAR LAMBDA (X) (CAR (CAR (CAR X))))
(CAADAR LAMBDA (X) (CAR (CAR (CDR (CAR X)))))
(CAADDR LAMBDA (X) (CAR (CAR (CDR (CDR X)))))
(CAADR LAMBDA (X) (CAR (CAR (CDR X))))
(CAAR LAMBDA (X) (CAR (CAR X)))
(CADAAR LAMBDA (X) (CAR (CDR (CAR (CAR X)))))
(CADADR LAMBDA (X) (CAR (CDR (CAR (CDR X)))))
(CADAR LAMBDA (X) (CAR (CDR (CAR X))))
(CADDAR LAMBDA (X) (CAR (CDR (CDR (CAR X)))))
(CADDDR LAMBDA (X) (CAR (CDR (CDR (CDR X)))))
(CADDR LAMBDA (X) (CAR (CDR (CDR X))))
(CADR LAMBDA (X) (CAR (CDR X)))
(CDAAAR LAMBDA (X) (CDR (CAR (CAR (CAR X)))))
(CDAADR LAMBDA (X) (CDR (CAR (CAR (CDR X)))))
(CDAAR LAMBDA (X) (CDR (CAR (CAR X))))
(CDADAR LAMBDA (X) (CDR (CAR (CDR (CAR X)))))
(CDADDR LAMBDA (X) (CDR (CAR (CDR (CDR X)))))
(CDADR LAMBDA (X) (CDR (CAR (CDR X))))
(CDAR LAMBDA (X) (CDR (CAR X)))
(CDDAAR LAMBDA (X) (CDR (CDR (CAR (CAR X)))))
(CDDADR LAMBDA (X) (CDR (CDR (CAR (CDR X)))))
(CDDAR LAMBDA (X) (CDR (CDR (CAR X))))
(CDDDAR LAMBDA (X) (CDR (CDR (CDR (CAR X)))))
(CDDDDR LAMBDA (X) (CDR (CDR (CDR (CDR X)))))
(CDDDR LAMBDA (X) (CDR (CDR (CDR X))))
(CDDR LAMBDA (X) (CDR (CDR X)))
(CDR)
(CONS)
(CONSP)
((NIL 32767 APVAL NIL)
(T 32767 APVAL T)
(F 32767 APVAL NIL)
(ADD1 32767 SUBR (BEOWULF HOST ADD1))
(AND 32767 SUBR (BEOWULF HOST AND))
(APPEND
32767
EXPR
(LAMBDA
(X Y) (COND ((NULL X) Y) ((QUOTE T) (CONS (CAR X) (APPEND (CDR X) Y))))))
(APPLY 32767 SUBR (BEOWULF BOOTSTRAP APPLY))
(ASSOC
32767
EXPR
(LAMBDA
(X L)
(COND
((NULL L) (QUOTE NIL))
((AND (CONSP (CAR L)) (EQ (CAAR L) X)) (CAR L))
((QUOTE T) (ASSOC X (CDR L)))))
SUBR (BEOWULF HOST ASSOC))
(ATOM 32767 SUBR (BEOWULF HOST ATOM))
(CAR 32767 SUBR (BEOWULF HOST CAR))
(CAAAAR 32767 EXPR (LAMBDA (X) (CAR (CAR (CAR (CAR X))))))
(CAAADR 32767 EXPR (LAMBDA (X) (CAR (CAR (CAR (CDR X))))))
(CAAAR 32767 EXPR (LAMBDA (X) (CAR (CAR (CAR X)))))
(CAADAR 32767 EXPR (LAMBDA (X) (CAR (CAR (CDR (CAR X))))))
(CAADDR 32767 EXPR (LAMBDA (X) (CAR (CAR (CDR (CDR X))))))
(CAADR 32767 EXPR (LAMBDA (X) (CAR (CAR (CDR X)))))
(CAAR 32767 EXPR (LAMBDA (X) (CAR (CAR X))))
(CADAAR 32767 EXPR (LAMBDA (X) (CAR (CDR (CAR (CAR X))))))
(CADADR 32767 EXPR (LAMBDA (X) (CAR (CDR (CAR (CDR X))))))
(CADAR 32767 EXPR (LAMBDA (X) (CAR (CDR (CAR X)))))
(CADDAR 32767 EXPR (LAMBDA (X) (CAR (CDR (CDR (CAR X))))))
(CADDDR 32767 EXPR (LAMBDA (X) (CAR (CDR (CDR (CDR X))))))
(CADDR 32767 EXPR (LAMBDA (X) (CAR (CDR (CDR X)))))
(CADR 32767 EXPR (LAMBDA (X) (CAR (CDR X))))
(CDAAAR 32767 EXPR (LAMBDA (X) (CDR (CAR (CAR (CAR X))))))
(CDAADR 32767 EXPR (LAMBDA (X) (CDR (CAR (CAR (CDR X))))))
(CDAAR 32767 EXPR (LAMBDA (X) (CDR (CAR (CAR X)))))
(CDADAR 32767 EXPR (LAMBDA (X) (CDR (CAR (CDR (CAR X))))))
(CDADDR 32767 EXPR (LAMBDA (X) (CDR (CAR (CDR (CDR X))))))
(CDADR 32767 EXPR (LAMBDA (X) (CDR (CAR (CDR X)))))
(CDAR 32767 EXPR (LAMBDA (X) (CDR (CAR X))))
(CDDAAR 32767 EXPR (LAMBDA (X) (CDR (CDR (CAR (CAR X))))))
(CDDADR 32767 EXPR (LAMBDA (X) (CDR (CDR (CAR (CDR X))))))
(CDDAR 32767 EXPR (LAMBDA (X) (CDR (CDR (CAR X)))))
(CDDDAR 32767 EXPR (LAMBDA (X) (CDR (CDR (CDR (CAR X))))))
(CDDDDR 32767 EXPR (LAMBDA (X) (CDR (CDR (CDR (CDR X))))))
(CDDDR 32767 EXPR (LAMBDA (X) (CDR (CDR (CDR X)))))
(CDDR 32767 EXPR (LAMBDA (X) (CDR (CDR X))))
(CDR 32767 SUBR (BEOWULF HOST CDR))
(CONS 32767 SUBR (BEOWULF HOST CONS))
(CONSP 32767 SUBR (BEOWULF HOST CONSP))
(COPY
LAMBDA
(X)
(COND
((NULL X) (QUOTE NIL))
((ATOM X) X) ((QUOTE T) (CONS (COPY (CAR X)) (COPY (CDR X))))))
(DEFINE)
(DIFFERENCE)
32767
EXPR
(LAMBDA
(X)
(COND
((NULL X) (QUOTE NIL))
((ATOM X) X) ((QUOTE T) (CONS (COPY (CAR X)) (COPY (CDR X)))))))
(DEFINE 32767 SUBR (BEOWULF HOST DEFINE))
(DIFFERENCE 32767 SUBR (BEOWULF HOST DIFFERENCE))
(DIVIDE
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)
(EQ)
(EQUAL)
(EVAL)
32767
EXPR
(LAMBDA (X Y) (CONS (QUOTIENT X Y) (CONS (REMAINDER X Y) (QUOTE NIL)))))
(DOC 32767 SUBR (BEOWULF HOST DOC))
(EFFACE
32767
EXPR
(LAMBDA
(X L)
(COND
((NULL L) (QUOTE NIL))
((EQUAL X (CAR L)) (CDR L)) ((QUOTE T) (RPLACD L (EFFACE X (CDR L)))))))
(ERROR 32767 SUBR (BEOWULF HOST ERROR))
(EQ 32767 SUBR (BEOWULF HOST EQ))
(EQUAL 32767 SUBR (BEOWULF HOST EQUAL))
(EVAL 32767 SUBR (BEOWULF BOOTSTRAP EVAL))
(FACTORIAL
LAMBDA (N) (COND ((EQ N 1) 1) (T (TIMES N (FACTORIAL (SUB1 N))))))
(FIXP)
(GENSYM)
32767
EXPR (LAMBDA (N) (COND ((EQ N 1) 1) (T (TIMES N (FACTORIAL (SUB1 N)))))))
(FIXP 32767 SUBR (BEOWULF HOST FIXP))
(GENSYM 32767 SUBR (BEOWULF HOST GENSYM))
(GET
LAMBDA
(X Y)
(COND
((NULL X) (QUOTE NIL))
((EQ (CAR X) Y) (CAR (CDR X))) ((QUOTE T) (GET (CDR X) Y))))
(GREATERP)
(INTEROP)
32767
EXPR
(LAMBDA
(X Y)
(COND
((NULL X) (QUOTE NIL))
((EQ (CAR X) Y) (CAR (CDR X))) ((QUOTE T) (GET (CDR X) Y))))
SUBR (BEOWULF HOST GET))
(GREATERP 32767 SUBR (BEOWULF HOST GREATERP))
(INTEROP 32767 SUBR (BEOWULF INTEROP INTEROP))
(INTERSECTION
LAMBDA
(X Y)
(COND
((NULL X) (QUOTE NIL))
((MEMBER (CAR X) Y) (CONS (CAR X) (INTERSECTION (CDR X) Y)))
((QUOTE T) (INTERSECTION (CDR X) Y))))
32767
EXPR
(LAMBDA
(X Y)
(COND
((NULL X) (QUOTE NIL))
((MEMBER (CAR X) Y) (CONS (CAR X) (INTERSECTION (CDR X) Y)))
((QUOTE T) (INTERSECTION (CDR X) Y)))))
(LENGTH
LAMBDA
(L) (COND ((EQ NIL L) 0) ((CONSP (CDR L)) (ADD1 (LENGTH (CDR L)))) (T 0)))
(LESSP)
(MAPLIST LAMBDA (L F) (COND ((NULL L) NIL) ((QUOTE T) (CONS (F (CAR L)) (MAPLIST (CDR L) F)))))
32767
EXPR
(LAMBDA
(L)
(COND ((EQ NIL L) 0) ((CONSP (CDR L)) (ADD1 (LENGTH (CDR L)))) (T 0))))
(LESSP 32767 SUBR (BEOWULF HOST LESSP))
(MAPLIST
32767
EXPR
(LAMBDA
(L F)
(COND
((NULL L) NIL) ((QUOTE T) (CONS (F (CAR L)) (MAPLIST (CDR L) F))))))
(MEMBER
LAMBDA
(A X)
(COND
((NULL X) (QUOTE F))
((EQ A (CAR X)) (QUOTE T)) ((QUOTE T) (MEMBER A (CDR X)))))
(MINUSP LAMBDA (X) (LESSP X 0))
(NOT LAMBDA (X) (COND (X (QUOTE NIL)) ((QUOTE T) (QUOTE T))))
(NULL LAMBDA (X) (COND ((EQUAL X NIL) (QUOTE T)) (T (QUOTE F))))
(NUMBERP)
(OBLIST)
(ONEP LAMBDA (X) (EQ X 1))
32767
EXPR
(LAMBDA
(A X)
(COND
((NULL X) (QUOTE F))
((EQ A (CAR X)) (QUOTE T)) ((QUOTE T) (MEMBER A (CDR X))))))
(MINUSP 32767 EXPR (LAMBDA (X) (LESSP X 0)))
(NOT 32767 EXPR (LAMBDA (X) (COND (X (QUOTE NIL)) ((QUOTE T) (QUOTE T)))))
(NULL
32767 EXPR (LAMBDA (X) (COND ((EQUAL X NIL) (QUOTE T)) (T (QUOTE F)))))
(NUMBERP 32767 SUBR (BEOWULF HOST NUMBERP))
(OBLIST 32767 SUBR (BEOWULF HOST OBLIST))
(ONEP 32767 EXPR (LAMBDA (X) (EQ X 1)))
(PAIR
LAMBDA
(X Y)
(COND
((AND (NULL X) (NULL Y)) NIL)
((NULL X) (ERROR (QUOTE F2)))
((NULL Y) (ERROR (QUOTE F3)))
(T (CONS (CONS (CAR X) (CAR Y)) (PAIR (CDR X) (CDR Y))))))
(PAIRLIS LAMBDA (X Y A)
(COND
((NULL X) A)
((QUOTE T) (CONS (CONS (CAR X) (CAR Y)) (PAIRLIS (CDR X) (CDR Y) A)))))
(PLUS)
(PRETTY)
(PRINT)
32767
EXPR
(LAMBDA
(X Y)
(COND
((AND (NULL X) (NULL Y)) NIL)
((NULL X) (ERROR (QUOTE F2)))
((NULL Y) (ERROR (QUOTE F3)))
(T (CONS (CONS (CAR X) (CAR Y)) (PAIR (CDR X) (CDR Y)))))))
(PAIRLIS
32767
EXPR
(LAMBDA
(X Y A)
(COND
((NULL X) A)
((QUOTE T) (CONS (CONS (CAR X) (CAR Y)) (PAIRLIS (CDR X) (CDR Y) A)))))
SUBR (BEOWULF HOST PAIRLIS))
(PLUS 32767 SUBR (BEOWULF HOST PLUS))
(PRETTY 32767)
(PRINT 32767)
(PROP
LAMBDA
(X Y U)
(COND
((NULL X) (U)) ((EQ (CAR X) Y) (CDR X)) ((QUOTE T) (PROP (CDR X) Y U))))
(QUOTE LAMBDA (X) X)
(QUOTIENT)
(RANGE LAMBDA (N M) (COND ((LESSP M N) (QUOTE NIL)) ((QUOTE T) (CONS N (RANGE (ADD1 N) M)))))
(READ)
(REMAINDER)
32767
EXPR
(LAMBDA
(X Y U)
(COND
((NULL X) (U))
((EQ (CAR X) Y) (CDR X)) ((QUOTE T) (PROP (CDR X) Y U)))))
(QUOTE 32767 EXPR (LAMBDA (X) X))
(QUOTIENT 32767 SUBR (BEOWULF HOST QUOTIENT))
(RANGE
32767
EXPR
(LAMBDA
(N M)
(COND
((LESSP M N) (QUOTE NIL)) ((QUOTE T) (CONS N (RANGE (ADD1 N) M))))))
(READ 32767 SUBR (BEOWULF READ READ))
(REMAINDER 32767 SUBR (BEOWULF HOST REMAINDER))
(REPEAT
LAMBDA (N X) (COND ((EQ N 0) NIL) (T (CONS X (REPEAT (SUB1 N) X)))))
(RPLACA)
(RPLACD)
(SET)
(SUB1 LAMBDA (N) (DIFFERENCE N 1))
(SUB2 LAMBDA (A Z)
(COND
((NULL A) Z)
((EQ (CAAR A) Z) (CDAR A))
((QUOTE T) (SUB2 (CDAR A) Z))))
(SUBLIS LAMBDA (A Y) (COND ((ATOM Y) (SUB2 A Y)) ((QUOTE T) (CONS))))
(SUBST LAMBDA (X Y Z)
(COND
((EQUAL Y Z) X)
((ATOM Z) Z)
((QUOTE T) (CONS (SUBST X Y (CAR Z)) (SUBST X Y (CDR Z))))))
(SYSIN)
(SYSOUT) (TERPRI) (TIMES) (TRACE)
(UNION LAMBDA (X Y)
(COND
((NULL X) Y)
((MEMBER (CAR X) Y) (UNION (CDR X) Y))
(T (CONS (CAR X) (UNION (CDR X) Y)))))
(UNTRACE)
(ZEROP LAMBDA (N) (EQ N 0)))
32767
EXPR
(LAMBDA (N X) (COND ((EQ N 0) NIL) (T (CONS X (REPEAT (SUB1 N) X))))))
(RPLACA 32767 SUBR (BEOWULF HOST RPLACA))
(RPLACD 32767 SUBR (BEOWULF HOST RPLACD))
(SET 32767 SUBR (BEOWULF HOST SET))
(SUB1 32767 EXPR (LAMBDA (N) (DIFFERENCE N 1)) SUBR (BEOWULF HOST SUB1))
(SUB2
32767
EXPR
(LAMBDA
(A Z)
(COND
((NULL A) Z) ((EQ (CAAR A) Z) (CDAR A)) ((QUOTE T) (SUB2 (CDAR A) Z)))))
(SUBLIS
32767 EXPR (LAMBDA (A Y) (COND ((ATOM Y) (SUB2 A Y)) ((QUOTE T) (CONS)))))
(SUBST
32767
EXPR
(LAMBDA
(X Y Z)
(COND
((EQUAL Y Z) X)
((ATOM Z) Z)
((QUOTE T) (CONS (SUBST X Y (CAR Z)) (SUBST X Y (CDR Z)))))))
(SYSIN 32767 SUBR (BEOWULF IO SYSIN))
(SYSOUT 32767 SUBR (BEOWULF IO SYSOUT))
(TERPRI 32767)
(TIMES 32767 SUBR (BEOWULF HOST TIMES))
(TRACE 32767 SUBR (BEOWULF HOST TRACE))
(UNION
32767
EXPR
(LAMBDA
(X Y)
(COND
((NULL X) Y)
((MEMBER (CAR X) Y) (UNION (CDR X) Y))
(T (CONS (CAR X) (UNION (CDR X) Y))))))
(UNTRACE 32767 SUBR (BEOWULF HOST UNTRACE))
(ZEROP 32767 EXPR (LAMBDA (N) (EQ N 0))))

View file

@ -9,18 +9,11 @@
ALLUPPERCASE are Lisp 1.5 functions (although written in Clojure) and that
therefore all arguments must be numbers, symbols or `beowulf.cons_cell.ConsCell`
objects."
(:require [clojure.string :as s]
[beowulf.cons-cell :refer [make-beowulf-list make-cons-cell
pretty-print T F]]
[beowulf.host :refer [ADD1 AND ASSOC ATOM ATOM? CAR CDR CONS DEFINE
DIFFERENCE DOC EQ EQUAL ERROR FIXP GENSYM
GREATERP lax? LESSP LIST NUMBERP OBLIST PAIRLIS
PLUS
QUOTIENT REMAINDER RPLACA RPLACD SET
TIMES TRACE traced? UNTRACE]]
[beowulf.io :refer [SYSIN SYSOUT]]
[beowulf.oblist :refer [*options* oblist NIL]]
[beowulf.read :refer [READ]])
(:require [beowulf.cons-cell :refer [make-cons-cell T]]
[beowulf.host :refer [ATOM CAAR CADAR CADDR CADR CAR CDR GET LIST
NUMBERP PAIRLIS traced?]]
[beowulf.interop :refer [to-clojure]]
[beowulf.oblist :refer [*options* NIL oblist]])
(:import [beowulf.cons_cell ConsCell]
[clojure.lang Symbol]))
@ -51,171 +44,6 @@
[f]
`(quote ~f))
(defn uaf
"Universal access function; `l` is expected to be an arbitrary LISP list, `path`
a (clojure) list of the characters `a` and `d`. Intended to make declaring
all those fiddly `#'c[ad]+r'` functions a bit easier"
[l path]
(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}))))))
(defmacro CAAR [x] `(uaf ~x '(\a \a)))
(defmacro CADR [x] `(uaf ~x '(\a \d)))
(defmacro CDDR [x] `(uaf ~x '(\d \d)))
(defmacro CDAR [x] `(uaf ~x '(\d \a)))
(defmacro CAAAR [x] `(uaf ~x '(\a \a \a)))
(defmacro CAADR [x] `(uaf ~x '(\a \a \d)))
(defmacro CADAR [x] `(uaf ~x '(\a \d \a)))
(defmacro CADDR [x] `(uaf ~x '(\a \d \d)))
(defmacro CDDAR [x] `(uaf ~x '(\d \d \a)))
(defmacro CDDDR [x] `(uaf ~x '(\d \d \d)))
(defmacro CDAAR [x] `(uaf ~x '(\d \a \a)))
(defmacro CDADR [x] `(uaf ~x '(\d \a \d)))
(defmacro CAAAAR [x] `(uaf ~x '(\a \a \a \a)))
(defmacro CAADAR [x] `(uaf ~x '(\a \a \d \a)))
(defmacro CADAAR [x] `(uaf ~x '(\a \d \a \a)))
(defmacro CADDAR [x] `(uaf ~x '(\a \d \d \a)))
(defmacro CDDAAR [x] `(uaf ~x '(\d \d \a \a)))
(defmacro CDDDAR [x] `(uaf ~x '(\d \d \d \a)))
(defmacro CDAAAR [x] `(uaf ~x '(\d \a \a \a)))
(defmacro CDADAR [x] `(uaf ~x '(\d \a \d \a)))
(defmacro CAAADR [x] `(uaf ~x '(\a \a \a \d)))
(defmacro CAADDR [x] `(uaf ~x '(\a \a \d \d)))
(defmacro CADADR [x] `(uaf ~x '(\a \d \a \d)))
(defmacro CADDDR [x] `(uaf ~x '(\a \d \d \d)))
(defmacro CDDADR [x] `(uaf ~x '(\d \d \a \d)))
(defmacro CDDDDR [x] `(uaf ~x '(\d \d \d \d)))
(defmacro CDAADR [x] `(uaf ~x '(\d \a \a \d)))
(defmacro CDADDR [x] `(uaf ~x '(\d \a \d \d)))
;;;; INTEROP feature ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn interop-interpret-q-name
"For interoperation with Clojure, it will often be necessary to pass
qualified names that are not representable in Lisp 1.5. This function
takes a sequence in the form `(PART PART PART... NAME)` and returns
a symbol in the form `PART.PART.PART/NAME`. This symbol will then be
tried in both that form and lower-cased. Names with hyphens or
underscores cannot be represented with this scheme."
[l]
(if
(seq? l)
(symbol
(s/reverse
(s/replace-first
(s/reverse
(s/join "." (map str l)))
"."
"/")))
l))
(defn to-beowulf
"Return a beowulf-native representation of the Clojure object `o`.
Numbers and symbols are unaffected. Collections have to be converted;
strings must be converted to symbols."
[o]
(cond
(coll? o) (make-beowulf-list o)
(string? o) (symbol (s/upper-case o))
:else o))
(defn to-clojure
"If l is a `beowulf.cons_cell.ConsCell`, return a Clojure list having the
same members in the same order."
[l]
(cond
(not (instance? beowulf.cons_cell.ConsCell l))
l
(= (CDR l) NIL)
(list (to-clojure (CAR l)))
:else
(conj (to-clojure (CDR l)) (to-clojure (CAR l)))))
(defn INTEROP
"Clojure (or other host environment) interoperation API. `fn-symbol` is expected
to be either
1. a symbol bound in the host environment to a function; or
2. a sequence (list) of symbols forming a qualified path name bound to a
function.
Lower case characters cannot normally be represented in Lisp 1.5, so both the
upper case and lower case variants of `fn-symbol` will be tried. If the
function you're looking for has a mixed case name, that is not currently
accessible.
`args` is expected to be a Lisp 1.5 list of arguments to be passed to that
function. Return value must be something acceptable to Lisp 1.5, so either
a symbol, a number, or a Lisp 1.5 list.
If `fn-symbol` is not found (even when cast to lower case), or is not a function,
or the value returned cannot be represented in Lisp 1.5, an exception is thrown
with `:cause` bound to `:interop` and `:detail` set to a value representing the
actual problem."
[fn-symbol args]
(if-not (:strict *options*)
(let
[q-name (if
(seq? fn-symbol)
(interop-interpret-q-name fn-symbol)
fn-symbol)
l-name (symbol (s/lower-case q-name))
f (cond
(try
(fn? (eval l-name))
(catch java.lang.ClassNotFoundException _ nil)) l-name
(try
(fn? (eval q-name))
(catch java.lang.ClassNotFoundException _ nil)) q-name
:else (throw
(ex-info
(str "INTEROP: unknown function `" fn-symbol "`")
{:cause :interop
:detail :not-found
:name fn-symbol
:also-tried l-name})))
args' (to-clojure args)]
(print (str "INTEROP: evaluating `" (cons f args') "`"))
(flush)
(let [result (eval (conj args' f))] ;; this has the potential to blow up the world
(println (str "; returning `" result "`"))
(cond
(instance? beowulf.cons_cell.ConsCell result) result
(coll? result) (make-beowulf-list result)
(symbol? result) result
(string? result) (symbol result)
(number? result) result
:else (throw
(ex-info
(str "INTEROP: Cannot return `" result "` to Lisp 1.5.")
{:cause :interop
:detail :not-representable
:result result})))))
(throw
(ex-info
(str "INTEROP not allowed in strict mode.")
{:cause :interop
:detail :strict}))))
(defn- traced-apply
"Like `APPLY`, but with trace output to console."
[function-symbol args lisp-fn environment depth]
@ -247,47 +75,10 @@
environment
depth)
(APPLY lisp-fn args environment depth))
(case function-symbol ;; there must be a better way of doing this!
ADD1 (safe-apply ADD1 args)
AND (safe-apply AND args)
APPLY (APPLY (first args) (rest args) environment depth)
ATOM (ATOM? (CAR args))
CAR (safe-apply CAR args)
CDR (safe-apply CDR args)
CONS (safe-apply CONS args)
DEFINE (DEFINE (CAR args))
DIFFERENCE (DIFFERENCE (CAR args) (CADR args))
DOC (DOC (first args))
EQ (safe-apply EQ args)
EQUAL (safe-apply EQUAL args)
ERROR (safe-apply ERROR args)
;; think about EVAL. Getting the environment right is subtle
FIXP (safe-apply FIXP args)
GENSYM (GENSYM)
GREATERP (safe-apply GREATERP args)
INTEROP (when (lax? INTEROP) (safe-apply INTEROP args))
LESSP (safe-apply LESSP args)
LIST (safe-apply LIST args)
NUMBERP (safe-apply NUMBERP args)
OBLIST (OBLIST)
PLUS (safe-apply PLUS args)
PRETTY (when (lax? 'PRETTY)
(safe-apply pretty-print args))
PRINT (safe-apply print args)
QUOTIENT (safe-apply QUOTIENT args)
READ (READ)
REMAINDER (safe-apply REMAINDER args)
RPLACA (safe-apply RPLACA args)
RPLACD (safe-apply RPLACD args)
SET (safe-apply SET args)
SYSIN (when (lax? 'SYSIN)
(safe-apply SYSIN args))
SYSOUT (when (lax? 'SYSOUT)
(safe-apply SYSOUT args))
TERPRI (println)
TIMES (safe-apply TIMES args)
TRACE (safe-apply TRACE args)
UNTRACE (safe-apply UNTRACE args)
(if function-symbol
(let [f (GET function-symbol 'SUBR)]
(when f
(apply @(resolve f) (to-clojure args))))
;; else
(ex-info "No function found"
{:context "APPLY"
@ -309,7 +100,7 @@
{:context "APPLY"
:function "NIL"
:args args})))
(= (ATOM? function) T) (apply-symbolic function args environment (inc depth))
(= (ATOM function) T) (apply-symbolic function args environment (inc depth))
:else (case (first function)
LABEL (APPLY
(CADDR function)
@ -355,14 +146,13 @@
(EVAL (CAR args) env depth)
(EVLIS (CDR args) env depth))))
(defn- eval-symbolic [^Symbol s env]
(let [binding (ASSOC s env)]
(if (= binding NIL)
(throw (ex-info (format "No binding for symbol `%s`" s)
{:phase :eval
:symbol s}))
(CDR binding))))
;; (defn- eval-symbolic [^Symbol s env]
;; (let [binding (ASSOC s env)]
;; (if (= binding NIL)
;; (throw (ex-info (format "No binding for symbol `%s`" s)
;; {:phase :eval
;; :symbol s}))
;; (CDR binding))))
(defn EVAL
"Evaluate this `expr` and return the result. If `environment` is not passed,
@ -376,7 +166,7 @@
([expr env depth]
(cond
(= (NUMBERP expr) T) expr
(symbol? expr) (eval-symbolic expr env)
(symbol? expr) (GET expr 'APVAL)
(string? expr) (if (:strict *options*)
(throw
(ex-info
@ -385,18 +175,16 @@
:detail :strict
:expr expr}))
(symbol expr))
(=
(ATOM? (CAR expr))
T) (case (CAR expr)
QUOTE (CADR expr)
FUNCTION (LIST 'FUNARG (CADR expr) )
COND (EVCON (CDR expr) env depth)
(= (ATOM (CAR expr)) T) (case (CAR expr)
QUOTE (CADR expr)
FUNCTION (LIST 'FUNARG (CADR expr))
COND (EVCON (CDR expr) env depth)
;; else
(APPLY
(CAR expr)
(EVLIS (CDR expr) env depth)
env
depth))
(APPLY
(CAR expr)
(EVLIS (CDR expr) env depth)
env
depth))
:else (APPLY
(CAR expr)
(EVLIS (CDR expr) env depth)

View file

@ -424,9 +424,9 @@
(make-beowulf-list (map CAR @oblist))
NIL))
(def ^:private magic-marker
(def magic-marker
"The unexplained magic number which marks the start of a property list."
(Integer/parseInt "777778" 8))
(Integer/parseInt "77777" 8))
(defn PUT
"Put this `value` as the value of the property indicated by this `indicator`
@ -460,7 +460,13 @@
It's clear that `GET` is expected to be defined in terms of `PROP`, but
we can't implement `PROP` here because we lack `EVAL`; and we can't have
`EVAL` here because it depends on `GET`."
`EVAL` here because both it and `APPLY` depends on `GET`.
OK, It's worse than that: the statement of the definition of `GET` (and
of) `PROP` on page 59 says that the first argument to each must be a list;
But the in the definition of `ASSOC` on page 70, when `GET` is called its
first argument is always an atom. Since it's `ASSOC` and `EVAL` which I
need to make work, I'm going to assume that page 59 is wrong."
[symbol indicator]
(let [binding (ASSOC symbol @oblist)]
(cond

129
src/beowulf/interop.clj Normal file
View file

@ -0,0 +1,129 @@
(ns beowulf.interop
(:require [beowulf.cons-cell :refer [make-beowulf-list]]
[beowulf.host :refer [CAR CDR]]
[beowulf.oblist :refer [*options* NIL]]
[clojure.string :as s :refer [last-index-of lower-case split
upper-case]]))
;;;; INTEROP feature ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn listify-qualified-name
"We need to be able to print something we can link to the particular Clojure
function `subr` in a form in which Lisp 1.5 is able to read it back in and
relink it.
This assumes `subr` is either
1. a string in the format `#'beowulf.io/SYSIN` or `beowulf.io/SYSIN`; or
2. something which, when coerced to a string with `str`, will have such
a format."
[subr]
(make-beowulf-list
(map
#(symbol (upper-case %))
(remove empty? (split (str subr) #"[#'./]")))))
(defn interpret-qualified-name
"For interoperation with Clojure, it will often be necessary to pass
qualified names that are not representable in Lisp 1.5. This function
takes a sequence in the form `(PART PART PART... NAME)` and returns
a symbol in the form `part.part.part/NAME`. This symbol will then be
tried in both that form and lower-cased. Names with hyphens or
underscores cannot be represented with this scheme."
([l]
(symbol
(let [n (s/join "."
(concat (map #(lower-case (str %)) (butlast l))
(list (last l))))
s (last-index-of n ".")]
(if s
(str (subs n 0 s) "/" (subs n (inc s)))
n)))))
(defn to-beowulf
"Return a beowulf-native representation of the Clojure object `o`.
Numbers and symbols are unaffected. Collections have to be converted;
strings must be converted to symbols."
[o]
(cond
(coll? o) (make-beowulf-list o)
(string? o) (symbol (s/upper-case o))
:else o))
(defn to-clojure
"If l is a `beowulf.cons_cell.ConsCell`, return a Clojure list having the
same members in the same order."
[l]
(cond
(not (instance? beowulf.cons_cell.ConsCell l))
l
(= (CDR l) NIL)
(list (to-clojure (CAR l)))
:else
(conj (to-clojure (CDR l)) (to-clojure (CAR l)))))
(defn INTEROP
"Clojure (or other host environment) interoperation API. `fn-symbol` is expected
to be either
1. a symbol bound in the host environment to a function; or
2. a sequence (list) of symbols forming a qualified path name bound to a
function.
Lower case characters cannot normally be represented in Lisp 1.5, so both the
upper case and lower case variants of `fn-symbol` will be tried. If the
function you're looking for has a mixed case name, that is not currently
accessible.
`args` is expected to be a Lisp 1.5 list of arguments to be passed to that
function. Return value must be something acceptable to Lisp 1.5, so either
a symbol, a number, or a Lisp 1.5 list.
If `fn-symbol` is not found (even when cast to lower case), or is not a function,
or the value returned cannot be represented in Lisp 1.5, an exception is thrown
with `:cause` bound to `:interop` and `:detail` set to a value representing the
actual problem."
[fn-symbol args]
(if-not (:strict *options*)
(let
[q-name (if
(seq? fn-symbol)
(interpret-qualified-name fn-symbol)
fn-symbol)
l-name (symbol (s/lower-case q-name))
f (cond
(try
(fn? (eval l-name))
(catch java.lang.ClassNotFoundException _ nil)) l-name
(try
(fn? (eval q-name))
(catch java.lang.ClassNotFoundException _ nil)) q-name
:else (throw
(ex-info
(str "INTEROP: unknown function `" fn-symbol "`")
{:cause :interop
:detail :not-found
:name fn-symbol
:also-tried l-name})))
args' (to-clojure args)]
(print (str "INTEROP: evaluating `" (cons f args') "`"))
(flush)
(let [result (eval (conj args' f))] ;; this has the potential to blow up the world
(println (str "; returning `" result "`"))
(cond
(instance? beowulf.cons_cell.ConsCell result) result
(coll? result) (make-beowulf-list result)
(symbol? result) result
(string? result) (symbol result)
(number? result) result
:else (throw
(ex-info
(str "INTEROP: Cannot return `" result "` to Lisp 1.5.")
{:cause :interop
:detail :not-representable
:result result})))))
(throw
(ex-info
(str "INTEROP not allowed in strict mode.")
{:cause :interop
:detail :strict}))))

View file

@ -15,8 +15,12 @@
oblist with data from that file.
Hence functions SYSOUT and SYSIN, which do just that."
(:require [beowulf.cons-cell :refer [pretty-print]]
[beowulf.oblist :refer [*options* oblist]]
(:require [beowulf.cons-cell :refer [make-beowulf-list make-cons-cell
pretty-print]]
[beowulf.host :refer [CADR CAR CDDR CDR]]
[beowulf.interop :refer [interpret-qualified-name
listify-qualified-name]]
[beowulf.oblist :refer [*options* NIL oblist]]
[beowulf.read :refer [READ]]
[clojure.java.io :refer [file resource]]
[clojure.string :refer [ends-with?]]
@ -44,7 +48,7 @@
(def ^:constant default-sysout "resources/lisp1.5.lsp")
(defn- full-path
(defn- full-path
[fp]
(str
(if (:filepath *options*)
@ -59,6 +63,24 @@
""
".lsp")))
;; (find-var (symbol "beowulf.io/SYSIN"))
;; (@(resolve (symbol "beowulf.host/TIMES")) 2 2)
(defn safely-wrap-subr
[entry]
(cond (= entry NIL) NIL
(= (CAR entry) 'SUBR) (make-cons-cell
(CAR entry)
(make-cons-cell
(listify-qualified-name (CADR entry))
(CDDR entry)))
:else (make-cons-cell
(CAR entry) (safely-wrap-subr (CDR entry)))))
(defn safely-wrap-subrs
[objects]
(make-beowulf-list (map safely-wrap-subr objects)))
(defn SYSOUT
"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
@ -79,7 +101,38 @@
(println (format ";; generated by %s" (System/getenv "USER"))))
(println (apply str (repeat 79 ";")))
(println)
(pretty-print @oblist)))))
(let [output (safely-wrap-subrs @oblist)]
(pretty-print output)
)))))
(defn- resolve-subr
"If this oblist `entry` references a subroutine, attempt to fix up that
reference."
[entry]
(cond (= entry NIL) NIL
(= (CAR entry) 'SUBR) (try
(make-cons-cell
(CAR entry)
(make-cons-cell
(interpret-qualified-name
(CADR entry))
(CDDR entry)))
(catch Exception _
(print "Warning: failed to resolve "
(CADR entry))
(CDDR entry)))
:else (make-cons-cell
(CAR entry) (resolve-subr (CDR entry)))))
(defn- resolve-subroutines
"Attempt to fix up the references to subroutines (Clojure functions) among
these `objects`, being new content for the object list."
[objects]
(make-beowulf-list
(map
resolve-subr
objects)))
(defn SYSIN
"Read the contents of the file at this `filename` into the object list.
@ -100,14 +153,16 @@
([]
(SYSIN (or (:read *options*) default-sysout)))
([filename]
(let [fp (file (full-path (str filename)))
file (when (and (.exists fp) (.canRead fp)) fp)
res (try (resource filename)
(catch Throwable _ nil))
content (try (READ (slurp (or file res)))
(catch Throwable any
(throw (ex-info "Could not read from file"
{:context "SYSIN"
:filepath fp}
any))))]
(swap! oblist #(when (or % (seq content)) content)))))
(let [fp (file (full-path (str filename)))
file (when (and (.exists fp) (.canRead fp)) fp)
res (try (resource filename)
(catch Throwable _ nil))
content (try (READ (slurp (or file res)))
(catch Throwable any
(throw (ex-info "Could not read from file"
{:context "SYSIN"
:filepath fp}
any))))]
(swap! oblist
#(when (or % (seq content))
(resolve-subroutines content))))))

View file

@ -1,6 +1,7 @@
(ns beowulf.interop-test
(:require [clojure.test :refer [deftest is testing]]
[beowulf.bootstrap :refer [EVAL INTEROP]]
[beowulf.bootstrap :refer [EVAL]]
[beowulf.interop :refer [INTEROP]]
[beowulf.read :refer [gsp]]))