diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/browser.cc | 50 | ||||
| -rw-r--r-- | src/net/http_client.cc | 13 | ||||
| -rw-r--r-- | src/parser/html_lexer.cc | 122 | ||||
| -rw-r--r-- | src/parser/tree_builder.cc | 87 |
4 files changed, 133 insertions, 139 deletions
diff --git a/src/core/browser.cc b/src/core/browser.cc index 7acc915..e4c4c60 100644 --- a/src/core/browser.cc +++ b/src/core/browser.cc @@ -23,18 +23,18 @@ #include <iostream> namespace { -void print_dom_tree(const ricochet::parser::DomNode& node, std::size_t depth = 0) // NOLINT(misc-no-recursion) +void print_dom_tree( const ricochet::parser::DomNode& node, std::size_t depth = 0 ) // NOLINT(misc-no-recursion) { - const std::string indent(depth * 2, ' '); - - if (node.tag_name.empty()) { - std::cout << indent << "\"" << node.text_content << "\"\n"; - } else { - std::cout << indent << "<" << node.tag_name << ">\n"; - for (const auto& child : node.children) { - print_dom_tree(child, depth + 1); - } + const std::string indent( depth * 2, ' ' ); + + if ( node.tag_name.empty() ) { + std::cout << indent << "\"" << node.text_content << "\"\n"; + } else { + std::cout << indent << "<" << node.tag_name << ">\n"; + for ( const auto& child : node.children ) { + print_dom_tree( child, depth + 1 ); } + } } } // namespace namespace ricochet::core { @@ -43,26 +43,26 @@ int Browser::run( std::string_view initial_url ) { std::cout << "-> Fetching: " << initial_url << "...\n"; - const net::HttpClient client; - auto response_result = client.fetch(initial_url); + const net::HttpClient client; + auto response_result = client.fetch( initial_url ); - if (!response_result.has_value()) { - std::cerr << "[!] Ricochet failed to load page: " << response_result.error() << "\n"; - return 1; - } + if ( !response_result.has_value() ) { + std::cerr << "[!] Ricochet failed to load page: " << response_result.error() << "\n"; + return 1; + } + + const auto& response = response_result.value(); - const auto& response = response_result.value(); - - const parser::HtmlLexer lexer; - const auto tokens = lexer.tokenize(response.body); + const parser::HtmlLexer lexer; + const auto tokens = lexer.tokenize( response.body ); - const parser::TreeBuilder builder; - const auto dom_root = builder.build(tokens); + const parser::TreeBuilder builder; + const auto dom_root = builder.build( tokens ); - std::cout << "\n[=== Abstract Syntax Tree (DOM) ===]\n"; - print_dom_tree(dom_root); + std::cout << "\n[=== Abstract Syntax Tree (DOM) ===]\n"; + print_dom_tree( dom_root ); - return 0; + return 0; } } // namespace ricochet::core diff --git a/src/net/http_client.cc b/src/net/http_client.cc index 256c1f2..d45de40 100644 --- a/src/net/http_client.cc +++ b/src/net/http_client.cc @@ -59,7 +59,8 @@ std::expected<ParsedUrl, std::string> parse_url( std::string_view url ) } } // namespace -std::expected<HttpResponse, std::string> HttpClient::fetch( std::string_view url ) const // NOLINT(readability-convert-member-functions-to-static) +std::expected<HttpResponse, std::string> HttpClient::fetch( // NOLINT(readability-convert-member-functions-to-static) + std::string_view url ) const { auto parsed_url = parse_url( url ); if ( !parsed_url.has_value() ) { @@ -75,11 +76,11 @@ std::expected<HttpResponse, std::string> HttpClient::fetch( std::string_view url socket.connect( server_addr ); const std::string request = std::format( "GET {} HTTP/1.1\r\n" - "Host: {}\r\n" - "User-Agent: Ricochet/0.1.0\r\n" - "Connection: close\r\n\r\n", - path, - host ); + "Host: {}\r\n" + "User-Agent: Ricochet/0.1.0\r\n" + "Connection: close\r\n\r\n", + path, + host ); size_t bytes_written = 0; while ( bytes_written < request.size() ) { 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 |
