Skip to content

Commit d4933f6

Browse files
committed
wip: Various improvements
1 parent 18b8b71 commit d4933f6

File tree

5 files changed

+127
-91
lines changed

5 files changed

+127
-91
lines changed

Diff for: README.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
# bash-json
22

3-
The _first_ Bash library for parsing JSON in pure Bash (no subshells, no exec, only builtins, etc.)
3+
The _first_ Bash library parsing JSON in Bash
44

5-
Made possible with [bash-object](https://github.com/hyperupcall/bash-object)
5+
## Summary
6+
7+
- Uses only builtins and no subshells
8+
- Leverages [bash-object](https://github.com/hyperupcall/bash-object)
69

710
## Installation
811

Diff for: pkg/src/public/bash-json.sh

+25-15
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,40 @@ bash_json.parse() {
99
local -a TOKENS=()
1010

1111
# Construct token sequence
12-
local i=
12+
local -i col=1 row=1 i=
1313
for ((i=0; i<${#content}; ++i)); do
1414
local char="${content:$i:1}"
1515

16+
local prefix="$col:$row:"
1617
case $char in
17-
'{') TOKENS+=('TOKEN_OPEN_CURLEYBRACE') ;;
18-
'}') TOKENS+=('TOKEN_CLOSE_CURLEYBRACE') ;;
19-
'[') TOKENS+=('TOKEN_OPEN_SQUAREBRACKET') ;;
20-
']') TOKENS+=('TOKEN_CLOSE_SQUAREBRACKET') ;;
21-
':') TOKENS+=('TOKEN_COLON') ;;
22-
',') TOKENS+=('TOKEN_COMMA') ;;
18+
'{') TOKENS+=("$prefix"'TOKEN_OPEN_CURLEYBRACE') ;;
19+
'}') TOKENS+=("$prefix"'TOKEN_CLOSE_CURLEYBRACE') ;;
20+
'[') TOKENS+=("$prefix"'TOKEN_OPEN_SQUAREBRACKET') ;;
21+
']') TOKENS+=("$prefix"'TOKEN_CLOSE_SQUAREBRACKET') ;;
22+
':') TOKENS+=("$prefix"'TOKEN_COLON') ;;
23+
',') TOKENS+=("$prefix"'TOKEN_COMMA') ;;
2324
'"')
2425
local str=
25-
bash_json.util_tokenize_string || true
26-
TOKENS+=($'\x01'"$str")
26+
bash_json.tokenize_string || true
27+
TOKENS+=($'\x01'"${prefix}${str}")
28+
col+=$((${#str}+1))
2729
;;
2830
[[:digit:]])
2931
local str=
30-
bash_json.util_tokenize_number || true
31-
TOKENS+=($'\x02'"$str")
32+
bash_json.tokenize_number || true
33+
TOKENS+=($'\x02'"${prefix}${str}")
34+
col+=$((${#str}-1))
35+
;;
36+
$'\n')
37+
row+=1
38+
col=0
39+
;;
40+
' '|$'\t'|$'\r')
41+
;;
42+
*) core.print_die "Failed: $char"
3243
;;
33-
' '|$'\t'|$'\n'|$'\r') ;;
34-
*) core.print_die "Failed: $char" ;;
3544
esac
45+
col+=1
3646
done; unset -v i
3747

