From 6a8417a367db00d77b2cf83ed203a03026c6b7ef Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Sun, 26 Mar 2023 16:04:22 +0100 Subject: [PATCH] Working assignment, reader macros, mexpr interpretation. --- README.md | 7 +- docs/codox/beowulf.bootstrap.html | 24 +++-- docs/codox/beowulf.cons-cell.html | 2 +- docs/codox/beowulf.core.html | 2 +- docs/codox/beowulf.host.html | 2 +- docs/codox/beowulf.read.html | 6 +- docs/codox/beowulf.reader.generate.html | 3 + docs/codox/beowulf.reader.macros.html | 3 + docs/codox/beowulf.reader.parser.html | 3 + docs/codox/beowulf.reader.simplify.html | 3 + docs/codox/index.html | 2 +- docs/codox/intro.html | 2 +- resources/img/screenshot.png | Bin 0 -> 37768 bytes resources/img/screenshot.xcf | Bin 0 -> 185407 bytes src/beowulf/bootstrap.clj | 24 ++--- src/beowulf/host.clj | 7 +- src/beowulf/read.clj | 4 +- src/beowulf/reader/generate.clj | 87 +++++++++------- src/beowulf/reader/macros.clj | 36 +++++++ src/beowulf/reader/parser.clj | 2 +- src/beowulf/reader/simplify.clj | 130 +++++++++++++----------- test/beowulf/reader_macro_test.clj | 11 ++ test/beowulf/sexpr_test.clj | 2 +- 23 files changed, 227 insertions(+), 135 deletions(-) create mode 100644 docs/codox/beowulf.reader.generate.html create mode 100644 docs/codox/beowulf.reader.macros.html create mode 100644 docs/codox/beowulf.reader.parser.html create mode 100644 docs/codox/beowulf.reader.simplify.html create mode 100644 resources/img/screenshot.png create mode 100644 resources/img/screenshot.xcf create mode 100644 src/beowulf/reader/macros.clj create mode 100644 test/beowulf/reader_macro_test.clj diff --git a/README.md b/README.md index 9cdc5c9..c8781ef 100644 --- a/README.md +++ b/README.md @@ -89,9 +89,9 @@ Intended deviations from the behaviour of the real Lisp reader are as follows: 1. It reads the meta-expression language `MEXPR` in addition to the symbolic expression language `SEXPR`, which I do not believe the Lisp 1.5 reader ever did; -2. It treats everything between a semi-colon and an end of line as a comment, - as most modern Lisps do; but I do not believe Lisp 1.5 had this feature. - +2. It treats everything between a double semi-colon and an end of line as + a comment, as most modern Lisps do; but I do not believe Lisp 1.5 had + this feature. ### BUT WHY?!!?! @@ -116,7 +116,6 @@ implementations. I'm convinced you could still use Lisp 1.5 for interesting and useful software (which isn't to say that some modern Lisps aren't better, but this is software which is almost sixty years old). - ## Installation At present, clone the source and build it using diff --git a/docs/codox/beowulf.bootstrap.html b/docs/codox/beowulf.bootstrap.html index 4bd0a94..e2883df 100644 --- a/docs/codox/beowulf.bootstrap.html +++ b/docs/codox/beowulf.bootstrap.html @@ -1,12 +1,20 @@ -beowulf.bootstrap documentation

beowulf.bootstrap

Lisp as defined in Chapter 1 (pages 1-14) of the Lisp 1.5 Programmer's Manual; that is to say, a very simple Lisp language, which should, I believe, be sufficient in conjunction with the functions provided by beowulf.host, be sufficient to bootstrap the full Lisp 1.5 interpreter..

-

The convention is adopted that functions in this file with names in 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.

*options*

dynamic

Command line options from invocation.

APPEND

(APPEND x y)

Append the the elements of y to the elements of x.

-

All args are assumed to be beowulf.cons-cell/ConsCell objects. See page 11 of the Lisp 1.5 Programmers Manual.

APPLY

(APPLY function args environment)

For bootstrapping, at least, a version of APPLY written in Clojure. All args are assumed to be symbols or beowulf.cons-cell/ConsCell objects. See page 13 of the Lisp 1.5 Programmers Manual.

ASSOC

(ASSOC x a)

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 it is a table searching function.

-

All args are assumed to be beowulf.cons-cell/ConsCell objects. See page 12 of the Lisp 1.5 Programmers Manual.

ATOM

macro

(ATOM x)

Returns T if and only is the argument x is bound to and atom; else F. It is not clear to me from the documentation whether (ATOM 7) should return T or F. I’m going to assume T.

ATOM?

macro

(ATOM? x)

The convention of returning F from predicates, rather than NIL, is going to tie me in knots. This is a variant of ATOM which returns NIL on failure.

CAAAAR

(CAAAAR x)

TODO: write docs

CAAADR

(CAAADR x)

TODO: write docs

CAAAR

(CAAAR x)

TODO: write docs

CAADAR

(CAADAR x)

TODO: write docs

CAADDR

(CAADDR x)

TODO: write docs

CAADR

(CAADR x)

TODO: write docs

CAAR

(CAAR x)

TODO: write docs

CADAAR

(CADAAR x)

TODO: write docs

CADADR

(CADADR x)

TODO: write docs

CADAR

(CADAR x)

TODO: write docs

CADDAR

(CADDAR x)

TODO: write docs

CADDDR

(CADDDR x)

TODO: write docs

CADDR

(CADDR x)

TODO: write docs

CADR

(CADR x)

TODO: write docs

CAR

(CAR x)

Return the item indicated by the first pointer of a pair. NIL is treated specially: the CAR of NIL is NIL.

