From e44e52bd691bc225ad224682be593e1d2c820f03 Mon Sep 17 00:00:00 2001 From: Raphael Schweikert Date: Sun, 4 Nov 2018 22:56:02 +0100 Subject: [PATCH] Begin selector parsing impl --- lib/Sabberworm/CSS/OutputFormat.php | 28 ++++++++- lib/Sabberworm/CSS/Property/Selector.php | 17 +++++- .../Selector/AdjacentSiblingCombinator.php | 9 +++ .../RuleSet/Selector/AttributeSelector.php | 1 + .../CSS/RuleSet/Selector/ChildCombinator.php | 9 +++ .../CSS/RuleSet/Selector/ClassSelector.php | 26 ++++++++ .../RuleSet/Selector/DescendantCombinator.php | 9 +++ .../Selector/GeneralSiblingCombinator.php | 9 +++ .../CSS/RuleSet/Selector/IDSelector.php | 26 ++++++++ .../RuleSet/Selector/NamespaceSelector.php | 1 + .../CSS/RuleSet/Selector/Negation.php | 18 ++++++ .../CSS/RuleSet/Selector/PseudoSelector.php | 40 +++++++++++++ .../CSS/RuleSet/Selector/SelectorPart.php | 59 +++++++++++++++++++ .../CSS/RuleSet/Selector/TypeSelector.php | 26 ++++++++ .../RuleSet/Selector/UniversalSelector.php | 11 ++++ 15 files changed, 285 insertions(+), 4 deletions(-) create mode 100644 lib/Sabberworm/CSS/RuleSet/Selector/AdjacentSiblingCombinator.php create mode 100644 lib/Sabberworm/CSS/RuleSet/Selector/AttributeSelector.php create mode 100644 lib/Sabberworm/CSS/RuleSet/Selector/ChildCombinator.php create mode 100644 lib/Sabberworm/CSS/RuleSet/Selector/ClassSelector.php create mode 100644 lib/Sabberworm/CSS/RuleSet/Selector/DescendantCombinator.php create mode 100644 lib/Sabberworm/CSS/RuleSet/Selector/GeneralSiblingCombinator.php create mode 100644 lib/Sabberworm/CSS/RuleSet/Selector/IDSelector.php create mode 100644 lib/Sabberworm/CSS/RuleSet/Selector/NamespaceSelector.php create mode 100644 lib/Sabberworm/CSS/RuleSet/Selector/Negation.php create mode 100644 lib/Sabberworm/CSS/RuleSet/Selector/PseudoSelector.php create mode 100644 lib/Sabberworm/CSS/RuleSet/Selector/SelectorPart.php create mode 100644 lib/Sabberworm/CSS/RuleSet/Selector/TypeSelector.php create mode 100644 lib/Sabberworm/CSS/RuleSet/Selector/UniversalSelector.php diff --git a/lib/Sabberworm/CSS/OutputFormat.php b/lib/Sabberworm/CSS/OutputFormat.php index 45b97f52..1866907a 100644 --- a/lib/Sabberworm/CSS/OutputFormat.php +++ b/lib/Sabberworm/CSS/OutputFormat.php @@ -38,6 +38,11 @@ class OutputFormat { // This is what’s printed before and after the comma if a declaration block contains multiple selectors. public $sSpaceBeforeSelectorSeparator = ''; public $sSpaceAfterSelectorSeparator = ' '; + + // This is what’s printed before and after combinators in selectors + public $sSpaceBeforeSelectorCombinator = ' '; + public $sSpaceAfterSelectorCombinator = ' '; + // This is what’s printed after the comma of value lists public $sSpaceBeforeListArgumentSeparator = ''; public $sSpaceAfterListArgumentSeparator = ''; @@ -147,11 +152,22 @@ public static function create() { } public static function createCompact() { - return self::create()->set('Space*Rules', "")->set('Space*Blocks', "")->setSpaceAfterRuleName('')->setSpaceBeforeOpeningBrace('')->setSpaceAfterSelectorSeparator(''); + return self::create() + ->set('Space*Rules', "") + ->set('Space*Blocks', "") + ->setSpaceAfterRuleName('') + ->setSpaceBeforeOpeningBrace('') + ->setSpaceAfterSelectorSeparator('') + ->setSpaceBeforeSelectorCombinator('') + ->setSpaceAfterSelectorCombinator(''); } public static function createPretty() { - return self::create()->set('Space*Rules', "\n")->set('Space*Blocks', "\n")->setSpaceBetweenBlocks("\n\n")->set('SpaceAfterListArgumentSeparator', array('default' => '', ',' => ' ')); + return self::create() + ->set('Space*Rules', "\n") + ->set('Space*Blocks', "\n") + ->setSpaceBetweenBlocks("\n\n") + ->set('SpaceAfterListArgumentSeparator', array('default' => '', ',' => ' ')); } } @@ -211,6 +227,14 @@ public function spaceAfterSelectorSeparator() { return $this->space('AfterSelectorSeparator'); } + public function spaceBeforeSelectorCombinator() { + return $this->space('AfterSelectorSeparator'); + } + + public function spaceAfterSelectorCombinator() { + return $this->space('AfterSelectorSeparator'); + } + public function spaceBeforeListArgumentSeparator($sSeparator) { return $this->space('BeforeListArgumentSeparator', $sSeparator); } diff --git a/lib/Sabberworm/CSS/Property/Selector.php b/lib/Sabberworm/CSS/Property/Selector.php index d84171f5..50085379 100644 --- a/lib/Sabberworm/CSS/Property/Selector.php +++ b/lib/Sabberworm/CSS/Property/Selector.php @@ -2,10 +2,13 @@ namespace Sabberworm\CSS\Property; +use Sabberworm\CSS\Renderable; +use Sabberworm\CSS\Parsing\ParserState; + /** * Class representing a single CSS selector. Selectors have to be split by the comma prior to being passed into this class. */ -class Selector { +class Selector implements Renderable { //Regexes for specificity calculations const NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX = '/ @@ -37,14 +40,20 @@ class Selector { private $sSelector; private $iSpecificity; + private $iLineNo; - public function __construct($sSelector, $bCalculateSpecificity = false) { + public function __construct($sSelector, $bCalculateSpecificity = false, $iLineNo = 0) { $this->setSelector($sSelector); + $this->iLineNo = $iLineNo; if ($bCalculateSpecificity) { $this->getSpecificity(); } } + public static function parse(ParserState $oParserState, $aExpectedEnd = array('{', ',')) { + + } + public function getSelector() { return $this->sSelector; } @@ -58,6 +67,10 @@ public function __toString() { return $this->getSelector(); } + public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) { + return $this->getSelector(); + } + public function getSpecificity() { if ($this->iSpecificity === null) { $a = 0; diff --git a/lib/Sabberworm/CSS/RuleSet/Selector/AdjacentSiblingCombinator.php b/lib/Sabberworm/CSS/RuleSet/Selector/AdjacentSiblingCombinator.php new file mode 100644 index 00000000..56a335bc --- /dev/null +++ b/lib/Sabberworm/CSS/RuleSet/Selector/AdjacentSiblingCombinator.php @@ -0,0 +1,9 @@ +spaceBeforeSelectorCombinator().'+'.$oOutputFormat->spaceAfterSelectorCombinator(); + } +} diff --git a/lib/Sabberworm/CSS/RuleSet/Selector/AttributeSelector.php b/lib/Sabberworm/CSS/RuleSet/Selector/AttributeSelector.php new file mode 100644 index 00000000..6d3f6659 --- /dev/null +++ b/lib/Sabberworm/CSS/RuleSet/Selector/AttributeSelector.php @@ -0,0 +1 @@ +WIP \ No newline at end of file diff --git a/lib/Sabberworm/CSS/RuleSet/Selector/ChildCombinator.php b/lib/Sabberworm/CSS/RuleSet/Selector/ChildCombinator.php new file mode 100644 index 00000000..a3cb4766 --- /dev/null +++ b/lib/Sabberworm/CSS/RuleSet/Selector/ChildCombinator.php @@ -0,0 +1,9 @@ +spaceBeforeSelectorCombinator().'>'.$oOutputFormat->spaceAfterSelectorCombinator(); + } +} diff --git a/lib/Sabberworm/CSS/RuleSet/Selector/ClassSelector.php b/lib/Sabberworm/CSS/RuleSet/Selector/ClassSelector.php new file mode 100644 index 00000000..64195342 --- /dev/null +++ b/lib/Sabberworm/CSS/RuleSet/Selector/ClassSelector.php @@ -0,0 +1,26 @@ +sClassName = $sClassName; + } + + public function getClassName() { + return $this->sClassName; + } + + public function setClassName($sClassName) { + $this->sClassName = $sClassName; + } + + public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) { + return '.' . $this->sClassName; + } +} diff --git a/lib/Sabberworm/CSS/RuleSet/Selector/DescendantCombinator.php b/lib/Sabberworm/CSS/RuleSet/Selector/DescendantCombinator.php new file mode 100644 index 00000000..a26a5ac2 --- /dev/null +++ b/lib/Sabberworm/CSS/RuleSet/Selector/DescendantCombinator.php @@ -0,0 +1,9 @@ +spaceBeforeSelectorCombinator().'~'.$oOutputFormat->spaceAfterSelectorCombinator(); + } +} diff --git a/lib/Sabberworm/CSS/RuleSet/Selector/IDSelector.php b/lib/Sabberworm/CSS/RuleSet/Selector/IDSelector.php new file mode 100644 index 00000000..aefb149c --- /dev/null +++ b/lib/Sabberworm/CSS/RuleSet/Selector/IDSelector.php @@ -0,0 +1,26 @@ +sId = $sId; + } + + public function getId() { + return $this->sId; + } + + public function setId($sId) { + $this->sId = $sId; + } + + public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) { + return '#' . $this->sId; + } +} diff --git a/lib/Sabberworm/CSS/RuleSet/Selector/NamespaceSelector.php b/lib/Sabberworm/CSS/RuleSet/Selector/NamespaceSelector.php new file mode 100644 index 00000000..6d3f6659 --- /dev/null +++ b/lib/Sabberworm/CSS/RuleSet/Selector/NamespaceSelector.php @@ -0,0 +1 @@ +WIP \ No newline at end of file diff --git a/lib/Sabberworm/CSS/RuleSet/Selector/Negation.php b/lib/Sabberworm/CSS/RuleSet/Selector/Negation.php new file mode 100644 index 00000000..3e093ca0 --- /dev/null +++ b/lib/Sabberworm/CSS/RuleSet/Selector/Negation.php @@ -0,0 +1,18 @@ +oNegated = $oNegated; + } + + public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) { + return ':not('.$this->oNegated->render($oOutputFormat).')'; + } +} diff --git a/lib/Sabberworm/CSS/RuleSet/Selector/PseudoSelector.php b/lib/Sabberworm/CSS/RuleSet/Selector/PseudoSelector.php new file mode 100644 index 00000000..d4518d05 --- /dev/null +++ b/lib/Sabberworm/CSS/RuleSet/Selector/PseudoSelector.php @@ -0,0 +1,40 @@ +mValue = $mValue; + $this->bIsPseudoElement = $bIsPseudoElement; + } + + public function getValue() { + return $this->mValue; + } + + public function setValue($mValue) { + $this->mValue = $mValue; + } + + public function isPseudoFunction() { + return $this->mValue instanceof CSSFunction; + } + + public function isPseudoElement() { + return $bIsPseudoElement; + } + + public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) { + $sValue = $this->mValue; + if($sValue instanceof Renderable) { + $sValue = $sValue->render($oOutputFormat); + } + return ':' . ($this->bIsPseudoElement ? ':' : '') . $sValue; + } +} diff --git a/lib/Sabberworm/CSS/RuleSet/Selector/SelectorPart.php b/lib/Sabberworm/CSS/RuleSet/Selector/SelectorPart.php new file mode 100644 index 00000000..d2632b2e --- /dev/null +++ b/lib/Sabberworm/CSS/RuleSet/Selector/SelectorPart.php @@ -0,0 +1,59 @@ +iLineNo = $iLineNo; + } + + public static function parse(ParserState $oParserState) { + if($oParserState->comes('*')) { + $oParserState->consume('*'); + return new UniversalSelector($oParserState->currentLine()); + } + if($oParserState->comes('.')) { + $oParserState->consume('.'); + return new ClassSelector($oParserState->parseIdentifier(false), $oParserState->currentLine()); + } + if($oParserState->comes('#')) { + $oParserState->consume('#'); + return new IDSelector($oParserState->parseIdentifier(false), $oParserState->currentLine()); + } + if($oParserState->comes(':')) { + $oParserState->consume(':'); + $bIsElement = $oParserState->comes(':'); + if($bIsElement) { + $oParserState->consume(':'); + } + return new PseudoSelector(Value::parseIdentifierOrFunction($oParserState), $bIsElement, $oParserState->currentLine()); + } + if($oParserState->comes('+')) { + $oParserState->consume('+'); + return new AdjacentSiblingCombinator($oParserState->currentLine()); + } + if($oParserState->comes('~')) { + $oParserState->consume('~'); + return new GeneralSiblingCombinator($oParserState->currentLine()); + } + if($oParserState->comes('>')) { + $oParserState->consume('>'); + return new ChildCombinator($oParserState->currentLine()); + } + } + + public function __toString() { + return $this->render(new OutputFormat()); + } + + public function getLineNo() { + return $this->iLineNo; + } +} \ No newline at end of file diff --git a/lib/Sabberworm/CSS/RuleSet/Selector/TypeSelector.php b/lib/Sabberworm/CSS/RuleSet/Selector/TypeSelector.php new file mode 100644 index 00000000..9ff8c9cb --- /dev/null +++ b/lib/Sabberworm/CSS/RuleSet/Selector/TypeSelector.php @@ -0,0 +1,26 @@ +sType = $sType; + } + + public function getType() { + return $this->sType; + } + + public function setType($sType) { + $this->sType = $sType; + } + + public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) { + return $this->sType; + } +} diff --git a/lib/Sabberworm/CSS/RuleSet/Selector/UniversalSelector.php b/lib/Sabberworm/CSS/RuleSet/Selector/UniversalSelector.php new file mode 100644 index 00000000..4d5e7af3 --- /dev/null +++ b/lib/Sabberworm/CSS/RuleSet/Selector/UniversalSelector.php @@ -0,0 +1,11 @@ +