start work on a recursive descent parser

This commit is contained in:
Scott Richmond 2024-04-28 18:13:49 -04:00
parent 0de9f90f27
commit 3d570beb45
3 changed files with 115 additions and 4 deletions

View File

@ -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
View 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))
)

View File

@ -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)]