이 부록은 표준 문서가 아닙니다.
아래의 문법은 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;