PYK 2016-05-12: A variant of CmdStream, ycl parse tcl iter , uses ycl coro relay to deliver each command in the stream .
PYK:
http://ynform.org/w/Pub/TclCmdStream
makes use of a slightly-modified version of scriptSplit
walk through a text stream, extracting Tcl commands and collecting statistics
also useful for parsing config files that have the same format as a Tcl script, but where each "command" represents a configuration entry rather than a real command.
example data:
data {/path/to/data
path/to/moredata
} ; tmpdata /path/to/tmpdata
#this is a comment
cache /path/to/cache /path/to/another/cache ;#this is another comment
cache /path/to/a/third/cache
admin_account httpdadmin
fonts /path/to/fonts
fonts /path/to/more/fonts#! /bin/env tclsh
proc cmdStream {fh callback _state} {
upvar $_state state
set data {}
set empty [expr ![regexp {\S} $data]]
while {$empty || ![info complete $data]} {
if {[eof $fh]} {
if {[regexp {\S} $data]} {
return -code error \
"incomplete syntax in data at line $state(linecount)"
} else {
return
}
}
set newdata [gets $fh]
append data \n $newdata
set empty [expr ![regexp {\S} $newdata]]
if {$empty} {
incr state(emptycount)
}
incr state(linecount)
}
incr state(listcount)
uplevel 1 [list $callback $data $_state]
}
proc scriptSplit {body _state} {
upvar $_state state
set commands {}
set chunk ""
foreach line [split $body "\n"] {
append chunk $line
if {[info complete "$chunk\n"]} {
# $chunk ends in a complete Tcl command, and none of the
# newlines within it end a complete Tcl command. If there
# are multiple Tcl commands in $chunk, they must be
# separated by semi-colons.
set cmd ""
foreach part [split $chunk ";"] {
append cmd $part
if {[info complete "$cmd\n"]} {
set cmd [string trimleft $cmd]
# Drop empty commands and comments
if {[string match {} $cmd]} {
incr state(emptycount)
} elseif {[string match \#* $cmd]} {
incr state(commentcount)
} else {
lappend commands $cmd
}
if {[string match \#* $cmd]} {
set cmd "\#;"
} else {
set cmd ""
}
} else {
# No complete command yet.
# Replace semicolon and continue
append cmd ";"
}
}
set chunk ""
} else {
# No end of command yet. Put the newline back and continue
append chunk "\n"
}
}
if {![string match {} [string trimright $chunk]]} {
return -code error "Can't parse body into a\
sequence of commands.\n\tIncomplete\
command:\n-----\n$chunk\n-----"
}
return $commands
}
proc cmd {item _state} {
upvar $_state state
foreach cmd [scriptSplit $item state] {
puts "a command!: $cmd"
}
}
for {set fh [open data]} {![eof $fh]} {} {
cmdStream $fh cmd state
}
close $fh
puts "total lines: $state(linecount)"
puts "total lists: $state(listcount)"
puts "total comments: $state(commentcount)"
puts "total empty: $state(emptycount)"
unset state