diff --git a/bj-1line.sh b/bj-1line.sh index 990e7ed..acdc5e7 100755 --- a/bj-1line.sh +++ b/bj-1line.sh @@ -1,2 +1,2 @@ # (C) Isaac Freeman (memotype@gmail.com). See https://github.com/memotype/bj.sh -bj()([[ $1 = - ]]||exec<<<"$1";shift;c=;rd(){ l=$c;IFS= read -rN1 c;};pr(){ printf %s "${o[@]}";};st(){ p=$1;[[ $p ]]||o=();while rd;do [[ $p&&! $q ]]&&o+=("$l");case $c in \\)rd||break;[[ $p&&$q ]]||o+=("$l${c::!p}");;\")break;;*)[[ $p ]]||o+=("$c");;esac;done;};co(){ n= b=1;while rd;do [[ $q ]]||o+=("$l");case $c in \")[[ $1||! $q ]]&&st 1||{ st;k=$(pr);};;[|{)((b++));;]|\})((--b))||{ [[ $q ]]&&exit 1;o+=("$c");break;};;:)[[ $1$b = 1&&$q&&$k = "$q" ]]&&break;;,)((0$1&&b == 1))&&{ ((n++));[[ $n = "$q" ]]&&break;};;esac;done;};for q in "$@" "";do shift&&[[ ! $q ]]&&return 2;x=0;o=();while rd;do case $c in [[:space:]])! :;;\")st;;[tfn0-9-])while o+=("$c")&&rd&&[[ $c =~ [-+.0-9Ea-z] ]];do :;done;;{)co;;[)[[ $q = 0 ]]||co 1;;*)return 2;;esac&&x=1&&break;done;done;pr;((x));) +bj()([[ $1 = - ]]||exec<<<"$1";shift;c=;r(){ l=$c;IFS= read -rN1 c;};p(){ printf %s "${o[@]}";};s(){ p=$1;[[ $p ]]||o=();while r;do [[ $p&&! $q ]]&&o+=("$l");case $c in \\)r||break;[[ $p&&$q ]]||o+=("$l${c::!p}");;\")break;;*)[[ $p ]]||o+=("$c");;esac;done;};co(){ n= b=1;while r;do [[ $q ]]||o+=("$l");case $c in \")[[ $1||! $q ]]&&s 1||{ s;k=$(p);};;[|{)((b++));;]|\})((--b))||{ [[ $q ]]&&exit 1;o+=("$c");break;};;:)[[ $1$b = 1&&$q&&$k = "$q" ]]&&break;;,)((0$1&&b==1))&&[[ $q = $((++n)) ]]&&break;;esac;done;};for q in "$@" "";do shift&&[[ ! $q ]]&&return 2;x=0;o=();while r;do case $q$c in *[[:space:]])! :;;\")s;;[tfn0-9-])while o+=("$c")&&r&&[[ $c =~ [-+.0-9Ea-z] ]];do :;done;;*{)co;;*[)[[ $q = 0 ]]||co 1;;?)return 2;;*)return 1;;esac&&x=1&&break;done;done;p;((x));) diff --git a/bj-80-col.sh b/bj-80-col.sh index 89ccc24..0dea8ae 100755 --- a/bj-80-col.sh +++ b/bj-80-col.sh @@ -1,11 +1,11 @@ # (C) Isaac Freeman (memotype@gmail.com). See https://github.com/memotype/bj.sh -bj()([[ $1 = - ]]||exec<<<"$1";shift;c=;rd(){ l=$c;IFS= read -rN1 c;};pr(){ \ -printf %s "${o[@]}";};st(){ p=$1;[[ $p ]]||o=();while rd;do [[ $p&&! $q ]]&&o+=\ -("$l");case $c in \\)rd||break;[[ $p&&$q ]]||o+=("$l${c::!p}");;\")break;;*)[[ \ -$p ]]||o+=("$c");;esac;done;};co(){ n= b=1;while rd;do [[ $q ]]||o+=("$l") -case $c in \")[[ $1||! $q ]]&&st 1||{ st;k=$(pr);};;[|{)((b++));;]|\})((--b))|| -{ [[ $q ]]&&exit 1;o+=("$c");break;};;:)[[ $1$b = 1&&$q&&$k = "$q" ]]&&break;;,) -((0$1&&b == 1))&&{ ((n++));[[ $n = "$q" ]]&&break;};;esac;done;};for q in "$@" \ -"";do shift&&[[ ! $q ]]&&return 2;x=0;o=();while rd;do case $c in [[:space:]]) -! :;;\")st;;[tfn0-9-])while o+=("$c")&&rd&&[[ $c =~ [-+.0-9Ea-z] ]];do :;done;; -{)co;;[)[[ $q = 0 ]]||co 1;;*)return 2;;esac&&x=1&&break;done;done;pr;((x));) +bj()([[ $1 = - ]]||exec<<<"$1";shift;c=;r(){ l=$c;IFS= read -rN1 c;};p(){ \ +printf %s "${o[@]}";};s(){ p=$1;[[ $p ]]||o=();while r;do [[ $p&&! $q ]]&&o+=(\ +"$l");case $c in \\)r||break;[[ $p&&$q ]]||o+=("$l${c::!p}");;\")break;;*)[[ \ +$p ]]||o+=("$c");;esac;done;};co(){ n= b=1;while r;do [[ $q ]]||o+=("$l");case \ +$c in \")[[ $1||! $q ]]&&s 1||{ s;k=$(p);};;[|{)((b++));;]|\})((--b))||{ [[ $q \ +]]&&exit 1;o+=("$c");break;};;:)[[ $1$b = 1&&$q&&$k = "$q" ]]&&break;;,)((0$1&& +b==1))&&[[ $q = $((++n)) ]]&&break;;esac;done;};for q in "$@" "";do shift&&[[ \ +! $q ]]&&return 2;x=0;o=();while r;do case $q$c in *[[:space:]])! :;;\")s;; +[tfn0-9-])while o+=("$c")&&r&&[[ $c =~ [-+.0-9Ea-z] ]];do :;done;;*{)co;;*[)[[ \ +$q = 0 ]]||co 1;;?)return 2;;*)return 1;;esac&&x=1&&break;done;done;p;((x));) diff --git a/bj-90-col.sh b/bj-90-col.sh index b19f9db..a587db9 100755 --- a/bj-90-col.sh +++ b/bj-90-col.sh @@ -1,10 +1,10 @@ # (C) Isaac Freeman (memotype@gmail.com). See https://github.com/memotype/bj.sh -bj()([[ $1 = - ]]||exec<<<"$1";shift;c=;rd(){ l=$c;IFS= read -rN1 c;};pr(){ printf %s \ -"${o[@]}";};st(){ p=$1;[[ $p ]]||o=();while rd;do [[ $p&&! $q ]]&&o+=("$l");case $c in \\) -rd||break;[[ $p&&$q ]]||o+=("$l${c::!p}");;\")break;;*)[[ $p ]]||o+=("$c");;esac;done;} -co(){ n= b=1;while rd;do [[ $q ]]||o+=("$l");case $c in \")[[ $1||! $q ]]&&st 1||{ st;k\ -=$(pr);};;[|{)((b++));;]|\})((--b))||{ [[ $q ]]&&exit 1;o+=("$c");break;};;:)[[ $1$b = 1&& -$q&&$k = "$q" ]]&&break;;,)((0$1&&b == 1))&&{ ((n++));[[ $n = "$q" ]]&&break;};;esac;done -};for q in "$@" "";do shift&&[[ ! $q ]]&&return 2;x=0;o=();while rd;do case $c in \ -[[:space:]])! :;;\")st;;[tfn0-9-])while o+=("$c")&&rd&&[[ $c =~ [-+.0-9Ea-z] ]];do :;done -;;{)co;;[)[[ $q = 0 ]]||co 1;;*)return 2;;esac&&x=1&&break;done;done;pr;((x));) +bj()([[ $1 = - ]]||exec<<<"$1";shift;c=;r(){ l=$c;IFS= read -rN1 c;};p(){ printf %s \ +"${o[@]}";};s(){ p=$1;[[ $p ]]||o=();while r;do [[ $p&&! $q ]]&&o+=("$l");case $c in \\)r\ +||break;[[ $p&&$q ]]||o+=("$l${c::!p}");;\")break;;*)[[ $p ]]||o+=("$c");;esac;done;};co\ +(){ n= b=1;while r;do [[ $q ]]||o+=("$l");case $c in \")[[ $1||! $q ]]&&s 1||{ s;k=$(p);} +;;[|{)((b++));;]|\})((--b))||{ [[ $q ]]&&exit 1;o+=("$c");break;};;:)[[ $1$b = 1&&$q&&$k \ += "$q" ]]&&break;;,)((0$1&&b==1))&&[[ $q = $((++n)) ]]&&break;;esac;done;};for q in "$@" \ +"";do shift&&[[ ! $q ]]&&return 2;x=0;o=();while r;do case $q$c in *[[:space:]])! :;;\")s +;;[tfn0-9-])while o+=("$c")&&r&&[[ $c =~ [-+.0-9Ea-z] ]];do :;done;;*{)co;;*[)[[ $q = 0 \ +]]||co 1;;?)return 2;;*)return 1;;esac&&x=1&&break;done;done;p;((x));) diff --git a/bj.sh b/bj.sh index 66527f8..2be0c7d 100755 --- a/bj.sh +++ b/bj.sh @@ -18,30 +18,30 @@ bj() ( c= # Read a character - rd() { + r() { l=$c IFS= read -rN1 c } # Join the characters in the $o array - pr() { + p() { # Using ':' with arguments is nice for 'bash -x' debugging - : : "=== pr()" + : : "=== p()" printf %s "${o[@]}" } # Scan strings, saving to $o unless navigation only needs to skip them. - st() { - : : "=== st()" + s() { + : : "=== s()" p=$1 [[ $p ]] || o=() - while rd; do + while r; do : : "--- lc=$l$c=" [[ $p && ! $q ]] && o+=("$l") case $c in \\) - rd || break + r || break # Skip while navigating. Otherwise include $c only when capturing. [[ $p && $q ]] || o+=("$l${c::!p}") ;; @@ -55,11 +55,11 @@ bj() ( co() { : : "=== co()" n= b=1 - while rd; do + while r; do : : "--- l=$l= c=$c= q=$q= b=$b= ---" [[ $q ]] || o+=("$l") case $c in - \") [[ $1 || ! $q ]] && st 1 || { st; k=$(pr); } ;; + \") [[ $1 || ! $q ]] && s 1 || { s; k=$(p); } ;; [|{) ((b++)) ;; # ]) <- fix vim syntax ]|\}) ((--b)) || { @@ -73,10 +73,9 @@ bj() ( # Found the key, just stop and let the main loop parse from here. :) [[ $1$b = 1 && $q && $k = "$q" ]] && break ;; ,) - ((0$1 && b == 1)) && { - ((n++)) - [[ $n = "$q" ]] && break - } + ((0$1 && b==1)) \ + && [[ $q = $((++n)) ]] \ + && break ;; esac done @@ -89,26 +88,28 @@ bj() ( x=0 : : "--- q=$q" o=() - while rd; do + while r; do : : "mn --- l=$l c=$c" - case $c in - [[:space:]]) ! : ;; - \") st ;; + # Prefix with $q so scalar cases only match during final output. + case $q$c in + *[[:space:]]) ! : ;; + \") s ;; [tfn0-9-]) while o+=("$c") \ - && rd \ - && [[ $c =~ [-+.0-9Ea-z] ]] + && r \ + && [[ $c =~ [-+.0-9Ea-z] ]] do :;done ;; - {) co ;; - [) [[ $q = 0 ]] || co 1 ;; #]) - *) return 2 ;; + *{) co ;; + *[) [[ $q = 0 ]] || co 1 ;; #]) + ?) return 2 ;; + *) return 1 ;; esac && x=1 && break done done # Print whatever we last stored in $o - pr + p ((x)) ) diff --git a/test.sh b/test.sh index f6e8d95..b4dbe12 100755 --- a/test.sh +++ b/test.sh @@ -130,6 +130,22 @@ runtest top '{"target":"top","outer":{"target":"nested"}}' target runtest direct \ '{"level":{"child":{"target":"nested"},"target":"direct"}}' level target +# Traversal must stop when a selected value is a scalar. +runteststatus 1 '' '{"a":"x","b":"y"}' a extra +runteststatus 1 '' '{"b":"y","a":"x"}' a extra +runteststatus 1 '' '{"a":1,"b":2}' a extra +runteststatus 1 '' '{"b":2,"a":1}' a extra +runteststatus 1 '' '{"a":true,"b":false}' a extra +runteststatus 1 '' '{"b":false,"a":true}' a extra +runteststatus 1 '' '{"a":false,"b":true}' a extra +runteststatus 1 '' '{"b":true,"a":false}' a extra +runteststatus 1 '' '{"a":null,"b":0}' a extra +runteststatus 1 '' '{"b":0,"a":null}' a extra +runteststatus 1 '' '[1,2]' 0 extra +runteststatus 1 '' '[1,2]' 1 extra +runteststatus 1 '' '{"outer":{"a":1,"b":2}}' outer a extra +runteststatus 1 '' '{"a":1,"b":2}' a extra more + # Numbers tests runtest "4.2e10" '[0, -1, 4.2e10]' 2 runtest "-1" '[0, -1, 4.2e10]' 1 @@ -230,6 +246,17 @@ runtest hit \ '[0,1,2,3,4,5,6,7,8,9,{"result":"hit"}]' 10 result runtest hit '[{},[],{"result":"hit"}]' 2 result +# Empty caller query terms are unsupported and fail when reached. +runteststatus 2 '' '{"":42}' '' +runteststatus 2 '' '{"outer":{"":"value"}}' outer '' +runteststatus 2 '' '{"outer":{"leaf":1}}' '' outer +runteststatus 2 '' '{"outer":{"leaf":1}}' outer '' leaf +runteststatus 2 '' '{"outer":{"leaf":1}}' outer leaf '' +runteststatus 1 '' '{"outer":{"leaf":1}}' missing '' +runtest 7 '{"":42,"normal":7}' normal +runtest '{"":42}' '{"":42}' +runtest '' '{"value":""}' value + # Representative Kubernetes-shaped data used by build and shell automation kubernetes_json='{"items":[{"metadata":{"name":"api","annotations":{"example.com/config":"line1\nline2"}},"spec":{"nodeName":null,"containers":[{"name":"app","env":[{"name":"MODE","value":""}]}]}}]}' runtest api "$kubernetes_json" items 0 metadata name