beginnings of parser
[scpubgit/TenDotTcl.git] / json.tcl
CommitLineData
7b31d3c3 1namespace eval ten::json {
2
3 namespace eval deparse {
4
5 variable quotes [
6 list "\"" "\\\"" / \\/ \\ \\\\ \b \\b \f \\f \n \\n \r \\r \t \\t
7 ]
8
9 variable indent
10 variable indentBy 0
11 variable indentIncr
12 variable nl
13
14 proc indent_one {} {
15 variable indentIncr
16 variable indentBy [ expr $indentBy + $indentIncr ]
17 variable indent [ string repeat " " $indentBy ]
18 }
19
20 proc outdent_one {} {
21 variable indentIncr
22 variable indentBy [ expr $indentBy - $indentIncr ]
23 variable indent [ string repeat " " $indentBy ]
24 }
25
26 proc str {str} {
27 variable quotes
28 return \"[ string map $quotes $str ]\"
29 }
30
31 proc num {num} {
32 return $num
33 }
34
35 proc list {args} {
36 variable indent
37 variable nl
38 set out \[
39 indent_one
40 foreach el [lrange $args 0 end] {
41 append out $nl$indent
ced6e98e 42 append out [ deparse $el ],
7b31d3c3 43 }
44 outdent_one
45 append out $nl$indent\]
46 return $out
47 }
48
49 proc obj {args} {
50 variable indent
51 variable nl
52 set out \{
53 indent_one
54 dict for {k v} $args {
ced6e98e 55 append out $nl$indent[ str $k ]:\ [ deparse $v ],
7b31d3c3 56 }
57 outdent_one
58 append out $nl$indent\}
59 }
60
ced6e98e 61 proc deparse {data} {
62 switch -regexp [lindex $data 0] {
63 ^true|false|null$ { uplevel 1 return [lindex $data 0] }
64 ^num|str|obj|list$ {}
65 default { error [ concat "Invalid JSON type " [lindex $data 0 0] ] }
7b31d3c3 66 }
f5961f8c 67 eval $data
7b31d3c3 68 }
69 }
70
71 proc deparse_json {data {indentIncr 2}} {
72 set deparse::indentBy 0
73 set deparse::indentIncr $indentIncr
74 if [expr $indentIncr == 0] {
75 set deparse::nl ""
76 } else {
77 set deparse::nl "\n"
78 }
a001e5b2 79 deparse::deparse $data
80 }
81
82 namespace eval tclify {
83
84 proc str {str} { return $str }
85
86 proc num {num} { return $num }
87
88 proc obj {args} {
89 return $args
90 }
91
92 proc tclify {data} {
93 switch -regexp [lindex $data 0] {
94 ^true|false|null$ { uplevel 1 return [lindex $data 0] }
95 ^num|str|obj|list$ {}
96 default { error [ concat "Invalid JSON type " [lindex $data 0 0] ] }
97 }
f5961f8c 98 eval $data
a001e5b2 99 }
100 }
101
102 proc tclify_json {data} {
103 tclify::tclify $data
7b31d3c3 104 }
f5961f8c 105
106 namespace eval parse {
107
108 variable json
109
110 proc eat_comma {} {
111 variable json
112 set json [string trimleft $json]
113 if {[string index $json 0] eq ","} {
114 set json [string range $json 1 end]
115 }
116 }
117
118 proc parse_list {} {
119 variable json
120 set json [ string range $json 1 end ]
121 set tcl {list}
122 while {"$json" ne ""} {
123 if {[string index $json 0] eq "]"} {
124 return $tcl
125 }
126 lappend tcl [ parse ]
127 eat_comma
128 }
129 error "Ran out of JSON. Confused now."
130 }
131
132 proc parse_str {} {
133 variable json
134puts $json
135 # like Text::Balanced except ugly (borrowed from tcvJSON's code)
136 set reStr {(?:(?:\")(?:[^\\\"]*(?:\\.[^\\\"]*)*)(?:\"))}
137 if {![regexp $reStr $json string]} {
138 error [ concat "Invalid string: " [string range $json 0 32]
139 }
140 set json [string range $json [string length $string] end]
141 # chop off outer ""s and substitute backslashes
142 # This does more than the RFC-specified backslash sequences,
143 # but it does cover them all
144 list str [subst -nocommand -novariable [string range $string 1 end-1]]
145 }
146
147 proc parse {} {
148 variable json
149 set json [string trimleft $json]
150 if {$json eq ""} {
151 return
152 }
153 switch -regexp [string index $json 0] {
154 {\{} { parse_obj }
155 {\[} { parse_list }
156 {\"} { parse_str }
157
158 {[-0-9]} { parse_number }
159
160 default { error "argh" }
161 }
162 }
163 }
164
165 proc parse_json {json} {
166 set parse::json $json
167 parse::parse
168 }
7b31d3c3 169}
170
a001e5b2 171set ex_json { list {str foo} {num 0} {obj __remote_object__ {str 512}} {null} }
172
f5961f8c 173set jtext {
174 [
175 "foo",
176 0,
177 {
178 "__remote_object__": "512",
179 },
180 null,
181 ]
182}
183
a001e5b2 184puts [ ten::json::deparse_json $ex_json 2 ]
185
186dict for {k v} [ ten::json::tclify_json [
187 lindex [ ten::json::tclify_json $ex_json ] 2
188] ] { puts "$k: $v" }
f5961f8c 189
190#puts [ ten::json::parse_json $jtext ]
191
192puts [ ten::json::parse_json {["foo"]} ]