|
| 1 | +(ns pixie.parser.json |
| 2 | + (:require [pixie.parser :refer :all] |
| 3 | + [pixie.stdlib :as std])) |
| 4 | + |
| 5 | + |
| 6 | + |
| 7 | +;; Basic numeric parser. Supports integers (1, 2, 43), decimals (0.1, 1.1, 1000.11) and exponents (1e42, 1E-2) |
| 8 | +(defparser NumberParser [] |
| 9 | + NUMBER (and (maybe \-) |
| 10 | + -> sign |
| 11 | + |
| 12 | + (or (and |
| 13 | + (parse-if (set "123456789")) -> first |
| 14 | + (zero+chars digits) -> rest |
| 15 | + <- (str first rest)) |
| 16 | + (and \0 <- "0")) |
| 17 | + -> integer-digits |
| 18 | + |
| 19 | + (maybe (and \. |
| 20 | + (one+chars digits) -> digits |
| 21 | + <- digits)) |
| 22 | + -> fraction-digits |
| 23 | + |
| 24 | + |
| 25 | + (maybe (and (parse-if (set "eE")) |
| 26 | + (maybe (parse-if (set "-+"))) -> exp-sign |
| 27 | + (one+chars digits) -> exp-digits |
| 28 | + <- [(std/or exp-sign "") exp-digits])) |
| 29 | + -> exp-data |
| 30 | + |
| 31 | + <- (std/read-string (str (std/or sign "") |
| 32 | + integer-digits |
| 33 | + (if fraction-digits (str "." fraction-digits) "") |
| 34 | + (if exp-data (apply str "E" exp-data) ""))))) |
| 35 | + |
| 36 | +(def valid-escape-chars |
| 37 | + {\\ \\ |
| 38 | + \" \" |
| 39 | + \/ \/ |
| 40 | + \b \backspace |
| 41 | + \f \formfeed |
| 42 | + \n \newline |
| 43 | + \r \return |
| 44 | + \t \tab}) |
| 45 | + |
| 46 | + |
| 47 | +;; Defines a JSON escaped string parser. Supports all the normal \n \f \r stuff as well |
| 48 | +;; as \uXXXX unicode characters |
| 49 | +(defparser EscapedStringParser [] |
| 50 | + CHAR (or (and \\ |
| 51 | + (one-of valid-escape-chars) -> char |
| 52 | + <- (valid-escape-chars char)) |
| 53 | + |
| 54 | + (and \\ |
| 55 | + \u |
| 56 | + digits -> d1 |
| 57 | + digits -> d2 |
| 58 | + digits -> d3 |
| 59 | + digits -> d4 |
| 60 | + <- (char (std/read-string (str "0x" d1 d2 d3 d4)))) |
| 61 | + |
| 62 | + (parse-if #(not= % \"))) |
| 63 | + |
| 64 | + STRING (and \" |
| 65 | + (zero+chars CHAR) -> s |
| 66 | + \" |
| 67 | + <- s)) |
| 68 | + |
| 69 | +;; Basic JSON parser |
| 70 | +(defparser JSONParser [NumberParser EscapedStringParser] |
| 71 | + |
| 72 | + NULL (sequence "null" <- nil) |
| 73 | + TRUE (sequence "true" <- true) |
| 74 | + FALSE (sequence "false" <- false) |
| 75 | + ARRAY (and \[ |
| 76 | + (eat whitespace) |
| 77 | + (zero+ (and ENTRY -> e |
| 78 | + (maybe \,) |
| 79 | + <- e)) -> items |
| 80 | + (eat whitespace) |
| 81 | + (eat whitespace) |
| 82 | + \] |
| 83 | + <- items) |
| 84 | + MAP-ENTRY (and (eat whitespace) |
| 85 | + STRING -> key |
| 86 | + (eat whitespace) |
| 87 | + \: |
| 88 | + ENTRY -> value |
| 89 | + (maybe \,) |
| 90 | + <- [key value]) |
| 91 | + MAP (and \{ |
| 92 | + (zero+ MAP-ENTRY) -> items |
| 93 | + (eat whitespace) |
| 94 | + \} |
| 95 | + <- (apply hashmap (apply concat items))) |
| 96 | + ENTRY (and |
| 97 | + (eat whitespace) |
| 98 | + (or NUMBER MAP STRING NULL TRUE FALSE ARRAY) -> val |
| 99 | + (eat whitespace) |
| 100 | + <- val) |
| 101 | + ENTRY-AT-END (and ENTRY -> e |
| 102 | + (eat whitespace) |
| 103 | + end |
| 104 | + <- e)) |
| 105 | + |
| 106 | +(defn read-string [s] |
| 107 | + (let [c (string-cursor s) |
| 108 | + result ((:ENTRY-AT-END JSONParser) c)] |
| 109 | + (if (failure? result) |
| 110 | + (println (current c) (snapshot c)) |
| 111 | + result))) |
0 commit comments