CDAAAR

(CDAAAR x)

TODO: write docs

CDAADR

(CDAADR x)

TODO: write docs

CDAAR

(CDAAR x)

TODO: write docs

CDADAR

(CDADAR x)

TODO: write docs

CDADDR

(CDADDR x)

TODO: write docs

CDADR

(CDADR x)

TODO: write docs

CDAR

(CDAR x)

TODO: write docs

CDDAAR

(CDDAAR x)

TODO: write docs

CDDADR

(CDDADR x)

TODO: write docs

CDDAR

(CDDAR x)

TODO: write docs

CDDDAR

(CDDDAR x)

TODO: write docs

CDDDDR

(CDDDDR x)

TODO: write docs

CDDDR

(CDDDR x)

TODO: write docs

CDDR

(CDDR x)

TODO: write docs

CDR

(CDR x)

Return the item indicated by the second pointer of a pair. NIL is treated specially: the CDR of NIL is NIL.

EQ

(EQ x y)

Returns T if and only if both x and y are bound to the same atom, else F.

EQUAL

(EQUAL x y)

This is a predicate that is true if its two arguments are identical S-expressions, and false if they are different. (The elementary predicate EQ is defined only for atomic arguments.) The definition of EQUAL is an example of a conditional expression inside a conditional expression.

-

NOTE: returns F on failure, not NIL

EVAL

(EVAL expr env)

For bootstrapping, at least, a version of EVAL written in Clojure. All args are assumed to be symbols or beowulf.cons-cell/ConsCell objects. See page 13 of the Lisp 1.5 Programmers Manual.

MEMBER

(MEMBER x y)

This predicate is true if the S-expression x occurs among the elements of the list y.

-

All args are assumed to be symbols or beowulf.cons-cell/ConsCell objects. See page 11 of the Lisp 1.5 Programmers Manual.

NULL

macro

(NULL x)

Returns T if and only if the argument x is bound to NIL; else F.

oblist

The default environment.

PAIRLIS

(PAIRLIS x y a)

This function gives the list of pairs of corresponding elements of the lists x and y, and APPENDs this to the list a. The resultant list of pairs, which is like a table with two columns, is called an association list.

+beowulf.bootstrap documentation

beowulf.bootstrap

Lisp as defined in Chapter 1 (pages 1-14) of the Lisp 1.5 Programmer's Manual; that is to say, a very simple Lisp language, which should, I believe, be sufficient in conjunction with the functions provided by beowulf.host, be sufficient to bootstrap the full Lisp 1.5 interpreter..

+

The convention is adopted that functions in this file with names in 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.

*options*

dynamic

Command line options from invocation.

APPEND

(APPEND x y)

Append the the elements of y to the elements of x.

+

All args are assumed to be beowulf.cons-cell/ConsCell objects. See page 11 of the Lisp 1.5 Programmers Manual.

APPLY

(APPLY function args environment)

For bootstrapping, at least, a version of APPLY written in Clojure. All args are assumed to be symbols or beowulf.cons-cell/ConsCell objects. See page 13 of the Lisp 1.5 Programmers Manual.

ASSOC

(ASSOC x a)

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 it is a table searching function.

+

All args are assumed to be beowulf.cons-cell/ConsCell objects. See page 12 of the Lisp 1.5 Programmers Manual.

ATOM

macro

(ATOM x)

Returns T if and only if the argument x is bound to an atom; else F. It is not clear to me from the documentation whether (ATOM 7) should return T or F. I’m going to assume T.

ATOM?

macro

(ATOM? x)

The convention of returning F from predicates, rather than NIL, is going to tie me in knots. This is a variant of ATOM which returns NIL on failure.

CAAAAR

macro

(CAAAAR x)

TODO: write docs

CAAADR

macro

(CAAADR x)

TODO: write docs

CAAAR

macro

(CAAAR x)

TODO: write docs

CAADAR

macro

(CAADAR x)

TODO: write docs

CAADDR

macro

(CAADDR x)

TODO: write docs

CAADR

macro

(CAADR x)

TODO: write docs

CAAR

macro

(CAAR x)

TODO: write docs

CADAAR

macro

(CADAAR x)

TODO: write docs

CADADR

macro

(CADADR x)

TODO: write docs

CADAR

macro

(CADAR x)

TODO: write docs

CADDAR

macro

(CADDAR x)

TODO: write docs

CADDDR

macro

(CADDDR x)

TODO: write docs

CADDR

macro

(CADDR x)

TODO: write docs

CADR

macro

(CADR x)

TODO: write docs

CAR

(CAR x)

Return the item indicated by the first pointer of a pair. NIL is treated specially: the CAR of NIL is NIL.

CDAAAR

macro

(CDAAAR x)

TODO: write docs

CDAADR

macro

(CDAADR x)

TODO: write docs

CDAAR

macro

(CDAAR x)

TODO: write docs

CDADAR

macro

(CDADAR x)

TODO: write docs

CDADDR

macro

(CDADDR x)

TODO: write docs

CDADR

macro

(CDADR x)

TODO: write docs

CDAR

macro

(CDAR x)

TODO: write docs

CDDAAR

macro

(CDDAAR x)

TODO: write docs

CDDADR

macro

(CDDADR x)

TODO: write docs

CDDAR

macro

(CDDAR x)

TODO: write docs

CDDDAR

macro

(CDDDAR x)

TODO: write docs

CDDDDR

macro

(CDDDDR x)

TODO: write docs

CDDDR

macro

(CDDDR x)

TODO: write docs

CDDR

macro

(CDDR x)

TODO: write docs

CDR

