Creating a parser combinator library to parse JSON
Prev: many1, implemented | Contents | Next: JSON strings |
This was my definition for parsing a JSON number:
(= json-number (many1 json-number-char))
arc> (show-parse json-number "123abc") returning: (#\1 #\2 #\3) remaining: abc nil
But for the return value I want to get an Arc number, not just the list of parsed characters. Like with many1
, I want to modify the value returned by a parser, and so it’s time to make a function to do that:
(def on-result (f parser) (fn (p) (iflet (p2 r) (parser p) (return p2 (f r)))))
Now many1
can use on-result
:
(def many1 (parser) (on-result (fn ((r1 rs)) (cons r1 rs)) (seq parser (many parser))))
Which I can make a bit shorter with a macro:
(mac with-result (vars parser . body) `(on-result (fn (,vars) ,@body) ,parser))
(def many1 (parser) (with-result (r1 rs) (seq parser (many parser)) (cons r1 rs)))
For seq
specifically, perhaps I’ll often be working with the return value of each of the parsers in the sequence, so I might try a macro for that and see how often it’s used:
(mac with-seq (vars-parsers . body) (withs (ps (pair vars-parsers) vars (map car ps) parsers (map cadr ps)) `(on-result (fn (,vars) ,@body) (seq ,@parsers))))
(def many1 (parser) (with-seq (r1 parser rs (many parser)) (cons r1 rs)))
And now the implementation of json-number
:
(= json-number (with-result cs (many1 json-number-char) (coerce (string cs) 'num)))
arc> (show-parse json-number "123abc") returning: 123 remaining: abc nil
(= json-value (skipwhite:alt json-true json-false json-null json-number))
arc> (fromjson "45.6") 45.6
Prev: many1, implemented | Contents | Next: JSON strings |
Questions? Comments? Email me andrew.wilcox [at] gmail.com