부록 G. CSS 2.2의 문법

목차

이 부록은 표준 문서가 아닙니다.

아래의 문법은 CSS 2.2의 구문을 정의합니다. 그러나 이 문법은 어떤 면에서 CSS 2.2의 상위 집합이며, 이 명세는 이 문법에 표현되지 않은 추가적인 의미적 제약을 부과합니다. 준수하는 UA는 또한 전방 호환 구문 규칙, 선택자 표기법, 속성 및 값 표기법, 그리고 단위 표기법을 준수해야 합니다. 그러나 모든 구문적으로 올바른 CSS가 적용되는 것은 아니며, 문서 언어가 CSS에 없는 제한을 부과할 수 있습니다. 예를 들어, HTML은 "class" 속성의 가능한 값에 제한을 부과합니다.

G.1 문법

아래의 문법은 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*
  ;

G.2 어휘 스캐너

아래는 토크나이저입니다. 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;}

G.3 CSS 2.2와 CSS1의 토큰화 비교

CSS1 권장안([CSS1])에 지정된 구문과 위 구문에는 몇 가지 차이점이 있습니다. 대부분은 CSS2에서 새로 추가된 토큰 때문이며, 일부는 문법이 더 읽기 쉽게 재작성되었기 때문입니다. 그러나 CSS1 구문에서 오류로 간주된 몇 가지 비호환 변경 사항도 있습니다. 이는 아래에 설명되어 있습니다.

G.4 구현 노트

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;