ladybird/Libraries/LibWeb/CSS/Parser/TokenStream.h

223 lines
5.8 KiB
C
Raw Normal View History

/*
* Copyright (c) 2020-2021, the SerenityOS developers.
* Copyright (c) 2021-2024, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Format.h>
#include <AK/Vector.h>
#include <LibWeb/CSS/Parser/ComponentValue.h>
#include <LibWeb/CSS/Parser/Tokenizer.h>
namespace Web::CSS::Parser {
// https://drafts.csswg.org/css-syntax/#css-token-stream
template<typename T>
class TokenStream {
public:
class StateTransaction {
public:
explicit StateTransaction(TokenStream<T>& token_stream)
: m_token_stream(token_stream)
, m_saved_index(token_stream.m_index)
{
}
~StateTransaction()
{
if (!m_commit)
m_token_stream.m_index = m_saved_index;
}
StateTransaction create_child() { return StateTransaction(*this); }
void commit()
{
m_commit = true;
if (m_parent)
m_parent->commit();
}
private:
explicit StateTransaction(StateTransaction& parent)
: m_parent(&parent)
, m_token_stream(parent.m_token_stream)
, m_saved_index(parent.m_token_stream.m_index)
{
}
StateTransaction* m_parent { nullptr };
TokenStream<T>& m_token_stream;
size_t m_saved_index { 0 };
bool m_commit { false };
};
explicit TokenStream(Span<T const> tokens)
: m_tokens(tokens)
, m_eof(make_eof())
{
}
explicit TokenStream(Vector<T> const& tokens)
: m_tokens(tokens.span())
, m_eof(make_eof())
{
}
static TokenStream<T> of_single_token(T const& token)
{
return TokenStream(Span<T const> { &token, 1 });
}
TokenStream(TokenStream<T> const&) = delete;
TokenStream(TokenStream<T>&&) = default;
// https://drafts.csswg.org/css-syntax/#token-stream-next-token
[[nodiscard]] T const& next_token() const
{
// The item of tokens at index.
// If that index would be out-of-bounds past the end of the list, it’s instead an <eof-token>.
if (m_index < m_tokens.size())
return m_tokens[m_index];
return m_eof;
}
// https://drafts.csswg.org/css-syntax/#token-stream-empty
[[nodiscard]] bool is_empty() const
{
// A token stream is empty if the next token is an <eof-token>.
return next_token().is(Token::Type::EndOfFile);
}
// https://drafts.csswg.org/css-syntax/#token-stream-consume-a-token
[[nodiscard]] T const& consume_a_token()
{
// Let token be the next token. Increment index, then return token.
auto& token = next_token();
++m_index;
return token;
}
// https://drafts.csswg.org/css-syntax/#token-stream-discard-a-token
void discard_a_token()
{
// If the token stream is not empty, increment index.
if (!is_empty())
++m_index;
}
// https://drafts.csswg.org/css-syntax/#token-stream-mark
void mark()
{
// Append index to marked indexes.
m_marked_indexes.append(m_index);
}
// https://drafts.csswg.org/css-syntax/#token-stream-restore-a-mark
void restore_a_mark()
{
// Pop from marked indexes, and set index to the popped value.
m_index = m_marked_indexes.take_last();
}
// https://drafts.csswg.org/css-syntax/#token-stream-discard-a-mark
void discard_a_mark()
{
// Pop from marked indexes, and do nothing with the popped value.
m_marked_indexes.take_last();
}
// https://drafts.csswg.org/css-syntax/#token-stream-discard-whitespace
void discard_whitespace()
{
// While the next token is a <whitespace-token>, discard a token.
while (next_token().is(Token::Type::Whitespace))
discard_a_token();
}
bool has_next_token()
{
return !is_empty();
}
// Deprecated, used in older versions of the spec.
T const& current_token()
{
if (m_index < 1 || (m_index - 1) >= m_tokens.size())
return m_eof;
return m_tokens.at(m_index - 1);
}
// Deprecated
T const& peek_token(size_t offset = 0)
{
if (remaining_token_count() <= offset)
return m_eof;
return m_tokens.at(m_index + offset);
}
// Deprecated, was used in older versions of the spec.
void reconsume_current_input_token()
{
if (m_index > 0)
--m_index;
}
StateTransaction begin_transaction() { return StateTransaction(*this); }
size_t remaining_token_count() const
{
if (m_tokens.size() > m_index)
return m_tokens.size() - m_index;
return 0;
}
void dump_all_tokens()
{
dbgln("Dumping all tokens:");
for (size_t i = 0; i < m_tokens.size(); ++i) {
auto& token = m_tokens[i];
if (i == m_index)
dbgln("-> {}", token.to_debug_string());
else
dbgln(" {}", token.to_debug_string());
}
}
String dump_string()
{
// FIXME: The whitespace is only needed because we strip it when parsing property values. Remove it here once
// we stop doing that.
return MUST(String::join(" "sv, m_tokens));
}
private:
// https://drafts.csswg.org/css-syntax/#token-stream-tokens
Span<T const> m_tokens;
// https://drafts.csswg.org/css-syntax/#token-stream-index
size_t m_index { 0 };
// https://drafts.csswg.org/css-syntax/#token-stream-marked-indexes
Vector<size_t> m_marked_indexes;
T make_eof()
{
if constexpr (IsSame<T, Token>) {
return Tokenizer::create_eof_token();
}
if constexpr (IsSame<T, ComponentValue>) {
return ComponentValue(Tokenizer::create_eof_token());
}
}
T m_eof;
};
}