(CDR x)

Return the item indicated by the second pointer of a pair. NIL is treated specially: the CDR of NIL is NIL.

CONS

(CONS car cdr)

Construct a new instance of cons cell with this car and cdr.

DEFINE

(DEFINE args)

Bootstrap-only version of DEFINE which, post boostrap, can be overwritten in LISP.

+

The single argument to DEFINE should be an assoc list which should be nconc’ed onto the front of the oblist. Broadly, (SETQ OBLIST (NCONC ARG1 OBLIST))

EQ

(EQ x y)

Returns T if and only if both x and y are bound to the same atom, else NIL.

EQUAL

(EQUAL x y)

This is a predicate that is true if its two arguments are identical S-expressions, and false if they are different. (The elementary predicate EQ is defined only for atomic arguments.) The definition of EQUAL is an example of a conditional expression inside a conditional expression.

+

NOTE: returns F on failure, not NIL

EVAL

(EVAL expr)(EVAL expr env)

For bootstrapping, at least, a version of EVAL written in Clojure. All args are assumed to be symbols or beowulf.cons-cell/ConsCell objects. See page 13 of the Lisp 1.5 Programmers Manual.

eval-internal

(eval-internal expr env)

Common guts for both EVAL and traced-eval

INTEROP

(INTEROP & args__3196__auto__)

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. +
  3. a sequence (list) of symbols forming a qualified path name bound to a function.
  4. +
+

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.

interop-interpret-q-name

(interop-interpret-q-name & args__3196__auto__)

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.

MEMBER

(MEMBER x y)

This predicate is true if the S-expression x occurs among the elements of the list y.

+

All args are assumed to be symbols or beowulf.cons-cell/ConsCell objects. See page 11 of the Lisp 1.5 Programmers Manual.

NILP

macro

(NILP x)

Not part of LISP 1.5: T if o is NIL, else NIL.

NULL

macro

(NULL x)

Returns T if and only if the argument x is bound to NIL; else F.

OBLIST

(OBLIST)

Not certain whether or not this is part of LISP 1.5; adapted from PSL. return the current value of the object list. Note that in PSL this function returns a list of the symbols bound, not the whole association list.

oblist

The default environment.

PAIRLIS

(PAIRLIS x y a)

This function gives the list of pairs of corresponding elements of the lists x and y, and APPENDs this to the list a. The resultant list of pairs, which is like a table with two columns, is called an association list.

Eessentially, it builds the environment on the stack, implementing shallow binding.

-

All args are assumed to be beowulf.cons-cell/ConsCell objects. See page 12 of the Lisp 1.5 Programmers Manual.

SUBLIS

(SUBLIS a y)

Here a is assumed to be an association list of the form ((ul . vl)...(un . vn)), where the us are atomic, and y is any S-expression. What SUBLIS does, is to treat the us as variables when they occur in y, and to SUBSTitute the corresponding vs from the pair list.

+

All args are assumed to be beowulf.cons-cell/ConsCell objects. See page 12 of the Lisp 1.5 Programmers Manual.

QUOTE

macro

(QUOTE f)

Quote, but in upper case for LISP 1.5

SET

(SET symbol val)

Implementation of SET in Clojure. Add to the oblist a binding of the value of var to the value of val. NOTE WELL: this is not SETQ!

SUBLIS

(SUBLIS a y)

Here a is assumed to be an association list of the form ((ul . vl)...(un . vn)), where the us are atomic, and y is any S-expression. What SUBLIS does, is to treat the us as variables when they occur in y, and to SUBSTitute the corresponding vs from the pair list.

My interpretation is that this is variable binding in the stack frame.

-

All args are assumed to be beowulf.cons-cell/ConsCell objects. See page 12 of the Lisp 1.5 Programmers Manual.

SUBST

(SUBST x y z)

This function gives the result of substituting the S-expression x for all occurrences of the atomic symbol y in the S-expression z.

traced-eval

(traced-eval & args__2885__auto__)

Essentially, identical to EVAL except traced.

uaf

(uaf l path)

Universal access function; l is expected to be an arbitrary 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

\ No newline at end of file +

All args are assumed to be beowulf.cons-cell/ConsCell objects. See page 12 of the Lisp 1.5 Programmers Manual.

SUBST

(SUBST x y z)

This function gives the result of substituting the S-expression x for all occurrences of the atomic symbol y in the S-expression z.

to-beowulf

(to-beowulf o)

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.

to-clojure

(to-clojure l)

If l is a beowulf.cons_cell.ConsCell, return a Clojure list having the same members in the same order.

traced-eval

(traced-eval & args__3196__auto__)

Essentially, identical to EVAL except traced.

uaf

(uaf l path)

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

\ No newline at end of file diff --git a/docs/codox/beowulf.cons-cell.html b/docs/codox/beowulf.cons-cell.html index 5db7b53..df217e8 100644 --- a/docs/codox/beowulf.cons-cell.html +++ b/docs/codox/beowulf.cons-cell.html @@ -1,3 +1,3 @@ -beowulf.cons-cell documentation

beowulf.cons-cell

The fundamental cons cell on which all Lisp structures are built. Lisp 1.5 lists do not necessarily have a sequence as their CDR, so cannot be implemented on top of Clojure lists.

F

The canonical false value - different from NIL, which is not canonically false in Lisp 1.5.

make-beowulf-list

(make-beowulf-list x)

Construct a linked list of cons cells with the same content as the sequence x.

make-cons-cell

macro

(make-cons-cell car cdr)

Construct a new instance of cons cell with this car and cdr.

NIL

The canonical empty list symbol.

pretty-print

