summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-05-22 15:47:52 -0400
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-05-22 15:47:52 -0400
commita23ae9818310834f54a500c0e583c3652395f0da (patch)
tree3a93d4240e94982a86b9c521a02dfb2841dfb9a5
parentd5e2eee4dfe60ffc44883130cff22072f13efbe3 (diff)
wip
-rw-r--r--json.sh246
-rwxr-xr-xtrace30
2 files changed, 153 insertions, 123 deletions
diff --git a/json.sh b/json.sh
index 3da94e0..9d7701b 100644
--- a/json.sh
+++ b/json.sh
@@ -1,190 +1,190 @@
-#!/bin/bash
-
-gron() {
- set -e
- LC_ALL='C.UTF-8'
- local gron_prefix='json'
- local gron_str
- gron_str="$(cat)"
- _gron_value
+#!/hint/bash
+
+json_parse() {
+ type json_object_beg &>/dev/null || json_object_beg() { :; } # no args
+ type json_object_end &>/dev/null || json_object_end() { :; } # no args
+ type json_array_beg &>/dev/null || json_array_beg() { :; } # no args
+ type json_array_end &>/dev/null || json_array_end() { :; } # no args
+ type json_atom &>/dev/null || json_atom() { :; } # $1=(key|string|number|bool|null) $2=$val
+
+ local _json_buf
+ _json_buf="$(cat)"
+ _json_value
+ _json_ws
+ if [[ -n "$_json_buf" ]]; then
+ _json_error 'unexpected data after json: %q' "$_json_buf"
+ fi
}
-_gron_error() {
- printf 'unexpected character: %q\n' "${gron_str::1}" >&2
+_json_error() {
+ printf >&2 "json error: $1"'\n' "${@:2}"
return 1
}
-_gron_expect() {
- if [[ "${gron_str::1}" != "$1" ]]; then
- _gron_error
+_json_expect() {
+ if [[ "${_json_buf::1}" != "$1" ]]; then
+ _json_error 'expected character %q, got %q' "$1" "${_json_buf::1}"
fi
- gron_str=${gron_str:1}
+ _json_buf=${_json_buf:1}
}
-_gron_ws() {
- while [[ "${gron_str::1}" == [$' \t\n\r'] ]]; do
- gron_str=${gron_str:1}
+_json_ws() {
+ while [[ "${_json_buf::1}" == [$' \t\n\r'] ]]; do
+ _json_buf=${_json_buf: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 ;;
+_json_value() {
+ _json_ws
+ case "${_json_buf::1}" in
+ '{' ) _json_object ;;
+ '[' ) _json_array ;;
+ '"' ) _json_string string;;
+ 't' ) _json_lit 'true' ;;
+ 'f' ) _json_lit 'false' ;;
+ 'n' ) _json_lit 'null' ;;
+ [-+0-9] ) _json_number ;;
+ * ) _json_error 'unexpected value-start character: %q' "${_json_buf::1}" ;;
esac
}
-
-_gron_object() {
- printf '%s={}\n' "$gron_prefix"
-
- local gron_base_prefix=$gron_prefix
-
- _gron_expect '{'
- _gron_ws
- case "${gron_str::1}" in
+_json_object() {
+ _json_expect '{'
+ json_object_beg
+ _json_ws
+ case "${_json_buf::1}" in
'"' )
- local k
+ local _json_obj_key
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
+ _json_string key
+ _json_ws
+ _json_expect ':'
+ _json_value
+ _json_ws
+ case "${_json_buf::1}" in
',' )
- gron_str=${gron_str:1}
- _gron_ws
+ _json_buf=${_json_buf:1}
+ _json_ws
;;
'}' )
- gron_str=${gron_str:1}
+ _json_buf=${_json_buf:1}
+ json_object_end
return
;;
esac
done
;;
'}' )
- gron_str=${gron_str:1}
+ _json_buf=${_json_buf:1}
+ json_object_end
return
;;
esac
}
-_gron_array() {
- printf '%s=[]\n' "$gron_prefix"
-
- local gron_base_prefix=$gron_prefix
-
- _gron_expect '['
- _gron_ws
- if [[ "${gron_str::1}" == ']' ]]; then
+_json_array() {
+ _json_expect '['
+ json_array_beg
+ _json_ws
+ if [[ "${_json_buf::1}" == ']' ]]; then
+ _json_buf=${_json_buf:1}
+ json_array_end
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
+ while true; do
+ _json_value
+ _json_ws
+ case "${_json_buf::1}" in
',' )
- gron_str=${gron_str:1}
- _gron_ws
+ _json_buf=${_json_buf:1}
+ _json_ws
;;
']' )
- gron_str=${gron_str:1}
+ _json_buf=${_json_buf:1}
+ json_array_end
return
;;
esac
done
}
-_gron_read_string() {
- _gron_expect '"'
- local v
+# $1=(key|string)
+_json_string() {
+ _json_expect '"'
+ local _json_strval=''
+ local _json_re='^[^\"]+'
+ local _json_c json_n _json_n2
while true; do
- case "${gron_str::1}" in
+ case "${_json_buf::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} ;;
+ _json_buf=${_json_buf:1}
+ case "${_json_buf::1}" in
+ '"' ) _json_strval+='"'; _json_buf=${_json_buf:1} ;;
+ "\\" ) _json_strval+="\\"; _json_buf=${_json_buf:1} ;;
+ '/' ) _json_strval+='/'; _json_buf=${_json_buf:1} ;;
+ 'b' ) _json_strval+=$'\b'; _json_buf=${_json_buf:1} ;;
+ 'f' ) _json_strval+=$'\f'; _json_buf=${_json_buf:1} ;;
+ 'n' ) _json_strval+=$'\n'; _json_buf=${_json_buf:1} ;;
+ 'r' ) _json_strval+=$'\r'; _json_buf=${_json_buf:1} ;;
+ 't' ) _json_strval+=$'\t'; _json_buf=${_json_buf: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
+ if ! [[ ${_json_buf::5} == u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] ]]; then
+ _json_error 'expected 4 hex characters, got: %q' "${_json_buf:1:4}"
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
+ _json_c="$(printf "\\${_json_buf::5}")"
+ _json_n="0x${_json_buf:1:4}"
+ _json_buf=${_json_buf:5}
+ if (( 0xDC00 <= _json_n && _json_n <= 0xDFFF )); then
+ if ! [[ ${_json_buf::6} == '\u'[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] ]]; then
+ _json_error 'expected a \\uABCD sequence, got: %q' "${_json_buf::6}"
fi
- n2="0x${gron_str:2:4}"
- gron_str=${gron_str:6}
- gron_str=${gron_str:5}
+ _json_n2="0x${_json_buf:2:4}"
+ _json_buf=${_json_buf:6}
if ! (( 0xD800 <= n2 && n2 <= 0xDBFF )); then
- _gron_error
+ _json_error 'expected a UTF-16 high surrogate, got: 0x%x' "$n2"
fi
- n=$(( 0x10000 + ((n-0xDC00)<<10) + (n2-0xD8000) ))
- printf -v n '0x%08' "$n"
- printf -v c "\\U$n"
+ _json_n=$(( 0x10000 + ((n-0xDC00)<<10) + (n2-0xD8000) ))
+ printf -v _json_n '0x%08' "$_json_n"
+ printf -v _json_c "\\U$_json_n"
fi
- v+="$c"
+ _json_strval+="$_json_c"
;;
- *) _gron_error ;;
+ *) _json_error 'unexpected backslash sequence: \\%q' "${json_buf::1}";;
esac
;;
'"' )
- gron_str=${gron_str:1}
- printf -v "$1" "$v"
+ _json_buf=${_json_buf:1}
+ json_atom "$1" "$_json_strval"
+ return
;;
* )
# 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]}"}
+ [[ $_json_buf =~ $_json_re ]]
+ _json_strval+=${BASH_REMATCH[0]}
+ _json_buf=${_json_buf#"${BASH_REMATCH[0]}"}
;;
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
+_json_number() {
+ local _json_re='^-?(0|[1-9][0-9]+)(\.[0-9]+)?([eE][-+]?[0-9]+)?'
+ if ! [[ $_json_buf =~ $_json_re ]]; then
+ _json_error 'invalid number: %q' "${_json_buf::16}"
fi
- gron_str=${gron_str#"${BASH_REMATCH[1]}"}
- printf '%s=%s\n' "$gron_prefix" "${BASH_REMATCH[1]}"
+ _json_buf=${_json_buf:${#BASH_REMATCH[0]}}
+ json_atom number "${BASH_REMATCH[0]}"
}
-_gron_lit() {
- if [[ ${gron_str::${#1}} != "$1" ]]; then
- _gron_error
+_json_lit() {
+ if [[ ${_json_buf::${#1}} != "$1" ]]; then
+ _json_error 'expected %q, got: %q' "$1" "${_json_buf::${#1}}"
+ fi
+ _json_buf=${_json_buf:${#1}}
+ if [[ "$1" == 'null' ]]; then
+ json_atom null null
+ else
+ json_atom bool "$1"
fi
- gron_str=${gron_str:${#1}}
- printf '%s=%s\n' "$gron_prefix" "$1"
}
-
-gron
diff --git a/trace b/trace
new file mode 100755
index 0000000..3aa657d
--- /dev/null
+++ b/trace
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# no args
+json_object_beg() {
+ echo json_object_beg
+}
+
+# no args
+json_object_end() {
+ echo json_object_end
+}
+
+# no args
+json_array_beg() {
+ echo json_array_beg
+}
+
+# no args
+json_array_end() {
+ echo json_array_end
+}
+
+# $1=(key|string|number|bool|null) $2=$val
+json_atom() {
+ printf 'json_atom %q %q\n' "$1" "$2"
+}
+
+. "$(dirname -- "${BASH_SOURCE[0]}")/json.sh"
+
+json_parse