/* * Copyright (c) 2020-2021, the SerenityOS developers. * Copyright (c) 2021-2024, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include namespace Web::CSS::Parser { // https://drafts.csswg.org/css-syntax/#css-token-stream template class TokenStream { public: class StateTransaction { public: explicit StateTransaction(TokenStream& 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& m_token_stream; size_t m_saved_index { 0 }; bool m_commit { false }; }; explicit TokenStream(Span tokens) : m_tokens(tokens) , m_eof(make_eof()) { } explicit TokenStream(Vector const& tokens) : m_tokens(tokens.span()) , m_eof(make_eof()) { } static TokenStream of_single_token(T const& token) { return TokenStream(Span { &token, 1 }); } TokenStream(TokenStream const&) = delete; TokenStream(TokenStream&&) = 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 . 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 . 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 , 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 - 1) dbgln("-> {}", token.to_debug_string()); else dbgln(" {}", token.to_debug_string()); } } void copy_state(Badge, TokenStream const& other) { m_index = other.m_index; } private: // https://drafts.csswg.org/css-syntax/#token-stream-tokens Span 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 m_marked_indexes; T make_eof() { if constexpr (IsSame) { return Tokenizer::create_eof_token(); } if constexpr (IsSame) { return ComponentValue(Tokenizer::create_eof_token()); } } T m_eof; }; }