File: record.tcl

package info (click to toggle)
tcllib 1.20%2Bdfsg-1
  • links: PTS
  • area: main
  • in suites: bullseye
  • size: 68,064 kB
  • sloc: tcl: 216,842; ansic: 14,250; sh: 2,846; xml: 1,766; yacc: 1,145; pascal: 881; makefile: 107; perl: 84; f90: 84; python: 33; ruby: 13; php: 11
file content (830 lines) | stat: -rw-r--r-- 21,261 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
#============================================================
# ::struct::record --
#
#    Implements a container data structure similar to a 'C'
#    structure. It hides the ugly details about keeping the
#    data organized by using a combination of arrays, lists
#    and namespaces.
#
#    Each record definition is kept in a master array
#    (_recorddefn) under the ::struct::record namespace. Each
#    instance of a record is kept within a separate namespace
#    for each record definition. Hence, instances of
#    the same record definition are managed under the
#    same namespace. This avoids possible collisions, and
#    also limits one big global array mechanism.
#
# Copyright (c) 2002 by Brett Schwarz
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# This code may be distributed under the same terms as Tcl.
#
#============================================================
#
####  FIX ERROR MESSAGES SO THEY MAKE SENSE (Wrong args)

namespace eval ::struct {}

namespace eval ::struct::record {

    ##
    ##  array of lists that holds the definition (variables) for each
    ##  record
    ##
    ##  _recorddefn(some_record) var1 var2 var3 ...
    ##
    variable _recorddefn

    ##
    ##  holds the count for each record in cases where the instance is
    ##  automatically generated
    ##
    ##  _count(some_record) 0
    ##

    ## This is not a count, but an id generator. Its value has to
    ## increase monotonicaly.

    variable _count

    ##
    ##  array that holds the defining record's name for each instances
    ##
    ##  _defn(some_instances) name_of_defining_record
    ##
    variable  _defn
    array set _defn {}

    ##
    ##  This holds the defaults for a record definition.  If no
    ##  default is given for a member of a record, then the value is
    ##  assigned to the empty string
    ##
    variable _defaults

    ##
    ##  These are the possible sub commands
    ##
    variable commands
    set commands [list define delete exists show]

    ##
    ##  This keeps track of the level that we are in when handling
    ##  nested records. This is kind of a hack, and probably can be
    ##  handled better
    ##
    set _level 0

    namespace export record
}

#------------------------------------------------------------
# ::struct::record::record --
#
#    main command used to access the other sub commands
#
# Arguments:
#    cmd_   The sub command (i.e. define, show, delete, exists)
#    args   arguments to pass to the sub command
#
# Results:
#  none returned
#------------------------------------------------------------
#
proc ::struct::record::record {cmd_ args} {
    variable commands

    if {[lsearch $commands $cmd_] < 0} {
        error "Sub command \"$cmd_\" is not recognized. Must be [join $commands ,]"
    }

    set cmd_ [string totitle "$cmd_"]
    return [uplevel 1 ::struct::record::${cmd_} $args]

}; # end proc ::struct::record::record


