]> Pileus Git - ~andy/rhawk/blobdiff - json.awk
Save game after flipping the table
[~andy/rhawk] / json.awk
index 2f735c4b44405340cef0ad5c1353849d6d03f01c..cfb8eac56c680460b9e12d4c6d7a1e7c3f45a61c 100644 (file)
--- 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,14 +294,29 @@ 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)
 {
-       cmd = "mktemp ." file ".XXX"
+       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) "]"
 }