diff --git a/src/Value/CSSFunction.php b/src/Value/CSSFunction.php index 7ee7b91d..7d3f8c1f 100644 --- a/src/Value/CSSFunction.php +++ b/src/Value/CSSFunction.php @@ -67,7 +67,7 @@ private static function parseName(ParserState $oParserState, bool $bIgnoreCase = * @throws UnexpectedEOFException * @throws UnexpectedTokenException */ - private static function parseArguments(ParserState $oParserState) + protected static function parseArguments(ParserState $oParserState) { return Value::parseValue($oParserState, ['=', ' ', ',']); } diff --git a/src/Value/Expression.php b/src/Value/Expression.php new file mode 100644 index 00000000..ac48cc25 --- /dev/null +++ b/src/Value/Expression.php @@ -0,0 +1,27 @@ +consume('('); + $aArguments = parent::parseArguments($oParserState); + $mResult = new Expression("", $aArguments, ',', $oParserState->currentLine()); + $oParserState->consume(')'); + return $mResult; + } +} diff --git a/src/Value/Value.php b/src/Value/Value.php index 5fb6e3d0..ec761ee0 100644 --- a/src/Value/Value.php +++ b/src/Value/Value.php @@ -162,6 +162,8 @@ public static function parsePrimitiveValue(ParserState $oParserState) $oValue = LineName::parse($oParserState); } elseif ($oParserState->comes('U+')) { $oValue = self::parseUnicodeRangeValue($oParserState); + } elseif ($oParserState->comes("(")) { + $oValue = Expression::parse($oParserState); } else { $sNextChar = $oParserState->peek(1); try { diff --git a/tests/ParserTest.php b/tests/ParserTest.php index 2c1c7287..5baccbcc 100644 --- a/tests/ParserTest.php +++ b/tests/ParserTest.php @@ -513,6 +513,20 @@ public function expandShorthands(): void self::assertSame($sExpected, $oDoc->render()); } + /** + * @test + */ + public function parseExpressions(): void + { + $oDoc = self::parsedStructureForFile('expressions'); + $sExpected = 'div {height: (vh - 10);}' + . "\n" + . 'div {height: (vh - 10)/2;}' + . "\n" + . 'div {height: max(5,(vh - 10));}'; + self::assertSame($sExpected, $oDoc->render()); + } + /** * @test */ @@ -689,8 +703,8 @@ public function calcNestedInFile(): void public function invalidCalcInFile(): void { $oDoc = self::parsedStructureForFile('calc-invalid', Settings::create()->withMultibyteSupport(true)); - $sExpected = 'div {} -div {} + $sExpected = 'div {height: calc (25% - 1em);} +div {height: calc (25% - 1em);} div {} div {height: -moz-calc;} div {height: calc;}'; @@ -1239,6 +1253,19 @@ public function lonelyImport(): void self::assertSame($sExpected, $oDoc->render()); } + /** + * @test + */ + public function functionArithmeticInFile(): void + { + $oDoc = self::parsedStructureForFile('function-arithmetic', Settings::create()->withMultibyteSupport(true)); + $sExpected = 'div {height: max(300,vh + 10);} +div {height: max(300,vh - 10);} +div {height: max(300,vh * 10);} +div {height: max(300,vh / 10);}'; + self::assertSame($sExpected, $oDoc->render()); + } + public function escapedSpecialCaseTokens(): void { $oDoc = $this->parsedStructureForFile('escaped-tokens'); diff --git a/tests/Value/ExpressionTest.php b/tests/Value/ExpressionTest.php new file mode 100644 index 00000000..4e2ad5f0 --- /dev/null +++ b/tests/Value/ExpressionTest.php @@ -0,0 +1,63 @@ + + */ + public static function provideExpressions(): array + { + return [ + [ + 'input' => '(vh - 10) / 2', + 'expected_output' => '(vh - 10)/2', + 'expression_index' => 0, + ], + [ + 'input' => 'max(5, (vh - 10))', + 'expected_output' => 'max(5,(vh - 10))', + 'expression_index' => 1 + ], + ]; + } + + /** + * @test + * + * @dataProvider provideExpressions + */ + public function parseExpressions(string $input, string $expected, int $expression_index): void + { + $val = Value::parseValue( + new ParserState($input, Settings::create()), + $this->getDelimiters('height') + ); + + self::assertInstanceOf(ValueList::class, $val); + self::assertInstanceOf(Expression::class, $val->getListComponents()[$expression_index]); + self::assertSame($expected, (string) $val); + } + + private function getDelimiters(string $rule): array + { + $closure = function($rule) { + return self::listDelimiterForRule($rule); + }; + + $getter = $closure->bindTo(null, Rule::class); + return $getter($rule); + } +} diff --git a/tests/fixtures/expressions.css b/tests/fixtures/expressions.css new file mode 100644 index 00000000..50df58c9 --- /dev/null +++ b/tests/fixtures/expressions.css @@ -0,0 +1,11 @@ +div { + height: (vh - 10); +} + +div { + height: (vh - 10) / 2; +} + +div { + height: max(5, (vh - 10)); +} diff --git a/tests/fixtures/function-arithmetic.css b/tests/fixtures/function-arithmetic.css new file mode 100644 index 00000000..07a2c318 --- /dev/null +++ b/tests/fixtures/function-arithmetic.css @@ -0,0 +1,12 @@ +div { + height: max(300, vh + 10); +} +div { + height: max(300, vh - 10); +} +div { + height: max(300, vh * 10); +} +div { + height: max(300, vh / 10); +}