From 53b71fe790e89e8d239389adf6d949f4c6d969c4 Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Sun, 17 Dec 2023 23:13:50 -0500 Subject: [PATCH 1/5] Panic is now a form, not a function. --- src/ludus/base.cljc | 13 ++++++------- src/ludus/grammar.cljc | 5 ++++- src/ludus/interpreter.cljc | 7 +++++++ src/ludus/node.cljc | 11 +++++++++-- src/ludus/prelude.ld | 24 +++++------------------- src/ludus/scanner.cljc | 3 +-- target/repl-port | 2 +- 7 files changed, 33 insertions(+), 32 deletions(-) diff --git a/src/ludus/base.cljc b/src/ludus/base.cljc index 0c5fa64..dbe7021 100644 --- a/src/ludus/base.cljc +++ b/src/ludus/base.cljc @@ -73,12 +73,11 @@ (defn- stringify-args [arglist] (apply str (interpose " " (into [] (map print-show) (rest arglist))))) - -(def panic! {:name "panic!" - ::data/type ::data/clj - :body (fn panic-inner - ([] (panic-inner [::data/list])) - ([args] (throw (ex-info (stringify-args args) {}))))}) +; (def panic! {:name "panic!" +; ::data/type ::data/clj +; :body (fn panic-inner +; ([] (panic-inner [::data/list])) +; ([args] (throw (ex-info (stringify-args args) {}))))}) (def print- {:name "print" ::data/type ::data/clj @@ -410,7 +409,7 @@ :to_vec to_vec :fold fold :map map - :panic! panic! + ; :panic! panic! :prn prn- :concat concat- :str str- diff --git a/src/ludus/grammar.cljc b/src/ludus/grammar.cljc index 2290430..47e0978 100644 --- a/src/ludus/grammar.cljc +++ b/src/ludus/grammar.cljc @@ -234,7 +234,9 @@ (defp collection flat choice [;struct-literal dict list-literal set-literal tuple]) -(defp simple flat choice [literal collection synthetic recur-call lambda]) +(defp panic group order-1 [(quiet :panic) expression]) + +(defp simple flat choice [literal collection synthetic recur-call lambda panic]) (defp compound flat choice [match loop-expr if-expr when-expr do-expr block repeat-expr]) @@ -268,3 +270,4 @@ (defp script order-0 [nls? (one+ script-line) (quiet :eof)]) + diff --git a/src/ludus/interpreter.cljc b/src/ludus/interpreter.cljc index 6b8b470..5f7c069 100644 --- a/src/ludus/interpreter.cljc +++ b/src/ludus/interpreter.cljc @@ -817,11 +817,18 @@ (defn- interpret-literal [ast] (-> ast :data first)) +(defn- interpret-panic [ast ctx] + (let [msg-value (interpret-ast (:data ast) ctx) + msg-string (show/show msg-value)] + (throw (ex-info (str "Ludus panicked: " msg-string) {:ast ast})))) + (defn interpret-ast [ast ctx] (case (:type ast) (:nil :true :false :number :string :keyword) (interpret-literal ast) + :panic! (interpret-panic ast ctx) + :let-expr (interpret-let ast ctx) :if-expr (interpret-if ast ctx) diff --git a/src/ludus/node.cljc b/src/ludus/node.cljc index a1708b3..33dbb91 100644 --- a/src/ludus/node.cljc +++ b/src/ludus/node.cljc @@ -33,9 +33,7 @@ (defn run [source] (let [user_scanned (s/scan source) user_tokens (:tokens user_scanned) - _ (println "Tokens: " user_tokens) user_parsed (p/apply-parser g/script user_tokens) - _ (println "Ast: " (i/prettify-ast user_parsed)) user_result (i/interpret-safe source user_parsed {}) result_str (show/show user_result) post_scanned (s/scan pre/postlude) @@ -60,3 +58,12 @@ ) )) +(do + + (def source "panic! :oops") + + (def tokens (s/scan source)) + + ) + + diff --git a/src/ludus/prelude.ld b/src/ludus/prelude.ld index e3e62f9..ff18830 100644 --- a/src/ludus/prelude.ld +++ b/src/ludus/prelude.ld @@ -276,19 +276,6 @@ fn report! { } } -fn panic! { - "Causes Ludus to panic, outputting any arguments as messages." - () -> { - add_msg! ("Ludus panicked!") - base :panic! () - } - (...args) -> { - add_msg! ("Ludus panicked!") - add_msg! (args) - base :panic! (args) - } -} - fn doc! { "Prints the documentation of a function to the console." (f as :fn) -> do f > base :doc > print! @@ -396,7 +383,7 @@ fn mult { fn div { "Divides numbers. Panics on division by zero." (x as :number) -> x - (_, 0) -> panic! ("Division by zero.") + (_, 0) -> panic! "Division by zero." (x as :number, y as :number) -> base :div (x, y) (x, y, ...zs) -> { let divisor = fold (mult, zs, y) @@ -898,8 +885,8 @@ fn err? { fn unwrap! { "Takes a result tuple. If it's :ok, then returns the value. If it's not :ok, then it panics. If it's not a result tuple, it also panics." ((:ok, value)) -> value - ((:err, msg)) -> panic! ("Unwrapped :err", msg) - (_) -> panic! ("Cannot unwrap something that's not an error tuple.") + ((:err, msg)) -> panic! string ("Unwrapped :err! ", msg) + (_) -> panic! "Cannot unwrap something that's not an error tuple." } fn unwrap_or { @@ -910,10 +897,10 @@ fn unwrap_or { fn assert! { "Asserts a condition: returns the value if the value is truthy, panics if the value is falsy. Takes an optional message." - (value) -> if value then value else panic! ("Assert failed", value) + (value) -> if value then value else panic! string ("Assert failed", value) (value, message) -> if value then value - else panic! ("Assert failed:", message, value) + else panic! string ("Assert failed:", message, value) } &&& Turtle & other graphics @@ -1217,7 +1204,6 @@ ns prelude { show prn! report! - panic! doc! concat ref? diff --git a/src/ludus/scanner.cljc b/src/ludus/scanner.cljc index b0a9fde..bc140df 100644 --- a/src/ludus/scanner.cljc +++ b/src/ludus/scanner.cljc @@ -20,7 +20,7 @@ "match" :match ;; impl "nil" :nil ;; impl -> literal word "ns" :ns ;; impl - ;; "panic!" :panic ;; impl (should be a function) + "panic!" :panic ;; impl (should _not_ be a function) "recur" :recur ;; impl "ref" :ref ;; impl "then" :then ;; impl @@ -329,4 +329,3 @@ {:tokens (:tokens scanner) :errors (:errors scanner)}) (recur (-> scanner (scan-token) (next-token)))))) - diff --git a/target/repl-port b/target/repl-port index 8dbb69f..88c8772 100644 --- a/target/repl-port +++ b/target/repl-port @@ -1 +1 @@ -51500 \ No newline at end of file +51828 \ No newline at end of file -- 2.45.1 From 4e646101e218ba8d5d83480ff05f53cfefda4a18 Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Sun, 17 Dec 2023 23:16:12 -0500 Subject: [PATCH 2/5] Remove duplicate error messages. --- src/ludus/interpreter.cljc | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/ludus/interpreter.cljc b/src/ludus/interpreter.cljc index 5f7c069..8dfae12 100644 --- a/src/ludus/interpreter.cljc +++ b/src/ludus/interpreter.cljc @@ -818,16 +818,16 @@ (defn- interpret-literal [ast] (-> ast :data first)) (defn- interpret-panic [ast ctx] - (let [msg-value (interpret-ast (:data ast) ctx) + (let [msg-value (interpret-ast (-> ast :data first) ctx) msg-string (show/show msg-value)] - (throw (ex-info (str "Ludus panicked: " msg-string) {:ast ast})))) + (throw (ex-info msg-string {:ast ast})))) (defn interpret-ast [ast ctx] (case (:type ast) (:nil :true :false :number :string :keyword) (interpret-literal ast) - :panic! (interpret-panic ast ctx) + :panic (interpret-panic ast ctx) :let-expr (interpret-let ast ctx) @@ -893,7 +893,7 @@ ; :struct-literal ; (interpret-struct ast ctx) - (throw (ex-info (str "Unknown AST node type " (get ast :type :err) " on line " (get-in ast [:token :line])) {:ast ast})))) + (throw (ex-info (str "Unknown AST node type " (get ast :type :none) " on line " (get-in ast [:token :line])) {:ast ast})))) (defn get-line [source line] (if line @@ -984,24 +984,35 @@ :message (ex-message e)} )))) +; No prelude; helps when there are errors in the prelude +(defn interpret-bare [source parsed ctx] + (let [base-ctx (volatile! {::parent (volatile! ctx)})] + (try + (interpret-ast parsed base-ctx) + (catch Throwable e + {::data/error true + :line (get-in (ex-data e) [:ast :token :line]) + :message (ex-message e)})))) + ;; repl -(comment - +(do + (println "***********") (def source " - let times = 1.3 - repeat times { print! (:foo) } + panic! (:oh, :no) ") (def tokens (-> source scanner/scan :tokens)) (def ast (p/apply-parser g/script tokens)) - (def result (interpret-safe source ast {})) + (def result (interpret-bare source ast {})) - ;(-> ast prettify-ast println) + (println tokens) + + (-> ast prettify-ast println) (println result) -- 2.45.1 From be2c91b7dcb76b454c4fb2afb01df1c3bbe727cb Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Mon, 18 Dec 2023 00:20:34 -0500 Subject: [PATCH 3/5] Fix line number reporting bugs: look for lines in the right place. --- src/ludus/error.cljc | 22 +++++++++++++--------- src/ludus/interpreter.cljc | 10 ++++++---- src/ludus/node.cljc | 16 +++++++++------- src/ludus/prelude.ld | 6 +++--- src/ludus/scanner.cljc | 17 +++++++++++------ src/ludus/token.cljc | 4 +++- target/repl-port | 1 - 7 files changed, 45 insertions(+), 31 deletions(-) delete mode 100644 target/repl-port diff --git a/src/ludus/error.cljc b/src/ludus/error.cljc index 22f2cf4..2fc5ec9 100644 --- a/src/ludus/error.cljc +++ b/src/ludus/error.cljc @@ -1,7 +1,7 @@ (ns ludus.error (:require [clojure.string :as string])) -(defn get-line [source {:keys [line]}] +(defn get-line [source line] (let [lines (string/split-lines source) the_line (nth lines (dec line))] the_line)) @@ -21,22 +21,26 @@ (defn scan-error [] :TODO) -(defn parse-error [source {:keys [trace token]}] - (let [line (get-line source token) +(defn parse-error [{:keys [trace token]}] + (let [source (:source token) + input (:input token) + line-num (:line token) + line (get-line source line-num) line-num (:line token) prefix (str line-num ": ") underline (get-underline source token (count prefix)) expected (first trace) got (:type token) - message (str "Ludus found a parsing error on line " line-num ".\nExpected: " expected "\nGot: " got "\n") + message (str "Ludus found a parsing error on line " line-num " in " input ".\nExpected: " expected "\nGot: " got "\n") ] (str message "\n" prefix line "\n" underline) ) ) -(defn run-error [source {:keys [line message]}] - (if line - (str "Ludus panicked on line " line ":\n" (get-line source {:line line}) "\n" message) - (str "Ludus panicked!\n" message) - )) +(defn run-error [{:keys [message token line]}] + (let [source (:source token) input (:input token)] + (if line + (str "Ludus panicked!: " message "\nOn line " line " in " input "\n" (get-line source line)) + (str "Ludus panicked!\n" message) + ))) diff --git a/src/ludus/interpreter.cljc b/src/ludus/interpreter.cljc index 8dfae12..6c45a75 100644 --- a/src/ludus/interpreter.cljc +++ b/src/ludus/interpreter.cljc @@ -915,11 +915,11 @@ (into {} (map-keys kw->str) ns)) (def ludus-prelude - (let [scanned (scanner/scan prelude/prelude) + (let [scanned (scanner/scan prelude/prelude "prelude") parsed (p/apply-parser g/script (:tokens scanned)) ; _ (println "Parse status: " (:status parsed)) ; _ (if (= :err (:status parsed)) - ; (throw (ex-info (error/parse-error prelude/prelude parsed) {}))) + ; (throw (ex-info (error/parse-error parsed) {}))) base-ctx (volatile! {::parent (volatile! {"base" base/base})}) interpreted (interpret-ast parsed base-ctx) namespace (dissoc interpreted ::data/type ::data/name ::data/struct) @@ -980,6 +980,7 @@ ;(pp/pprint (ex-data e)) ;(throw e) {::data/error true + :token (get-in (ex-data e) [:ast :token]) :line (get-in (ex-data e) [:ast :token :line]) :message (ex-message e)} )))) @@ -991,12 +992,13 @@ (interpret-ast parsed base-ctx) (catch Throwable e {::data/error true + :token (get-in (ex-data e) [:ast :token]) :line (get-in (ex-data e) [:ast :token :line]) :message (ex-message e)})))) ;; repl -(do +(comment (println "***********") @@ -1004,7 +1006,7 @@ panic! (:oh, :no) ") - (def tokens (-> source scanner/scan :tokens)) + (def tokens (-> source (scanner/scan "test input") :tokens)) (def ast (p/apply-parser g/script tokens)) diff --git a/src/ludus/node.cljc b/src/ludus/node.cljc index 33dbb91..d63f27a 100644 --- a/src/ludus/node.cljc +++ b/src/ludus/node.cljc @@ -31,12 +31,12 @@ #?(:clj value :cljs (clj->js value))) (defn run [source] - (let [user_scanned (s/scan source) + (let [user_scanned (s/scan source "user input") user_tokens (:tokens user_scanned) user_parsed (p/apply-parser g/script user_tokens) user_result (i/interpret-safe source user_parsed {}) result_str (show/show user_result) - post_scanned (s/scan pre/postlude) + post_scanned (s/scan pre/postlude "postlude") post_tokens (:tokens post_scanned) post_parsed (p/apply-parser g/script post_tokens) post_result (i/interpret-safe source post_parsed {}) @@ -48,22 +48,24 @@ (clean-out {:errors (:errors user_tokens)}) (= :err (:status user_parsed)) - (clean-out {:errors [(error/parse-error source user_parsed)]}) + (clean-out {:errors [(error/parse-error user_parsed)]}) (::data/error user_result) - (clean-out (assoc (ld->clj post_result) :errors [(error/run-error source user_result)])) + (clean-out (assoc (ld->clj post_result) :errors [(error/run-error user_result)])) :else (clean-out clj_result) ) )) -(do +(comment + (def source " - (def source "panic! :oops") + a b c - (def tokens (s/scan source)) + ") + (-> source run :errors println) ) diff --git a/src/ludus/prelude.ld b/src/ludus/prelude.ld index ff18830..5afd761 100644 --- a/src/ludus/prelude.ld +++ b/src/ludus/prelude.ld @@ -897,10 +897,10 @@ fn unwrap_or { fn assert! { "Asserts a condition: returns the value if the value is truthy, panics if the value is falsy. Takes an optional message." - (value) -> if value then value else panic! string ("Assert failed", value) - (value, message) -> if value + (value) -> if value then value else panic! string ("Assert failed:", value) + (msg, value) -> if value then value - else panic! string ("Assert failed:", message, value) + else panic! string ("Assert failed: ", msg, " with ", value) } &&& Turtle & other graphics diff --git a/src/ludus/scanner.cljc b/src/ludus/scanner.cljc index bc140df..f55531a 100644 --- a/src/ludus/scanner.cljc +++ b/src/ludus/scanner.cljc @@ -51,8 +51,9 @@ (defn- new-scanner "Creates a new scanner." - [source] + [source input] {:source source + :input input :length (count source) :errors [] :start 0 @@ -135,7 +136,9 @@ (current-lexeme scanner) literal (:line scanner) - (:start scanner))))) + (:start scanner) + (:source scanner) + (:input scanner))))) ;; TODO: errors should also be in the vector of tokens ;; The goal is to be able to be able to hand this to an LSP? @@ -146,7 +149,9 @@ (current-lexeme scanner) nil (:line scanner) - (:start scanner)) + (:start scanner) + (:source scanner) + (:input scanner)) err-token (assoc token :message msg)] (-> scanner (update :errors conj err-token) @@ -240,7 +245,7 @@ comm (str char)] (let [char (current-char scanner)] (if (= \newline char) - (update scanner :line inc) + scanner (recur (advance scanner) (str comm char)))))) (defn- scan-token [scanner] @@ -322,8 +327,8 @@ (defn- next-token [scanner] (assoc scanner :start (:current scanner))) -(defn scan [source] - (loop [scanner (new-scanner source)] +(defn scan [source input] + (loop [scanner (new-scanner source input)] (if (at-end? scanner) (let [scanner (add-token (add-token scanner :break) :eof)] {:tokens (:tokens scanner) diff --git a/src/ludus/token.cljc b/src/ludus/token.cljc index 5188fbd..6638bc0 100644 --- a/src/ludus/token.cljc +++ b/src/ludus/token.cljc @@ -1,9 +1,11 @@ (ns ludus.token) (defn token - [type text literal line start] + [type text literal line start source input] {:type type :lexeme text :literal literal :line line + :source source + :input input :start start}) diff --git a/target/repl-port b/target/repl-port deleted file mode 100644 index 88c8772..0000000 --- a/target/repl-port +++ /dev/null @@ -1 +0,0 @@ -51828 \ No newline at end of file -- 2.45.1 From ff1e1345b87c97c72613e1aac768292587792c94 Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Mon, 18 Dec 2023 00:20:50 -0500 Subject: [PATCH 4/5] Stop tracking repl-port --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c27d6a7..2379caa 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ node_modules/ .shadow-cljs .cljs_node_repl/ .helix/ +target/repl-port -- 2.45.1 From b12b49c19726b6d0733ebe98a28f533ddabb00a4 Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Mon, 18 Dec 2023 00:22:42 -0500 Subject: [PATCH 5/5] Fix cljs/clj bug --- src/ludus/interpreter.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ludus/interpreter.cljc b/src/ludus/interpreter.cljc index 6c45a75..7d8fa28 100644 --- a/src/ludus/interpreter.cljc +++ b/src/ludus/interpreter.cljc @@ -990,7 +990,7 @@ (let [base-ctx (volatile! {::parent (volatile! ctx)})] (try (interpret-ast parsed base-ctx) - (catch Throwable e + (catch #?(:clj Throwable :cljs js/Object) e {::data/error true :token (get-in (ex-data e) [:ast :token]) :line (get-in (ex-data e) [:ast :token :line]) -- 2.45.1