X-Git-Url: http://pileus.org/git/?p=~andy%2Frhawk;a=blobdiff_plain;f=json.awk;h=cfb8eac56c680460b9e12d4c6d7a1e7c3f45a61c;hp=59b5eb5c5f7c53ae9f9b0af5762160bd96d38867;hb=HEAD;hpb=6fd3e11f6de61947f4f7c704b09ce77f827de4e2 diff --git a/json.awk b/json.awk index 59b5eb5..cfb8eac 100644 --- a/json.awk +++ b/json.awk @@ -1,3 +1,12 @@ +# usage: +# @include "json.awk" +# BEGIN { +# src["hello"] = "world" +# json_save("test.json", src) +# json_load("test.json", dst) +# print dst["hello"] +# } +# # value: # object: { string : value, .. } # array: [ value, .. ] @@ -19,7 +28,6 @@ # \t # \u four-hex-digits - # Helpers function json_gettype(value, key, sort, n, i) { @@ -33,6 +41,8 @@ function json_gettype(value, key, sort, n, i) return "object" return "array" } else { + if (value == 0 && value == "") + return "null" if (value == value + 0) return "number" if (value == value "") @@ -71,6 +81,7 @@ function json_write_value(value, pad) case "array": return json_write_array(value, pad) case "number": return json_write_number(value) case "string": return json_write_string(value) + case "null": return "null" default: return "error" } } @@ -106,29 +117,41 @@ function json_write_number(number) function json_write_string(string) { - # todo: special characters + gsub(/\\/, "\\\\", string) + gsub(/"/, "\\\"", string) + gsub(/\b/, "\\b", string) + gsub(/\f/, "\\f", string) + gsub(/\n/, "\\n", string) + gsub(/\r/, "\\r", string) + gsub(/\t/, "\\t", string) + return "\"" string "\"" } # Read functions -function json_tokenize(str, tokens, i, items, table, type, found) +function json_tokenize(str, tokens, i, line, items, table, type, found) { table["term"] = "^[\\[\\]{}:,]" - table["str"] = "^\"[^\"]*\"" - table["num"] = "^[0-9]+" + table["str"] = "^\"([^\\\\\"]|\\\\.)*\"" + table["num"] = "^[+-]?[0-9]+(.[0-9]+)?" table["var"] = "^(true|false|null)" - table["space"] = "^[ \\t\\n]+" - i = 0 + table["space"] = "^[ \\t]+" + table["line"] = "^\n" + i = 0; + line = 1; 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 == "line") + line++; if (type == "term") type = items[0] - if (type != "space") { + if (type != "space" && type != "line") { + tokens[i]["line"] = line tokens[i]["type"] = type tokens[i]["text"] = items[0] i++ @@ -139,22 +162,24 @@ function json_tokenize(str, tokens, i, items, table, type, found) } } if (!found) { - debug("error tokenizing") + debug("line " line ": 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"] + + return i } -function json_parse_value(tokens, i, value, key, type, text) +function json_parse_value(tokens, i, value, key, line, type, text) { if (!i ) i = 0; if (!depth) depth = 0 depth++ + line = tokens[i]["line"] type = tokens[i]["type"] text = tokens[i]["text"] #printf "parse %d: i=%-2d type=%-3s text=%s\n", depth, i, type, text @@ -164,7 +189,7 @@ function json_parse_value(tokens, i, value, key, type, text) 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 + default: debug("line "line": error type="type" text="text); return 0 } depth-- return i @@ -175,18 +200,20 @@ 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"] != "}") { + 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; @@ -197,17 +224,18 @@ function json_parse_object(tokens, i, value, key, object, k, v) 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"] != "]") { + 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; @@ -227,28 +255,38 @@ function json_parse_number(tokens, i, value, key, text) function json_parse_string(tokens, i, value, key, text) { text = tokens[i++]["text"] - text = substr(text, 2, length(text)-2) + len = length(text); + text = len == 2 ? "" : substr(text, 2, len-2) + + gsub(/\\\\/, "\\", text) + gsub(/\\"/, "\"", text) + gsub(/\\b/, "\b", text) + gsub(/\\f/, "\f", text) + gsub(/\\n/, "\n", text) + gsub(/\\r/, "\r", text) + gsub(/\\t/, "\t", text) + json_copy(value, key, text) - #print "parse_string: " text + #print "parse_string: [" text "]" return i } -function json_parse_var(tokens, i, value, key, text) +function json_parse_var(tokens, i, value, key, text, null) { - text = tokens[i++]["text"] - json_copy(value, key, text == "true") + switch (tokens[i++]["text"]) { + case "true": json_copy(value, key, 1==1); break; + case "false": json_copy(value, key, 1==2); break; + case "null": json_copy(value, key, null); break; + } #print "parse_var: " text " -> " text == "true" return i } -# Nice API? -function json_load(file, var, line, text, tokens, data, key) +# String API +function json_decode(string, var, tokens, data, key) { - while ((getline line < file) > 0) - text = text line - close(file) - if (!json_tokenize(text, tokens)) + if (!json_tokenize(string, tokens)) return "" if (!json_parse_value(tokens, 0, data, 0)) return "" @@ -256,6 +294,21 @@ function json_load(file, var, line, text, tokens, data, key) return data[0] for (key in data[0]) json_copy(var, key, data[0][key]) + return 1 +} + +function json_encode(var, pad) +{ + return json_write_value(var, pad) +} + +# File API +function json_load(file, var, line, string) +{ + delete var + while ((getline line < file) > 0) + string = string line "\n" + return json_decode(string, var) } function json_save(file, var, cmd, tmp) @@ -263,7 +316,7 @@ function json_save(file, var, cmd, tmp) cmd = "mktemp " file ".XXX" cmd | getline tmp close(cmd) - print json_write_value(var) > tmp + print json_encode(var) > tmp close(tmp) system("mv " tmp " " file) } @@ -272,6 +325,7 @@ function json_save(file, var, cmd, tmp) # Test functions function json_test_write() { + print "Testing JSON Write:" num = 42 str = "hello, world" arr[0] = "zero" @@ -286,54 +340,60 @@ function json_test_write() 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) + print json_encode(num) + print json_encode(str) + print json_encode(arr) + print json_encode(obj) + print json_encode(mix) + print json_encode(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]) + print "Testing JSON Read:" + + json_decode("[8, \"abc\", 9]", array); + print json_encode(array) + + json_decode("{\"abc\": 1, \"def\": 2}", array) + print json_encode(array) + + json = "{ \"first\": { \"arr\": [ \"\", \n" \ + " -1, \n" \ + " 1.2, \n" \ + " true, \n" \ + " false, \n" \ + " null ], \n" \ + " \"number\": 42, \n" \ + " \"eobj\": [ ], \n" \ + " \"earr\": { }, \n" \ + " \"obj\": { \"A\": \"a!\", \n" \ + " \"B\": \"b!\", \n" \ + " \"C\": \"c!\" }, \n" \ + " \"str\": \"hello, world\" }, \n" \ + " \"second\": { \"arr\": [ \"zero\", \n" \ + " \"one\", \n" \ + " \"two\", \n" \ + " \"\\\"\t\r\b\" ], \n" \ + " \"number\": 42, \n" \ + " \"obj\": { \"A\": \"a!\", \n" \ + " \"B\": \"b!\", \n" \ + " \"C\": \"c!\" }, \n" \ + " \"str\": \"hello, world\" } }\n" + + json_decode(json, array) + print json_encode(array) } function json_test_files() { + print "Testing JSON Files:" print "load: [" json_load("email.txt", mail) "]" - print "mail: " json_write_value(mail, " ") + print "mail: " json_encode(mail, " ") mail["andy753421"] = "andy753421@gmail.com" mail["andy"] = "andy@gmail.com" mail["somebody"] = "foo@example.com" - print "mail: " json_write_value(mail, " ") + print "mail: " json_encode(mail, " ") print "save: [" json_save("email.txt", mail) "]" }