From 913f196326f12f5c9d60d1879af921238a513f85 Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Fri, 21 Jun 2024 19:20:19 +0100 Subject: [PATCH 1/4] [FEATURE] Support arithmetic operators in CSS functions This is the enhancement from #390. --- src/Value/Value.php | 11 +++++- tests/Value/ValueTest.php | 82 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 tests/Value/ValueTest.php diff --git a/src/Value/Value.php b/src/Value/Value.php index 4950e35a..1cc1b5c9 100644 --- a/src/Value/Value.php +++ b/src/Value/Value.php @@ -164,7 +164,16 @@ public static function parsePrimitiveValue(ParserState $oParserState) } elseif ($oParserState->comes("U+")) { $oValue = self::parseUnicodeRangeValue($oParserState); } else { - $oValue = self::parseIdentifierOrFunction($oParserState); + $sNextChar = $oParserState->peek(1); + try { + $oValue = self::parseIdentifierOrFunction($oParserState); + } catch (UnexpectedTokenException $e) { + if (\in_array($sNextChar, ['+', '-', '*', '/'], true)) { + $oValue = $oParserState->consume(1); + } else { + throw $e; + } + } } $oParserState->consumeWhiteSpace(); return $oValue; diff --git a/tests/Value/ValueTest.php b/tests/Value/ValueTest.php new file mode 100644 index 00000000..e2ce2826 --- /dev/null +++ b/tests/Value/ValueTest.php @@ -0,0 +1,82 @@ + + */ + public static function provideArithmeticOperator(): array + { + $units = ['+', '-', '*', '/']; + + return \array_combine( + $units, + \array_map( + function (string $unit): array { + return [$unit]; + }, + $units + ) + ); + } + + /** + * @test + * + * @dataProvider provideArithmeticOperator + */ + public function parsesArithmeticInFunctions(string $operator): void + { + $subject = Value::parseValue(new ParserState('max(300px, 50vh ' . $operator . ' 10px);', Settings::create())); + + self::assertSame('max(300px,50vh ' . $operator . ' 10px)', (string) $subject); + } + + /** + * @test + */ + public function parsesArithmeticWithMultipleOperatorsInFunctions(): void + { + $subject = Value::parseValue(new ParserState('calc(300px + 10% + 10vw);', Settings::create())); + + self::assertSame('calc(300px + 10% + 10vw)', (string) $subject); + } + + /** + * @return array + */ + public static function provideMalformedLengthOperands(): array + { + return [ + 'LHS missing value' => ['vh', '10px'], + 'RHS missing value' => ['50vh', 'px'], + 'LHS missing unit' => ['50', '10px'], + 'RHS missing unit' => ['50vh', '10'], + ]; + } + + /** + * @test + * + * @dataProvider provideMalformedLengthOperands + */ + public function parsesArithmeticWithMalformedOperandsInFunctions(string $leftOperand, string $rightOperand): void + { + $subject = Value::parseValue(new ParserState( + 'max(300px, ' . $leftOperand . ' + ' . $rightOperand . ');', + Settings::create() + )); + + self::assertSame('max(300px,' . $leftOperand . ' + ' . $rightOperand . ')', (string) $subject); + } +} From 27ab167d6e7bbc06568d536bede310c8527f4f46 Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Fri, 21 Jun 2024 22:20:21 +0100 Subject: [PATCH 2/4] Enable strict typing in new PHP file. --- tests/Value/ValueTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Value/ValueTest.php b/tests/Value/ValueTest.php index e2ce2826..58a27212 100644 --- a/tests/Value/ValueTest.php +++ b/tests/Value/ValueTest.php @@ -1,5 +1,7 @@ Date: Fri, 21 Jun 2024 22:50:41 +0100 Subject: [PATCH 3/4] - Add changelog entry; - Correct nomenclature in datum name for numeric part of `length`. --- CHANGELOG.md | 1 + tests/Value/ValueTest.php | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e4f999e..bd9bcd3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). ## x.y.z ### Added +- Support arithmetic operators in CSS function arguments (#607) - Add support for inserting an item in a CSS list (#545) - Add a class diagram to the README (#482) - Add support for the `dvh`, `lvh` and `svh` length units (#415) diff --git a/tests/Value/ValueTest.php b/tests/Value/ValueTest.php index 58a27212..7a94346d 100644 --- a/tests/Value/ValueTest.php +++ b/tests/Value/ValueTest.php @@ -60,8 +60,8 @@ public function parsesArithmeticWithMultipleOperatorsInFunctions(): void public static function provideMalformedLengthOperands(): array { return [ - 'LHS missing value' => ['vh', '10px'], - 'RHS missing value' => ['50vh', 'px'], + 'LHS missing number' => ['vh', '10px'], + 'RHS missing number' => ['50vh', 'px'], 'LHS missing unit' => ['50', '10px'], 'RHS missing unit' => ['50vh', '10'], ]; From 3808d0775c0ea9b97f2fb94eee2b0bbb6cc254c7 Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Sat, 22 Jun 2024 00:43:43 +0100 Subject: [PATCH 4/4] Extend test for multiple operators to include `max`. `calc` was already supported. --- tests/Value/ValueTest.php | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/tests/Value/ValueTest.php b/tests/Value/ValueTest.php index 7a94346d..11526f9f 100644 --- a/tests/Value/ValueTest.php +++ b/tests/Value/ValueTest.php @@ -45,13 +45,38 @@ public function parsesArithmeticInFunctions(string $operator): void } /** - * @test + * @return array + * The first datum is a template for the parser (using `sprintf` insertion marker `%s` for some expression). + * The second is for the expected result, which may have whitespace and trailing semicolon removed. */ - public function parsesArithmeticWithMultipleOperatorsInFunctions(): void + public static function provideCssFunctionTemplates(): array { - $subject = Value::parseValue(new ParserState('calc(300px + 10% + 10vw);', Settings::create())); + return [ + 'calc' => [ + 'to be parsed' => 'calc(%s);', + 'expected' => 'calc(%s)', + ], + 'max' => [ + 'to be parsed' => 'max(300px, %s);', + 'expected' => 'max(300px,%s)', + ], + ]; + } + + /** + * @test + * + * @dataProvider provideCssFunctionTemplates + */ + public function parsesArithmeticWithMultipleOperatorsInFunctions( + string $parserTemplate, + string $expectedResultTemplate + ): void { + static $expression = '300px + 10% + 10vw'; + + $subject = Value::parseValue(new ParserState(\sprintf($parserTemplate, $expression), Settings::create())); - self::assertSame('calc(300px + 10% + 10vw)', (string) $subject); + self::assertSame(\sprintf($expectedResultTemplate, $expression), (string) $subject); } /**