tcl2tex

if 0 {Richard Suchenwirth 2005-04-21 - Here's a little tool ("poor man's Doxygen") to produce TeX documentation from mildly marked-up Tcl source files (see itself for examples). The following markups are detected:

 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] {
            lassign [string trimright $line \{] - name arguments
            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
    lassign $data arguments comment params
    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 $argv

if 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 }