Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#pragma once

#include <optional>
#include <string_view>

#include <react/utils/fnv1a.h>

namespace facebook::react {

/**
* Numeric keyword constants usable within CSS math functions.
* https://www.w3.org/TR/css-values-4/#calc-constants
*/
enum class CSSMathConstant {
E,
Pi,
Infinity,
NegativeInfinity,
NaN,
};

constexpr auto parseCSSMathConstant(std::string_view unit) -> std::optional<CSSMathConstant>
{
switch (fnv1aLowercase(unit)) {
case fnv1a("e"):
return CSSMathConstant::E;
case fnv1a("pi"):
return CSSMathConstant::Pi;
case fnv1a("infinity"):
return CSSMathConstant::Infinity;
case fnv1a("-infinity"):
return CSSMathConstant::NegativeInfinity;
case fnv1a("nan"):
return CSSMathConstant::NaN;
default:
return std::nullopt;
}
}

} // namespace facebook::react
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#pragma once

#include <optional>
#include <utility>
#include <react/utils/to_underlying.h>

namespace facebook::react {

/**
* Arithmetic operators used in CSS math functions.
* https://www.w3.org/TR/css-values-4/#calc-syntax
*/
enum class CSSMathOperator : char {
Add = '+',
Subtract = '-',
Multiply = '*',
Divide = '/',
};

constexpr auto parseCSSMathOperator(char c) -> std::optional<CSSMathOperator>
{
for (auto op : {CSSMathOperator::Add, CSSMathOperator::Subtract,
CSSMathOperator::Multiply, CSSMathOperator::Divide}) {
if (c == to_underlying(op)) {
return op;
}
}
return std::nullopt;
}

} // namespace facebook::react
31 changes: 31 additions & 0 deletions packages/react-native/ReactCommon/react/renderer/css/CSSToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@

#pragma once

#include <optional>
#include <string_view>

#include <react/renderer/css/CSSMathConstant.h>
#include <react/renderer/css/CSSMathOperator.h>