3848
# Print token sequence
@@ -44,12 +54,12 @@ bash_json.parse() {
4454
done
4555

4656
# Construct object using token sequence
47-
if [ "${TOKENS[0]}" = 'TOKEN_OPEN_CURLEYBRACE' ]; then
57+
if [ "${TOKENS[0]##*:}" = 'TOKEN_OPEN_CURLEYBRACE' ]; then
4858
bash_json.parse_array_or_object "$variable_prefix" 0 '.'
4959

5060
REPLY1=$variable_prefix
5161
REPLY2='object'
52-
elif [ "${TOKENS[0]}" = 'TOKEN_OPEN_SQUAREBRACKET' ]; then
62+
elif [ "${TOKENS[0]##*:}" = 'TOKEN_OPEN_SQUAREBRACKET' ]; then
5363
bash_json.parse_array_or_object "$variable_prefix" 0 '.'
5464

5565
REPLY1=$variable_prefix

Diff for: pkg/src/util/parse.sh

+33-27
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,61 @@
11
# shellcheck shell=bash
22

33
bash_json.parse_array_or_object() {
4+
unset -v REPLY; REPLY=
45
local variable_prefix="$1"
56
local idx="$2"
67
local hier="$3"
78

8-
local idx_value="${TOKENS[$idx]}"
9+
bash_json_get_idx_values "$idx"
10+
local idx_value_0="$REPLY1" idx_row_0="$REPLY2" idx_col_0="$REPLY3"
911

1012
if ((idx > ${#TOKENS[@]} )); then
11-
core.print_die "Forgot to close an opening curley brace or bracket"
13+
bash_json.util_die "Forgot to close an opening curley brace or bracket"
1214
fi
1315

1416
# object
15-
if [ "$idx_value" = 'TOKEN_OPEN_CURLEYBRACE' ]; then
17+
if [ "$idx_value_0" = 'TOKEN_OPEN_CURLEYBRACE' ]; then
1618
idx=$((idx+1))
17-
idx_value=${TOKENS[$idx]}
19+
bash_json_get_idx_values "$idx"
20+
local idx_value_0="$REPLY1"; idx_row_0="$REPLY2" idx_col_0="$REPLY3"
1821
bash_json.util_is_within_bounds "$idx" 'Expected closing square curley bracket 1'
19-
# echo bbbbb $idx
20-
21-
until [ "$idx_value" = 'TOKEN_CLOSE_CURLEYBRACE' ]; do
22-
idx_value="${TOKENS[$idx]}"
23-
local idx_value_1="${TOKENS[$idx+1]}"
24-
local idx_value_2="${TOKENS[$idx+2]}"
25-
local idx_value_3="${TOKENS[$idx+3]}"
2622

23+
# Until we see a closing curley brace, we inspect each key-value pair
24+
until [ "$idx_value_0" = 'TOKEN_CLOSE_CURLEYBRACE' ]; do
25+
bash_json_get_idx_values $((idx))
26+
local idx_value_0="$REPLY1" idx_row_0="$REPLY2" idx_col_0="$REPLY3"
27+
bash_json_get_idx_values $((idx+1))
28+
local idx_value_1="$REPLY1" idx_row_1="$REPLY2" idx_col_1="$REPLY3"
29+
bash_json_get_idx_values $((idx+2))
30+
local idx_value_2="$REPLY1" idx_row_2="$REPLY2" idx_col_2="$REPLY3"
31+
bash_json_get_idx_values $((idx+3))
32+
local idx_value_3="$REPLY1" idx_row_3="$REPLY2" idx_col_3="$REPLY3"
33+
34+
printf '%s\n' "JJ: $idx_value_0 | $idx_value_1 | $idx_value_2 | $idx_value_3"
2735
# 0
28-
# TODO does not correctly proces numbers
29-
if ! bash_json.util_is_primitive "$idx_value"; then
30-
core.print_die "Expected a literal or object or array (dangling comma?)"
36+
if ! bash_json.util_is_primitive "$idx_value_0"; then
37+
bash_json.util_die "Expected a literal or object or array (dangling comma?)"
3138
fi
3239

3340
# 1
3441
if [ "$idx_value_1" != 'TOKEN_COLON' ]; then
35-
core.print_die "Expected a colon after an object key"
42+
bash_json.util_die "Expected a colon after an object key"
3643
fi
3744

3845
# 2
39-
# TODO does not correctly proces numbers
4046
if bash_json.util_is_primitive "$idx_value_2"; then
41-
bobject set-string --ref "$variable_prefix" "$hier.$idx_value" 'idx_value_2'
47+
bobject set-string --ref "$variable_prefix" "$hier.$idx_value_0" 'idx_value_2'
4248
elif [ "$idx_value_2" = 'TOKEN_OPEN_CURLEYBRACE' ]; then
43-
# echo descending to "$hier$idx_value" $idx
49+
# echo descending to "$hier$idx_value_0" $idx
4450
local -A obj=()
45-
local str="$hier${idx_value:1}"
51+
local str="$hier${idx_value_0:1}"
4652
bobject set-object --ref "$variable_prefix" "$str" 'obj'
4753
bash_json.parse_array_or_object "$variable_prefix" $((idx+2)) "$str"
4854
# echo v___ "$REPLY"
4955
idx=(REPLY + 4)
5056
continue
5157
else
52-
core.print_die "Expected a literal or object or array (as key of object)"
58+
bash_json.util_die "Expected a literal or object or array (as key of object)"
5359
fi
5460

5561
# 3
@@ -58,7 +64,7 @@ bash_json.parse_array_or_object() {
5864
elif [ "$idx_value_3" = 'TOKEN_CLOSE_CURLEYBRACE' ]; then
5965
break
6066
else
61-
core.print_die "Invalid, expected either comma or squigly bracket (got $idx_value_3)"
67+
bash_json.util_die "Invalid, expected either comma or squigly bracket (got $idx_value_3)"
6268
fi
6369

6470
# Continue loop
@@ -68,20 +74,20 @@ bash_json.parse_array_or_object() {
6874

6975
REPLY=$idx
7076
# array
71-
elif [ "$idx_value" = 'TOKEN_OPEN_SQUAREBRACKET' ]; then
77+
elif [ "$idx_value_0" = 'TOKEN_OPEN_SQUAREBRACKET' ]; then
7278
local -a temporary_array=()
7379

7480
idx=$((idx+1))
75-
idx_value="${TOKENS[$idx]}"
81+
idx_value_0="${TOKENS[$idx]}"
7682
bash_json.util_is_within_bounds "$idx" 'Expected closing square bracket 1'
7783

78-
until [ "$idx_value" = 'TOKEN_CLOSE_SQUAREBRACKET' ]; do
79-
idx_value="${TOKENS[$idx]}"
84+
until [ "$idx_value_0" = 'TOKEN_CLOSE_SQUAREBRACKET' ]; do
85+
idx_value_0="${TOKENS[$idx]}"
8086
local idx_value_1="${TOKENS[$idx+1]}"
8187

8288
# 0
83-
if bash_json.util_is_primitive "$idx_value"; then
84-
temporary_array+=("${idx_value:1}")
89+
if bash_json.util_is_primitive "$idx_value_0"; then
90+
temporary_array+=("${idx_value_0:1}")
8591
# bash_json.parse_array_or_object "$idx"
8692
else
8793
# TODO: ALPHA

Diff for: pkg/src/util/tokenize.sh

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# shellcheck shell=bash
2+
3+
bash_json.tokenize_string() {
4+
i=$((i+1))
5+
6+
local char="${content:$i:1}"
7+
8+
case $char in
9+
$'\x10'|$'\x11'|$'\x12'|$'\x13'|$'\x14')
10+
core.print_die 'Unexpected whitespace in string'
11+
;;
12+
'"'|'')
13+
return 1
14+
;;
15+
*)
16+
str+=$char
17+
;;
18+
esac
19+
20+
until bash_json.tokenize_string; do
21+
return 1
22+
done
23+
}
24+
25+
bash_json.tokenize_number() {
26+
case $char in
27+
$'\x09'|$'\x0B'|$'\x0C')
28+
core.print_die 'Unexpected whitespace in number'
29+
;;
30+
','|']')
31+
i=$((i-1))
32+
return 1
33+
;;
34+
$'\x0A'|'')
35+
i=$((i-1))
36+
return 1
37+
;;
38+
*)
39+
str+=$char
40+
;;
41+
esac
42+
43+
i=$((i+1))
44+
local char="${content:$i:1}"
45+
46+
until bash_json.tokenize_number; do
47+
return 1
48+
done
49+
}

