1 namespace eval ten::json {
3 namespace eval deparse {
6 list "\"" "\\\"" / \\/ \\ \\\\ \b \\b \f \\f \n \\n \r \\r \t \\t
16 variable indentBy [ expr $indentBy + $indentIncr ]
17 variable indent [ string repeat " " $indentBy ]
22 variable indentBy [ expr $indentBy - $indentIncr ]
23 variable indent [ string repeat " " $indentBy ]
28 return \"[ string map $quotes $str ]\"
36 switch [string index $str end] {
37 , { string range $str 0 end-1 }
38 default { return $str }
47 foreach el [lrange $args 0 end] {
49 append out [ deparse $el ],
51 set out [ decomma $out ]
53 append out $nl$indent\]
62 dict for {k v} $args {
63 append out $nl$indent[ str $k ]:\ [ deparse $v ],
65 set out [ decomma $out ]
67 append out $nl$indent\}
71 switch -regexp [lindex $data 0] {
72 ^(true|false|null)$ { lindex $data 0 }
73 ^(num|str|obj|list)$ { eval $data }
74 default { error [ concat "Invalid JSON type " [lindex $data 0 0] ] }
79 proc deparse_json {data {indentIncr 2}} {
80 set deparse::indentBy 0
81 set deparse::indentIncr $indentIncr
82 if [expr $indentIncr == 0] {
87 deparse::deparse $data
90 namespace eval tclify {
92 proc str {str} { return $str }
94 proc num {num} { return $num }
101 switch -regexp [lindex $data 0] {
102 ^(true|false|null)$ { uplevel 1 return [lindex $data 0] }
103 ^(num|str|obj|list)$ {}
104 default { error [ concat "Invalid JSON type " [lindex $data 0 0] ] }
110 proc tclify_json {data} {
114 namespace eval parse {
120 set json [string trimleft $json]
123 proc eat_char {char} {
126 if {[string index $json 0] eq "$char"} {
133 set json [ string range $json 1 end ]
140 while {"$json" ne ""} {
142 if {[string index $json 0] eq "]"} {
146 lappend tcl [ parse ]
149 error "Ran out of JSON. Confused now."
156 while {"$json" ne ""} {
158 if {[string index $json 0] eq "\}"} {
163 lappend tcl [ parse_str ]
167 lappend tcl [ parse ]
170 error "Ran out of JSON. Confused now."
175 # like Text::Balanced except ugly (borrowed from tcvJSON's code)
176 set reStr {(?:(?:\")(?:[^\\\"]*(?:\\.[^\\\"]*)*)(?:\"))}
177 if {![regexp $reStr $json string]} {
178 error "Invalid string: $json"
180 set json [string range $json [string length $string] end]
181 # chop off outer ""s and substitute backslashes
182 # This does more than the RFC-specified backslash sequences,
183 # but it does cover them all
184 list str [subst -nocommand -novariable [string range $string 1 end-1]]
189 string is double -failindex last $json
191 error "Saw number but wasn't - $json"
193 set num [string range $json 0 [expr {$last - 1}]]
194 set json [string range $json $last end]
200 if [regexp {^(true|false|null)} $json matched] {
201 set json [ string range $json [ string length $matched ] end ]
204 error "Out of ideas parsing: $json"
214 switch -regexp [string index $json 0] {
219 {[-0-9]} { parse_num }
221 default { parse_bare }
226 proc parse_json {json} {
227 set parse::json [ string trim $json ]
228 set result [ parse::parse ]
230 if {$parse::json ne ""} {
231 error "Had JSON left over: $parse::json"
236 namespace export parse_json deparse_json tclify_json
239 set ex_json { list {str foo} {num 0} {obj __remote_object__ {str 512}} {null} }
246 "__remote_object__": "512",
252 namespace eval ten::connection {
254 proc receive_data_for {name} {
257 proc conn_setup {name input output initial_handlers} {
258 variable "${name}_input" $input
259 variable "${name}_input_closed" ""
260 variable "${name}_output" $output
261 variable "${name}_handlers"
262 array set "${name}_handlers" $initial_handlers
263 fileevent $input readable [list receive_data_for $name]
267 proc run_until_close {name} {
268 vwait "${name}_input_closed"
269 set close_value "\$${name}_input_closed"
274 proc teardown {name} {
275 close [ expr "\$${name}_output" ]
276 unset "${name}_input"
277 unset "${name}_input_closed"
278 unset "${name}_output"
279 unset "${name}_handlers"
282 namespace import ten::json::*
284 puts [ deparse_json $ex_json 2 ]
286 dict for {k v} [ tclify_json [
287 lindex [ tclify_json $ex_json ] 2
288 ] ] { puts "$k: $v" }
290 puts [ parse_json $jtext ]
292 puts [ parse_json {["foo",2345,["bar"]]} ]