| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Language::AttributeGrammar; |
|
2
|
|
|
|
|
|
|
|
|
3
|
5
|
|
|
5
|
|
129803
|
use 5.006001; |
|
|
5
|
|
|
|
|
22
|
|
|
|
5
|
|
|
|
|
181
|
|
|
4
|
5
|
|
|
5
|
|
24
|
use strict; |
|
|
5
|
|
|
|
|
10
|
|
|
|
5
|
|
|
|
|
144
|
|
|
5
|
5
|
|
|
5
|
|
24
|
use warnings; |
|
|
5
|
|
|
|
|
14
|
|
|
|
5
|
|
|
|
|
159
|
|
|
6
|
5
|
|
|
5
|
|
26
|
no warnings 'uninitialized'; |
|
|
5
|
|
|
|
|
7
|
|
|
|
5
|
|
|
|
|
259
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = '0.08'; |
|
9
|
|
|
|
|
|
|
|
|
10
|
5
|
|
|
5
|
|
2645
|
use Language::AttributeGrammar::Parser; |
|
|
5
|
|
|
|
|
268932
|
|
|
|
5
|
|
|
|
|
229
|
|
|
11
|
5
|
|
|
5
|
|
66
|
use Perl6::Attributes; |
|
|
5
|
|
|
|
|
32
|
|
|
|
5
|
|
|
|
|
52
|
|
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
my $methnum = '0'; |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub new { |
|
16
|
19
|
|
|
19
|
0
|
18638
|
my ($class, $options, $grammar) = @_; |
|
17
|
19
|
100
|
|
|
|
103
|
unless (ref $options eq 'HASH') { |
|
18
|
18
|
|
|
|
|
31
|
$grammar = $options; |
|
19
|
18
|
|
|
|
|
41
|
$options = {}; |
|
20
|
|
|
|
|
|
|
} |
|
21
|
|
|
|
|
|
|
|
|
22
|
19
|
|
|
|
|
164
|
my $engine = Language::AttributeGrammar::Parser->new($grammar, $options->{prefix}); |
|
23
|
18
|
|
|
|
|
113
|
my $meth = '_AG_visit_' . $methnum++; |
|
24
|
18
|
|
|
|
|
83
|
$engine->make_visitor($meth); |
|
25
|
|
|
|
|
|
|
|
|
26
|
18
|
|
33
|
|
|
264
|
bless { |
|
27
|
|
|
|
|
|
|
engine => $engine, |
|
28
|
|
|
|
|
|
|
meth => $meth, |
|
29
|
|
|
|
|
|
|
} => ref $class || $class; |
|
30
|
|
|
|
|
|
|
} |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
sub apply { |
|
33
|
21
|
|
|
21
|
0
|
289
|
my ($self, $top, $attr, $topattrs) = @_; |
|
34
|
|
|
|
|
|
|
|
|
35
|
21
|
|
|
|
|
113
|
$.engine->evaluate($.meth, $top, $attr, $topattrs); |
|
36
|
|
|
|
|
|
|
} |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
sub annotate { |
|
39
|
0
|
|
|
0
|
0
|
|
my ($self, $top, $topattrs) = @_; |
|
40
|
0
|
|
|
|
|
|
Language::AttributeGrammar::Annotator->new($.engine->annotate($.meth, $top, $topattrs)); |
|
41
|
|
|
|
|
|
|
} |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
package Language::AttributeGrammar::Annotator; |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub new { |
|
46
|
0
|
|
|
0
|
|
|
my ($class, $ann) = @_; |
|
47
|
|
|
|
|
|
|
|
|
48
|
0
|
|
0
|
|
|
|
bless { |
|
49
|
|
|
|
|
|
|
ann => $ann, |
|
50
|
|
|
|
|
|
|
} => ref $class || $class; |
|
51
|
|
|
|
|
|
|
} |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
our $AUTOLOAD; |
|
54
|
|
|
|
|
|
|
sub AUTOLOAD { |
|
55
|
0
|
|
|
0
|
|
|
(my $attr = $AUTOLOAD) =~ s/.*:://; |
|
56
|
0
|
0
|
|
|
|
|
return if $attr eq 'DESTROY'; |
|
57
|
|
|
|
|
|
|
|
|
58
|
0
|
|
|
|
|
|
my ($self, $node) = @_; |
|
59
|
0
|
|
|
|
|
|
$self->get($node)->get($attr)->get; |
|
60
|
|
|
|
|
|
|
} |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
1; |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=head1 NAME |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
Language::AttributeGrammar - Attribute grammars for doing computations over trees. |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
use Language::AttributeGrammar; |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# Grammar to return a new tree that is just like the old one, except |
|
74
|
|
|
|
|
|
|
# every leaf's value is the value of the minimum leaf. |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
my $grammar = new Language::AttributeGrammar <<'END_GRAMMAR'; |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# find the minimum of a tree from the leaves up |
|
79
|
|
|
|
|
|
|
Leaf: $/.min = { $ } |
|
80
|
|
|
|
|
|
|
Branch: $/.min = { List::Util::min($.min, $.min)) } |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# find the global minimum and propagate it back down the tree |
|
83
|
|
|
|
|
|
|
ROOT: $/.gmin = { $/.min } |
|
84
|
|
|
|
|
|
|
Branch: $.gmin = { $/.gmin } |
|
85
|
|
|
|
|
|
|
| $.gmin) = { $/.gmin } |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# reconstruct the tree with every leaf replaced with the minimum value |
|
88
|
|
|
|
|
|
|
Leaf: $/.result = { Leaf->new($/.gmin) } |
|
89
|
|
|
|
|
|
|
Branch: $/.result = { Branch->new($.result, $.result) } |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
END_GRAMMAR |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# This grammar expects that you define these classes: |
|
94
|
|
|
|
|
|
|
# Branch (with a ->left and ->right attribute) |
|
95
|
|
|
|
|
|
|
# Leaf (with a ->value attribute) |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# Use the grammar |
|
98
|
|
|
|
|
|
|
my $tree = Branch->new( Leaf->new(1), |
|
99
|
|
|
|
|
|
|
Branch->new( Leaf->new(2), Leaf->new(3))); |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# Apply the attribute grammar to the data structure and fetch the result |
|
102
|
|
|
|
|
|
|
my $result = $grammar->apply($tree, 'result'); |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
This module implements simple (for now) Attribute Grammar support for Perl data |
|
107
|
|
|
|
|
|
|
structures. An attribute grammar is a way to specify I over a |
|
108
|
|
|
|
|
|
|
predefined data structure, say, as generated by L. This is |
|
109
|
|
|
|
|
|
|
done by associating I with the nodes of the data structure. |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
There are two types of attributes: synthesized and inherited. Synthesized |
|
112
|
|
|
|
|
|
|
attributes propagate bottom-up, that is, they use information from the children |
|
113
|
|
|
|
|
|
|
of a node to infer the attribute's value on that node. Inherited attributes |
|
114
|
|
|
|
|
|
|
are the opposite: they use information from a node in the structure to infer |
|
115
|
|
|
|
|
|
|
attributes on its chilren. |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
In the example above in the synopsis, the C attribute is synthesized, |
|
118
|
|
|
|
|
|
|
since it takes the values at the leaves and infers the minimum at a branch. |
|
119
|
|
|
|
|
|
|
The C (global minimum) attribute is inherited, since it uses C that |
|
120
|
|
|
|
|
|
|
was computed at the root node and propagates it downward to the leaves. |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=head2 Syntax |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
Some special syntax is used in throughout the definitions, borrowed from the |
|
125
|
|
|
|
|
|
|
syntax for Perl 6 grammars. |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=over |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=item * C<$/> |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
The current node. |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
=item * C<$/.attr> |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
The C attribute on the current node. |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=item * C<< $ >> |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
The child node named C of the current node. |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=item * C<< $.attr >> |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
The C attribute on the child node. |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=item * C<< `arbitrary(code)`.attr >> |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
Execute C B and fetch the C attribute |
|
148
|
|
|
|
|
|
|
from each element. So: |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
Foo: $/.bar = { `get_child($/)`.bar } # WRONG |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
C<$/.bar> will always be 1 (the number of things C returned). If |
|
153
|
|
|
|
|
|
|
you want to do this right, since you are only intending to use one value: |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
Foo: $/.bar = { `get_child($/)`.bar[0] } # okay |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
Also, the code inside backticks must not refer to any lexical variables or any |
|
158
|
|
|
|
|
|
|
attributes. That is, C<$/> and his children are the only variables you may |
|
159
|
|
|
|
|
|
|
refer to (but you may call methods on them, etc.). |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=back |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
The grammar definition is composed of a series of I definitions. An |
|
164
|
|
|
|
|
|
|
example semantic definition is: |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
Foo: $/.baz = { $.baz } |
|
167
|
|
|
|
|
|
|
| $.quux = { $/.quux } |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
This specifies the implementations of the I C and |
|
170
|
|
|
|
|
|
|
the I C for nodes of type Foo. That is, you can |
|
171
|
|
|
|
|
|
|
find the C attribute of the current node by looking at the baz attribute |
|
172
|
|
|
|
|
|
|
of its child, and you can find the C attribute of any node's child by |
|
173
|
|
|
|
|
|
|
looking at the C attribute of the node itself. |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
The C<< $ >> notation is defined to pretty much do the right thing. |
|
176
|
|
|
|
|
|
|
But, in the name of predictability, here are the semantics: |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
If C<$/> has a method named C (for the attribute C<< $ >>), then |
|
179
|
|
|
|
|
|
|
that method is called with no arguments to fetch the attribute. Otherwise, if |
|
180
|
|
|
|
|
|
|
C<$/> is a blessed hash, then the module snoops inside the hash and pulls out |
|
181
|
|
|
|
|
|
|
the key named "child". If the hash has no such key, or the object is not a |
|
182
|
|
|
|
|
|
|
blessed hash (eg. a blessed array), then we give up. |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
If your tree has a different convention for extracting child nodes, you may use |
|
185
|
|
|
|
|
|
|
the backtick syntax described above: |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
Cons: $/.sum = { `$/->get_child('head')`.sum + `$/->get_child('tail')`.sum } |
|
188
|
|
|
|
|
|
|
Nil: $/.sum = { 0 } |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
Cons: `$/->get_child('head')`.gsum = { $/.gsum } |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
In the future I may provide a callback that allows the user to define |
|
193
|
|
|
|
|
|
|
the meaning of C<< $ >>. |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
There is one special class name that can go to the left of the colon: |
|
196
|
|
|
|
|
|
|
C. This represents the root of the data structure you were given, |
|
197
|
|
|
|
|
|
|
and is used to avoid the common annoyance of creating a Root node |
|
198
|
|
|
|
|
|
|
class tha just bootstraps the "real" tree. So when you say: |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
ROOT: $/.gmin = { $/.min } |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
That means that when you're at the root of the data structure, the |
|
203
|
|
|
|
|
|
|
global minimum is equal to the local minimum. |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=head2 Usage |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
After you have a grammar specification in a string, create a new grammar |
|
208
|
|
|
|
|
|
|
object: |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
my $grammar = Language::AttributeGrammar->new($grammar_string); |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
This contains a minimal data structure of the semantics definitions. The |
|
213
|
|
|
|
|
|
|
constructor also can take an options hash as its first argument: |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
my $grammar = Language::AttributeGrammar->new({ prefix => 'Foo::' }, $grammar_string); |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
The only option at the moment is C, which will prepend this |
|
218
|
|
|
|
|
|
|
prefix to all the types mentioned in your grammar. However, if you need |
|
219
|
|
|
|
|
|
|
to omit this prefix, name the type in your grammar starting with a |
|
220
|
|
|
|
|
|
|
C<::>, and the prefix will not be prepended. |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
In order to find an attribute on the root node of a data structure, C it |
|
223
|
|
|
|
|
|
|
to the data structure, giving the name of the attribute you wish to find. |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
my $attr = $grammar->apply($data, 'attr'); |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
You may set attributes on the root of the data structure by passing a hash. |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
my $attr = $grammar->apply($data, 'attr', { |
|
230
|
|
|
|
|
|
|
starting_number => 0, |
|
231
|
|
|
|
|
|
|
}); |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
In order to find attributes on nodes that are lower in the structure, you must |
|
234
|
|
|
|
|
|
|
concoct your attribute grammar to propagate that information up the tree |
|
235
|
|
|
|
|
|
|
somehow. Usually this is done using a synthesized attribute that mirrors the |
|
236
|
|
|
|
|
|
|
given data structure. |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=head1 AUTHOR |
|
239
|
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
Luke Palmer |