diff --git a/lib/Sabberworm/CSS/Parser.php b/lib/Sabberworm/CSS/Parser.php index b60ff61b2..ff563ddd0 100644 --- a/lib/Sabberworm/CSS/Parser.php +++ b/lib/Sabberworm/CSS/Parser.php @@ -555,7 +555,7 @@ private function parseURLValue() { } private function parseCalcValue() { - $aOperators = array('+', '-', '*', '/', '(', ')'); + $aOperators = array('+', '-', '*', '/'); $sFunction = trim($this->consumeUntil('(', false, true)); $oCalcList = new CalcRuleValueList($this->iLineNo); $oList = new RuleValueList(',', $this->iLineNo); @@ -563,26 +563,31 @@ private function parseCalcValue() { $iLastComponentType = NULL; while(!$this->comes(')') || $iNestingLevel > 0) { $this->consumeWhiteSpace(); - if (in_array($this->peek(), $aOperators)) { - if (($this->comes('-') || $this->comes('+'))) { - if ($this->peek(1, -1) != ' ' || !($this->comes('- ') || $this->comes('+ '))) { - throw new UnexpectedTokenException(" {$this->peek()} ", $this->peek(1, -1) . $this->peek(2), 'literal', $this->iLineNo); - } - } else if ($this->comes('(')) { - $iNestingLevel++; - } else if ($this->comes(')')) { - $iNestingLevel--; - } + if ($this->comes('(')) { + $iNestingLevel++; $oCalcList->addListComponent($this->consume(1)); - $iLastComponentType = CalcFunction::T_OPERATOR; - } else { + continue; + } else if ($this->comes(')')) { + $iNestingLevel--; + $oCalcList->addListComponent($this->consume(1)); + continue; + } + if ($iLastComponentType != CalcFunction::T_OPERAND) { $oVal = $this->parsePrimitiveValue(); - if ($iLastComponentType == CalcFunction::T_OPERAND) { - throw new UnexpectedTokenException(sprintf('Next token was expected to be an operand of type %s. Instead "%s" was found.', implode(', ', $aOperators), $oVal), '', 'custom', $this->iLineNo); - } - $oCalcList->addListComponent($oVal); $iLastComponentType = CalcFunction::T_OPERAND; + } else { + if (in_array($this->peek(), $aOperators)) { + if (($this->comes('-') || $this->comes('+'))) { + if ($this->peek(1, -1) != ' ' || !($this->comes('- ') || $this->comes('+ '))) { + throw new UnexpectedTokenException(" {$this->peek()} ", $this->peek(1, -1) . $this->peek(2), 'literal', $this->iLineNo); + } + } + $oCalcList->addListComponent($this->consume(1)); + $iLastComponentType = CalcFunction::T_OPERATOR; + } else { + throw new UnexpectedTokenException(sprintf('Next token was expected to be an operand of type %s. Instead "%s" was found.', implode(', ', $aOperators), $oVal), '', 'custom', $this->iLineNo); + } } } $oList->addListComponent($oCalcList); diff --git a/tests/Sabberworm/CSS/ParserTest.php b/tests/Sabberworm/CSS/ParserTest.php index 43c22e22f..21c1dc1b8 100644 --- a/tests/Sabberworm/CSS/ParserTest.php +++ b/tests/Sabberworm/CSS/ParserTest.php @@ -390,10 +390,17 @@ function testUrlInFile() { function testCalcInFile() { $oDoc = $this->parsedStructureForFile('calc', Settings::create()->withMultibyteSupport(true)); $sExpected = 'div {width: calc(100% / 4);} +div {margin-top: calc(-120% - 4px);} div {height: -webkit-calc(9 / 16 * 100%) !important;width: -moz-calc(( 50px - 50% ) * 2);}'; $this->assertSame($sExpected, $oDoc->render()); } + function testCalcNestedInFile() { + $oDoc = $this->parsedStructureForFile('calc-nested', Settings::create()->withMultibyteSupport(true)); + $sExpected = '.test {font-size: calc(( 3 * 4px ) + -2px);top: calc(200px - calc(20 * 3px));}'; + $this->assertSame($sExpected, $oDoc->render()); + } + function testGridLineNameInFile() { $oDoc = $this->parsedStructureForFile('grid-linename', Settings::create()->withMultibyteSupport(true)); $sExpected = "div {grid-template-columns: [linename] 100px;}\nspan {grid-template-columns: [linename1 linename2] 100px;}"; diff --git a/tests/files/calc-nested.css b/tests/files/calc-nested.css new file mode 100644 index 000000000..5ad356d4b --- /dev/null +++ b/tests/files/calc-nested.css @@ -0,0 +1,4 @@ +.test { + font-size: calc((3 * 4px) + -2px); + top: calc(200px - calc(20 * 3px)); +} diff --git a/tests/files/calc.css b/tests/files/calc.css index 3ad06fdd5..676f7a4d7 100644 --- a/tests/files/calc.css +++ b/tests/files/calc.css @@ -1,4 +1,5 @@ div { width: calc(100% / 4); } +div { margin-top: calc(-120% - 4px); } div { height: -webkit-calc(9/16 * 100%)!important; width: -moz-calc((50px - 50%)*2);