(pretty-print cell)(pretty-print cell width level)

This isn’t the world’s best pretty printer but it sort of works.

T

The canonical true value.

\ No newline at end of file +beowulf.cons-cell documentation

beowulf.cons-cell

The fundamental cons cell on which all Lisp structures are built. Lisp 1.5 lists do not necessarily have a sequence as their CDR, and must have both CAR and CDR mutable, so cannot be implemented on top of Clojure lists.

cons-cell?

(cons-cell? o)

Is this object o a beowulf cons-cell?

F

The canonical false value - different from NIL, which is not canonically false in Lisp 1.5.

make-beowulf-list

(make-beowulf-list x)

Construct a linked list of cons cells with the same content as the sequence x.

make-cons-cell

(make-cons-cell car cdr)

Construct a new instance of cons cell with this car and cdr.

MutableSequence

protocol

Like a sequence, but mutable.

members

getCar

(getCar this)

Return the first element of this sequence.

getCdr

(getCdr this)

like more, q.v., but returns List NIL not Clojure nil when empty.

getUid

(getUid this)

Returns a unique identifier for this object

rplaca

(rplaca this value)

replace the first element of this sequence with this value

rplacd

(rplacd this value)

replace the rest (but-first; cdr) of this sequence with this value

NIL

The canonical empty list symbol.

pretty-print

(pretty-print cell)(pretty-print cell width level)

This isn’t the world’s best pretty printer but it sort of works.

T

The canonical true value.

\ No newline at end of file diff --git a/docs/codox/beowulf.core.html b/docs/codox/beowulf.core.html index f5e11fe..26270d3 100644 --- a/docs/codox/beowulf.core.html +++ b/docs/codox/beowulf.core.html @@ -1,3 +1,3 @@ -beowulf.core documentation

beowulf.core

Essentially, the -main function and the bootstrap read-eval-print loop.

-main

(-main & opts)

Parse options, print the banner, read the init file if any, and enter the read/eval/print loop.

cli-options

TODO: write docs

repl

(repl prompt)

Read/eval/print loop.

\ No newline at end of file +beowulf.core documentation

beowulf.core

Essentially, the -main function and the bootstrap read-eval-print loop.

-main

(-main & opts)

Parse options, print the banner, read the init file if any, and enter the read/eval/print loop.

cli-options

TODO: write docs

repl

(repl prompt)

Read/eval/print loop.

stop-word

TODO: write docs

\ No newline at end of file diff --git a/docs/codox/beowulf.host.html b/docs/codox/beowulf.host.html index 946857b..ee61b9b 100644 --- a/docs/codox/beowulf.host.html +++ b/docs/codox/beowulf.host.html @@ -1,3 +1,3 @@ -beowulf.host documentation

beowulf.host

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 host language, in this case Clojure.

\ No newline at end of file +beowulf.host documentation

beowulf.host

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 host language, in this case Clojure.

ADD1

(ADD1 x)

TODO: write docs

DIFFERENCE

(DIFFERENCE x y)

TODO: write docs

FIXP

(FIXP x)

TODO: write docs

LIST

(LIST & args)

TODO: write docs

NUMBERP

(NUMBERP x)

TODO: write docs

PLUS2

(PLUS2 x y)

Lisp 1.5 PLUS is varargs, and implementing varargs functions in Clojure is not an added complexity I want. So this is a two arg PLUS, on which a varargs PLUS can be built in the Lisp 1.5 layer using REDUCE.

QUOTIENT

(QUOTIENT x y)

I’m not certain from the documentation whether Lisp 1.5 QUOTIENT returned the integer part of the quotient, or a realnum representing the whole quotient. I am for now implementing the latter.

REMAINDER

(REMAINDER x y)

TODO: write docs

RPLACA

(RPLACA cell value)

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 performance hacks in early Lisps)

RPLACD

(RPLACD cell value)

Replace the CDR pointer of this cell with this value. Dangerous, should really not exist, but does in Lisp 1.5 (and was important for some performance hacks in early Lisps)

SUB1

(SUB1 x)

TODO: write docs

TIMES2

(TIMES2 x y)

TODO: write docs

\ No newline at end of file diff --git a/docs/codox/beowulf.read.html b/docs/codox/beowulf.read.html index 3deec4d..e5d4d8b 100644 --- a/docs/codox/beowulf.read.html +++ b/docs/codox/beowulf.read.html @@ -1,9 +1,9 @@ -beowulf.read documentation

beowulf.read

This provides the reader required for boostrapping. It’s not a bad reader - it provides feedback on errors found in the input - but it isn’t the real Lisp reader.

+beowulf.read documentation

beowulf.read

This provides the reader required for boostrapping. It’s not a bad reader - it provides feedback on errors found in the input - but it isn’t the real Lisp reader.

Intended deviations from the behaviour of the real Lisp reader are as follows:

    -
  1. It reads the meta-expression language MEXPR in addition to the symbolic expression language SEXPR, which I do not believe the Lisp 1.5 reader ever did;
  2. +
  3. It reads the meta-expression language MEXPR in addition to fLAMthe symbolic expression language SEXPR, which I do not believe the Lisp 1.5 reader ever did;
  4. It treats everything between a semi-colon and an end of line as a comment, as most modern Lisps do; but I do not believe Lisp 1.5 had this feature.
-

Both these extensions can be disabled by using the --strict command line switch.

gen-cond

(gen-cond p)

Generate a cond statement from this simplified parse tree fragment p; returns nil if p does not represent a (MEXPR) cond statement.

gen-cond-clause

(gen-cond-clause p)

Generate a cond clause from this simplified parse tree fragment p; returns nil if p does not represent a cond clause.

