File Coverage

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

linestmtbrancondsubtimecode
1package CSS::Grammar::CSS30;
2
3
4
4
4
19
7
22
use strict;
4
4
4
4
24
8
20
use warnings;
5
6
4
4
4
26
7
23
use base 'CSS::Grammar';
7
8#
9# http://www.w3.org/TR/2003/WD-css3-syntax-20030813/
10#
11
12sub init {
13
4
11
        my ($self) = @_;
14
15
4
8
        my %rx;
16
17        #####################################################################################
18
19        # %option case-insensitive
20
21
4
13
        $self->{case_insensitive} = 1;
22
23        #####################################################################################
24
25        #nl \n|\r\n|\r|\f
26        #h [0-9a-f]
27        #nonascii [\200-\377]
28        #unicode \\{h}{1,6}[ \t\r\n\f]?
29        #escape {unicode}|\\[ -~\200-\377]
30        #nmstart [a-z]|{nonascii}|{escape}
31        #nmchar [a-z0-9-]|{nonascii}|{escape}
32        #string1 \"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\"
33        #string2 \'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\'
34
35
4
11
        $rx{nl} = '(\n|\r\n|\r|\f)';
36
4
12
        $rx{h} = '[0-9a-f]';
37
4
11
        $rx{nonascii} = '[\\x80-\\xff]';
38
4
20
        $rx{unicode} = '(\\'.$rx{h}.'{1,6}(\\r\\n|[ \\t\\r\\n\\f])?)';
39
4
17
        $rx{escape} = '('.$rx{unicode}.'|\\\\[ -~\\x80-\\xff])';
40
4
25
        $rx{nmstart} = '([a-z]|'.$rx{nonascii}.'|'.$rx{escape}.')';
41
4
22
        $rx{nmchar} = '([a-zA-Z0-9-]|'.$rx{nonascii}.'|'.$rx{escape}.')';
42
4
33
        $rx{string1} = '("([\\t !#$%&(-~]|\\\\('.$rx{nl}.')|\'|('.$rx{nonascii}.')|('.$rx{escape}.'))*")';
43
4
32
        $rx{string2} = '(\'([\\t !#$%&(-~]|\\\\('.$rx{nl}.')|"|('.$rx{nonascii}.')|('.$rx{escape}.'))*\')';
44
45
46        #ident [-]?{nmstart}{nmchar}*
47        #name {nmchar}+
48        #num [0-9]+|[0-9]*"."[0-9]+
49        #string {string1}|{string2}
50        #url ([!#$%&*-~]|{nonascii}|{escape})*
51        #w [ \t\r\n\f]*
52        #range \?{1,6}|{h}(\?{0,5}|{h}(\?{0,4}|{h}(\?{0,3}|{h}(\?{0,2}|{h}(\??|{h})))))
53
54
4
23
        $rx{ident} = "(-?$rx{nmstart}$rx{nmchar}*)";
55
4
19
        $rx{name} = "($rx{nmchar}+)";
56
4
10
        $rx{num} = '([0-9]+|[0-9]*\\.[0-9]+)';
57
4
26
        $rx{string} = "($rx{string1}|$rx{string2})";
58
4
24
        $rx{url} = "(([!#\$%&*-~]|$rx{nonascii}|$rx{escape})*)";
59
4
11
        $rx{w} = '[ \t\r\n\f]*';
60
4
48
        $rx{range} = "(\\?{1,6}|$rx{h}(\\?{0,5}|$rx{h}(\\?{0,4}|$rx{h}(\\?{0,3}|$rx{h}(\\?{0,2}|$rx{h}(\\??|$rx{h}))))))";
61
62
63        #####################################################################################
64
65        #[ \t\r\n\f]+ {return S;}
66        #"<!--" {return CDO;}
67        #"-->" {return CDC;}
68        #"~=" {return INCLUDES;}
69        #"|=" {return DASHMATCH;}
70
71        #{string} {return STRING;}
72        #{ident} {return IDENT;}
73        #"#"{name} {return HASH;}
74
75
4
21
        $self->add_toke_rule('S' , "[ \t\r\n\f]+");
76
4
16
        $self->add_toke_rule('CDO' , '<!--');
77
4
15
        $self->add_toke_rule('CDC' , '-->');
78
4
15
        $self->add_toke_rule('INCLUDES' , '~=');
79
4
14
        $self->add_toke_rule('DASHMATCH' , '\\|=');
80
81
4
17
        $self->add_toke_rule('STRING' , $rx{string});
82
4
17
        $self->add_toke_rule('IDENT' , $rx{ident});
83
4
22
        $self->add_toke_rule('HASH' , "#$rx{name}");
84
85
86        #"@import" {return IMPORT_SYM;}
87        #"@page" {return PAGE_SYM;}
88        #"@media" {return MEDIA_SYM;}
89        #"@font-face" {return FONT_FACE_SYM;}
90        #"@charset" {return CHARSET_SYM;}
91        #"@namespace" {return NAMESPACE_SYM;}
92
93        #"!{w}important" {return IMPORTANT_SYM;}
94
95
4
19
        $self->add_toke_rule('IMPORT_SYM' , '@import');
96
4
16
        $self->add_toke_rule('PAGE_SYM' , '@page');
97
4
16
        $self->add_toke_rule('MEDIA_SYM' , '@media');
98
4
13
        $self->add_toke_rule('FONT_FACE_SYM' , '@font-face');
99
4
48
        $self->add_toke_rule('CHARSET_SYM' , '@charset');
100
4
16
        $self->add_toke_rule('NAMESPACE_SYM' , '@namespace');
101
102
4
23
        $self->add_toke_rule('IMPORTANT_SYM' , "!$rx{w}important");
103
104
105        #{num}em {return EMS;}
106        #{num}ex {return EXS;}
107        #{num}px {return LENGTH;}
108        #{num}cm {return LENGTH;}
109        #{num}mm {return LENGTH;}
110        #{num}in {return LENGTH;}
111        #{num}pt {return LENGTH;}
112        #{num}pc {return LENGTH;}
113        #{num}deg {return ANGLE;}
114        #{num}rad {return ANGLE;}
115        #{num}grad {return ANGLE;}
116        #{num}ms {return TIME;}
117        #{num}s {return TIME;}
118        #{num}Hz {return FREQ;}
119        #{num}kHz {return FREQ;}
120        #{num}{ident} {return DIMEN;}
121        #{num}% {return PERCENTAGE;}
122        #{num} {return NUMBER;}
123
124
4
21
        $self->add_toke_rule('EMS' , "$rx{num}em");
125
4
24
        $self->add_toke_rule('EXS' , "$rx{num}ex");
126
4
20
        $self->add_toke_rule('LENGTH' , "$rx{num}(px|cm|mm|in|pt|pc)");
127
4
21
        $self->add_toke_rule('ANGLE' , "$rx{num}(deg|rad|grad)");
128
4
20
        $self->add_toke_rule('TIME' , "$rx{num}(ms|s)");
129
4
22
        $self->add_toke_rule('FREQ' , "$rx{num}(Hz|kHz)");
130
4
23
        $self->add_toke_rule('DIMEN' , "$rx{num}$rx{ident}");
131
4
22
        $self->add_toke_rule('PERCENTAGE' , "$rx{num}%");
132
4
21
        $self->add_toke_rule('NUMBER' , "$rx{num}");
133
134
135        #"url("{w}{string}{w}")" {return URI;}
136        #"url("{w}{url}{w}")" {return URI;}
137        #{ident}"(" {return FUNCTION;}
138
139        #U\+{range} {return UNICODERANGE;}
140        #U\+{h}{1,6}-{h}{1,6} {return UNICODERANGE;}
141
142
4
38
        $self->add_toke_rule('URI' , "url\\($rx{w}($rx{string}|$rx{url}$rx{w})\\)");
143
4
22
        $self->add_toke_rule('FUNCTION' , "$rx{ident}\\(");
144
145
4
31
        $self->add_toke_rule('UNICODERANGE' , "U\\+($rx{range}|${rx{h}}{1,6}-${rx{h}}{1,6})");
146
147
148        #####################################################################################
149
150
4
15
        $self->add_toke_rule('_SEMICOLON' , ';');
151
4
15
        $self->add_toke_rule('_COMMA' , ',');
152
4
13
        $self->add_toke_rule('_BRACE_OPEN' , '{');
153
4
13
        $self->add_toke_rule('_BRACE_CLOSE' , '}');
154
4
15
        $self->add_toke_rule('_COLON' , ':');
155
4
15
        $self->add_toke_rule('_SLASH' , '/');
156
4
16
        $self->add_toke_rule('_PLUS' , '\\+');
157
4
15
        $self->add_toke_rule('_GREATER_THAN' , '>');
158
4
18
        $self->add_toke_rule('_MINUS' , '-');
159
4
15
        $self->add_toke_rule('_PERIOD' , '\\.');
160
4
14
        $self->add_toke_rule('_STAR' , '\\*');
161
4
15
        $self->add_toke_rule('_SQUARE_OPEN' , '\\[');
162
4
15
        $self->add_toke_rule('_EQUALS' , '=');
163
4
17
        $self->add_toke_rule('_SQUARE_CLOSE' , '\\]');
164
4
14
        $self->add_toke_rule('_ROUND_CLOSE' , '\\)');
165
166
167        #####################################################################################
168
169        #stylesheet
170        # : [ CHARSET_SYM S* STRING S* ';' ]?
171        # [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
172        # [ namespace [S|CDO|CDC]* ]*
173        # [ [ ruleset | media | page | font_face ] [S|CDO|CDC]* ]*
174        # ;
175        #import
176        # : IMPORT_SYM S*
177        # [STRING|URI] S* [ medium [ ',' S* medium]* ]? ';' S*
178        # ;
179        #namespace
180        # : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
181        # ;
182
183
4
25
        $self->add_lex_rule('stylesheet', '[ CHARSET_SYM S* STRING S* _SEMICOLON ]? [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*'.
184                ' [ namespace [S|CDO|CDC]* ]* [ [ ruleset | media | page | font_face ] [S|CDO|CDC]* ]*');
185
4
16
        $self->add_lex_rule('import', 'IMPORT_SYM S* [STRING|URI] S* [ medium [ _COMMA S* medium]* ]? _SEMICOLON S*');
186
4
17
        $self->add_lex_rule('namespace', 'NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* _SEMICOLON S*');
187
188
189        #namespace_prefix
190        # : IDENT
191        # ;
192        #media
193        # : MEDIA_SYM S* medium [ ',' S* medium ]* '{' S* ruleset* '}' S*
194        # ;
195        #medium
196        # : IDENT S*
197        # ;
198        #page
199        # : PAGE_SYM S* IDENT? pseudo_page? S*
200        # '{' S* declaration [ ';' S* declaration ]* '}' S*
201        # ;
202
203
4
16
        $self->add_lex_rule('namespace_prefix', 'IDENT');
204
4
17
        $self->add_lex_rule('media', 'MEDIA_SYM S* medium [ _COMMA S* medium ]* _BRACE_OPEN S* ruleset* _BRACE_CLOSE S*');
205
4
14
        $self->add_lex_rule('medium', 'IDENT S*');
206
4
16
        $self->add_lex_rule('page', 'PAGE_SYM S* IDENT? pseudo_page? S* _BRACE_OPEN S* declaration [ _SEMICOLON S* declaration ]* _BRACE_CLOSE S*');
207
208
209        #pseudo_page
210        # : ':' IDENT
211        # ;
212        #font_face
213        # : FONT_FACE_SYM S*
214        # '{' S* declaration [ ';' S* declaration ]* '}' S*
215        # ;
216        #operator
217        # : '/' S* | ',' S* | /* empty */
218        # ;
219        #combinator
220        # : '+' S* | '>' S* | /* empty */
221        # ;
222
223
4
16
        $self->add_lex_rule('pseudo_page', '_COLON IDENT');
224
4
17
        $self->add_lex_rule('font_face', 'FONT_FACE_SYM S* _BRACE_OPEN S* declaration [ _SEMICOLON S* declaration ]* _BRACE_CLOSE S*');
225
4
16
        $self->add_lex_rule('operator', '_SLASH S* | _COMMA S* | ');
226
4
17
        $self->add_lex_rule('combinator', '_PLUS S* | _GREATER_THAN S* | ');
227
228
229        #unary_operator
230        # : '-' | '+'
231        # ;
232        #property
233        # : IDENT S*
234        # ;
235        #ruleset
236        # : selector [ ',' S* selector ]*
237        # '{' S* declaration [ ';' S* declaration ]* '}' S*
238        # ;
239        #selector
240        # : simple_selector [ combinator simple_selector ]*
241        # ;
242
243
4
16
        $self->add_lex_rule('unary_operator', '_MINUS | _PLUS');
244
4
17
        $self->add_lex_rule('property', 'IDENT S*');
245
4
15
        $self->add_lex_rule('ruleset', 'selector [ _COMMA S* selector ]* _BRACE_OPEN S* declaration [ _SEMICOLON S* declaration ]* _BRACE_CLOSE S*');
246
4
17
        $self->add_lex_rule('selector', 'simple_selector [ combinator simple_selector ]*');
247
248
249        #simple_selector
250        # : element_name? [ HASH | class | attrib | pseudo ]* S*
251        # ;
252        #class
253        # : '.' IDENT
254        # ;
255        #element_name
256        # : IDENT | '*'
257        # ;
258        #attrib
259        # : '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*
260        # [ IDENT | STRING ] S* ]? ']'
261        # ;
262
263
4
16
        $self->add_lex_rule('simple_selector', 'element_name? [ HASH | class | attrib | pseudo ]* S*');
264
4
16
        $self->add_lex_rule('class', '_PERIOD IDENT');
265
4
18
        $self->add_lex_rule('element_name', 'IDENT | _STAR');
266
4
15
        $self->add_lex_rule('attrib', '_SQUARE_OPEN S* IDENT S* [ [ _EQUALS_EQUALS | INCLUDES | DASHMATCH ] S* [ IDENT | STRING ] S* ]? _SQUARE_CLOSE');
267
268
269        #pseudo
270        # : ':' [ IDENT | FUNCTION S* IDENT S* ')' ]
271        # ;
272        #declaration
273        # : property ':' S* expr prio?
274        # | /* empty */
275        # ;
276        #prio
277        # : IMPORTANT_SYM S*
278        # ;
279        #expr
280        # : term [ operator term ]*
281        # ;
282
283
4
14
        $self->add_lex_rule('pseudo', '_COLON [ IDENT | FUNCTION S* IDENT S* _ROUND_CLOSE ]');
284
4
14
        $self->add_lex_rule('declaration', 'property _COLON S* expr prio? | ');
285
4
17
        $self->add_lex_rule('prio', 'IMPORTANT_SYM S*');
286
4
18
        $self->add_lex_rule('expr', 'term [ operator term ]*');
287
288
289        #term
290        # : unary_operator?
291        # [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* |
292        # TIME S* | FREQ S* | function ]
293        # | STRING S* | IDENT S* | URI S* | UNICODERANGE S* | hexcolor
294        # ;
295        #function
296        # : FUNCTION S* expr ')' S*
297        # ;
298        #hexcolor
299        # : HASH S*
300        # ;
301
302
4
16
        $self->add_lex_rule('term', 'unary_operator? [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* | TIME S* | FREQ S* '.
303                '| function ] | STRING S* | IDENT S* | URI S* | UNICODERANGE S* | hexcolor');
304
4
17
        $self->add_lex_rule('function', 'FUNCTION S* expr _ROUND_CLOSE S*');
305
4
17
        $self->add_lex_rule('hexcolor', 'HASH S*');
306
307
308        #####################################################################################
309
310
4
28
        $self->set_base('stylesheet');
311}
312
313
314sub toke {
315
3
20
        my ($self, $input) = @_;
316
317        # strip comments first
318
3
8
        $input =~ s!/\*[^*]*\*+([^/*][^*]*\*+)*/!!g;
319
320
3
31
        $self->SUPER::toke($input);
321}
322
3231;
324