1# -*- tcl -*- 2# ### ### ### ######### ######### ######### 3 4# Perform mode analysis (x) on the PE grammar delivered by the 5# frontend. The grammar is in normalized form (*). 6# 7# (x) = See "doc_emodes.txt". 8# and "doc_emodes_alg.txt". 9# (*) = See "doc_normalize.txt". 10 11# This package assumes to be used from within a PAGE plugin. It uses 12# the API commands listed below. These are identical across the major 13# types of PAGE plugins, allowing this package to be used in reader, 14# transform, and writer plugins. It cannot be used in a configuration 15# plugin, and this makes no sense either. 16# 17# To ensure that our assumption is ok we require the relevant pseudo 18# package setup by the PAGE plugin management code. 19# 20# -----------------+-- 21# page_info | Reporting to the user. 22# page_warning | 23# page_error | 24# -----------------+-- 25# page_log_error | Reporting of internals. 26# page_log_warning | 27# page_log_info | 28# -----------------+-- 29 30# ### ### ### ######### ######### ######### 31## Requisites 32 33# @mdgen NODEP: page::plugin 34 35package require page::plugin ; # S.a. pseudo-package. 36package require page::util::flow ; # Dataflow walking. 37package require page::util::peg ; # General utilities. 38package require treeql 39 40namespace eval ::page::analysis::peg::emodes { 41 namespace import ::page::util::peg::* 42} 43 44# ### ### ### ######### ######### ######### 45## API 46 47proc ::page::analysis::peg::emodes::compute {t} { 48 49 # Ignore call if already done before 50 if {[$t keyexists root page::analysis::peg::emodes]} {return 1} 51 52 # We do not actually compute per node a mode, but rather their 53 # gen'erate and acc'eptance properties, as described in 54 # "doc_emodes.txt". 55 56 # Note: This implementation will not compute acc/gen information 57 # for unreachable nodes. 58 59 # --- --- --- --------- --------- --------- 60 61 array set acc {} ; # Per node X, acc(X), undefined if no element 62 array set call {} ; # Per definition node, number of users 63 array set cala {} ; # Per definition node, number of (non-)accepting users 64 65 foreach {sym def} [$t get root definitions] { 66 set call($def) [llength [$t get $def users]] 67 set cala(0,$def) 0 68 set cala(1,$def) 0 69 } 70 71 set acc(root) 1 ; # Sentinel for root of start expression. 72 73 # --- --- --- --------- --------- --------- 74 75 #puts stderr ~~~~\t~~~\t~~~\t~~~\t~~~ 76 #puts stderr Node\tAcc\tNew\tWhat\tOp 77 #puts stderr ~~~~\t~~~\t~~~\t~~~\t~~~ 78 79 # A node is visited if its value for acc() is either undefined or 80 # may have changed. Basic flow is top down, from the start 81 # expression and a definition a child of its invokers. 82 83 set gstart [$t get root start] 84 if {$gstart eq ""} { 85 page_error " No start expression, unable to compute accept/generate properties" 86 return 0 87 } 88 89 page::util::flow [list $gstart] flow n { 90 # Determine first or new value. 91 92 #puts -nonewline stderr [string replace $n 1 3] 93 94 if {![info exists acc($n)]} { 95 set a [Accepting $t $n acc call cala] 96 set acc($n) $a 97 set change 0 98 99 #puts -nonewline stderr \t-\t$a\t^ 100 } else { 101 set a [Accepting $t $n acc call cala] 102 set old $acc($n) 103 if {$a == $old} { 104 #puts stderr \t$old\t$a\t\ = 105 continue 106 } 107 set change 1 108 set acc($n) $a 109 110 #puts -nonewline stderr \t$old\t$a\t\ \ * 111 } 112 113 # Update counters in definitions, if the node invokes them. 114 # Also, schedule the children for their (re)definition. 115 116 if {[$t keyexists $n symbol]} { 117 #puts -nonewline stderr \t\ DEF\t[$t get $n symbol]\t[$t get $n mode] 118 } else { 119 #puts -nonewline stderr \t[$t get $n op]\t\t 120 } 121 122 if {[$t keyexists $n op] && ([$t get $n op] eq "n")} { 123 #puts -nonewline stderr ->\ [$t get $n sym] 124 set def [$t get $n def] 125 if {$def eq ""} continue 126 127 if {$change} { 128 incr cala($old,$def) -1 129 } 130 incr cala($a,$def) 131 $flow visit $def 132 133 #puts -nonewline stderr @$def\t(0a$cala(0,$def),\ 1a$cala(1,$def),\ #$call($def))\tv($def) 134 #puts stderr "" 135 continue 136 } 137 138 #puts stderr \t\t\t\tv([$t children $n]) 139 $flow visitl [$t children $n] 140 } 141 142 # --- --- --- --------- --------- --------- 143 144 array set gen {} ; # Per node X, gen(X), undefined if no element 145 array set nc {} ; # Per node, number of children 146 array set ng {} ; # Per node, number of (non-)generating children 147 148 foreach n [$t nodes] { 149 set nc($n) [$t numchildren $n] 150 set ng(0,$n) 0 151 set ng(1,$n) 0 152 } 153 154 # --- --- --- --------- --------- --------- 155 156 #puts stderr ~~~~\t~~~\t~~~\t~~~\t~~~ 157 #puts stderr Node\tGen\tNew\tWhat\tOp 158 #puts stderr ~~~~\t~~~\t~~~\t~~~\t~~~ 159 160 # A node is visited if its value for gen() is either undefined or 161 # may have changed. Basic flow is bottom up, from the all 162 # leaves (and lookahead operators). Users of a definition are 163 # considered as its parents. 164 165 set start [$t leaves] 166 set q [treeql q -tree $t] 167 q query tree withatt op ! over n {lappend start $n} 168 q query tree withatt op & over n {lappend start $n} 169 q destroy 170 171 page::util::flow $start flow n { 172 # Ignore root. 173 174 if {$n eq "root"} continue 175 176 #puts -nonewline stderr [string replace $n 1 3] 177 178 # Determine first or new value. 179 180 if {![info exists gen($n)]} { 181 set g [Generating $t $n gen nc ng acc call cala] 182 set gen($n) $g 183 184 #puts -nonewline stderr \t-\t$g\t^ 185 186 } else { 187 set g [Generating $t $n gen nc ng acc call cala] 188 set old $gen($n) 189 if {$g eq $old} { 190 #puts stderr \t$old\t$g\t\ = 191 continue 192 } 193 set gen($n) $g 194 195 #puts -nonewline stderr \t$old\t$g\t\ \ * 196 } 197 198 if {($g ne "maybe") && !$g && $acc($n)} { 199 # No generate here implies that none of our children will 200 # generate anything either. So the current acceptance of 201 # these non-existing values can be safely forced to 202 # non-acceptance. 203 204 set acc($n) 0 205 #puts -nonewline stderr "-a" 206 } 207 208 if {0} { 209 if {[$t keyexists $n symbol]} { 210 #puts -nonewline stderr \t\ DEF\t[$t get $n symbol]\t[$t get $n mode] 211 } else { 212 #puts -nonewline stderr \t[$t get $n op]\t\t 213 } 214 } 215 216 #puts -nonewline stderr \t(0g$ng(0,$n),1g$ng(1,$n),\ #$nc($n)) 217 218 # Update counters in the (virtual) parents, and schedule them 219 # for a visit. 220 221 if {[$t keyexists $n symbol]} { 222 # Users are virtual parents. 223 224 set users [$t get $n users] 225 $flow visitl $users 226 227 if {$g ne "maybe"} { 228 foreach u $users {incr ng($g,$u)} 229 } 230 #puts stderr \tv($users) 231 continue 232 } 233 234 set p [$t parent $n] 235 $flow visit $p 236 if {$g ne "maybe"} { 237 incr ng($g,$p) 238 } 239 240 #puts stderr \tv($p) 241 } 242 243 # --- --- --- --------- --------- --------- 244 245 # Copy the calculated data over into the tree. 246 # Note: There will be no data for unreachable nodes. 247 248 foreach n [$t nodes] { 249 if {$n eq "root"} continue 250 if {![info exists acc($n)]} continue 251 $t set $n acc $acc($n) 252 $t set $n gen $gen($n) 253 } 254 255 # Recompute the modes based on the current 256 # acc/gen status of the definitions. 257 258 #puts stderr ~~~~\t~~~\t~~~~\t~~~\t~~~\t~~~ 259 #puts stderr Node\tSym\tMode\tNew\tGen\tAcc 260 #puts stderr ~~~~\t~~~\t~~~~\t~~~\t~~~\t~~~ 261 262 foreach {sym def} [$t get root definitions] { 263 set m {} 264 265 set old [$t get $def mode] 266 267 if {[info exists acc($def)]} { 268 switch -exact -- $gen($def)/$acc($def) { 269 0/0 {set m discard} 270 0/1 {error "Bad gen/acc for $sym"} 271 1/0 {# don't touch (match, leaf)} 272 1/1 {set m value} 273 maybe/0 {error "Bad gen/acc for $sym"} 274 maybe/1 {set m value} 275 } 276 if {$m ne ""} { 277 # Should check correctness of change, if any (We can drop 278 # to discard, nothing else). 279 $t set $def mode $m 280 } 281 #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] 282 } else { 283 #puts stderr [string replace $def 1 3]\t$sym\t$old\t\t\t\tNOT_REACHED 284 } 285 } 286 287 #puts stderr ~~~~\t~~~\t~~~~\t~~~\t~~~\t~~~ 288 289 # Wrap up the whole state and save it in the tree. No need to 290 # throw this away, useful for other mode based transforms and 291 # easier to get in this way than walking the tree again. 292 293 $t set root page::analysis::peg::emodes [list \ 294 [array get acc] \ 295 [array get call] \ 296 [array get cala] \ 297 [array get gen] \ 298 [array get nc] \ 299 [array get ng]] 300 return 1 301} 302 303proc ::page::analysis::peg::emodes::reset {t} { 304 # Remove marker, allow recalculation of emodesness after changes. 305 306 $t unset root page::analysis::peg::emodes 307 return 308} 309 310# ### ### ### ######### ######### ######### 311## Internal 312 313proc ::page::analysis::peg::emodes::Accepting {t n av cv cav} { 314 upvar 1 $av acc $cv call $cav cala 315 316 # Definitions accept based on how they are called first, and on 317 # their mode if that is not possible. 318 319 if {[$t keyexists $n symbol]} { 320 # Call based acceptance. 321 # !acc if all callers do not accept. 322 323 if {$cala(0,$n) >= $call($n)} { 324 return 0 325 } 326 327 # Falling back to mode specific accptance 328 return [expr {([$t get $n mode] eq "value") ? 1 : 0}] 329 } 330 331 set op [$t get $n op] 332 333 # Lookahead operators will never accept. 334 335 if {($op eq "!") || ($op eq "&")} { 336 return 0 337 } 338 339 # All other operators inherit the acceptance 340 # of their parent. 341 342 return $acc([$t parent $n]) 343} 344 345proc ::page::analysis::peg::emodes::Generating {t n gv ncv ngv av cv cav} { 346 upvar 1 $gv gen $ncv nc $ngv ng $av acc $cv call $cav cala 347 # ~~~ ~~ ~~ ~~~ ~~~~ ~~~~ 348 349 # Definitions generate based on their mode, their defining 350 # expression, and the acceptance of their callers. 351 352 if {[$t keyexists $n symbol]} { 353 354 # If no caller accepts a value, then this definition will not 355 # generate one, even if its own mode asked it to do so. 356 357 if {$cala(0,$n) >= $call($n)} { 358 return 0 359 } 360 361 # The definition has callers accepting values and callres not 362 # doing so. It will generate as per its own mode and defining 363 # expression. 364 365 # The special modes know if they generate a value or not. 366 # The pass through mode looks at the expression for the 367 # information. 368 369 switch -exact -- [$t get $n mode] { 370 value {return $gen([lindex [$t children $n] 0])} 371 match {return 1} 372 leaf {return 1} 373 discard {return 0} 374 } 375 error PANIC 376 } 377 378 set op [$t get $n op] 379 380 # Inner nodes generate based on operator and children. 381 382 if {$nc($n)} { 383 switch -exact -- $op { 384 ! - & {return 0} 385 ? - * { 386 # No for all children --> no 387 # Otherwise --> maybe 388 389 if {$ng(0,$n) >= $nc($n)} { 390 return 0 391 } else { 392 return maybe 393 } 394 } 395 + - / - | { 396 # Yes for all children --> yes 397 # No for all children --> no 398 # Otherwise --> maybe 399 400 if {$ng(1,$n) >= $nc($n)} { 401 return 1 402 } elseif {$ng(0,$n) >= $nc($n)} { 403 return 0 404 } else { 405 return maybe 406 } 407 } 408 x { 409 # Yes for some children --> yes 410 # No for all children --> no 411 # Otherwise --> maybe 412 413 if {$ng(1,$n) > 0} { 414 return 1 415 } elseif {$ng(0,$n) >= $nc($n)} { 416 return 0 417 } else { 418 return maybe 419 } 420 } 421 } 422 error PANIC 423 } 424 425 # Nonterminal leaves generate based on acceptance from their 426 # parent and the referenced definition. 427 428 # As acc(X) == acc(parent(X)) the test doesn't have to go to the 429 # parent itself. 430 431 if {$op eq "n"} { 432 if {[info exists acc($n)] && !$acc($n)} {return 0} 433 434 set def [$t get $n def] 435 436 # Undefine symbols do not generate anything. 437 if {$def eq ""} {return 0} 438 439 # Inherit directly from the definition, if existing. 440 if {![info exists gen($def)]} { 441 return maybe 442 } 443 444 return $gen($def) 445 } 446 447 # Terminal leaves generate values if and only if such values are 448 # accepted by their parent. As acc(X) == acc(parent(X) the test 449 # doesn't have to go to the parent itself. 450 451 452 return $acc($n) 453} 454 455# ### ### ### ######### ######### ######### 456## Ready 457 458package provide page::analysis::peg::emodes 0.1 459