# -*- tcl -*- # ### ### ### ######### ######### ######### ## This package provides a number of utility commands to ## transformations for common operations. It assumes a 'Normalized PE ## Grammar Tree' as input, possibly augmented with attributes coming ## from transformation not in conflict with the base definition. # ### ### ### ######### ######### ######### ## Requisites package require page::util::quote namespace eval ::page::util::peg { namespace export \ symbolOf symbolNodeOf \ updateUndefinedDueRemoval \ flatten peOf printTclExpr \ getWarnings printWarnings # Get the peg char de/encoder commands. # (unquote, quote'tcl). namespace import ::page::util::quote::* } # ### ### ### ######### ######### ######### ## API proc ::page::util::peg::symbolNodeOf {t n} { # Given an arbitrary root it determines the node (itself or an # ancestor) containing the name of the nonterminal symbol the node # belongs to, and returns its id. The result is either the root of # the tree (for the start expression), or a definition mode. while {![$t keyexists $n symbol]} { set n [$t parent $n] } return $n } proc ::page::util::peg::symbolOf {t n} { # As above, but returns the symbol name. return [$t get [symbolNodeOf $t $n] symbol] } proc ::page::util::peg::updateUndefinedDueRemoval {t} { # The removal of nodes may have caused symbols to lose one or more # users. Example: A used by B and C, B is reachable, C is not, so A # now loses a node in the expression for C calling it, or rather # not anymore. foreach {sym def} [$t get root definitions] { set res {} foreach u [$t get $def users] { if {![$t exists $u]} continue lappend res $u } $t set $def users $res } # Update the knowledge of undefined nonterminals. To be used when # a transformation can remove invokations of undefined symbols, # and is not able to generate such invokations. set res {} foreach {sym invokers} [$t get root undefined] { set sres {} foreach n $invokers { if {![$t exists $n]} continue lappend sres $n } if {[llength $sres]} { lappend res $sym $sres } } $t set root undefined $res return } proc ::page::util::peg::flatten {q t} { # Flatten nested x-, or /-operators. # See peg_normalize.tcl, peg::normalize::ExprFlatten foreach op {x /} { # Locate all x operators, whose parents are x oerators as # well, then go back to the child operators and cut them out. $q query \ tree withatt op $op \ parent unique withatt op $op \ children withatt op $op \ over n { $t cut $n } } return } proc ::page::util::peg::getWarnings {t} { # Look at the attributes for problems with the grammar and issue # warnings. They do not prevent us from writing the grammar, but # still represent problems with it the user should be made aware # of. array set msg {} array set undefined [$t get root undefined] foreach sym [array names undefined] { set msg($sym) {} foreach ref $undefined($sym) { lappend msg($sym) "Undefined symbol used by the definition of '[symbolOf $t $ref]'." } } foreach {sym def} [$t get root definitions] { if {[llength [$t get $def users]] == 0} { set msg($sym) [list "This symbol has been defined, but is not used."] } } return [array get msg] } proc ::page::util::peg::printWarnings {msg} { if {![llength $msg]} return set dict {} set max -1 foreach {k v} $msg { set l [string length [list $k]] if {$l > $max} {set max $l} lappend dict [list $k $v $l] } foreach e [lsort -dict -index 0 $dict] { foreach {k msgs l} $e break set off [string repeat " " [expr {$max - $l}]] page_info "[list $k]$off : [lindex $msgs 0]" if {[llength $msgs] > 1} { set indent [string repeat " " [string length [list $k]]] foreach m [lrange $msgs 1 end] { puts stderr " $indent$off : $m" } } } return } proc ::page::util::peg::peOf {t eroot} { set op [$t get $eroot op] set pe [list $op] set ch [$t children $eroot] if {[llength $ch]} { foreach c $ch { lappend pe [peOf $t $c] } } elseif {$op eq "n"} { lappend pe [$t get $eroot sym] } elseif {$op eq "t"} { lappend pe [unquote [$t get $eroot char]] } elseif {$op eq ".."} { lappend pe \ [unquote [$t get $eroot begin]] \ [unquote [$t get $eroot end]] } return $pe } proc ::page::util::peg::printTclExpr {pe} { list [PrintExprSub $pe] } # ### ### ### ######### ######### ######### ## Internal proc ::page::util::peg::PrintExprSub {pe} { set op [lindex $pe 0] set args [lrange $pe 1 end] #puts stderr "PE [llength $args] $op | $args" if {$op eq "t"} { set a [lindex $args 0] return "$op [quote'tcl $a]" } elseif {$op eq ".."} { set a [lindex $args 0] set b [lindex $args 1] return "$op [quote'tcl $a] [quote'tcl $b]" } elseif {$op eq "n"} { return $pe } else { set res $op foreach a $args { lappend res [PrintExprSub $a] } return $res } } # ### ### ### ######### ######### ######### ## Ready package provide page::util::peg 0.1