Updated 2015-06-18 08:56:31 by aspect

An extension for Tcl, written in C, that lets Tcl scripts access Tcl's own parser via the parse command.

As a package, the name is parser.

aspect maintains a fossil repo of tclparser by itself.

The module originated in TclPro, whose source code is available from sourceforge CVS

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:[email protected]:/cvsroot/tclpro login
  cvs -z3 -d:pserver:[email protected]:/cvsroot/tclpro co tclparser
  mkdir tclparser/build
  cd tclparser/build
  ../configure --prefix=/home/tcl --with-tcl=/home/tcl/lib
  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.

See also:

  • parsetcl (pure-Tcl alternative, different API)
  • scriptSplit uses introspection to get a similar result