awwx.ws

Creating a parser combinator library to parse JSON

Prev: many1, implementedContentsNext: JSON strings

JSON numbers, implemented

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, implementedContentsNext: JSON strings


Questions? Comments? Email me andrew.wilcox [at] gmail.com