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 '''https://chiselapp.com/user/aspect/repository/tclparser/%|%a fossil repo of tclparser%|%''' by itself. The module originated in [TclPro], whose source code is available from '''http://tclpro.cvs.sourceforge.net/tclpro/tclparser/%|%sourceforge CVS%|%''' or https://github.com/ActiveState/teapot/tree/master/lib/tclparser ** Building ** 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 ** As the http://tclpro.cvs.sourceforge.net/viewvc/tclpro/tclparser/doc/parse.man%|%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 http://www.tcl.tk/man/tcl/TclLib/ParseCmd.htm%|%functions in tclParse.c%|%. ** Parsing Expressions ** A simple example to turn an [expr]ession into [namespace eval]able 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 ** 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 [http://wiki.tcl.tk/21701%|%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: * [parsetcl] (pure-Tcl alternative, different API) * [cmdSplit%|%scriptSplit] uses introspection to get a similar result <> Package | Internals | Parsing