Poniższa gramatyka definiuje składnię CSS 2.1. W pewnym sensie jest to jednak nadzbiór CSS 2.1, ponieważ niniejsza specyfikacja nakłada pewne dodatkowe ograniczenia semantyczne, które nie zostały w tej gramatyce wyrażone. Zgodna aplikacja kliencka musi również spełniać zasady analizy składniowej z uwzględnieniem zgodności z przyszłymi wersjami oraz obsługiwać notację selektorów, notację własności i wartości oraz notację jednostek. Jednak nie każdy syntaktycznie poprawny kod CSS może zadziałać, ponieważ w języku dokumentu mogą istnieć ograniczenia, których nie ma w CSS. Na przykład w języku HTML są ograniczenia dotyczące wartości atrybutu "class".
G.1 Gramatyka
Jest to gramatyka typu LALR(1) (należy jednak zauważyć, że większość aplikacji klienckich nie powinna z niej korzystać bezpośrednio, ponieważ nie wyraża ona konwencji parsowania, a jedynie składnię CSS 2.1). Format produkcji został zoptymalizowany pod kątem użyteczności dla człowieka oraz zostały użyte pewne cechy skróconej notacji spoza Yacc (zobacz [YACC]).
- *: 0 lub więcej
- +: 1 lub więcej
- ?: 0 lub 1
- |: oddziela alternatywy
- [ ]: grupowanie
Produkcje:
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 LBRACE 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*
;
unary_operator
: '-' | '+'
;
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
: unary_operator?
[ 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*
;
/*
* There is a constraint on the color that it must
* have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
* after the "#"; e.g., "#000" is OK, but "#abcd" is not.
*/
hexcolor
: HASH S*
;
G.2 Skaner leksykalny
Poniżej znajduje się tokenizer napisany w notacji Flex (zobacz [FLEX]). Tokenizer ten rozróżnia małe i wielkie litery.
Wartość "377" reprezentuje najwyższą liczbę znaku obsługiwaną aktualnie przez Flex (255 w systemie dziesiętnym). Należy ją czytać jako "4177777" (1114111 w systemie dziesiętnym). Jest to najwyższa możliwa jednostka kodowa w standardzie Unicode/ISO-10646.
%option case-insensitive
h [0-9a-f]
nonascii [200-377]
unicode {h}{1,6}(rn|[ trnf])?
escape {unicode}|[^rnf0-9a-f]
nmstart [_a-z]|{nonascii}|{escape}
nmchar [_a-z0-9-]|{nonascii}|{escape}
string1 "([^nrf"]|{nl}|{escape})*"
string2 '([^nrf']|{nl}|{escape})*'
invalid1 "([^nrf"]|{nl}|{escape})*
invalid2 '([^nrf']|{nl}|{escape})*
comment /*[^*]**+([^/*][^*]**+)*/
ident -?{nmstart}{nmchar}*
name {nmchar}+
num [0-9]+|[0-9]*"."[0-9]+
string {string1}|{string2}
invalid {invalid1}|{invalid2}
url ([!#$%&*-~]|{nonascii}|{escape})*
s [ trnf]+
w {s}?
nl n|rn|r|f
A a|�{0,4}(41|61)(rn|[ trnf])?
C c|�{0,4}(43|63)(rn|[ trnf])?
D d|�{0,4}(44|64)(rn|[ trnf])?
E e|�{0,4}(45|65)(rn|[ trnf])?
G g|�{0,4}(47|67)(rn|[ trnf])?|g
H h|�{0,4}(48|68)(rn|[ trnf])?|h
I i|�{0,4}(49|69)(rn|[ trnf])?|i
K k|�{0,4}(4b|6b)(rn|[ trnf])?|k
L l|�{0,4}(4c|6c)(rn|[ trnf])?|l
M m|�{0,4}(4d|6d)(rn|[ trnf])?|m
N n|�{0,4}(4e|6e)(rn|[ trnf])?|n
O o|�{0,4}(4f|6f)(rn|[ trnf])?|o
P p|�{0,4}(50|70)(rn|[ trnf])?|p
R r|�{0,4}(52|72)(rn|[ trnf])?|r
S s|�{0,4}(53|73)(rn|[ trnf])?|s
T t|�{0,4}(54|74)(rn|[ trnf])?|t
U u|�{0,4}(55|75)(rn|[ trnf])?|u
X x|�{0,4}(58|78)(rn|[ trnf])?|x
Z z|�{0,4}(5a|7a)(rn|[ trnf])?|z
%%
{s} {return S;}
/*[^*]**+([^/*][^*]**+)*/ /* ignore comments */
"<!--" {return CDO;}
"-->" {return CDC;}
"~=" {return INCLUDES;}
"|=" {return DASHMATCH;}
{string} {return STRING;}
{invalid} {return INVALID; /* unclosed 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;}
{ident}"(" {return FUNCTION;}
. {return *yytext;}
G.3 Porównanie podziały na tokeny w CSS 2.1 i CSS 1
Składnia zdefiniowana w specyfikacji CSS 1 ([CSS1]) nieco różni się od przedstawionej powyżej. Większość tych różnic jest związana z wprowadzeniem W CSS 2 nowych tokenów, których nie ma w CSS 1. Niektóre różnice powstały z powodu przepisania gramatyki w celu uczynienia jej czytelniejszą. Są też pewne zmiany powodujące niezgodność, dotyczące części składni CSS 1, które zostały uznane za błędne. Ich opis znajduje się poniżej.
- Arkusze stylów CSS 1 można było kodować tylko za pomocą systemów kodowania, w których na jeden znak przypada jeden bajt, np.: ASCII i ISO-8859-1. W CSS 2.1 nie ma takiego ograniczenia. W praktyce ekstrapolacja tokenizera CSS 1 nie przysparzała dużych problemów i niektóre aplikacje klienckie zaakceptowały kodowania dwubajtowe.
- W CSS 1 za ukośnikiem wstecznym () mogły występować cztery cyfry szesnastkowe określające znaki Unicode. W CSS 2 może być ich sześć. Ponadto w CSS 2 biały znak może oddzielać sekwencję specjalną. Na przykład zgodnie z CSS 1 łańcuch "abcdef" zawiera trzy litery (abcd, e oraz f). W CSS 2 ten sam łańcuch ma tylko jedną (abcdef).
- Znak tabulatora (ASCII 9) był zabroniony w łańcuchach. Ponieważ jednak w CSS 1 łańcuchy służyły tylko jako nazwy fontów i adresy URL, jedyna sytuacja, w której może dojść do niezgodności między CSS 1 i CSS 2 to taka, gdy arkusz stylów zawiera nazwę rodziny fontów zawierającą tabulator.
- Analogicznie w łańcuchach w CSS 1 były zabronione znaki nowego wiersza (zapisane w postaci sekwencji specjalnej z ukośnikiem wstecznym).
- CSS 2 parsuje liczbę, po której znajduje się identyfikator jako token DIMENSION (tzn. nieznana jednostka). W CSS 1 liczby takie były parsowane jako liczba i identyfikator. Oznacza to, że w CSS 1 deklaracja ‚font: 10pt/1.2serif’ była poprawna, tak jak ‚font: 10pt/12pt serif’. W CSS 2 przed "serif" konieczna jest spacja. (Niektóre aplikacje klienckie akceptowały pierwszą wersję, ale nie drugą.)
- W CSS 1 nazwa klasy mogła zaczynać się od cyfry (".55ft"), jeśli nie była wymiarem (".55in"). W CSS 2 takie klasy są parsowane jako nieznane wymiary (dzięki temu w przyszłości możliwe będzie dodawanie jednostek). Aby ".55ft" było poprawną klasą w CSS 2, pierwsza cyfra musi zostać zapisana w postaci sekwencji specjalnej (".35 5ft").





Wysyłam...
Dodaj komentarz