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 |