AMG: Coroutines are especially well suited to parsing XML in SAX mode, i.e. as a stream of events. Here's one way to do it with tDOM:
# parseXmlFile -- # Parses an XML file using the supplied event dispatch table. # Source: https://wiki.tcl-lang.org/49298 proc parseXmlFile {dispatch type input args} { # Determine the parse method. set method [dict get { -file parsefile -channel parsechannel -data parse } [tcl::prefix match {-file -channel -data} $type]] # Create parser coroutine. for {set i 0} {[info commands [set coro ::Parse$i]] ne {}} {incr i} {} coroutine $coro apply {{Dispatch} { while {1} { # Yield to the Expat parser, which will invoke this coroutine again # the next time a registered event occurs. The return value of # [yieldto] will be the event type and arguments. set Args [lrange [set Key [yieldto return -level 0]] 1 end] # Run all applicable event handlers, starting with most specific. for {} {$Key ne {}} {set Key [lreplace $Key end end]} { if {[dict exists $Dispatch $Key]} { lassign $Args {*}[lindex [dict get $Dispatch $Key] 0] eval [lindex [dict get $Dispatch $Key] 1] } } } }} $dispatch # Create Expat parser and glue to the parser coroutine. set parser [expat] foreach event [lsort -unique [lmap i [dict keys $dispatch] {lindex $i 0}]] { if {[string index $event 0] eq "-"} { $parser configure $event [list $coro $event] } } # Enable TNC to automatically validate against DTD schema. catch {tnc $parser enable} # Initialize parser coroutine. if {[dict exists $dispatch initialize]} { $coro initialize $input {*}$args } # Run Expat parser, which will exercise the coroutine. $parser $method $input $parser free # Finalize parser coroutine. if {[dict exists $dispatch finalize]} { return [$coro finalize] } else { rename $coro {} } }
Examples to come.