summaryrefslogtreecommitdiff
path: root/jp
diff options
context:
space:
mode:
Diffstat (limited to 'jp')
-rwxr-xr-xjp218
1 files changed, 218 insertions, 0 deletions
diff --git a/jp b/jp
new file mode 100755
index 0000000..28cd767
--- /dev/null
+++ b/jp
@@ -0,0 +1,218 @@
+#!/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, "", 0)
+ 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)
+ }
+ 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)
+ }
+ 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)
+ }
+ }
+}