Minor progress on Lisp functions

This commit is contained in:
Simon Brooke 2023-03-29 16:56:13 +01:00
parent 37ba03f05e
commit c3b327f760
8 changed files with 116 additions and 3 deletions

35
TEST.lsp Normal file
View file

@ -0,0 +1,35 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Beowulf Sysout file generated at 2023-03-29T12:34:39.278
;; generated by simon
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
((NULL
LAMBDA (X) (COND ((EQUAL X (QUOTE NIL)) (QUOTE T)) ((QUOTE T) (QUOTE F))))
(GCD
LAMBDA
(X Y)
(COND
((GREATERP X Y) (GCD Y X))
((EQUAL (REMAINDER Y X) 0) X) ((QUOTE T) (GCD (REMAINDER Y X) X))))
(NIL)
(T . T)
(F)
(ADD1)
(APPEND)
(APPLY)
(ATOM)
(CAR)
(CDR)
(CONS)
(DEFINE)
(DIFFERENCE)
(EQ)
(EQUAL)
(EVAL)
(FIXP)
(INTEROP)
(NUMBERP)
(OBLIST)
(PLUS)
(PRETTY)
(QUOTIENT) (REMAINDER) (RPLACA) (RPLACD) (SET) (SYSIN) (SYSOUT) (TIMES))

62
doc/mexpr.md Normal file
View file

@ -0,0 +1,62 @@
# M-Expressions
M-Expressions ('mexprs') are the grammar which John McCarthy origininally used to write Lisp, and the grammar in which many of the function definitions in the [Lisp 1.5 Programmer's Manual](https://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf) are stated. However, I have not seen anywhere a claim that Lisp 1.5 could *read* M-Expressions, and it is not clear to me whether it was even planned that it should do so.
Rather, it seems to me probably that M-Expressions were only ever a grammar intended to be written on paper, like [Backus Naur Form](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form), to describe and to reason about algorithms.
I set out to make Beowulf read M-Expressions essentially out of curiousity, to see whether it could be done. I had this idea that if it could be done, I could implement most of Lisp 1.5 simply by copying in the M-Expression definitions out of the manual.
Consequently, the Beowulf parser can parse the M-Expression grammar as stated in the manual, and generate S-Expressions from it according to the table specified on page 10 of the manual.
There are two problems with this.
## Problems with interpreting M-Expressions
### Generating idiomatic Lisp
In the M-Expression notation, a lower case character or sequence of characters represents a variable; an upper case character represents a constant. As the manual says,
> 2 . The obvious translation of letting a constant translate into itself will not work.
Since the translation of `x` is `X`, the translation of `X` must be something else to avoid
ambiguity. The solution is to quote it. Thus `X` is translated into `(QUOTE X)`.
Thus, necessarily, the translation of a constant must always be quoted. In practice, key constants in Lisp such as `T` are bound to themselves, so it is idiomatic in Lisp, certainly in the way we have learned to use it, to write, for example,
```
(SET (QUOTE NULL)
(QUOTE (LAMBDA (X)
(COND
((EQUAL X NIL) T) (T F)))))
```
However, the literal translation of
```
null[x] = [x = NIL -> T; T -> F]
```
is
```
(SET (QUOTE NULL)
(QUOTE (LAMBDA (X)
(COND
((EQUAL X (QUOTE NIL)) (QUOTE T))
((QUOTE T) (QUOTE F))))))
```
This is certainly more prolix and more awkward, but it also risks being flat wrong.
Is the value of `NIL` the atom `NIL`, or is it the empty list `()`? If the former, then the translation from the M-Expression above is correct. However, that means that recursive functions which recurse down a list seeking the end will fail. So the latter must be the case.
`NULL` is described thus (Ibid, p11):
> This is a predicate useful for deciding when a list is exhausted. It is true if and only if its argument is `NIL`.
I think there is an ambiguity in referencing constants which are not bound to themselves in the M-Expression notation as given in the manual. This is particularly problematic with regards to `NIL`, but there may be others instances.
### Curly braces
The use of curly braces is not defined in the grammar as stated on page 10. They are not used in the initial definition of `APPLY` on page 13, but they are used in the more developed restatement on page 70. I believe they are to be read as indicating a `DO` statement -- a list of function calls to be made sequentially but without strict functional dependence on one another -- but I don't find the exposition here particularly clear and I'm not sure of this.
Consequently, the M-Expression interpreter in Beowulf does not interpret curly braces.

View file

@ -1 +0,0 @@
(DEFUN COUNT (L) (COND ((EQ '() L) 0) (T (PLUS 1 (COUNT (CDR L))))))

View file

@ -1,3 +1,5 @@
gcd[x;y] = [x>y -> gcd[y;x];
rem[y;x] = 0 -> x;
T -> gcd[rem[y;x];x]]
T -> gcd[rem[y;x];x]]
;; gcd[x;y] = [x>y -> gcd[y;x]; rem[y;x] = 0 -> x; T -> gcd[rem[y;x];x]]

1
resources/length.lsp Normal file
View file

@ -0,0 +1 @@
(DEFUN LENGTH (L) (COND ((EQ NIL L) 0) (T (ADD1 (LENGTH (CDR L))))))

View file

@ -7,6 +7,7 @@
;; Binding all system functions to NIL so that you can see on the OBLIST that
;; they exist.
(ADD1 . NIL)
(AND . NIL)
(APPEND . NIL)
(APPLY . NIL)
(ATOM . NIL)

View file

@ -13,7 +13,7 @@
[clojure.tools.trace :refer [deftrace]]
[beowulf.cons-cell :refer [CAR CDR CONS LIST make-beowulf-list make-cons-cell
pretty-print T F]]
[beowulf.host :refer [ADD1 DIFFERENCE FIXP NUMBERP PLUS QUOTIENT
[beowulf.host :refer [AND ADD1 DIFFERENCE FIXP NUMBERP PLUS QUOTIENT
REMAINDER RPLACA RPLACD SUB1 TIMES]]
[beowulf.io :refer [SYSIN SYSOUT]]
[beowulf.oblist :refer [*options* oblist NIL]]
@ -404,6 +404,7 @@
(APPLY fn args environment)
(case function-symbol ;; there must be a better way of doing this!
ADD1 (apply ADD1 args)
AND (apply AND args)
APPEND (apply APPEND args)
APPLY (apply APPLY args)
ATOM (ATOM? (CAR args))
@ -417,6 +418,7 @@
;; think about EVAL. Getting the environment right is subtle
FIXP (apply FIXP args)
INTEROP (when (lax? INTEROP) (apply INTEROP args))
LIST (apply LIST args)
NUMBERP (apply NUMBERP args)
OBLIST (OBLIST)
PLUS (apply PLUS args)

View file

@ -13,6 +13,17 @@
;; those which can be implemented in Lisp should be, since that aids
;; portability.
(defn AND
"True if and only if none of my `args` evaluate to either `F` or `NIL`,
else `F`.
In `beowulf.host` principally because I don't yet feel confident to define
varargs functions in Lisp."
[& args]
(if (empty? (filter #(or (= 'F %) (empty? %)) args))
'T
'F))
(defn RPLACA
"Replace the CAR pointer of this `cell` with this `value`. Dangerous, should
really not exist, but does in Lisp 1.5 (and was important for some