#include #include #include #include #include #include #include #include #include #include #include #include extern "C" { #include } extern "C" const TSLanguage* tree_sitter_tsf(void); #include "../../src/language/ast/deserializer.hpp" #include "../../src/language/symbol/table.hpp" #include "./debug_printer.hpp" using namespace lsp::language; // ==================== 文件读取 ==================== std::string ReadFile(const std::string& filepath) { std::ifstream file(filepath); if (!file.is_open()) { throw std::runtime_error("Cannot open file: " + filepath); } std::ostringstream oss; oss << file.rdbuf(); return oss.str(); } // ==================== Tree-Sitter 解析器 ==================== class TreeSitterParser { public: TreeSitterParser() { parser_ = ts_parser_new(); if (!parser_) { throw std::runtime_error("Failed to create parser"); } if (!ts_parser_set_language(parser_, tree_sitter_tsf())) { ts_parser_delete(parser_); throw std::runtime_error("Failed to set language"); } } ~TreeSitterParser() { if (tree_) { ts_tree_delete(tree_); } if (parser_) { ts_parser_delete(parser_); } } TSTree* Parse(const std::string& source) { if (tree_) { ts_tree_delete(tree_); tree_ = nullptr; } tree_ = ts_parser_parse_string( parser_, nullptr, source.c_str(), source.length()); if (!tree_) { throw std::runtime_error("Failed to parse source"); } return tree_; } TSNode GetRootNode() { if (!tree_) { throw std::runtime_error("No tree available"); } return ts_tree_root_node(tree_); } private: TSParser* parser_ = nullptr; TSTree* tree_ = nullptr; }; // ==================== 命令行选项 ==================== struct Options { std::string input_file; std::string output_file; bool print_all = true; bool print_definitions = false; bool print_scopes = false; bool print_references = false; bool print_inheritance = false; bool print_calls = false; bool compact_mode = false; bool statistics_only = false; std::string search_symbol; bool verbose = false; bool show_ast = false; bool no_color = false; bool print_overview = false; }; void PrintUsage(const char* program_name) { std::cout << "Symbol Table Analyzer - Analyze source code symbols\n\n"; std::cout << "Usage: " << program_name << " [options]\n\n"; std::cout << "Options:\n"; std::cout << " -o, --output Write output to file instead of stdout\n"; std::cout << " -d, --definitions Print only symbol definitions\n"; std::cout << " -s, --scopes Print only scope hierarchy\n"; std::cout << " -r, --references Print only references\n"; std::cout << " -i, --inheritance Print only inheritance graph\n"; std::cout << " -c, --calls Print only call graph\n"; std::cout << " -C, --compact Use compact output format\n"; std::cout << " -S, --stats Print statistics only\n"; std::cout << " -O, --overview Print overview only\n"; std::cout << " -f, --find Search for a specific symbol\n"; std::cout << " -v, --verbose Enable verbose output\n"; std::cout << " -a, --ast Show AST structure\n"; std::cout << " --no-color Disable colored output\n"; std::cout << " -h, --help Show this help message\n\n"; std::cout << "Examples:\n"; std::cout << " " << program_name << " program.tsf\n"; std::cout << " " << program_name << " program.tsf -o symbols.txt\n"; std::cout << " " << program_name << " program.tsf --definitions --scopes\n"; std::cout << " " << program_name << " program.tsf --find MyClass\n"; std::cout << " " << program_name << " program.tsf --compact --stats\n"; std::cout << " " << program_name << " program.tsf --overview\n"; } bool ParseArguments(int argc, char* argv[], Options& options) { if (argc < 2) return false; options.input_file = argv[1]; bool any_specific_print = false; for (int i = 2; i < argc; ++i) { std::string arg = argv[i]; if (arg == "-h" || arg == "--help") { return false; } else if (arg == "-o" || arg == "--output") { if (i + 1 < argc) { options.output_file = argv[++i]; } else { std::cerr << "Error: " << arg << " requires an argument\n"; return false; } } else if (arg == "-d" || arg == "--definitions") { options.print_definitions = true; any_specific_print = true; } else if (arg == "-s" || arg == "--scopes") { options.print_scopes = true; any_specific_print = true; } else if (arg == "-r" || arg == "--references") { options.print_references = true; any_specific_print = true; } else if (arg == "-i" || arg == "--inheritance") { options.print_inheritance = true; any_specific_print = true; } else if (arg == "-c" || arg == "--calls") { options.print_calls = true; any_specific_print = true; } else if (arg == "-C" || arg == "--compact") { options.compact_mode = true; } else if (arg == "-S" || arg == "--stats") { options.statistics_only = true; any_specific_print = true; } else if (arg == "-O" || arg == "--overview") { options.print_overview = true; any_specific_print = true; } else if (arg == "-f" || arg == "--find") { if (i + 1 < argc) { options.search_symbol = argv[++i]; } else { std::cerr << "Error: " << arg << " requires an argument\n"; return false; } } else if (arg == "-v" || arg == "--verbose") { options.verbose = true; } else if (arg == "-a" || arg == "--ast") { options.show_ast = true; } else if (arg == "--no-color") { options.no_color = true; } else { std::cerr << "Error: Unknown option: " << arg << "\n"; return false; } } if (any_specific_print) options.print_all = false; return true; } std::string NodeKindToString(ast::NodeKind kind) { switch (kind) { case ast::NodeKind::kProgram: return "Program"; case ast::NodeKind::kFunctionDefinition: return "FunctionDefinition"; case ast::NodeKind::kFunctionDeclaration: return "FunctionDeclaration"; case ast::NodeKind::kClassDefinition: return "ClassDefinition"; case ast::NodeKind::kUnitDefinition: return "UnitDefinition"; case ast::NodeKind::kVarDeclaration: return "VarDeclaration"; case ast::NodeKind::kConstDeclaration: return "ConstDeclaration"; case ast::NodeKind::kIfStatement: return "IfStatement"; case ast::NodeKind::kForInStatement: return "ForInStatement"; case ast::NodeKind::kWhileStatement: return "WhileStatement"; default: return "Unknown"; } } void PrintASTStructure(const ast::ParseResult& parse_result, std::ostream& os) { os << "\n╔════════════════════════════════════════════════════════════╗\n"; os << "║ AST STRUCTURE ║\n"; os << "╚════════════════════════════════════════════════════════════╝\n\n"; os << "Statements: " << parse_result.root->statements.size() << "\n\n"; for (size_t i = 0; i < parse_result.root->statements.size(); ++i) { const auto& stmt = parse_result.root->statements[i]; if (!stmt) continue; os << "[" << i << "] " << NodeKindToString(stmt->kind) << " at [" << stmt->span.start_line << ":" << stmt->span.start_column << "]"; if (auto* func_def = dynamic_cast(stmt.get())) { os << " - Function: " << func_def->name; } else if (auto* class_def = dynamic_cast(stmt.get())) { os << " - Class: " << class_def->name; } else if (auto* unit_def = dynamic_cast(stmt.get())) { os << " - Unit: " << unit_def->name; } os << "\n"; } os << "\n"; } // ==================== 符号表构建器 ==================== class SymbolBuilder { public: explicit SymbolBuilder(symbol::SymbolTable& table) : table_(table) {} void Build(const ast::Program& program) { EnterScope(symbol::ScopeKind::kGlobal, program.span, std::nullopt); for (const auto& stmt : program.statements) { HandleStatement(stmt); } LeaveScope(); } private: static std::string ToLower(std::string s) { std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); return s; } std::optional ExtractTypeName(const std::optional& type) const { if (type) return type->name; return std::nullopt; } std::vector BuildParameters(const std::vector>& parameters) const { std::vector result; result.reserve(parameters.size()); for (const auto& param : parameters) { if (!param) continue; symbol::Parameter p; p.name = param->name; if (param->type) { p.type = param->type->name; } if (param->default_value) { p.default_value = ""; } result.push_back(std::move(p)); } return result; } symbol::SymbolId AddSymbol(symbol::Symbol symbol, const std::string& name) { auto id = table_.CreateSymbol(std::move(symbol)); auto scope = CurrentScope(); if (scope != symbol::kInvalidScopeId) { table_.AddSymbolToScope(scope, name, id); } name_index_[ToLower(name)] = id; return id; } void EnterScope(symbol::ScopeKind kind, const ast::Location& range, std::optional owner) { std::optional parent_scope = std::nullopt; if (!scope_stack_.empty()) { parent_scope = scope_stack_.back(); } auto id = table_.CreateScope(kind, range, parent_scope, owner); if (owner) { scope_by_owner_[*owner] = id; } scope_stack_.push_back(id); } void LeaveScope() { if (!scope_stack_.empty()) { scope_stack_.pop_back(); } } symbol::ScopeId CurrentScope() const { return scope_stack_.empty() ? symbol::kInvalidScopeId : scope_stack_.back(); } void HandleStatement(const ast::StatementPtr& stmt) { if (!stmt) return; switch (stmt->kind) { case ast::NodeKind::kFunctionDefinition: HandleFunctionDefinition(*static_cast(stmt.get())); break; case ast::NodeKind::kFunctionDeclaration: HandleFunctionDeclaration(*static_cast(stmt.get())); break; case ast::NodeKind::kMethodDeclaration: HandleMethodDeclaration(*static_cast(stmt.get()), std::nullopt, ast::AccessModifier::kPublic); break; case ast::NodeKind::kPropertyDeclaration: HandlePropertyDeclaration(*static_cast(stmt.get()), std::nullopt, ast::AccessModifier::kPublic); break; case ast::NodeKind::kClassDefinition: HandleClassDefinition(*static_cast(stmt.get())); break; case ast::NodeKind::kUnitDefinition: HandleUnitDefinition(*static_cast(stmt.get())); break; case ast::NodeKind::kVarDeclaration: HandleVariable(*static_cast(stmt.get()), symbol::VariableScope::kAutomatic); break; case ast::NodeKind::kStaticDeclaration: HandleStatic(*static_cast(stmt.get())); break; case ast::NodeKind::kGlobalDeclaration: HandleGlobalDeclaration(*static_cast(stmt.get())); break; case ast::NodeKind::kConstDeclaration: HandleConstant(*static_cast(stmt.get())); break; case ast::NodeKind::kExternalMethodDefinition: HandleExternalMethod(*static_cast(stmt.get())); break; case ast::NodeKind::kBlockStatement: HandleBlock(*static_cast(stmt.get()), true); break; case ast::NodeKind::kIfStatement: HandleIfStatement(*static_cast(stmt.get())); break; case ast::NodeKind::kForInStatement: HandleForInStatement(*static_cast(stmt.get())); break; case ast::NodeKind::kForToStatement: HandleForToStatement(*static_cast(stmt.get())); break; case ast::NodeKind::kWhileStatement: HandleWhileStatement(*static_cast(stmt.get())); break; case ast::NodeKind::kRepeatStatement: HandleRepeatStatement(*static_cast(stmt.get())); break; case ast::NodeKind::kCaseStatement: HandleCaseStatement(*static_cast(stmt.get())); break; case ast::NodeKind::kTryStatement: HandleTryStatement(*static_cast(stmt.get())); break; default: break; } } void HandleFunctionDeclaration(ast::FunctionDeclaration& node) { symbol::Function fn; fn.name = node.name; fn.selection_range = node.span; fn.range = node.span; fn.declaration_range = node.span; fn.parameters = BuildParameters(node.parameters); fn.return_type = ExtractTypeName(node.return_type); AddSymbol(symbol::Symbol(std::move(fn)), node.name); } void HandleFunctionDefinition(ast::FunctionDefinition& node) { symbol::Function fn; fn.name = node.name; fn.selection_range = node.location; fn.range = node.span; fn.declaration_range = node.location; fn.implementation_range = node.location; fn.parameters = BuildParameters(node.parameters); fn.return_type = ExtractTypeName(node.return_type); auto fn_id = AddSymbol(symbol::Symbol(std::move(fn)), node.name); if (node.body) { EnterScope(symbol::ScopeKind::kFunction, node.body->span, fn_id); for (const auto& param : node.parameters) { if (!param) continue; symbol::Variable var; var.name = param->name; var.selection_range = param->location; var.range = param->location; var.storage = symbol::VariableScope::kParameter; var.type = param->type ? std::optional(param->type->name) : std::nullopt; AddSymbol(symbol::Symbol(std::move(var)), param->name); } HandleBlock(*node.body, false); LeaveScope(); } } void HandleClassDefinition(ast::ClassDefinition& node) { symbol::Class cls; cls.name = node.name; cls.selection_range = node.location; cls.range = node.span; auto class_id = AddSymbol(symbol::Symbol(std::move(cls)), node.name); EnterScope(symbol::ScopeKind::kClass, node.span, class_id); for (const auto& parent : node.parent_classes) { auto it = name_index_.find(ToLower(parent.name)); if (it != name_index_.end()) { table_.AddInheritance(class_id, it->second); } } for (auto& member : node.members) { if (member) HandleClassMember(*member); } LeaveScope(); } void HandleClassMember(ast::ClassMember& member) { std::visit( [&](auto& ptr) { using T = std::decay_t; if (!ptr) return; if constexpr (std::is_same_v>) { HandleFieldDeclaration(*ptr, member.access_modifier, false); } else if constexpr (std::is_same_v>) { HandleStatic(*ptr, member.access_modifier, true); } else if constexpr (std::is_same_v>) { HandleMethodDeclaration(*ptr, CurrentScopeOwner(), member.access_modifier); } else if constexpr (std::is_same_v>) { HandlePropertyDeclaration(*ptr, CurrentScopeOwner(), member.access_modifier); } }, member.member); } std::optional CurrentScopeOwner() const { if (scope_stack_.empty()) return std::nullopt; auto current = scope_stack_.back(); for (const auto& [owner, scope_id] : scope_by_owner_) { if (scope_id == current) { return owner; } } return std::nullopt; } void HandleMethodDeclaration(ast::MethodDeclaration& node, std::optional /*owner*/, ast::AccessModifier access) { symbol::Method method; method.name = node.name; method.selection_range = node.location; method.range = node.span; method.declaration_range = node.location; if (node.body) { method.implementation_range = node.body->span; } method.method_kind = node.method_kind; method.method_modifier = node.modifier; method.is_static = node.is_static; method.access = access; method.parameters = BuildParameters(node.parameters); method.return_type = ExtractTypeName(node.return_type); auto method_id = AddSymbol(symbol::Symbol(std::move(method)), node.name); if (node.body) { EnterScope(symbol::ScopeKind::kFunction, node.body->span, method_id); for (const auto& param : node.parameters) { if (!param) continue; symbol::Variable var; var.name = param->name; var.selection_range = param->location; var.range = param->location; var.storage = symbol::VariableScope::kParameter; var.type = param->type ? std::optional(param->type->name) : std::nullopt; AddSymbol(symbol::Symbol(std::move(var)), param->name); } HandleBlock(*node.body, false); LeaveScope(); } } void HandlePropertyDeclaration(ast::PropertyDeclaration& node, std::optional, ast::AccessModifier access) { symbol::Property property; property.name = node.name; property.selection_range = node.location; property.range = node.span; property.access = access; property.type = ExtractTypeName(node.type); AddSymbol(symbol::Symbol(std::move(property)), node.name); } void HandleFieldDeclaration(ast::FieldDeclaration& node, ast::AccessModifier access, bool is_static) { symbol::Field field; field.name = node.name; field.selection_range = node.location; field.range = node.span; field.access = access; field.reference_modifier = node.reference_modifier; field.type = ExtractTypeName(node.type); field.is_static = is_static; AddSymbol(symbol::Symbol(std::move(field)), node.name); } void HandleVariable(ast::VarDeclaration& node, symbol::VariableScope storage) { symbol::Variable var; var.name = node.name; var.selection_range = node.location; var.range = node.span; var.storage = storage; var.type = ExtractTypeName(node.type); var.has_initializer = node.initializer.has_value(); AddSymbol(symbol::Symbol(std::move(var)), node.name); } void HandleStatic(ast::StaticDeclaration& node, ast::AccessModifier access = ast::AccessModifier::kPublic, bool is_class_member = false) { if (is_class_member) { symbol::Field field; field.name = node.name; field.selection_range = node.location; field.range = node.span; field.access = access; field.reference_modifier = node.reference_modifier; field.type = ExtractTypeName(node.type); field.is_static = true; AddSymbol(symbol::Symbol(std::move(field)), node.name); } else { symbol::Variable var; var.name = node.name; var.selection_range = node.location; var.range = node.span; var.storage = symbol::VariableScope::kStatic; var.reference_modifier = node.reference_modifier; var.type = ExtractTypeName(node.type); var.has_initializer = node.initializer.has_value(); AddSymbol(symbol::Symbol(std::move(var)), node.name); } } void HandleConstant(ast::ConstDeclaration& node) { symbol::Constant constant; constant.name = node.name; constant.selection_range = node.location; constant.range = node.span; constant.type = ExtractTypeName(node.type); constant.value = ""; AddSymbol(symbol::Symbol(std::move(constant)), node.name); } void HandleGlobalDeclaration(ast::GlobalDeclaration& node) { symbol::Variable var; var.name = node.name; var.selection_range = node.location; var.range = node.span; var.storage = symbol::VariableScope::kGlobal; var.type = ExtractTypeName(node.type); var.has_initializer = node.initializer.has_value(); AddSymbol(symbol::Symbol(std::move(var)), node.name); } void HandleExternalMethod(ast::ExternalMethodDefinition& node) { // Try to find owner class scope auto owner_it = name_index_.find(ToLower(node.owner_class.name)); std::optional owner_id; std::optional owner_scope; if (owner_it != name_index_.end()) { owner_id = owner_it->second; auto scope_it = scope_by_owner_.find(*owner_id); if (scope_it != scope_by_owner_.end()) { owner_scope = scope_it->second; } } symbol::Method method; method.name = node.name; method.selection_range = node.location; method.range = node.span; method.declaration_range = node.location; method.implementation_range = node.location; method.method_kind = node.method_kind; method.method_modifier = node.modifier; method.is_static = node.is_static; method.parameters = BuildParameters(node.parameters); method.return_type = ExtractTypeName(node.return_type); bool pushed_owner = false; if (owner_scope) { scope_stack_.push_back(*owner_scope); pushed_owner = true; } auto method_id = AddSymbol(symbol::Symbol(std::move(method)), node.name); if (node.body) { EnterScope(symbol::ScopeKind::kFunction, node.body->span, method_id); HandleBlock(*node.body, false); LeaveScope(); } if (pushed_owner) { scope_stack_.pop_back(); } } void HandleUnitDefinition(ast::UnitDefinition& node) { symbol::Unit unit; unit.name = node.name; unit.selection_range = node.location; unit.range = node.span; auto unit_id = AddSymbol(symbol::Symbol(std::move(unit)), node.name); EnterScope(symbol::ScopeKind::kUnit, node.span, unit_id); for (auto& stmt : node.interface_statements) { HandleStatement(stmt); } for (auto& stmt : node.implementation_statements) { HandleStatement(stmt); } LeaveScope(); } void HandleBlock(ast::BlockStatement& block, bool create_scope) { std::optional scope_holder; if (create_scope) { EnterScope(symbol::ScopeKind::kBlock, block.span, std::nullopt); scope_holder = CurrentScope(); } for (auto& stmt : block.statements) { HandleStatement(stmt); } if (scope_holder) { LeaveScope(); } } void HandleIfStatement(ast::IfStatement& node) { for (auto& branch : node.branches) { if (branch.body) HandleStatement(branch.body); } if (node.else_body && *node.else_body) { HandleStatement(*node.else_body); } } void HandleForInStatement(ast::ForInStatement& node) { if (node.body) HandleStatement(node.body); } void HandleForToStatement(ast::ForToStatement& node) { if (node.body) HandleStatement(node.body); } void HandleWhileStatement(ast::WhileStatement& node) { if (node.body) HandleStatement(node.body); } void HandleRepeatStatement(ast::RepeatStatement& node) { for (auto& stmt : node.body) { HandleStatement(stmt); } } void HandleCaseStatement(ast::CaseStatement& node) { for (auto& branch : node.branches) { if (branch.body) HandleStatement(branch.body); } if (node.else_body && *node.else_body) { HandleStatement(*node.else_body); } } void HandleTryStatement(ast::TryStatement& node) { if (node.try_body) HandleBlock(*node.try_body, true); if (node.except_body) HandleBlock(*node.except_body, true); } private: symbol::SymbolTable& table_; std::vector scope_stack_; std::unordered_map name_index_; std::unordered_map scope_by_owner_; }; // ==================== 主分析函数 ==================== void AnalyzeFile(const Options& options) { if (!options.compact_mode) { std::cout << "\n╔════════════════════════════════════════════════════════════╗\n"; std::cout << "║ SYMBOL TABLE ANALYZER ║\n"; std::cout << "╚════════════════════════════════════════════════════════════╝\n\n"; std::cout << "Input file: " << options.input_file << "\n"; } if (options.verbose) std::cout << "Reading file...\n"; std::string source = ReadFile(options.input_file); if (options.verbose) { std::cout << "File size: " << source.length() << " bytes\n"; std::cout << "----------------------------------------\n"; std::cout << source << "\n"; std::cout << "----------------------------------------\n\n"; } if (options.verbose) std::cout << "Parsing with Tree-Sitter...\n"; TreeSitterParser ts_parser; [[maybe_unused]] TSTree* tree = ts_parser.Parse(source); TSNode root = ts_parser.GetRootNode(); if (options.verbose) { std::cout << "Root node type: " << ts_node_type(root) << "\n"; std::cout << "Root node child count: " << ts_node_child_count(root) << "\n\n"; } if (options.verbose) std::cout << "Deserializing to AST...\n"; ast::Deserializer deserializer; ast::ParseResult parse_result = deserializer.Parse(root, source); if (parse_result.HasErrors()) { std::cerr << "\n⚠️ Parse Errors:\n"; for (const auto& error : parse_result.errors) { std::cerr << " [" << error.location.start_line << ":" << error.location.start_column << "] " << error.message << "\n"; } std::cerr << "\n"; } if (options.show_ast) { PrintASTStructure(parse_result, std::cout); } if (options.verbose) std::cout << "Building symbol table...\n"; symbol::SymbolTable table; auto start = std::chrono::high_resolution_clock::now(); SymbolBuilder builder(table); builder.Build(*parse_result.root); auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start); if (options.verbose) std::cout << "Symbol table built in " << duration.count() << " ms\n\n"; std::ostream* out = &std::cout; std::ofstream file_out; if (!options.output_file.empty()) { file_out.open(options.output_file); if (!file_out.is_open()) { std::cerr << "Error: Cannot write to file: " << options.output_file << "\n"; return; } out = &file_out; *out << "Symbol Table Analysis\n"; *out << "Source: " << options.input_file << "\n"; auto now = std::chrono::system_clock::now(); auto time = std::chrono::system_clock::to_time_t(now); *out << "Generated: " << std::ctime(&time); *out << std::string(80, '=') << "\n\n"; } symbol::debug::PrintOptions print_opts; if (options.compact_mode) print_opts = symbol::debug::PrintOptions::Compact(); else if (options.verbose) print_opts = symbol::debug::PrintOptions::Verbose(); else print_opts = symbol::debug::PrintOptions::Default(); if (options.no_color || !options.output_file.empty()) print_opts.use_color = false; print_opts.show_references = options.print_references || options.verbose; symbol::debug::DebugPrinter printer(table, print_opts); if (!options.search_symbol.empty()) { printer.FindAndPrint(options.search_symbol, *out); } else if (options.statistics_only) { printer.PrintStatistics(*out); } else if (options.print_overview) { printer.PrintOverview(*out); } else if (options.compact_mode) { symbol::debug::PrintCompact(table, *out); } else if (options.print_all) { printer.PrintAll(*out); } else { bool printed_anything = false; if (options.print_definitions) { printer.PrintSymbolList(*out); printed_anything = true; } if (options.print_scopes) { printer.PrintScopeHierarchy(*out); printed_anything = true; } if (options.print_references) { printer.PrintAllReferences(*out); printed_anything = true; } if (options.print_inheritance) { printer.PrintAllInheritance(*out); printed_anything = true; } if (options.print_calls) { printer.PrintAllCalls(*out); printed_anything = true; } if (printed_anything) { *out << "\n"; } printer.PrintStatistics(*out); } if (file_out.is_open()) { file_out.close(); std::cout << "✓ Symbol table exported to: " << options.output_file << "\n"; } if (!options.compact_mode) { std::cout << "\n========================================\n"; std::cout << "Smary:\n"; std::cout << " File: " << options.input_file << "\n"; std::cout << " Size: " << source.length() << " bytes\n"; std::cout << " Lines: " << std::count(source.begin(), source.end(), '\n') + 1 << "\n"; std::cout << " Statements: " << parse_result.root->statements.size() << "\n"; std::cout << " Symbols: " << table.all_definitions().size() << "\n"; std::cout << " Scopes: " << table.scopes().all_scopes().size() << "\n"; std::cout << " Parse Errors: " << parse_result.errors.size() << "\n"; std::cout << " Build Time: " << duration.count() << " ms\n"; if (parse_result.HasErrors()) { std::cout << " Status: ⚠️ COMPLETED WITH ERRORS\n"; } else { std::cout << " Status: ✓ SUCCESS\n"; } std::cout << "========================================\n\n"; } } // ==================== 主程序 ==================== int main(int argc, char* argv[]) { Options options; if (!ParseArguments(argc, argv, options)) { PrintUsage(argv[0]); return 1; } try { AnalyzeFile(options); return 0; } catch (const std::exception& e) { std::cerr << "❌ Fatal error: " << e.what() << "\n"; return 1; } }