123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- /*
- * 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());
- }
- }
- void copy_state(Badge<Parser>, TokenStream<T> const& other)
- {
- m_index = other.m_index;
- }
- 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;
- };
- }
|