diff --git a/janet/myparser.janet b/janet/myparser.janet index a154d75..60c6d11 100644 --- a/janet/myparser.janet +++ b/janet/myparser.janet @@ -387,6 +387,7 @@ if (:)foo then :bar else :baz (comment Okay, so I'm trying to figure out how to manage panics in this declarative (not recursive-descent) parser. -The question is how to - +The question is how to do the thing where we pop out of the stack up to the most relevant rule. +Given that we may have deeply nested rules, we either want: + - A stack of rules in the parser, which ) diff --git a/janet/recursive.janet b/janet/recursive.janet new file mode 100644 index 0000000..5d38a2a --- /dev/null +++ b/janet/recursive.janet @@ -0,0 +1,109 @@ +### A recursive descent parser for Ludus + + +### First, some mutual recursion helpers +(defn unreachable + "A function that errors out if called." + [&] (error "cannot call recursive function before definition")) + +(defmacro declare + "Forward-declares a function name, so that it can be called in a mutually recursive manner." + [& names] + (def bindings @[]) + (loop [name :in names] + (def binding ~(var ,name unreachable)) + (array/push bindings binding)) + ~(upscope ,;bindings)) + +(defmacro defrec + "Defines a function depended on by another function, that has been forward `declare`d." + [name & forms] + (if-not (dyn name) (error "recursive functions must be declared before they are defined")) + ~(set ,name (defn ,name ,;forms))) + +### Next: a data structure for a parser +(defn- new-parser [tokens] + @{ + :tokens (tokens :tokens) + :ast @[] + :current 0 + :errors @[] + }) + +### and some helper functions for interfacing with that data structure +(defn- current [parser] (get (parser :tokens) (parser :current))) + +(defn- peek [parser] (get (parser :tokens) (inc (parser :current)))) + +(defn- advance [parser] (update parser :current inc)) + +(defn- type [token] (get token :type)) + +(defn- expect [parser type] + (def current-type (-> parser current (get :type))) + (= type current-type)) + +### Parsing functions +(declare expr) + +# atoms +(defn- bool [parser] + (def curr (-> parser current)) + (def ttype (type curr)) + (def value (if (= ttype :true) true false)) + (def ast {:type :bool :value value :token curr}) + (update parser :ast array/push ast) + (advance parser)) + +(defn- num [parser] + (def curr (-> parser current)) + (def ast {:type :number :value (curr :literal) :token curr}) + (update parser :ast array/push ast) + (advance parser)) + +(defn- kw [parser] + (def curr (-> parser current)) + (def ast {:type :keyword :value (curr :literal) :token curr}) + (update parser :ast array/push ast) + (advance parser)) + +(defn- nill [parser] + (def ast {:type :nil :token (current parser)}) + (update parser :ast array/push ast) + (advance parser)) + +(defn- str [parser] + (def curr (-> parser current)) + (def ast {:type :string :value (curr :literal) :token curr}) + (update parser :ast array/push ast) + (advance parser)) + +(defrec expr [parser] + (def curr (current parser)) + (case (type curr) + # atoms + :nil (nill parser) + :true (bool parser) + :false (bool parser) + :number (num parser) + :keyword (kw parser) + + # strings + :string (str parser) + ### TODO: interpolated strings + + ) +) + +(do +#(comment +(os/cd "janet") # For repl to do relative imports +(import ./scanner :as s) + +(def scanned (s/scan "\"foo bar baz\"")) +(def a-parser (new-parser scanned)) +(def parsed (expr a-parser)) +(-> parsed (get :ast) (get 0) (get :value)) +) + + diff --git a/janet/scanner.janet b/janet/scanner.janet index 1a205c1..391ce9b 100644 --- a/janet/scanner.janet +++ b/janet/scanner.janet @@ -192,12 +192,13 @@ [scanner] (print "Adding string") (defn recur [scanner buff interpolate?] - (print "\n printing a thing from add-stirng" buff) (let [char (current-char scanner)] (case char "{" (recur (advance scanner) (buffer/push buff char) true) # allow multiline strings "\n" (recur (update (advance scanner) :line inc) (buffer/push buff char) interpolate?) + ### TODO: add interpolated strings + ### :string -> (if (interpolate? :interpolated :string)) "\"" (add-token (advance scanner) :string (string buff)) "\\" (let [next (next-char scanner) scanner (if (= next "\n") @@ -314,7 +315,7 @@ (put scanner :start (get scanner :current))) (defn scan [source &opt input] - (default input "") + (default input :input) (defn recur [scanner] (if (at-end? scanner) (let [scanner (add-token (add-token scanner :break) :eof)]