Added command-line flags, some of which actually work.

This commit is contained in:
Simon Brooke 2019-08-19 16:52:36 +01:00
parent 692eefeece
commit 75bf19e38e
8 changed files with 174 additions and 40 deletions

View file

@ -7,10 +7,62 @@ LISP 1.5 is to all Lisp dialects as Beowulf is to Emglish literature.
A work-in-progress towards an implementation of Lisp 1.5 in Clojure. The A work-in-progress towards an implementation of Lisp 1.5 in Clojure. The
objective is to build a complete and accurate implementation of Lisp 1.5 objective is to build a complete and accurate implementation of Lisp 1.5
as described in the manual, with, in so far as is possible, exactly the as described in the manual, with, in so far as is possible, exactly the
same bahaviour; the only intended deviation is that the parser reads same bahaviour - except as documented below.
'mexprs' (meta language expressions) as well as sexprs.
## BUT WHY?!!?! ### Status
Boots to REPL, but few functions yet available.
### Architectural plan
Not everything documented in this section is yet built. It indicates the
direction of travel and intended destination, not the current state.
#### resources/lisp1.5.lsp
The objective is to have within `resources/lisp1.5.lsp`, all those functions
defined in the Lisp 1.5 Programmer's Manual which can be implemented in Lisp.
This means that, while Beowulf is hosted on Clojure, all that would be
required to rehost Lisp 1.5 on a different platform would be to reimplement
* bootstrap.clj
* host.clj
* read.clj
#### beowulf/boostrap.clj
This file is essentially 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.
In addition it contains the function `INTEROP`, which allows host language
functions to be called from Lisp.
#### beowulf/host.clj
This file 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.clj
This file 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. 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.
### BUT WHY?!!?!
Because. Because.
@ -25,7 +77,11 @@ Because I'm barking mad, and this is therapy.
## Installation ## Installation
Download from http://example.com/FIXME. At present, clone the source and build it using
`lein uberjar`.
You will require to have [Leiningen](https://leiningen.org/) installed.
## Usage ## Usage

View file

@ -1,10 +1,11 @@
(defproject beowulf "0.1.0-SNAPSHOT" (defproject beowulf "0.2.0-SNAPSHOT"
:description "An implementation of LISP 1.5 in Clojure" :description "An implementation of LISP 1.5 in Clojure"
:url "http://example.com/FIXME" :url "http://example.com/FIXME"
:license {:name "GPL-2.0-or-later" :license {:name "GPL-2.0-or-later"
:url "https://www.eclipse.org/legal/epl-2.0/"} :url "https://www.eclipse.org/legal/epl-2.0/"}
:dependencies [[org.clojure/clojure "1.10.0"] :dependencies [[org.clojure/clojure "1.8.0"]
[org.clojure/math.numeric-tower "0.0.4"] [org.clojure/math.numeric-tower "0.0.4"]
[org.clojure/tools.cli "0.4.2"]
[org.clojure/tools.trace "0.7.10"] [org.clojure/tools.trace "0.7.10"]
[environ "1.1.0"] [environ "1.1.0"]
[instaparse "1.4.10"]] [instaparse "1.4.10"]]

0
resources/lisp1.5.lsp Normal file
View file

View file

@ -1,7 +1,17 @@
(ns beowulf.eval (ns beowulf.bootstrap
(:require [clojure.tools.trace :refer :all] (:require [clojure.tools.trace :refer :all]
[beowulf.cons-cell :refer [make-beowulf-list make-cons-cell NIL T F]])) [beowulf.cons-cell :refer [make-beowulf-list make-cons-cell NIL T F]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; This file is essentially 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.
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare EVAL) (declare EVAL)
(def oblist (def oblist
@ -9,6 +19,10 @@
be Lisp 1.5's EQuivalent of `SETQ`), possibly by other things." be Lisp 1.5's EQuivalent of `SETQ`), possibly by other things."
(atom NIL)) (atom NIL))
(def ^:dynamic *trace?*
"Whether or not to trace `EVAL`."
false)
(defn NULL (defn NULL
[x] [x]
(if (= x NIL) 'T 'F)) (if (= x NIL) 'T 'F))
@ -208,7 +222,7 @@
:else :else
(make-cons-cell (SUBLIS a (CAR y)) (SUBLIS a (CDR y))))) (make-cons-cell (SUBLIS a (CAR y)) (SUBLIS a (CDR y)))))
(deftrace APPLY (defn APPLY
"For bootstrapping, at least, a version of APPLY written in Clojure. "For bootstrapping, at least, a version of APPLY written in Clojure.
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."
@ -217,6 +231,8 @@
(= (=
(ATOM? function) (ATOM? function)
'T)(cond 'T)(cond
;; TODO: doesn't check whether `function` is bound in the environment;
;; we'll need that before we can bootstrap.
(= function 'CAR) (CAAR args) (= function 'CAR) (CAAR args)
(= function 'CDR) (CDAR args) (= function 'CDR) (CDAR args)
(= function 'CONS) (make-cons-cell (CAR args) (CADR args)) (= function 'CONS) (make-cons-cell (CAR args) (CADR args))
@ -261,11 +277,8 @@
(EVAL (CAR args) env) (EVAL (CAR args) env)
(EVLIS (CDR args) env)))) (EVLIS (CDR args) env))))
(deftrace traced-eval
(deftrace EVAL "Essentially, identical to EVAL except traced."
"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."
[expr env] [expr env]
(cond (cond
(= (=
@ -285,3 +298,29 @@
(EVLIS (CDR expr) env) (EVLIS (CDR expr) env)
env))) env)))
(defn EVAL
"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."
[expr env]
(cond
*trace?* (traced-eval expr env)
(=
(ATOM? expr) 'T)
(CDR (ASSOC expr env))
(=
(ATOM? (CAR expr))
'T)(cond
(= (CAR expr) 'QUOTE) (CADR expr)
(= (CAR expr) 'COND) (EVCON (CDR expr) env)
:else (APPLY
(CAR expr)
(EVLIS (CDR expr) env)
env))
:else (APPLY
(CAR expr)
(EVLIS (CDR expr) env)
env)))

View file

@ -1,21 +1,35 @@
(ns beowulf.core (ns beowulf.core
(:require [beowulf.eval :refer [EVAL oblist]] (:require [beowulf.bootstrap :refer [EVAL oblist *trace?*]]
[beowulf.read :refer [READ]] [beowulf.read :refer [READ]]
[clojure.java.io :as io]
[clojure.pprint :refer [pprint]] [clojure.pprint :refer [pprint]]
[clojure.tools.cli :refer [parse-opts]]
[environ.core :refer [env]]) [environ.core :refer [env]])
(:gen-class)) (:gen-class))
(def cli-options
[["-h" "--help"]
["-p PROMPT" "--prompt PROMPT" "Set the REPL prompt to PROMPT"
:default "Sprecan::"]
["-r INITFILE" "--read INITFILE" "Read Lisp functions from the file INITFILE"
:validate [#(and
(.exists (io/file %))
(.canRead (io/file %)))
"Could not find initfile"]]
["-s" "--strict" "Strictly interpret the Lisp 1.5 language, without extensions."]
["-t" "--trace" "Trace Lisp evaluation."]])
(defn repl (defn repl
"Read/eval/print loop." "Read/eval/print loop."
[] [prompt]
(loop [] (loop []
(print "Sprecan:: ") (print prompt)
(flush) (flush)
(try (try
(let [input (read-line)] (let [input (read-line)]
(cond (cond
(= input "quit") (throw (ex-info "Færwell!" {:cause :quit})) (= input "quit") (throw (ex-info "\nFærwell!" {:cause :quit}))
input (println (str "> " (EVAL (READ input) @oblist))) input (println (str "> " (print-str (EVAL (READ input) @oblist))))
:else (println))) :else (println)))
(catch (catch
Exception Exception
@ -32,14 +46,23 @@
(recur))) (recur)))
(defn -main (defn -main
[& args] [& opts]
(let [args (parse-opts opts cli-options)]
(println (println
(str (str
"Hider wilcuman. Béowulf is mín nama\nSíðe " "\nHider wilcuman. Béowulf is mín nama.\n"
(if
(System/getProperty "beowulf.version") (System/getProperty "beowulf.version")
"\n\n")) (str "Síðe " (System/getProperty "beowulf.version") "\n"))
(if
(:help (:options args))
(:summary args))
(if (:errors args)
(apply str (interpose "; " (:errors args))))
"\nSprecan 'quit' tó laéfan\n"))
(binding [*trace?* (true? (:trace (:options args)))]
(try (try
(repl) (repl (str (:prompt (:options args)) " "))
(catch (catch
Exception Exception
e e
@ -50,4 +73,4 @@
:quit nil :quit nil
;; default ;; default
(pprint data)) (pprint data))
(println e)))))) (println e))))))))

