1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
|
# -*- tcl -*-
# ### ### ### ######### ######### #########
# Perform realizability analysis (x) on the PE grammar delivered by
# the frontend. The grammar is in normalized form (reduced to
# essentials, graph like node-x-references, expression trees).
#
# (x) = See "doc_realizable.txt".
# This package assumes to be used from within a PAGE plugin. It uses
# the API commands listed below. These are identical across the major
# types of PAGE plugins, allowing this package to be used in reader,
# transform, and writer plugins. It cannot be used in a configuration
# plugin, and this makes no sense either.
#
# To ensure that our assumption is ok we require the relevant pseudo
# package setup by the PAGE plugin management code.
#
# -----------------+--
# page_info | Reporting to the user.
# page_warning |
# page_error |
# -----------------+--
# page_log_error | Reporting of internals.
# page_log_warning |
# page_log_info |
# -----------------+--
# ### ### ### ######### ######### #########
## Requisites
# @mdgen NODEP: page::plugin
package require page::plugin ; # S.a. pseudo-package.
package require page::util::flow ; # Dataflow walking.
package require page::util::peg ; # General utilities.
package require treeql
namespace eval ::page::analysis::peg::realizable {
namespace import ::page::util::peg::*
}
# ### ### ### ######### ######### #########
## API
proc ::page::analysis::peg::realizable::compute {t} {
# Ignore call if already done before
if {[$t keyexists root page::analysis::peg::realizable]} return
# We compute the set of realizable nonterminal symbols by doing the
# computation for all partial PE's in the grammar. We start at the
# leaves and then iteratively propagate the property as far as
# possible using the rules defining it, see the specification.
# --- --- --- --------- --------- ---------
# Initialize all nodes and the local arrays. Everything is not
# realizable, except for the terminal leafs of the tree. Their parents
# are scheduled to be visited as well.
array set realizable {} ; # Place where realizable nodes are held
array set unrealizable {} ; # Place where unrealizable nodes are held
array set nc {} ; # Per node, number of children.
array set uc {} ; # Per node, number of realizable children.
set nodeset [$t leaves]
set q [treeql q -tree $t]
$q query tree withatt op * over n {lappend nodeset $n}
$q query tree withatt op ? over n {lappend nodeset $n}
q destroy
foreach n [$t nodes] {
set unrealizable($n) .
set nc($n) [$t numchildren $n]
set uc($n) 0
}
# A node is visited if it _may_ have changed its status (to
# realizability).
page::util::flow $nodeset flow n {
# Realizable nodes cannot change, ignore them.
if {[info exists realizable($n)]} continue
# Determine new state of realizability, ignore a node if it is
# unchanged.
if {![Realizable $t $n nc uc realizable]} continue
# Reclassify changed node, it is now realizable.
unset unrealizable($n)
set realizable($n) .
# Schedule visits to nodes which may have been affected by
# this change. Update the relevant counters as well.
# @ root - none
# @ definition - users of the definition
# otherwise - parent of operator.
if {$n eq "root"} continue
if {[$t keyexists $n symbol]} {
set users [$t get $n users]
$flow visitl $users
foreach u $users {
incr uc($u)
}
continue
}
set p [$t parent $n]
incr uc($p)
$flow visit $p
}
# Set marker preventing future calls.
$t set root page::analysis::peg::realizable [array names realizable]
$t set root page::analysis::peg::unrealizable [array names unrealizable]
return
}
proc ::page::analysis::peg::realizable::remove! {t} {
# Determine which parts of the grammar are realizable
compute $t
# Remove anything which is not realizable (and all their children),
# except for the root itself, should it be unrealizablel.
set unreal [$t get root page::analysis::peg::unrealizable]
foreach n [lsort $unreal] {
if {$n eq "root"} continue
if {[$t exists $n]} {
$t delete $n
}
}
# Notify the user of the definitions which were among the removed
# nodes. Keep only the still-existing definitions.
set res {}
foreach {sym def} [$t get root definitions] {
if {![$t exists $def]} {
page_warning " $sym: Nonterminal symbol is not realizable, removed."
} else {
lappend res $sym $def
}
}
$t set root definitions $res
if {![$t exists [$t get root start]]} {
page_warning " <Start expression>: Is not realizable, removed."
$t set root start {}
}
# Find and cut operator chains, very restricted. Cut only chains
# of x- and /-operators. The other operators have only one child
# by definition and are thus not chains.
set q [treeql q -tree $t]
# q query tree over n
foreach n [$t children -all root] {
if {[$t keyexists $n symbol]} continue
if {[llength [$t children $n]] != 1} continue
set op [$t get $n op]
if {($op ne "/") && ($op ne "x")} continue
$t cut $n
}
flatten $q $t
q destroy
# Clear computation results.
$t unset root page::analysis::peg::realizable
$t unset root page::analysis::peg::unrealizable
updateUndefinedDueRemoval $t
return
}
proc ::page::analysis::peg::realizable::reset {t} {
# Remove marker, allow recalculation of realizability after changes.
$t unset root page::analysis::peg::realizable
return
}
# ### ### ### ######### ######### #########
## Internal
proc ::page::analysis::peg::realizable::First {v} {
upvar 1 $v visit
set id [array startsearch visit]
set first [array nextelement visit $id]
array donesearch visit $id
unset visit($first)
return $first
}
proc ::page::analysis::peg::realizable::Realizable {t node ncv ucv uv} {
upvar 1 $ncv nc $ucv uc $uv realizable
if {$node eq "root"} {
# Root inherits realizability of the start expression.
return [info exists realizable([$t get root start])]
}
if {[$t keyexists $node symbol]} {
# Symbol definitions inherit the realizability of their
# expression.
return [expr {$uc($node) >= $nc($node)}]
}
switch -exact -- [$t get $node op] {
t - .. - epsilon - alpha - alnum - dot - * - ? {
# The terminal symbols are all realizable.
return 1
}
n {
# Symbol invokation inherits realizability of its definition.
# Calls to undefined symbols are not realizable.
set def [$t get $node def]
if {$def eq ""} {return 0}
return [info exists realizable($def)]
}
/ - | {
# Choice, ordered and unordered. Realizable if we have at
# least one realizable branch. A quick test based on the count
# of realizable children is used.
return [expr {$uc($node) > 0}]
}
default {
# Sequence, and all other operators, are realizable if and
# only if all its children are realizable. A quick test based
# on the count of realizable children is used.
return [expr {$uc($node) >= $nc($node)}]
}
}
}
# ### ### ### ######### ######### #########
## Ready
package provide page::analysis::peg::realizable 0.1
|