Updated 2018-08-14 07:20:08 by pooryorick

tclparser, a component of Tcl Dev Kit written in C, parses Tcl scripts.

Attributes  edit

repository
Tcl Dev Kit
repository
https://chiselapp.com/user/aspect/repository/tclparser/, which is just tclparser.

See Also  edit

parsetcl
pure-Tcl alternative, different API
scriptSplit
Sses introspection to get a similar result

Synopsis  edit

package require parser

Description  edit

The module originated in TclPro, whose source code is available from sourceforge CVS or https://github.com/ActiveState/teapot/tree/master/lib/tclparser

Building  edit

fossil clone https://chiselapp.com/user/aspect/repository/tclparser tclparser.fossil
mkdir -p tclparser/build
cd tclparser
fossil open ../tclparser.fossil
cd build
../configure --prefix=/home/tcl --with-tcl=/home/tcl/lib
make all install

cvs -d:pserver:anonymous@tclpro.cvs.sourceforge.net:/cvsroot/tclpro login
cvs -z3 -d:pserver:anonymous@tclpro.cvs.sourceforge.net:/cvsroot/tclpro co tclparser
mkdir tclparser/build
cd tclparser/build
../configure --prefix=/home/tcl --with-tcl=/home/tcl/lib
make
make install

Despite CVS history showing no activity since 2007, this builds cleanly against 8.6.4. That's a stable interface!

Using  edit

As the manual explains, it exposes one command parse $type $text $range, which returns a structure similar to tcltest's [testparser], but a bit more convenient for script manipulation. Like tcltest::testparser, this is a lightweight wrapper around functions in tclParse.c.

Parsing Expressions  edit

A simple example to turn an expression into namespace evalable code:
package require parser 

proc expr2tcl {expr {parse ""}} {
    if {$parse eq ""} {
        set parse [parse expr $expr {0 end}]
    }
    lassign $parse type range parts
    lassign $range min max
    incr max $min
    incr max -1
    set text [string range $expr $min $max]
    set result ""
    switch $type {
        subexpr {
            set result [join [lmap part $parts {expr2tcl $expr $part}] " "]
            if {[lindex $parts 0 0] eq "operator"} {
                return \[$result\]
            } else {
                return $result
            }
        }
        default {
            return $text
        }
    }
}

# % puts [expr2tcl {sin($x)+4*$x-$x**(pow($x,2))}]
# [- [+ [sin $x] [* 4 $x]] [** $x [pow $x 2]]]

Parsing Scripts and Commands  edit

To parse a script, repeatedly call parse command. Its result is a 4-tuple {commentRange commandRange restRange parseTree}. parseTree is an interesting structure that breaks down the words in the command, and the rest are just index pairs as illustrated in the below work-alike of scriptSplit.
package require parser

proc stringRange {string start step} {
    tailcall string range $string $start [expr {$start+$step}]
}

proc ss {script} {
    set restRange {0 end}
    while {1} {
        lassign [parse command $script $restRange] commentRange commandRange restRange tree
        set comment [stringRange $script {*}$commentRange]
        set command [stringRange $script {*}$commandRange]
        if {$command eq ""} break
        #puts "Parsed a command {$tree} {$command}"
        lappend result $command
    }
    return $result
}

Note that the command terminator (newline or semicolon) is included in commandRange. But only if it is present. This is discussed somewhere else.

APN: Minor comment on the above use of string range. The expr is not needed as string range arguments can include + and - arithmetic operators. Also, the use of tailcall here does nothing useful other than slow down the procedure.