# [ʞ] lib/json.janet
# ~ lexi hale <lexi@hale.su>
# 🄯 AGPLv3
# ? a quick-and-dirty json parser good enough to
# make sense of extension_api.json and no better
# > (import :/lib/json)
(defn- mk-json-obj [& body]
(tabseq [[key val] :in body]
key val))
(defn- json-kw [kw & pat]
~(* (constant ,kw) ,;pat))
(def parse-fail {
:->string (fn [me]
(string/format "parse error %s at %d:%d"
(me :kind) (me :line) (me :col)))
})
(defn- fail [kind]
(defn mk [line col]
(struct/with-proto parse-fail
:kind kind
:line line
:col col))
~(error (cmt (* (line) (column)) ,mk)))
(def pattern (peg/compile ~{
:bool (+ ,(json-kw true "true")
,(json-kw false "false"))
:null ,(json-kw :null "null")
:val (* :ws (+ :obj :str :num
:ary :bool :null
,(fail :bad-val)) :ws)
:ary (group
(+ "[]"
(* "[" (+
(* :val
(any (* "," :val))
(? (* "," :ws)))
:wso
,(fail :bad-array))
"]" )))
:obj (cmt (* "{" :ws
(? (*
:pair
(any (* :ws "," :ws :pair))
:ws (? ",") :ws))
:ws "}") ,mk-json-obj)
:pair (group (* :str :ws ":" :ws :val))
:str (* `"` (<- (any (+ `\"` (if-not `"` 1)))) `"`)
# numerals #
:dec-digit (range "09")
:dec-int-lit (some :dec-digit)
:dec-lit-base (+ (* :dec-int-lit "." (? :dec-int-lit))
(* "." :dec-int-lit)
:dec-int-lit)
:dec-lit (+ (* :dec-lit-base "e" :dec-lit-base)
:dec-lit-base)
:hex-digit (+ :dec-digit
(range "af")
(range "AF"))
:hex-lit (* "0x" (some :hex-digit))
:num (number (* (+ "-" "+" "")
(+ :hex-lit :dec-lit)))
# misc #
:ws-char (+ " " "\t" "\n")
:wso (some :ws-char)
:ws (any :ws-char)
:main :val
}))
(defn parse [body]
(try (let [parsed (peg/match pattern body)]
(when (= nil parsed)
(error :unknown))
(first parsed))
([e] (error (match e
:unknown "mystery error"
_ (:->string e))))))
(defn main [& argv]
(pp (parse ``
{"test": 123,
"array": [{}, true, {"no":"yes", }]}
``)))