| 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 | ||||||