00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 package require page::plugin ;
00036 package require page::util::flow ;
00037 package require page::util::peg ;
00038 package require treeql
00039
00040 namespace ::page::analysis::peg::emodes {
00041 namespace import ::page::util::peg::*
00042 }
00043
00044
00045
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
00457
00458 package provide page::analysis::peg::emodes 0.1
00459