summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-05-22 15:09:31 -0400
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-05-22 15:09:31 -0400
commitd5e2eee4dfe60ffc44883130cff22072f13efbe3 (patch)
tree1caf09e8f081098c7e2478f88132b660e8b516ad
initial commit
-rw-r--r--json.sh190
1 files changed, 190 insertions, 0 deletions
diff --git a/json.sh b/json.sh
new file mode 100644
index 0000000..3da94e0
--- /dev/null
+++ b/json.sh
@@ -0,0 +1,190 @@
+#!/bin/bash
+
+gron() {
+ set -e
+ LC_ALL='C.UTF-8'
+ local gron_prefix='json'
+ local gron_str
+ gron_str="$(cat)"
+ _gron_value
+}
+
+_gron_error() {
+ printf 'unexpected character: %q\n' "${gron_str::1}" >&2
+ return 1
+}
+
+_gron_expect() {
+ if [[ "${gron_str::1}" != "$1" ]]; then
+ _gron_error
+ fi
+ gron_str=${gron_str:1}
+}
+
+_gron_ws() {
+ while [[ "${gron_str::1}" == [$' \t\n\r'] ]]; do
+ gron_str=${gron_str:1}
+ done
+}
+
+_gron_value() {
+ _gron_ws
+ case "${gron_str::1}" in
+ '{' ) _gron_object ;;
+ '[' ) _gron_array ;;
+ '"' ) _gron_string ;;
+ 't' ) _gron_lit 'true' ;;
+ 'f' ) _gron_lit 'false' ;;
+ 'n' ) _gron_lit 'null' ;;
+ [-+0-9] ) _gron_number ;;
+ * ) _gron_error ;;
+ esac
+}
+
+
+_gron_object() {
+ printf '%s={}\n' "$gron_prefix"
+
+ local gron_base_prefix=$gron_prefix
+
+ _gron_expect '{'
+ _gron_ws
+ case "${gron_str::1}" in
+ '"' )
+ local k
+ while true; do
+ _gron_read_string k
+ gron_prefix="${gron_base_prefix}[${k@Q}]"
+ _gron_ws
+ _gron_expect ':'
+ _gron_value
+ _gron_ws
+ case "${gron_str::1}" in
+ ',' )
+ gron_str=${gron_str:1}
+ _gron_ws
+ ;;
+ '}' )
+ gron_str=${gron_str:1}
+ return
+ ;;
+ esac
+ done
+ ;;
+ '}' )
+ gron_str=${gron_str:1}
+ return
+ ;;
+ esac
+}
+
+_gron_array() {
+ printf '%s=[]\n' "$gron_prefix"
+
+ local gron_base_prefix=$gron_prefix
+
+ _gron_expect '['
+ _gron_ws
+ if [[ "${gron_str::1}" == ']' ]]; then
+ return
+ fi
+ local i
+ for ((i = 0; 1; i++)); do
+ gron_prefix="${gron_base_prefix}[${i}]"
+ _gron_value
+ _gron_ws
+ case "${gron_str::1}" in
+ ',' )
+ gron_str=${gron_str:1}
+ _gron_ws
+ ;;
+ ']' )
+ gron_str=${gron_str:1}
+ return
+ ;;
+ esac
+ done
+}
+
+_gron_read_string() {
+ _gron_expect '"'
+ local v
+ while true; do
+ case "${gron_str::1}" in
+ "\\" )
+ gron_str=${gron_str:1}
+ case "${gron_str::1}" in
+ '"' ) v+='"'; gron_str=${gron_str:1} ;;
+ "\\" ) v+="\\"; gron_str=${gron_str:1} ;;
+ '/' ) v+='/'; gron_str=${gron_str:1} ;;
+ 'b' ) v+=$'\b'; gron_str=${gron_str:1} ;;
+ 'f' ) v+=$'\f'; gron_str=${gron_str:1} ;;
+ 'n' ) v+=$'\n'; gron_str=${gron_str:1} ;;
+ 'r' ) v+=$'\r'; gron_str=${gron_str:1} ;;
+ 't' ) v+=$'\t'; gron_str=${gron_str:1} ;;
+ 'u' )
+ if ! [[ ${gron_str::5} == u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] ]]; then
+ _gron_error
+ fi
+ c="$(printf "\\${gron_str::5}")"
+ n="0x${gron_str:1:4}"
+ gron_str=${gron_str:5}
+ if (( 0xDC00 <= n && n <= 0xDFFF )); then
+ if ! [[ ${gron_str::6} == '\u'[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] ]]; then
+ _gron_error
+ fi
+ n2="0x${gron_str:2:4}"
+ gron_str=${gron_str:6}
+ gron_str=${gron_str:5}
+ if ! (( 0xD800 <= n2 && n2 <= 0xDBFF )); then
+ _gron_error
+ fi
+ n=$(( 0x10000 + ((n-0xDC00)<<10) + (n2-0xD8000) ))
+ printf -v n '0x%08' "$n"
+ printf -v c "\\U$n"
+ fi
+ v+="$c"
+ ;;
+ *) _gron_error ;;
+ esac
+ ;;
+ '"' )
+ gron_str=${gron_str:1}
+ printf -v "$1" "$v"
+ ;;
+ * )
+ # Consume multiple characters at once,
+ # or else this is horribly slow.
+ local re='^[^\"]+'
+ [[ $gron_str =~ $re ]]
+ v+=${BASH_REMATCH[1]}
+ gron_str=${gron_str#"${BASH_REMATCH[1]}"}
+ ;;
+ esac
+ done
+}
+
+_gron_string() {
+ local v
+ _gron_read_string v
+ printf '%s=%q\n' "$gron_prefix" "$v"
+}
+
+_gron_number() {
+ local re='^-?(0|[1-9][0-9]+)(\.[0-9]+)?([eE][-+]?[0-9]+)?'
+ if ! [[ $gron_str =~ $re ]]; then
+ _gron_error
+ fi
+ gron_str=${gron_str#"${BASH_REMATCH[1]}"}
+ printf '%s=%s\n' "$gron_prefix" "${BASH_REMATCH[1]}"
+}
+
+_gron_lit() {
+ if [[ ${gron_str::${#1}} != "$1" ]]; then
+ _gron_error
+ fi
+ gron_str=${gron_str:${#1}}
+ printf '%s=%s\n' "$gron_prefix" "$1"
+}
+
+gron