File Coverage

File:blib/lib/CSS/Grammar/CSS21.pm
Coverage:100.0%

linestmtbrancondsubtimecode
1package CSS::Grammar::CSS21;
2
3
4
4
4
20
8
23
use strict;
4
4
4
4
28
13
23
use warnings;
5
6
4
4
4
32
11
23
use base 'CSS::Grammar';
7
8#
9# http://www.w3.org/TR/CSS21/
10# http://www.w3.org/TR/CSS21/grammar.html
11# http://www.w3.org/TR/CSS21/syndata.html#parsing-errors
12#
13
14sub init {
15
4
13
        my ($self) = @_;
16
17
4
7
        my %rx;
18
19        #####################################################################################
20
21
4
12
        $self->{case_insensitive} = 1;
22
23
24        #####################################################################################
25
26        #h [0-9a-f]
27        #nonascii [\200-\377]
28        #unicode \\{h}{1,6}(\r\n|[ \t\r\n\f])?
29        #escape {unicode}|\\[^\r\n\f0-9a-f]
30        #s [ \t\r\n\f]+
31        #w {s}?
32        #nl \n|\r\n|\r|\f
33
34
4
14
        $rx{h} = '[0-9a-f]';
35
4
11
        $rx{nonascii} = '[\\x80-\\xff]';
36
4
18
        $rx{unicode} = '(\\'.$rx{h}.'{1,6}(\\r\\n|[ \\t\\r\\n\\f])?)';
37
4
20
        $rx{escape} = '('.$rx{unicode}.'|\\\\[ -~\\x80-\\xff])';
38
4
9
        $rx{s} = '[ \t\r\n\f]';
39
4
17
        $rx{w} = "$rx{s}*";
40
4
11
        $rx{nl} = '(\\n|\\r\\n|\\r|\\f)';
41
42
43        #nmstart [_a-z]|{nonascii}|{escape}
44        #nmchar [_a-z0-9-]|{nonascii}|{escape}
45        #string1 \"([^\n\r\f\\"]|\\{nl}|{escape})*\"
46        #string2 \'([^\n\r\f\\']|\\{nl}|{escape})*\'
47
48
4
183
        $rx{nmstart} = '([_a-z]|'.$rx{nonascii}.'|'.$rx{escape}.')';
49
4
25
        $rx{nmchar} = '([_a-zA-Z0-9-]|'.$rx{nonascii}.'|'.$rx{escape}.')';
50
4
28
        $rx{string1} = '("([\\t !#$%&(-~]|\\\\('.$rx{nl}.')|\'|('.$rx{nonascii}.')|('.$rx{escape}.'))*")';
51
4
28
        $rx{string2} = '(\'([\\t !#$%&(-~]|\\\\('.$rx{nl}.')|"|('.$rx{nonascii}.')|('.$rx{escape}.'))*\')';
52
53
54        #ident -?{nmstart}{nmchar}*
55        #name {nmchar}+
56        #num [0-9]+|[0-9]*"."[0-9]+
57        #string {string1}|{string2}
58        #url ([!#$%&*-~]|{nonascii}|{escape})*
59
60
4
20
        $rx{ident} = "$rx{nmstart}$rx{nmchar}*";
61
4
13
        $rx{name} = "$rx{nmchar}+";
62
4
12
        $rx{num} = '([0-9]+|[0-9]*\\.[0-9]+)';
63
4
24
        $rx{string} = "($rx{string1}|$rx{string2})";
64
4
26
        $rx{url} = "(([!#\$%&*-~]|$rx{nonascii}|$rx{escape})*)";
65
66
67        # these aren't used in any productions...
68        #invalid1 \"([^\n\r\f\\"]|\\{nl}|{escape})*
69        #invalid2 \'([^\n\r\f\\']|\\{nl}|{escape})*
70        #invalid {invalid1}|{invalid2}
71
72
73        #A a|\\0{0,4}(41|61)(\r\n|[ \t\r\n\f])?
74        #C c|\\0{0,4}(43|63)(\r\n|[ \t\r\n\f])?
75        #D d|\\0{0,4}(44|64)(\r\n|[ \t\r\n\f])?
76        #E e|\\0{0,4}(45|65)(\r\n|[ \t\r\n\f])?
77        #G g|\\0{0,4}(47|67)(\r\n|[ \t\r\n\f])?|\\g
78        #H h|\\0{0,4}(48|68)(\r\n|[ \t\r\n\f])?|\\h
79        #I i|\\0{0,4}(49|69)(\r\n|[ \t\r\n\f])?|\\i
80        #K k|\\0{0,4}(4b|6b)(\r\n|[ \t\r\n\f])?|\\k
81        #M m|\\0{0,4}(4d|6d)(\r\n|[ \t\r\n\f])?|\\m
82        #N n|\\0{0,4}(4e|6e)(\r\n|[ \t\r\n\f])?|\\n
83        #O o|\\0{0,4}(51|71)(\r\n|[ \t\r\n\f])?|\\o
84        #P p|\\0{0,4}(50|70)(\r\n|[ \t\r\n\f])?|\\p
85        #R r|\\0{0,4}(52|72)(\r\n|[ \t\r\n\f])?|\\r
86        #S s|\\0{0,4}(53|73)(\r\n|[ \t\r\n\f])?|\\s
87        #T t|\\0{0,4}(54|74)(\r\n|[ \t\r\n\f])?|\\t
88        #X x|\\0{0,4}(58|78)(\r\n|[ \t\r\n\f])?|\\x
89        #Z z|\\0{0,4}(5a|7a)(\r\n|[ \t\r\n\f])?|\\z
90
91
4
11
        $rx{A} = '(a|\\\\0{0,4}(41|61)(\r\n|[ \t\r\n\f])?)';
92
4
13
        $rx{C} = '(c|\\\\0{0,4}(43|63)(\r\n|[ \t\r\n\f])?)';
93
4
11
        $rx{D} = '(d|\\\\0{0,4}(44|64)(\r\n|[ \t\r\n\f])?)';
94
4
11
        $rx{E} = '(e|\\\\0{0,4}(45|65)(\r\n|[ \t\r\n\f])?)';
95
4
11
        $rx{G} = '(g|\\\\0{0,4}(47|67)(\r\n|[ \t\r\n\f])?|\\\\g)';
96
4
13
        $rx{H} = '(h|\\\\0{0,4}(48|68)(\r\n|[ \t\r\n\f])?|\\\\h)';
97
4
11
        $rx{I} = '(i|\\\\0{0,4}(49|69)(\r\n|[ \t\r\n\f])?|\\\\i)';
98
4
11
        $rx{K} = '(k|\\\\0{0,4}(4b|6b)(\r\n|[ \t\r\n\f])?|\\\\k)';
99
4
11
        $rx{M} = '(m|\\\\0{0,4}(4d|6d)(\r\n|[ \t\r\n\f])?|\\\\m)';
100
4
12
        $rx{N} = '(n|\\\\0{0,4}(4e|6e)(\r\n|[ \t\r\n\f])?|\\\\n)';
101
4
13
        $rx{O} = '(o|\\\\0{0,4}(51|71)(\r\n|[ \t\r\n\f])?|\\\\o)';
102
4
10
        $rx{P} = '(p|\\\\0{0,4}(50|70)(\r\n|[ \t\r\n\f])?|\\\\p)';
103
4
12
        $rx{R} = '(r|\\\\0{0,4}(52|72)(\r\n|[ \t\r\n\f])?|\\\\r)';
104
4
9
        $rx{S} = '(s|\\\\0{0,4}(53|73)(\r\n|[ \t\r\n\f])?|\\\\s)';
105
4
11
        $rx{T} = '(t|\\\\0{0,4}(54|74)(\r\n|[ \t\r\n\f])?|\\\\t)';
106
4
12
        $rx{X} = '(x|\\\\0{0,4}(58|78)(\r\n|[ \t\r\n\f])?|\\\\x)';
107
4
18
        $rx{Z} = '(z|\\\\0{0,4}(5a|7a)(\r\n|[ \t\r\n\f])?|\\\\z)';
108
109
110        #####################################################################################
111
112        #{s} {return S;}
113        #"<!--" {return CDO;}
114        #"-->" {return CDC;}
115        #"~=" {return INCLUDES;}
116        #"|=" {return DASHMATCH;}
117
118
4
28
        $self->add_toke_rule('S' , "$rx{s}+");
119
4
17
        $self->add_toke_rule('CDO' , '<!--');
120
4
15
        $self->add_toke_rule('CDC' , '-->');
121
4
15
        $self->add_toke_rule('INCLUDES' , '~=');
122
4
15
        $self->add_toke_rule('DASHMATCH' , '\\|=');
123
124
125        #{w}"{" {return LBRACE;}
126        #{w}"+" {return PLUS;}
127        #{w}">" {return GREATER;}
128        #{w}"," {return COMMA;}
129
130
4
23
        $self->add_toke_rule('LBRACE' , "$rx{w}\\{");
131
4
20
        $self->add_toke_rule('PLUS' , "$rx{w}\\+");
132
4
20
        $self->add_toke_rule('GREATER' , "$rx{w}>");
133
4
20
        $self->add_toke_rule('COMMA' , "$rx{w},");
134
135
136        #{string} {return STRING;}
137        #{invalid} {return INVALID; /* unclosed string */}
138        #{ident} {return IDENT;}
139        #"#"{name} {return HASH;}
140
141
4
16
        $self->add_toke_rule('STRING' , $rx{string});
142
4
18
        $self->add_toke_rule('IDENT' , $rx{ident});
143
4
22
        $self->add_toke_rule('HASH' , "#$rx{name}");
144
145
146        #@{I}{M}{P}{O}{R}{T} {return IMPORT_SYM;}
147        #@{P}{A}{G}{E} {return PAGE_SYM;}
148        #@{M}{E}{D}{I}{A} {return MEDIA_SYM;}
149        #@{C}{H}{A}{R}{S}{E}{T} {return CHARSET_SYM;}
150
151        #"!"({w}|{comment})*{I}{M}{P}{O}{R}{T}{A}{N}{T} {return IMPORTANT_SYM;}
152
153
4
40
        $self->add_toke_rule('IMPORT_SYM' , "\@$rx{I}$rx{M}$rx{P}$rx{O}$rx{R}$rx{T}");
154
4
31
        $self->add_toke_rule('PAGE_SYM' , "\@$rx{P}$rx{A}$rx{G}$rx{E}");
155
4
35
        $self->add_toke_rule('MEDIA_SYM' , "\@$rx{M}$rx{E}$rx{D}$rx{I}$rx{A}");
156
4
43
        $self->add_toke_rule('CHARSET_SYM' , "\@$rx{C}$rx{H}$rx{A}$rx{R}$rx{S}$rx{E}$rx{T}");
157
158
4
56
        $self->add_toke_rule('IMPORTANT_SYM' , "!$rx{w}$rx{I}$rx{M}$rx{P}$rx{O}$rx{R}$rx{T}$rx{A}$rx{N}$rx{T}");
159
160
161        #{num}{E}{M} {return EMS;}
162        #{num}{E}{X} {return EXS;}
163        #{num}{P}{X} {return LENGTH;}
164        #{num}{C}{M} {return LENGTH;}
165        #{num}{M}{M} {return LENGTH;}
166        #{num}{I}{N} {return LENGTH;}
167        #{num}{P}{T} {return LENGTH;}
168        #{num}{P}{C} {return LENGTH;}
169        #{num}{D}{E}{G} {return ANGLE;}
170        #{num}{R}{A}{D} {return ANGLE;}
171        #{num}{G}{R}{A}{D} {return ANGLE;}
172        #{num}{M}{S} {return TIME;}
173        #{num}{S} {return TIME;}
174        #{num}{H}{Z} {return FREQ;}
175        #{num}{K}{H}{Z} {return FREQ;}
176        #{num}{ident} {return DIMENSION;}
177
178        #{num}% {return PERCENTAGE;}
179        #{num} {return NUMBER;}
180
181
4
28
        $self->add_toke_rule('EMS' , "$rx{num}$rx{E}$rx{M}");
182
4
27
        $self->add_toke_rule('EXS' , "$rx{num}$rx{E}$rx{X}");
183
4
119
        $self->add_toke_rule('LENGTH' , "$rx{num}($rx{P}$rx{X}|$rx{C}$rx{M}|$rx{M}$rx{M}|$rx{I}$rx{N}|$rx{P}$rx{T}|$rx{P}$rx{C})");
184
4
61
        $self->add_toke_rule('ANGLE' , "$rx{num}($rx{D}$rx{E}$rx{G}|$rx{R}$rx{A}$rx{D}|$rx{G}$rx{R}$rx{A}$rx{D})");
185
4
33
        $self->add_toke_rule('TIME' , "$rx{num}($rx{M}$rx{S}|$rx{S})");
186
4
41
        $self->add_toke_rule('FREQ' , "$rx{num}($rx{H}$rx{Z}|$rx{K}$rx{H}$rx{Z})");
187
4
25
        $self->add_toke_rule('DIMEN' , "$rx{num}$rx{ident}");
188
4
20
        $self->add_toke_rule('PERCENTAGE' , "$rx{num}%");
189
4
72
        $self->add_toke_rule('NUMBER' , "$rx{num}");
190
191
192        #"url("{w}{string}{w}")" {return URI;}
193        #"url("{w}{url}{w}")" {return URI;}
194        #{ident}"(" {return FUNCTION;}
195
196
4
40
        $self->add_toke_rule('URI' , "url\\($rx{w}($rx{string}|$rx{url})$rx{w}\\)");
197
4
20
        $self->add_toke_rule('FUNCTION' , "$rx{ident}\\(");
198
199
200        #####################################################################################
201
202
4
15
        $self->add_toke_rule('_SEMICOLON' , ';');
203
4
15
        $self->add_toke_rule('_BRACE_CLOSE' , '}');
204
4
14
        $self->add_toke_rule('_COLON' , ':');
205
4
16
        $self->add_toke_rule('_SLASH' , '/');
206
4
16
        $self->add_toke_rule('_MINUS' , '-');
207
4
16
        $self->add_toke_rule('_PERIOD' , '\\.');
208
4
16
        $self->add_toke_rule('_STAR' , '\\*');
209
4
15
        $self->add_toke_rule('_SQUARE_OPEN' , '\\[');
210
4
14
        $self->add_toke_rule('_SQUARE_CLOSE' , '\\]');
211
4
15
        $self->add_toke_rule('_EQUALS' , '=');
212
4
15
        $self->add_toke_rule('_ROUND_CLOSE' , '\\)');
213
214
215        #####################################################################################
216
217        #stylesheet
218        # : [ CHARSET_SYM STRING ';' ]?
219        # [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
220        # [ [ ruleset | media | page ] [S|CDO|CDC]* ]*
221        # ;
222        #import
223        # : IMPORT_SYM S*
224        # [STRING|URI] S* [ medium [ COMMA S* medium]* ]? ';' S*
225        # ;
226        #media
227        # : MEDIA_SYM S* medium [ COMMA S* medium ]* LBRACE S* ruleset* '}' S*
228        # ;
229
230
4
26
        $self->add_lex_rule('stylesheet', '[ CHARSET_SYM S* STRING S* _SEMICOLON ]? [S|CDO|CDC]* [ import [S|CDO|CDC]* ]* '.
231                '[ [ ruleset | media | page ] [S|CDO|CDC]* ]*');
232
4
16
        $self->add_lex_rule('import', 'IMPORT_SYM S* [STRING|URI] S* [ medium [ COMMA S* medium]* ]? _SEMICOLON S*');
233
4
15
        $self->add_lex_rule('media', 'MEDIA_SYM S* medium [ COMMA S* medium ]* LBRACE S* ruleset* _BRACE_CLOSE S*');
234
235
236        #medium
237        # : IDENT S*
238        # ;
239        #page
240        # : PAGE_SYM S* pseudo_page? S*
241        # LBRACE S* declaration [ ';' S* declaration ]* '}' S*
242        # ;
243        #pseudo_page
244        # : ':' IDENT
245        # ;
246        #operator
247        # : '/' S* | COMMA S* | /* empty */
248        # ;
249
250
4
15
        $self->add_lex_rule('medium', 'IDENT S*');
251
4
18
        $self->add_lex_rule('page', 'PAGE_SYM S* pseudo_page? S* LBRACE S* declaration [ _SEMICOLON S* declaration ]* _BRACE_CLOSE S*');
252
4
15
        $self->add_lex_rule('pseudo_page', '_COLON IDENT');
253
4
16
        $self->add_lex_rule('operator', '_SLASH S* | COMMA S* | ');
254
255
256        #combinator
257        # : PLUS S*
258        # | GREATER S*
259        # | S
260        # ;
261        #unary_operator
262        # : '-' | PLUS
263        # ;
264        #property
265        # : IDENT S*
266        # ;
267        #ruleset
268        # : selector [ COMMA S* selector ]*
269        # LBRACE S* declaration [ ';' S* declaration ]* '}' S*
270        # ;
271
272
4
15
        $self->add_lex_rule('combinator', 'PLUS S* | GREATER S* | S+');
273
4
17
        $self->add_lex_rule('unary_operator', '_MINUS | PLUS');
274
4
16
        $self->add_lex_rule('property', 'IDENT S*');
275
4
17
        $self->add_lex_rule('ruleset', 'selector [ COMMA S* selector ]* S* LBRACE S* declaration [ _SEMICOLON S* declaration ]* _BRACE_CLOSE S*');
276
277
278        #selector
279        # : simple_selector [ combinator simple_selector ]*
280        # ;
281        #simple_selector
282        # : element_name [ HASH | class | attrib | pseudo ]*
283        # | [ HASH | class | attrib | pseudo ]+
284        # ;
285        #class
286        # : '.' IDENT
287        # ;
288        #element_name
289        # : IDENT | '*'
290        # ;
291
292
4
15
        $self->add_lex_rule('selector', 'simple_selector [ combinator simple_selector ]*');
293
4
15
        $self->add_lex_rule('simple_selector', 'element_name [ HASH | class | attrib | pseudo ]* | [ HASH | class | attrib | pseudo ]+');
294
4
16
        $self->add_lex_rule('class', '_PERIOD IDENT');
295
4
16
        $self->add_lex_rule('element_name', 'IDENT | _STAR');
296
297
298        #attrib
299        # : '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*
300        # [ IDENT | STRING ] S* ]? ']'
301        # ;
302        #pseudo
303        # : ':' [ IDENT | FUNCTION S* IDENT? S* ')' ]
304        # ;
305        #declaration
306        # : property ':' S* expr prio?
307        # | /* empty */
308        # ;
309        #prio
310        # : IMPORTANT_SYM S*
311        # ;
312
313
4
16
        $self->add_lex_rule('attrib', '_SQUARE_OPEN S* IDENT S* [ [ _EQUALS | INCLUDES | DASHMATCH ] S* [ IDENT | STRING ] S* ]? _SQUARE_CLOSE');
314
4
17
        $self->add_lex_rule('pseudo', '_COLON [ IDENT | FUNCTION S* IDENT? S* _ROUND_CLOSE ]');
315
4
16
        $self->add_lex_rule('declaration', 'property _COLON S* expr prio? | ');
316
4
15
        $self->add_lex_rule('prio', 'IMPORTANT_SYM S*');
317
318
319        ##expr
320        # : term [ operator term ]*
321        # ;
322        #term
323        # : unary_operator?
324        # [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* |
325        # TIME S* | FREQ S* ]
326        # | STRING S* | IDENT S* | URI S* | hexcolor | function
327        # ;
328        #function
329        # : FUNCTION S* expr ')' S*
330        # ;
331        #hexcolor
332        # : HASH S*
333        # ;
334
335
4
16
        $self->add_lex_rule('expr', 'term [ operator term ]*');
336
4
18
        $self->add_lex_rule('term', 'unary_operator? [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* | TIME S* | FREQ S* ] '.
337                '| STRING S* | IDENT S* | URI S* | hexcolor | function');
338
4
16
        $self->add_lex_rule('function', 'FUNCTION S* expr _ROUND_CLOSE S*');
339
4
16
        $self->add_lex_rule('hexcolor', 'HASH S*');
340
341
342        #####################################################################################
343
344
4
33
        $self->set_base('stylesheet');
345}
346
347sub toke {
348
3
19
        my ($self, $input) = @_;
349
350        # strip comments first
351
3
9
        $input =~ s!/\*[^*]*\*+([^/*][^*]*\*+)*/! !g;
352
353
3
27
        $self->SUPER::toke($input);
354}
355
3561;