]> Pileus Git - ~andy/rhawk/blob - json.awk
Fix broken playto scores
[~andy/rhawk] / json.awk
1 # value:
2 #       object: { string : value, .. }
3 #       array:  [ value, .. ]
4 #       string: "char .."
5 #       number: (builtin)
6 #       true
7 #       false
8 #       null
9
10 # chars:
11 #       any-Unicode-character-except-"-or-\-or-control-character
12 #       \"
13 #       \\
14 #       \/
15 #       \b
16 #       \f
17 #       \n
18 #       \r
19 #       \t
20 #       \u four-hex-digits
21
22 # Helpers
23 function json_gettype(value,   key, sort, n, i)
24 {
25         if (isarray(value)) { 
26                 for (key in value)
27                         if (isarray(key))
28                                 return "error"
29                 n = asorti(value, sort)
30                 for (i = 0; i < n; i++)
31                         if (sort[i+1] != i)
32                                 return "object"
33                 return "array"
34         } else {
35                 if (value == 0 && value == "")
36                         return "null"
37                 if (value == value + 0)
38                         return "number"
39                 if (value == value "")
40                         return "string"
41                 return "error"
42         }
43 }
44
45 function json_join(array, sep,   i, str)
46 {
47         str = array[0]
48         for (i = 1; i < length(array); i++)
49                 str = str sep array[i]
50         return str
51 }
52
53 function json_copy(dst, to, src,   key)
54 {
55         if (isarray(src)) {
56                 delete dst[to]
57                 for (key in src) {
58                         dst[to][key]
59                         json_copy(dst[to], key, src[key])
60                 }
61         } else {
62                 dst[to] = src
63         }
64 }
65
66
67 # Write functions
68 function json_write_value(value, pad)
69 {
70         switch (json_gettype(value)) {
71                 case "object": return json_write_object(value, pad)
72                 case "array":  return json_write_array(value, pad)
73                 case "number": return json_write_number(value)
74                 case "string": return json_write_string(value)
75                 case "null":   return "null"
76                 default:       return "error"
77         }
78 }
79
80 function json_write_object(object, pad,   n, i, sort, key, val, data, len, max)
81 {
82         n = asorti(object, sort)
83         for (i = 0; i < n; i++) {
84                 key = json_write_string(sort[i+1])
85                 if (length(key) > max)
86                         max = length(key)
87         }
88         for (i = 0; i < n; i++) {
89                 key = json_write_string(sort[i+1])
90                 val = json_write_value(object[sort[i+1]],
91                         sprintf("%s  %"max"s  ", pad, ""))
92                 data[i] = sprintf("%-"(max+1)"s %s", key":", val)
93         }
94         return "{ " json_join(data, ",\n  " pad) " }"
95 }
96
97 function json_write_array(array, pad,   i, data)
98 {
99         for (i = 0; i < length(array); i++)
100                 data[i] = json_write_value(array[i], pad "  ")
101         return "[ " json_join(data, ",\n  " pad) " ]"
102 }
103
104 function json_write_number(number)
105 {
106         return "" number ""
107 }
108
109 function json_write_string(string)
110 {
111         # todo: special characters
112         return "\"" string "\""
113 }
114
115
116 # Read functions
117 function json_tokenize(str, tokens,   i, items, table, type, found)
118 {
119         table["term"]  = "^[\\[\\]{}:,]"
120         table["str"]   = "^\"[^\"]*\""
121         table["num"]   = "^[+-]?[0-9]+(.[0-9]+)?"
122         table["var"]   = "^(true|false|null)"
123         table["space"] = "^[ \\t\\n]+"
124         i = 0
125         while (length(str) > 0) {
126                 found = 0
127                 for (type in table) {
128                         #print "match: str=["str"] type="type" regex=/"table[type]"/"
129                         if (match(str, table[type], items) > 0) {
130                                 #print "       len="RLENGTH" item=["items[0]"]"
131                                 if (type == "term")
132                                         type = items[0]
133                                 if (type != "space") {
134                                         tokens[i]["type"] = type
135                                         tokens[i]["text"] = items[0]
136                                         i++
137                                 }
138                                 str = substr(str, RLENGTH+1)
139                                 found = 1
140                                 break
141                         }
142                 }
143                 if (!found) {
144                         debug("error tokenizing")
145                         return 0
146                 }
147         }
148         return i
149
150         #for (i = 0; i < length(tokens); i++)
151         #       printf "%-3s %-5s [%s]\n", i":",
152         #               tokens[i]["type"], tokens[i]["text"]
153 }
154
155 function json_parse_value(tokens, i, value, key,   type, text)
156 {
157         if (!i    ) i = 0;
158         if (!depth) depth = 0
159         depth++
160         type = tokens[i]["type"]
161         text = tokens[i]["text"]
162         #printf "parse %d: i=%-2d type=%-3s text=%s\n", depth, i, type, text
163         switch (type) {
164                 case "{":   i = json_parse_object(tokens, i, value, key); break
165                 case "[":   i = json_parse_array(tokens,  i, value, key); break
166                 case "str": i = json_parse_string(tokens, i, value, key); break
167                 case "num": i = json_parse_number(tokens, i, value, key); break
168                 case "var": i = json_parse_var(tokens,    i, value, key); break
169                 default:    debug("error: type="type" text="text); return 0
170         }
171         depth--
172         return i
173 }
174
175 function json_parse_object(tokens, i, value, key,   object, k, v)
176 {
177         if (tokens[i++]["text"] != "{")
178                 return 0;
179
180         do {
181                 delete k
182                 delete v
183                 if (!(i=json_parse_value(tokens, i, k, 0)))
184                         return 0
185                 if (tokens[i++]["text"] != ":")
186                         return 0
187                 if (!(i=json_parse_value(tokens, i, v, 0)))
188                         return 0
189                 json_copy(object, k[0], v[0]) 
190         } while (tokens[i++]["text"] == ",")
191         i--
192
193         if (tokens[i++]["text"] != "}")
194                 return 0;
195
196         json_copy(value, key, object)
197         return i
198 }
199
200 function json_parse_array(tokens, i, value, key,   array, k, v)
201 {
202         #print "parse_array: .."
203         if (tokens[i++]["text"] != "[")
204                 return 0;
205
206         do {
207                 delete v
208                 if (!(i=json_parse_value(tokens, i, v, 0)))
209                         return 0
210                 json_copy(array, k++, v[0]) 
211         } while (tokens[i++]["text"] == ",")
212         i--
213
214         if (tokens[i++]["text"] != "]")
215                 return 0;
216
217         json_copy(value, key, array)
218         return i
219 }
220
221 function json_parse_number(tokens, i, value, key,   text)
222 {
223         text = tokens[i++]["text"]
224         json_copy(value, key, text + 0)
225         #print "parse_number: " (text + 0)
226         return i
227 }
228
229 function json_parse_string(tokens, i, value, key,   text)
230 {
231         text = tokens[i++]["text"]
232         len  = length(text);
233         text = len == 2 ? "" : substr(text, 2, len-2)
234         json_copy(value, key, text)
235         #print "parse_string: [" text "]"
236         return i
237 }
238
239 function json_parse_var(tokens, i, value, key,   text, null)
240 {
241         switch (tokens[i++]["text"]) {
242                 case "true":  json_copy(value, key, 1==1); break;
243                 case "false": json_copy(value, key, 1==2); break;
244                 case "null":  json_copy(value, key, null); break;
245         }
246         #print "parse_var: " text " -> " text == "true"
247         return i
248 }
249
250
251 # Nice API?
252 function json_load(file, var,   line, text, tokens, data, key)
253 {
254         while ((getline line < file) > 0)
255                 text = text line
256         close(file)
257         if (!json_tokenize(text, tokens))
258                 return ""
259         if (!json_parse_value(tokens, 0, data, 0))
260                 return ""
261         if (!isarray(data[0]))
262                 return data[0]
263         for (key in data[0])
264                 json_copy(var, key, data[0][key])
265         return 1
266 }
267
268 function json_save(file, var,   cmd, tmp)
269 {
270         cmd = "mktemp " file ".XXX"
271         cmd | getline tmp
272         close(cmd)
273         print json_write_value(var) > tmp
274         close(tmp)
275         system("mv " tmp " " file)
276 }
277
278
279 # Test functions
280 function json_test_write()
281 {
282         num      = 42
283         str      = "hello, world"
284         arr[0]   = "zero"
285         arr[1]   = "one"
286         arr[2]   = "two"
287         obj["A"] = "a!"
288         obj["B"] = "b!"
289         obj["C"] = "c!"
290         json_copy(mix, "number", num);
291         json_copy(mix, "str",    str);
292         json_copy(mix, "arr",    arr);
293         json_copy(mix, "obj",    obj);
294         json_copy(dub, "first",  mix);
295         json_copy(dub, "second", mix);
296         print json_write_value(num)
297         print json_write_value(str)
298         print json_write_value(arr)
299         print json_write_value(obj)
300         print json_write_value(mix)
301         print json_write_value(dub)
302 }
303
304 function json_test_read()
305 {
306         json_tokenize("[8, \"abc\", 9]", tokens)
307         json_parse_value(tokens, 0, array, 0)
308         print json_write_value(array[0])
309
310         json_tokenize("{\"abc\": 1, \"def\": 2}", tokens)
311         json_parse_value(tokens, 0, array, 0)
312         print json_write_value(array[0])
313
314         json = "{ \"first\":  { \"arr\":    [ \"\",             " \
315                "                              -1,               " \
316                "                              1.2,              " \
317                "                              true,             " \
318                "                              false,            " \
319                "                              null    ],        " \
320                "                \"number\": 42,                 " \
321                "                \"obj\":    { \"A\": \"a!\",    " \
322                "                              \"B\": \"b!\",    " \
323                "                              \"C\": \"c!\" },  " \
324                "                \"str\":    \"hello, world\" }, " \
325                "  \"second\": { \"arr\":    [ \"zero\",         " \
326                "                              \"one\",          " \
327                "                              \"two\" ],        " \
328                "                \"number\": 42,                 " \
329                "                \"obj\":    { \"A\": \"a!\",    " \
330                "                              \"B\": \"b!\",    " \
331                "                              \"C\": \"c!\" },  " \
332                "                \"str\":    \"hello, world\" } }"
333
334         json_tokenize(json, tokens)
335         json_parse_value(tokens, 0, array, 0)
336         print json_write_value(array[0])
337 }
338
339 function json_test_files()
340 {
341         print "load: [" json_load("email.txt", mail) "]"
342         print "mail: "  json_write_value(mail, "      ")
343         mail["andy753421"] = "andy753421@gmail.com"
344         mail["andy"]       = "andy@gmail.com"
345         mail["somebody"]   = "foo@example.com"
346         print "mail: "  json_write_value(mail, "      ")
347         print "save: [" json_save("email.txt", mail) "]"
348 }
349
350 # Main
351 BEGIN {
352         #json_test_write()
353         #json_test_read()
354         #json_test_files()
355 }