이 부록은 표준 문서가 아닙니다.
아래의 문법은 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*
;
selector
: 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*
;
/*
* color에는 "#"
* 다음에 반드시 3 또는 6개의 16진수가 와야 한다는 제약이 있습니다(예: "#000"은 OK, 그러나 "#abcd"는 아님).
*/
hexcolor
: HASH S*
;
아래는 토크나이저입니다. Flex(참조 [FLEX]) 표기법으로 작성되었습니다. 이 토크나이저는 대소문자를 구분하지 않습니다.
"\377"은 현재 버전의 Flex가 처리할 수 있는 가장 높은 문자 번호(십진수 255)를 나타냅니다. 이것은 "\4177777"(십진수 1114111)로 읽어야 하며, 이는 유니코드/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;}
\/\*[^*]*\*+([^/*][^*]*\*+)*\/ /* 주석 무시 */
{badcomment} /* 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 구문에서 오류로 간주된 몇 가지 비호환 변경 사항도 있습니다. 이는 아래에 설명되어 있습니다.
4.1.1절의 CSS 핵심 구문에 대한 어휘 스캐너는 백업 없이 스캐너로 구현할 수 있습니다. 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;