analysis_peg_emodes.tcl

Go to the documentation of this file.
00001 /*  -*- tcl -*-*/
00002 /*  ### ### ### ######### ######### #########*/
00003 
00004 /*  Perform mode analysis (x) on the PE grammar delivered by the*/
00005 /*  frontend. The grammar is in normalized form (*).*/
00006 /* */
00007 /*  (x) = See "doc_emodes.txt".*/
00008 /*        and "doc_emodes_alg.txt".*/
00009 /*  (*) = See "doc_normalize.txt".*/
00010 
00011 /*  This package assumes to be used from within a PAGE plugin. It uses*/
00012 /*  the API commands listed below. These are identical across the major*/
00013 /*  types of PAGE plugins, allowing this package to be used in reader,*/
00014 /*  transform, and writer plugins. It cannot be used in a configuration*/
00015 /*  plugin, and this makes no sense either.*/
00016 /* */
00017 /*  To ensure that our assumption is ok we require the relevant pseudo*/
00018 /*  package setup by the PAGE plugin management code.*/
00019 /* */
00020 /*  -----------------+--*/
00021 /*  page_info        | Reporting to the user.*/
00022 /*  page_warning     |*/
00023 /*  page_error       |*/
00024 /*  -----------------+--*/
00025 /*  page_log_error   | Reporting of internals.*/
00026 /*  page_log_warning |*/
00027 /*  page_log_info    |*/
00028 /*  -----------------+--*/
00029 
00030 /*  ### ### ### ######### ######### #########*/
00031 /*  Requisites*/
00032 
00033 /*  @mdgen NODEP: page::plugin*/
00034 
00035 package require page::plugin     ; /*  S.a. pseudo-package.*/
00036 package require page::util::flow ; /*  Dataflow walking.*/
00037 package require page::util::peg  ; /*  General utilities.*/
00038 package require treeql
00039 
00040 namespace ::page::analysis::peg::emodes {
00041     namespace import ::page::util::peg::*
00042 }
00043 
00044 /*  ### ### ### ######### ######### #########*/
00045 /*  API*/
00046 
00047 ret  ::page::analysis::peg::emodes::compute (type t) {
00048 
00049     # Ignore call if already done before
00050     if {[$t keyexists root page::analysis::peg::emodes]} {return 1}
00051 
00052     # We do not actually compute per node a mode, but rather their
00053     # gen'erate and acc'eptance properties, as described in
00054     # "doc_emodes.txt".
00055 
00056     # Note: This implementation will not compute acc/gen information
00057     # for unreachable nodes.
00058 
00059     # --- --- --- --------- --------- ---------
00060 
00061     array set acc  {} ; # Per node X, acc(X), undefined if no element
00062     array set call {} ; # Per definition node, number of users
00063     array set cala {} ; # Per definition node, number of (non-)accepting users
00064 
00065     foreach {sym def} [$t get root definitions] {
00066     set call($def)   [llength [$t get $def users]]
00067     set cala(0,$def) 0
00068     set cala(1,$def) 0
00069     }
00070 
00071     set acc(root) 1 ; # Sentinel for root of start expression.
00072 
00073     # --- --- --- --------- --------- ---------
00074 
00075     #puts stderr ~~~~\t~~~\t~~~\t~~~\t~~~
00076     #puts stderr Node\tAcc\tNew\tWhat\tOp
00077     #puts stderr ~~~~\t~~~\t~~~\t~~~\t~~~
00078 
00079     # A node is visited if its value for acc() is either undefined or
00080     # may have changed. Basic flow is top down, from the start
00081     # expression and a definition a child of its invokers.
00082 
00083     set gstart [$t get root start]
00084     if {$gstart eq ""} {
00085     page_error "  No start expression, unable to compute accept/generate properties"
00086     return 0
00087     }
00088 
00089     page::util::flow [list $gstart] flow n {
00090     # Determine first or new value.
00091 
00092     #puts -nonewline stderr [string replace $n 1 3]
00093 
00094     if {![info exists acc($n)]} {
00095         set a [Accepting $t $n acc call cala]
00096         set acc($n) $a
00097         set change 0
00098 
00099         #puts -nonewline stderr \t-\t$a\t^
00100     } else {
00101         set a   [Accepting $t $n acc call cala]
00102         set old $acc($n)
00103         if {$a == $old} {
00104         #puts stderr \t$old\t$a\t\ =
00105         continue
00106         }
00107         set change 1
00108         set acc($n) $a
00109 
00110         #puts -nonewline stderr \t$old\t$a\t\ \ *
00111     }
00112 
00113     # Update counters in definitions, if the node invokes them.
00114     # Also, schedule the children for their (re)definition.
00115 
00116     if {[$t keyexists $n symbol]} {
00117         #puts -nonewline stderr \t\ DEF\t[$t get $n symbol]\t[$t get $n mode]
00118     } else {
00119         #puts -nonewline stderr \t[$t get $n op]\t\t
00120     }
00121 
00122     if {[$t keyexists $n op] && ([$t get $n op] eq "n")} {
00123         #puts -nonewline stderr ->\ [$t get $n sym]
00124         set def [$t get $n def]
00125         if {$def eq ""} continue
00126 
00127         if {$change} {
00128         incr cala($old,$def) -1
00129         }
00130         incr cala($a,$def)
00131         $flow visit $def
00132 
00133         #puts -nonewline stderr @$def\t(0a$cala(0,$def),\ 1a$cala(1,$def),\ #$call($def))\tv($def)
00134         #puts stderr ""
00135         continue
00136     }
00137 
00138     #puts stderr \t\t\t\tv([$t children $n])
00139     $flow visitl [$t children $n]
00140     }
00141 
00142     # --- --- --- --------- --------- ---------
00143 
00144     array set gen {} ; # Per node X, gen(X), undefined if no element
00145     array set nc  {} ; # Per node, number of children
00146     array set ng  {} ; # Per node, number of (non-)generating children
00147 
00148     foreach n [$t nodes] {
00149     set nc($n)       [$t numchildren $n]
00150     set ng(0,$n)     0
00151     set ng(1,$n)     0
00152     }
00153 
00154     # --- --- --- --------- --------- ---------
00155 
00156     #puts stderr ~~~~\t~~~\t~~~\t~~~\t~~~
00157     #puts stderr Node\tGen\tNew\tWhat\tOp
00158     #puts stderr ~~~~\t~~~\t~~~\t~~~\t~~~
00159 
00160     # A node is visited if its value for gen() is either undefined or
00161     # may have changed. Basic flow is bottom up, from the all
00162     # leaves (and lookahead operators). Users of a definition are
00163     # considered as its parents.
00164 
00165     set start [$t leaves]
00166     set q [treeql q -tree $t]
00167     q query tree withatt op ! over n {lappend start $n}
00168     q query tree withatt op & over n {lappend start $n}
00169     q destroy
00170 
00171     page::util::flow $start flow n {
00172     # Ignore root.
00173 
00174     if {$n eq "root"} continue
00175 
00176     #puts -nonewline stderr [string replace $n 1 3]
00177 
00178     # Determine first or new value.
00179 
00180     if {![info exists gen($n)]} {
00181         set g [Generating $t $n gen nc ng acc call cala]
00182         set gen($n) $g
00183 
00184         #puts -nonewline stderr \t-\t$g\t^
00185 
00186     } else {
00187         set g   [Generating $t $n gen nc ng acc call cala]
00188         set old $gen($n)
00189         if {$g eq $old} {
00190         #puts stderr \t$old\t$g\t\ =
00191         continue
00192         }
00193         set gen($n) $g
00194 
00195         #puts -nonewline stderr \t$old\t$g\t\ \ *
00196     }
00197 
00198     if {($g ne "maybe") && !$g && $acc($n)} {
00199         # No generate here implies that none of our children will
00200         # generate anything either. So the current acceptance of
00201         # these non-existing values can be safely forced to
00202         # non-acceptance.
00203 
00204         set acc($n) 0
00205         #puts -nonewline stderr "-a"
00206     }
00207 
00208     if {0} {
00209         if {[$t keyexists $n symbol]} {
00210         #puts -nonewline stderr \t\ DEF\t[$t get $n symbol]\t[$t get $n mode]
00211         } else {
00212         #puts -nonewline stderr \t[$t get $n op]\t\t
00213         }
00214     }
00215 
00216     #puts -nonewline stderr \t(0g$ng(0,$n),1g$ng(1,$n),\ #$nc($n))
00217 
00218     # Update counters in the (virtual) parents, and schedule them
00219     # for a visit.
00220 
00221     if {[$t keyexists $n symbol]} {
00222         # Users are virtual parents.
00223 
00224         set users  [$t get $n users]
00225         $flow visitl $users
00226 
00227         if {$g ne "maybe"} {
00228         foreach u $users {incr ng($g,$u)}
00229         }
00230         #puts stderr \tv($users)
00231         continue
00232     }
00233 
00234     set p [$t parent $n]
00235     $flow visit $p
00236     if {$g ne "maybe"} {
00237         incr ng($g,$p)
00238     }
00239 
00240     #puts stderr \tv($p)
00241     }
00242 
00243     # --- --- --- --------- --------- ---------
00244 
00245     # Copy the calculated data over into the tree.
00246     # Note: There will be no data for unreachable nodes.
00247 
00248     foreach n [$t nodes] {
00249     if {$n eq "root"}           continue
00250     if {![info exists acc($n)]} continue
00251     $t set $n acc $acc($n)
00252     $t set $n gen $gen($n)
00253     }
00254 
00255     # Recompute the modes based on the current
00256     # acc/gen status of the definitions.
00257 
00258     #puts stderr ~~~~\t~~~\t~~~~\t~~~\t~~~\t~~~
00259     #puts stderr Node\tSym\tMode\tNew\tGen\tAcc
00260     #puts stderr ~~~~\t~~~\t~~~~\t~~~\t~~~\t~~~
00261 
00262     foreach {sym def} [$t get root definitions] {
00263     set m {}
00264 
00265     set old [$t get $def mode]
00266 
00267     if {[info exists acc($def)]} {
00268         switch -exact -- $gen($def)/$acc($def) {
00269         0/0     {set m discard}
00270         0/1     {error "Bad gen/acc for $sym"}
00271         1/0     {# don't touch (match, leaf)}
00272         1/1     {set m value}
00273         maybe/0 {error "Bad gen/acc for $sym"}
00274         maybe/1 {set m value}
00275         }
00276         if {$m ne ""} {
00277         # Should check correctness of change, if any (We can drop
00278         # to discard, nothing else).
00279         $t set $def mode $m
00280         }
00281         #puts stderr [string replace $def 1 3]\t$sym\t$old\t[$t get $def mode]\t[$t get $def gen]\t[$t get $def acc]
00282     } else {
00283         #puts stderr [string replace $def 1 3]\t$sym\t$old\t\t\t\tNOT_REACHED
00284     }
00285     }
00286 
00287     #puts stderr ~~~~\t~~~\t~~~~\t~~~\t~~~\t~~~
00288 
00289     # Wrap up the whole state and save it in the tree. No need to
00290     # throw this away, useful for other mode based transforms and
00291     # easier to get in this way than walking the tree again.
00292 
00293     $t set root page::analysis::peg::emodes [list \
00294         [array get acc] \
00295         [array get call] \
00296         [array get cala] \
00297         [array get gen] \
00298         [array get nc] \
00299         [array get ng]]
00300     return 1
00301 }
00302 
00303 ret  ::page::analysis::peg::emodes::reset (type t) {
00304     # Remove marker, allow recalculation of emodesness after changes.
00305 
00306     $t unset root page::analysis::peg::emodes
00307     return
00308 }
00309 
00310 /*  ### ### ### ######### ######### #########*/
00311 /*  Internal*/
00312 
00313 ret  ::page::analysis::peg::emodes::Accepting (type t , type n , type av , type cv , type cav) {
00314     upvar 1 $av acc $cv call $cav cala
00315 
00316     # Definitions accept based on how they are called first, and on
00317     # their mode if that is not possible.
00318 
00319     if {[$t keyexists $n symbol]} {
00320     # Call based acceptance.
00321     # !acc if all callers do not accept.
00322 
00323     if {$cala(0,$n) >= $call($n)} {
00324         return 0
00325     }
00326 
00327     # Falling back to mode specific accptance
00328     return [expr {([$t get $n mode] eq "value") ? 1 : 0}]
00329     }
00330 
00331     set op [$t get $n op]
00332 
00333     # Lookahead operators will never accept.
00334 
00335     if {($op eq "!") || ($op eq "&")} {
00336     return 0
00337     }
00338 
00339     # All other operators inherit the acceptance
00340     # of their parent.
00341 
00342     return $acc([$t parent $n])
00343 }
00344 
00345 ret  ::page::analysis::peg::emodes::Generating (type t , type n , type gv , type ncv , type ngv , type av , type cv , type cav) {
00346     upvar 1 $gv gen $ncv nc $ngv ng $av acc $cv call $cav cala
00347     #           ~~~      ~~      ~~     ~~~     ~~~~      ~~~~
00348 
00349     # Definitions generate based on their mode, their defining
00350     # expression, and the acceptance of their callers.
00351 
00352     if {[$t keyexists $n symbol]} {
00353 
00354     # If no caller accepts a value, then this definition will not
00355     # generate one, even if its own mode asked it to do so.
00356 
00357     if {$cala(0,$n) >= $call($n)} {
00358         return 0
00359     }
00360 
00361     # The definition has callers accepting values and callres not
00362     # doing so. It will generate as per its own mode and defining
00363     # expression.
00364 
00365     # The special modes know if they generate a value or not.
00366     # The pass through mode looks at the expression for the
00367     # information.
00368 
00369     switch -exact -- [$t get $n mode] {
00370         value   {return $gen([lindex [$t children $n] 0])}
00371         match   {return 1}
00372         leaf    {return 1}
00373         discard {return 0}
00374     }
00375     error PANIC
00376     }
00377 
00378     set op [$t get $n op]
00379 
00380     # Inner nodes generate based on operator and children.
00381 
00382     if {$nc($n)} {
00383     switch -exact -- $op {
00384         ! - & {return 0}
00385         ? - * {
00386         # No for all children --> no
00387         # Otherwise           --> maybe
00388 
00389         if {$ng(0,$n) >= $nc($n)} {
00390             return 0
00391         } else {
00392             return maybe
00393         }
00394         }
00395         + - / - | {
00396         # Yes for all children --> yes
00397         # No for all children  --> no
00398         # Otherwise            --> maybe
00399 
00400         if {$ng(1,$n) >= $nc($n)} {
00401             return 1
00402         } elseif {$ng(0,$n) >= $nc($n)} {
00403             return 0
00404         } else {
00405             return maybe
00406         }
00407         }
00408         x {
00409         # Yes for some children --> yes
00410         # No for all children   --> no
00411         # Otherwise             --> maybe
00412 
00413         if {$ng(1,$n) > 0} {
00414             return 1
00415         } elseif {$ng(0,$n) >= $nc($n)} {
00416             return 0
00417         } else {
00418             return maybe
00419         }
00420         }
00421     }
00422     error PANIC
00423     }
00424 
00425     # Nonterminal leaves generate based on acceptance from their
00426     # parent and the referenced definition.
00427 
00428     # As acc(X) == acc(parent(X)) the test doesn't have to go to the
00429     # parent itself.
00430 
00431     if {$op eq "n"} {
00432     if {[info exists acc($n)] && !$acc($n)} {return 0}
00433 
00434     set def [$t get $n def]
00435 
00436     # Undefine symbols do not generate anything.
00437     if {$def eq ""} {return 0}
00438 
00439     # Inherit directly from the definition, if existing.
00440     if {![info exists gen($def)]} {
00441         return maybe
00442     }
00443 
00444     return $gen($def)
00445     }
00446 
00447     # Terminal leaves generate values if and only if such values are
00448     # accepted by their parent. As acc(X) == acc(parent(X) the test
00449     # doesn't have to go to the parent itself.
00450 
00451 
00452     return $acc($n)
00453 }
00454 
00455 /*  ### ### ### ######### ######### #########*/
00456 /*  Ready*/
00457 
00458 package provide page::analysis::peg::emodes 0.1
00459 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1