1# json.tcl -- 2# 3# The JSON export plugin. Generation of Java Script Object Notation. 4# 5# Copyright (c) 2009 Andreas Kupries <andreas_kupries@sourceforge.net> 6# 7# See the file "license.terms" for information on usage and redistribution 8# of this file, and for a DISCLAIMER OF ALL WARRANTIES. 9# 10# RCS: @(#) $Id: export_json.tcl,v 1.2 2009/08/07 18:53:11 andreas_kupries Exp $ 11 12# This package is a plugin for the doctools::idx v2 system. It takes 13# the list serialization of a keyword index and produces text in JSON 14# format. 15 16# ### ### ### ######### ######### ######### 17## Requisites 18 19# @mdgen NODEP: doctools::idx::export::plugin 20 21package require Tcl 8.4 22package require doctools::idx::export::plugin ; # Presence of this 23 # pseudo package 24 # indicates execution 25 # inside of a properly 26 # initialized plugin 27 # interpreter. 28package require doctools::idx::structure ; # Verification that 29 # the input is proper. 30package require textutil::adjust 31 32# ### ### ### ######### ######### ######### 33## API. 34 35proc export {serial configuration} { 36 37 # Phase I. Check that we got a canonical index serialization. That 38 # makes the unpacking easier, as we can mix it with the 39 # generation of the output, knowing that everything is 40 # already sorted as it should be. 41 42 ::doctools::idx::structure verify-as-canonical $serial 43 44 # ### ### ### ######### ######### ######### 45 # Configuration ... 46 # * Standard entries 47 # - user = person running the application doing the formatting 48 # - format = name of this format 49 # - file = name of the file the index came from. Optional. 50 # - map = maps symbolic references to actual file path. Optional. 51 # * json/format specific entries 52 # - indented = boolean. objects indented per the index structure. 53 # - aligned = boolean. object keys tabular aligned vertically. 54 # 55 # Notes 56 # * This format ignores 'map' even if set, as the written json 57 # contains the symbolic references and only them. 58 # * aligned => indented 59 60 # Combinations of the format specific entries 61 # N I A | 62 # - - - + --------------------- 63 # 0 0 0 | Ultracompact (no whitespace, single line) 64 # 1 0 0 | Compact (no whitespace, multiple lines) 65 # 1 1 0 | Indented 66 # 1 0 1 | Tabular aligned references 67 # 1 1 1 | Indented + Tabular aligned references 68 # - - - + --------------------- 69 # 0 1 0 | Not possible, per the implications above. 70 # 0 0 1 | ditto 71 # 0 1 1 | ditto 72 # - - - + --------------------- 73 74 # Import the configuration and initialize the internal state 75 array set config { 76 indented 0 77 aligned 0 78 } 79 array set config $configuration 80 81 # Force the implications mentioned in the notes above. 82 if {$config(aligned)} { 83 set config(indented) 1 84 } 85 86 # ### ### ### ######### ######### ######### 87 88 # Phase II. Generate the output, taking the configuration into 89 # account. We construct this from the inside out. 90 91 # Unpack the serialization. 92 array set idx $serial 93 array set idx $idx(doctools::idx) 94 unset idx(doctools::idx) 95 96 set keywords {} 97 foreach {kw references} $idx(keywords) { 98 set tmp {} 99 foreach id $references { lappend tmp [JsonString $id] } 100 lappend keywords $kw [JsonArrayList $tmp] 101 } 102 103 if {$config(aligned)} { set max 9 } 104 105 set references {} 106 foreach {id decl} $idx(references) { 107 foreach {type label} $decl break 108 set type [JsonString $type] 109 set label [JsonString $label] 110 if {$config(aligned)} { 111 set type [FmtR max $type] 112 } 113 lappend references $id [JsonArray $type $label] 114 } 115 116 return [JsonObject doctools::idx \ 117 [JsonObject \ 118 label [JsonString $idx(label)] \ 119 keywords [JsonObjectDict $keywords] \ 120 references [JsonObjectDict $references] \ 121 title [JsonString $idx(title)]]] 122 123 # ### ### ### ######### ######### ######### 124} 125 126# ### ### ### ######### ######### ######### 127 128proc JsonQuotes {} { 129 return [list "\"" "\\\"" / \\/ \\ \\\\ \b \\b \f \\f \n \\n \r \\r \t \\t] 130} 131 132proc JsonString {s} { 133 return "\"[string map [JsonQuotes] $s]\"" 134} 135 136proc JsonArray {args} { 137 upvar 1 config config 138 return [JsonArrayList $args] 139} 140 141proc JsonArrayList {list} { 142 # compact form. 143 return "\[[join $list ,]\]" 144} 145 146proc JsonObject {args} { 147 upvar 1 config config 148 return [JsonObjectDict $args] 149} 150 151proc JsonObjectDict {dict} { 152 # The dict maps string keys to json-formatted data. I.e. we have 153 # to quote the keys, but not the values, as the latter are already 154 # in the proper format. 155 upvar 1 config config 156 157 set tmp {} 158 foreach {k v} $dict { lappend tmp [JsonString $k] $v } 159 set dict $tmp 160 161 if {$config(aligned)} { Align $dict max } 162 163 if {$config(indented)} { 164 set content {} 165 foreach {k v} $dict { 166 if {$config(aligned)} { set k [FmtR max $k] } 167 if {[string match *\n* $v]} { 168 # multi-line value 169 lappend content " $k : [textutil::adjust::indent $v { } 1]" 170 } else { 171 # single line value. 172 lappend content " $k : $v" 173 } 174 } 175 if {[llength $content]} { 176 return "\{\n[join $content ,\n]\n\}" 177 } else { 178 return "\{\}" 179 } 180 } else { 181 # ultra compact form. 182 set tmp {} 183 foreach {k v} $dict { lappend tmp "$k:$v" } 184 return "\{[join $tmp ,]\}" 185 } 186} 187 188proc Align {dict mv} { 189 upvar 1 $mv max 190 # Generate a list of references sortable by name, and also find the 191 # max length of all relevant names. 192 set max 0 193 foreach {str _} $dict { Max max $str } 194 return 195} 196 197proc Max {v str} { 198 upvar 1 $v max 199 set x [string length $str] 200 if {$x <= $max} return 201 set max $x 202 return 203} 204 205proc FmtR {v str} { 206 upvar 1 $v max 207 return $str[string repeat { } [expr {$max - [string length $str]}]] 208} 209 210# ### ### ### ######### ######### ######### 211## Ready 212 213package provide doctools::idx::export::json 0.1 214return 215