diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-05-22 15:09:31 -0400 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-05-22 15:09:31 -0400 |
commit | d5e2eee4dfe60ffc44883130cff22072f13efbe3 (patch) | |
tree | 1caf09e8f081098c7e2478f88132b660e8b516ad |
initial commit
-rw-r--r-- | json.sh | 190 |
1 files changed, 190 insertions, 0 deletions
@@ -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 |