Permalink
Find file
8f635b1 Nov 3, 2015
406 lines (363 sloc) 9.28 KB
# 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, .. ]
# 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 == 0 && value == "")
return "null"
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)
case "null": return "null"
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)
{
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, line, items, table, type, found)
{
table["term"] = "^[\\[\\]{}:,]"
table["str"] = "^\"([^\\\\\"]|\\\\.)*\""
table["num"] = "^[+-]?[0-9]+(.[0-9]+)?"
table["var"] = "^(true|false|null)"
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" && type != "line") {
tokens[i]["line"] = line
tokens[i]["type"] = type
tokens[i]["text"] = items[0]
i++
}
str = substr(str, RLENGTH+1)
found = 1
break
}
}
if (!found) {
debug("line " line ": error tokenizing")
return 0
}
}
#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, 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
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("line "line": 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;
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;
json_copy(value, key, object)
return i
}
function json_parse_array(tokens, i, value, key, array, k, v)
{
if (tokens[i++]["text"] != "[")
return 0;
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;
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"]
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 "]"
return i
}
function json_parse_var(tokens, i, value, key, text, null)
{
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
}
# String API
function json_decode(string, var, tokens, data, key)
{
if (!json_tokenize(string, 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])
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 | getline tmp
close(cmd)
print json_encode(var) > tmp
close(tmp)
system("mv " tmp " " file)
}
# Test functions
function json_test_write()
{
print "Testing JSON 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_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()
{
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_encode(mail, " ")
mail["andy753421"] = "andy753421@gmail.com"
mail["andy"] = "andy@gmail.com"
mail["somebody"] = "foo@example.com"
print "mail: " json_encode(mail, " ")
print "save: [" json_save("email.txt", mail) "]"
}
# Main
BEGIN {
#json_test_write()
#json_test_read()
#json_test_files()
}