in lib/parser.dart [463:741]
Directive? processDirective() {
var start = _peekToken.span;
var tokId = processVariableOrDirective();
if (tokId is VarDefinitionDirective) return tokId;
final tokenId = tokId as int;
switch (tokenId) {
case TokenKind.DIRECTIVE_IMPORT:
_next();
// @import "uri_string" or @import url("uri_string") are identical; only
// a url can follow an @import.
String? importStr;
if (_peekIdentifier()) {
var func = processFunction(identifier());
if (func is UriTerm) {
importStr = func.text;
}
} else {
importStr = processQuotedString(false);
}
// Any medias?
var medias = processMediaQueryList();
if (importStr == null) {
_error('missing import string', _peekToken.span);
}
return ImportDirective(importStr!.trim(), medias, _makeSpan(start));
case TokenKind.DIRECTIVE_MEDIA:
_next();
// Any medias?
var media = processMediaQueryList();
var rules = <TreeNode>[];
if (_maybeEat(TokenKind.LBRACE)) {
while (!_maybeEat(TokenKind.END_OF_FILE)) {
final rule = processRule();
if (rule == null) break;
rules.add(rule);
}
if (!_maybeEat(TokenKind.RBRACE)) {
_error('expected } after ruleset for @media', _peekToken.span);
}
} else {
_error('expected { after media before ruleset', _peekToken.span);
}
return MediaDirective(media, rules, _makeSpan(start));
case TokenKind.DIRECTIVE_HOST:
_next();
var rules = <TreeNode>[];
if (_maybeEat(TokenKind.LBRACE)) {
while (!_maybeEat(TokenKind.END_OF_FILE)) {
final rule = processRule();
if (rule == null) break;
rules.add(rule);
}
if (!_maybeEat(TokenKind.RBRACE)) {
_error('expected } after ruleset for @host', _peekToken.span);
}
} else {
_error('expected { after host before ruleset', _peekToken.span);
}
return HostDirective(rules, _makeSpan(start));
case TokenKind.DIRECTIVE_PAGE:
// @page S* IDENT? pseudo_page?
// S* '{' S*
// [ declaration | margin ]?
// [ ';' S* [ declaration | margin ]? ]* '}' S*
//
// pseudo_page :
// ':' [ "left" | "right" | "first" ]
//
// margin :
// margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S*
//
// margin_sym : @top-left-corner, @top-left, @bottom-left, etc.
//
// See http://www.w3.org/TR/css3-page/#CSS21
_next();
// Page name
Identifier? name;
if (_peekIdentifier()) {
name = identifier();
}
// Any pseudo page?
Identifier? pseudoPage;
if (_maybeEat(TokenKind.COLON)) {
if (_peekIdentifier()) {
pseudoPage = identifier();
// TODO(terry): Normalize pseudoPage to lowercase.
if (isChecked &&
!(pseudoPage.name == 'left' ||
pseudoPage.name == 'right' ||
pseudoPage.name == 'first')) {
_warning(
'Pseudo page must be left, top or first', pseudoPage.span);
return null;
}
}
}
var pseudoName = pseudoPage is Identifier ? pseudoPage.name : '';
var ident = name is Identifier ? name.name : '';
return PageDirective(
ident, pseudoName, processMarginsDeclarations(), _makeSpan(start));
case TokenKind.DIRECTIVE_CHARSET:
// @charset S* STRING S* ';'
_next();
var charEncoding = processQuotedString(false);
return CharsetDirective(charEncoding, _makeSpan(start));
// TODO(terry): Workaround Dart2js bug continue not implemented in switch
// see https://code.google.com/p/dart/issues/detail?id=8270
/*
case TokenKind.DIRECTIVE_MS_KEYFRAMES:
// TODO(terry): For now only IE 10 (are base level) supports @keyframes,
// -moz- has only been optional since Oct 2012 release of Firefox, not
// all versions of webkit support @keyframes and opera doesn't yet
// support w/o -o- prefix. Add more warnings for other prefixes when
// they become optional.
if (isChecked) {
_warning('@-ms-keyframes should be @keyframes', _makeSpan(start));
}
continue keyframeDirective;
keyframeDirective:
*/
case TokenKind.DIRECTIVE_KEYFRAMES:
case TokenKind.DIRECTIVE_WEB_KIT_KEYFRAMES:
case TokenKind.DIRECTIVE_MOZ_KEYFRAMES:
case TokenKind.DIRECTIVE_O_KEYFRAMES:
// TODO(terry): Remove workaround when bug 8270 is fixed.
case TokenKind.DIRECTIVE_MS_KEYFRAMES:
if (tokenId == TokenKind.DIRECTIVE_MS_KEYFRAMES && isChecked) {
_warning('@-ms-keyframes should be @keyframes', _makeSpan(start));
}
// TODO(terry): End of workaround.
// Key frames grammar:
//
// @[browser]? keyframes [IDENT|STRING] '{' keyframes-blocks '}';
//
// browser: [-webkit-, -moz-, -ms-, -o-]
//
// keyframes-blocks:
// [keyframe-selectors '{' declarations '}']* ;
//
// keyframe-selectors:
// ['from'|'to'|PERCENTAGE] [',' ['from'|'to'|PERCENTAGE] ]* ;
_next();
Identifier? name;
if (_peekIdentifier()) {
name = identifier();
}
_eat(TokenKind.LBRACE);
var keyframe = KeyFrameDirective(tokenId, name, _makeSpan(start));
do {
var selectors = Expressions(_makeSpan(start));
do {
var term = processTerm() as Expression;
// TODO(terry): Only allow from, to and PERCENTAGE ...
selectors.add(term);
} while (_maybeEat(TokenKind.COMMA));
keyframe.add(KeyFrameBlock(
selectors, processDeclarations(), _makeSpan(start)));
} while (!_maybeEat(TokenKind.RBRACE) && !isPrematureEndOfFile());
return keyframe;
case TokenKind.DIRECTIVE_FONTFACE:
_next();
return FontFaceDirective(processDeclarations(), _makeSpan(start));
case TokenKind.DIRECTIVE_STYLET:
// Stylet grammar:
//
// @stylet IDENT '{'
// ruleset
// '}'
_next();
dynamic name;
if (_peekIdentifier()) {
name = identifier();
}
_eat(TokenKind.LBRACE);
var productions = <TreeNode>[];
start = _peekToken.span;
while (!_maybeEat(TokenKind.END_OF_FILE)) {
final rule = processRule();
if (rule == null) {
break;
}
productions.add(rule);
}
_eat(TokenKind.RBRACE);
return StyletDirective(name as String, productions, _makeSpan(start));
case TokenKind.DIRECTIVE_NAMESPACE:
// Namespace grammar:
//
// @namespace S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
// namespace_prefix : IDENT
_next();
Identifier? prefix;
if (_peekIdentifier()) {
prefix = identifier();
}
// The namespace URI can be either a quoted string url("uri_string")
// are identical.
String? namespaceUri;
if (_peekIdentifier()) {
var func = processFunction(identifier());
if (func is UriTerm) {
namespaceUri = func.text;
}
} else {
if (prefix != null && prefix.name == 'url') {
var func = processFunction(prefix);
if (func is UriTerm) {
// @namespace url("");
namespaceUri = func.text;
prefix = null;
}
} else {
namespaceUri = processQuotedString(false);
}
}
return NamespaceDirective(
prefix?.name ?? '', namespaceUri, _makeSpan(start));
case TokenKind.DIRECTIVE_MIXIN:
return processMixin();
case TokenKind.DIRECTIVE_INCLUDE:
return processInclude(_makeSpan(start));
case TokenKind.DIRECTIVE_CONTENT:
// TODO(terry): TBD
_warning('@content not implemented.', _makeSpan(start));
return null;
case TokenKind.DIRECTIVE_MOZ_DOCUMENT:
return processDocumentDirective();
case TokenKind.DIRECTIVE_SUPPORTS:
return processSupportsDirective();
case TokenKind.DIRECTIVE_VIEWPORT:
case TokenKind.DIRECTIVE_MS_VIEWPORT:
return processViewportDirective();
}
return null;
}