start work on a recursive descent parser
This commit is contained in:
parent
0de9f90f27
commit
3d570beb45
|
@ -387,6 +387,7 @@ if (:)foo then :bar else :baz
|
||||||
(comment
|
(comment
|
||||||
|
|
||||||
Okay, so I'm trying to figure out how to manage panics in this declarative (not recursive-descent) parser.
|
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
|
||||||
)
|
)
|
||||||
|
|
109
janet/recursive.janet
Normal file
109
janet/recursive.janet
Normal file
|
@ -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))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
|
@ -192,12 +192,13 @@
|
||||||
[scanner]
|
[scanner]
|
||||||
(print "Adding string")
|
(print "Adding string")
|
||||||
(defn recur [scanner buff interpolate?]
|
(defn recur [scanner buff interpolate?]
|
||||||
(print "\n printing a thing from add-stirng" buff)
|
|
||||||
(let [char (current-char scanner)]
|
(let [char (current-char scanner)]
|
||||||
(case char
|
(case char
|
||||||
"{" (recur (advance scanner) (buffer/push buff char) true)
|
"{" (recur (advance scanner) (buffer/push buff char) true)
|
||||||
# allow multiline strings
|
# allow multiline strings
|
||||||
"\n" (recur (update (advance scanner) :line inc) (buffer/push buff char) interpolate?)
|
"\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))
|
"\"" (add-token (advance scanner) :string (string buff))
|
||||||
"\\" (let [next (next-char scanner)
|
"\\" (let [next (next-char scanner)
|
||||||
scanner (if (= next "\n")
|
scanner (if (= next "\n")
|
||||||
|
@ -314,7 +315,7 @@
|
||||||
(put scanner :start (get scanner :current)))
|
(put scanner :start (get scanner :current)))
|
||||||
|
|
||||||
(defn scan [source &opt input]
|
(defn scan [source &opt input]
|
||||||
(default input "")
|
(default input :input)
|
||||||
(defn recur [scanner]
|
(defn recur [scanner]
|
||||||
(if (at-end? scanner)
|
(if (at-end? scanner)
|
||||||
(let [scanner (add-token (add-token scanner :break) :eof)]
|
(let [scanner (add-token (add-token scanner :break) :eof)]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user