Appendice D : La grammaire de CSS2

Contents

Cet appendice est normatif.

La grammaire ci-dessous définit la syntaxe de CSS2. C'est, en quelque sorte, un sur-ensemble de CSS2, dans la mesure où cette spécification impose des contraintes sémantiques supplémentaires que n'exprime pas cette grammaire. Un agent utilisateur conforme doit aussi adhérer aux règles d'interprétation ascendantes, à la convention de notation des propriétés et valeurs et celle de la notation des unités. Le langage du document pouvant en plus imposer certaines restrictions (ex. en HTML, il existe des restrictions sur les valeurs admises par l'attribut "class").

D.1 La grammaire

La grammaire ci-dessous est LL(1) (mais noter que la plupart des agents utilisateurs ne devraient pas l'employer telle quelle car celle-ci n'exprime pas les conventions d'interprétation, mais seulement la syntaxe CSS2). Le format de ces productions est optimisé pour une consultation humaine, certaines notations raccourcies allant au-delà de Yacc (voir [YACC]) étant employées :

Voici les productions :

stylesheet
  : [ CHARSET_SYM S* STRING S* ';' ]?
    [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
    [ [ ruleset | media | page | font_face ] [S|CDO|CDC]* ]*
  ;
import
  : IMPORT_SYM S*
    [STRING|URI] S* [ medium [ ',' S* medium]* ]? ';' S*
  ;
media
  : MEDIA_SYM S* medium [ ',' S* medium ]* '{' S* ruleset* '}' S*
  ;
medium
  : IDENT S*
  ;
page
  : PAGE_SYM S* IDENT? pseudo_page? S*
    '{' S* declaration [ ';' S* declaration ]* '}' S*
  ;
pseudo_page
  : ':' IDENT
  ;
font_face
  : FONT_FACE_SYM S*
    '{' S* declaration [ ';' S* declaration ]* '}' S*
  ;
operator
  : '/' S* | ',' S* | /* vide */
  ;
combinator
  : '+' S* | '>' S* | /* vide */
  ;
unary_operator
  : '-' | '+'
  ;
property
  : IDENT S*
  ;
ruleset
  : selector [ ',' S* selector ]*
    '{' S* declaration [ ';' S* declaration ]* '}' S*
  ;
selector
  : simple_selector [ combinator simple_selector ]*
  ;
simple_selector
  : element_name? [ HASH | class | attrib | pseudo ]* S*
  ;
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?
  | /* vide */
  ;
prio
  : IMPORTANT_SYM S*
  ;
expr
  : term [ operator term ]*
  ;
term
  : unary_operator?
    [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* |
      TIME S* | FREQ S* | function ]
  | STRING S* | IDENT S* | URI S* | UNICODERANGE S* | hexcolor
  ;
function
  : FUNCTION S* expr ')' S*
  ;
/*
 * Il y a une contrainte sur la couleur,
 * celle-ci devant avoir 3 ou 6 chiffres hexadécimaux (ex. [0-9a-fA-F])
 * après le signe "#" ; ex. "#000" est OK, mais pas "#abcd".
 */
hexcolor
  : HASH S*
  ;

D.2 Le scanner lexical

Ce qui suit est l'atomiseur, écrit en notation Flex (voir [FLEX]) L'atomiseur est insensible à la casse.

Les deux expressions "\377" qui surviennent représentent le nombre du caractère le plus élevé que Flex peut gérer (en décimal : 255). Celles-ci devraient être interprétées comme étant "\4177777" (en décimal : 1114111), lequel est le code du point le plus élevé pour Unicode/ISO-10646.

%option case-insensitive

h		[0-9a-f]
nonascii	[\200-\377]
unicode		\\{h}{1,6}[ \t\r\n\f]?
escape		{unicode}|\\[ -~\200-\377]
nmstart		[a-z_]|{nonascii}|{escape}
nmchar		[a-z0-9-_]|{nonascii}|{escape}
string1		\"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\"
string2		\'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\'

ident		{nmstart}{nmchar}*
name		{nmchar}+
num		[0-9]+|[0-9]*"."[0-9]+
string		{string1}|{string2}
url		([!#$%&*-~]|{nonascii}|{escape})*
w		[ \t\r\n\f]*
nl		\n|\r\n|\r|\f
range		\?{1,6}|{h}(\?{0,5}|{h}(\?{0,4}|{h}(\?{0,3}|{h}(\?{0,2}|{h}(\??|{h})))))

%%

[ \t\r\n\f]+		{return S;}

\/\*[^*]*\*+([^/][^*]*\*+)*\/	/* ignorer les commentaires */

"<!--"			{return CDO;}
"-->"			{return CDC;}
"~="			{return INCLUDES;}
"|="			{return DASHMATCH;}

{string}		{return STRING;}

{ident}			{return IDENT;}

"#"{name}		{return HASH;}

"@import"		{return IMPORT_SYM;}
"@page"			{return PAGE_SYM;}
"@media"		{return MEDIA_SYM;}
"@font-face"		{return FONT_FACE_SYM;}
"@charset"		{return CHARSET_SYM;}

"!{w}important"		{return IMPORTANT_SYM;}

{num}em			{return EMS;}
{num}ex			{return EXS;}
{num}px			{return LENGTH;}
{num}cm			{return LENGTH;}
{num}mm			{return LENGTH;}
{num}in			{return LENGTH;}
{num}pt			{return LENGTH;}
{num}pc			{return LENGTH;}
{num}deg		{return ANGLE;}
{num}rad		{return ANGLE;}
{num}grad		{return ANGLE;}
{num}ms			{return TIME;}
{num}s			{return TIME;}
{num}Hz			{return FREQ;}
{num}kHz		{return FREQ;}
{num}{ident}		{return DIMEN;}
{num}%			{return PERCENTAGE;}
{num}			{return NUMBER;}

"url("{w}{string}{w}")"	{return URI;}
"url("{w}{url}{w}")"	{return URI;}
{ident}"("		{return FUNCTION;}

U\+{range}		{return UNICODERANGE;}
U\+{h}{1,6}-{h}{1,6}	{return UNICODERANGE;}

.			{return *yytext;}

D.3 La comparaison entre les atomisations de CSS2 et CSS1

Il existe quelques différences entre la syntaxe spécifiée dans la recommandation CSS1 ([CSS1]) et celle spécifiée plus haut. La plupart de ces différences étant dues aux nouveaux jetons de CSS2 qui n'existaient pas en CSS1. Et les autres étant dues au fait que la grammaire a été récrite pour une meilleure lisibilité. Cependant, il y a certains changements incompatibles, ceux-ci étaient considérés comme des erreurs de la syntaxe CSS1. Leur explication suit :