本附录是非规范性的。
下面的语法定义了 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;