finally get when clauses & forms right

This commit is contained in:
Scott Richmond 2024-05-08 17:14:51 -04:00
parent 942f55fb39
commit 4f16cf5cb0

View File

@ -116,8 +116,8 @@
(defn- expect-ret
"Same as expect, but captures the error, returning it as a value"
[parser type]
(try (expect parser type) ([e] e)))
[parser type & types]
(try (expect parser type ;types) ([e] e)))
(defn- capture
"Applies the parse function to the parser, returning the parsed AST. If there is a panic, captures the panic and returns it as a value."
@ -126,13 +126,13 @@
(defn- accept-one
"Accepts a single token of passed type, advancing the parser if a match, doing nothing if not."
[parser type]
(if (check parser type) (advance parser)))
[parser type & types]
(if (check parser type ;types) (advance parser)))
(defn- accept-many
"Accepts any number of tokens of a passed type, advancing the parser on match until there are no more matches. Does nothing on no match."
[parser type]
(while (check parser type) (advance parser)))
[parser type & types]
(while (check parser type ;types) (advance parser)))
# atoms
(defn- bool [parser]
@ -310,6 +310,8 @@
(advance parser)
ast)
### patterns
### conditional forms
# if {simple} then {nonbinding} else {nonbinding}
(defn- iff [parser]
@ -328,42 +330,39 @@
(array/push (ast :data) (capture nonbinding parser))
ast)
### XXX: We've got an off-by-one error here
# We're expecting a terminator, we panic until we get to a terminator, and we then return back to something that expects a terminator, and now we've started parsing again *at the terminator*
(defn- terminator [parser]
(if-not (terminates? parser)
# this line panics, captures the panic, advances the parser, and re-throws the error
(try (panic parser "expected terminator") ([e] (advance parser) (error e))))
# this line panics, captures the panic, advances the parser, and re-throws the error; solves an off-by-one error
(panic parser "expected terminator"))
(advance parser)
(while (terminates? parser) (advance parser)))
# {simple} -> {nonbinding} {terminator}
(defn- when-clause [parser]
(def clause @[])
(print "parsing lhs: " (-> parser current type))
(array/push clause (capture simple parser))
(print "parsing arrow")
(if-let [err (expect-ret parser :arrow)]
(array/push clause err)
(advance parser))
(print "accepting newlines")
(try
(do
(def lhs (simple parser))
(expect parser :arrow)
(advance parser)
(accept-many parser :newline)
(print "parsing rhs")
(array/push clause (nonbinding parser))
(print "parsing terminator")
(try (terminator parser) ([e] (array/push clause e)))
clause)
(def rhs (nonbinding parser))
(terminator parser)
[lhs rhs])
([err]
(advance parser) # consume the breaking token
(accept-many parser :newline :semicolon :break) # ...and any additional ones
err)))
(defn- whenn [parser]
(def ast {:type :when :data @[] :origin (current parser)})
(advance parser) # consume cond
(advance parser) # consume when
(if-let [err (expect-ret parser :lbrace)]
(do
(array/push (ast :data) err)
(break ast))
(break ast)) # early return; just bail if we don't have {
(advance parser))
(accept-many parser :newline)
(while (not (check parser :rbrace :eof))
(while (not (check parser :rbrace :eof)) # make sure we don't roll past eof
(array/push (ast :data) (capture when-clause parser)))
(advance parser)
ast)
@ -515,8 +514,10 @@
(do
#(comment
(def source `when {
a -> b
foo -> bar quux
bar -> baz
-> baz
c -> d
}
`)
(def scanned (s/scan source))