if 0 {# block comment, can span multiple lines}
## description line, is associated with the following proc
##@param name description of a parameter to the following proc
proc name ...The feature I like best is the cross-reference xref, which adds to a proc's description which other procs it calls, and by which other procs (inside the examined file) it is called. For testing, remove the leading blank I had to add to proc and # lines so they come in code style. Example of the produced output:tcl2tex fp ?name?The worker function that operates on one file pointer, called one or more times. Returns nothing.Calls: tex_escape,tex_render.Called by: main.Parameters:fp A file pointer open to read (stdin or an open file)name The name of the file. Optional, defaults to {}. }
if 0 {# tcl2tex.tcl -- extract TeX documentation from a Tcl source file.
Given one or more Tcl source file, this script extracts special comments
of the form ##..., or "if 0 {#...}" for multi-line comments, and produces
LATeX source code on stdout.
}
## The main function of this script.
## Returns nothing.
##@param argv List of the arguments on the command line
proc main argv {
if [llength $argv] {
foreach file $argv {
set fp [open $file]
tcl2tex $fp $file
close $fp
}
} else {tcl2tex stdin}
}
## The worker function that operates on one file pointer, called one or more times.
## Returns nothing.
##@param fp A file pointer open to read (stdin or an open file)
##@param name The name of the file.
proc tcl2tex {fp {name {}}} {
set mode ""
set params {}
puts "\\subsection{Commands provided by [tex_escape [file tail $name]]}"
while {[gets $fp line] >= 0} {
if {$mode eq "block"} {
append block \n$line
if [info complete $block] {
set mode ""
puts [tex_escape [string range $block 7 end-1]]\\\\\n
set block ""
}
} elseif {$mode eq "proc"} {
append proc \n$line
if [info complete $proc] {
set mode ""
set procs($name) $proc
set proc ""
}
} elseif [string match "if 0 \{#*" $line] {
set mode block
set block $line
} elseif [string match "## *" $line] {
append comment [string range $line 2 end]
} elseif [string match "##@param *" $line] {
lappend params [string range $line 9 end]
} elseif [string match "proc *" $line] {
foreach {- name arguments} [string trimright $line \{] break
set a($name) [list $arguments $comment $params]
set comment ""
set params ""
set mode proc
set proc $line
}
}
#-- loop over commands in alphabetic order
foreach name [lsort [array names a]] {
tex_render $name $a($name) procs
}
array unset default
}
## Output a function documentation to stdout.
##@param name Name of the Tcl function
##@param data List of: arguments, function description, parameter descriptions
##@param _procs Array name where seen proc definitions are stored
proc tex_render {name data _procs} {
upvar 1 $_procs procs
foreach {arguments comment params} $data break
append comment [xref procs $name]
set args {}
foreach arg $arguments {
if {[llength $arg]==2} {
set default([lindex $arg 0]) [lindex $arg 1]
set arg ?[lindex $arg 0]?
}
lappend args $arg
}
set name [tex_escape $name]
set args [tex_escape $args]
puts "\\rule{16.3cm}{0.1mm}\\\\"
puts "\\texttt{\\textbf{$name} \\textit{$args}}\\newline"
puts -nonewline [tex_escape $comment]
if [llength $params] {
puts "\\begin{tabbing}"
puts "\\textbf{Parameters:}\\=\\\\"
foreach param $params {
regexp {([^ ]+) +(.+)} $param -> name text
if [info exists default($name)] {
append text " Optional, defaults to [list $default($name)]."
}
puts "\\textit{[tex_escape $name]}\\>[tex_escape $text]\\\\"
}
puts "\\end{tabbing}"
} else {puts "\\bigskip"}
puts ""
}
## Escapes characters special to TeX: underscore _, braces {},
## pound sign(#), by prefixing a backslash.
## Returns the escaped string.
proc tex_escape string {
string map {_ \\_ \{ \\\{ \} \\\} # \\# $ \\$ & \\& % \\%} $string
}
## Creates a cross reference of what functions are mentioned in which
## function bodies
##@param _procs Array name where seen proc definitions are stored
##@param name Name of the Tcl function
proc xref {_procs name} {
upvar 1 $_procs procs
set calls {}
set called {}
foreach i [lsort [array names procs]] {
if {$i eq $name} continue
if {[string first $i $procs($name)]>=0} {lappend calls $i}
if {[string first $name $procs($i)]>=0} {lappend called $i}
}
set res ""
if [llength $calls] {append res "\n\nCalls: [join $calls ,]."}
if [llength $called] {append res "\n\nCalled by: [join $called ,]."}
set res
}
main $argvif 0 {Lars H: That tex_escape procedure leads me to believe that this hasn't been tested much. You don't escape $, %, &, or \ itself (now, the backslash is indeed tricky to escape)!?! Other unsafe characters include ", <, >, ~, and ^. - RS: As only dedicated comments and proc lines are extracted, no special characters have shown up with problems yet... But % and & may of course occur in comments. Thanks, updated :)
Category Documentation | Arts and crafts of Tcl-Tk programming }

