| File: | blib/lib/CSS/Grammar.pm |
| Coverage: | 98.5% |
| line | stmt | bran | cond | sub | time | code |
|---|---|---|---|---|---|---|
| 1 | package CSS::Grammar; | |||||
| 2 | ||||||
| 3 | 12 12 12 | 52 17 57 | use strict; | |||
| 4 | 12 12 12 | 108 22 46 | use warnings; | |||
| 5 | ||||||
| 6 | 12 12 12 | 85 25 88 | use CSS::Parse::Rule; | |||
| 7 | 12 12 12 | 93 25 80 | use CSS::Stylesheet; | |||
| 8 | 12 12 12 | 61 18 53 | use Time::HiRes qw(gettimeofday tv_interval); | |||
| 9 | 12 12 12 | 67 20 63 | use Data::Dumper; | |||
| 10 | ||||||
| 11 | sub new { | |||||
| 12 | 36 | 265 | my ($class) = @_; | |||
| 13 | 36 | 137 | my $self = bless {}, $class; | |||
| 14 | ||||||
| 15 | 36 | 130 | $self->{toke_rules} = {}; | |||
| 16 | 36 | 122 | $self->{toke_order} = []; | |||
| 17 | 36 | 102 | $self->{case_insensitive} = 0; | |||
| 18 | 36 | 104 | $self->{base_rule} = ''; | |||
| 19 | 36 | 113 | $self->{lex_rules} = {}; | |||
| 20 | 36 | 106 | $self->{error} = ''; | |||
| 21 | ||||||
| 22 | 36 | 139 | $self->init(); | |||
| 23 | ||||||
| 24 | 36 | 198 | return $self; | |||
| 25 | } | |||||
| 26 | ||||||
| 27 | sub init { | |||||
| 28 | 4 | 16 | my ($self) = @_; | |||
| 29 | } | |||||
| 30 | ||||||
| 31 | sub add_toke_rule { | |||||
| 32 | 957 | 3147 | my ($self, $name, $rx) = @_; | |||
| 33 | ||||||
| 34 | 957 | 2422 | $self->{error} = ''; | |||
| 35 | ||||||
| 36 | 957 | 2934 | if ($self->{case_insensitive}){ | |||
| 37 | ||||||
| 38 | 954 | 17737 | $self->{toke_rules}->{$name} = qr/^($rx)/is; | |||
| 39 | }else{ | |||||
| 40 | 3 | 44 | $self->{toke_rules}->{$name} = qr/^($rx)/s; | |||
| 41 | } | |||||
| 42 | ||||||
| 43 | 957 957 | 1888 3930 | push @{$self->{toke_order}}, $name; | |||
| 44 | } | |||||
| 45 | ||||||
| 46 | sub add_lex_rule { | |||||
| 47 | 521 | 1709 | my ($self, $name, $rule_source) = @_; | |||
| 48 | ||||||
| 49 | 521 | 1358 | $self->{error} = ''; | |||
| 50 | ||||||
| 51 | 521 | 2009 | my $rule = CSS::Parse::Rule->new($self, $name, $rule_source); | |||
| 52 | ||||||
| 53 | 521 | 1842 | if ($rule->{error}){ | |||
| 54 | 1 | 5 | $self->{error} = $rule->{error}; | |||
| 55 | 1 | 11 | return 0; | |||
| 56 | } | |||||
| 57 | ||||||
| 58 | 520 | 1867 | $self->{lex_rules}->{$name} = $rule; | |||
| 59 | 520 | 1330 | return 1; | |||
| 60 | } | |||||
| 61 | ||||||
| 62 | sub toke { | |||||
| 63 | 28 | 105 | my ($self, $input) = @_; | |||
| 64 | ||||||
| 65 | 28 | 78 | $self->{error} = ''; | |||
| 66 | ||||||
| 67 | 28 | 73 | my $tokens = []; | |||
| 68 | ||||||
| 69 | 28 | 115 | while(length $input){ | |||
| 70 | ||||||
| 71 | 1058 | 2115 | my $matched = 0; | |||
| 72 | ||||||
| 73 | 1058 1058 | 1780 3765 | for my $rule(@{$self->{toke_order}}){ | |||
| 74 | ||||||
| 75 | #my $match_start = [gettimeofday]; | |||||
| 76 | ||||||
| 77 | 14416 | 78313 | if ($input =~ $self->{toke_rules}->{$rule}){ | |||
| 78 | ||||||
| 79 | #$self->{time_match} += tv_interval($match_start, [gettimeofday]); | |||||
| 80 | ||||||
| 81 | #my $push_start = [gettimeofday]; | |||||
| 82 | 1056 1056 | 1654 4007 | push @{$tokens}, CSS::Token->new($rule, $1); | |||
| 83 | #$self->{time_push} += tv_interval($push_start, [gettimeofday]); | |||||
| 84 | ||||||
| 85 | #my $substr_start = [gettimeofday]; | |||||
| 86 | 1056 | 3020 | $input = substr $input, length $1; | |||
| 87 | #$self->{time_substr} += tv_interval($substr_start, [gettimeofday]); | |||||
| 88 | ||||||
| 89 | 1056 | 1813 | $matched = 1; | |||
| 90 | ||||||
| 91 | 1056 | 1770 | last; | |||
| 92 | } | |||||
| 93 | ||||||
| 94 | #$self->{time_no_match} += tv_interval($match_start, [gettimeofday]); | |||||
| 95 | } | |||||
| 96 | ||||||
| 97 | 1058 | 4767 | unless ($matched){ | |||
| 98 | ||||||
| 99 | 2 2 | 4 14 | push @{$tokens}, CSS::Token->new('MISC', substr $input, 0, 1); | |||
| 100 | 2 | 11 | $input = substr $input, 1; | |||
| 101 | } | |||||
| 102 | } | |||||
| 103 | ||||||
| 104 | 28 | 96 | return $tokens; | |||
| 105 | } | |||||
| 106 | ||||||
| 107 | sub lex { | |||||
| 108 | 26 | 137 | my ($self, $input) = @_; | |||
| 109 | ||||||
| 110 | 26 | 78 | $self->{error} = ''; | |||
| 111 | ||||||
| 112 | 26 | 107 | my $rule = $self->{lex_rules}->{$self->{base_rule}}; | |||
| 113 | ||||||
| 114 | 26 | 86 | return undef unless defined $rule; | |||
| 115 | ||||||
| 116 | 25 | 104 | my $match = $rule->match($input, 0); | |||
| 117 | ||||||
| 118 | 25 | 86 | return undef unless defined $match; | |||
| 119 | ||||||
| 120 | 24 | 87 | my $leftover = $match->tokens_left; | |||
| 121 | ||||||
| 122 | 24 | 74 | if ($leftover){ | |||
| 123 | ||||||
| 124 | 1 | 5 | $self->{error} = "Lexer didn't match all tokens with base rule ($leftover left over)\n"; | |||
| 125 | } | |||||
| 126 | ||||||
| 127 | 24 | 73 | return $match; | |||
| 128 | } | |||||
| 129 | ||||||
| 130 | sub set_base { | |||||
| 131 | 33 | 109 | my ($self, $base_rule) = @_; | |||
| 132 | ||||||
| 133 | 33 | 236 | $self->{base_rule} = $base_rule; | |||
| 134 | } | |||||
| 135 | ||||||
| 136 | sub find_lex_rule { | |||||
| 137 | 4550 | 12083 | my ($self, $rule_name) = @_; | |||
| 138 | ||||||
| 139 | 4550 | 19344 | return $self->{lex_rules}->{$rule_name}; | |||
| 140 | } | |||||
| 141 | ||||||
| 142 | sub parse { | |||||
| 143 | 12 | 41 | my ($self, $input) = @_; | |||
| 144 | ||||||
| 145 | # | |||||
| 146 | # this method just ties together a bunch of stuff to turn an input string into a | |||||
| 147 | # CSS::Stylesheet object | |||||
| 148 | # | |||||
| 149 | ||||||
| 150 | 12 | 46 | my $tokens = $self->toke($input); | |||
| 151 | 12 12 | 22 47 | return undef unless scalar(@{$tokens}); | |||
| 152 | ||||||
| 153 | 12 | 98 | my $tree = $self->lex($tokens); | |||
| 154 | 12 | 37 | return undef unless defined $tree; | |||
| 155 | ||||||
| 156 | 12 | 52 | $tree->scrub; | |||
| 157 | 12 | 47 | $tree->reduce; | |||
| 158 | ||||||
| 159 | 12 | 73 | my $sheet = $self->walk($tree); | |||
| 160 | ||||||
| 161 | 12 | 671 | return $sheet; | |||
| 162 | } | |||||
| 163 | ||||||
| 164 | sub walk { | |||||
| 165 | 16 | 53 | my ($self, $tree) = @_; | |||
| 166 | ||||||
| 167 | 16 | 82 | my $stylesheet = new CSS::Stylesheet; | |||
| 168 | ||||||
| 169 | 16 | 55 | return $stylesheet unless defined $tree; | |||
| 170 | 15 | 72 | return $stylesheet unless $tree->{subrule} eq $self->{base_rule}; | |||
| 171 | ||||||
| 172 | 14 | 64 | $self->walk_stylesheet($stylesheet, $tree->{submatches}); | |||
| 173 | ||||||
| 174 | 14 | 37 | return $stylesheet; | |||
| 175 | } | |||||
| 176 | ||||||
| 177 | ||||||
| 178 | package CSS::Token; | |||||
| 179 | ||||||
| 180 | sub new { | |||||
| 181 | 1059 | 3372 | my ($class, $type, $content) = @_; | |||
| 182 | 1059 | 3133 | my $self = bless {}, $class; | |||
| 183 | ||||||
| 184 | 1059 | 3273 | $self->{type} = $type; | |||
| 185 | 1059 | 2705 | $self->{content} = $content; | |||
| 186 | ||||||
| 187 | 1059 | 2703 | return $self; | |||
| 188 | } | |||||
| 189 | ||||||
| 190 | 1; | |||||
| 191 | ||||||