9
src/beowulf/host.clj Normal file
View file

@ -0,0 +1,9 @@
(ns beowulf.host)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; This file 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.
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -2,9 +2,15 @@
(:require [clojure.math.numeric-tower :refer [expt]] (:require [clojure.math.numeric-tower :refer [expt]]
[clojure.string :refer [starts-with? upper-case]] [clojure.string :refer [starts-with? upper-case]]
[instaparse.core :as i] [instaparse.core :as i]
[beowulf.cons-cell :refer [make-beowulf-list make-cons-cell NIL]]) [beowulf.cons-cell :refer [make-beowulf-list make-cons-cell NIL]]))
;; (:import [beowulf.cons-cell ConsCell])
) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; This file 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.
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare generate) (declare generate)

View file

@ -2,7 +2,7 @@
(:require [clojure.math.numeric-tower :refer [abs]] (:require [clojure.math.numeric-tower :refer [abs]]
[clojure.test :refer :all] [clojure.test :refer :all]
[beowulf.cons-cell :refer [make-beowulf-list make-cons-cell NIL T F]] [beowulf.cons-cell :refer [make-beowulf-list make-cons-cell NIL T F]]
[beowulf.eval :refer :all] [beowulf.bootstrap :refer :all]
[beowulf.read :refer [gsp]])) [beowulf.read :refer [gsp]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;