From 526b71bce7facf398b34d9dd8cbdee5c465021f5 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Sat, 17 Aug 2019 13:48:49 +0100 Subject: [PATCH] Considerably improved REPL, with better parse error handling --- README.md | 17 ++++++++++++--- project.clj | 2 ++ src/beowulf/core.clj | 52 ++++++++++++++++++++++++++++++++++++++------ src/beowulf/read.clj | 12 ++++++---- 4 files changed, 69 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 31efe93..e335a1c 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,22 @@ LISP 1.5 is to all Lisp dialects as Beowulf is to Emglish literature. ## What this is -A work-in-progress towards an implementation of Lisp 1.5 in Clojure. +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. ## BUT WHY?!!?! Because. -Because Lisp is the only computer language worth learning, and if a thing is worth learning, it's worth learning properly; which means going back to the beginning and trying to understand that. +Because Lisp is the only computer language worth learning, and if a thing +is worth learning, it's worth learning properly; which means going back to +the beginning and trying to understand that. -Because there is, so far as I know, no working implementation of Lisp 1.5 for modern machines. +Because there is, so far as I know, no working implementation of Lisp 1.5 +for modern machines. Because I'm barking mad, and this is therapy. @@ -24,6 +31,10 @@ Download from http://example.com/FIXME. `java -jar beowulf-0.1.0-standalone.jar` +This will start a Lisp 1.5 read/eval/print loop (REPL). + +To end a session, type `quit` at the command prompt. + ## Learning Lisp 1.5 The `Lisp 1.5 Programmer's Manual` is still diff --git a/project.clj b/project.clj index 3985efa..7738a79 100644 --- a/project.clj +++ b/project.clj @@ -6,7 +6,9 @@ :dependencies [[org.clojure/clojure "1.10.0"] [org.clojure/math.numeric-tower "0.0.4"] [org.clojure/tools.trace "0.7.10"] + [environ "1.1.0"] [instaparse "1.4.10"]] :main ^:skip-aot beowulf.core + :plugins [[lein-environ "1.1.0"]] :target-path "target/%s" :profiles {:uberjar {:aot :all}}) diff --git a/src/beowulf/core.clj b/src/beowulf/core.clj index 9d9e9ea..49eb105 100644 --- a/src/beowulf/core.clj +++ b/src/beowulf/core.clj @@ -1,15 +1,53 @@ (ns beowulf.core (:require [beowulf.eval :refer [EVAL oblist]] - [beowulf.read :refer [READ]]) + [beowulf.read :refer [READ]] + [clojure.pprint :refer [pprint]] + [environ.core :refer [env]]) (:gen-class)) -(defn -main +(defn repl "Read/eval/print loop." - [& args] - (println "Béowulf is mín nama") + [] (loop [] (print ":: ") (flush) - (let [input (READ)] - (println (str "> " (EVAL input @oblist))) - (recur)))) + (try + (let [input (read-line)] + (cond + (= input "quit") (throw (ex-info "Færwell!" {:cause :quit})) + input (println (str "> " (EVAL (READ input) @oblist))) + :else (println))) + (catch + Exception + e + (let [data (ex-data e)] + (if + data + (case (:cause data) + :parse-failure (println (:failure data)) + :quit (throw e) + ;; default + (pprint data)) + (println (.getMessage e)))))) + (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)] + (if + data + (case (:cause data) + :quit (println (.getMessage e)) + ;; default + (pprint data)) + (println e)))))) diff --git a/src/beowulf/read.clj b/src/beowulf/read.clj index bac8430..c68cfd9 100644 --- a/src/beowulf/read.clj +++ b/src/beowulf/read.clj @@ -64,9 +64,13 @@ scale-factor := #'[0-9]*'"))) (defn simplify - "Simplify this parse tree `p`." + "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] - (simplify p :sexpr)) + (if + (instance? instaparse.gll.Failure p) + (throw (ex-info "Parse error" {:cause :parse-failure :failure p})) + (simplify p :sexpr))) ([p context] (if (coll? p) @@ -274,5 +278,5 @@ `(generate (simplify (parse ~s)))) (defn READ - [] - (gsp (read-line))) + [input] + (gsp (or input (read-line))))