Better errors? WIP

This commit is contained in:
Scott Richmond 2022-02-19 14:34:07 -05:00
parent 40367d7e57
commit 23e2e95e77

View File

@ -9,34 +9,34 @@
"List of Ludus reserved words." "List of Ludus reserved words."
;; see ludus-spec repo for more info ;; see ludus-spec repo for more info
{ {
"as" ::token/as "as" ::token/as
"cond" ::token/cond "cond" ::token/cond
"else" ::token/else "else" ::token/else
"false" ::token/false "false" ::token/false
"fn" ::token/fn "fn" ::token/fn
"if" ::token/if "if" ::token/if
"let" ::token/let "let" ::token/let
"match" ::token/match "match" ::token/match
"mut" ::token/mut "mut" ::token/mut
"nil" ::token/nil "nil" ::token/nil
"panic!" ::token/panic "panic!" ::token/panic
"then" ::token/then "then" ::token/then
"true" ::token/true "true" ::token/true
"var" ::token/var "var" ::token/var
"with" ::token/with "with" ::token/with
;; below here, probable ;; below here, probable
"defer" ::token/defer "defer" ::token/defer
"gen" ::token/gen "gen" ::token/gen
"loop" ::token/loop "loop" ::token/loop
"ns" ::token/ns "ns" ::token/ns
"recur" ::token/recur "recur" ::token/recur
"repeat" ::token/repeat "repeat" ::token/repeat
"test" ::token/test "test" ::token/test
"wait" ::token/wait "wait" ::token/wait
"yield" ::token/yield "yield" ::token/yield
;; below here, possible ;; below here, possible
"when" ::token/when "when" ::token/when
}) })
(defn- new-scanner (defn- new-scanner
@ -122,34 +122,45 @@
;; The goal is to be able to be able to hand this to an LSP? ;; The goal is to be able to be able to hand this to an LSP?
;; Do we need a different structure ;; Do we need a different structure
(defn- add-error [scanner msg] (defn- add-error [scanner msg]
(update scanner ::errors conj {:msg msg :line (::line scanner) :start (::start scanner)})) (let [
token (token/token
::token/error
(current-lexeme scanner)
nil
(::line scanner)
(::start scanner))
err-token (assoc token :msg msg)
]
(-> scanner
(update ::errors conj err-token)
(update ::tokens conj err-token))))
(defn- add-keyword (defn- add-keyword
[scanner] [scanner]
(loop [scanner scanner (loop [scanner scanner
key ""] key ""]
(let [char (current-char scanner)] (let [char (current-char scanner)]
(cond (cond
(terminates? char) (add-token scanner ::token/keyword (keyword key)) (terminates? char) (add-token scanner ::token/keyword (keyword key))
(word-char? char) (recur (advance scanner) (str key char)) (word-char? char) (recur (advance scanner) (str key char))
:else (add-error scanner "Unexpected " char "after keyword :" key))))) :else (add-error scanner (str "Unexpected " char "after keyword :" key))))))
;; TODO: improve number parsing? ;; TODO: improve number parsing?
;; Currently this uses Clojure's number formatting rules (since we use the EDN reader) ;; Currently this uses Clojure's number formatting rules (since we use the EDN reader)
;; These rules are here: https://cljs.github.io/api/syntax/number ;; These rules are here: https://cljs.github.io/api/syntax/number
(defn- add-number [char scanner] (defn- add-number [char scanner]
(loop [scanner scanner (loop [scanner scanner
num (str char) num (str char)
float? false] float? false]
(let [curr (current-char scanner)] (let [curr (current-char scanner)]
(cond (cond
(= curr \_) (recur (advance scanner) num float?) ;; consume underscores unharmed (= curr \_) (recur (advance scanner) num float?) ;; consume underscores unharmed
(= curr \.) (if float? (= curr \.) (if float?
(add-error scanner (str "Unexpected second decimal point after " num ".")) (add-error scanner (str "Unexpected second decimal point after " num "."))
(recur (advance scanner) (str num curr) true)) (recur (advance scanner) (str num curr) true))
(terminates? curr) (add-token scanner ::token/number (edn/read-string num)) (terminates? curr) (add-token scanner ::token/number (edn/read-string num))
(digit? curr) (recur (advance scanner) (str num curr) float?) (digit? curr) (recur (advance scanner) (str num curr) float?)
:else (add-error scanner (str "Unexpected " curr " after number " num ".")))))) :else (add-error scanner (str "Unexpected " curr " after number " num "."))))))
;; TODO: add string interpolation ;; TODO: add string interpolation
;; This still has to be devised ;; This still has to be devised
@ -163,9 +174,9 @@
\" (add-token (advance scanner) ::token/string string) \" (add-token (advance scanner) ::token/string string)
\\ (let [next (next-char scanner) \\ (let [next (next-char scanner)
scanner (if (= next \newline) scanner (if (= next \newline)
(update scanner ::line inc) (update scanner ::line inc)
scanner)] scanner)]
(recur (advance (advance scanner)) (str string next))) (recur (advance (advance scanner)) (str string next)))
(if (at-end? scanner) (if (at-end? scanner)
(add-error scanner "Unterminated string.") (add-error scanner "Unterminated string.")
(recur (advance scanner) (str string char))))))) (recur (advance scanner) (str string char)))))))
@ -174,7 +185,7 @@
[char scanner] [char scanner]
(loop [scanner scanner (loop [scanner scanner
word (str char)] word (str char)]
(let [curr (current-char scanner)] (let [curr (current-char scanner)]
(cond (cond
(terminates? curr) (add-token scanner (get reserved-words word ::token/word)) (terminates? curr) (add-token scanner (get reserved-words word ::token/word))
(word-char? curr) (recur (advance scanner) (str word curr)) (word-char? curr) (recur (advance scanner) (str word curr))
@ -184,11 +195,11 @@
[scanner] [scanner]
(loop [scanner scanner (loop [scanner scanner
ignored "_"] ignored "_"]
(let [char (current-char scanner)] (let [char (current-char scanner)]
(cond (cond
(terminates? char) (add-token scanner ::token/ignored) (terminates? char) (add-token scanner ::token/ignored)
(word-char? char) (recur (advance scanner) (str ignored char)) (word-char? char) (recur (advance scanner) (str ignored char))
:else (add-error scanner (str "Unexpected " char " after word " ignored ".")))))) :else (add-error scanner (str "Unexpected " char " after word " ignored "."))))))
(defn- add-comment [char scanner] (defn- add-comment [char scanner]
(loop [scanner scanner (loop [scanner scanner
@ -221,9 +232,9 @@
;; two-character tokens ;; two-character tokens
;; -> ;; ->
\- (cond \- (cond
(= next \>) (add-token (advance scanner) ::token/rarrow) (= next \>) (add-token (advance scanner) ::token/rarrow)
(digit? next) (add-number char scanner) (digit? next) (add-number char scanner)
:else (add-error scanner (str "Expected -> or negative number. Got " char next))) :else (add-error scanner (str "Expected -> or negative number. Got " char next)))
;; at current we're not using this ;; at current we're not using this
;; <- ;; <-
@ -268,15 +279,15 @@
;; TODO: instead of a separate token, scan a whole type keyword ;; TODO: instead of a separate token, scan a whole type keyword
;; e.g. ::string, ::number ;; e.g. ::string, ::number
\: (cond \: (cond
;;(= \: next) (add-token (advance scanner) ::token/doublecolon)) ;;(= \: next) (add-token (advance scanner) ::token/doublecolon))
(alpha? next) (add-keyword scanner) (alpha? next) (add-keyword scanner)
:else (add-error scanner (str "Expected keyword. Got " char next))) :else (add-error scanner (str "Expected keyword. Got " char next)))
;; splats ;; splats
\. (let [after_next (current-char (advance scanner))] \. (let [after_next (current-char (advance scanner))]
(if (= ".." (str next after_next)) (if (= ".." (str next after_next))
(add-token (advance (advance scanner)) ::token/splat) (add-token (advance (advance scanner)) ::token/splat)
(add-error scanner (str "Expected splat: ... . Got " (str "." next after_next))))) (add-error scanner (str "Expected splat: ... . Got " (str "." next after_next)))))
;; strings ;; strings
\" (add-string scanner) \" (add-string scanner)