Added command-line flags, some of which actually work.
This commit is contained in:
parent
692eefeece
commit
75bf19e38e
64
README.md
64
README.md
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
0
resources/lisp1.5.lsp
Normal 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)))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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,22 +46,31 @@
|
||||||
(recur)))
|
(recur)))
|
||||||
|
|
||||||
(defn -main
|
(defn -main
|
||||||
[& args]
|
[& opts]
|
||||||
(println
|
(let [args (parse-opts opts cli-options)]
|
||||||
(str
|
(println
|
||||||
"Hider wilcuman. Béowulf is mín nama\nSíðe "
|
(str
|
||||||
(System/getProperty "beowulf.version")
|
"\nHider wilcuman. Béowulf is mín nama.\n"
|
||||||
"\n\n"))
|
|
||||||
(try
|
|
||||||
(repl)
|
|
||||||
(catch
|
|
||||||
Exception
|
|
||||||
e
|
|
||||||
(let [data (ex-data e)]
|
|
||||||
(if
|
(if
|
||||||
data
|
(System/getProperty "beowulf.version")
|
||||||
(case (:cause data)
|
(str "Síðe " (System/getProperty "beowulf.version") "\n"))
|
||||||
:quit nil
|
(if
|
||||||
;; default
|
(:help (:options args))
|
||||||
(pprint data))
|
(:summary args))
|
||||||
(println e))))))
|
(if (:errors args)
|
||||||
|
(apply str (interpose "; " (:errors args))))
|
||||||
|
"\nSprecan 'quit' tó laéfan\n"))
|
||||||
|
(binding [*trace?* (true? (:trace (:options args)))]
|
||||||
|
(try
|
||||||
|
(repl (str (:prompt (:options args)) " "))
|
||||||
|
(catch
|
||||||
|
Exception
|
||||||
|
e
|
||||||
|
(let [data (ex-data e)]
|
||||||
|
(if
|
||||||
|
data
|
||||||
|
(case (:cause data)
|
||||||
|
:quit nil
|
||||||
|
;; default
|
||||||
|
(pprint data))
|
||||||
|
(println e))))))))
|
||||||
|
|
9
src/beowulf/host.clj
Normal file
9
src/beowulf/host.clj
Normal 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.
|
||||||
|
;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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]]))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
Loading…
Reference in a new issue