D Die Grammatik von CSS2
Dieser Anhang ist normativ.
Die nachfolgende Grammatik definiert die Syntax von CSS1. Es handelt sich dabei jedoch in gewisser Hinsicht um eine Obermenge von CSS2, weil diese Spezifikation zusätzliche semantische Beschränkungen einführt, die nicht in dieser Grammatik ausgedrückt sind. Ein konformes Benutzerprogramm muss außerdem die Regeln zum vorwärtskompatiblen Parsing, die Eigenschaft/Wert-Notation und die Einheitennotation berücksichtigen. Darüber hinaus kann die Dokumentsprache Beschränkungen einführen, z.B. legt HTML Beschränkungen zu den möglichen Werten des „class“-Attributs fest.
D.1 Grammatik
Die nachfolgende Grammatik ist LL(1) (beachten Sie jedoch, dass die meisten Benutzerprogramme sie nicht direkt verwenden sollten, weil sie die Parsing-Konventionen nicht ausdrückt, sondern nur die CSS2-Syntax). Das Format der Produktionen ist für die menschliche Wahrnehmung optimiert, und es wird eine Art abkürzende Notation jenseits von Yacc (siehe [YACC]) verwendet:
- *: 0 oder mehrmals
- +: einmal oder mehrmals
- ?: 0 oder 1
- |: trennt Alternativen
- []: Gruppierung
Die Produktionen sind:
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* | /* leer */
;
combinator
: '+' S* | '>' S* | /* leer */
;
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?
| /* leer */
;
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* | RGB S* | UNICODERANGE S* | hexcolor
;
function
: FUNCTION S* expr ')' S*
;
/*
* Es gibt eine Beschränkung bezüglich der
* Farbe, nämlich, dass sie 3 oder
* 6 Hex-Ziffern (i.e., [0-9a-fA-F]) hinter
* dem"#" haben muss; e.g.,"#000" ist OK,
*"#abcd" nicht
*/
hexcolor
: HASH S*
;
D.2 Lexikalischer Scanner
Nachfolgend sehen Sie den Tokenizer, geschrieben in Flex-Notation (siehe [FLEX]). Der Tokenizer berücksichtigt keine Groß-/Kleinschreibung.
Die beiden Vorkommen von „\377“ stellen die höchste Zeichennummer dar, mit der aktuelle Versionen von Flex zurechtkommen (dezimal 255). Sie sollten als „\4177777“ (dezimal 1114111) gelesen werden, der höchstmögliche Codepunkt in 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;}
\/\*[^*]*\*+([^/][^*]*\*+)*\/ /* Kommentare ignorieren */
"<!--" {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;}
"@"{ident} {return ATKEYWORD;}
"!{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 Vergleich der Tokenbildung zwischen CSS1 und CSS2
Es gibt einige Unterschiede zwischen der Syntax, wie sie in der CSS1-Empfehlung ([CSS1]) angegeben ist, und der oben gezeigten. Diese Unterschiede sind größtenteils durch neue Token in CSS2 bedingt, die es in CSS1 nicht gab. Andere Unterschiede sind entstanden, weil die Grammatik der besseren Lesbarkeit umformuliert wurde. Es gibt jedoch einige inkompatible Ãnderungen, die in der CSS1-Syntax als Fehler erkannt wurden. Sie sind nachfolgend beschrieben.
- CSS1-Stylesheets konnten nur in 1-Byte/Zeichen-Codierungen formuliert werden wie beispielsweise ASCII und ISO-8859-1. CSS2 weist keine solche Beschränkung auf. In der Praxis war es nicht schwierig, den CSS1-Tokenizer zu extrapolieren, und einige User Agents haben auch 2-Byte-Codierungen akzeptiert.
- CSS1 erlaubte nur vier hexadezimale Ziffern hinter dem Backslash (\), um auf Unicode-Zeichen zu verweisen, CSS2 erlaubt sechs. Darüber hinaus erlaubt CSS2, dass die Escape-Folge durch ein Leerraumzeichen begrenzt wird. Gemäß CSS1 z.B. umfasst der String „\abcdef“ 3 Buchstaben, (\abcd, e und f), gemäß CSS2 nur einen (\abcdef).
- Das Tabulator-Zeichen (ASCII 9) war in Zeichenketten nicht erlaubt. Weil Zeichenketten in CSS1 jedoch nur für Schriftnamen und URLs verwendet wurden, war die einzige Möglichkeit, wie dies zu Inkompatibilität zwischen CSS1 und CSS2 führen konnte, dass ein Stylesheet eine Schriftfamilie enthielt, in deren Namen ein Tabulatorzeichen enthalten war.
- Auf ähnliche Weise waren in CSS1 auch keine Neuezeile-Zeichen (Escape mit Backslash) in Zeichenketten erlaubt.
- CSS2 parst eine Zahl unmittelbar gefolgt von einem Bezeichner als DIMEN-Token (d.h. eine unbekannte Einheit), CSS1 parst sie als Zahl und Bezeichner. Das bedeutet, in CSS1 war die Deklaration 'font: 10pt/1.2serif' korrekt, ebenso wie 'font: 10pt/12pt serif'; in CSS2 muss vor „serif“ ein Leerzeichen angegeben werden. (Einige Benutzerprogramme haben das erste Beispiel akzeptiert, nicht aber das zweite.)
- In CSS1 konnte ein Klassenname mit einer Ziffer beginnen („.55ft“), es sei denn, es handelte sich dabei um eine Größe („.55in“). In CSS2 werden solche Klassen als unbekannte Größen geparst (um zukünftige Ergänzungen um neue Einheiten zu unterstützen). Damit „.55ft“ eine gültige Klasse ist, muss in CSS2 die erste Ziffer als Escape gekennzeichnet werden („.\55ft“).