Updated 2017-12-12 02:13:18 by AMG

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: http://wiki.tcl.tk/49298
proc parseXmlFile {dispatch filename args} {
    # Create parser coroutine.
    for {set i 0} {[llength [info commands [set coro Parse$i]]]} {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 {} {[llength $Key]} {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 $filename {*}$args

    # Run Expat parser, which will exercise the coroutine.
    $parser parsefile $filename
    $parser free

    # Finalize parser coroutine.
    if {[dict exists $dispatch finalize]} {
        return [$coro finalize]
    } else {
        rename $coro {}

Examples to come.