Diff for: pkg/src/util/util.sh

+15-47
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,5 @@
11
# shellcheck shell=bash
22

3-
bash_json.util_tokenize_string() {
4-
i=$((i+1))
5-
6-
local char="${content:$i:1}"
7-
8-
case $char in
9-
$'\x10'|$'\x11'|$'\x12'|$'\x13'|$'\x14')
10-
core.print_die "Unexpected whitespace in string"
11-
;;
12-
'"'|'')
13-
return 1
14-
;;
15-
*)
16-
str+="$char"
17-
;;
18-
esac
19-
20-
until bash_json.util_tokenize_string; do
21-
return 1
22-
done
23-
}
24-
25-
bash_json.util_tokenize_number() {
26-
case $char in
27-
$'\x09'|$'\x0B'|$'\x0C')
28-
core.print_die "Unexpected whitespace in number"
29-
;;
30-
','|']')
31-
i=$((i-1))
32-
return 1
33-
;;
34-
$'\x0A'|'')
35-
return 1
36-
;;
37-
*)
38-
str+="$char"
39-
;;
40-
esac
41-
42-
i=$((i+1))
43-
local char="${content:$i:1}"
44-
45-
until bash_json.util_tokenize_number; do
46-
return 1
47-
done
48-
}
49-
503
bash_json.util_is_primitive() {
514
local value="$1"
525

@@ -65,3 +18,18 @@ bash_json.util_is_within_bounds() {
6518
core.print_die "$message"
6619
fi
6720
}
21+
22+
bash_json_get_idx_values() {
23+
unset -v REPLY{1,2,3}; REPLY1= REPLY2= REPLY3=
24+
local i="$1"
25+
26+
REPLY1=${TOKENS[$i]##*:}
27+
REPLY2=${TOKENS[$i]%%:*}
28+
REPLY3=${TOKENS[$i]#*:}; REPLY3=${REPLY3%%:*}
29+
}
30+
31+
bash_json.util_die() {
32+
local msg="$1"
33+
34+
core.print_die "$msg (row $idx_row, column: $idx_col)"
35+
}

0 commit comments

Comments
 (0)