gen-dot-terminated-list

(gen-dot-terminated-list p)

Generate a list, which may be dot-terminated, from this partial parse tree ‘p’. Note that the function acts recursively and progressively decapitates its argument, so that the argument will not always be a valid parse tree.

gen-fn-call

(gen-fn-call p)

Generate a function call from this simplified parse tree fragment p; returns nil if p does not represent a (MEXPR) function call.

generate

(generate p)

Generate lisp structure from this parse tree p. It is assumed that p has been simplified.

gsp

macro

(gsp s)

Shortcut macro - the internals of read; or, if you like, read-string. Argument s should be a string representation of a valid Lisp expression.

parse

Parse a string presented as argument into a parse tree which can then be operated upon further.

READ

(READ input)

An implementation of a Lisp reader sufficient for bootstrapping; not necessarily the final Lisp reader.

simplify

(simplify p)(simplify p context)

Simplify this parse tree p. If p is an instaparse failure object, throw an ex-info, with p as the value of its :failure key.

strip-leading-zeros

(strip-leading-zeros s)(strip-leading-zeros s prefix)

read-string interprets strings with leading zeros as octal; strip any from this string s. If what’s left is empty (i.e. there were only zeros, return "0".

\ No newline at end of file +

Both these extensions can be disabled by using the --strict command line switch.

gsp

(gsp s)

Shortcut macro - the internals of read; or, if you like, read-string. Argument s should be a string representation of a valid Lisp expression.

number-lines

(number-lines s)(number-lines s e)

TODO: write docs

READ

(READ)(READ input)

An implementation of a Lisp reader sufficient for bootstrapping; not necessarily the final Lisp reader. input should be either a string representation of a LISP expression, or else an input stream. A single form will be read.

read-from-console

(read-from-console)

Attempt to read a complete lisp expression from the console.

strip-line-comments

(strip-line-comments s)

Strip blank lines and comment lines from this string s, expected to be Lisp source.

\ No newline at end of file diff --git a/docs/codox/beowulf.reader.generate.html b/docs/codox/beowulf.reader.generate.html new file mode 100644 index 0000000..3788098 --- /dev/null +++ b/docs/codox/beowulf.reader.generate.html @@ -0,0 +1,3 @@ + +beowulf.reader.generate documentation

beowulf.reader.generate

TODO: write docs

gen-cond

(gen-cond p)

Generate a cond statement from this simplified parse tree fragment p; returns nil if p does not represent a (MEXPR) cond statement.

gen-cond-clause

(gen-cond-clause p)

Generate a cond clause from this simplified parse tree fragment p; returns nil if p does not represent a cond clause.

gen-dot-terminated-list

(gen-dot-terminated-list p)

Generate a list, which may be dot-terminated, from this partial parse tree ‘p’. Note that the function acts recursively and progressively decapitates its argument, so that the argument will not always be a valid parse tree.

gen-fn-call

(gen-fn-call p)

Generate a function call from this simplified parse tree fragment p; returns nil if p does not represent a (MEXPR) function call.

gen-iexpr

(gen-iexpr tree)

TODO: write docs

generate

(generate p)

Generate lisp structure from this parse tree p. It is assumed that p has been simplified.

generate-assign

(generate-assign tree)

Generate an assignment statement based on this tree. If the thing being assigned to is a function signature, then we have to do something different to if it’s an atom.

generate-defn

(generate-defn tree)

TODO: write docs

generate-set

(generate-set tree)

Actually not sure what the mexpr representation of set looks like

strip-leading-zeros

(strip-leading-zeros s)(strip-leading-zeros s prefix)

read-string interprets strings with leading zeros as octal; strip any from this string s. If what’s left is empty (i.e. there were only zeros, return "0".

\ No newline at end of file diff --git a/docs/codox/beowulf.reader.macros.html b/docs/codox/beowulf.reader.macros.html new file mode 100644 index 0000000..5534744 --- /dev/null +++ b/docs/codox/beowulf.reader.macros.html @@ -0,0 +1,3 @@ + +beowulf.reader.macros documentation

beowulf.reader.macros

Can I implement reader macros? let’s see!

*readmacros*

dynamic

TODO: write docs

expand-macros

(expand-macros form)

TODO: write docs

\ No newline at end of file diff --git a/docs/codox/beowulf.reader.parser.html b/docs/codox/beowulf.reader.parser.html new file mode 100644 index 0000000..43b0526 --- /dev/null +++ b/docs/codox/beowulf.reader.parser.html @@ -0,0 +1,3 @@ + +beowulf.reader.parser documentation

beowulf.reader.parser

The actual parser, supporting both S-expression and M-expression syntax.

parse

Parse a string presented as argument into a parse tree which can then be operated upon further.

\ No newline at end of file diff --git a/docs/codox/beowulf.reader.simplify.html b/docs/codox/beowulf.reader.simplify.html new file mode 100644 index 0000000..11b5d77 --- /dev/null +++ b/docs/codox/beowulf.reader.simplify.html @@ -0,0 +1,3 @@ + +beowulf.reader.simplify documentation

beowulf.reader.simplify

Simplify parse trees. Be aware that this is very tightly coupled with the parser.

remove-nesting

(remove-nesting tree context)

TODO: write docs

remove-optional-space

(remove-optional-space tree)

TODO: write docs

simplify

(simplify p)(simplify p context)

Simplify this parse tree p. If p is an instaparse failure object, throw an ex-info, with p as the value of its :failure key.

\ No newline at end of file diff --git a/docs/codox/index.html b/docs/codox/index.html index 7dfb2d3..d77bafa 100644 --- a/docs/codox/index.html +++ b/docs/codox/index.html @@ -1,3 +1,3 @@ -Beowulf 0.2.0

Beowulf 0.2.0

Released under the GPL-2.0-or-later

An implementation of LISP 1.5 in Clojure.

Installation

To install, add the following dependency to your project or build file:

[beowulf "0.2.0"]

Topics

Namespaces

beowulf.bootstrap

Lisp as defined in Chapter 1 (pages 1-14) of the Lisp 1.5 Programmer's Manual; that is to say, a very simple Lisp language, which should, I believe, be sufficient in conjunction with the functions provided by beowulf.host, be sufficient to bootstrap the full Lisp 1.5 interpreter..

beowulf.cons-cell

The fundamental cons cell on which all Lisp structures are built. Lisp 1.5 lists do not necessarily have a sequence as their CDR, so cannot be implemented on top of Clojure lists.

Public variables and functions:

beowulf.core

Essentially, the -main function and the bootstrap read-eval-print loop.

Public variables and functions:

beowulf.host

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 host language, in this case Clojure.

Public variables and functions:

    beowulf.read

    This provides the reader required for boostrapping. It’s not a bad reader - it provides feedback on errors found in the input - but it isn’t the real Lisp reader.

    \ No newline at end of file +Beowulf 0.2.1-SNAPSHOT

    Beowulf 0.2.1-SNAPSHOT

    Released under the GPL-2.0-or-later

    An implementation of LISP 1.5 in Clojure.

    Installation

    To install, add the following dependency to your project or build file:

    [beowulf "0.2.1-SNAPSHOT"]

    Topics

    Namespaces

    beowulf.bootstrap

    Lisp as defined in Chapter 1 (pages 1-14) of the Lisp 1.5 Programmer's Manual; that is to say, a very simple Lisp language, which should, I believe, be sufficient in conjunction with the functions provided by beowulf.host, be sufficient to bootstrap the full Lisp 1.5 interpreter..

    beowulf.cons-cell

    The fundamental cons cell on which all Lisp structures are built. Lisp 1.5 lists do not necessarily have a sequence as their CDR, and must have both CAR and CDR mutable, so cannot be implemented on top of Clojure lists.

    beowulf.core

    Essentially, the -main function and the bootstrap read-eval-print loop.

    Public variables and functions:

    beowulf.host

    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 host language, in this case Clojure.

    beowulf.read

    This provides the reader required for boostrapping. It’s not a bad reader - it provides feedback on errors found in the input - but it isn’t the real Lisp reader.

    Public variables and functions:

    beowulf.reader.macros

    Can I implement reader macros? let’s see!

    Public variables and functions:

    beowulf.reader.parser

    The actual parser, supporting both S-expression and M-expression syntax.

    Public variables and functions:

    beowulf.reader.simplify

    Simplify parse trees. Be aware that this is very tightly coupled with the parser.

    Public variables and functions:

    \ No newline at end of file diff --git a/docs/codox/intro.html b/docs/codox/intro.html index 17bdf0c..0598d94 100644 --- a/docs/codox/intro.html +++ b/docs/codox/intro.html @@ -1,4 +1,4 @@ -Introduction to beowulf

    Introduction to beowulf

    +Introduction to beowulf

    Introduction to beowulf

    TODO: write great documentation

    \ No newline at end of file diff --git a/resources/img/screenshot.png b/resources/img/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..d8e5fb46555444e59f3af803fc94408a219e1342 GIT binary patch literal 37768 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 a;^|4(@NaxM6hs65$Vw`{Dtr0P|Nj8ZeYZOR literal 0 HcmV?d00001 diff --git a/resources/img/screenshot.xcf b/resources/img/screenshot.xcf new file mode 100644 index 0000000000000000000000000000000000000000..18488bd71cb9ff6645eceeb3ae75a78085806821 GIT binary patch literal 185407 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 tree rest rest rest)))))))) +(defn gen-iexpr + [tree] + (let [bundle (reduce #(assoc %1 (first %2) (nth %2 1)) + {} + (rest tree))] + (list (generate (:iop bundle)) + (generate (:lhs bundle)) + (generate (:rhs bundle))))) + (defn generate-set "Actually not sure what the mexpr representation of set looks like" [tree] @@ -152,46 +162,51 @@ `p` has been simplified." [p] (try - (if - (coll? p) - (case (first p) - :λ "LAMBDA" - :λexpr (make-cons-cell - (generate (nth p 1)) - (make-cons-cell (generate (nth p 2)) - (generate (nth p 3)))) - :args (make-beowulf-list (map generate (rest p))) - :atom (symbol (second p)) - :bindings (generate (second p)) - :body (make-beowulf-list (map generate (rest p))) - :cond (gen-cond p) - :cond-clause (gen-cond-clause p) - (:decimal :integer) (read-string (strip-leading-zeros (second p))) - :defn (generate-assign p) - :dotted-pair (make-cons-cell - (generate (nth p 1)) - (generate (nth p 2))) - :exponent (generate (second p)) - :fncall (gen-fn-call p) - :list (gen-dot-terminated-list (rest p)) - :mvar (symbol (upper-case (second p))) - :octal (let [n (read-string (strip-leading-zeros (second p) "0")) - scale (generate (nth p 2))] - (* n (expt 8 scale))) + (expand-macros + (if + (coll? p) + (case (first p) + ">" 'GREATERP + :λ "LAMBDA" + :λexpr (make-cons-cell + (generate (nth p 1)) + (make-cons-cell (generate (nth p 2)) + (generate (nth p 3)))) + :args (make-beowulf-list (map generate (rest p))) + :atom (symbol (second p)) + :bindings (generate (second p)) + :body (make-beowulf-list (map generate (rest p))) + :cond (gen-cond p) + :cond-clause (gen-cond-clause p) + (:decimal :integer) (read-string (strip-leading-zeros (second p))) + :defn (generate-assign p) + :dotted-pair (make-cons-cell + (generate (nth p 1)) + (generate (nth p 2))) + :exponent (generate (second p)) + :fncall (gen-fn-call p) + :iexpr (gen-iexpr p) + :list (gen-dot-terminated-list (rest p)) + (:lhs :rhs) (generate (second p)) + :mexpr (generate (second p)) + :mvar (symbol (upper-case (second p))) + :octal (let [n (read-string (strip-leading-zeros (second p) "0")) + scale (generate (nth p 2))] + (* n (expt 8 scale))) ;; the quote read macro (which probably didn't exist in Lisp 1.5, but...) - :quoted-expr (make-beowulf-list (list 'QUOTE (generate (second p)))) - :scale-factor (if - (empty? (second p)) 0 - (read-string (strip-leading-zeros (second p)))) - :scientific (let [n (generate (second p)) - exponent (generate (nth p 2))] - (* n (expt 10 exponent))) + :quoted-expr (make-beowulf-list (list 'QUOTE (generate (second p)))) + :scale-factor (if + (empty? (second p)) 0 + (read-string (strip-leading-zeros (second p)))) + :scientific (let [n (generate (second p)) + exponent (generate (nth p 2))] + (* n (expt 10 exponent))) ;; default - (throw (ex-info (str "Unrecognised head: " (first p)) - {:generating p}))) - p) + (throw (ex-info (str "Unrecognised head: " (first p)) + {:generating p}))) + p)) (catch Throwable any (throw (ex-info "Could not generate" {:generating p} diff --git a/src/beowulf/reader/macros.clj b/src/beowulf/reader/macros.clj new file mode 100644 index 0000000..2fc4214 --- /dev/null +++ b/src/beowulf/reader/macros.clj @@ -0,0 +1,36 @@ +(ns beowulf.reader.macros + "Can I implement reader macros? let's see!" + (:require [beowulf.bootstrap :refer [CADR CADDR CDDR CONS]] + [beowulf.cons-cell :refer [make-beowulf-list]] + [beowulf.host :refer [LIST]] + [clojure.string :refer [join]]) + (:import [beowulf.cons_cell ConsCell])) + +;; We don't need (at least, in the Clojure reader) to rewrite forms like +;; "'FOO", because that's handled by the parser. But we do need to rewrite +;; things which don't evaluate their arguments, like `SETQ`, because (unless +;; LABEL does it, which I'm not yet sure of) we're not yet able to implement +;; things which don't evaluate arguments. + +(def ^:dynamic *readmacros* + {:car {'DEFUN (fn [f] + (LIST 'SET (LIST 'QUOTE (CADR f)) + (CONS 'LAMBDA (CDDR f)))) + 'SETQ (fn [f] (LIST 'SET (LIST 'QUOTE (CADR f)) (CADDR f)))}}) + +(defn expand-macros + [form] + (try + (if-let [car (when (and (coll? form) (symbol? (first form))) + (first form))] + (if-let [macro (-> *readmacros* :car car)] + (make-beowulf-list (apply macro (list form))) + form) + form) + (catch Exception any + (println (join "\n" + ["# ERROR while expanding macro:" + (str "# Form: " form) + (str "# Error class: " (.getName (.getClass any))) + (str "# Message: " (.getMessage any))])) + form))) \ No newline at end of file diff --git a/src/beowulf/reader/parser.clj b/src/beowulf/reader/parser.clj index 91acde0..f248a3b 100644 --- a/src/beowulf/reader/parser.clj +++ b/src/beowulf/reader/parser.clj @@ -46,7 +46,7 @@ ;; Infix operators appear in mexprs, e.g. on page 7. Ooops! ;; I do not know what infix operators are considered legal. "iexpr := iexp iop iexp; - iexp := mexpr | mvar | number | mexpr | opt-space iexp opt-space; + iexp := mexpr | number | opt-space iexp opt-space; iop := '>' | '<' | '+' | '-' | '/' | '=' ;" ;; comments. I'm pretty confident Lisp 1.5 did NOT have these. diff --git a/src/beowulf/reader/simplify.clj b/src/beowulf/reader/simplify.clj index 48ed5d0..1cf525a 100644 --- a/src/beowulf/reader/simplify.clj +++ b/src/beowulf/reader/simplify.clj @@ -2,8 +2,9 @@ "Simplify parse trees. Be aware that this is very tightly coupled with the parser." (:require [beowulf.bootstrap :refer [*options*]] + [clojure.tools.trace :refer [deftrace]] [instaparse.failure :as f]) - (:import [instaparse.gll Failure])) + (:import [instaparse.gll Failure])) (declare simplify) @@ -12,83 +13,90 @@ (if (vector? tree) (if (= :opt-space (first tree)) nil - (remove nil? - (map remove-optional-space tree))) + (let [v (remove nil? + (map remove-optional-space tree))] + (if (seq v) + (apply vector v) + v))) tree)) (defn remove-nesting - [tree] + [tree context] (let [tree' (remove-optional-space tree)] - (if-let [key (when (and (vector? tree') (keyword? (first tree'))) (first tree'))] + (if-let [key (when (and (vector? tree') + (keyword? (first tree'))) + (first tree'))] (loop [r tree'] (if (and r (vector? r) (keyword? (first r))) (if (= (first r) key) - (recur (simplify (second r) :foo)) + (recur (simplify (second r) context)) r) r)) tree'))) - (defn simplify "Simplify this parse tree `p`. If `p` is an instaparse failure object, throw an `ex-info`, with `p` as the value of its `:failure` key." ([p] (if (instance? Failure p) - (throw (ex-info (str "Ic ne behæfd: " (f/pprint-failure p)) {:cause :parse-failure - :phase :simplify - :failure p})) + (throw (ex-info + (str "Ic ne behæfd: " (f/pprint-failure p)) + {:cause :parse-failure + :phase :simplify + :failure p})) (simplify p :expr))) ([p context] - (if - (coll? p) - (apply - vector - (remove - #(when (coll? %) (empty? %)) - (case (first p) - (:λexpr - :args :bindings :body :cond :cond-clause :defn :dot-terminal - :fncall :lhs :octal :quoted-expr :rhs :scientific) (map #(simplify % context) p) - (:arg :expr :coefficient :fn-name :number) (simplify (second p) context) - (:arrow :dot :e :lpar :lsqb :opt-comment :opt-space :q :quote :rpar :rsqb - :semi-colon :sep :space) nil - :atom (if - (= context :mexpr) - [:quoted-expr p] - p) - :comment (when - (:strict *options*) - (throw - (ex-info "Cannot parse comments in strict mode" - {:cause :strict}))) - :dotted-pair (if - (= context :mexpr) - [:fncall - [:mvar "cons"] - [:args - (simplify (nth p 1) context) - (simplify (nth p 2) context)]] - (map simplify p)) - :iexp (second (remove-nesting p)) - :iexpr [:iexpr - [:lhs (simplify (second p) context)] - (simplify (nth p 2) context) ;; really should be the operator - [:rhs (simplify (nth p 3) context)]] - :mexpr (if - (:strict *options*) - (throw - (ex-info "Cannot parse meta expressions in strict mode" - {:cause :strict})) - (simplify (second p) :mexpr)) - :list (if - (= context :mexpr) - [:fncall - [:mvar "list"] - [:args (apply vector (map simplify (rest p)))]] - (map #(simplify % context) p)) - :raw (first (remove empty? (map simplify (rest p)))) - :sexpr (simplify (second p) :sexpr) + (cond + (string? p) p + (coll? p) (apply + vector + (remove + #(when (coll? %) (empty? %)) + (case (first p) + (:λexpr + :args :bindings :body :cond :cond-clause :defn :dot-terminal + :fncall :lhs :octal :quoted-expr :rhs :scientific) (map #(simplify % context) p) + (:arg :expr :coefficient :fn-name :number) (simplify (second p) context) + (:arrow :dot :e :lpar :lsqb :opt-comment :opt-space :q :quote :rpar :rsqb + :semi-colon :sep :space) nil + :atom (if + (= context :mexpr) + [:quoted-expr p] + p) + :comment (when + (:strict *options*) + (throw + (ex-info "Cannot parse comments in strict mode" + {:cause :strict}))) + :decimal p + :dotted-pair (if + (= context :mexpr) + [:fncall + [:mvar "cons"] + [:args + (simplify (nth p 1) context) + (simplify (nth p 2) context)]] + (map #(simplify % context) p)) + :iexp (simplify (second p) context) + :iexpr [:iexpr + [:lhs (simplify (second p) context)] + (simplify (nth p 2) context) ;; really should be the operator + [:rhs (simplify (nth p 3) context)]] + :mexpr (if + (:strict *options*) + (throw + (ex-info "Cannot parse meta expressions in strict mode" + {:cause :strict})) + (simplify (second p) :mexpr)) + :list (if + (= context :mexpr) + [:fncall + [:mvar "list"] + [:args (apply vector (map simplify (rest p)))]] + (map #(simplify % context) p)) + :raw (first (remove empty? (map simplify (rest p)))) + :sexpr (simplify (second p) :sexpr) ;;default - p))) - p))) + p))) + :else p))) diff --git a/test/beowulf/reader_macro_test.clj b/test/beowulf/reader_macro_test.clj new file mode 100644 index 0000000..228a6a9 --- /dev/null +++ b/test/beowulf/reader_macro_test.clj @@ -0,0 +1,11 @@ +(ns beowulf.reader-macro-test + (:require [clojure.test :refer [deftest is testing]] + [beowulf.read :refer [gsp]] + [beowulf.reader.macros :refer [expand-macros]])) + +(deftest macro-expansion + (testing "Expanding DEFUN" + (let [expected "(SET (QUOTE FACT) (LAMBDA (X) (COND ((ZEROP X) 1) (T (TIMES X (FACT (SUB1 X)))))))" + source "(DEFUN FACT (X) (COND ((ZEROP X) 1) (T (TIMES X (FACT (SUB1 X))))))" + actual (print-str (gsp source))] + (is (= actual expected))))) \ No newline at end of file diff --git a/test/beowulf/sexpr_test.clj b/test/beowulf/sexpr_test.clj index e8e0892..a175eb5 100644 --- a/test/beowulf/sexpr_test.clj +++ b/test/beowulf/sexpr_test.clj @@ -114,6 +114,6 @@ (deftest list-tests (testing "Reading arbitrarily structured lists" - (let [expected "(DEFUN FACT (X) (COND ((ZEROP X) 1) (T (TIMES X (FACT (SUB1 X))))))" + (let [expected "(SET (QUOTE FACT) (LAMBDA (X) (COND ((ZEROP X) 1) (T (TIMES X (FACT (SUB1 X)))))))" actual (print-str (gsp expected))] (is (= actual expected)))))