diff --git a/css2/token.h b/css2/token.h index 2c931e1c..d790127a 100644 --- a/css2/token.h +++ b/css2/token.h @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: 2022 Mikael Larsson -// SPDX-FileCopyrightText: 2024 Robin Lindén +// SPDX-FileCopyrightText: 2024-2025 Robin Lindén // // SPDX-License-Identifier: BSD-2-Clause @@ -69,7 +69,7 @@ struct NumberToken { }; struct PercentageToken { - std::variant data{}; + std::variant data{}; [[nodiscard]] bool operator==(PercentageToken const &) const = default; [[nodiscard]] constexpr bool is_integer() const { return std::holds_alternative(data); } diff --git a/css2/tokenizer.cpp b/css2/tokenizer.cpp index 449b5263..304f7944 100644 --- a/css2/tokenizer.cpp +++ b/css2/tokenizer.cpp @@ -92,8 +92,7 @@ void Tokenizer::run() { continue; case '+': { if (inputs_starts_number(*c)) { - auto number = consume_number(*c); - emit(NumberToken{number}); + emit(consume_a_numeric_token(*c)); } else { emit(DelimToken{'+'}); } @@ -104,8 +103,7 @@ void Tokenizer::run() { continue; case '-': { if (inputs_starts_number(*c)) { - auto number = consume_number(*c); - emit(NumberToken{number}); + emit(consume_a_numeric_token(*c)); continue; } @@ -125,8 +123,7 @@ void Tokenizer::run() { } case '.': { if (auto next_input = peek_input(0); is_digit(next_input)) { - auto number = consume_number(*c); - emit(NumberToken{number}); + emit(consume_a_numeric_token(*c)); continue; } @@ -161,9 +158,7 @@ void Tokenizer::run() { case '7': case '8': case '9': { - // TODO(robinlinden): https://www.w3.org/TR/css-syntax-3/#consume-a-numeric-token - auto number = consume_number(*c); - emit(NumberToken{number}); + emit(consume_a_numeric_token(*c)); continue; } default: @@ -542,4 +537,15 @@ std::string Tokenizer::consume_an_escaped_code_point() { return std::string{*c}; } +Token Tokenizer::consume_a_numeric_token(char first_byte) { + // TODO(robinlinden): https://www.w3.org/TR/css-syntax-3/#consume-a-numeric-token + auto number = consume_number(first_byte); + if (peek_input(0) == '%') { + std::ignore = consume_next_input_character(); + return PercentageToken{number}; + } + + return NumberToken{number}; +} + } // namespace css2 diff --git a/css2/tokenizer.h b/css2/tokenizer.h index 7819674d..25d1c12c 100644 --- a/css2/tokenizer.h +++ b/css2/tokenizer.h @@ -72,6 +72,7 @@ class Tokenizer { std::variant consume_number(char first_byte); std::string consume_an_escaped_code_point(); + Token consume_a_numeric_token(char first_byte); }; } // namespace css2 diff --git a/css2/tokenizer_test.cpp b/css2/tokenizer_test.cpp index a7aacfa3..40e45284 100644 --- a/css2/tokenizer_test.cpp +++ b/css2/tokenizer_test.cpp @@ -438,6 +438,26 @@ int main() { expect_token(output, NumberToken{.data = 1}); }); + s.add_test("percentage: integer", [](etest::IActions &a) { + auto output = run_tokenizer(a, "13%"); + expect_token(output, PercentageToken{.data = 13}); + }); + + s.add_test("percentage: large", [](etest::IActions &a) { + auto output = run_tokenizer(a, "12147483647%"); + expect_token(output, PercentageToken{std::numeric_limits::max()}); + }); + + s.add_test("percentage: large negative", [](etest::IActions &a) { + auto output = run_tokenizer(a, "-12147483648%"); + expect_token(output, PercentageToken{std::numeric_limits::min()}); + }); + + s.add_test("percentage: number", [](etest::IActions &a) { + auto output = run_tokenizer(a, "13.25%"); + expect_token(output, PercentageToken{.data = 13.25}); + }); + s.add_test("plus: delim", [](etest::IActions &a) { auto output = run_tokenizer(a, "+hello"); expect_token(output, DelimToken{'+'});