xref: /plugin/combo/vendor/carica/phpcss/src/PhpCss/Parser/Sequence.php (revision 04fd306c7c155fa133ebb3669986875d65988276)
1*04fd306cSNickeau<?php
2*04fd306cSNickeau/**
3*04fd306cSNickeau * The Sequence parser parses a list of simple selector tokens into the AST.
4*04fd306cSNickeau *
5*04fd306cSNickeau * @license http://www.opensource.org/licenses/mit-license.php The MIT License
6*04fd306cSNickeau * @copyright Copyright 2010-2012 PhpCss Team
7*04fd306cSNickeau */
8*04fd306cSNickeau
9*04fd306cSNickeaunamespace PhpCss\Parser {
10*04fd306cSNickeau
11*04fd306cSNickeau  use PhpCss;
12*04fd306cSNickeau  use PhpCss\Ast;
13*04fd306cSNickeau  use PhpCss\Scanner;
14*04fd306cSNickeau
15*04fd306cSNickeau  /**
16*04fd306cSNickeau   * The Sequence parser parses a list of simple selector tokens into the AST.
17*04fd306cSNickeau   *
18*04fd306cSNickeau   * It delegates to separate parsers for pseudo classes and attributes.
19*04fd306cSNickeau   *
20*04fd306cSNickeau   * A css combinator delegates to a new instance of this class.
21*04fd306cSNickeau   */
22*04fd306cSNickeau  class Sequence extends PhpCss\Parser {
23*04fd306cSNickeau
24*04fd306cSNickeau    /**
25*04fd306cSNickeau     * Parse the token stream for a simple selector sequence,
26*04fd306cSNickeau     * after the first element the type selector is not allowed any more,
27*04fd306cSNickeau     * but a combinator is possible.
28*04fd306cSNickeau     *
29*04fd306cSNickeau     * @throws PhpCss\Exception\ParserException
30*04fd306cSNickeau     */
31*04fd306cSNickeau    public function parse(): Ast\Node {
32*04fd306cSNickeau      $sequence = new Ast\Selector\Sequence();
33*04fd306cSNickeau      $token = $this->lookahead(
34*04fd306cSNickeau        [
35*04fd306cSNickeau          Scanner\Token::IDENTIFIER,
36*04fd306cSNickeau          Scanner\Token::ID_SELECTOR,
37*04fd306cSNickeau          Scanner\Token::CLASS_SELECTOR,
38*04fd306cSNickeau          Scanner\Token::PSEUDO_CLASS,
39*04fd306cSNickeau          Scanner\Token::PSEUDO_ELEMENT,
40*04fd306cSNickeau          Scanner\Token::ATTRIBUTE_SELECTOR_START,
41*04fd306cSNickeau          Scanner\Token::COMBINATOR,
42*04fd306cSNickeau        ]
43*04fd306cSNickeau      );
44*04fd306cSNickeau      while (isset($token)) {
45*04fd306cSNickeau        if ($selector = $this->createSelector($token)) {
46*04fd306cSNickeau          $this->read($token->type);
47*04fd306cSNickeau          $sequence->simples[] = $selector;
48*04fd306cSNickeau        }
49*04fd306cSNickeau        switch ($token->type) {
50*04fd306cSNickeau        case Scanner\Token::SEPARATOR :
51*04fd306cSNickeau          $this->read(Scanner\Token::SEPARATOR);
52*04fd306cSNickeau          return $sequence;
53*04fd306cSNickeau        case Scanner\Token::PSEUDO_CLASS :
54*04fd306cSNickeau          $sequence->simples[] = $this->delegate(PseudoClass::CLASS);
55*04fd306cSNickeau          break;
56*04fd306cSNickeau        case Scanner\Token::PSEUDO_ELEMENT :
57*04fd306cSNickeau          $sequence->simples[] = $this->createPseudoElement($token);
58*04fd306cSNickeau          $this->read($token->type);
59*04fd306cSNickeau          break;
60*04fd306cSNickeau        case Scanner\Token::ATTRIBUTE_SELECTOR_START :
61*04fd306cSNickeau          $this->read($token->type);
62*04fd306cSNickeau          $sequence->simples[] = $this->delegate(Attribute::CLASS);
63*04fd306cSNickeau          break;
64*04fd306cSNickeau        case Scanner\Token::COMBINATOR :
65*04fd306cSNickeau        case Scanner\Token::WHITESPACE :
66*04fd306cSNickeau          $this->read($token->type);
67*04fd306cSNickeau          $subSequence = $this->delegate(get_class($this));
68*04fd306cSNickeau          /**
69*04fd306cSNickeau           * @var Ast\Selector\Sequence $subSequence
70*04fd306cSNickeau           */
71*04fd306cSNickeau          $sequence->combinator = $this->createCombinator(
72*04fd306cSNickeau            $token, $subSequence
73*04fd306cSNickeau          );
74*04fd306cSNickeau          return $sequence;
75*04fd306cSNickeau        }
76*04fd306cSNickeau        if ($this->endOfTokens()) {
77*04fd306cSNickeau          $token = NULL;
78*04fd306cSNickeau          continue;
79*04fd306cSNickeau        }
80*04fd306cSNickeau        $token = $this->lookahead(
81*04fd306cSNickeau          [
82*04fd306cSNickeau            Scanner\Token::ID_SELECTOR,
83*04fd306cSNickeau            Scanner\Token::CLASS_SELECTOR,
84*04fd306cSNickeau            Scanner\Token::PSEUDO_CLASS,
85*04fd306cSNickeau            Scanner\Token::PSEUDO_ELEMENT,
86*04fd306cSNickeau            Scanner\Token::ATTRIBUTE_SELECTOR_START,
87*04fd306cSNickeau            Scanner\Token::COMBINATOR,
88*04fd306cSNickeau            Scanner\Token::WHITESPACE,
89*04fd306cSNickeau            Scanner\Token::SEPARATOR,
90*04fd306cSNickeau          ]
91*04fd306cSNickeau        );
92*04fd306cSNickeau      }
93*04fd306cSNickeau      return $sequence;
94*04fd306cSNickeau    }
95*04fd306cSNickeau
96*04fd306cSNickeau    private function createSelector(Scanner\Token $token) {
97*04fd306cSNickeau      switch ($token->type) {
98*04fd306cSNickeau      case Scanner\Token::IDENTIFIER :
99*04fd306cSNickeau        if (FALSE !== strpos($token->content, '|')) {
100*04fd306cSNickeau          [$prefix, $name] = explode('|', $token->content);
101*04fd306cSNickeau        } else {
102*04fd306cSNickeau          $prefix = '';
103*04fd306cSNickeau          $name = $token->content;
104*04fd306cSNickeau        }
105*04fd306cSNickeau        if ($name === '*') {
106*04fd306cSNickeau          return new Ast\Selector\Simple\Universal($prefix);
107*04fd306cSNickeau        }
108*04fd306cSNickeau        return new Ast\Selector\Simple\Type($name, $prefix);
109*04fd306cSNickeau      case Scanner\Token::ID_SELECTOR :
110*04fd306cSNickeau        return new Ast\Selector\Simple\Id(substr($token->content, 1));
111*04fd306cSNickeau      case Scanner\Token::CLASS_SELECTOR :
112*04fd306cSNickeau        return new Ast\Selector\Simple\ClassName(substr($token->content, 1));
113*04fd306cSNickeau      }
114*04fd306cSNickeau      return NULL;
115*04fd306cSNickeau    }
116*04fd306cSNickeau
117*04fd306cSNickeau    private function createCombinator(
118*04fd306cSNickeau      Scanner\Token $token,
119*04fd306cSNickeau      Ast\Selector\Sequence $sequence
120*04fd306cSNickeau    ) {
121*04fd306cSNickeau      switch (trim($token->content)) {
122*04fd306cSNickeau      case '>' :
123*04fd306cSNickeau        return new Ast\Selector\Combinator\Child($sequence);
124*04fd306cSNickeau      case '+' :
125*04fd306cSNickeau        return new Ast\Selector\Combinator\Next($sequence);
126*04fd306cSNickeau      case '~' :
127*04fd306cSNickeau        return new Ast\Selector\Combinator\Follower($sequence);
128*04fd306cSNickeau      default :
129*04fd306cSNickeau        return new Ast\Selector\Combinator\Descendant($sequence);
130*04fd306cSNickeau      }
131*04fd306cSNickeau    }
132*04fd306cSNickeau
133*04fd306cSNickeau    /**
134*04fd306cSNickeau     * @throws PhpCss\Exception\UnknownPseudoElementException
135*04fd306cSNickeau     */
136*04fd306cSNickeau    private function createPseudoElement($token): Ast\Selector\Simple\PseudoElement {
137*04fd306cSNickeau      $name = substr($token->content, 2);
138*04fd306cSNickeau      switch ($name) {
139*04fd306cSNickeau      case 'first-line' :
140*04fd306cSNickeau      case 'first-letter' :
141*04fd306cSNickeau      case 'after' :
142*04fd306cSNickeau      case 'before' :
143*04fd306cSNickeau        return new Ast\Selector\Simple\PseudoElement($name);
144*04fd306cSNickeau      }
145*04fd306cSNickeau      throw new PhpCss\Exception\UnknownPseudoElementException($token);
146*04fd306cSNickeau    }
147*04fd306cSNickeau  }
148*04fd306cSNickeau}
149