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