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