summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Vertes <mvertes@free.fr>2025-07-11 18:08:11 +0200
committerMarc Vertes <mvertes@free.fr>2025-07-11 18:08:11 +0200
commit43e3a8a08668b068eb07b5c1ee3aaa767021d3c0 (patch)
treec509903adbd8143aeb2d6c8e794775a52fc8ea4c
parentc5cbfacba61a9fba9e1bf3ea6abc4f067298c80f (diff)
add format. Improve tests.
-rwxr-xr-xmp149
-rwxr-xr-xtests19
2 files changed, 115 insertions, 53 deletions
diff --git a/mp b/mp
index 337f316..9f5209a 100755
--- a/mp
+++ b/mp
@@ -1,7 +1,7 @@
#!/usr/bin/awk -f
BEGIN {
- version = "2025-07-08"
+ version = "0.1"
help[""] = "Commands:\n" \
" help [cmd] prints cmd help or this help text\n"
@@ -16,18 +16,28 @@ BEGIN {
if (filename) src = getfile(filename)
prompt("mp-" version " meta parser. Type \"help\" for help.\n")
}
+{ ERROR = "" }
$1 == "help" {
prompt(help[$2])
next
}
$1 == "src" {
- prompt(src)
+ printf "%s", src
+ prompt()
+ next
+}
+$1 == "format" {
+ print format_json(v, "", 1)
+ prompt()
next
}
$1 == "parse" {
delete v
- if ($2) parse_json($2, v); else parse_json(src, v)
- for (k in v) print "v[" k "]: " v[k]
+ sub(/^[[:space:]]+/, "")
+ $0 = substr($0, 6)
+ if ($0) src = $0
+ parse_json(src, v)
+ print format_json(v)
prompt()
next
}
@@ -35,65 +45,112 @@ $1 == "parse" {
if (NF) error("invalid command: " $1)
prompt()
}
+END { if (ERROR) exit 1 }
-function error(s) { ERROR = s }
+function error(s, n) { ERROR = s; return n }
function prompt(s) {
- if (ERROR) {
- print ERROR > "/dev/stderr"
- ERROR = ""
- }
- if (tty) printf("%smp> ", s)
+ if (ERROR) print ERROR > "/dev/stderr"
+ if (tty) printf("%s> ", s)
}
function getfile(name, l, r, o) {
while (r = getline l < name) {
- if (r == -1) {
- error = "getfile " name ": getline error"
- break
- }
+ if (r == -1) return error("getfile " name ": getline error")
o = o l "\n"
}
- close(name)
return o
}
-# parse the next json value in s, returns the number of characters read.
-function parse_json(s, a, p, c, i, n) {
- if (match(s, /^[[:space:]]+/)) {
+function next_json_token(str, n) {
+ n = 0
+ TOKEN = TOKSTR = ""
+ if (match(str, /^[[:space:]]+/)) {
n = RLENGTH
- s = substr(s, RLENGTH+1)
+ str = substr(str, RLENGTH+1)
}
- if (!s) return n
- if (match(s, /^null|true|false/)) {
- a[p, "type"] = substr(s, 1, RLENGTH)
- return n + RLENGTH
+ if (match(str, /^(null|true|false|[][}{,:])/)) {
+ TOKEN = substr(str, 1, RLENGTH)
+ } else if (match(str, /^[.0-9Ee+-]+/)) {
+ TOKEN = "number"
+ TOKSTR = substr(str, 1, RLENGTH)
+ } else if (match(str, /^"(\\.|[^\\"])*"/)) {
+ TOKEN = "string"
+ TOKSTR = substr(str, 2, RLENGTH-2)
+ } else {
+ TOKSTR = substr(str, 1, 1)
+ TOKEN = TOKSTR ? "invalid" : ""
}
- if (match(s, /^[.0-9Ee+-]+/)) {
- a[p, "type"] = "number"
- a[p, "string"] = substr(s, 1, RLENGTH)
- return n + RLENGTH
- }
- if (match(s, /^"(\\.|[^\\"])*"/)) {
- a[p, "type"] = "string"
- a[p, "string"] = substr(s, 2, RLENGTH-2)
- return n + RLENGTH
+ return n + RLENGTH
+}
+
+function parse_json(str, arr, pk, i, n) {
+ n = next_json_token(str)
+ if (TOKEN == "[") {
+ arr[pk, "type"] = "array"
+ for (i = 0; TOKEN != "]"; i++) {
+ if (i > 0 && TOKEN != ",") return error("not a ','", n)
+ n += parse_json(substr(str, n+1), arr, pk SUBSEP "value" SUBSEP i)
+ if (ERROR) {
+ if (i == 0 && TOKEN == "]") ERROR = ""
+ break
+ }
+ n += next_json_token(substr(str, n+1))
+ }
+ arr[pk, "size"] = i
+ } else if (TOKEN == "{") {
+ arr[pk, "type"] = "object"
+ for (i = 0; TOKEN != "}"; i++) {
+ if (i > 0 && TOKEN != ",") return error("not a ','", n)
+ n += next_json_token(substr(str, n+1))
+ if (i == 0 && TOKEN == "}") break
+ if (TOKEN != "string") return error("not a string", n)
+ arr[pk, "key", i] = TOKSTR
+ n += next_json_token(substr(str, n+1))
+ if (TOKEN != ":") return error("not a ':'", n)
+ n += parse_json(substr(str, n+1), arr, pk SUBSEP "value" SUBSEP i)
+ if (ERROR) return n
+ n += next_json_token(substr(str, n+1))
+ }
+ arr[pk, "size"] = i
+ } else if (TOKEN ~ /^(null|true|false|string|number)$/) {
+ arr[pk, "type"] = TOKEN
+ if (TOKSTR) arr[pk, "string"] = TOKSTR
+ } else if (TOKEN) ERROR = "invalid token '" TOKEN "'"
+ return n
+}
+
+function format_json(arr, pk, il, i, l, t, s, bl, al, ps) {
+ if (il) {
+ bl = space(il * 2)
+ al = "\n"
+ ps = " "
}
- c = substr(s, 1, 1)
- if (c == "{") {
- a[p, "type"] = "object"
- i = 0
- n++
- s = substr(s, 2)
- if (match(s, /^[[:space:]]+/)) {
- n += RLENGTH
- s = substr(s, RLENGTH+1)
+ t = arr[pk, "type"]
+ if (t == "null" || t == "true" || t == "false") return t
+ if (t == "number") return arr[pk, "string"]
+ if (t == "string") return "\"" arr[pk, "string"] "\""
+ if (t == "object") {
+ s = "{" al
+ l = arr[pk, "size"]
+ for (i = 0; i < l; i++) {
+ s = s bl "\"" arr[pk, "key", i] "\":" ps format_json(arr, pk SUBSEP "value" SUBSEP i, il ? il+1 : 0)
+ if (i < l-1) s = s "," al
}
- if (match(s, /^"(\\.|[^\\"])*"/)) {
- a[p, "key", i] = substr(s, 2, RLENGTH-2)
- } else {
- error("invalid key: " s)
- return n
+ return s al substr(bl, 3) "}"
+ }
+ if (t == "array") {
+ s = "[" al
+ l = arr[pk, "size"]
+ for (i = 0; i < arr[pk, "size"]; i++) {
+ s = s bl format_json(arr, pk SUBSEP "value" SUBSEP i, il ? il+1 : 0)
+ if (i < l-1) s = s "," al
}
+ return s al substr(bl, 3) "]"
}
}
+
+function space(n, i, s) {
+ for (i = 0; i < n; i++) s = s " "
+ return s
+}
diff --git a/tests b/tests
index 1c8dcff..50e1c75 100755
--- a/tests
+++ b/tests
@@ -2,8 +2,9 @@
# Test suite for mp.
run() {
- [ "$filter" ] && case $1 in $filter) ;; *) return; esac
- out=$(echo "$2" | ./mp 2>&1 | tr -d '\034' | awk -v ORS='\\n' 1)
+ # shellcheck disable=SC2254
+ [ "$filter" ] && case $1 in ($filter) ;; (*) return; esac
+ out=$(echo "$2" | ./mp 2>&1)
[ "$out" = "$3" ] && pass=$((pass + 1)) && return
printf "%s FAIL\nWant: \"%s\"\n Got: \"%s\"\n" "$1" "$3" "$out"
fail=$((fail + 1))
@@ -14,8 +15,12 @@ run() {
pass=0 fail=0 filter="$1"
trap 'echo "$pass passed, $fail failed"; exit $((fail))' EXIT
-run test1 'parse null' 'v[type]: null\n'
-run test2 'parse true' 'v[type]: true\n'
-run test3 'parse false' 'v[type]: false\n'
-run test4 'parse 12' 'v[string]: 12\nv[type]: number\n'
-run test5 'parse "hello"' 'v[string]: hello\nv[type]: string\n'
+run basic1 'parse' ''
+run basic2 'parse null' 'null'
+run basic3 'parse true' 'true'
+run basic4 'parse false' 'false'
+run num1 'parse 12' '12'
+run str1 'parse "hello"' '"hello"'
+run obj1 'parse {}' '{}'
+run arr1 'parse []' '[]'
+