line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Language::AttributeGrammar::Parser; |
2
|
|
|
|
|
|
|
|
3
|
5
|
|
|
5
|
|
25
|
use strict; |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
161
|
|
4
|
5
|
|
|
5
|
|
26
|
use warnings; |
|
5
|
|
|
|
|
17
|
|
|
5
|
|
|
|
|
124
|
|
5
|
5
|
|
|
5
|
|
24
|
no warnings 'uninitialized'; |
|
5
|
|
|
|
|
8
|
|
|
5
|
|
|
|
|
149
|
|
6
|
|
|
|
|
|
|
|
7
|
5
|
|
|
5
|
|
2685
|
use Language::AttributeGrammar::Engine; |
|
5
|
|
|
|
|
16
|
|
|
5
|
|
|
|
|
144
|
|
8
|
5
|
|
|
5
|
|
14539
|
use Parse::RecDescent; |
|
5
|
|
|
|
|
167823
|
|
|
5
|
|
|
|
|
37
|
|
9
|
5
|
|
|
5
|
|
290
|
use Scalar::Util qw; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
700
|
|
10
|
5
|
|
|
5
|
|
34
|
use Carp::Clan '^Language::AttributeGrammar'; |
|
5
|
|
|
|
|
6
|
|
|
5
|
|
|
|
|
52
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
my $prefix = 'Language::AttributeGrammar::Parser'; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $AUTOACTION = q { |
15
|
|
|
|
|
|
|
if ($item[0] eq 'TOKEN') { 1 } |
16
|
|
|
|
|
|
|
elsif (@item == 2) { $item[1] } |
17
|
|
|
|
|
|
|
elsif ($item{TOKEN}) { |
18
|
|
|
|
|
|
|
bless { value => $item[2], thisline => $thisline } => "$prefix\::$item[0]"; |
19
|
|
|
|
|
|
|
} |
20
|
|
|
|
|
|
|
else { |
21
|
|
|
|
|
|
|
bless { %item, thisline => $thisline } => "$prefix\::$item[0]"; |
22
|
|
|
|
|
|
|
} |
23
|
|
|
|
|
|
|
}; |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
our $SKIP = qr/(?: \s+ | (?: \# .*? \n ) )*/x; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
our $GRAMMAR = <<'#\'END_GRAMMAR'; # vim hack |
28
|
|
|
|
|
|
|
#\ |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
{ |
31
|
|
|
|
|
|
|
our $prefix = 'Language::AttributeGrammar::Parser'; |
32
|
|
|
|
|
|
|
} |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
grammar: attrsdef(s?) /\z/ |
35
|
|
|
|
|
|
|
{ bless { attrsdefs => $item[1] } => "$prefix\::$item[0]"; } |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
attrsdef: case ':' attrdef(s? /\|/) |
38
|
|
|
|
|
|
|
{ bless { case => $item[1], attrdefs => $item[3] } => "$prefix\::$item[0]"; } |
39
|
|
|
|
|
|
|
| |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
attrdef: attrcall '=' attrblock |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
attrcall: target '.' attr |
44
|
|
|
|
|
|
|
| |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
target: self | child | special |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
attrblock: TOKEN |
49
|
|
|
|
|
|
|
| |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
case: TOKEN /(?: :: )? \w+ (?: :: \w+ )*/x |
52
|
|
|
|
|
|
|
attr: TOKEN /\w+/ |
53
|
|
|
|
|
|
|
self: TOKEN '$/' |
54
|
|
|
|
|
|
|
child: TOKEN /\$<\w+>/ |
55
|
|
|
|
|
|
|
special: TOKEN /`.*?`/ |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
TOKEN: # null |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
#'END_GRAMMAR |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
my $namecount = '0'; |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
sub _get_child { |
64
|
100
|
|
|
100
|
|
277
|
my ($self, $child, $at) = @_; |
65
|
100
|
50
|
66
|
|
|
873
|
if ($self->can($child)) { $self->$child } |
|
0
|
100
|
|
|
|
0
|
|
66
|
|
|
|
|
|
|
elsif (reftype($self) eq 'HASH' && exists $self->{$child}) { |
67
|
99
|
|
|
|
|
416
|
$self->{$child}; |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
else { |
70
|
1
|
|
|
|
|
9
|
croak "Cannot find a way to access $child of $self at $at"; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub _filter_direct { |
75
|
56
|
|
|
56
|
|
101
|
my ($code, $at) = @_; |
76
|
56
|
|
|
|
|
108
|
$code =~ s[\$/][\$_AG_SELF]gx; |
77
|
56
|
|
|
|
|
128
|
$code =~ s[\$<(\w+)>][Language::AttributeGrammar::Parser::_get_child(\$_AG_SELF, '$1', '$at')]gx; |
78
|
56
|
|
|
|
|
137
|
$code; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
sub _filter_code { |
82
|
56
|
|
|
56
|
|
128
|
my ($target, $attr, $code, $at) = @_; |
83
|
56
|
|
|
|
|
69
|
my $result; |
84
|
|
|
|
|
|
|
my $idxa = sub { |
85
|
40
|
|
|
40
|
|
131
|
my ($itarget, $iattr) = @_; |
86
|
40
|
|
|
|
|
103
|
my $id = '$_AG_N' . $namecount++; |
87
|
40
|
|
|
|
|
183
|
$result .= "my $id = \$_AG_ATTR->get($itarget)->get('$iattr');\n"; |
88
|
40
|
|
|
|
|
208
|
"$id->get('$iattr', '$at')"; |
89
|
56
|
|
|
|
|
279
|
}; |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
my $idxarray = sub { |
92
|
1
|
|
|
1
|
|
6
|
my ($itarget, $iattr) = @_; |
93
|
1
|
|
|
|
|
4
|
my $id = '@_AG_N' . $namecount++; |
94
|
1
|
|
|
|
|
7
|
$result .= "my $id = map { \$_AG_ATTR->get(\$_)->get('$iattr') } $itarget;\n"; |
95
|
1
|
|
|
|
|
8
|
"(map { \$_->get('$iattr', '$at') } $id)"; |
96
|
56
|
|
|
|
|
312
|
}; |
97
|
|
|
|
|
|
|
|
98
|
56
|
|
|
|
|
217
|
$code =~ s[\$/ \s* \. \s* (\w+)] |
99
|
19
|
|
|
|
|
47
|
[$idxa->('$_AG_SELF', $1)]gex; |
100
|
56
|
|
|
|
|
188
|
$code =~ s[\$<(\w+)> \s* \. \s* (\w+)] |
101
|
21
|
|
|
|
|
116
|
[$idxa->("Language::AttributeGrammar::Parser::_get_child(\$_AG_SELF, '$1', '$at')", $2)]gex; |
102
|
56
|
|
|
|
|
180
|
$code = _filter_direct($code, $at); |
103
|
56
|
|
|
|
|
112
|
$code =~ s[`(.*?)` \s* \. \s* (\w+)] |
104
|
1
|
|
|
|
|
4
|
[$idxarray->($1, $2)]gex; |
105
|
|
|
|
|
|
|
|
106
|
56
|
|
|
|
|
207
|
$result .= "\$_AG_ATTR->get($target)->get('$attr')->set(sub $code, '$attr', '$at');\n"; |
107
|
56
|
|
|
|
|
635
|
$result; |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# use an attribute grammar to process the attribute grammar grammar |
111
|
|
|
|
|
|
|
our $ENGINE = Language::AttributeGrammar::Engine->new; |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
add_visitor $ENGINE "$prefix\::grammar" => sub { |
114
|
|
|
|
|
|
|
my ($self, $attrs) = @_; |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
my $prefix = $attrs->get($self)->get('prefix'); |
117
|
|
|
|
|
|
|
for (@{$self->{attrsdefs}}) { |
118
|
|
|
|
|
|
|
$attrs->get($_)->get('prefix')->set(sub { $prefix->get }); |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
my @defthunks = map { $attrs->get($_)->get('defthunks') } @{$self->{attrsdefs}}; |
121
|
|
|
|
|
|
|
my @cases = map { $attrs->get($_)->get('case') } @{$self->{attrsdefs}}; |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
$attrs->get($self)->get('engine')->set(sub { |
124
|
|
|
|
|
|
|
my %visitors; |
125
|
|
|
|
|
|
|
for my $defs (@defthunks) { |
126
|
|
|
|
|
|
|
for my $def (@{$defs->get}) { |
127
|
|
|
|
|
|
|
my $case = $def->{case}->get; |
128
|
|
|
|
|
|
|
$visitors{$case} ||= "my (\$_AG_SELF, \$_AG_ATTR) = \@_;\n"; |
129
|
|
|
|
|
|
|
$visitors{$case} .= $def->{visitor}->get; |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
my $engine = Language::AttributeGrammar::Engine->new; |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
for my $case (@cases) { |
137
|
|
|
|
|
|
|
$engine->add_case($case->get); |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
for my $case (keys %visitors) { |
141
|
|
|
|
|
|
|
my $code = eval "sub {\n$visitors{$case}\n}" or croak $@; |
142
|
|
|
|
|
|
|
$engine->add_visitor($case => $code); |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
$engine; |
146
|
|
|
|
|
|
|
}); |
147
|
|
|
|
|
|
|
}; |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
add_visitor $ENGINE "$prefix\::attrsdef" => sub { |
150
|
|
|
|
|
|
|
my ($self, $attrs) = @_; |
151
|
|
|
|
|
|
|
my $prefix = $attrs->get($self)->get('prefix'); |
152
|
|
|
|
|
|
|
$attrs->get($self->{case})->get('prefix')->set(sub { $prefix->get }); |
153
|
|
|
|
|
|
|
my $case = $attrs->get($self->{case})->get('name'); |
154
|
|
|
|
|
|
|
for (@{$self->{attrdefs}}) { |
155
|
|
|
|
|
|
|
$attrs->get($_)->get('case')->set(sub { $case->get }); |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
my @defthunks = map { |
158
|
|
|
|
|
|
|
{ |
159
|
|
|
|
|
|
|
case => $case, |
160
|
|
|
|
|
|
|
visitor => $attrs->get($_)->get('visitor'), |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
} @{$self->{attrdefs}}; |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
$attrs->get($self)->get('defthunks')->set(sub { \@defthunks }); |
165
|
|
|
|
|
|
|
$attrs->get($self)->get('case')->set(sub { $case->get }); |
166
|
|
|
|
|
|
|
}; |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
add_visitor $ENGINE "$prefix\::attrdef" => sub { |
169
|
|
|
|
|
|
|
my ($self, $attrs) = @_; |
170
|
|
|
|
|
|
|
my $target = $attrs->get($self->{attrcall})->get('target'); |
171
|
|
|
|
|
|
|
my $attr = $attrs->get($self->{attrcall})->get('attr'); |
172
|
|
|
|
|
|
|
my $code = $attrs->get($self->{attrblock})->get('code'); |
173
|
|
|
|
|
|
|
$attrs->get($self)->get('visitor')->set(sub { |
174
|
|
|
|
|
|
|
_filter_code($target->get, $attr->get, $code->get, "grammar line $self->{thisline}"); |
175
|
|
|
|
|
|
|
}); |
176
|
|
|
|
|
|
|
}; |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
add_visitor $ENGINE "$prefix\::attrcall" => sub { |
179
|
|
|
|
|
|
|
my ($self, $attrs) = @_; |
180
|
|
|
|
|
|
|
my $invocant = $attrs->get($self->{target})->get('invocant'); |
181
|
|
|
|
|
|
|
my $attr = $attrs->get($self->{attr})->get('name'); |
182
|
|
|
|
|
|
|
$attrs->get($self)->get('target')->set(sub { $invocant->get }); |
183
|
|
|
|
|
|
|
$attrs->get($self)->get('attr')->set(sub { $attr->get }); |
184
|
|
|
|
|
|
|
}; |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
add_visitor $ENGINE "$prefix\::attrblock" => sub { |
187
|
|
|
|
|
|
|
my ($self, $attrs) = @_; |
188
|
|
|
|
|
|
|
$attrs->get($self)->get('code')->set(sub { $self->{value} }); |
189
|
|
|
|
|
|
|
}; |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
add_visitor $ENGINE "$prefix\::case" => sub { |
192
|
|
|
|
|
|
|
my ($self, $attrs) = @_; |
193
|
|
|
|
|
|
|
my $prefixa = $attrs->get($self)->get('prefix'); |
194
|
|
|
|
|
|
|
$attrs->get($self)->get('name')->set(sub { |
195
|
|
|
|
|
|
|
my $prefix = $prefixa->get; |
196
|
|
|
|
|
|
|
if ($self->{value} =~ /^::/) { |
197
|
|
|
|
|
|
|
$self->{value}; |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
else { |
200
|
|
|
|
|
|
|
"$prefix$self->{value}"; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
}); |
203
|
|
|
|
|
|
|
}; |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
add_visitor $ENGINE "$prefix\::attr" => sub { |
206
|
|
|
|
|
|
|
my ($self, $attrs) = @_; |
207
|
|
|
|
|
|
|
$attrs->get($self)->get('name')->set(sub { $self->{value} }); |
208
|
|
|
|
|
|
|
}; |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
add_visitor $ENGINE "$prefix\::self" => sub { |
211
|
|
|
|
|
|
|
my ($self, $attrs) = @_; |
212
|
|
|
|
|
|
|
$attrs->get($self)->get('invocant')->set(sub { '$_AG_SELF' }); |
213
|
|
|
|
|
|
|
}; |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
add_visitor $ENGINE "$prefix\::child" => sub { |
216
|
|
|
|
|
|
|
my ($self, $attrs) = @_; |
217
|
|
|
|
|
|
|
my ($name) = $self->{value} =~ /^\$<(\w+)>$/; |
218
|
|
|
|
|
|
|
$attrs->get($self)->get('invocant')->set(sub { |
219
|
|
|
|
|
|
|
"Language::AttributeGrammar::Parser::_get_child(\$_AG_SELF, '$name', 'grammar line $self->{thisline}')" |
220
|
|
|
|
|
|
|
}); |
221
|
|
|
|
|
|
|
}; |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
add_visitor $ENGINE "$prefix\::special" => sub { |
224
|
|
|
|
|
|
|
my ($self, $attrs) = @_; |
225
|
|
|
|
|
|
|
my ($code) = $self->{value} =~ /^`(.*)`$/; |
226
|
|
|
|
|
|
|
$code = _filter_direct($code, $self->{thisline}); |
227
|
|
|
|
|
|
|
$attrs->get($self)->get('invocant')->set(sub { $code }); |
228
|
|
|
|
|
|
|
}; |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
$ENGINE->make_visitor('visit'); |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
our $PARSER; |
234
|
|
|
|
|
|
|
{ |
235
|
|
|
|
|
|
|
local $Parse::RecDescent::skip = $SKIP; |
236
|
|
|
|
|
|
|
local $::RD_AUTOACTION = $AUTOACTION; |
237
|
|
|
|
|
|
|
$PARSER = Parse::RecDescent->new($GRAMMAR); |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
sub new { |
241
|
19
|
|
|
19
|
0
|
56
|
my ($class, $grammar, $prefix) = @_; |
242
|
19
|
|
100
|
|
|
105
|
$prefix ||= ''; |
243
|
19
|
100
|
|
|
|
220
|
my $tree = $PARSER->grammar($grammar) or croak "Parse error"; |
244
|
18
|
|
|
|
|
224338
|
return $ENGINE->evaluate('visit', $tree, 'engine', { |
245
|
|
|
|
|
|
|
prefix => $prefix, |
246
|
|
|
|
|
|
|
}); |
247
|
|
|
|
|
|
|
} |