#------------------------------------------------------------
# ::struct::record::Define --
#
#    Used to define a record
#
# Arguments:
#    defn_    the name of the record definition
#    vars_    the variables of the record (as a list)
#    args     instances to be create during definition
#
# Results:
#   Returns the name of the definition during successful
#   creation.
#------------------------------------------------------------
#
proc ::struct::record::Define {defn_ vars_ args} {
    variable _recorddefn
    variable _count
    variable _defaults

    # puts .([info level 0])...

    set defn_ [Qualify $defn_]

    if {[info exists _recorddefn($defn_)]} {
        error "Record definition $defn_ already exists"
    }

    if {[lsearch [info commands] $defn_] >= 0} {
        error "Structure definition name can not be a Tcl command name"
    }

    set _defaults($defn_)   [list]
    set _recorddefn($defn_) [list]

    ##
    ##  Loop through the members of the record
    ##  definition
    ##
    foreach V $vars_ {
        set len [llength $V]
        set D ""

        if {$len == 2} {
	    ##  2 --> there is a default value
	    ##        assigned to the member

            set D [lindex $V 1]
            set V [lindex $V 0]

        } elseif {$len == 3} {
	    ##  3 --> there is a nested record
	    ##        definition given as a member
	    ##  V = ('record' record-name field-name)

            if {![string match "record" "[lindex $V 0]"]} {
                Delete record $defn_
                error "$V is a Bad member for record definition. Definition creation aborted."
            }

            set new [lindex $V 1]
            set new [Qualify $new]
	    # puts .\tchild=$new
            ##
            ##  Right now, there can not be circular records
            ##  so, we abort the creation
            ##
            if {[string match "$defn_" "$new"]} {
		# puts .\tabort
                Delete record $defn_
                error "Can not have circular records. Structure was not created."
            }

            ##
            ##  Will take care of the nested record later
            ##  We just join by :: because this is how it
            ##  use to be declared, so the parsing code
            ##  is already there.
            ##
            set V [join [lrange $V 1 2] "::"]
        }

	# puts .\tfield($V)=default($D)

        lappend _recorddefn($defn_) $V
        lappend _defaults($defn_)   $D
    }

    # Create class command as alias to instance creator.
    uplevel #0 [list interp alias \
		    {} $defn_ \
		    {} ::struct::record::Create $defn_]

    set _count($defn_) 0

    # Create class namespace. This will hold all the instance information.
    namespace eval ::struct::record${defn_} {
        variable values
        variable instances
	variable record

        set instances [list]
    }

    set ::struct::record${defn_}::record $defn_

    ##
    ##    If there were args given (instances), then
    ##    create them now
    ##
    foreach A $args {
        uplevel 1 [list ::struct::record::Create $defn_ $A]
    }

    # puts .=>${defn_}
    return $defn_

}; # end proc ::struct::record::Define


#------------------------------------------------------------
# ::struct::record::Create --
#
#    Creates an instance of a record definition
#
# Arguments:
#    defn_    the name of the record definition
#    inst_    the name of the instances to create
#    args     values to set to the record's members
#
# Results:
#   Returns the name of the instance for a successful creation
#------------------------------------------------------------
#
proc ::struct::record::Create {defn_ inst_ args} {
    variable _recorddefn
    variable _count
    variable _defn
    variable _defaults
    variable _level

    # puts .([info level 0])...

    set inst_ [Qualify "$inst_"]

    ##
    ##    test to see if the record
    ##    definition has been defined yet
    ##
    if {![info exists _recorddefn($defn_)]} {
        error "Structure $defn_ does not exist"
    }

    ##
    ##    if there was no argument given,
    ##    then assume that the record
    ##    variable is automatically
    ##    generated
    ##
    if {[string match "[Qualify #auto]" "$inst_"]} {
        set c $_count($defn_)
        set inst_ [format "%s%s" ${defn_} $_count($defn_)]
        incr _count($defn_)
    }

    ##
    ##    Test to see if this instance is already
    ##    created. This avoids any collisions with
    ##    previously created instances
    ##
    if {[info exists _defn($inst_)]} {
        incr _count($defn_) -1
        error "Instances $inst_ already exists"
    }

    set _defn($inst_) $defn_

    ##
    ##    Initialize record variables to defaults
    ##

    # Create instance command as alias of instance dispatcher.
    uplevel #0 [list interp alias {} ${inst_} {} ::struct::record::Cmd $inst_]

    # Locate manager namespace, i.e. class namespace for new instance
    set nsi [Ns $inst_]
    # puts .\tnsi=$nsi

    # Import the state of the manager namespace
    upvar 0 ${nsi}values    __values
    upvar 0 ${nsi}instances __instances

    set cnt 0
    foreach V $_recorddefn($defn_) D $_defaults($defn_) {

	# puts .\tfield($V)=default($D)

	set __values($inst_,$V) $D

        ##
        ##  Test to see if there is a nested record
        ##
        if {[regexp -- {([\w]*)::([\w]*)} $V -> def inst]} {

            if {$_level == 0} {
                set _level 2
            }

            ##
            ##  This is to guard against if the creation had failed,
            ##  that there isn't any lingering variables/alias around
            ##
            set def [Qualify $def $_level]

            if {![info exists _recorddefn($def)]} {
                Delete inst "$inst_"
                return
            }

            ##
            ##    evaluate the nested record. If there were values for
            ##    the variables passed in, then we assume that the
            ##    value for this nested record is a list corresponding
            ##    the the nested list's variables, and so we pass that
            ##    to the nested record's instantiation.  We then get
            ##    rid of those args for later processing.
            ##
            set cnt_plus [expr {$cnt + 1}]
            set mem [lindex $args $cnt]
            if {![string match "" "$mem"]} {
		if {![string match "-$inst" "$mem"]} {
                    Delete inst "$inst_"
                    error "$inst is not a member of $defn_"
                }
            }
            incr _level
            set narg [lindex $args $cnt_plus]

	    # Create instance of the nested record.
            eval [linsert $narg 0 Create $def ${inst_}.${inst}]

            set args [lreplace $args $cnt $cnt_plus]

            incr _level -1
        } else {
	    # Regular field, not a nested record. Create alias for
	    # field access.
            uplevel #0 [list interp alias \
			    {} ${inst_}.$V \
			    {} ::struct::record::Access $defn_ $inst_ $V]
            incr cnt 2
        }
    }; # end foreach variable

    # Remember new instance.
    lappend __instances $inst_

    # Apply field values handed to the instance constructor.
    foreach {k v} $args {
        Access $defn_ $inst_ [string trimleft "$k" -] $v
    }; # end foreach arg {}

    if {$_level == 2} {
	set _level 0
    }

    # puts .=>${inst_}
    return $inst_

}; # end proc ::struct::record::Create


