File: | lib/Parse/EBNF/Token.pm |
Coverage: | 93.8% |
line | stmt | bran | cond | sub | time | code |
---|---|---|---|---|---|---|
1 | package Parse::EBNF::Token; | |||||
2 | ||||||
3 | sub new { | |||||
4 | 61 | 156 | my ($class) = @_; | |||
5 | 61 | 196 | my $self = bless {}, $class; | |||
6 | 61 | 176 | $self->{error} = 0; | |||
7 | 61 | 163 | return $self; | |||
8 | } | |||||
9 | ||||||
10 | sub reduce_alternations { | |||||
11 | 51 | 125 | my ($self) = @_; | |||
12 | ||||||
13 | 51 | 229 | return 1 unless $self->{type} eq 'list'; | |||
14 | ||||||
15 | ||||||
16 | # | |||||
17 | # reduce our own children first | |||||
18 | # | |||||
19 | ||||||
20 | 21 21 | 35 71 | for my $token(@{$self->{tokens}}){ | |||
21 | 31 | 94 | $token->reduce_alternations(); | |||
22 | } | |||||
23 | ||||||
24 | ||||||
25 | # | |||||
26 | # now check if we have any alts | |||||
27 | # | |||||
28 | ||||||
29 | 21 | 42 | my $alts = 0; | |||
30 | 21 21 | 34 71 | for my $token(@{$self->{tokens}}){ | |||
31 | 31 | 134 | $alts++ if $token->{type} eq 'alt'; | |||
32 | } | |||||
33 | ||||||
34 | 21 | 99 | return 1 unless $alts; | |||
35 | ||||||
36 | ||||||
37 | # | |||||
38 | # we have alts - change our base type and create new alt children | |||||
39 | # | |||||
40 | ||||||
41 | 2 | 5 | my $our_tokens = $self->{tokens}; | |||
42 | 2 | 6 | $self->{tokens} = []; | |||
43 | 2 | 6 | $self->{type} = 'alternation'; | |||
44 | ||||||
45 | 2 | 6 | my $current = Parse::EBNF::Token->new(); | |||
46 | 2 | 5 | $current->{type} = 'list'; | |||
47 | 2 | 7 | $current->{tokens} = []; | |||
48 | ||||||
49 | 2 2 | 4 5 | for my $token(@{$our_tokens}){ | |||
50 | ||||||
51 | 6 | 22 | if ($token->{type} eq 'alt'){ | |||
52 | ||||||
53 | 2 2 | 4 6 | push @{$self->{tokens}}, $current; | |||
54 | ||||||
55 | 2 | 6 | $current = Parse::EBNF::Token->new(); | |||
56 | 2 | 5 | $current->{type} = 'list'; | |||
57 | 2 | 8 | $current->{tokens} = []; | |||
58 | ||||||
59 | }else{ | |||||
60 | 4 4 | 7 16 | push @{$current->{tokens}}, $token; | |||
61 | } | |||||
62 | } | |||||
63 | ||||||
64 | 2 2 | 5 7 | push @{$self->{tokens}}, $current; | |||
65 | ||||||
66 | 2 | 9 | return 1; | |||
67 | } | |||||
68 | ||||||
69 | sub reduce_repetition { | |||||
70 | 53 | 127 | my ($self) = @_; | |||
71 | ||||||
72 | 53 | 395 | return 1 unless (($self->{type} eq 'list') || ($self->{type} eq 'alternation')); | |||
73 | ||||||
74 | # | |||||
75 | # reduce our own children first | |||||
76 | # | |||||
77 | ||||||
78 | 25 25 | 38 85 | for my $token(@{$self->{tokens}}){ | |||
79 | 33 | 85 | $token->reduce_repetition(); | |||
80 | } | |||||
81 | ||||||
82 | ||||||
83 | # | |||||
84 | # do it | |||||
85 | # | |||||
86 | ||||||
87 | 25 | 63 | my $old_tokens = $self->{tokens}; | |||
88 | 25 | 74 | $self->{tokens} = []; | |||
89 | ||||||
90 | 25 25 | 41 62 | for my $token(@{$old_tokens}){ | |||
91 | ||||||
92 | 33 | 118 | if ($token->{type} =~ m!^rep (.*)$!){ | |||
93 | ||||||
94 | 3 | 8 | my $new = Parse::EBNF::Token->new(); | |||
95 | 3 | 15 | $new->{type} = 'repeat '.$1; | |||
96 | 3 | 10 | $new->{tokens} = []; | |||
97 | ||||||
98 | 3 3 | 6 9 | my $subject = pop @{$self->{tokens}}; | |||
99 | ||||||
100 | 3 | 10 | unless (defined $subject){ | |||
101 | 0 | 0 | $self->{error} = "repetition operator without suject"; | |||
102 | 0 | 0 | return 0; | |||
103 | } | |||||
104 | ||||||
105 | 3 3 | 5 11 | push @{$new->{tokens}}, $subject; | |||
106 | ||||||
107 | 3 3 | 5 14 | push @{$self->{tokens}}, $new; | |||
108 | }else{ | |||||
109 | ||||||
110 | 30 30 | 45 128 | push @{$self->{tokens}}, $token; | |||
111 | } | |||||
112 | } | |||||
113 | ||||||
114 | 25 | 92 | return 1; | |||
115 | } | |||||
116 | ||||||
117 | sub reduce_empty { | |||||
118 | 53 | 126 | my ($self) = @_; | |||
119 | ||||||
120 | ||||||
121 | # | |||||
122 | # reduce our own children first | |||||
123 | # | |||||
124 | ||||||
125 | 53 | 179 | if (defined($self->{tokens})){ | |||
126 | 28 28 | 46 88 | for my $token(@{$self->{tokens}}){ | |||
127 | 33 | 90 | $token->reduce_empty(); | |||
128 | } | |||||
129 | } | |||||
130 | ||||||
131 | ||||||
132 | # | |||||
133 | # reduce self? | |||||
134 | # | |||||
135 | ||||||
136 | 53 | 188 | if ($self->{type} eq 'list'){ | |||
137 | 23 23 | 32 102 | if (scalar(@{$self->{tokens}}) == 1){ | |||
138 | 21 | 64 | my $child = $self->{tokens}->[0]; | |||
139 | ||||||
140 | 21 21 63 | 35 85 191 | for my $key(keys %{$self}){ delete $self->{$key}; } | |||
141 | 21 21 64 | 44 72 258 | for my $key(keys %{$child}){ $self->{$key} = $child->{$key}; } | |||
142 | } | |||||
143 | } | |||||
144 | ||||||
145 | 53 | 193 | return 1; | |||
146 | } | |||||
147 | ||||||
148 | sub reduce_rx { | |||||
149 | 32 | 76 | my ($self) = @_; | |||
150 | ||||||
151 | ||||||
152 | # | |||||
153 | # reduce our own children first | |||||
154 | # | |||||
155 | ||||||
156 | 32 | 110 | if (defined($self->{tokens})){ | |||
157 | 7 7 | 11 23 | for my $token(@{$self->{tokens}}){ | |||
158 | 12 | 39 | $token->reduce_rx(); | |||
159 | } | |||||
160 | } | |||||
161 | ||||||
162 | 32 | 309 | return 1 unless (($self->{type} eq 'alternation') || ($self->{type} eq 'list')); | |||
163 | ||||||
164 | ||||||
165 | # | |||||
166 | # see if we're in a position to reduce self... | |||||
167 | # | |||||
168 | ||||||
169 | 4 4 | 8 14 | for my $token(@{$self->{tokens}}){ | |||
170 | 5 | 21 | next if $token->{type} eq 'literal'; | |||
171 | 3 | 11 | next if $token->{type} eq 'rx'; | |||
172 | 3 | 13 | return 1; | |||
173 | } | |||||
174 | ||||||
175 | ||||||
176 | # | |||||
177 | # we can reduce all of our children into a single rx | |||||
178 | # | |||||
179 | ||||||
180 | 1 | 2 | my @rx; | |||
181 | ||||||
182 | 1 1 | 4 5 | for my $token(@{$self->{tokens}}){ | |||
183 | ||||||
184 | 2 | 8 | if ($token->{type} eq 'literal'){ | |||
185 | 2 | 9 | push @rx, '('.quotemeta($token->{content}).')'; | |||
186 | } | |||||
187 | ||||||
188 | 2 | 10 | if ($token->{type} eq 'rx'){ | |||
189 | 0 | 0 | push @rx, $token->{content}; | |||
190 | } | |||||
191 | } | |||||
192 | ||||||
193 | 1 | 3 | my $rx = ''; | |||
194 | 1 | 4 | $rx = join('', @rx) if $self->{type} eq 'list'; | |||
195 | 1 | 6 | $rx = join('|', @rx) if $self->{type} eq 'alternation'; | |||
196 | ||||||
197 | 1 | 2 | $self->{type} = 'rx'; | |||
198 | 1 | 3 | $self->{content} = $rx; | |||
199 | 1 | 3 | $self->{tokens} = []; | |||
200 | ||||||
201 | 1 | 6 | return 1; | |||
202 | } | |||||
203 | ||||||
204 | 1; | |||||
205 |