protected function parseSimpleSelector($stream)
{
$peek = $stream->peek();
if ($peek != '*' && !$peek->isType('Symbol')) {
$element = $namespace = '*';
} else {
$next = $stream->next();
if ($next != '*' && !$next->isType('Symbol')) {
throw new SyntaxError(sprintf("Expected symbol, got '%s'", $next));
}
if ($stream->peek() == '|') {
$namespace = $next;
$stream->next();
$element = $stream->next();
if ($element != '*' && !$next->isType('Symbol')) {
throw new SyntaxError(sprintf("Expected symbol, got '%s'", $next));
}
} else {
$namespace = '*';
$element = $next;
}
}
$result = new Node\ElementNode($namespace, $element);
$has_hash = false;
while (1) {
$peek = $stream->peek();
if ($peek == '#') {
if ($has_hash) {
/* You can't have two hashes
(FIXME: is there some more general rule I'm missing?) */
// @codeCoverageIgnoreStart
break;
// @codeCoverageIgnoreEnd
}
$stream->next();
$result = new Node\HashNode($result, $stream->next());
$has_hash = true;
continue;
} elseif ($peek == '.') {
$stream->next();
$result = new Node\ClassNode($result, $stream->next());
continue;
} elseif ($peek == '[') {
$stream->next();
$result = $this->parseAttrib($result, $stream);
$next = $stream->next();
if ($next != ']') {
throw new SyntaxError(sprintf("] expected, got '%s'", $next));
}
continue;
} elseif ($peek == ':' || $peek == '::') {
$type = $stream->next();
$ident = $stream->next();
if (!$ident || !$ident->isType('Symbol')) {
throw new SyntaxError(sprintf("Expected symbol, got '%s'", $ident));
}
if ($stream->peek() == '(') {
$stream->next();
$peek = $stream->peek();
if ($peek->isType('String')) {
$selector = $stream->next();
} elseif ($peek->isType('Symbol') && is_int($peek)) {
$selector = intval($stream->next());
} else {
// FIXME: parseSimpleSelector, or selector, or...?
$selector = $this->parseSimpleSelector($stream);
}
$next = $stream->next();
if ($next != ')') {
throw new SyntaxError(sprintf("Expected ')', got '%s' and '%s'", $next, $selector));
}
$result = new Node\FunctionNode($result, $type, $ident, $selector);
} else {
$result = new Node\PseudoNode($result, $type, $ident);
}
continue;
} else {
if ($peek == ' ') {
$stream->next();
}
break;
}
// FIXME: not sure what "negation" is
}
return $result;
}