#------------------------------------------------------------
# ::struct::record::Access --
#
#    Provides a common proc to access the variables
#    from the aliases create for each variable in the record
#
# Arguments:
#    defn_    the name of the record to access
#    inst_    the name of the instance to create
#    var_     the variable of the record to access
#    args     a value to set to var_ (if any)
#
# Results:
#    Returns the value of the record member (var_)
#------------------------------------------------------------
#
proc ::struct::record::Access {defn_ inst_ var_ args} {

    variable _recorddefn
    variable _defn

    set i [lsearch $_recorddefn($defn_) $var_]

    if {$i < 0} {
	error "$var_ does not exist in record $defn_"
    }

    if {![info exists _defn($inst_)]} {

	error "$inst_ does not exist"
    }

    if {[set idx [lsearch $args "="]] >= 0} {
        set args [lreplace $args $idx $idx]
    }

    set nsi [Ns $inst_]
    # puts .\tnsi=$nsi

    # Import the state of the manager namespace
    upvar 0 ${nsi}values    __values

    ##
    ##    If a value was given, then set it
    ##
    if {[llength $args] != 0} {

        set val_ [lindex $args 0]

        set __values($inst_,$var_) $val_
    }

    return $__values($inst_,$var_)

}; # end proc ::struct::record::Access


#------------------------------------------------------------
# ::struct::record::Cmd --
#
#    Used to process the set/get requests.
#
# Arguments:
#    inst_    the record instance name
#    args     For 'get' this is the record members to
#             retrieve. For 'set' this is a member/value
#             pair.
#
# Results:
#   For 'set' returns the empty string. For 'get' it returns
#   the member values.
#------------------------------------------------------------
#
proc ::struct::record::Cmd {inst_ args} {

    variable _defn

    set result [list]

    set len [llength $args]
    if {$len <= 1} {return [Show values "$inst_"]}

    set cmd [lindex $args 0]

    if {[string match "cget" "$cmd"]} {

	set cnt 0
	foreach k [lrange $args 1 end] {
	    if {[catch {set r [${inst_}.[string trimleft ${k} -]]} err]} {
		error "Bad option \"$k\""
	    }

	    lappend result $r
	    incr cnt
	}
	if {$cnt == 1} {set result [lindex $result 0]}
	return $result

    } elseif {[string match "config*" "$cmd"]} {

	set L [lrange $args 1 end]
	foreach {k v} $L {
	    ${inst_}.[string trimleft ${k} -] $v
	}

    } else {
	error "Wrong argument.
            must be \"object cget|configure args\""
    }

    return [list]

}; # end proc ::struct::record::Cmd


