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
|
||||
|
||||
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]
|
||||
(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)]
|
||||
|
|
Loading…
Reference in New Issue
Block a user