#!/usr/bin/env awk -f BEGIN { version = "0.1" help[""] = "Commands:\n" \ " help [cmd] prints cmd help or this help text\n" help["help"] = "help [cmd]\n" \ " Prints cmd help or general help text.\n" \ " Example: help help\n" lnum = cnum = 0 if (str) { lnum = 1 line = ARGV[1] if (err = parse(v, "", line)) print "parse err: " err for (k in v) print k " " v[k] print "---" print format(v, "", 1) exit ERROR != 0 } else file = ARGV[1] delete ARGV[1] if (file) parse(v) if (!notty) printf "%s\njp> ", "jp version " version ". Type \"help\" for help." } { ERROR = "" if ($1 == "help") { printf "%s", help[$2] } else if ($1 == "format") { if ($2 == "\"\"") $2 = "" print format(v, key(v, $2), !raw) } else if ($1 == "key") { print key(v, $2) } else if ($1 == "filter") { filter(v, "", $2) } else if ($1 == "dump") { if ($2 ~ /^\|/) { cmd = substr($0, 1+index($0, "|")) for (k in v) print k " " v[k] | cmd close(cmd) } else for (k in v) print k " " v[k] } else if ($1 == "parse_string") { delete v sub(/^[[:space:]]+/, "") $0 = substr($0, length($1)+2) parse("", v, "", $0) } else if ($1 == "parse") { delete v parse($2, v) } else if (NF) { error("invalid command: " $1) } if (ERROR) print ERROR > "/dev/stderr" if (!notty) printf "%sjp> ", s } END { exit ERROR != "" } function error(s, n) { ERROR = s; print "#error: " s >"/dev/stderr"; return n } function debug(s) { print s > "/dev/stderr" } function scan( c) { while (c = substr(line, 1, 1)) { if (c == "\n") { lnum++; cnum = 0 } else if (c ~ /[ \t]/) cnum++ else break line = substr(line, 2) } if (file) { while (line == "") { if ((getline line < file) <= 0) return lnum++; cnum = 0 if (match(line, /^[ \t]+/)) { cnum += RLENGTH line = substr(line, RLENGTH+1) } } } if (match(line, /^(null|true|false|[][}{,:])/)) { tok = substr(line, 1, 1) } else if (match(line, /^[.0-9Ee+-]+/)) { tok = "d" substr(line, 1, RLENGTH) } else if (match(line, /^"(\\.|[^\\"])*"/)) { tok = "s" substr(line, 2, RLENGTH-2) } else { tok = "b" substr(line, 1, 1) } cnum += RLENGTH line = substr(line, RLENGTH+1) } # parse gets the next object from source and return an error if any. function parse(values, key, id, ids, i, k, err) { scan() if (tok == "[") { for (i = 0; tok != "]"; i++) { if (i > 0 && tok != ",") return error("not a ',': ", tok) if (err = parse(values, key SUBSEP i)) { if (tok == "]" && i == 0) continue return err } scan() } values[key] = "a" i } else if (tok == "{") { for (i = 0; tok != "}"; i++) { if (i > 0 && tok != ",") return "i: " i ", not a ','" scan() if (i == 0 && tok == "}") break if (tok !~ /^s/) return num ":" cnum ": not a string: " tok k = substr(tok, 2) ids = ids (i ? SUBSEP : "") substr(tok, 2) scan() if (tok != ":") return file ":" lnum ":" cnum -length(tok) ": not a ':'" if (err = parse(values, key SUBSEP i)) return err scan() } values[key] = "o" ids } else if (tok ~ /^[ntfsd]/) { values[key] = tok } else return "unexpected tok: " tok } function format(values, key, indent, id, c, i, s, v, pre, post, sp) { if (! (key in values)) return if (indent) { pre = space(indent * 2) post = "\n" sp = " " } if (id) id = "\"" id "\":" sp v = values[key] c = substr(v, 1, 1) if (c == "n") return id "null" if (c == "t") return id "true" if (c == "f") return id "false" if (c == "d") return id substr(v, 2) if (c == "s") return id "\"" substr(v, 2) "\"" if (c == "a") { s = "[" post for (i = 0; ((key, i) in values); i++) { if (i) s = s "," post s = s pre format(values, key SUBSEP i, indent ? indent+1 : 0) } if (substr(s, length(s)-2, 1) == "[") return id substr(s, 1, length(s)-1) "]" return id s post substr(pre, 3) "]" } if (c == "o") { s = "{" post v = substr(v, 2) for (i = 0; ((key, i) in values); i++) { if (i) s = s "," post v = tail(v, SUBSEP) s = s pre "\"" HEAD "\":" sp format(values, key SUBSEP i, indent ? indent+1 : 0) } if (substr(s, length(s)-2, 1) == "{") return id substr(s, 1, length(s)-1) "}" return id s post substr(pre, 3) "}" } } function space(n, i, s) { for (i = 0; i < n; i++) s = s " " return s } function tail(str, sep, i) { if (i = index(str, sep)) { HEAD = substr(str, 1, i-1) return substr(str, i+1) } else HEAD = str } function lindex(list, item, i, k) { for (i = 0; list; i++) { list = tail(list, SUBSEP) if (HEAD == item) return i } return -1 } # TODO: remove recursivity # TODO: handle spaces function key(values, pattern, k1, result, i, k, s, w) { if (! (k1 in values)) return gsub(/[.]/, SUBSEP, pattern) pattern = tail(pattern, SUBSEP) k = HEAD if (k ~ /^[0-9]+$/) { result = result SUBSEP k } else { s = substr(values[k1], 2) if ((i = lindex(s, k)) >= 0) result = result SUBSEP i } if (pattern) return key(values, pattern, k1 SUBSEP i, result) return result } function filter(values, key, sk, i, k, n, v) { v = values[key] if (v ~ /^o/) { v = substr(v, 2) for (i = 0; ((key, i) in values); i++) { v = tail(v, SUBSEP) k = HEAD sk = sk ? sk "." k : k print sk filter(values, key SUBSEP i, sk) } while (v) return } if (v ~ /^a/) { for (i = 0; ((key, i) in values); i++) { sk = sk ? sk "." i : i print sk filter(values, key SUBSEP i, sk) } } }