File Coverage

blib/lib/Language/AttributeGrammar.pm
Criterion Covered Total %
statement 28 36 77.7
branch 2 4 50.0
condition 1 6 16.6
subroutine 8 11 72.7
pod 0 3 0.0
total 39 60 65.0


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