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 or https://github.com/ActiveState/teapot/tree/master/lib/tclparser
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 make install
Despite CVS history showing no activity since 2007, this builds cleanly against 8.6.4. That's a stable interface!
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 .
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]]]
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.
See also: