--- /dev/null
+# value:
+# object: { string : value, .. }
+# array: [ value, .. ]
+# string: "char .."
+# number: (builtin)
+# true
+# false
+# null
+#
+# chars:
+# any-Unicode-character-except-"-or-\-or-control-character
+# \"
+# \\
+# \/
+# \b
+# \f
+# \n
+# \r
+# \t
+# \u four-hex-digits
+
+
+# Helpers
+function json_gettype(value, key, sort, n, i)
+{
+ if (isarray(value)) {
+ for (key in value)
+ if (isarray(key))
+ return "error"
+ n = asorti(value, sort)
+ for (i = 0; i < n; i++)
+ if (sort[i+1] != i)
+ return "object"
+ return "array"
+ } else {
+ if (value == value + 0)
+ return "number"
+ if (value == value "")
+ return "string"
+ return "error"
+ }
+}
+
+function json_join(array, sep, i, str)
+{
+ str = array[0]
+ for (i = 1; i < length(array); i++)
+ str = str sep array[i]
+ return str
+}
+
+function json_copy(dst, to, src, key)
+{
+ if (isarray(src)) {
+ delete dst[to]
+ for (key in src) {
+ dst[to][key]
+ json_copy(dst[to], key, src[key])
+ }
+ } else {
+ dst[to] = src
+ }
+}
+
+
+# Write functions
+function json_write_value(value, pad)
+{
+ switch (json_gettype(value)) {
+ case "object": return json_write_object(value, pad)
+ case "array": return json_write_array(value, pad)
+ case "number": return json_write_number(value)
+ case "string": return json_write_string(value)
+ default: return "error"
+ }
+}
+
+function json_write_object(object, pad, n, i, sort, key, val, data, len, max)
+{
+ n = asorti(object, sort)
+ for (i = 0; i < n; i++) {
+ key = json_write_string(sort[i+1])
+ if (length(key) > max)
+ max = length(key)
+ }
+ for (i = 0; i < n; i++) {
+ key = json_write_string(sort[i+1])
+ val = json_write_value(object[sort[i+1]],
+ sprintf("%s %"max"s ", pad, ""))
+ data[i] = sprintf("%-"(max+1)"s %s", key":", val)
+ }
+ return "{ " json_join(data, ",\n " pad) " }"
+}
+
+function json_write_array(array, pad, i, data)
+{
+ for (i = 0; i < length(array); i++)
+ data[i] = json_write_value(array[i], pad " ")
+ return "[ " json_join(data, ",\n " pad) " ]"
+}
+
+function json_write_number(number)
+{
+ return "" number ""
+}
+
+function json_write_string(string)
+{
+ # todo: special characters
+ return "\"" string "\""
+}
+
+
+# Read functions
+function json_tokenize(str, tokens, i, items, table, type, found)
+{
+ table["term"] = "^[\\[\\]{}:,]"
+ table["str"] = "^\"[^\"]*\""
+ table["num"] = "^[0-9]+"
+ table["var"] = "^(true|false|null)"
+ table["space"] = "^[ \\t\\n]+"
+ i = 0
+ while (length(str) > 0) {
+ found = 0
+ for (type in table) {
+ #print "match: str=["str"] type="type" regex=/"table[type]"/"
+ if (match(str, table[type], items) > 0) {
+ #print " len="RLENGTH" item=["items[0]"]"
+ if (type == "term")
+ type = items[0]
+ if (type != "space") {
+ tokens[i]["type"] = type
+ tokens[i]["text"] = items[0]
+ i++
+ }
+ str = substr(str, RLENGTH+1)
+ found = 1
+ break
+ }
+ }
+ if (!found) {
+ debug("error tokenizing")
+ return 0
+ }
+ }
+ return i
+
+ #for (i = 0; i < length(tokens); i++)
+ # printf "%-3s %-5s [%s]\n", i":",
+ # tokens[i]["type"], tokens[i]["text"]
+}
+
+function json_parse_value(tokens, i, value, key, type, text)
+{
+ if (!i ) i = 0;
+ if (!depth) depth = 0
+ depth++
+ type = tokens[i]["type"]
+ text = tokens[i]["text"]
+ #printf "parse %d: i=%-2d type=%-3s text=%s\n", depth, i, type, text
+ switch (type) {
+ case "{": i = json_parse_object(tokens, i, value, key); break
+ case "[": i = json_parse_array(tokens, i, value, key); break
+ case "str": i = json_parse_string(tokens, i, value, key); break
+ case "num": i = json_parse_number(tokens, i, value, key); break
+ case "var": i = json_parse_var(tokens, i, value, key); break
+ default: debug("error: type="type" text="text); return 0
+ }
+ depth--
+ return i
+}
+
+function json_parse_object(tokens, i, value, key, object, k, v)
+{
+ if (tokens[i++]["text"] != "{")
+ return 0;
+
+ do {
+ delete k
+ delete v
+ if (!(i=json_parse_value(tokens, i, k, 0)))
+ return 0
+ if (tokens[i++]["text"] != ":")
+ return 0
+ if (!(i=json_parse_value(tokens, i, v, 0)))
+ return 0
+ json_copy(object, k[0], v[0])
+ } while (tokens[i++]["text"] == ",")
+ i--
+
+ if (tokens[i++]["text"] != "}")
+ return 0;
+
+ json_copy(value, key, object)
+ return i
+}
+
+function json_parse_array(tokens, i, value, key, array, k, v)
+{
+ #print "parse_array: .."
+ if (tokens[i++]["text"] != "[")
+ return 0;
+
+ do {
+ delete v
+ if (!(i=json_parse_value(tokens, i, v, 0)))
+ return 0
+ json_copy(array, k++, v[0])
+ } while (tokens[i++]["text"] == ",")
+ i--
+
+ if (tokens[i++]["text"] != "]")
+ return 0;
+
+ json_copy(value, key, array)
+ return i
+}
+
+function json_parse_number(tokens, i, value, key, text)
+{
+ text = tokens[i++]["text"]
+ json_copy(value, key, text + 0)
+ #print "parse_number: " (text + 0)
+ return i
+}
+
+function json_parse_string(tokens, i, value, key, text)
+{
+ text = tokens[i++]["text"]
+ text = substr(text, 2, length(text)-2)
+ json_copy(value, key, text)
+ #print "parse_string: " text
+ return i
+}
+
+function json_parse_var(tokens, i, value, key, text)
+{
+ text = tokens[i++]["text"]
+ json_copy(value, key, text == "true")
+ #print "parse_var: " text " -> " text == "true"
+ return i
+}
+
+
+# Nice API?
+function json_load(file, var, line, text, tokens, data, key)
+{
+ while ((getline line < file) > 0)
+ text = text line
+ close(file)
+ if (!json_tokenize(text, tokens))
+ return ""
+ if (!json_parse_value(tokens, 0, data, 0))
+ return ""
+ if (!isarray(data[0]))
+ return data[0]
+ for (key in data[0])
+ json_copy(var, key, data[0][key])
+}
+
+function json_save(file, var, cmd, tmp)
+{
+ cmd = "mktemp ." file ".XXX"
+ cmd | getline tmp
+ close(cmd)
+ print json_write_value(var) > tmp
+ close(tmp)
+ system("mv " tmp " " file)
+}
+
+
+# Test functions
+function json_test_write()
+{
+ num = 42
+ str = "hello, world"
+ arr[0] = "zero"
+ arr[1] = "one"
+ arr[2] = "two"
+ obj["A"] = "a!"
+ obj["B"] = "b!"
+ obj["C"] = "c!"
+ json_copy(mix, "number", num);
+ json_copy(mix, "str", str);
+ json_copy(mix, "arr", arr);
+ json_copy(mix, "obj", obj);
+ json_copy(dub, "first", mix);
+ json_copy(dub, "second", mix);
+ print json_write_value(num)
+ print json_write_value(str)
+ print json_write_value(arr)
+ print json_write_value(obj)
+ print json_write_value(mix)
+ print json_write_value(dub)
+}
+
+function json_test_read()
+{
+ json_tokenize("[8, \"abc\", 9]", tokens)
+ json_parse_value(tokens, 0, array, 0)
+ json_print write_value(array[0])
+
+ json_tokenize("{\"abc\": 1, \"def\": 2}", tokens)
+ json_parse_value(tokens, 0, array, 0)
+ json_print write_value(array[0])
+
+ json = "{ \"first\": { \"arr\": [ \"zero\", " \
+ " \"one\", " \
+ " \"two\" ], " \
+ " \"number\": 42, " \
+ " \"obj\": { \"A\": \"a!\", " \
+ " \"B\": \"b!\", " \
+ " \"C\": \"c!\" }, " \
+ " \"str\": \"hello, world\" }, " \
+ " \"second\": { \"arr\": [ \"zero\", " \
+ " \"one\", " \
+ " \"two\" ], " \
+ " \"number\": 42, " \
+ " \"obj\": { \"A\": \"a!\", " \
+ " \"B\": \"b!\", " \
+ " \"C\": \"c!\" }, " \
+ " \"str\": \"hello, world\" } }"
+
+ json_tokenize(json, tokens)
+ json_parse_value(tokens, 0, array, 0)
+ print json_write_value(array[0])
+}
+
+function json_test_files()
+{
+ print "load: [" json_load("email.txt", mail) "]"
+ print "mail: " json_write_value(mail, " ")
+ mail["andy753421"] = "andy753421@gmail.com"
+ mail["andy"] = "andy@gmail.com"
+ mail["somebody"] = "foo@example.com"
+ print "mail: " json_write_value(mail, " ")
+ print "save: [" json_save("email.txt", mail) "]"
+}
+
+# Main
+BEGIN {
+ #json_test_write()
+ #json_test_read()
+ #json_test_files()
+}