# [ʞ] 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", }]}
			   ``)))
