本附录是非规范性的。
下面的语法定义了 CSS 2.2 的语法。然而,从某种意义上说,它是 CSS 2.2 的超集,因为本规范还施加了一些未在此语法中表达的语义约束。一个符合标准的 UA 还必须遵循 前向兼容的解析规则、选择器表示法、属性和值的表示法以及单位表示法。然而,并非所有语法上正确的 CSS 都能生效,因为文档语言可能会施加 CSS 中未规定的限制,例如 HTML 对 "class" 属性的可能值施加了限制。
下面的语法是 LALR(1) 的(但请注意,大多数 UA 不应直接使用它,因为它并不表达 解析约定,仅表达 CSS 2.2 的语法)。生成规则的格式经过优化,便于人类阅读,并使用了一些超出 Yacc(参见 [YACC])的简写表示法:
生成规则如下:
stylesheet : [ CHARSET_SYM STRING ';' ]? [S|CDO|CDC]* [ import [ CDO S* | CDC S* ]* ]* [ [ ruleset | media | page ] [ CDO S* | CDC S* ]* ]* ; import : IMPORT_SYM S* [STRING|URI] S* media_list? ';' S* ; media : MEDIA_SYM S* media_list '{' S* ruleset* '}' S* ; media_list : medium [ COMMA S* medium]* ; medium : IDENT S* ; page : PAGE_SYM S* pseudo_page? '{' S* declaration? [ ';' S* declaration? ]* '}' S* ; pseudo_page : ':' IDENT S* ; operator : '/' S* | ',' S* ; combinator : '+' S* | '>' S* ; property : IDENT S* ; ruleset : selector [ ',' S* selector ]* '{' S* declaration? [ ';' S* declaration? ]* '}' S* ; 选择器 : simple_selector [ combinator selector | S+ [ combinator? selector ]? ]? ; simple_selector : element_name [ HASH | class | attrib | pseudo ]* | [ HASH | class | attrib | pseudo ]+ ; class : '.' IDENT ; element_name : IDENT | '*' ; attrib : '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S* [ IDENT | STRING ] S* ]? ']' ; pseudo : ':' [ IDENT | FUNCTION S* [IDENT S*]? ')' ] ; declaration : property ':' S* expr prio? ; prio : IMPORTANT_SYM S* ; expr : term [ operator? term ]* ; term : [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* | TIME S* | FREQ S* ] | STRING S* | IDENT S* | URI S* | hexcolor | function ; function : FUNCTION S* expr ')' S* ; /* * 这里对 颜色 有一个约束,即它必须 * 在 "#" 之后有 3 或 6 个十六进制数字(即 [0-9a-fA-F]) * 例如,"#000" 是可以的,但 "#abcd" 不可以。 */ hexcolor : HASH S* ;
以下是词法分析器,以 Flex(参见 [FLEX])表示法编写。词法分析器不区分大小写。
"\377" 代表当前版本的 Flex 能处理的最高字符编号(十进制 255)。它应该读作 "\4177777"(十进制 1114111),这是 Unicode/ISO-10646 中的最高代码点。
%option case-insensitive h [0-9a-f] nonascii [\240-\377] unicode \\{h}{1,6}(\r\n|[ \t\r\n\f])? escape {unicode}|\\[^\r\n\f0-9a-f] nmstart [_a-z]|{nonascii}|{escape} nmchar [_a-z0-9-]|{nonascii}|{escape} string1 \"([^\n\r\f\\"]|\\{nl}|{escape})*\" string2 \'([^\n\r\f\\']|\\{nl}|{escape})*\' badstring1 \"([^\n\r\f\\"]|\\{nl}|{escape})*\\? badstring2 \'([^\n\r\f\\']|\\{nl}|{escape})*\\? badcomment1 \/\*[^*]*\*+([^/*][^*]*\*+)* badcomment2 \/\*[^*]*(\*+[^/*][^*]*)* baduri1 {U}{R}{L}\({w}([!#$%&*-\[\]-~]|{nonascii}|{escape})*{w} baduri2 {U}{R}{L}\({w}{string}{w} baduri3 {U}{R}{L}\({w}{badstring} comment \/\*[^*]*\*+([^/*][^*]*\*+)*\/ ident -?{nmstart}{nmchar}* name {nmchar}+ num [-+]?[0-9]+|[-+]?[0-9]*"."[0-9]+ string {string1}|{string2} badstring {badstring1}|{badstring2} badcomment {badcomment1}|{badcomment2} baduri {baduri1}|{baduri2}|{baduri3} url ([!#$%&*-~]|{nonascii}|{escape})* s [ \t\r\n\f]+ w {s}? nl \n|\r\n|\r|\f A a|\\0{0,4}(41|61)(\r\n|[ \t\r\n\f])? C c|\\0{0,4}(43|63)(\r\n|[ \t\r\n\f])? D d|\\0{0,4}(44|64)(\r\n|[ \t\r\n\f])? E e|\\0{0,4}(45|65)(\r\n|[ \t\r\n\f])? G g|\\0{0,4}(47|67)(\r\n|[ \t\r\n\f])?|\\g H h|\\0{0,4}(48|68)(\r\n|[ \t\r\n\f])?|\\h I i|\\0{0,4}(49|69)(\r\n|[ \t\r\n\f])?|\\i K k|\\0{0,4}(4b|6b)(\r\n|[ \t\r\n\f])?|\\k L l|\\0{0,4}(4c|6c)(\r\n|[ \t\r\n\f])?|\\l M m|\\0{0,4}(4d|6d)(\r\n|[ \t\r\n\f])?|\\m N n|\\0{0,4}(4e|6e)(\r\n|[ \t\r\n\f])?|\\n O o|\\0{0,4}(4f|6f)(\r\n|[ \t\r\n\f])?|\\o P p|\\0{0,4}(50|70)(\r\n|[ \t\r\n\f])?|\\p R r|\\0{0,4}(52|72)(\r\n|[ \t\r\n\f])?|\\r S s|\\0{0,4}(53|73)(\r\n|[ \t\r\n\f])?|\\s T t|\\0{0,4}(54|74)(\r\n|[ \t\r\n\f])?|\\t U u|\\0{0,4}(55|75)(\r\n|[ \t\r\n\f])?|\\u X x|\\0{0,4}(58|78)(\r\n|[ \t\r\n\f])?|\\x Z z|\\0{0,4}(5a|7a)(\r\n|[ \t\r\n\f])?|\\z %% {s} {return S;} \/\*[^*]*\*+([^/*][^*]*\*+)*\/ /* ignore comments */ {badcomment} /* unclosed comment at EOF */ "<!--" {return CDO;} "-->" {return CDC;} "~=" {return INCLUDES;} "|=" {return DASHMATCH;} {string} {return STRING;} {badstring} {return BAD_STRING;} {ident} {return IDENT;} "#"{name} {return HASH;} @{I}{M}{P}{O}{R}{T} {return IMPORT_SYM;} @{P}{A}{G}{E} {return PAGE_SYM;} @{M}{E}{D}{I}{A} {return MEDIA_SYM;} "@charset " {return CHARSET_SYM;} "!"({w}|{comment})*{I}{M}{P}{O}{R}{T}{A}{N}{T} {return IMPORTANT_SYM;} {num}{E}{M} {return EMS;} {num}{E}{X} {return EXS;} {num}{P}{X} {return LENGTH;} {num}{C}{M} {return LENGTH;} {num}{M}{M} {return LENGTH;} {num}{I}{N} {return LENGTH;} {num}{P}{T} {return LENGTH;} {num}{P}{C} {return LENGTH;} {num}{D}{E}{G} {return ANGLE;} {num}{R}{A}{D} {return ANGLE;} {num}{G}{R}{A}{D} {return ANGLE;} {num}{M}{S} {return TIME;} {num}{S} {return TIME;} {num}{H}{Z} {return FREQ;} {num}{K}{H}{Z} {return FREQ;} {num}{ident} {return DIMENSION;} {num}% {return PERCENTAGE;} {num} {return NUMBER;} {U}{R}{L}"("{w}{string}{w}")" {return URI;} {U}{R}{L}"("{w}{url}{w}")" {return URI;} {baduri} {return BAD_URI;} {ident}"(" {return FUNCTION;} . {return *yytext;}
CSS1 推荐规范中指定的语法([CSS1])与上述语法在某些方面存在差异。其中大多数是由于 CSS2 中出现的新标记,CSS1 中不存在。其他是因为语法已经过重写,以便更易读。然而,也有一些不兼容的变化,认为是 CSS1 语法中的错误。以下对这些差异进行了说明。
CSS 核心语法在 第 4.1.1 节 中的词法分析器可以实现为无备份的扫描器。在 Lex 表示法中,这需要添加以下模式(这些模式不改变返回的标记,只提高扫描器的效率):
{ident}/\\ return IDENT; #{name}/\\ return HASH; @{ident}/\\ return ATKEYWORD; #/\\ return DELIM; @/\\ return DELIM; @/- return DELIM; @/-\\ return DELIM; -/\\ return DELIM; -/- return DELIM; \</! return DELIM; \</!- return DELIM; {num}{ident}/\\ return DIMENSION; {num}/\\ return NUMBER; {num}/- return NUMBER; {num}/-\\ return NUMBER; [0-9]+/\. return NUMBER; u/\+ return IDENT; u\+[0-9a-f?]{1,6}/- return UNICODE_RANGE;