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
|
||||
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
|
||||
same bahaviour; the only intended deviation is that the parser reads
|
||||
'mexprs' (meta language expressions) as well as sexprs.
|
||||
same bahaviour - except as documented below.
|
||||
|
||||
## 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.
|
||||
|
||||
|
@ -25,7 +77,11 @@ Because I'm barking mad, and this is therapy.
|
|||
|
||||
## 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
|
||||
|
||||
|
|
|
@ -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"
|
||||
:url "http://example.com/FIXME"
|
||||
:license {:name "GPL-2.0-or-later"
|
||||
: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/tools.cli "0.4.2"]
|
||||
[org.clojure/tools.trace "0.7.10"]
|
||||
[environ "1.1.0"]
|
||||
[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]
|
||||
[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)
|
||||
|
||||
(def oblist
|
||||
|
@ -9,6 +19,10 @@
|
|||
be Lisp 1.5's EQuivalent of `SETQ`), possibly by other things."
|
||||
(atom NIL))
|
||||
|
||||
(def ^:dynamic *trace?*
|
||||
"Whether or not to trace `EVAL`."
|
||||
false)
|
||||
|
||||
(defn NULL
|
||||
[x]
|
||||
(if (= x NIL) 'T 'F))
|
||||
|
@ -208,7 +222,7 @@
|
|||
:else
|
||||
(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.
|
||||
All args are assumed to be symbols or `beowulf.cons-cell/ConsCell` objects.
|
||||
See page 13 of the Lisp 1.5 Programmers Manual."
|
||||
|
@ -217,6 +231,8 @@
|
|||
(=
|
||||
(ATOM? function)
|
||||
'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 'CDR) (CDAR args)
|
||||
(= function 'CONS) (make-cons-cell (CAR args) (CADR args))
|
||||
|
@ -261,11 +277,8 @@
|
|||
(EVAL (CAR args) env)
|
||||
(EVLIS (CDR args) env))))
|
||||
|
||||
|
||||
(deftrace 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."
|
||||
(deftrace traced-eval
|
||||
"Essentially, identical to EVAL except traced."
|
||||
[expr env]
|
||||
(cond
|
||||
(=
|
||||
|
@ -285,3 +298,29 @@
|
|||
(EVLIS (CDR expr) 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
|
||||
(:require [beowulf.eval :refer [EVAL oblist]]
|
||||
(:require [beowulf.bootstrap :refer [EVAL oblist *trace?*]]
|
||||
[beowulf.read :refer [READ]]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.pprint :refer [pprint]]
|
||||
[clojure.tools.cli :refer [parse-opts]]
|
||||
[environ.core :refer [env]])
|
||||
(: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
|
||||
"Read/eval/print loop."
|
||||
[]
|
||||
[prompt]
|
||||
(loop []
|
||||
(print "Sprecan:: ")
|
||||
(print prompt)
|
||||
(flush)
|
||||
(try
|
||||
(let [input (read-line)]
|
||||
(cond
|
||||
(= input "quit") (throw (ex-info "Færwell!" {:cause :quit}))
|
||||
input (println (str "> " (EVAL (READ input) @oblist)))
|
||||
(= input "quit") (throw (ex-info "\nFærwell!" {:cause :quit}))
|
||||
input (println (str "> " (print-str (EVAL (READ input) @oblist))))
|
||||
:else (println)))
|
||||
(catch
|
||||
Exception
|
||||
|
@ -32,22 +46,31 @@
|
|||
(recur)))
|
||||
|
||||
(defn -main
|
||||
[& args]
|
||||
(println
|
||||
(str
|
||||
"Hider wilcuman. Béowulf is mín nama\nSíðe "
|
||||
(System/getProperty "beowulf.version")
|
||||
"\n\n"))
|
||||
(try
|
||||
(repl)
|
||||
(catch
|
||||
Exception
|
||||
e
|
||||
(let [data (ex-data e)]
|
||||
[& opts]
|
||||
(let [args (parse-opts opts cli-options)]
|
||||
(println
|
||||
(str
|
||||
"\nHider wilcuman. Béowulf is mín nama.\n"
|
||||
(if
|
||||
data
|
||||
(case (:cause data)
|
||||
:quit nil
|
||||
;; default
|
||||
(pprint data))
|
||||
(println e))))))
|
||||
(System/getProperty "beowulf.version")
|
||||
(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
|
||||
(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]]
|
||||
[clojure.string :refer [starts-with? upper-case]]
|
||||
[instaparse.core :as i]
|
||||
[beowulf.cons-cell :refer [make-beowulf-list make-cons-cell NIL]])
|
||||
;; (:import [beowulf.cons-cell ConsCell])
|
||||
)
|
||||
[beowulf.cons-cell :refer [make-beowulf-list make-cons-cell NIL]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;
|
||||
;;; 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)
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
(:require [clojure.math.numeric-tower :refer [abs]]
|
||||
[clojure.test :refer :all]
|
||||
[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]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
Loading…
Reference in a new issue