1135 lines
34 KiB
C++
1135 lines
34 KiB
C++
#include <algorithm>
|
|
#include <chrono>
|
|
#include <cctype>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <optional>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <vector>
|
|
#include <ctime>
|
|
|
|
extern "C" {
|
|
#include <tree_sitter/api.h>
|
|
}
|
|
|
|
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 << " <input_file> [options]\n\n";
|
|
std::cout << "Options:\n";
|
|
std::cout << " -o, --output <file> 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 <name> 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<ast::FunctionDefinition*>(stmt.get()))
|
|
{
|
|
os << " - Function: " << func_def->name;
|
|
}
|
|
else if (auto* class_def = dynamic_cast<ast::ClassDefinition*>(stmt.get()))
|
|
{
|
|
os << " - Class: " << class_def->name;
|
|
}
|
|
else if (auto* unit_def = dynamic_cast<ast::UnitDefinition*>(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<char>(std::tolower(c)); });
|
|
return s;
|
|
}
|
|
|
|
std::optional<std::string> ExtractTypeName(const std::optional<ast::TypeAnnotation>& type) const
|
|
{
|
|
if (type)
|
|
return type->name;
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::vector<symbol::Parameter> BuildParameters(const std::vector<std::unique_ptr<ast::Parameter>>& parameters) const
|
|
{
|
|
std::vector<symbol::Parameter> 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 = "<expr>";
|
|
}
|
|
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<symbol::SymbolId> owner)
|
|
{
|
|
std::optional<symbol::ScopeId> 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<ast::FunctionDefinition*>(stmt.get()));
|
|
break;
|
|
case ast::NodeKind::kFunctionDeclaration:
|
|
HandleFunctionDeclaration(*static_cast<ast::FunctionDeclaration*>(stmt.get()));
|
|
break;
|
|
case ast::NodeKind::kMethodDeclaration:
|
|
HandleMethodDeclaration(*static_cast<ast::MethodDeclaration*>(stmt.get()), std::nullopt, ast::AccessModifier::kPublic);
|
|
break;
|
|
case ast::NodeKind::kPropertyDeclaration:
|
|
HandlePropertyDeclaration(*static_cast<ast::PropertyDeclaration*>(stmt.get()), std::nullopt, ast::AccessModifier::kPublic);
|
|
break;
|
|
case ast::NodeKind::kClassDefinition:
|
|
HandleClassDefinition(*static_cast<ast::ClassDefinition*>(stmt.get()));
|
|
break;
|
|
case ast::NodeKind::kUnitDefinition:
|
|
HandleUnitDefinition(*static_cast<ast::UnitDefinition*>(stmt.get()));
|
|
break;
|
|
case ast::NodeKind::kVarDeclaration:
|
|
HandleVariable(*static_cast<ast::VarDeclaration*>(stmt.get()), symbol::VariableScope::kAutomatic);
|
|
break;
|
|
case ast::NodeKind::kStaticDeclaration:
|
|
HandleStatic(*static_cast<ast::StaticDeclaration*>(stmt.get()));
|
|
break;
|
|
case ast::NodeKind::kGlobalDeclaration:
|
|
HandleGlobalDeclaration(*static_cast<ast::GlobalDeclaration*>(stmt.get()));
|
|
break;
|
|
case ast::NodeKind::kConstDeclaration:
|
|
HandleConstant(*static_cast<ast::ConstDeclaration*>(stmt.get()));
|
|
break;
|
|
case ast::NodeKind::kExternalMethodDefinition:
|
|
HandleExternalMethod(*static_cast<ast::ExternalMethodDefinition*>(stmt.get()));
|
|
break;
|
|
case ast::NodeKind::kBlockStatement:
|
|
HandleBlock(*static_cast<ast::BlockStatement*>(stmt.get()), true);
|
|
break;
|
|
case ast::NodeKind::kIfStatement:
|
|
HandleIfStatement(*static_cast<ast::IfStatement*>(stmt.get()));
|
|
break;
|
|
case ast::NodeKind::kForInStatement:
|
|
HandleForInStatement(*static_cast<ast::ForInStatement*>(stmt.get()));
|
|
break;
|
|
case ast::NodeKind::kForToStatement:
|
|
HandleForToStatement(*static_cast<ast::ForToStatement*>(stmt.get()));
|
|
break;
|
|
case ast::NodeKind::kWhileStatement:
|
|
HandleWhileStatement(*static_cast<ast::WhileStatement*>(stmt.get()));
|
|
break;
|
|
case ast::NodeKind::kRepeatStatement:
|
|
HandleRepeatStatement(*static_cast<ast::RepeatStatement*>(stmt.get()));
|
|
break;
|
|
case ast::NodeKind::kCaseStatement:
|
|
HandleCaseStatement(*static_cast<ast::CaseStatement*>(stmt.get()));
|
|
break;
|
|
case ast::NodeKind::kTryStatement:
|
|
HandleTryStatement(*static_cast<ast::TryStatement*>(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<std::string>(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<decltype(ptr)>;
|
|
if (!ptr)
|
|
return;
|
|
|
|
if constexpr (std::is_same_v<T, std::unique_ptr<ast::FieldDeclaration>>)
|
|
{
|
|
HandleFieldDeclaration(*ptr, member.access_modifier, false);
|
|
}
|
|
else if constexpr (std::is_same_v<T, std::unique_ptr<ast::StaticDeclaration>>)
|
|
{
|
|
HandleStatic(*ptr, member.access_modifier, true);
|
|
}
|
|
else if constexpr (std::is_same_v<T, std::unique_ptr<ast::MethodDeclaration>>)
|
|
{
|
|
HandleMethodDeclaration(*ptr, CurrentScopeOwner(), member.access_modifier);
|
|
}
|
|
else if constexpr (std::is_same_v<T, std::unique_ptr<ast::PropertyDeclaration>>)
|
|
{
|
|
HandlePropertyDeclaration(*ptr, CurrentScopeOwner(), member.access_modifier);
|
|
}
|
|
},
|
|
member.member);
|
|
}
|
|
|
|
std::optional<symbol::SymbolId> 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<symbol::SymbolId> /*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<std::string>(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<symbol::SymbolId>, 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 = "<const>";
|
|
|
|
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<symbol::SymbolId> owner_id;
|
|
std::optional<symbol::ScopeId> 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<symbol::ScopeId> 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<symbol::ScopeId> scope_stack_;
|
|
std::unordered_map<std::string, symbol::SymbolId> name_index_;
|
|
std::unordered_map<symbol::SymbolId, symbol::ScopeId> 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<std::chrono::milliseconds>(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;
|
|
}
|
|
}
|