| File: | blib/lib/CSS/Grammar/CSS30.pm |
| Coverage: | 100.0% |
| line | stmt | bran | cond | sub | time | code |
|---|---|---|---|---|---|---|
| 1 | package 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 | ||||||
| 12 | sub 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 | ||||||
| 314 | sub 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 | ||||||
| 323 | 1; | |||||
| 324 | ||||||