]> Pileus Git - ~andy/rhawk/blob - json.awk
Fix color loading
[~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, line, 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]+"
124         table["line"]  = "^\n"
125         i = 0;
126         line = 1;
127         while (length(str) > 0) {
128                 found = 0
129                 for (type in table) {
130                         #print "match: str=["str"] type="type" regex=/"table[type]"/"
131                         if (match(str, table[type], items) > 0) {
132                                 #print "       len="RLENGTH" item=["items[0]"]"
133                                 if (type == "line")
134                                         line++;
135                                 if (type == "term")
136                                         type = items[0]
137                                 if (type != "space" && type != "line") {
138                                         tokens[i]["line"] = line
139                                         tokens[i]["type"] = type
140                                         tokens[i]["text"] = items[0]
141                                         i++
142                                 }
143                                 str = substr(str, RLENGTH+1)
144                                 found = 1
145                                 break
146                         }
147                 }
148                 if (!found) {
149                         debug("line " line ": error tokenizing")
150                         return 0
151                 }
152         }
153         return i
154
155         #for (i = 0; i < length(tokens); i++)
156         #       printf "%-3s %-5s [%s]\n", i":",
157         #               tokens[i]["type"], tokens[i]["text"]
158 }
159
160 function json_parse_value(tokens, i, value, key,   line, type, text)
161 {
162         if (!i    ) i = 0;
163         if (!depth) depth = 0
164         depth++
165         line = tokens[i]["line"]
166         type = tokens[i]["type"]
167         text = tokens[i]["text"]
168         #printf "parse %d: i=%-2d type=%-3s text=%s\n", depth, i, type, text
169         switch (type) {
170                 case "{":   i = json_parse_object(tokens, i, value, key); break
171                 case "[":   i = json_parse_array(tokens,  i, value, key); break
172                 case "str": i = json_parse_string(tokens, i, value, key); break
173                 case "num": i = json_parse_number(tokens, i, value, key); break
174                 case "var": i = json_parse_var(tokens,    i, value, key); break
175                 default:    debug("line "line": error type="type" text="text); return 0
176         }
177         depth--
178         return i
179 }
180
181 function json_parse_object(tokens, i, value, key,   object, k, v)
182 {
183         if (tokens[i++]["text"] != "{")
184                 return 0;
185
186         if (tokens[i]["text"] != "}") {
187                 do {
188                         delete k
189                         delete v
190                         if (!(i=json_parse_value(tokens, i, k, 0)))
191                                 return 0
192                         if (tokens[i++]["text"] != ":")
193                                 return 0
194                         if (!(i=json_parse_value(tokens, i, v, 0)))
195                                 return 0
196                         json_copy(object, k[0], v[0]) 
197                 } while (tokens[i++]["text"] == ",")
198                 i--
199         }
200
201         if (tokens[i++]["text"] != "}")
202                 return 0;
203
204         json_copy(value, key, object)
205         return i
206 }
207
208 function json_parse_array(tokens, i, value, key,   array, k, v)
209 {
210         if (tokens[i++]["text"] != "[")
211                 return 0;
212
213         if (tokens[i]["text"] != "]") {
214                 do {
215                         delete v
216                         if (!(i=json_parse_value(tokens, i, v, 0)))
217                                 return 0
218                         json_copy(array, k++, v[0]) 
219                 } while (tokens[i++]["text"] == ",")
220                 i--
221         }
222
223         if (tokens[i++]["text"] != "]")
224                 return 0;
225
226         json_copy(value, key, array)
227         return i
228 }
229
230 function json_parse_number(tokens, i, value, key,   text)
231 {
232         text = tokens[i++]["text"]
233         json_copy(value, key, text + 0)
234         #print "parse_number: " (text + 0)
235         return i
236 }
237
238 function json_parse_string(tokens, i, value, key,   text)
239 {
240         text = tokens[i++]["text"]
241         len  = length(text);
242         text = len == 2 ? "" : substr(text, 2, len-2)
243         json_copy(value, key, text)
244         #print "parse_string: [" text "]"
245         return i
246 }
247
248 function json_parse_var(tokens, i, value, key,   text, null)
249 {
250         switch (tokens[i++]["text"]) {
251                 case "true":  json_copy(value, key, 1==1); break;
252                 case "false": json_copy(value, key, 1==2); break;
253                 case "null":  json_copy(value, key, null); break;
254         }
255         #print "parse_var: " text " -> " text == "true"
256         return i
257 }
258
259
260 # Nice API?
261 function json_load(file, var,   line, text, tokens, data, key)
262 {
263         while ((getline line < file) > 0)
264                 text = text line
265         close(file)
266         if (!json_tokenize(text, tokens))
267                 return ""
268         if (!json_parse_value(tokens, 0, data, 0))
269                 return ""
270         if (!isarray(data[0]))
271                 return data[0]
272         for (key in data[0])
273                 json_copy(var, key, data[0][key])
274         return 1
275 }
276
277 function json_save(file, var,   cmd, tmp)
278 {
279         cmd = "mktemp " file ".XXX"
280         cmd | getline tmp
281         close(cmd)
282         print json_write_value(var) > tmp
283         close(tmp)
284         system("mv " tmp " " file)
285 }
286
287
288 # Test functions
289 function json_test_write()
290 {
291         num      = 42
292         str      = "hello, world"
293         arr[0]   = "zero"
294         arr[1]   = "one"
295         arr[2]   = "two"
296         obj["A"] = "a!"
297         obj["B"] = "b!"
298         obj["C"] = "c!"
299         json_copy(mix, "number", num);
300         json_copy(mix, "str",    str);
301         json_copy(mix, "arr",    arr);
302         json_copy(mix, "obj",    obj);
303         json_copy(dub, "first",  mix);
304         json_copy(dub, "second", mix);
305         print json_write_value(num)
306         print json_write_value(str)
307         print json_write_value(arr)
308         print json_write_value(obj)
309         print json_write_value(mix)
310         print json_write_value(dub)
311 }
312
313 function json_test_read()
314 {
315         json_tokenize("[8, \"abc\", 9]", tokens)
316         json_parse_value(tokens, 0, array, 0)
317         print json_write_value(array[0])
318
319         json_tokenize("{\"abc\": 1, \"def\": 2}", tokens)
320         json_parse_value(tokens, 0, array, 0)
321         print json_write_value(array[0])
322
323         json = "{ \"first\":  { \"arr\":    [ \"\",             \n" \
324                "                              -1,               \n" \
325                "                              1.2,              \n" \
326                "                              true,             \n" \
327                "                              false,            \n" \
328                "                              null    ],        \n" \
329                "                \"number\": 42,                 \n" \
330                "                \"eobj\":   [ ],                \n" \
331                "                \"earr\":   { },                \n" \
332                "                \"obj\":    { \"A\": \"a!\",    \n" \
333                "                              \"B\": \"b!\",    \n" \
334                "                              \"C\": \"c!\" },  \n" \
335                "                \"str\":    \"hello, world\" }, \n" \
336                "  \"second\": { \"arr\":    [ \"zero\",         \n" \
337                "                              \"one\",          \n" \
338                "                              \"two\" ],        \n" \
339                "                \"number\": 42,                 \n" \
340                "                \"obj\":    { \"A\": \"a!\",    \n" \
341                "                              \"B\": \"b!\",    \n" \
342                "                              \"C\": \"c!\" },  \n" \
343                "                \"str\":    \"hello, world\" } }\n"
344
345         json_tokenize(json, tokens)
346         json_parse_value(tokens, 0, array, 0)
347         print json_write_value(array[0])
348 }
349
350 function json_test_files()
351 {
352         print "load: [" json_load("email.txt", mail) "]"
353         print "mail: "  json_write_value(mail, "      ")
354         mail["andy753421"] = "andy753421@gmail.com"
355         mail["andy"]       = "andy@gmail.com"
356         mail["somebody"]   = "foo@example.com"
357         print "mail: "  json_write_value(mail, "      ")
358         print "save: [" json_save("email.txt", mail) "]"
359 }
360
361 # Main
362 BEGIN {
363         #json_test_write()
364         #json_test_read()
365         #json_test_files()
366 }