Skip to content

Commit

Permalink
Fix CSSTokenizer handling of decimals without leading zeroes (#44432)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #44432

Implements a bit more of the tokenizer algorithm, to correctly support dimensions like `.25turn` instead of just `0.25turn`.

Changelog: [Internal]

Reviewed By: joevilches

Differential Revision: D57033796

fbshipit-source-id: 6d73de22e3a0f0ca0de432be56bca97f0069ad96
  • Loading branch information
NickGerleman authored and facebook-github-bot committed May 7, 2024
1 parent 0c9b2a4 commit 9b77506
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 27 deletions.
72 changes: 47 additions & 25 deletions packages/react-native/ReactCommon/react/renderer/css/CSSTokenizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,42 +83,46 @@ class CSSTokenizer {
explicit constexpr CSSTokenizer(std::string_view characters)
: remainingCharacters_{characters} {}

/**
* Returns the next token according to the algorithm described in
* https://www.w3.org/TR/css-syntax-3/#consume-token
*/
constexpr CSSToken next() {
// https://www.w3.org/TR/css-syntax-3/#token-diagrams
char nextChar = peek();

if (isWhitespace(nextChar)) {
return consumeWhitespace();
} else if (nextChar == '+') {
if (isDigit(peekNext())) {
return consumeNumeric();
} else {
return consumeDelim();
}
} else if (nextChar == '-') {
if (isDigit(peekNext())) {
return consumeNumeric();
} else {
return consumeDelim();
}
} else if (isDigit(nextChar)) {
}

switch (nextChar) {
case '+':
case '-':
case '.':
if (wouldStartNumber()) {
return consumeNumeric();
} else {
return consumeDelim();
}
}

if (isDigit(nextChar)) {
return consumeNumeric();
} else if (isIdentStart(nextChar)) {
}

if (isIdentStart(nextChar)) {
return consumeIdent();
} else if (nextChar == '\0') {
}

if (nextChar == '\0') {
return CSSToken{CSSTokenType::EndOfFile};
} else {
return consumeDelim();
}

return consumeDelim();
}

private:
constexpr char peek() const {
auto index = position_;
return index >= remainingCharacters_.size() ? '\0'
: remainingCharacters_[index];
}
constexpr char peekNext() const {
auto index = position_ + 1;
constexpr char peek(size_t i = 0) const {
auto index = position_ + i;
return index >= remainingCharacters_.size() ? '\0'
: remainingCharacters_[index];
}
Expand All @@ -141,6 +145,24 @@ class CSSTokenizer {
return CSSToken{CSSTokenType::WhiteSpace};
}

constexpr bool wouldStartNumber() const {
// https://www.w3.org/TR/css-syntax-3/#starts-with-a-number
if (peek() == '+' || peek() == '-') {
if (isDigit(peek(1))) {
return true;
}
if (peek(1) == '.' && isDigit(peek(2))) {
return true;
}
} else if (peek() == '.' && isDigit(peek(1))) {
return true;
} else if (isDigit(peek())) {
return true;
}

return false;
}

constexpr CSSToken consumeNumber() {
// https://www.w3.org/TR/css-syntax-3/#consume-number
// https://www.w3.org/TR/css-syntax-3/#convert-a-string-to-a-number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,9 +290,9 @@ TEST(CSSParser, angle_values) {
EXPECT_EQ(gradianValue.type(), CSSValueType::Angle);
ASSERT_NEAR(gradianValue.getAngle().degrees, 9.0f, 0.001f);

auto turnValue = parseCSSComponentValue<CSSWideKeyword, CSSAngle>("1turn");
auto turnValue = parseCSSComponentValue<CSSWideKeyword, CSSAngle>(".25turn");
EXPECT_EQ(turnValue.type(), CSSValueType::Angle);
EXPECT_EQ(turnValue.getAngle().degrees, 360.0f);
EXPECT_EQ(turnValue.getAngle().degrees, 90.0f);
}

TEST(CSSParser, parse_prop) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ TEST(CSSTokenizer, number_values) {
"+81.07e+0",
CSSToken{CSSTokenType::Number, +81.07e+0},
CSSToken{CSSTokenType::EndOfFile});

EXPECT_TOKENS(
"+.123e+0",
CSSToken{CSSTokenType::Number, +.123e+0},
CSSToken{CSSTokenType::EndOfFile});
}

TEST(CSSTokenizer, dimension_values) {
Expand All @@ -102,6 +107,11 @@ TEST(CSSTokenizer, dimension_values) {
"463.2abc",
CSSToken{CSSTokenType::Dimension, 463.2, "abc"},
CSSToken{CSSTokenType::EndOfFile});

EXPECT_TOKENS(
".3xyz",
CSSToken{CSSTokenType::Dimension, 0.3, "xyz"},
CSSToken{CSSTokenType::EndOfFile});
}

TEST(CSSTokenizer, percent_values) {
Expand All @@ -114,6 +124,11 @@ TEST(CSSTokenizer, percent_values) {
"-28.5%",
CSSToken{CSSTokenType::Percentage, -28.5f},
CSSToken{CSSTokenType::EndOfFile});

EXPECT_TOKENS(
".02%",
CSSToken{CSSTokenType::Percentage, 0.02f},
CSSToken{CSSTokenType::EndOfFile});
}

TEST(CSSTokenizer, mixed_values) {
Expand Down

0 comments on commit 9b77506

Please sign in to comment.