]> git.plutz.net Git - rigidfind/blob - json.sh
Squashed 'cgilite/' content from commit 426dac5
[rigidfind] / json.sh
1 #!/bin/sh
2
3 [ -n "$include_json" ] && return 0
4 include_json="$0"
5
6 . "${_EXEC:-.}/cgilite/db23.sh"
7
8 # debug(){ [ $# -gt 0 ] && printf '%s\n' "$@" >&2 || tee -a /dev/stderr; }
9
10 json_except() {
11   printf '%s\n' "$@" >&2;
12   printf 'Exc: %s\n' "$json_document" >&2
13 }
14
15 json_space() {
16   while true; do case "$json_document" in
17     [" ${BR}${CR}       "]*) json_document="${json_document#?}";;
18     *) break ;;
19   esac; done
20 }
21
22 json_string() {
23   local string json_document="$json_document" end=0
24
25   json_space
26   case $json_document in
27     \"*) json_document="${json_document#?}"
28       ;;
29     *) json_except "Expected string specifyer starting with (\")"
30       return 1
31       ;;
32   esac
33   while [ "$json_document" ]; do case $json_document in
34     \\?*)
35       string="${string}${json_document%"${json_document#??}"}"
36       json_document="${json_document#??}"
37       ;;
38     \"*)
39       json_document="${json_document#?}"
40       end=1
41       break
42       ;;
43     *) 
44       string="${string}${json_document%"${json_document#?}"}"
45       json_document="${json_document#?}"
46       ;;
47   esac; done
48
49   if [ $end -eq 0 ]; then
50     json_except "Document ended mid-string"
51     return 1
52   fi
53
54   printf "%s    %s\n" "$(STRING "$string")" "$json_document"
55 }
56
57 json_key() {
58   local key json_document="$json_document"
59
60   json_space
61   case $json_document in
62     \"*)
63       key="$(json_string)" || return 1
64       json_document="${key#*    }"
65       key="${key%%      *}"
66       ;;
67     *) json_except "Expected key specifyer starting with '\"'"
68       return 1
69       ;;
70   esac
71   json_space
72   case $json_document in
73     :*) json_document="${json_document#?}"
74       ;;
75     *) json_except "Expected value separator \":\""
76       return 1
77       ;;
78   esac
79
80   printf '%s    %s\n' "$key" "$json_document"
81 }
82
83 json_number() {
84   local number json_document="$json_document"
85
86   json_space
87   number="${json_document%%["   ${BR}${CR} ,}]"]*}"
88   json_document="${json_document#"$number"}"
89   if ! number="$(printf %f "$number")"; then
90     json_except "Invalid number format"
91     return 1
92   fi
93
94   printf '%s    %s\n' "${number%.000000}" "$json_document"
95 }
96
97 json_array() {
98   local struct="$(DB2 new)" value json_document="$json_document"
99
100   json_space
101   case $json_document in
102     "["*) json_document="${json_document#?}"
103       ;;
104     *) json_except "Expected array starting with \"[\""
105       return 1
106       ;;
107   esac
108
109   json_space
110   case $json_document in
111     "]"*)
112       printf "%s        %s\n" "" "${json_document#?}"
113       return 0
114       ;;
115   esac
116
117   while :; do
118     json_space
119
120     value="$(json_value)" || return 1
121     json_document="${value#*    }"
122     value="$(UNSTRING "${value%%        *}")"
123
124        struct="$(DB2 "$struct" append "@" "$value")" \
125     || struct="$(DB2 "$struct" set    "@" "$value")"
126
127     json_space
128     case $json_document in
129       ,*) json_document="${json_document#?}"
130         ;;
131       "]"*) json_document="${json_document#?}"
132         break
133         ;;
134       *) json_except "Unexpected character mid-array"
135         return 1
136         ;;
137     esac
138   done
139
140   printf "%s    %s\n" "$(STRING "$struct")" "$json_document"
141 }
142
143 json_object() {
144   local struct="$(DB2 new)" key value json_document="$json_document"
145
146   json_space
147   case $json_document in
148     "{"*) json_document="${json_document#?}"
149       ;;
150     *) json_except "Expected object starting with \"{\""
151       return 1
152       ;;
153   esac
154
155   json_space
156   case $json_document in
157     "}"*)
158       printf "%s        %s\n" "" "${json_document#?}"
159       return 0
160       ;;
161   esac
162
163   while :; do
164     json_space
165
166     key="$(json_key)" || return 1
167     json_document="${key#*      }"
168     key="$(UNSTRING "${key%%    *}")"
169
170     value="$(json_value)" || return 1
171     json_document="${value#*    }"
172     value="$(UNSTRING "${value%%        *}")"
173
174     struct="$(DB2 "$struct" set "$key" "$value")"
175
176     json_space
177     case $json_document in
178       ,*) json_document="${json_document#?}"
179         ;;
180       "}"*) json_document="${json_document#?}"
181         break
182         ;;
183       *) json_except "Unexpected character mid-object"
184         return 1
185         ;;
186     esac
187   done
188
189   printf "%s    %s\n" "$(STRING "$struct")" "$json_document"
190 }
191
192 json_value() {
193   local value json_document="$json_document"
194   json_type=""
195
196   json_space
197   case $json_document in
198     \"*)
199       value="$(json_string)" || return 1
200       json_document="${value#*  }"
201       value="str:${value%%      *}"
202       json_type=string
203       ;;
204     [+-.0-9]*)
205       value="$(json_number)" || return 1
206       json_document="${value#*  }"
207       value="num:${value%%      *}"
208       json_type=number
209       ;;
210     "{"*)
211       value="$(json_object)" || return 1
212       json_document="${value#*  }"
213       value="obj:${value%%      *}"
214       json_type=object
215       ;;
216     "["*)
217       value="$(json_array)" || return 1
218       json_document="${value#*  }"
219       value="arr:${value%%      *}"
220       json_type=array
221       ;;
222     null*)
223       json_document="${json_document#null}"
224       value="null"
225       json_type=null
226       ;;
227     true*)
228       json_document="${json_document#true}"
229       value="true"
230       json_type=boolean
231       ;;
232     false*)
233       json_document="${json_document#false}"
234       value="false"
235       json_type=boolean
236       ;;
237   esac
238
239   printf "%s    %s\n" "$value" "$json_document"
240 }
241
242 json_load() {
243   local json_document="$1" json
244
245   json_value |UNSTRING
246 }
247
248 json_get() {
249   local json="$1" jpath="${2#.}" key idx
250   json_type=''
251
252   case $json in
253     str:*) json_type="string";;
254     arr:*) json_type="array";;
255     obj:*) json_type="object";;
256     num:*) json_type="number";;
257     true|false)
258            json_type="boolean";;
259     null)  json_type="null";;
260   esac
261
262   case $jpath in
263     "")
264       printf %s\\n "${json#???:}"
265       return 0
266       ;;
267     "["[0-9]*"]"*)
268       idx="${jpath%%"]"*}" idx="${idx#"["}"
269       jpath="${jpath#"["*"]"}"
270       ;;
271     "['"*"']"*)
272       key="${jpath%%"']"*}" key="${key#"['"}"
273       jpath="${jpath#"['"*"']"}"
274       ;;
275     "$"*)
276       jpath="${jpath#?}"
277       ;;
278     *) key="${jpath%%[".["]*}"
279       jpath="${jpath#"$key"}"
280       ;;
281   esac
282
283   if   [ "$key" -a "$json_type" = object ]; then
284     if ! json="$(DB2 "${json#obj:}" get "$key")"; then
285       debug "Key not found: \"$key\""
286       return 1
287     fi
288   elif [ "$idx" -a "$json_type" = array ]; then
289     if ! json="$(DB2 "${json#arr:}" get @ "$(( idx + 1 ))")"; then
290       debug "Array index not found: \"$idx\""
291       return 1
292     fi
293   elif [ "$key" ]; then
294     debug "Cannot select key (\"$key\") from value of type \"$json_type\""
295     return 1
296   elif [ "$idx" ]; then
297     debug "Cannot select index ($idx) from value of type \"$json_type\""
298     return 1
299   fi
300   json_get "$json" "$jpath"
301   return $?
302 }
303
304 json_dump_string() {
305   local in="$1" out=''
306   while [ "$in" ]; do case $in in
307     \\*) out="${out}\\\\"; in="${in#\\}" ;;
308     "$BR"*) out="${out}\\n"; in="${in#${BR}}" ;;
309     "$CR"*) out="${out}\\r"; in="${in#${CR}}" ;;
310     "   "*) out="${out}\\t"; in="${in#  }" ;;
311     \"*) out="${out}\\\""; in="${in#\"}" ;;
312     *) out="${out}${in%%[\\${CR}${BR}   \"]*}"; in="${in#"${in%%[\\${BR}${CR}   \"]*}"}" ;;
313   esac; done
314   printf '"%s"' "${out}"
315 }
316
317 json_dump_array() {
318   local json="$1" value out=''
319
320   for value in $(DB2 "$json" iterate @); do
321     out="${out},$(json_dump "$(UNSTRING "$value")")"
322   done
323   printf '[%s]' "${out#,}"
324 }
325
326 json_dump_object() {
327   local json="$1" key value out=''
328
329   while read -r key value; do
330     out="${out},$(json_dump_string "$(UNSTRING "$key")"):$(json_dump "$(UNSTRING "$value")")"
331   done <<-EOF
332         ${json}
333         EOF
334   printf '{%s}' "${out#,}"
335 }
336
337 json_dump() {
338   local json="$1"
339
340   case $json in
341     str:*)
342       json_dump_string "${json#str:}"
343       ;;
344     arr:*)
345       json_dump_array "${json#arr:}"
346       ;;
347     obj:*)
348       json_dump_object "${json#obj:}"
349       ;;
350     num:*)
351       printf "${json#num:}"
352       ;;
353     true|false|null)
354       printf %s\\n "$json"
355       ;;
356     *)
357       json_dump_string "${json}"
358       ;;
359   esac
360 }