#------------------------------------------------------------
# ::struct::record::Ns --
#
#    This just constructs a fully qualified namespace for a
#    particular instance.
#
# Arguments;
#    inst_    instance to construct the namespace for.
#
# Results:
#    Returns the fully qualified namespace for the instance
#------------------------------------------------------------
#
proc ::struct::record::Ns {inst_} {

    variable _defn

    if {[catch {set ret $_defn($inst_)} err]} {
        return $inst_
    }

    return [format "%s%s%s" "::struct::record" $ret "::"]

}; # end proc ::struct::record::Ns


#------------------------------------------------------------
# ::struct::record::Show --
#
#     Display info about the record that exist
#
# Arguments:
#    what_    subcommand
#    record_  record or instance to process
#
# Results:
#    if what_ = record, then return list of records
#               definition names.
#    if what_ = members, then return list of members
#               or members of the record.
#    if what_ = instance, then return a list of instances
#               with record definition of record_
#    if what_ = values, then it will return the values
#               for a particular instance
#------------------------------------------------------------
#
proc ::struct::record::Show {what_ {record_ ""}} {
    variable _recorddefn
    variable _defn
    variable _defaults

    set record_ [Qualify $record_]

    ##
    ## We just prepend :: to the record_ argument
    ##
    #if {![string match "::*" "$record_"]} {set record_ "::$record_"}

    if {[string match "record*" "$what_"]} {
	# Show record

        return [lsort [array names _recorddefn]]
    }

    if {[string match "mem*" "$what_"]} {
	# Show members

	if {[string match "" "$record_"] || ![info exists _recorddefn($record_)]} {
	    error "Bad arguments while accessing members. Bad record name"
	}

	set res [list]
	set cnt 0
	foreach m $_recorddefn($record_) {
	    set def [lindex $_defaults($record_) $cnt]
	    if {[regexp -- {([\w]+)::([\w]+)} $m m d i]} {
		lappend res [list record $d $i]
	    } elseif {![string match "" "$def"]} {
		lappend res [list $m $def]
	    } else {
		lappend res $m
	    }

	    incr cnt
	}

	return $res
    }

    if {[string match "inst*" "$what_"]} {
	# Show instances

	if {![namespace exists ::struct::record${record_}]} {
	    return [list]
	}

	# Import the state of the manager namespace
	upvar 0 ::struct::record${record_}::instances __instances

        if {![info exists __instances]} {
            return [list]
        }
        return [lsort $__instances]

    }

    if {[string match "val*" "$what_"]} {
	# Show values

	set nsi [Ns $record_]
	upvar 0 ${nsi}::instances __instances
	upvar 0 ${nsi}::values    __values
	upvar 0 ${nsi}::record    __record

	if {[string match "" "$record_"] ||
	    ([lsearch $__instances $record_] < 0)} {

	    error "Wrong arguments to values. Bad instance name"
	}

	set ret [list]
	foreach k $_recorddefn($__record) {
	    set v $__values($record_,$k)

	    if {[regexp -- {([\w]*)::([\w]*)} $k m def inst]} {
		set v [::struct::record::Show values ${record_}.${inst}]
	    }

	    lappend ret -[namespace tail $k] $v
	}
	return $ret

    }

    # Bogus submethod
    return [list]

}; # end proc ::struct::record::Show


