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