2
# STUBS handling -- Container.
4
# (c) 2011 Andreas Kupries http://wiki.tcl.tk/andreas%20kupries
6
# A stubs table is represented by a dictionary value.
7
# A container is a variable holding a stubs table value.
9
# stubs table dictionary keys
13
# The name of the entire library. This value is used to compute
14
# the USE_*_STUB_PROCS macro and the name of the init file.
18
# A dictionary indexed by interface name that is used to maintain
19
# the set of valid interfaces. The value is empty.
23
# Storage class specifier for external function declarations.
24
# Normally "EXTERN", may be set to something like XYZAPI
28
# The epoch and revision numbers of the interface currently being defined.
29
# (@@@TODO: should be an array mapping interface names -> numbers)
33
# A dictionary indexed by interface name that contains the set of
34
# subinterfaces that should be defined for a given interface.
38
# This three dimensional dictionary is indexed first by interface
39
# name, second by platform name, and third by a numeric
40
# offset. Each numeric offset contains the C function
41
# specification that should be used for the given entry in the
42
# table. The specification consists of a list in the form returned
43
# by ParseDecl in the stubs reader package, i.e.
45
# decl = list (return-type fun-name arguments)
46
# arguments = void | list (arg-info ...)
47
# arg-info = list (type name ?array?)
52
# This two dimensional dictionary is indexed first by interface name,
53
# and second by platform name. The associated entry contains the
54
# largest numeric offset used for a given interface/platform
57
# # ## ### ##### ######## #############
60
package require Tcl 8.4
61
package require dict84 ; # Ensure presence of a dict command.
63
namespace eval ::stubs::container {}
65
# # ## ### ##### ######## #############
68
proc ::stubs::container::new {} {
81
# Methods to incrementally fill the container with data. Strongly
82
# related to the API commands of the stubs reader package.
84
proc ::stubs::container::library {tablevar name} {
86
dict set t library $name
90
proc ::stubs::container::interface {tablevar name} {
92
if {[dict exists $t interfaces $name]} {
93
return -code error "Duplicate declaration of interface \"$name\""
95
dict set t interfaces $name {}
99
proc ::stubs::container::scspec {tablevar value} {
101
dict set t scspec $value
105
proc ::stubs::container::epoch {tablevar value} {
108
if {![string is integer -strict $value]} {
109
return -code error "Expected integer for epoch, but got \"$value\""
112
dict set t epoch $value
116
proc ::stubs::container::hooks {tablevar interface names} {
118
dict set t hooks $interface $names
122
proc ::stubs::container::declare {tablevar interface index platforms decl} {
123
variable legalplatforms
126
#puts "DECLARE ($interface $index) \[$platforms\] =\n\t'[join $decl "'\n\t'"]'"
128
if {![dict exists $t interfaces $interface]} {
129
return -code error "Unknown interface \"$interface\""
131
if {![string is integer -strict $index]} {
132
return -code error "Bad index \"$index\", expected integer"
135
# legal platform codes
136
# - unix, win, macosx, x11, aqua
138
# Check for duplicate declarations, then add the declaration and
139
# bump the lastNum counter if necessary.
141
foreach platform $platforms {
142
if {![dict exists $legalplatforms $platform]} {
143
set expected [linsert [join [lsort -dict [dict keys $legalplatforms]] {, }] end-1 or]
144
return -code error "Bad platform \"$platform\", expected one of $expected"
147
set key $interface,$platform,$index
148
if {[dict exists $t stubs $key]} {
150
"Duplicate entry: declare $interface $index $platforms $decl"
154
if {![llength $decl]} return
158
foreach platform $platforms {
159
set group $interface,$platform
160
set key $interface,$platform,$index
162
dict set t stubs $key $decl
163
if {![dict exists $t last $group] ||
164
($index > [dict get $t last $group])} {
165
dict set t last $group $index
171
# # ## ### ##### ######## #############
174
proc ::stubs::container::library? {table} {
175
return [dict get $table library]
178
proc ::stubs::container::hooks? {table interface} {
179
if {![dict exists $table interfaces $interface]} {
180
return -code error "Unknown interface \"$interface\""
182
return [dict exists $table hooks $interface]
185
proc ::stubs::container::slot? {table interface platform at} {
186
if {![dict exists $table interfaces $interface]} {
187
return -code error "Unknown interface \"$interface\""
189
return [dict exists $table stubs $interface,$platform,$at]
192
proc ::stubs::container::scspec? {table} {
193
return [dict get $table scspec]
196
proc ::stubs::container::revision? {table} {
197
return [dict get $table revision]
200
proc ::stubs::container::epoch? {table} {
201
return [dict get $table epoch]
204
# # ## ### ##### ######## #############
207
proc ::stubs::container::interfaces {table} {
208
return [dict keys [dict get $table interfaces]]
211
proc ::stubs::container::hooksof {table interface} {
212
if {![dict exists $table interfaces $interface]} {
213
return -code error "Unknown interface \"$interface\""
215
if {![dict exists $table hooks $interface]} {
218
return [dict get $table hooks $interface]
221
proc ::stubs::container::platforms {table interface} {
222
if {![dict exists $table interfaces $interface]} {
223
return -code error "Unknown interface \"$interface\""
226
#checker exclude warnArgWrite
228
#checker -scope block exclude warnUndefinedVar
229
# 'last' is dict element.
230
foreach k [dict keys $last $interface,*] {
231
lappend res [lindex [split $k ,] end]
237
proc ::stubs::container::lastof {table interface {platform {}}} {
238
if {![dict exists $table interfaces $interface]} {
239
return -code error "Unknown interface \"$interface\""
241
if {[llength [info level 0]] == 4} {
242
set key $interface,$platform
243
if {![dict exists $table last $key]} {
246
return [dict get $table last $key]
250
#checker exclude warnArgWrite
252
#checker -scope block exclude warnUndefinedVar
253
# 'last' is dict element.
254
foreach k [dict keys $last $interface,*] {
255
lappend res [dict get $last $k]
261
proc ::stubs::container::slotplatforms {table interface at} {
262
if {![dict exists $table interfaces $interface]} {
263
return -code error "Unknown interface \"$interface\""
266
#checker exclude warnArgWrite
268
#checker -scope block exclude warnUndefinedVar
269
# 'stubs' is dict element.
270
foreach k [dict keys $stubs $interface,*,$at] {
271
lappend res [lindex [split $k ,] 1]
277
proc ::stubs::container::slot {table interface platform at} {
278
if {![dict exists $table interfaces $interface]} {
279
return -code error "Unknown interface \"$interface\""
281
if {![dict exists $table stubs $interface,$platform,$at]} {
282
return -code error "Unknown slot \"$platform,$at\""
284
return [dict get $table stubs $interface,$platform,$at]
287
# # ## ### ##### ######## #############
288
## Serialize, also nicely formatted for readability.
290
proc ::stubs::container::print {table} {
292
lappend lines "stubs [list [library? $table]] \{"
293
lappend lines " scspec [list [scspec? $table]]"
294
lappend lines " epoch [list [epoch? $table]]"
295
lappend lines " revision [list [revision? $table]]"
297
foreach if [interfaces $table] {
298
lappend lines " interface [list $if] \{"
299
lappend lines " hooks [list [hooksof $table $if]]"
302
foreach l [lastof $table $if] {
303
if {$l > $n} { set n $l }
305
# n = max lastof for the interface.
307
for {set at 0} {$at <= $n} {incr at} {
309
set pl [slotplatforms $table $if $at]
310
if {![llength $pl]} continue
313
lappend d $p [slot $table $if $p $at]
314
#puts |[lindex $d end-1]|[lindex $d end]|
316
# d = list of decls for the slot, per platform.
317
# invert and collapse...
319
foreach {d plist} [Invert $d] {
323
# d = list (rtype fname arguments)
324
# arguments = list (argdef)
325
# argdef = list (atype aname arrayflag)
326
# | list (atype aname)
329
lassign $d rtype fname fargs
331
lappend lines " declare $at [list $plist] \{"
332
lappend lines " function [list $fname]"
333
lappend lines " return [list $rtype]"
335
lappend lines " argument [list $a]"
346
return [join $lines \n]
349
proc ::stubs::container::Invert {dict} {
350
# input dict : key -> list(value)
351
# result is a dict : value -> list(key)
354
foreach {k v} $dict {
359
foreach k [lsort -dict [array names res]] {
360
lappend final $k [lsort -dict $res($k)]
365
# # ## ### ##### ######## #############
368
namespace eval ::stubs::container {
369
variable legalplatforms {
379
new library interface scspec epoch hooks declare \
380
library? hooks? slot? scspec? revision? epoch? \
381
interfaces hooksof platforms lastof slotplatforms slot
385
package provide stubs::container 1