namespace facebook::react {

/**
Expand Down Expand Up @@ -76,6 +80,33 @@ class CSSToken {
return unit_;
}

/**
* If this token is a <delim-token> representing a math operator (+, -, *,
* /), return the corresponding CSSMathOperator.
* https://www.w3.org/TR/css-values-4/#calc-syntax
*/
constexpr std::optional<CSSMathOperator> mathOperator() const
{
if (type_ != CSSTokenType::Delim || stringValue_.size() != 1) {
return std::nullopt;
}
return parseCSSMathOperator(stringValue_[0]);
}

/**
* If this token is an <ident-token> representing a CSS math constant
* (e, pi, infinity, -infinity, NaN), return the corresponding
* CSSMathConstant
* https://www.w3.org/TR/css-values-4/#calc-constants
*/
constexpr std::optional<CSSMathConstant> mathConstant() const
{
if (type_ != CSSTokenType::Ident) {
return std::nullopt;
}
return parseCSSMathConstant(stringValue_);
}

constexpr bool operator==(const CSSToken &other) const = default;

private:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,20 @@ class CSSTokenizer {
case ',':
return consumeCharacter(CSSTokenType::Comma);
case '+':
case '-':
case '.':
if (wouldStartNumber()) {
return consumeNumeric();
} else {
return consumeDelim();
}
case '-':
if (wouldStartNumber()) {
return consumeNumeric();
} else if (wouldStartIdentSequence()) {
return consumeIdentlikeToken();
} else {
return consumeDelim();
}
case '#':
if (isIdent(peek(1))) {
return consumeHash();
Expand Down Expand Up @@ -140,6 +147,16 @@ class CSSTokenizer {
return false;
}

constexpr bool wouldStartIdentSequence() const
{
// https://www.w3.org/TR/css-syntax-3/#would-start-an-identifier
if (peek() == '-') {
return isIdentStart(peek(1)) || peek(1) == '-';
}

return isIdentStart(peek());
}

constexpr CSSToken consumeNumber()
{
// https://www.w3.org/TR/css-syntax-3/#consume-number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,48 @@ TEST(CSSTokenizer, ident_values) {
CSSToken{CSSTokenType::WhiteSpace},
CSSToken{CSSTokenType::Ident, "left"},
CSSToken{CSSTokenType::EndOfFile});

EXPECT_TOKENS(
"-infinity",
CSSToken{CSSTokenType::Ident, "-infinity"},
CSSToken{CSSTokenType::EndOfFile});

EXPECT_TOKENS(
"--custom-ident",
CSSToken{CSSTokenType::Ident, "--custom-ident"},
CSSToken{CSSTokenType::EndOfFile});

EXPECT_TOKENS(
"e pi infinity NaN",
CSSToken{CSSTokenType::Ident, "e"},
CSSToken{CSSTokenType::WhiteSpace},
CSSToken{CSSTokenType::Ident, "pi"},
CSSToken{CSSTokenType::WhiteSpace},
CSSToken{CSSTokenType::Ident, "infinity"},
CSSToken{CSSTokenType::WhiteSpace},
CSSToken{CSSTokenType::Ident, "NaN"},
CSSToken{CSSTokenType::EndOfFile});

EXPECT_TOKENS(
"5 - 3",
CSSToken{CSSTokenType::Number, 5.0f},
CSSToken{CSSTokenType::WhiteSpace},
CSSToken{CSSTokenType::Delim, "-"},
CSSToken{CSSTokenType::WhiteSpace},
CSSToken{CSSTokenType::Number, 3.0f},
CSSToken{CSSTokenType::EndOfFile});

EXPECT_TOKENS(
"5 -3",
CSSToken{CSSTokenType::Number, 5.0f},
CSSToken{CSSTokenType::WhiteSpace},
CSSToken{CSSTokenType::Number, -3.0f},
CSSToken{CSSTokenType::EndOfFile});

EXPECT_TOKENS(
"-calc(",
CSSToken{CSSTokenType::Function, "-calc"},
CSSToken{CSSTokenType::EndOfFile});
}

TEST(CSSTokenizer, number_values) {
Expand Down Expand Up @@ -220,6 +262,17 @@ TEST(CSSTokenizer, invalid_values) {
CSSToken{CSSTokenType::OpenParen},
CSSToken{CSSTokenType::Delim, "%"},
CSSToken{CSSTokenType::EndOfFile});

EXPECT_TOKENS(
"+ - * /",
CSSToken{CSSTokenType::Delim, "+"},
CSSToken{CSSTokenType::WhiteSpace},
CSSToken{CSSTokenType::Delim, "-"},
CSSToken{CSSTokenType::WhiteSpace},
CSSToken{CSSTokenType::Delim, "*"},
CSSToken{CSSTokenType::WhiteSpace},
CSSToken{CSSTokenType::Delim, "/"},
CSSToken{CSSTokenType::EndOfFile});
}

TEST(CSSTokenizer, hash_values) {
Expand All @@ -239,4 +292,23 @@ TEST(CSSTokenizer, hash_values) {
CSSToken{CSSTokenType::Delim, "*"},
CSSToken{CSSTokenType::EndOfFile});
}
TEST(CSSTokenizer, mathOperator) {
EXPECT_EQ(CSSTokenizer{"+"}.next().mathOperator(), CSSMathOperator::Add);
EXPECT_EQ(CSSTokenizer{"-"}.next().mathOperator(), CSSMathOperator::Subtract);
EXPECT_EQ(CSSTokenizer{"*"}.next().mathOperator(), CSSMathOperator::Multiply);
EXPECT_EQ(CSSTokenizer{"/"}.next().mathOperator(), CSSMathOperator::Divide);
EXPECT_EQ(CSSTokenizer{"%"}.next().mathOperator(), std::nullopt);
}
TEST(CSSTokenizer, math_constants) {
EXPECT_EQ(CSSTokenizer{"pi"}.next().mathConstant(), CSSMathConstant::Pi);
EXPECT_EQ(CSSTokenizer{"e"}.next().mathConstant(), CSSMathConstant::E);
EXPECT_EQ(
CSSTokenizer{"infinity"}.next().mathConstant(),
CSSMathConstant::Infinity);
EXPECT_EQ(
CSSTokenizer{"-infinity"}.next().mathConstant(),
CSSMathConstant::NegativeInfinity);
EXPECT_EQ(CSSTokenizer{"NaN"}.next().mathConstant(), CSSMathConstant::NaN);
EXPECT_EQ(CSSTokenizer{"abc"}.next().mathConstant(), std::nullopt);
}
} // namespace facebook::react