diff options
Diffstat (limited to 'src/parser')
| -rw-r--r-- | src/parser/html_lexer.cc | 122 | ||||
| -rw-r--r-- | src/parser/tree_builder.cc | 87 |
2 files changed, 101 insertions, 108 deletions
diff --git a/src/parser/html_lexer.cc b/src/parser/html_lexer.cc index bbf8c57..73591b3 100644 --- a/src/parser/html_lexer.cc +++ b/src/parser/html_lexer.cc @@ -21,99 +21,101 @@ #include <cstdint> #include <string_view> - namespace ricochet::parser { namespace { -enum class LexerState : std::uint8_t { - Data, // Reading plain text - TagOpen, // Just encountered '<' - TagName, // Reading the name of an opening tag (e.g., "div") - ClosingTagName, // Reading the name of a closing tag (e.g., "/div") - SwallowAttributes // Ignore attributes mode (ignore until encountering '>') +enum class LexerState : std::uint8_t +{ + Data, // Reading plain text + TagOpen, // Just encountered '<' + TagName, // Reading the name of an opening tag (e.g., "div") + ClosingTagName, // Reading the name of a closing tag (e.g., "/div") + SwallowAttributes // Ignore attributes mode (ignore until encountering '>') }; -void emit_text(std::string& buffer, std::vector<HtmlToken>& tokens) { - if (buffer.empty()) { - return; // 提前返回,减少 if 嵌套层级 - } +void emit_text( std::string& buffer, std::vector<HtmlToken>& tokens ) +{ + if ( buffer.empty() ) { + return; // 提前返回,减少 if 嵌套层级 + } - bool all_whitespace = true; - for (const char c : buffer) { - if (!std::isspace(static_cast<unsigned char>(c))) { - all_whitespace = false; - break; - } - } - - if (!all_whitespace) { - tokens.emplace_back(TextToken{buffer}); + bool all_whitespace = true; + for ( const char c : buffer ) { + if ( !std::isspace( static_cast<unsigned char>( c ) ) ) { + all_whitespace = false; + break; } - buffer.clear(); + } + + if ( !all_whitespace ) { + tokens.emplace_back( TextToken { buffer } ); + } + buffer.clear(); } } // namespace -std::vector<HtmlToken> HtmlLexer::tokenize( std::string_view html ) const // NOLINT(readability-convert-member-functions-to-static) +std::vector<HtmlToken> HtmlLexer::tokenize( // NOLINT(readability-convert-member-functions-to-static) + std::string_view html ) const { std::vector<HtmlToken> tokens; LexerState state = LexerState::Data; - std::string buffer{}; + std::string buffer {}; - for (const char c : html) { - switch (state) { - case LexerState::Data: - if (c == '<') { - emit_text(buffer, tokens); - state = LexerState::TagOpen; + for ( const char c : html ) { + switch ( state ) { + case LexerState::Data: + if ( c == '<' ) { + emit_text( buffer, tokens ); + state = LexerState::TagOpen; } else { - buffer += c; + buffer += c; } break; - case LexerState::TagOpen: - if (c == '/') { - state = LexerState::ClosingTagName; - } else if (std::isalpha(static_cast<unsigned char>(c))) { - buffer.push_back(c); - state = LexerState::TagName; + case LexerState::TagOpen: + if ( c == '/' ) { + state = LexerState::ClosingTagName; + } else if ( std::isalpha( static_cast<unsigned char>( c ) ) ) { + buffer.push_back( c ); + state = LexerState::TagName; } else { - buffer.push_back('<'); - buffer.push_back(c); - state = LexerState::Data; + buffer.push_back( '<' ); + buffer.push_back( c ); + state = LexerState::Data; } break; - case LexerState::TagName: - if (c == '>') { - tokens.emplace_back(TagOpenToken{buffer}); - buffer.clear(); - state = LexerState::Data; - } else if (std::isspace(static_cast<unsigned char>(c))) { - tokens.emplace_back(TagOpenToken{buffer}); - buffer.clear(); - state = LexerState::SwallowAttributes; + case LexerState::TagName: + if ( c == '>' ) { + tokens.emplace_back( TagOpenToken { buffer } ); + buffer.clear(); + state = LexerState::Data; + } else if ( std::isspace( static_cast<unsigned char>( c ) ) ) { + tokens.emplace_back( TagOpenToken { buffer } ); + buffer.clear(); + state = LexerState::SwallowAttributes; } else { - buffer += static_cast<char>(std::tolower(static_cast<unsigned char>(c))); + buffer += static_cast<char>( std::tolower( static_cast<unsigned char>( c ) ) ); } break; - case LexerState::ClosingTagName: - if (c == '>') { - tokens.emplace_back(TagCloseToken{buffer}); - buffer.clear(); - state = LexerState::Data; + case LexerState::ClosingTagName: + if ( c == '>' ) { + tokens.emplace_back( TagCloseToken { buffer } ); + buffer.clear(); + state = LexerState::Data; } else { - buffer += static_cast<char>(std::tolower(static_cast<unsigned char>(c))); + buffer += static_cast<char>( std::tolower( static_cast<unsigned char>( c ) ) ); } break; - case LexerState::SwallowAttributes: - if (c == '>') { - state = LexerState::Data; + case LexerState::SwallowAttributes: + if ( c == '>' ) { + state = LexerState::Data; } break; } } - emit_text(buffer, tokens); + emit_text( buffer, tokens ); return tokens; } diff --git a/src/parser/tree_builder.cc b/src/parser/tree_builder.cc index 51a8a51..4f7f87e 100644 --- a/src/parser/tree_builder.cc +++ b/src/parser/tree_builder.cc @@ -24,67 +24,58 @@ namespace ricochet::parser { namespace { -struct TokenVisitor { - std::vector<DomNode>* stack; +struct TokenVisitor +{ + std::vector<DomNode>* stack; - void operator()(const TextToken& token) const { - stack->back().children.emplace_back( - DomNode{ - .tag_name = "", - .text_content = token.content, - .children = {} - } - ); - } + void operator()( const TextToken& token ) const + { + stack->back().children.emplace_back( + DomNode { .tag_name = "", .text_content = token.content, .children = {} } ); + } + + void operator()( const TagOpenToken& token ) const + { + DomNode new_node { .tag_name = token.name, .text_content = "", .children = {} }; - void operator()(const TagOpenToken& token) const { - DomNode new_node{ - .tag_name = token.name, - .text_content = "", - .children = {} - }; + if ( token.name == "meta" || token.name == "link" || token.name == "img" || token.name == "br" + || token.name == "hr" || token.name == "input" || token.name == "base" || token.name == "col" ) { - if (token.name == "meta" || token.name == "link" || token.name == "img" || - token.name == "br" || token.name == "hr" || token.name == "input" || - token.name == "base" || token.name == "col") { - - stack->back().children.push_back(std::move(new_node)); - } else { - stack->push_back(std::move(new_node)); - } + stack->back().children.push_back( std::move( new_node ) ); + } else { + stack->push_back( std::move( new_node ) ); } + } - void operator()(const TagCloseToken& /*token*/) const { - if (stack->size() > 1) { - DomNode completed_node = std::move(stack->back()); - stack->pop_back(); - stack->back().children.push_back(std::move(completed_node)); - } + void operator()( const TagCloseToken& /*token*/ ) const + { + if ( stack->size() > 1 ) { + DomNode completed_node = std::move( stack->back() ); + stack->pop_back(); + stack->back().children.push_back( std::move( completed_node ) ); } + } }; } // namespace -DomNode TreeBuilder::build(const std::vector<HtmlToken>& tokens) const // NOLINT(readability-convert-member-functions-to-static) +DomNode TreeBuilder::build( // NOLINT(readability-convert-member-functions-to-static) + const std::vector<HtmlToken>& tokens ) const { - std::vector<DomNode> stack; - stack.emplace_back(DomNode{ - .tag_name = "Document", - .text_content = "", - .children = {} - }); + std::vector<DomNode> stack; + stack.emplace_back( DomNode { .tag_name = "Document", .text_content = "", .children = {} } ); - TokenVisitor visitor{&stack}; - for (const auto& token : tokens) { - std::visit(visitor, token); - } + TokenVisitor visitor { &stack }; + for ( const auto& token : tokens ) { + std::visit( visitor, token ); + } - while (stack.size() > 1) { - DomNode completed_node = std::move(stack.back()); - stack.pop_back(); - stack.back().children.push_back(std::move(completed_node)); - } + while ( stack.size() > 1 ) { + DomNode completed_node = std::move( stack.back() ); + stack.pop_back(); + stack.back().children.push_back( std::move( completed_node ) ); + } - return std::move(stack.front()); + return std::move( stack.front() ); } } // namespace ricochet::parser
\ No newline at end of file |