#------------------------------------------------------------
# ::struct::record::Delete --
#
#    Deletes a record instance or a record definition
#
# Arguments:
#    sub_    what to delete. Either 'instance' or 'record'
#    item_   the specific record instance or definition
#            delete.
#
# Returns:
#    none
#
#------------------------------------------------------------
#
proc ::struct::record::Delete {sub_ item_} {
    variable _recorddefn
    variable _defn
    variable _count
    variable _defaults

    # puts .([info level 0])...

    set item_ [Qualify $item_]

    switch -- $sub_ {
        instance -
        instances -
        inst    {
	    # puts .instance
	    # puts .is-instance=[Exists instance $item_]

            if {[Exists instance $item_]} {

		# Locate manager namespace, i.e. class namespace for
		# instance to remove
		set nsi [Ns $item_]
		# puts .\tnsi=$nsi

		# Import the state of the manager namespace
		upvar 0 ${nsi}values    __values
		upvar 0 ${nsi}instances __instances
		upvar 0 ${nsi}record    __record
		# puts .\trecord=$__record

		# Remove instance from state
		set i [lsearch $__instances $item_]
		set __instances [lreplace $__instances $i $i]
		unset _defn($item_)

		# Process instance fields.

		foreach V $_recorddefn($__record) {
		    # puts .\tfield($V)=/clear

		    if {[regexp -- {([\w]*)::([\w]*)} $V m def inst]} {
			# Nested record detected.
			# Determine associated instance and delete recursively.
			Delete inst ${item_}.${inst}
		    } else {
			# Delete field accessor alias
			# puts .de-alias\t($item_.$V)
			uplevel #0 [list interp alias {} ${item_}.$V {}]
		    }

		    unset __values($item_,$V)
		}

		# Auto-generated id numbers increase monotonically.
		# Reverting here causes the next auto to fail, claiming
		# that the instance exists.
                # incr _count($ns) -1

            } else {
                #error "$item_ is not a instance"
            }
        }
        record  -
        records   {
	    # puts .record
            ##
            ##  Delete the instances for this
            ##  record
            ##
	    # puts .get-instances
            foreach I [Show instance "$item_"] {
                catch {
		    # puts .di/$I
		    Delete instance "$I"
		}
            }

            catch {
                unset _recorddefn($item_)
                unset _defaults($item_)
                unset _count($item_)
                namespace delete ::struct::record${item_}
            }
        }
        default   {
            error "Wrong arguments to delete"
        }

    }; # end switch

    # Remove alias associated with instance or record (class)
    # puts .de-alias\t($item_)
    catch { uplevel #0 [list interp alias {} $item_ {}]}

    # puts ./
    return

}; # end proc ::struct::record::Delete


#------------------------------------------------------------
# ::struct::record::Exists --
#
#    Tests whether a record definition or record
#    instance exists.
#
# Arguments:
#    sub_    what to test. Either 'instance' or 'record'
#    item_   the specific record instance or definition
#            that needs to be tested.
#
# Tests to see if a particular instance exists
#
#------------------------------------------------------------
#
proc ::struct::record::Exists {sub_ item_} {

    # puts .([info level 0])...

    set item_ [Qualify $item_]

    switch -glob -- $sub_ {
        inst* {
	    variable _defn
            return [info exists _defn($item_)]
        }
        record {
	    variable _recorddefn
            return [info exists _recorddefn($item_)]
        }
        default  {
            error "Wrong arguments. Must be exists record|instance target"
        }
    }; # end switch

}; # end proc ::struct::record::Exists


#------------------------------------------------------------
# ::struct::record::Qualify --
#
#    Contructs the qualified name of the calling scope. This
#    defaults to 2 levels since there is an extra proc call in
#    between.
#
# Arguments:
#    item_   the command that needs to be qualified
#    level_  how many levels to go up (default = 2)
#
# Results:
#    the item_ passed in fully qualified
#
#------------------------------------------------------------
#
proc ::struct::record::Qualify {item_ {level_ 2}} {
    if {![string match "::*" "$item_"]} {
        set ns [uplevel $level_ [list namespace current]]

        if {![string match "::" "$ns"]} {
            append ns "::"
        }

        set item_ "$ns${item_}"
    }

    return "$item_"

}; # end proc ::struct::record::Qualify

# ### ### ### ######### ######### #########
## Ready

namespace eval ::struct {
    # Get 'record::record' into the general structure namespace.
    namespace import -force record::record
    namespace export record
}

package provide struct::record 1.2.2
return