parent
8f25f212a7
commit
cee6e614bf
|
|
@ -1,10 +1,91 @@
|
|||
#include <mutex>
|
||||
#include <variant>
|
||||
#include "../../utils/string.hpp"
|
||||
#include "./tree_sitter_utils.hpp"
|
||||
#include "./detail.hpp"
|
||||
|
||||
namespace lsp::language::ast::detail
|
||||
{
|
||||
namespace
|
||||
{
|
||||
template<typename... Ts>
|
||||
struct Overloaded : Ts...
|
||||
{
|
||||
using Ts::operator()...;
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
Overloaded(Ts...) -> Overloaded<Ts...>;
|
||||
|
||||
struct AccessModifierToken
|
||||
{
|
||||
AccessModifier modifier;
|
||||
};
|
||||
|
||||
struct ReferenceModifierToken
|
||||
{
|
||||
ReferenceModifier modifier;
|
||||
};
|
||||
|
||||
struct ClassMemberNodeToken
|
||||
{
|
||||
std::unique_ptr<ClassMember> member;
|
||||
};
|
||||
|
||||
using ClassMemberToken = std::variant<AccessModifierToken, ReferenceModifierToken, ClassMemberNodeToken>;
|
||||
|
||||
class ClassMemberStateMachine
|
||||
{
|
||||
public:
|
||||
ClassMemberStateMachine(AccessModifier access, ReferenceModifier reference) :
|
||||
current_access_(access),
|
||||
current_reference_(reference)
|
||||
{
|
||||
}
|
||||
|
||||
AccessModifier CurrentAccess() const { return current_access_; }
|
||||
ReferenceModifier CurrentReference() const { return current_reference_; }
|
||||
|
||||
std::unique_ptr<ClassMember> Consume(ClassMemberToken&& token)
|
||||
{
|
||||
return std::visit(Overloaded{
|
||||
[this](AccessModifierToken& t) -> std::unique_ptr<ClassMember> {
|
||||
current_access_ = t.modifier;
|
||||
return nullptr;
|
||||
},
|
||||
[this](ReferenceModifierToken& t) -> std::unique_ptr<ClassMember> {
|
||||
current_reference_ = t.modifier;
|
||||
return nullptr;
|
||||
},
|
||||
[this](ClassMemberNodeToken& t) -> std::unique_ptr<ClassMember> {
|
||||
if (t.member)
|
||||
t.member->access_modifier = current_access_;
|
||||
return std::move(t.member);
|
||||
} },
|
||||
token);
|
||||
}
|
||||
|
||||
private:
|
||||
AccessModifier current_access_;
|
||||
ReferenceModifier current_reference_;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
std::unique_ptr<ClassMember> WrapClassMember(Location span, std::unique_ptr<T> decl)
|
||||
{
|
||||
if (!decl)
|
||||
return nullptr;
|
||||
|
||||
auto member = MakeNode<ClassMember>();
|
||||
member->span = span;
|
||||
member->member = std::move(decl);
|
||||
return member;
|
||||
}
|
||||
|
||||
std::vector<ClassMemberToken> BuildVariableDeclarationTokens(TSNode node, ParseContext& ctx, const ClassMemberStateMachine& state);
|
||||
std::vector<ClassMemberToken> TokenizeClassMemberChild(TSNode node, ParseContext& ctx, const ClassMemberStateMachine& state);
|
||||
}
|
||||
|
||||
void RegisterStatementParsers()
|
||||
{
|
||||
static std::once_flag once;
|
||||
|
|
@ -1224,8 +1305,7 @@ namespace lsp::language::ast::detail
|
|||
TSNode body_node = ts_node_child_by_field_name(node, "body", 4);
|
||||
if (!ts_node_is_null(body_node))
|
||||
{
|
||||
AccessModifier current_access = AccessModifier::kPublic;
|
||||
ReferenceModifier current_reference = ReferenceModifier::kNone;
|
||||
ClassMemberStateMachine member_state(AccessModifier::kPublic, ReferenceModifier::kNone);
|
||||
|
||||
uint32_t body_count = ts_node_child_count(body_node);
|
||||
for (uint32_t i = 0; i < body_count; i++)
|
||||
|
|
@ -1253,13 +1333,11 @@ namespace lsp::language::ast::detail
|
|||
if (!ts_node_is_named(cm_child))
|
||||
continue;
|
||||
|
||||
auto results = ParseClassMember(cm_child, ctx, current_access, current_reference);
|
||||
for (auto& result : results)
|
||||
auto tokens = TokenizeClassMemberChild(cm_child, ctx, member_state);
|
||||
for (auto& token : tokens)
|
||||
{
|
||||
current_access = result.access_modifier;
|
||||
current_reference = result.reference_modifier;
|
||||
if (result.member)
|
||||
class_def->members.push_back(std::move(result.member));
|
||||
if (auto member = member_state.Consume(std::move(token)))
|
||||
class_def->members.push_back(std::move(member));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2112,60 +2190,41 @@ namespace lsp::language::ast::detail
|
|||
return prop;
|
||||
}
|
||||
|
||||
std::vector<ClassMemberParseResult> ParseClassMember(TSNode node, ParseContext& ctx, AccessModifier current_access, ReferenceModifier current_reference)
|
||||
namespace
|
||||
{
|
||||
std::vector<ClassMemberParseResult> results;
|
||||
std::string_view node_type = ts_node_type(node);
|
||||
|
||||
// 如果是访问修饰符,返回新的访问级别
|
||||
if (node_type == "access_modifier")
|
||||
std::vector<ClassMemberToken> BuildVariableDeclarationTokens(TSNode node, ParseContext& ctx, const ClassMemberStateMachine& state)
|
||||
{
|
||||
ClassMemberParseResult result;
|
||||
result.access_modifier = ParseAccessModifier(node, ctx);
|
||||
results.push_back(std::move(result));
|
||||
return results;
|
||||
}
|
||||
|
||||
// 如果是引用修饰符,返回新的引用修饰符
|
||||
if (node_type == "reference_modifier")
|
||||
{
|
||||
ClassMemberParseResult result;
|
||||
result.reference_modifier = ParseReferenceModifier(node, ctx);
|
||||
results.push_back(std::move(result));
|
||||
return results;
|
||||
}
|
||||
|
||||
// 变量声明
|
||||
if (node_type == "variable_declaration")
|
||||
{
|
||||
bool is_static = false;
|
||||
ReferenceModifier ref_mod = current_reference;
|
||||
std::vector<ClassMemberToken> tokens;
|
||||
ReferenceModifier ref_mod = state.CurrentReference();
|
||||
|
||||
TSNode ref_node = ts_node_child_by_field_name(node, "ref_modifier", 12);
|
||||
if (!ts_node_is_null(ref_node))
|
||||
{
|
||||
ref_mod = ParseReferenceModifier(ref_node, ctx);
|
||||
}
|
||||
|
||||
TSNode variable_node = ts_node_child_by_field_name(node, "variable", 8);
|
||||
if (ts_node_is_null(variable_node))
|
||||
return results;
|
||||
return tokens;
|
||||
|
||||
TSNode member_var_node = ts_node_child(variable_node, 0);
|
||||
if (ts_node_is_null(member_var_node))
|
||||
return results;
|
||||
return tokens;
|
||||
|
||||
std::string_view child_type = ts_node_type(member_var_node);
|
||||
bool is_static = false;
|
||||
if (child_type == "static_member_variable")
|
||||
is_static = true;
|
||||
else if (child_type == "field_member_variable")
|
||||
is_static = false;
|
||||
else
|
||||
return results;
|
||||
return tokens;
|
||||
|
||||
// 收集所有名称
|
||||
std::vector<std::string> names;
|
||||
std::vector<Location> locations;
|
||||
struct NamedVariable
|
||||
{
|
||||
std::string name;
|
||||
Location location;
|
||||
};
|
||||
|
||||
std::vector<NamedVariable> names;
|
||||
uint32_t count = ts_node_child_count(member_var_node);
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
{
|
||||
|
|
@ -2174,11 +2233,15 @@ namespace lsp::language::ast::detail
|
|||
continue;
|
||||
|
||||
TSNode name_node = ts_node_child(member_var_node, i);
|
||||
names.push_back(ts::Text(name_node, ctx.Source()));
|
||||
locations.push_back(ts::NodeLocation(name_node));
|
||||
names.push_back(NamedVariable{
|
||||
ts::Text(name_node, ctx.Source()),
|
||||
ts::NodeLocation(name_node)
|
||||
});
|
||||
}
|
||||
|
||||
// 类型和初始值
|
||||
if (names.empty())
|
||||
return tokens;
|
||||
|
||||
std::optional<TypeAnnotation> type_ann;
|
||||
TSNode type_node = ts_node_child_by_field_name(member_var_node, "type", 4);
|
||||
if (!ts_node_is_null(type_node))
|
||||
|
|
@ -2192,87 +2255,93 @@ namespace lsp::language::ast::detail
|
|||
ExpressionPtr initial_value;
|
||||
TSNode value_node = ts_node_child_by_field_name(member_var_node, "value", 5);
|
||||
if (!ts_node_is_null(value_node))
|
||||
{
|
||||
initial_value = ParseExpression(value_node, ctx);
|
||||
}
|
||||
|
||||
// 为每个名称创建成员
|
||||
for (size_t i = 0; i < names.size(); i++)
|
||||
{
|
||||
auto member = MakeNode<ClassMember>();
|
||||
member->span = locations[i];
|
||||
member->access_modifier = current_access;
|
||||
Location span = names[i].location;
|
||||
|
||||
if (is_static)
|
||||
{
|
||||
auto decl = MakeNode<StaticDeclaration>();
|
||||
decl->span = locations[i];
|
||||
decl->name = names[i];
|
||||
decl->location = locations[i];
|
||||
decl->span = span;
|
||||
decl->name = names[i].name;
|
||||
decl->location = span;
|
||||
decl->reference_modifier = ref_mod;
|
||||
|
||||
// 只有最后一个变量有类型和初始值
|
||||
if (i == names.size() - 1)
|
||||
{
|
||||
decl->type = type_ann;
|
||||
decl->initializer = std::move(initial_value);
|
||||
}
|
||||
|
||||
member->member = std::move(decl);
|
||||
if (auto member = WrapClassMember(span, std::move(decl)))
|
||||
tokens.emplace_back(ClassMemberNodeToken{ std::move(member) });
|
||||
}
|
||||
else
|
||||
{
|
||||
auto decl = MakeNode<FieldDeclaration>();
|
||||
decl->span = locations[i];
|
||||
decl->name = names[i];
|
||||
decl->location = locations[i];
|
||||
decl->span = span;
|
||||
decl->name = names[i].name;
|
||||
decl->location = span;
|
||||
decl->reference_modifier = ref_mod;
|
||||
|
||||
// 只有最后一个变量有类型和初始值
|
||||
if (i == names.size() - 1)
|
||||
{
|
||||
decl->type = type_ann;
|
||||
decl->initializer = std::move(initial_value);
|
||||
}
|
||||
|
||||
member->member = std::move(decl);
|
||||
if (auto member = WrapClassMember(span, std::move(decl)))
|
||||
tokens.emplace_back(ClassMemberNodeToken{ std::move(member) });
|
||||
}
|
||||
|
||||
ClassMemberParseResult result;
|
||||
result.member = std::move(member);
|
||||
results.push_back(std::move(result));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
// 方法声明
|
||||
else if (node_type == "method_declaration")
|
||||
|
||||
std::vector<ClassMemberToken> TokenizeClassMemberChild(TSNode node, ParseContext& ctx, const ClassMemberStateMachine& state)
|
||||
{
|
||||
auto member = MakeNode<ClassMember>();
|
||||
member->span = ts::NodeLocation(node);
|
||||
member->access_modifier = current_access;
|
||||
std::vector<ClassMemberToken> tokens;
|
||||
std::string_view node_type = ts_node_type(node);
|
||||
|
||||
auto method = ParseMethodDeclaration(node, ctx);
|
||||
member->member = std::move(method);
|
||||
if (node_type == "access_modifier")
|
||||
{
|
||||
tokens.emplace_back(AccessModifierToken{ ParseAccessModifier(node, ctx) });
|
||||
return tokens;
|
||||
}
|
||||
|
||||
ClassMemberParseResult result;
|
||||
result.member = std::move(member);
|
||||
results.push_back(std::move(result));
|
||||
if (node_type == "reference_modifier")
|
||||
{
|
||||
tokens.emplace_back(ReferenceModifierToken{ ParseReferenceModifier(node, ctx) });
|
||||
return tokens;
|
||||
}
|
||||
|
||||
if (node_type == "variable_declaration")
|
||||
return BuildVariableDeclarationTokens(node, ctx, state);
|
||||
|
||||
if (node_type == "method_declaration")
|
||||
{
|
||||
if (auto method = ParseMethodDeclaration(node, ctx))
|
||||
{
|
||||
if (auto member = WrapClassMember(ts::NodeLocation(node), std::move(method)))
|
||||
tokens.emplace_back(ClassMemberNodeToken{ std::move(member) });
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
if (node_type == "property_declaration")
|
||||
{
|
||||
if (auto property = ParsePropertyDeclaration(node, ctx))
|
||||
{
|
||||
if (auto member = WrapClassMember(ts::NodeLocation(node), std::move(property)))
|
||||
tokens.emplace_back(ClassMemberNodeToken{ std::move(member) });
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
// 属性声明
|
||||
else if (node_type == "property_declaration")
|
||||
{
|
||||
auto member = MakeNode<ClassMember>();
|
||||
member->span = ts::NodeLocation(node);
|
||||
member->access_modifier = current_access;
|
||||
|
||||
auto prop = ParsePropertyDeclaration(node, ctx);
|
||||
member->member = std::move(prop);
|
||||
|
||||
ClassMemberParseResult result;
|
||||
result.member = std::move(member);
|
||||
results.push_back(std::move(result));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
template<typename DeclType>
|
||||
|
|
|
|||
|
|
@ -29,15 +29,6 @@ namespace lsp::language::ast::detail
|
|||
std::vector<ParseError>& errors_;
|
||||
};
|
||||
|
||||
// ===== 类成员解析结果 =====
|
||||
|
||||
struct ClassMemberParseResult
|
||||
{
|
||||
std::unique_ptr<ClassMember> member;
|
||||
AccessModifier access_modifier = AccessModifier::kPublic;
|
||||
ReferenceModifier reference_modifier = ReferenceModifier::kNone;
|
||||
};
|
||||
|
||||
// ===== 语句解析器注册表 =====
|
||||
|
||||
class StatementParserRegistry
|
||||
|
|
@ -91,9 +82,6 @@ namespace lsp::language::ast::detail
|
|||
std::unique_ptr<MethodDeclaration> ParseMethodDeclaration(TSNode node, ParseContext& ctx);
|
||||
std::unique_ptr<PropertyDeclaration> ParsePropertyDeclaration(TSNode node, ParseContext& ctx);
|
||||
|
||||
// 类成员解析
|
||||
std::vector<ClassMemberParseResult> ParseClassMember(TSNode node, ParseContext& ctx, AccessModifier current_access, ReferenceModifier current_reference);
|
||||
|
||||
// ===== 通用解析函数 =====
|
||||
std::vector<std::unique_ptr<Parameter>> ParseParameters(TSNode params_node, ParseContext& ctx);
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
// 符号表构建器
|
||||
// Symbol table builder - responsible for stable traversal logic only
|
||||
class Builder : public ast::ASTVisitor
|
||||
{
|
||||
public:
|
||||
|
|
@ -12,7 +12,7 @@ namespace lsp::language::symbol
|
|||
|
||||
void Build(ast::ASTNode& root);
|
||||
|
||||
// 访问语句和声明
|
||||
// Visit statements and declarations
|
||||
void VisitProgram(ast::Program& node) override;
|
||||
void VisitUnitDefinition(ast::UnitDefinition& node) override;
|
||||
void VisitClassDefinition(ast::ClassDefinition& node) override;
|
||||
|
|
@ -23,7 +23,7 @@ namespace lsp::language::symbol
|
|||
void VisitExternalMethodDefinition(ast::ExternalMethodDefinition& node) override;
|
||||
void VisitClassMember(ast::ClassMember& node) override;
|
||||
|
||||
// 声明
|
||||
// Declarations
|
||||
void VisitVarDeclaration(ast::VarDeclaration& node) override;
|
||||
void VisitStaticDeclaration(ast::StaticDeclaration& node) override;
|
||||
void VisitGlobalDeclaration(ast::GlobalDeclaration& node) override;
|
||||
|
|
@ -39,24 +39,24 @@ namespace lsp::language::symbol
|
|||
void VisitCaseStatement(ast::CaseStatement& node) override;
|
||||
void VisitTryStatement(ast::TryStatement& node) override;
|
||||
|
||||
// Uses 语句处理
|
||||
// Uses statement handling
|
||||
void VisitUsesStatement(ast::UsesStatement& node) override;
|
||||
|
||||
// 访问表达式(收集引用)
|
||||
// Visit expressions (collect references)
|
||||
void VisitIdentifier(ast::Identifier& node) override;
|
||||
void VisitCallExpression(ast::CallExpression& node) override;
|
||||
void VisitAttributeExpression(ast::AttributeExpression& node) override;
|
||||
void VisitAssignmentExpression(ast::AssignmentExpression& node) override;
|
||||
|
||||
// 其他节点访问
|
||||
void VisitLiteral(ast::Literal&) override {}
|
||||
// Other node visits
|
||||
void VisitLiteral([[maybe_unused]] ast::Literal& node) override {}
|
||||
void VisitBinaryExpression(ast::BinaryExpression& node) override;
|
||||
void VisitTernaryExpression(ast::TernaryExpression& node) override;
|
||||
void VisitSubscriptExpression(ast::SubscriptExpression& node) override;
|
||||
void VisitArrayExpression(ast::ArrayExpression& node) override;
|
||||
void VisitAnonymousFunctionExpression(ast::AnonymousFunctionExpression& node) override;
|
||||
|
||||
// 一元表达式(细化的类型)
|
||||
// Unary expressions (refined types)
|
||||
void VisitUnaryPlusExpression(ast::UnaryPlusExpression& node) override;
|
||||
void VisitUnaryMinusExpression(ast::UnaryMinusExpression& node) override;
|
||||
void VisitPrefixIncrementExpression(ast::PrefixIncrementExpression& node) override;
|
||||
|
|
@ -69,9 +69,9 @@ namespace lsp::language::symbol
|
|||
void VisitMatrixTransposeExpression(ast::MatrixTransposeExpression& node) override;
|
||||
void VisitExprOperatorExpression(ast::ExprOperatorExpression& node) override;
|
||||
|
||||
void VisitFunctionPointerExpression(ast::FunctionPointerExpression&) override;
|
||||
void VisitFunctionPointerExpression([[maybe_unused]] ast::FunctionPointerExpression& node) override;
|
||||
|
||||
// 其他表达式
|
||||
// Other expressions
|
||||
void VisitNewExpression(ast::NewExpression& node) override;
|
||||
void VisitEchoExpression(ast::EchoExpression& node) override;
|
||||
void VisitRaiseExpression(ast::RaiseExpression& node) override;
|
||||
|
|
@ -79,44 +79,78 @@ namespace lsp::language::symbol
|
|||
void VisitParenthesizedExpression(ast::ParenthesizedExpression& node) override;
|
||||
|
||||
void VisitExpressionStatement(ast::ExpressionStatement& node) override;
|
||||
void VisitBreakStatement(ast::BreakStatement&) override {}
|
||||
void VisitContinueStatement(ast::ContinueStatement&) override {}
|
||||
void VisitBreakStatement([[maybe_unused]] ast::BreakStatement& node) override {}
|
||||
void VisitContinueStatement([[maybe_unused]] ast::ContinueStatement& node) override {}
|
||||
void VisitReturnStatement(ast::ReturnStatement& node) override;
|
||||
void VisitTSSQLExpression(ast::TSSQLExpression&) override {}
|
||||
void VisitTSSQLExpression([[maybe_unused]] ast::TSSQLExpression& node) override {}
|
||||
void VisitColumnReference(ast::ColumnReference& node) override;
|
||||
void VisitUnpackPattern(ast::UnpackPattern&) override;
|
||||
void VisitUnpackPattern([[maybe_unused]] ast::UnpackPattern& node) override;
|
||||
void VisitMatrixIterationStatement(ast::MatrixIterationStatement& node) override;
|
||||
|
||||
// 编译指令(正确的名称,无Statement后缀)
|
||||
void VisitCompilerDirective(ast::CompilerDirective& node) override;
|
||||
// Compiler directives (correct names without Statement suffix)
|
||||
void VisitCompilerDirective([[maybe_unused]] ast::CompilerDirective& node) override;
|
||||
void VisitConditionalDirective(ast::ConditionalDirective& node) override;
|
||||
void VisitConditionalBlock(ast::ConditionalBlock& node) override;
|
||||
void VisitTSLXBlock(ast::TSLXBlock&) override {}
|
||||
void VisitTSLXBlock([[maybe_unused]] ast::TSLXBlock& node) override {}
|
||||
|
||||
void VisitParameter(ast::Parameter&) override {}
|
||||
void VisitParameter([[maybe_unused]] ast::Parameter& node) override {}
|
||||
|
||||
private:
|
||||
SymbolId CreateSymbol(const std::string& name, SymbolKind kind, const ast::Location& location, const std::optional<std::string>& type_hint = std::nullopt, bool is_class_method = false);
|
||||
SymbolId CreateSymbol(const std::string& name, SymbolKind kind, const ast::ASTNode& node, const std::optional<std::string>& type_hint = std::nullopt, bool is_class_method = false);
|
||||
ScopeId EnterScopeWithSymbol(ScopeKind kind, SymbolId symbol_id, const ast::Location& range);
|
||||
ScopeId EnterScope(ScopeKind kind, const ast::Location& range);
|
||||
|
||||
void ExitScope();
|
||||
void VisitStatements(const std::vector<ast::StatementPtr>& statements);
|
||||
void VisitExpression(ast::Expression& expr);
|
||||
|
||||
void ProcessLValue(const ast::LValue& lvalue, bool is_write);
|
||||
|
||||
// 修改后的签名:不再使用Signature结构
|
||||
std::string FormatSignature(
|
||||
// Symbol creation helpers - now returns symbol with parameters pre-set
|
||||
SymbolId CreateFunctionSymbol(
|
||||
const std::string& name,
|
||||
const ast::Location& location,
|
||||
const std::vector<std::unique_ptr<ast::Parameter>>& parameters,
|
||||
const std::optional<ast::TypeAnnotation>& return_type);
|
||||
|
||||
SymbolId CreateMethodSymbol(
|
||||
const std::string& name,
|
||||
const ast::Location& location,
|
||||
const std::vector<std::unique_ptr<ast::Parameter>>& parameters,
|
||||
const std::optional<ast::TypeAnnotation>& return_type);
|
||||
|
||||
SymbolId CreateSymbol(
|
||||
const std::string& name,
|
||||
SymbolKind kind,
|
||||
const ast::Location& location,
|
||||
const std::optional<std::string>& type_hint = std::nullopt);
|
||||
|
||||
SymbolId CreateSymbol(
|
||||
const std::string& name,
|
||||
SymbolKind kind,
|
||||
const ast::ASTNode& node,
|
||||
const std::optional<std::string>& type_hint = std::nullopt);
|
||||
|
||||
// Scope management
|
||||
ScopeId EnterScopeWithSymbol(ScopeKind kind, SymbolId symbol_id, const ast::Location& range);
|
||||
ScopeId EnterScope(ScopeKind kind, const ast::Location& range);
|
||||
void ExitScope();
|
||||
|
||||
// Traversal helpers
|
||||
void VisitStatements(const std::vector<ast::StatementPtr>& statements);
|
||||
void VisitExpression(ast::Expression& expr);
|
||||
|
||||
// LValue processing for assignments
|
||||
void ProcessLValue(const ast::LValue& lvalue, bool is_write);
|
||||
|
||||
// Type and parameter extraction
|
||||
std::optional<std::string> ExtractTypeName(
|
||||
const std::optional<ast::TypeAnnotation>& type) const;
|
||||
|
||||
std::vector<language::symbol::Parameter> BuildParameters(
|
||||
const std::vector<std::unique_ptr<ast::Parameter>>& parameters) const;
|
||||
|
||||
private:
|
||||
// Symbol table reference
|
||||
SymbolTable& table_;
|
||||
|
||||
// Current traversal state
|
||||
ScopeId current_scope_id_;
|
||||
std::optional<SymbolId> current_parent_symbol_id_;
|
||||
std::optional<SymbolId> current_function_id_;
|
||||
|
||||
// Interface/implementation section tracking
|
||||
bool in_interface_section_;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace lsp::language::symbol
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "../interface.hpp"
|
||||
#include "../types.hpp"
|
||||
|
||||
namespace lsp::language::symbol::graph
|
||||
{
|
||||
|
||||
class Call : public ISymbolGraph
|
||||
{
|
||||
public:
|
||||
void OnSymbolRemoved(SymbolId id) override
|
||||
{
|
||||
callers_map_.erase(id);
|
||||
callees_map_.erase(id);
|
||||
|
||||
// Remove all calls where this symbol is the caller
|
||||
for (auto& [symbol_id, calls] : callers_map_)
|
||||
{
|
||||
calls.erase(std::remove_if(calls.begin(), calls.end(), [id](const symbol::Call& call) {
|
||||
return call.caller == id;
|
||||
}),
|
||||
calls.end());
|
||||
}
|
||||
|
||||
// Remove all calls where this symbol is the callee
|
||||
for (auto& [symbol_id, calls] : callees_map_)
|
||||
{
|
||||
calls.erase(std::remove_if(calls.begin(), calls.end(), [id](const symbol::Call& call) {
|
||||
return call.callee == id;
|
||||
}),
|
||||
calls.end());
|
||||
}
|
||||
}
|
||||
|
||||
void Clear() override
|
||||
{
|
||||
callers_map_.clear();
|
||||
callees_map_.clear();
|
||||
}
|
||||
|
||||
void AddCall(SymbolId caller, SymbolId callee, const ast::Location& location)
|
||||
{
|
||||
symbol::Call call{ caller, callee, location };
|
||||
callers_map_[callee].push_back(call); // Who calls this symbol
|
||||
callees_map_[caller].push_back(call); // What this symbol calls
|
||||
}
|
||||
|
||||
// Accessor (snake_case)
|
||||
const std::vector<symbol::Call>& callers(SymbolId id) const
|
||||
{
|
||||
static const std::vector<symbol::Call> kEmpty;
|
||||
auto it = callers_map_.find(id);
|
||||
return it != callers_map_.end() ? it->second : kEmpty;
|
||||
}
|
||||
|
||||
// Accessor (snake_case)
|
||||
const std::vector<symbol::Call>& callees(SymbolId id) const
|
||||
{
|
||||
static const std::vector<symbol::Call> kEmpty;
|
||||
auto it = callees_map_.find(id);
|
||||
return it != callees_map_.end() ? it->second : kEmpty;
|
||||
}
|
||||
|
||||
private:
|
||||
// Map: symbol -> who calls it (incoming calls)
|
||||
std::unordered_map<SymbolId, std::vector<symbol::Call>> callers_map_;
|
||||
|
||||
// Map: symbol -> what it calls (outgoing calls)
|
||||
std::unordered_map<SymbolId, std::vector<symbol::Call>> callees_map_;
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol::graph
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "../interface.hpp"
|
||||
#include "../types.hpp"
|
||||
|
||||
namespace lsp::language::symbol::graph
|
||||
{
|
||||
|
||||
class Inheritance : public ISymbolGraph
|
||||
{
|
||||
public:
|
||||
void OnSymbolRemoved(SymbolId id) override
|
||||
{
|
||||
base_classes_.erase(id);
|
||||
derived_classes_.erase(id);
|
||||
|
||||
// Remove from all base class lists
|
||||
for (auto& [derived_id, bases] : base_classes_)
|
||||
{
|
||||
bases.erase(std::remove(bases.begin(), bases.end(), id), bases.end());
|
||||
}
|
||||
|
||||
// Remove from all derived class lists
|
||||
for (auto& [base_id, derived] : derived_classes_)
|
||||
{
|
||||
derived.erase(std::remove(derived.begin(), derived.end(), id),
|
||||
derived.end());
|
||||
}
|
||||
}
|
||||
|
||||
void Clear() override
|
||||
{
|
||||
base_classes_.clear();
|
||||
derived_classes_.clear();
|
||||
}
|
||||
|
||||
void AddInheritance(SymbolId derived, SymbolId base)
|
||||
{
|
||||
base_classes_[derived].push_back(base);
|
||||
derived_classes_[base].push_back(derived);
|
||||
}
|
||||
|
||||
// Accessor (snake_case)
|
||||
const std::vector<SymbolId>& base_classes(SymbolId id) const
|
||||
{
|
||||
static const std::vector<SymbolId> kEmpty;
|
||||
auto it = base_classes_.find(id);
|
||||
return it != base_classes_.end() ? it->second : kEmpty;
|
||||
}
|
||||
|
||||
// Accessor (snake_case)
|
||||
const std::vector<SymbolId>& derived_classes(SymbolId id) const
|
||||
{
|
||||
static const std::vector<SymbolId> kEmpty;
|
||||
auto it = derived_classes_.find(id);
|
||||
return it != derived_classes_.end() ? it->second : kEmpty;
|
||||
}
|
||||
|
||||
bool IsSubclassOf(SymbolId derived, SymbolId base) const
|
||||
{
|
||||
auto it = base_classes_.find(derived);
|
||||
if (it == base_classes_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (SymbolId parent : it->second)
|
||||
{
|
||||
if (parent == base || IsSubclassOf(parent, base))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
// Map from derived class to its base classes
|
||||
std::unordered_map<SymbolId, std::vector<SymbolId>> base_classes_;
|
||||
|
||||
// Map from base class to its derived classes
|
||||
std::unordered_map<SymbolId, std::vector<SymbolId>> derived_classes_;
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol::graph
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "../interface.hpp"
|
||||
#include "../types.hpp"
|
||||
|
||||
namespace lsp::language::symbol::graph
|
||||
{
|
||||
|
||||
class Reference : public ISymbolGraph
|
||||
{
|
||||
public:
|
||||
void OnSymbolRemoved(SymbolId id) override
|
||||
{
|
||||
references_.erase(id);
|
||||
|
||||
// Remove all references to the removed symbol
|
||||
for (auto& [symbol_id, refs] : references_)
|
||||
{
|
||||
refs.erase(std::remove_if(refs.begin(), refs.end(), [id](const symbol::Reference& ref) {
|
||||
return ref.symbol_id == id;
|
||||
}),
|
||||
refs.end());
|
||||
}
|
||||
}
|
||||
|
||||
void Clear() override { references_.clear(); }
|
||||
|
||||
void AddReference(SymbolId symbol_id, const ast::Location& location, bool is_definition = false, bool is_write = false)
|
||||
{
|
||||
references_[symbol_id].push_back(
|
||||
{ location, symbol_id, is_definition, is_write });
|
||||
}
|
||||
|
||||
// Accessor (snake_case)
|
||||
const std::vector<symbol::Reference>& references(SymbolId id) const
|
||||
{
|
||||
static const std::vector<symbol::Reference> kEmpty;
|
||||
auto it = references_.find(id);
|
||||
return it != references_.end() ? it->second : kEmpty;
|
||||
}
|
||||
|
||||
std::optional<ast::Location> FindDefinitionLocation(SymbolId id) const
|
||||
{
|
||||
auto it = references_.find(id);
|
||||
if (it != references_.end())
|
||||
{
|
||||
for (const auto& ref : it->second)
|
||||
{
|
||||
if (ref.is_definition)
|
||||
{
|
||||
return ref.location;
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
// Map from symbol to all its references
|
||||
std::unordered_map<SymbolId, std::vector<symbol::Reference>> references_;
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol::graph
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "../interface.hpp"
|
||||
#include "../types.hpp"
|
||||
|
||||
namespace lsp::language::symbol::index
|
||||
{
|
||||
|
||||
class Location : public ISymbolIndex
|
||||
{
|
||||
public:
|
||||
void OnSymbolAdded(const Symbol& symbol) override
|
||||
{
|
||||
const auto& loc = symbol.selection_range();
|
||||
entries_.push_back({ loc.start_offset, loc.end_offset, symbol.id() });
|
||||
needs_sort_ = true;
|
||||
}
|
||||
|
||||
void OnSymbolRemoved(SymbolId id) override
|
||||
{
|
||||
entries_.erase(std::remove_if(entries_.begin(), entries_.end(), [id](const Entry& e) {
|
||||
return e.symbol_id == id;
|
||||
}),
|
||||
entries_.end());
|
||||
}
|
||||
|
||||
void Clear() override
|
||||
{
|
||||
entries_.clear();
|
||||
needs_sort_ = false;
|
||||
}
|
||||
|
||||
std::optional<SymbolId> FindSymbolAt(const ast::Location& location) const
|
||||
{
|
||||
EnsureSorted();
|
||||
uint32_t pos = location.start_offset;
|
||||
|
||||
auto it = std::lower_bound(entries_.begin(), entries_.end(), pos, [](const Entry& e, uint32_t p) {
|
||||
return e.start < p;
|
||||
});
|
||||
|
||||
if (it != entries_.begin())
|
||||
{
|
||||
--it;
|
||||
}
|
||||
|
||||
std::optional<SymbolId> result;
|
||||
uint32_t min_span = UINT32_MAX;
|
||||
|
||||
for (; it != entries_.end() && it->start <= pos; ++it)
|
||||
{
|
||||
if (pos >= it->start && pos < it->end)
|
||||
{
|
||||
uint32_t span = it->end - it->start;
|
||||
if (span < min_span)
|
||||
{
|
||||
min_span = span;
|
||||
result = it->symbol_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Entry
|
||||
{
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
SymbolId symbol_id;
|
||||
|
||||
bool operator<(const Entry& other) const
|
||||
{
|
||||
if (start != other.start)
|
||||
{
|
||||
return start < other.start;
|
||||
}
|
||||
return end > other.end;
|
||||
}
|
||||
};
|
||||
|
||||
void EnsureSorted() const
|
||||
{
|
||||
if (needs_sort_)
|
||||
{
|
||||
std::sort(entries_.begin(), entries_.end());
|
||||
needs_sort_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
mutable std::vector<Entry> entries_;
|
||||
mutable bool needs_sort_ = false;
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol::index
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../interface.hpp"
|
||||
#include "../types.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
|
||||
struct Scope
|
||||
{
|
||||
ScopeId id;
|
||||
ScopeKind kind;
|
||||
ast::Location range;
|
||||
std::optional<ScopeId> parent;
|
||||
std::optional<SymbolId> owner;
|
||||
std::unordered_map<std::string, SymbolId> symbols;
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol
|
||||
|
||||
namespace lsp::language::symbol::index
|
||||
{
|
||||
|
||||
class Scope : public ISymbolIndex
|
||||
{
|
||||
public:
|
||||
void OnSymbolAdded(const Symbol&) override {}
|
||||
|
||||
void OnSymbolRemoved(SymbolId id) override
|
||||
{
|
||||
for (auto& [_, scope] : scopes_)
|
||||
{
|
||||
for (auto it = scope.symbols.begin(); it != scope.symbols.end();)
|
||||
{
|
||||
if (it->second == id)
|
||||
{
|
||||
it = scope.symbols.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Clear() override
|
||||
{
|
||||
scopes_.clear();
|
||||
next_scope_id_ = 1;
|
||||
global_scope_ = kInvalidScopeId;
|
||||
}
|
||||
|
||||
ScopeId CreateScope(ScopeKind kind, const ast::Location& range, std::optional<ScopeId> parent = std::nullopt, std::optional<SymbolId> owner = std::nullopt)
|
||||
{
|
||||
ScopeId id = next_scope_id_++;
|
||||
scopes_[id] = { id, kind, range, parent, owner, {} };
|
||||
|
||||
if (kind == ScopeKind::kGlobal)
|
||||
{
|
||||
global_scope_ = id;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void AddSymbol(ScopeId scope_id, const std::string& name, SymbolId symbol_id)
|
||||
{
|
||||
auto it = scopes_.find(scope_id);
|
||||
if (it != scopes_.end())
|
||||
{
|
||||
it->second.symbols[ToLower(name)] = symbol_id;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<SymbolId> FindSymbolInScope(
|
||||
ScopeId scope_id,
|
||||
const std::string& name) const
|
||||
{
|
||||
auto it = scopes_.find(scope_id);
|
||||
if (it == scopes_.end())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto sym_it = it->second.symbols.find(ToLower(name));
|
||||
return sym_it != it->second.symbols.end() ? std::optional(sym_it->second) : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<SymbolId> FindSymbolInScopeChain(
|
||||
ScopeId scope_id,
|
||||
const std::string& name) const
|
||||
{
|
||||
std::optional<ScopeId> current = scope_id;
|
||||
|
||||
while (current)
|
||||
{
|
||||
if (auto result = FindSymbolInScope(*current, name))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
auto it = scopes_.find(*current);
|
||||
current = it != scopes_.end() ? it->second.parent : std::nullopt;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Accessor (snake_case)
|
||||
const symbol::Scope* scope(ScopeId id) const
|
||||
{
|
||||
auto it = scopes_.find(id);
|
||||
return it != scopes_.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
// Accessor (snake_case)
|
||||
ScopeId global_scope() const { return global_scope_; }
|
||||
|
||||
// Accessor (snake_case)
|
||||
const std::unordered_map<ScopeId, symbol::Scope>& all_scopes() const
|
||||
{
|
||||
return scopes_;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::string ToLower(const std::string& s)
|
||||
{
|
||||
std::string result = s;
|
||||
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
|
||||
return result;
|
||||
}
|
||||
|
||||
ScopeId next_scope_id_ = 1;
|
||||
ScopeId global_scope_ = kInvalidScopeId;
|
||||
std::unordered_map<ScopeId, symbol::Scope> scopes_;
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol::index
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include "./types.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
|
||||
// 索引接口:用于通过各种方式查找符号
|
||||
class ISymbolIndex
|
||||
{
|
||||
public:
|
||||
virtual ~ISymbolIndex() = default;
|
||||
virtual void OnSymbolAdded(const Symbol& symbol) = 0;
|
||||
virtual void OnSymbolRemoved(SymbolId id) = 0;
|
||||
virtual void Clear() = 0;
|
||||
};
|
||||
|
||||
// 关系接口:用于管理符号之间的关系
|
||||
class ISymbolGraph
|
||||
{
|
||||
public:
|
||||
virtual ~ISymbolGraph() = default;
|
||||
virtual void OnSymbolRemoved(SymbolId id) = 0;
|
||||
virtual void Clear() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
#include <algorithm>
|
||||
#include "./location_index.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
void LocationIndex::AddSymbol(SymbolId id, const ast::Location& location)
|
||||
{
|
||||
symbol_entries_.push_back({ location.start_byte,
|
||||
location.end_byte,
|
||||
id });
|
||||
symbols_sorted_ = false;
|
||||
}
|
||||
|
||||
void LocationIndex::AddScope(ScopeId id, const ast::Location& range)
|
||||
{
|
||||
scope_entries_.push_back({ range.start_byte,
|
||||
range.end_byte,
|
||||
id });
|
||||
scopes_sorted_ = false;
|
||||
}
|
||||
|
||||
void LocationIndex::AddReference(SymbolId symbol_id, const ast::Location& location)
|
||||
{
|
||||
reference_entries_.push_back({ location.start_byte,
|
||||
location.end_byte,
|
||||
symbol_id });
|
||||
references_sorted_ = false;
|
||||
}
|
||||
|
||||
std::optional<SymbolId> LocationIndex::FindSymbolAt(const ast::Location& location) const
|
||||
{
|
||||
// 确保索引已排序
|
||||
if (!symbols_sorted_)
|
||||
return std::nullopt;
|
||||
|
||||
return FindInSortedEntries<LocationEntry, SymbolId>(symbol_entries_, location, true);
|
||||
}
|
||||
|
||||
std::optional<ScopeId> LocationIndex::FindScopeAt(const ast::Location& location) const
|
||||
{
|
||||
// 确保索引已排序
|
||||
if (!scopes_sorted_)
|
||||
return std::nullopt;
|
||||
|
||||
return FindInSortedEntries<ScopeEntry, ScopeId>(scope_entries_, location, true);
|
||||
}
|
||||
|
||||
std::optional<SymbolId> LocationIndex::FindReferenceAt(const ast::Location& location) const
|
||||
{
|
||||
// 确保索引已排序
|
||||
if (!references_sorted_)
|
||||
return std::nullopt;
|
||||
|
||||
auto it = std::lower_bound(
|
||||
reference_entries_.begin(),
|
||||
reference_entries_.end(),
|
||||
location.start_byte,
|
||||
[](const ReferenceEntry& entry, uint32_t pos) {
|
||||
return entry.start_byte < pos;
|
||||
});
|
||||
|
||||
if (it != reference_entries_.begin())
|
||||
--it;
|
||||
|
||||
for (; it != reference_entries_.end() && it->start_byte <= location.start_byte; ++it)
|
||||
{
|
||||
if (IsLocationInRange(location, it->start_byte, it->end_byte))
|
||||
return it->symbol_id;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void LocationIndex::RebuildIndex()
|
||||
{
|
||||
if (!symbols_sorted_)
|
||||
{
|
||||
std::sort(symbol_entries_.begin(), symbol_entries_.end());
|
||||
symbols_sorted_ = true;
|
||||
}
|
||||
|
||||
if (!scopes_sorted_)
|
||||
{
|
||||
std::sort(scope_entries_.begin(), scope_entries_.end());
|
||||
scopes_sorted_ = true;
|
||||
}
|
||||
|
||||
if (!references_sorted_)
|
||||
{
|
||||
std::sort(reference_entries_.begin(), reference_entries_.end());
|
||||
references_sorted_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void LocationIndex::Clear()
|
||||
{
|
||||
symbol_entries_.clear();
|
||||
scope_entries_.clear();
|
||||
reference_entries_.clear();
|
||||
symbols_sorted_ = true;
|
||||
scopes_sorted_ = true;
|
||||
references_sorted_ = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,119 +3,93 @@
|
|||
#include <vector>
|
||||
#include <optional>
|
||||
#include <algorithm>
|
||||
#include "./interface.hpp"
|
||||
#include "./types.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
class LocationIndex
|
||||
|
||||
class LocationIndex : public ISymbolIndex
|
||||
{
|
||||
public:
|
||||
struct LocationEntry
|
||||
void OnSymbolAdded(const Symbol& symbol) override
|
||||
{
|
||||
uint32_t start_byte;
|
||||
uint32_t end_byte;
|
||||
SymbolId symbol_id;
|
||||
const auto& range = symbol.range();
|
||||
entries_.push_back({ range.start_offset,
|
||||
range.end_offset,
|
||||
symbol.id() });
|
||||
needs_sort_ = true;
|
||||
}
|
||||
|
||||
bool operator<(const LocationEntry& other) const
|
||||
{
|
||||
if (start_byte != other.start_byte)
|
||||
return start_byte < other.start_byte;
|
||||
return end_byte > other.end_byte;
|
||||
}
|
||||
};
|
||||
|
||||
struct ScopeEntry
|
||||
void OnSymbolRemoved(SymbolId id) override
|
||||
{
|
||||
uint32_t start_byte;
|
||||
uint32_t end_byte;
|
||||
ScopeId scope_id;
|
||||
entries_.erase(
|
||||
std::remove_if(entries_.begin(), entries_.end(), [id](const Entry& e) { return e.symbol_id == id; }),
|
||||
entries_.end());
|
||||
}
|
||||
|
||||
bool operator<(const ScopeEntry& other) const
|
||||
{
|
||||
if (start_byte != other.start_byte)
|
||||
return start_byte < other.start_byte;
|
||||
return end_byte > other.end_byte;
|
||||
}
|
||||
};
|
||||
|
||||
struct ReferenceEntry
|
||||
void Clear() override
|
||||
{
|
||||
uint32_t start_byte;
|
||||
uint32_t end_byte;
|
||||
SymbolId symbol_id;
|
||||
entries_.clear();
|
||||
needs_sort_ = false;
|
||||
}
|
||||
|
||||
bool operator<(const ReferenceEntry& other) const
|
||||
{
|
||||
return start_byte < other.start_byte;
|
||||
}
|
||||
};
|
||||
|
||||
void AddSymbol(SymbolId id, const ast::Location& location);
|
||||
void AddScope(ScopeId id, const ast::Location& range);
|
||||
void AddReference(SymbolId symbol_id, const ast::Location& location);
|
||||
void RebuildIndex();
|
||||
void Clear();
|
||||
|
||||
std::optional<SymbolId> FindSymbolAt(const ast::Location& location) const;
|
||||
std::optional<ScopeId> FindScopeAt(const ast::Location& location) const;
|
||||
std::optional<SymbolId> FindReferenceAt(const ast::Location& location) const;
|
||||
|
||||
private:
|
||||
// 符号定义位置索引(按起始位置排序)
|
||||
std::vector<LocationEntry> symbol_entries_;
|
||||
bool symbols_sorted_ = true;
|
||||
|
||||
// 作用域位置索引(按起始位置排序)
|
||||
std::vector<ScopeEntry> scope_entries_;
|
||||
bool scopes_sorted_ = true;
|
||||
|
||||
// 引用位置索引(位置 -> 符号ID)
|
||||
std::vector<ReferenceEntry> reference_entries_;
|
||||
bool references_sorted_ = true;
|
||||
|
||||
// 辅助方法:在排序数组中查找位置(模板实现放在头文件)
|
||||
template<typename T, typename IdType>
|
||||
std::optional<IdType> FindInSortedEntries(const std::vector<T>& entries, const ast::Location& location, bool find_innermost = true) const
|
||||
std::optional<SymbolId> FindAt(const ast::Location& location) const
|
||||
{
|
||||
if (entries.empty())
|
||||
return std::nullopt;
|
||||
EnsureSorted();
|
||||
uint32_t pos = location.start_offset;
|
||||
|
||||
auto it = std::lower_bound(
|
||||
entries.begin(),
|
||||
entries.end(),
|
||||
location.start_byte,
|
||||
[](const T& entry, uint32_t pos) {
|
||||
return entry.start_byte < pos;
|
||||
});
|
||||
entries_.begin(), entries_.end(), pos, [](const Entry& e, uint32_t p) { return e.start < p; });
|
||||
|
||||
if (it != entries.begin())
|
||||
if (it != entries_.begin())
|
||||
--it;
|
||||
|
||||
std::vector<IdType> candidates;
|
||||
for (; it != entries.end() && it->start_byte <= location.start_byte; ++it)
|
||||
std::optional<SymbolId> result;
|
||||
uint32_t min_span = UINT32_MAX;
|
||||
|
||||
for (; it != entries_.end() && it->start <= pos; ++it)
|
||||
{
|
||||
if (IsLocationInRange(location, it->start_byte, it->end_byte))
|
||||
if (pos >= it->start && pos < it->end)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, LocationEntry> || std::is_same_v<T, ReferenceEntry>)
|
||||
candidates.push_back(it->symbol_id);
|
||||
else if constexpr (std::is_same_v<T, ScopeEntry>)
|
||||
candidates.push_back(it->scope_id);
|
||||
uint32_t span = it->end - it->start;
|
||||
if (span < min_span)
|
||||
{
|
||||
min_span = span;
|
||||
result = it->symbol_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (candidates.empty())
|
||||
return std::nullopt;
|
||||
|
||||
if (find_innermost)
|
||||
return candidates.back();
|
||||
else
|
||||
return candidates.front();
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool IsLocationInRange(const ast::Location& location, uint32_t start, uint32_t end)
|
||||
private:
|
||||
struct Entry
|
||||
{
|
||||
return location.start_byte >= start && location.start_byte < end;
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
SymbolId symbol_id;
|
||||
|
||||
bool operator<(const Entry& other) const
|
||||
{
|
||||
if (start != other.start)
|
||||
return start < other.start;
|
||||
return end > other.end;
|
||||
}
|
||||
};
|
||||
|
||||
void EnsureSorted() const
|
||||
{
|
||||
if (needs_sort_)
|
||||
{
|
||||
std::sort(entries_.begin(), entries_.end());
|
||||
needs_sort_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
mutable std::vector<Entry> entries_;
|
||||
mutable bool needs_sort_ = false;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace lsp::language::symbol
|
||||
|
|
|
|||
|
|
@ -1,116 +0,0 @@
|
|||
#include <unordered_set>
|
||||
#include "./relations.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
// ===== ReferenceGraph 实现 =====
|
||||
|
||||
void ReferenceGraph::AddReference(SymbolId symbol_id, const ast::Location& location, bool is_definition, bool is_write)
|
||||
{
|
||||
references_[symbol_id].push_back({ location, symbol_id, is_definition, is_write });
|
||||
}
|
||||
|
||||
const std::vector<Reference>* ReferenceGraph::GetReferences(SymbolId symbol_id) const
|
||||
{
|
||||
auto it = references_.find(symbol_id);
|
||||
return it != references_.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
std::optional<ast::Location> ReferenceGraph::GetDefinition(SymbolId symbol_id) const
|
||||
{
|
||||
auto it = references_.find(symbol_id);
|
||||
if (it != references_.end())
|
||||
{
|
||||
for (const auto& ref : it->second)
|
||||
{
|
||||
if (ref.is_definition)
|
||||
return ref.location;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void ReferenceGraph::Clear()
|
||||
{
|
||||
references_.clear();
|
||||
}
|
||||
|
||||
// ===== InheritanceGraph 实现 =====
|
||||
|
||||
void InheritanceGraph::AddInheritance(SymbolId derived_class, SymbolId base_class)
|
||||
{
|
||||
base_classes_[derived_class].push_back(base_class);
|
||||
derived_classes_[base_class].push_back(derived_class);
|
||||
}
|
||||
|
||||
const std::vector<SymbolId>* InheritanceGraph::GetBaseClasses(SymbolId class_id) const
|
||||
{
|
||||
auto it = base_classes_.find(class_id);
|
||||
return it != base_classes_.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
const std::vector<SymbolId>* InheritanceGraph::GetDerivedClasses(SymbolId class_id) const
|
||||
{
|
||||
auto it = derived_classes_.find(class_id);
|
||||
return it != derived_classes_.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
bool InheritanceGraph::IsSubclassOf(SymbolId derived, SymbolId base) const
|
||||
{
|
||||
std::unordered_set<SymbolId> visited;
|
||||
std::vector<SymbolId> queue = { derived };
|
||||
|
||||
while (!queue.empty())
|
||||
{
|
||||
SymbolId current = queue.back();
|
||||
queue.pop_back();
|
||||
|
||||
if (current == base)
|
||||
return true;
|
||||
|
||||
if (visited.count(current))
|
||||
continue;
|
||||
visited.insert(current);
|
||||
|
||||
auto bases = GetBaseClasses(current);
|
||||
if (bases)
|
||||
queue.insert(queue.end(), bases->begin(), bases->end());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InheritanceGraph::Clear()
|
||||
{
|
||||
base_classes_.clear();
|
||||
derived_classes_.clear();
|
||||
}
|
||||
|
||||
// ===== CallGraph 实现 =====
|
||||
|
||||
void CallGraph::AddCall(SymbolId caller, SymbolId callee, const ast::Location& location)
|
||||
{
|
||||
CallRelation rel{ caller, callee, location };
|
||||
callers_[callee].push_back(rel);
|
||||
callees_[caller].push_back(rel);
|
||||
}
|
||||
|
||||
const std::vector<CallRelation>* CallGraph::GetCallers(SymbolId function_id) const
|
||||
{
|
||||
auto it = callers_.find(function_id);
|
||||
return it != callers_.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
const std::vector<CallRelation>* CallGraph::GetCallees(SymbolId function_id) const
|
||||
{
|
||||
auto it = callees_.find(function_id);
|
||||
return it != callees_.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
void CallGraph::Clear()
|
||||
{
|
||||
callers_.clear();
|
||||
callees_.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "./types.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
// 引用图
|
||||
class ReferenceGraph
|
||||
{
|
||||
public:
|
||||
void AddReference(SymbolId symbol_id, const ast::Location& location, bool is_definition = false, bool is_write = false);
|
||||
void Clear();
|
||||
|
||||
// 返回引用避免拷贝,如果不存在返回 nullptr
|
||||
const std::vector<Reference>* GetReferences(SymbolId symbol_id) const;
|
||||
std::optional<ast::Location> GetDefinition(SymbolId symbol_id) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<SymbolId, std::vector<Reference>> references_;
|
||||
};
|
||||
|
||||
// 继承图
|
||||
class InheritanceGraph
|
||||
{
|
||||
public:
|
||||
void AddInheritance(SymbolId derived_class, SymbolId base_class);
|
||||
void Clear();
|
||||
|
||||
// 返回引用避免拷贝
|
||||
const std::vector<SymbolId>* GetBaseClasses(SymbolId class_id) const;
|
||||
const std::vector<SymbolId>* GetDerivedClasses(SymbolId class_id) const;
|
||||
bool IsSubclassOf(SymbolId derived, SymbolId base) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<SymbolId, std::vector<SymbolId>> base_classes_;
|
||||
std::unordered_map<SymbolId, std::vector<SymbolId>> derived_classes_;
|
||||
};
|
||||
|
||||
// 调用图
|
||||
class CallGraph
|
||||
{
|
||||
public:
|
||||
void AddCall(SymbolId caller, SymbolId callee, const ast::Location& location);
|
||||
void Clear();
|
||||
|
||||
// 返回引用避免拷贝
|
||||
const std::vector<CallRelation>* GetCallers(SymbolId function_id) const;
|
||||
const std::vector<CallRelation>* GetCallees(SymbolId function_id) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<SymbolId, std::vector<CallRelation>> callers_;
|
||||
std::unordered_map<SymbolId, std::vector<CallRelation>> callees_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
#include "./scope.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
ScopeId ScopeManager::CreateScope(ScopeKind kind, const ast::Location& range, std::optional<ScopeId> parent_scope_id, std::optional<SymbolId> associated_symbol_id)
|
||||
{
|
||||
ScopeId new_scope_id = next_scope_id_++;
|
||||
|
||||
ScopeInfo info;
|
||||
info.scope_id = new_scope_id;
|
||||
info.kind = kind;
|
||||
info.range = range;
|
||||
info.parent_scope_id = parent_scope_id;
|
||||
info.associated_symbol_id = associated_symbol_id;
|
||||
|
||||
scopes_[new_scope_id] = std::move(info);
|
||||
|
||||
if (kind == ScopeKind::Global)
|
||||
global_scope_id_ = new_scope_id;
|
||||
|
||||
return new_scope_id;
|
||||
}
|
||||
|
||||
void ScopeManager::AddSymbol(ScopeId scope_id, const std::string& name, SymbolId symbol_id)
|
||||
{
|
||||
auto it = scopes_.find(scope_id);
|
||||
if (it != scopes_.end())
|
||||
it->second.symbols[name] = symbol_id;
|
||||
}
|
||||
|
||||
std::optional<SymbolId> ScopeManager::FindInScope(ScopeId scope_id, const std::string& name) const
|
||||
{
|
||||
auto it = scopes_.find(scope_id);
|
||||
if (it == scopes_.end())
|
||||
return std::nullopt;
|
||||
|
||||
auto sym_it = it->second.symbols.find(name);
|
||||
if (sym_it != it->second.symbols.end())
|
||||
return sym_it->second;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<SymbolId> ScopeManager::FindInScopeChain(ScopeId scope_id, const std::string& name) const
|
||||
{
|
||||
auto current_id = scope_id;
|
||||
|
||||
while (current_id != kInvalidScopeId)
|
||||
{
|
||||
if (auto result = FindInScope(current_id, name))
|
||||
return result;
|
||||
|
||||
auto it = scopes_.find(current_id);
|
||||
if (it == scopes_.end() || !it->second.parent_scope_id)
|
||||
break;
|
||||
|
||||
current_id = *it->second.parent_scope_id;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const ScopeInfo* ScopeManager::GetScopeInfo(ScopeId scope_id) const
|
||||
{
|
||||
auto it = scopes_.find(scope_id);
|
||||
return it != scopes_.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
std::vector<SymbolId> ScopeManager::GetSymbolsInScope(ScopeId scope_id) const
|
||||
{
|
||||
std::vector<SymbolId> result;
|
||||
auto it = scopes_.find(scope_id);
|
||||
if (it != scopes_.end())
|
||||
{
|
||||
for (const auto& [name, id] : it->second.symbols)
|
||||
result.push_back(id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ScopeManager::Clear()
|
||||
{
|
||||
scopes_.clear();
|
||||
global_scope_id_ = kInvalidScopeId;
|
||||
next_scope_id_ = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,29 +1,130 @@
|
|||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "./interface.hpp"
|
||||
#include "./types.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
class ScopeManager
|
||||
|
||||
struct Scope
|
||||
{
|
||||
public:
|
||||
ScopeId CreateScope(ScopeKind kind, const ast::Location& range, std::optional<ScopeId> parent_scope_id = std::nullopt, std::optional<SymbolId> associated_symbol_id = std::nullopt);
|
||||
|
||||
void AddSymbol(ScopeId scope_id, const std::string& name, SymbolId symbol_id);
|
||||
std::optional<SymbolId> FindInScope(ScopeId scope_id, const std::string& name) const;
|
||||
std::optional<SymbolId> FindInScopeChain(ScopeId scope_id, const std::string& name) const;
|
||||
const ScopeInfo* GetScopeInfo(ScopeId scope_id) const;
|
||||
std::vector<SymbolId> GetSymbolsInScope(ScopeId scope_id) const;
|
||||
ScopeId GetGlobalScope() const { return global_scope_id_; }
|
||||
const std::unordered_map<ScopeId, ScopeInfo>& GetAllScopes() const { return scopes_; }
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
ScopeId next_scope_id_ = 1;
|
||||
ScopeId global_scope_id_ = kInvalidScopeId;
|
||||
std::unordered_map<ScopeId, ScopeInfo> scopes_;
|
||||
ScopeId id;
|
||||
ScopeKind kind;
|
||||
ast::Location range;
|
||||
std::optional<ScopeId> parent;
|
||||
std::optional<SymbolId> owner;
|
||||
std::unordered_map<std::string, SymbolId> symbols;
|
||||
};
|
||||
|
||||
}
|
||||
class ScopeIndex : public ISymbolIndex
|
||||
{
|
||||
public:
|
||||
void OnSymbolAdded(const Symbol&) override {}
|
||||
|
||||
void OnSymbolRemoved(SymbolId id) override
|
||||
{
|
||||
for (auto& [_, scope] : scopes_)
|
||||
{
|
||||
for (auto it = scope.symbols.begin(); it != scope.symbols.end();)
|
||||
{
|
||||
if (it->second == id)
|
||||
{
|
||||
it = scope.symbols.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Clear() override
|
||||
{
|
||||
scopes_.clear();
|
||||
next_scope_id_ = 1;
|
||||
global_scope_ = kInvalidScopeId;
|
||||
}
|
||||
|
||||
ScopeId CreateScope(ScopeKind kind, const ast::Location& range, std::optional<ScopeId> parent = std::nullopt, std::optional<SymbolId> owner = std::nullopt)
|
||||
{
|
||||
ScopeId id = next_scope_id_++;
|
||||
scopes_[id] = { id, kind, range, parent, owner, {} };
|
||||
|
||||
if (kind == ScopeKind::kGlobal)
|
||||
{
|
||||
global_scope_ = id;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void AddSymbol(ScopeId scope_id, const std::string& name, SymbolId symbol_id)
|
||||
{
|
||||
auto it = scopes_.find(scope_id);
|
||||
if (it != scopes_.end())
|
||||
{
|
||||
it->second.symbols[ToLower(name)] = symbol_id;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<SymbolId> FindInScope(ScopeId scope_id, const std::string& name) const
|
||||
{
|
||||
auto it = scopes_.find(scope_id);
|
||||
if (it == scopes_.end())
|
||||
return std::nullopt;
|
||||
|
||||
auto sym_it = it->second.symbols.find(ToLower(name));
|
||||
return sym_it != it->second.symbols.end() ?
|
||||
std::optional(sym_it->second) :
|
||||
std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<SymbolId> FindInScopeChain(ScopeId scope_id, const std::string& name) const
|
||||
{
|
||||
std::optional<ScopeId> current = scope_id;
|
||||
|
||||
while (current)
|
||||
{
|
||||
if (auto result = FindInScope(*current, name))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
auto it = scopes_.find(*current);
|
||||
current = it != scopes_.end() ? it->second.parent : std::nullopt;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const Scope* GetScope(ScopeId id) const
|
||||
{
|
||||
auto it = scopes_.find(id);
|
||||
return it != scopes_.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
ScopeId GetGlobalScope() const { return global_scope_; }
|
||||
|
||||
const std::unordered_map<ScopeId, Scope>& GetAllScopes() const
|
||||
{
|
||||
return scopes_;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::string ToLower(const std::string& s)
|
||||
{
|
||||
std::string result = s;
|
||||
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
|
||||
return result;
|
||||
}
|
||||
|
||||
ScopeId next_scope_id_ = 1;
|
||||
ScopeId global_scope_ = kInvalidScopeId;
|
||||
std::unordered_map<ScopeId, Scope> scopes_;
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol
|
||||
|
|
|
|||
|
|
@ -1,101 +0,0 @@
|
|||
#include <algorithm>
|
||||
#include "./store.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
SymbolId SymbolDefinitionStore::Add(SymbolDefinition def)
|
||||
{
|
||||
def.id = next_id_++;
|
||||
UpdateIndices(def);
|
||||
definitions_[def.id] = std::move(def);
|
||||
return def.id;
|
||||
}
|
||||
|
||||
const SymbolDefinition* SymbolDefinitionStore::Get(SymbolId id) const
|
||||
{
|
||||
auto it = definitions_.find(id);
|
||||
return it != definitions_.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
bool SymbolDefinitionStore::Update(SymbolId id, std::function<void(SymbolDefinition&)> updater)
|
||||
{
|
||||
auto it = definitions_.find(id);
|
||||
if (it == definitions_.end())
|
||||
return false;
|
||||
|
||||
RemoveFromIndices(it->second);
|
||||
updater(it->second);
|
||||
UpdateIndices(it->second);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SymbolDefinitionStore::Remove(SymbolId id)
|
||||
{
|
||||
auto it = definitions_.find(id);
|
||||
if (it == definitions_.end())
|
||||
return false;
|
||||
|
||||
RemoveFromIndices(it->second);
|
||||
definitions_.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<const SymbolDefinition*> SymbolDefinitionStore::GetAll() const
|
||||
{
|
||||
std::vector<const SymbolDefinition*> result;
|
||||
result.reserve(definitions_.size());
|
||||
for (const auto& [id, def] : definitions_)
|
||||
result.push_back(&def);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<const SymbolDefinition*> SymbolDefinitionStore::FindByName(const std::string& name) const
|
||||
{
|
||||
std::vector<const SymbolDefinition*> result;
|
||||
auto it = name_index_.find(name);
|
||||
if (it != name_index_.end())
|
||||
{
|
||||
for (SymbolId id : it->second)
|
||||
{
|
||||
if (auto def = Get(id))
|
||||
result.push_back(def);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<SymbolId> SymbolDefinitionStore::GetChildren(SymbolId parent_id) const
|
||||
{
|
||||
auto it = children_index_.find(parent_id);
|
||||
return it != children_index_.end() ? it->second : std::vector<SymbolId>{};
|
||||
}
|
||||
|
||||
void SymbolDefinitionStore::Clear()
|
||||
{
|
||||
definitions_.clear();
|
||||
name_index_.clear();
|
||||
children_index_.clear();
|
||||
next_id_ = 1;
|
||||
}
|
||||
|
||||
void SymbolDefinitionStore::UpdateIndices(const SymbolDefinition& def)
|
||||
{
|
||||
name_index_[def.name].push_back(def.id);
|
||||
|
||||
if (def.parent_id)
|
||||
children_index_[*def.parent_id].push_back(def.id);
|
||||
}
|
||||
|
||||
void SymbolDefinitionStore::RemoveFromIndices(const SymbolDefinition& def)
|
||||
{
|
||||
auto& name_vec = name_index_[def.name];
|
||||
name_vec.erase(std::remove(name_vec.begin(), name_vec.end(), def.id), name_vec.end());
|
||||
|
||||
if (def.parent_id)
|
||||
{
|
||||
auto& children_vec = children_index_[*def.parent_id];
|
||||
children_vec.erase(std::remove(children_vec.begin(), children_vec.end(), def.id), children_vec.end());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,35 +1,87 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include "./types.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
class SymbolDefinitionStore
|
||||
|
||||
class SymbolStore
|
||||
{
|
||||
public:
|
||||
SymbolId Add(SymbolDefinition def);
|
||||
SymbolId Add(Symbol def)
|
||||
{
|
||||
SymbolId id = next_id_++;
|
||||
// Update the symbol's ID
|
||||
std::visit([id](auto& s) { s.id = id; }, def.mutable_data());
|
||||
|
||||
const SymbolDefinition* Get(SymbolId id) const;
|
||||
auto [it, inserted] = definitions_.emplace(id, std::move(def));
|
||||
const auto& stored = it->second;
|
||||
by_name_[stored.name()].push_back(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
bool Update(SymbolId id, std::function<void(SymbolDefinition&)> updater);
|
||||
bool Remove(SymbolId id);
|
||||
void Clear();
|
||||
bool Remove(SymbolId id)
|
||||
{
|
||||
auto it = definitions_.find(id);
|
||||
if (it == definitions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<const SymbolDefinition*> GetAll() const;
|
||||
std::vector<const SymbolDefinition*> FindByName(const std::string& name) const;
|
||||
std::vector<SymbolId> GetChildren(SymbolId parent_id) const;
|
||||
const std::string& name = it->second.name();
|
||||
auto& ids = by_name_[name];
|
||||
ids.erase(std::remove(ids.begin(), ids.end(), id), ids.end());
|
||||
if (ids.empty())
|
||||
{
|
||||
by_name_.erase(name);
|
||||
}
|
||||
|
||||
definitions_.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
definitions_.clear();
|
||||
by_name_.clear();
|
||||
next_id_ = 1;
|
||||
}
|
||||
|
||||
// Accessor (snake_case) - 返回指针是合理的,因为可能不存在
|
||||
const Symbol* Get(SymbolId id) const
|
||||
{
|
||||
auto it = definitions_.find(id);
|
||||
return it != definitions_.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
// Accessor (snake_case) - 使用reference_wrapper替代指针
|
||||
std::vector<std::reference_wrapper<const Symbol>> GetAll() const
|
||||
{
|
||||
std::vector<std::reference_wrapper<const Symbol>> result;
|
||||
result.reserve(definitions_.size());
|
||||
for (const auto& [id, def] : definitions_)
|
||||
{
|
||||
result.push_back(std::cref(def));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<SymbolId> FindByName(const std::string& name) const
|
||||
{
|
||||
auto it = by_name_.find(name);
|
||||
return it != by_name_.end() ? it->second : std::vector<SymbolId>();
|
||||
}
|
||||
|
||||
private:
|
||||
SymbolId next_id_ = 1;
|
||||
std::unordered_map<SymbolId, SymbolDefinition> definitions_;
|
||||
std::unordered_map<std::string, std::vector<SymbolId>, utils::IHasher, utils::IEqualTo> name_index_;
|
||||
std::unordered_map<SymbolId, std::vector<SymbolId>> children_index_;
|
||||
|
||||
void UpdateIndices(const SymbolDefinition& def);
|
||||
void RemoveFromIndices(const SymbolDefinition& def);
|
||||
std::unordered_map<SymbolId, Symbol> definitions_;
|
||||
std::unordered_map<std::string, std::vector<SymbolId>> by_name_;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace lsp::language::symbol
|
||||
|
|
|
|||
|
|
@ -1,242 +0,0 @@
|
|||
#include <algorithm>
|
||||
#include "./table.hpp"
|
||||
#include "./builder.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
SymbolTable::SymbolTable()
|
||||
{
|
||||
}
|
||||
|
||||
void SymbolTable::Build(ast::ASTNode& root)
|
||||
{
|
||||
Clear();
|
||||
|
||||
Builder builder(*this);
|
||||
builder.Build(root);
|
||||
|
||||
// 构建完成后优化位置索引
|
||||
location_index_.RebuildIndex();
|
||||
}
|
||||
|
||||
void SymbolTable::Clear()
|
||||
{
|
||||
definition_store_.Clear();
|
||||
scope_manager_.Clear();
|
||||
location_index_.Clear();
|
||||
reference_graph_.Clear();
|
||||
inheritance_graph_.Clear();
|
||||
call_graph_.Clear();
|
||||
unit_imports_.clear();
|
||||
import_index_.clear();
|
||||
}
|
||||
|
||||
const SymbolDefinition* SymbolTable::GetDefinition(SymbolId id) const
|
||||
{
|
||||
return definition_store_.Get(id);
|
||||
}
|
||||
|
||||
std::vector<const SymbolDefinition*> SymbolTable::GetAllDefinitions() const
|
||||
{
|
||||
return definition_store_.GetAll();
|
||||
}
|
||||
|
||||
std::vector<const SymbolDefinition*> SymbolTable::FindDefinitionsByName(const std::string& name) const
|
||||
{
|
||||
return definition_store_.FindByName(name);
|
||||
}
|
||||
|
||||
std::optional<SymbolId> SymbolTable::FindSymbolAt(const ast::Location& location) const
|
||||
{
|
||||
return location_index_.FindSymbolAt(location);
|
||||
}
|
||||
|
||||
std::optional<SymbolId> SymbolTable::FindSymbol(const std::string& name, ScopeId scope_id) const
|
||||
{
|
||||
return scope_manager_.FindInScopeChain(scope_id, name);
|
||||
}
|
||||
|
||||
std::vector<SymbolId> SymbolTable::FindSymbolsByName(const std::string& name) const
|
||||
{
|
||||
std::vector<SymbolId> result;
|
||||
auto defs = definition_store_.FindByName(name);
|
||||
for (const auto* def : defs)
|
||||
result.push_back(def->id);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<ScopeId> SymbolTable::FindScopeAt(const ast::Location& location) const
|
||||
{
|
||||
return location_index_.FindScopeAt(location);
|
||||
}
|
||||
|
||||
std::optional<SymbolId> SymbolTable::FindReferenceAt(const ast::Location& location) const
|
||||
{
|
||||
return location_index_.FindReferenceAt(location);
|
||||
}
|
||||
|
||||
std::vector<SymbolId> SymbolTable::GetChildren(SymbolId symbol_id) const
|
||||
{
|
||||
return definition_store_.GetChildren(symbol_id);
|
||||
}
|
||||
|
||||
ScopeId SymbolTable::GetGlobalScope() const
|
||||
{
|
||||
return scope_manager_.GetGlobalScope();
|
||||
}
|
||||
|
||||
const std::vector<Reference>* SymbolTable::GetReferences(SymbolId symbol_id) const
|
||||
{
|
||||
return reference_graph_.GetReferences(symbol_id);
|
||||
}
|
||||
|
||||
std::optional<ast::Location> SymbolTable::GetDefinitionLocation(SymbolId symbol_id) const
|
||||
{
|
||||
return reference_graph_.GetDefinition(symbol_id);
|
||||
}
|
||||
|
||||
const std::vector<SymbolId>* SymbolTable::GetBaseClasses(SymbolId class_id) const
|
||||
{
|
||||
return inheritance_graph_.GetBaseClasses(class_id);
|
||||
}
|
||||
|
||||
const std::vector<SymbolId>* SymbolTable::GetDerivedClasses(SymbolId class_id) const
|
||||
{
|
||||
return inheritance_graph_.GetDerivedClasses(class_id);
|
||||
}
|
||||
|
||||
const std::vector<CallRelation>* SymbolTable::GetCallers(SymbolId function_id) const
|
||||
{
|
||||
return call_graph_.GetCallers(function_id);
|
||||
}
|
||||
|
||||
const std::vector<CallRelation>* SymbolTable::GetCallees(SymbolId function_id) const
|
||||
{
|
||||
return call_graph_.GetCallees(function_id);
|
||||
}
|
||||
|
||||
// ===== Unit 导入管理实现 =====
|
||||
|
||||
void SymbolTable::AddUnitImport(const std::string& unit_name, const ast::Location& location)
|
||||
{
|
||||
// 检查是否已经导入过(避免重复)
|
||||
if (import_index_.find(unit_name) != import_index_.end())
|
||||
{
|
||||
// 已经存在,可以选择:
|
||||
// 1. 忽略重复导入
|
||||
// 2. 记录多次导入(用于诊断)
|
||||
// 这里选择忽略
|
||||
return;
|
||||
}
|
||||
|
||||
size_t index = unit_imports_.size();
|
||||
unit_imports_.push_back(UnitImport{ unit_name, location });
|
||||
import_index_[unit_name] = index;
|
||||
}
|
||||
|
||||
const std::vector<UnitImport>& SymbolTable::GetUnitImports() const
|
||||
{
|
||||
return unit_imports_;
|
||||
}
|
||||
|
||||
std::optional<ast::Location> SymbolTable::FindImportLocation(const std::string& unit_name) const
|
||||
{
|
||||
auto it = import_index_.find(unit_name);
|
||||
if (it != import_index_.end() && it->second < unit_imports_.size())
|
||||
{
|
||||
return unit_imports_[it->second].location;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool SymbolTable::HasImport(const std::string& unit_name) const
|
||||
{
|
||||
return import_index_.find(unit_name) != import_index_.end();
|
||||
}
|
||||
|
||||
// ===== LSP 接口实现 =====
|
||||
|
||||
std::vector<const SymbolDefinition*> SymbolTable::GetDocumentSymbols() const
|
||||
{
|
||||
std::vector<const SymbolDefinition*> result;
|
||||
|
||||
for (const auto* def : definition_store_.GetAll())
|
||||
{
|
||||
// 只返回顶层符号(没有父符号的符号)
|
||||
if (!def->parent_id)
|
||||
result.push_back(def);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<const SymbolDefinition*> SymbolTable::GetWorkspaceSymbols(const std::string& query) const
|
||||
{
|
||||
if (query.empty())
|
||||
return definition_store_.GetAll();
|
||||
|
||||
// 使用小写查询字符串进行大小写无关的匹配
|
||||
std::string lower_query = utils::ToLower(query);
|
||||
|
||||
std::vector<const SymbolDefinition*> result;
|
||||
for (const auto* def : definition_store_.GetAll())
|
||||
{
|
||||
std::string lower_name = utils::ToLower(def->name);
|
||||
if (lower_name.find(lower_query) != std::string::npos)
|
||||
result.push_back(def);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ===== Builder 专用内部接口实现 =====
|
||||
|
||||
SymbolId SymbolTable::CreateSymbol(
|
||||
const std::string& name,
|
||||
SymbolKind kind,
|
||||
const ast::Location& location,
|
||||
ScopeId scope_id,
|
||||
std::optional<SymbolId> parent_id,
|
||||
const std::optional<std::string>& type_hint,
|
||||
bool is_class_method)
|
||||
{
|
||||
// 创建符号定义
|
||||
SymbolDefinition def;
|
||||
def.name = name;
|
||||
def.kind = kind;
|
||||
def.location = location;
|
||||
def.selection_range = location;
|
||||
def.type_hint = type_hint;
|
||||
def.parent_id = parent_id;
|
||||
def.is_class_method = is_class_method;
|
||||
|
||||
SymbolId symbol_id = definition_store_.Add(std::move(def));
|
||||
|
||||
// 统一处理所有索引更新
|
||||
scope_manager_.AddSymbol(scope_id, name, symbol_id);
|
||||
location_index_.AddSymbol(symbol_id, location);
|
||||
|
||||
// 添加定义引用
|
||||
reference_graph_.AddReference(symbol_id, location, true, false);
|
||||
location_index_.AddReference(symbol_id, location);
|
||||
|
||||
return symbol_id;
|
||||
}
|
||||
|
||||
ScopeId SymbolTable::CreateScope(ScopeKind kind, const ast::Location& range, std::optional<ScopeId> parent_scope_id, std::optional<SymbolId> associated_symbol_id)
|
||||
{
|
||||
ScopeId scope_id = scope_manager_.CreateScope(kind, range, parent_scope_id, associated_symbol_id);
|
||||
|
||||
// 添加到位置索引
|
||||
location_index_.AddScope(scope_id, range);
|
||||
|
||||
return scope_id;
|
||||
}
|
||||
|
||||
void SymbolTable::AddReference(SymbolId symbol_id, const ast::Location& location, bool is_write)
|
||||
{
|
||||
reference_graph_.AddReference(symbol_id, location, false, is_write);
|
||||
location_index_.AddReference(symbol_id, location);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,127 +1,148 @@
|
|||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "./graph/call.hpp"
|
||||
#include "./graph/inheritance.hpp"
|
||||
#include "./graph/reference.hpp"
|
||||
#include "./index/location.hpp"
|
||||
#include "./index/scope.hpp"
|
||||
#include "./store.hpp"
|
||||
#include "./scope.hpp"
|
||||
#include "./location_index.hpp"
|
||||
#include "./relations.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
class Builder;
|
||||
|
||||
// 统一的符号表接口
|
||||
class SymbolTable
|
||||
{
|
||||
public:
|
||||
SymbolTable();
|
||||
SymbolTable() = default;
|
||||
|
||||
// 构建符号表(完整构建)
|
||||
void Build(ast::ASTNode& root);
|
||||
// ===== Symbol Operations =====
|
||||
|
||||
// 清空所有数据
|
||||
void Clear();
|
||||
SymbolId CreateSymbol(Symbol symbol)
|
||||
{
|
||||
auto def = Symbol(std::move(symbol));
|
||||
auto id = store_.Add(def);
|
||||
|
||||
// ===== 第一层: 定义访问 =====
|
||||
const SymbolDefinition* GetDefinition(SymbolId id) const;
|
||||
std::vector<const SymbolDefinition*> GetAllDefinitions() const;
|
||||
std::vector<const SymbolDefinition*> FindDefinitionsByName(const std::string& name) const;
|
||||
// Notify all indexes
|
||||
location_index_.OnSymbolAdded(def);
|
||||
scope_index_.OnSymbolAdded(def);
|
||||
|
||||
// ===== 第二层: 作用域和位置查询 =====
|
||||
// 名称查找
|
||||
std::optional<SymbolId> FindSymbol(const std::string& name, ScopeId scope_id) const;
|
||||
std::vector<SymbolId> FindSymbolsByName(const std::string& name) const;
|
||||
return id;
|
||||
}
|
||||
|
||||
// 位置查找
|
||||
std::optional<SymbolId> FindSymbolAt(const ast::Location& location) const;
|
||||
std::optional<ScopeId> FindScopeAt(const ast::Location& location) const;
|
||||
std::optional<SymbolId> FindReferenceAt(const ast::Location& location) const;
|
||||
bool RemoveSymbol(SymbolId id)
|
||||
{
|
||||
// Notify all components
|
||||
location_index_.OnSymbolRemoved(id);
|
||||
scope_index_.OnSymbolRemoved(id);
|
||||
reference_graph_.OnSymbolRemoved(id);
|
||||
inheritance_graph_.OnSymbolRemoved(id);
|
||||
call_graph_.OnSymbolRemoved(id);
|
||||
|
||||
// 作用域查询
|
||||
std::vector<SymbolId> GetChildren(SymbolId symbol_id) const;
|
||||
ScopeId GetGlobalScope() const;
|
||||
return store_.Remove(id);
|
||||
}
|
||||
|
||||
// ===== 第三层: 关系访问 =====
|
||||
// 返回引用避免拷贝,使用指针表示可能为空
|
||||
const std::vector<Reference>* GetReferences(SymbolId symbol_id) const;
|
||||
std::optional<ast::Location> GetDefinitionLocation(SymbolId symbol_id) const;
|
||||
const std::vector<SymbolId>* GetBaseClasses(SymbolId class_id) const;
|
||||
const std::vector<SymbolId>* GetDerivedClasses(SymbolId class_id) const;
|
||||
const std::vector<CallRelation>* GetCallers(SymbolId function_id) const;
|
||||
const std::vector<CallRelation>* GetCallees(SymbolId function_id) const;
|
||||
void Clear()
|
||||
{
|
||||
store_.Clear();
|
||||
location_index_.Clear();
|
||||
scope_index_.Clear();
|
||||
reference_graph_.Clear();
|
||||
inheritance_graph_.Clear();
|
||||
call_graph_.Clear();
|
||||
}
|
||||
|
||||
// ===== Unit 导入管理 =====
|
||||
// 添加单元导入
|
||||
void AddUnitImport(const std::string& unit_name, const ast::Location& location);
|
||||
// ===== Basic Queries =====
|
||||
|
||||
// 获取所有导入的单元
|
||||
const std::vector<UnitImport>& GetUnitImports() const;
|
||||
std::vector<SymbolId> FindSymbolsByName(const std::string& name) const
|
||||
{
|
||||
return store_.FindByName(name);
|
||||
}
|
||||
|
||||
// 查找特定单元的导入位置(用于跳转到定义)
|
||||
std::optional<ast::Location> FindImportLocation(const std::string& unit_name) const;
|
||||
std::optional<SymbolId> FindSymbolAt(const ast::Location& location) const
|
||||
{
|
||||
return location_index_.FindSymbolAt(location);
|
||||
}
|
||||
|
||||
// 检查是否导入了某个单元
|
||||
bool HasImport(const std::string& unit_name) const;
|
||||
// ===== Accessors (snake_case) =====
|
||||
|
||||
// ===== LSP 便捷接口 =====
|
||||
// 获取文档符号(用于 textDocument/documentSymbol)
|
||||
std::vector<const SymbolDefinition*> GetDocumentSymbols() const;
|
||||
// Get single definition - 返回指针是合理的,因为可能不存在
|
||||
const Symbol* definition(SymbolId id) const
|
||||
{
|
||||
return store_.Get(id);
|
||||
}
|
||||
|
||||
// 获取工作区符号(用于 workspace/symbol)
|
||||
std::vector<const SymbolDefinition*> GetWorkspaceSymbols(const std::string& query = "") const;
|
||||
// Get all definitions - 使用reference_wrapper替代指针
|
||||
std::vector<std::reference_wrapper<const Symbol>> all_definitions() const
|
||||
{
|
||||
return store_.GetAll();
|
||||
}
|
||||
|
||||
// ===== Builder 专用内部接口 =====
|
||||
// 统一的符号创建接口,自动处理所有索引更新
|
||||
SymbolId CreateSymbol(
|
||||
const std::string& name,
|
||||
SymbolKind kind,
|
||||
const ast::Location& location,
|
||||
ScopeId scope_id,
|
||||
std::optional<SymbolId> parent_id = std::nullopt,
|
||||
const std::optional<std::string>& type_hint = std::nullopt,
|
||||
bool is_class_method = false);
|
||||
// Access indexes (non-const)
|
||||
index::Location& locations() { return location_index_; }
|
||||
index::Scope& scopes() { return scope_index_; }
|
||||
|
||||
// 创建作用域(可以选择关联符号)
|
||||
ScopeId CreateScope(
|
||||
ScopeKind kind,
|
||||
const ast::Location& range,
|
||||
std::optional<ScopeId> parent_scope_id = std::nullopt,
|
||||
std::optional<SymbolId> associated_symbol_id = std::nullopt);
|
||||
// Access indexes (const)
|
||||
const index::Location& locations() const { return location_index_; }
|
||||
const index::Scope& scopes() const { return scope_index_; }
|
||||
|
||||
// 添加引用
|
||||
void AddReference(SymbolId symbol_id, const ast::Location& location, bool is_write = false);
|
||||
// Access graphs (non-const)
|
||||
graph::Reference& references() { return reference_graph_; }
|
||||
graph::Inheritance& inheritance() { return inheritance_graph_; }
|
||||
graph::Call& calls() { return call_graph_; }
|
||||
|
||||
// 获取内部组件的访问权限(供 Builder 使用)
|
||||
SymbolDefinitionStore& GetDefinitionStore() { return definition_store_; }
|
||||
ScopeManager& GetScopeManager() { return scope_manager_; }
|
||||
LocationIndex& GetLocationIndex() { return location_index_; }
|
||||
ReferenceGraph& GetReferenceGraph() { return reference_graph_; }
|
||||
InheritanceGraph& GetInheritanceGraph() { return inheritance_graph_; }
|
||||
CallGraph& GetCallGraph() { return call_graph_; }
|
||||
// Access graphs (const)
|
||||
const graph::Reference& references() const { return reference_graph_; }
|
||||
const graph::Inheritance& inheritance() const { return inheritance_graph_; }
|
||||
const graph::Call& calls() const { return call_graph_; }
|
||||
|
||||
const SymbolDefinitionStore& GetDefinitionStore() const { return definition_store_; }
|
||||
const ScopeManager& GetScopeManager() const { return scope_manager_; }
|
||||
const LocationIndex& GetLocationIndex() const { return location_index_; }
|
||||
const ReferenceGraph& GetReferenceGraph() const { return reference_graph_; }
|
||||
const InheritanceGraph& GetInheritanceGraph() const { return inheritance_graph_; }
|
||||
const CallGraph& GetCallGraph() const { return call_graph_; }
|
||||
// ===== Convenience Methods (shortcuts for common operations) =====
|
||||
|
||||
// Create scope
|
||||
ScopeId CreateScope(ScopeKind kind, const ast::Location& range, std::optional<ScopeId> parent = std::nullopt, std::optional<SymbolId> owner = std::nullopt)
|
||||
{
|
||||
return scope_index_.CreateScope(kind, range, parent, owner);
|
||||
}
|
||||
|
||||
// Add symbol to scope
|
||||
void AddSymbolToScope(ScopeId scope_id, const std::string& name, SymbolId symbol_id)
|
||||
{
|
||||
scope_index_.AddSymbol(scope_id, name, symbol_id);
|
||||
}
|
||||
|
||||
// Add reference
|
||||
void AddReference(SymbolId symbol_id, const ast::Location& location, bool is_definition = false, bool is_write = false)
|
||||
{
|
||||
reference_graph_.AddReference(symbol_id, location, is_definition, is_write);
|
||||
}
|
||||
|
||||
// Add inheritance relationship
|
||||
void AddInheritance(SymbolId derived, SymbolId base)
|
||||
{
|
||||
inheritance_graph_.AddInheritance(derived, base);
|
||||
}
|
||||
|
||||
// Add call relationship
|
||||
void AddCall(SymbolId caller, SymbolId callee, const ast::Location& location)
|
||||
{
|
||||
call_graph_.AddCall(caller, callee, location);
|
||||
}
|
||||
|
||||
private:
|
||||
// 第一层
|
||||
SymbolDefinitionStore definition_store_;
|
||||
// Storage layer
|
||||
SymbolStore store_;
|
||||
|
||||
// 第二层
|
||||
ScopeManager scope_manager_;
|
||||
LocationIndex location_index_;
|
||||
// Index layer
|
||||
index::Location location_index_;
|
||||
index::Scope scope_index_;
|
||||
|
||||
// 第三层
|
||||
ReferenceGraph reference_graph_;
|
||||
InheritanceGraph inheritance_graph_;
|
||||
CallGraph call_graph_;
|
||||
|
||||
// Unit 导入
|
||||
std::vector<UnitImport> unit_imports_;
|
||||
// 快速查找: unit_name -> index in unit_imports_
|
||||
std::unordered_map<std::string, size_t, utils::IHasher, utils::IEqualTo> import_index_;
|
||||
// Graph layer
|
||||
graph::Reference reference_graph_;
|
||||
graph::Inheritance inheritance_graph_;
|
||||
graph::Call call_graph_;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace lsp::language::symbol
|
||||
|
|
|
|||
|
|
@ -1,65 +1,209 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "../ast/types.hpp"
|
||||
#include "../../utils/string.hpp"
|
||||
#include "../../protocol/protocol.hpp"
|
||||
|
||||
namespace lsp::language::symbol
|
||||
{
|
||||
using SymbolKind = protocol::SymbolKind;
|
||||
enum class ScopeKind
|
||||
{
|
||||
Global,
|
||||
Unit,
|
||||
Class,
|
||||
Function,
|
||||
Block
|
||||
};
|
||||
|
||||
// 符号ID类型
|
||||
// ===== Basic Types =====
|
||||
|
||||
using SymbolId = uint64_t;
|
||||
constexpr SymbolId kInvalidSymbolId = 0;
|
||||
|
||||
using ScopeId = uint64_t;
|
||||
constexpr ScopeId kInvalidScopeId = 0;
|
||||
|
||||
using SymbolKind = protocol::SymbolKind;
|
||||
|
||||
enum class ScopeKind
|
||||
{
|
||||
kGlobal,
|
||||
kUnit,
|
||||
kClass,
|
||||
kFunction,
|
||||
kAnonymousFunction,
|
||||
kBlock
|
||||
};
|
||||
|
||||
enum class VariableScope
|
||||
{
|
||||
kAutomatic,
|
||||
kStatic,
|
||||
kGlobal,
|
||||
kParameter,
|
||||
kField
|
||||
};
|
||||
|
||||
enum class UnitVisibility
|
||||
{
|
||||
kInterface,
|
||||
kImplementation
|
||||
};
|
||||
|
||||
// ===== Parameters =====
|
||||
|
||||
struct Parameter
|
||||
{
|
||||
std::string name;
|
||||
std::optional<std::string> type;
|
||||
std::optional<std::string> default_value;
|
||||
};
|
||||
|
||||
struct UnitImport
|
||||
{
|
||||
std::string unit_name;
|
||||
ast::Location location;
|
||||
};
|
||||
|
||||
struct SymbolDefinition
|
||||
// ===== Symbol Types (all fields directly expanded) =====
|
||||
|
||||
struct Function
|
||||
{
|
||||
static constexpr SymbolKind kind = SymbolKind::Function;
|
||||
|
||||
SymbolId id = kInvalidSymbolId;
|
||||
std::string name;
|
||||
SymbolKind kind;
|
||||
ast::Location location;
|
||||
ast::Location selection_range;
|
||||
ast::Location selection_range; // Name identifier location (for cursor)
|
||||
ast::Location range; // Full range (including modifiers, body)
|
||||
|
||||
std::optional<std::string> type_hint;
|
||||
ast::Location declaration_range;
|
||||
std::optional<ast::Location> implementation_range;
|
||||
std::vector<Parameter> parameters;
|
||||
std::optional<std::string> return_type;
|
||||
|
||||
std::string detail;
|
||||
std::vector<UnitImport> imports;
|
||||
std::optional<UnitVisibility> unit_visibility;
|
||||
|
||||
std::optional<ast::AccessModifier> access_modifier;
|
||||
std::optional<ast::MethodModifier> method_modifier;
|
||||
std::optional<ast::ReferenceModifier> reference_modifier;
|
||||
bool is_class_method = false;
|
||||
|
||||
std::optional<SymbolId> parent_id;
|
||||
bool HasImplementation() const
|
||||
{
|
||||
return implementation_range.has_value();
|
||||
}
|
||||
};
|
||||
|
||||
struct ScopeInfo
|
||||
struct Class
|
||||
{
|
||||
ScopeId scope_id;
|
||||
ScopeKind kind;
|
||||
static constexpr SymbolKind kind = SymbolKind::Class;
|
||||
|
||||
SymbolId id = kInvalidSymbolId;
|
||||
std::string name;
|
||||
ast::Location selection_range;
|
||||
ast::Location range;
|
||||
std::optional<ScopeId> parent_scope_id;
|
||||
std::optional<SymbolId> associated_symbol_id;
|
||||
std::unordered_map<std::string, SymbolId, utils::IHasher, utils::IEqualTo> symbols;
|
||||
|
||||
std::optional<UnitVisibility> unit_visibility;
|
||||
|
||||
std::vector<SymbolId> base_classes;
|
||||
std::vector<SymbolId> members;
|
||||
|
||||
std::vector<UnitImport> imports;
|
||||
};
|
||||
|
||||
struct Method
|
||||
{
|
||||
static constexpr SymbolKind kind = SymbolKind::Method;
|
||||
|
||||
SymbolId id = kInvalidSymbolId;
|
||||
std::string name;
|
||||
ast::Location selection_range;
|
||||
ast::Location range;
|
||||
|
||||
// Location information
|
||||
ast::Location declaration_range;
|
||||
std::optional<ast::Location> implementation_range;
|
||||
|
||||
// Method-specific
|
||||
ast::MethodKind method_kind = ast::MethodKind::kOrdinary;
|
||||
ast::AccessModifier access = ast::AccessModifier::kPublic;
|
||||
std::optional<ast::MethodModifier> method_modifier =
|
||||
ast::MethodModifier::kNone;
|
||||
bool is_static = false;
|
||||
std::vector<Parameter> parameters;
|
||||
std::optional<std::string> return_type;
|
||||
|
||||
std::vector<UnitImport> imports;
|
||||
|
||||
bool HasImplementation() const
|
||||
{
|
||||
return implementation_range.has_value();
|
||||
}
|
||||
};
|
||||
|
||||
struct Property
|
||||
{
|
||||
static constexpr SymbolKind kind = SymbolKind::Property;
|
||||
|
||||
SymbolId id = kInvalidSymbolId;
|
||||
std::string name;
|
||||
ast::Location selection_range;
|
||||
ast::Location range;
|
||||
|
||||
// Property-specific
|
||||
ast::AccessModifier access = ast::AccessModifier::kPublic;
|
||||
std::optional<std::string> type;
|
||||
std::optional<SymbolId> getter;
|
||||
std::optional<SymbolId> setter;
|
||||
};
|
||||
|
||||
struct Field
|
||||
{
|
||||
static constexpr SymbolKind kind = SymbolKind::Field;
|
||||
|
||||
SymbolId id = kInvalidSymbolId;
|
||||
std::string name;
|
||||
ast::Location selection_range;
|
||||
ast::Location range;
|
||||
|
||||
ast::AccessModifier access = ast::AccessModifier::kPublic;
|
||||
std::optional<ast::ReferenceModifier> reference_modifier;
|
||||
std::optional<std::string> type;
|
||||
bool is_static = false;
|
||||
};
|
||||
|
||||
struct Variable
|
||||
{
|
||||
static constexpr SymbolKind kind = SymbolKind::Variable;
|
||||
|
||||
SymbolId id = kInvalidSymbolId;
|
||||
std::string name;
|
||||
ast::Location selection_range;
|
||||
ast::Location range;
|
||||
|
||||
std::optional<std::string> type;
|
||||
std::optional<ast::ReferenceModifier> reference_modifier;
|
||||
VariableScope storage = VariableScope::kAutomatic;
|
||||
std::optional<UnitVisibility> unit_visibility;
|
||||
bool has_initializer = false;
|
||||
};
|
||||
|
||||
struct Constant
|
||||
{
|
||||
static constexpr SymbolKind kind = SymbolKind::Constant;
|
||||
|
||||
SymbolId id = kInvalidSymbolId;
|
||||
std::string name;
|
||||
ast::Location selection_range;
|
||||
ast::Location range;
|
||||
|
||||
std::optional<std::string> type;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
struct Unit
|
||||
{
|
||||
static constexpr SymbolKind kind = SymbolKind::Namespace;
|
||||
|
||||
SymbolId id = kInvalidSymbolId;
|
||||
std::string name;
|
||||
ast::Location selection_range;
|
||||
ast::Location range;
|
||||
|
||||
std::vector<UnitImport> interface_imports;
|
||||
std::vector<UnitImport> implementation_imports;
|
||||
};
|
||||
|
||||
struct Reference
|
||||
|
|
@ -70,11 +214,76 @@ namespace lsp::language::symbol
|
|||
bool is_write;
|
||||
};
|
||||
|
||||
struct CallRelation
|
||||
struct Call
|
||||
{
|
||||
SymbolId caller;
|
||||
SymbolId callee;
|
||||
ast::Location call_location;
|
||||
ast::Location call_site;
|
||||
};
|
||||
|
||||
// ===== Symbol Data Variant =====
|
||||
|
||||
using SymbolData = std::variant<Function, Class, Method, Property, Field, Variable, Constant, Unit>;
|
||||
|
||||
// ===== Symbol =====
|
||||
|
||||
class Symbol
|
||||
{
|
||||
public:
|
||||
explicit Symbol(SymbolData data) : data_(std::move(data)) {}
|
||||
|
||||
// Type checking and conversion
|
||||
template<typename T>
|
||||
bool Is() const
|
||||
{
|
||||
return std::holds_alternative<T>(data_);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T* As() const
|
||||
{
|
||||
return std::get_if<T>(&data_);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* As()
|
||||
{
|
||||
return std::get_if<T>(&data_);
|
||||
}
|
||||
|
||||
// Accessors (snake_case per Google style)
|
||||
const SymbolData& data() const { return data_; }
|
||||
SymbolData& mutable_data() { return data_; }
|
||||
|
||||
// Common accessors (all symbol types have these)
|
||||
SymbolId id() const
|
||||
{
|
||||
return std::visit([](const auto& s) { return s.id; }, data_);
|
||||
}
|
||||
|
||||
const std::string& name() const
|
||||
{
|
||||
return std::visit([](const auto& s) -> const auto& { return s.name; },
|
||||
data_);
|
||||
}
|
||||
|
||||
ast::Location selection_range() const
|
||||
{
|
||||
return std::visit([](const auto& s) { return s.selection_range; }, data_);
|
||||
}
|
||||
|
||||
ast::Location range() const
|
||||
{
|
||||
return std::visit([](const auto& s) { return s.range; }, data_);
|
||||
}
|
||||
|
||||
SymbolKind kind() const
|
||||
{
|
||||
return std::visit([](const auto& s) { return s.kind; }, data_);
|
||||
}
|
||||
|
||||
private:
|
||||
SymbolData data_;
|
||||
};
|
||||
|
||||
} // namespace lsp::language::symbol
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -3,6 +3,7 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
#include "../../src/language/ast/types.hpp"
|
||||
#include "../../src/language/ast/deserializer.hpp"
|
||||
|
||||
|
|
@ -188,6 +189,7 @@ namespace lsp::language::ast::debug
|
|||
void PrintIndent(std::optional<bool> is_last = std::nullopt);
|
||||
void PrintTreePrefix(bool is_last = false);
|
||||
std::string GetIndent() const;
|
||||
void EmitChildren(std::vector<std::function<void(bool)>>&& children);
|
||||
|
||||
// 颜色辅助
|
||||
const char* GetColor(const char* color) const;
|
||||
|
|
@ -196,11 +198,11 @@ namespace lsp::language::ast::debug
|
|||
// 节点信息输出
|
||||
void PrintNodeHeader(const std::string& type_name, const Location& loc);
|
||||
void PrintLocation(const Location& loc);
|
||||
void PrintSourceSnippet(const Location& loc);
|
||||
void PrintKeyValue(const std::string& key, const std::string& value, const Location& location, const char* value_color);
|
||||
void PrintKeyValue(const std::string& key, const std::string& value, const char* value_color = nullptr);
|
||||
void PrintKeyValue(const std::string& key, int value);
|
||||
void PrintKeyValue(const std::string& key, bool value);
|
||||
void PrintSourceSnippet(const Location& loc, bool is_last_child);
|
||||
void PrintKeyValue(const std::string& key, const std::string& value, const Location& location, const char* value_color, std::optional<bool> is_last = std::nullopt);
|
||||
void PrintKeyValue(const std::string& key, const std::string& value, const char* value_color = nullptr, std::optional<bool> is_last = std::nullopt);
|
||||
void PrintKeyValue(const std::string& key, int value, std::optional<bool> is_last = std::nullopt);
|
||||
void PrintKeyValue(const std::string& key, bool value, std::optional<bool> is_last = std::nullopt);
|
||||
|
||||
// 表达式和语句打印
|
||||
void PrintExpression(const Expression* expr, const std::string& label = "", bool is_last = false);
|
||||
|
|
|
|||
|
|
@ -68,17 +68,11 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
|
|||
set(SOURCES
|
||||
./test.cpp
|
||||
./debug_printer.cpp
|
||||
../../src/utils/string.cpp
|
||||
../../src/language/ast/deserializer.cpp
|
||||
../../src/language/ast/detail.cpp
|
||||
../../src/language/ast/tree_sitter_utils.cpp
|
||||
../../src/language/symbol/builder.cpp
|
||||
../../src/language/symbol/location_index.cpp
|
||||
../../src/language/symbol/relations.cpp
|
||||
../../src/language/symbol/store.cpp
|
||||
../../src/language/symbol/scope.cpp
|
||||
../../src/language/symbol/table.cpp
|
||||
../../src/language/symbol/builder.cpp
|
||||
../../src/utils/string.cpp
|
||||
../../src/tree-sitter/scanner.c
|
||||
../../src/tree-sitter/parser.c)
|
||||
|
||||
|
|
@ -104,4 +98,3 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
|||
$<$<CONFIG:Release>:-O3>
|
||||
)
|
||||
endif()
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -2,39 +2,11 @@
|
|||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "../../src/language/symbol/table.hpp"
|
||||
|
||||
namespace lsp::language::symbol::debug
|
||||
{
|
||||
// ==================== 颜色和样式 ====================
|
||||
|
||||
namespace Color
|
||||
{
|
||||
// ANSI 颜色码
|
||||
constexpr const char* Reset = "\033[0m";
|
||||
constexpr const char* Bold = "\033[1m";
|
||||
constexpr const char* Dim = "\033[2m";
|
||||
|
||||
// 前景色
|
||||
constexpr const char* Black = "\033[30m";
|
||||
constexpr const char* Red = "\033[31m";
|
||||
constexpr const char* Green = "\033[32m";
|
||||
constexpr const char* Yellow = "\033[33m";
|
||||
constexpr const char* Blue = "\033[34m";
|
||||
constexpr const char* Magenta = "\033[35m";
|
||||
constexpr const char* Cyan = "\033[36m";
|
||||
constexpr const char* White = "\033[37m";
|
||||
|
||||
// 亮色
|
||||
constexpr const char* BrightBlack = "\033[90m";
|
||||
constexpr const char* BrightRed = "\033[91m";
|
||||
constexpr const char* BrightGreen = "\033[92m";
|
||||
constexpr const char* BrightYellow = "\033[93m";
|
||||
constexpr const char* BrightBlue = "\033[94m";
|
||||
constexpr const char* BrightMagenta = "\033[95m";
|
||||
constexpr const char* BrightCyan = "\033[96m";
|
||||
constexpr const char* BrightWhite = "\033[97m";
|
||||
}
|
||||
|
||||
// ==================== 打印选项 ====================
|
||||
|
||||
|
|
@ -134,32 +106,21 @@ namespace lsp::language::symbol::debug
|
|||
std::string Color(const char* color_code) const;
|
||||
std::string Bold(const std::string& text) const;
|
||||
std::string Dim(const std::string& text) const;
|
||||
|
||||
void PrintSymbolData(const Symbol& symbol, std::ostream& os, int depth);
|
||||
void PrintSymbolWithChildren(SymbolId id, const std::unordered_multimap<SymbolId, SymbolId>& children, std::ostream& os, int depth, int current_depth);
|
||||
std::string GetSymbolIcon(const Symbol& symbol) const;
|
||||
};
|
||||
|
||||
// ==================== 快速打印函数 ====================
|
||||
|
||||
// 打印所有内容(带统计)
|
||||
void Print(const SymbolTable& table, std::ostream& os = std::cout);
|
||||
|
||||
// 打印概览
|
||||
void PrintOverview(const SymbolTable& table, std::ostream& os = std::cout);
|
||||
|
||||
// 打印统计信息
|
||||
void PrintStats(const SymbolTable& table, std::ostream& os = std::cout);
|
||||
|
||||
// 打印符号树
|
||||
void PrintSymbolTree(const SymbolTable& table, std::ostream& os = std::cout);
|
||||
|
||||
// 打印作用域树
|
||||
void PrintScopeTree(const SymbolTable& table, std::ostream& os = std::cout);
|
||||
|
||||
// 搜索并打印
|
||||
void Find(const SymbolTable& table, const std::string& name, std::ostream& os = std::cout);
|
||||
|
||||
// 紧凑打印
|
||||
void PrintCompact(const SymbolTable& table, std::ostream& os = std::cout);
|
||||
|
||||
// 详细打印
|
||||
void PrintVerbose(const SymbolTable& table, std::ostream& os = std::cout);
|
||||
|
||||
} // namespace lsp::language::symbol::debug
|
||||
|
|
|
|||
|
|
@ -1,8 +1,15 @@
|
|||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cctype>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <ctime>
|
||||
|
||||
extern "C" {
|
||||
#include <tree_sitter/api.h>
|
||||
|
|
@ -256,17 +263,28 @@ std::string NodeKindToString(ast::NodeKind kind)
|
|||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ast::NodeKind::kProgram: return "Program";
|
||||
case ast::NodeKind::kFunctionDefinition: return "FunctionDefinition";
|
||||
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";
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -284,11 +302,9 @@ void PrintASTStructure(const ast::ParseResult& parse_result, std::ostream& os)
|
|||
if (!stmt)
|
||||
continue;
|
||||
|
||||
// 修复:使用 kind 和 span
|
||||
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;
|
||||
|
|
@ -308,11 +324,591 @@ void PrintASTStructure(const ast::ParseResult& parse_result, std::ostream& os)
|
|||
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";
|
||||
|
|
@ -321,7 +917,6 @@ void AnalyzeFile(const Options& options)
|
|||
std::cout << "Input file: " << options.input_file << "\n";
|
||||
}
|
||||
|
||||
// 1. 读取源文件
|
||||
if (options.verbose)
|
||||
std::cout << "Reading file...\n";
|
||||
|
||||
|
|
@ -335,7 +930,6 @@ void AnalyzeFile(const Options& options)
|
|||
std::cout << "----------------------------------------\n\n";
|
||||
}
|
||||
|
||||
// 2. 使用 Tree-Sitter 解析
|
||||
if (options.verbose)
|
||||
std::cout << "Parsing with Tree-Sitter...\n";
|
||||
|
||||
|
|
@ -349,7 +943,6 @@ void AnalyzeFile(const Options& options)
|
|||
std::cout << "Root node child count: " << ts_node_child_count(root) << "\n\n";
|
||||
}
|
||||
|
||||
// 3. 反序列化为 AST
|
||||
if (options.verbose)
|
||||
std::cout << "Deserializing to AST...\n";
|
||||
|
||||
|
|
@ -373,14 +966,14 @@ void AnalyzeFile(const Options& options)
|
|||
PrintASTStructure(parse_result, std::cout);
|
||||
}
|
||||
|
||||
// 4. 构建符号表
|
||||
if (options.verbose)
|
||||
std::cout << "Building symbol table...\n";
|
||||
|
||||
symbol::SymbolTable table;
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
table.Build(*parse_result.root);
|
||||
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);
|
||||
|
|
@ -388,7 +981,6 @@ void AnalyzeFile(const Options& options)
|
|||
if (options.verbose)
|
||||
std::cout << "Symbol table built in " << duration.count() << " ms\n\n";
|
||||
|
||||
// 5. 准备输出流
|
||||
std::ostream* out = &std::cout;
|
||||
std::ofstream file_out;
|
||||
|
||||
|
|
@ -402,7 +994,6 @@ void AnalyzeFile(const Options& options)
|
|||
}
|
||||
out = &file_out;
|
||||
|
||||
// 写入文件头
|
||||
*out << "Symbol Table Analysis\n";
|
||||
*out << "Source: " << options.input_file << "\n";
|
||||
auto now = std::chrono::system_clock::now();
|
||||
|
|
@ -411,9 +1002,6 @@ void AnalyzeFile(const Options& options)
|
|||
*out << std::string(80, '=') << "\n\n";
|
||||
}
|
||||
|
||||
// 6. ✅ 使用新的 debug_print API
|
||||
|
||||
// 配置打印选项
|
||||
symbol::debug::PrintOptions print_opts;
|
||||
if (options.compact_mode)
|
||||
print_opts = symbol::debug::PrintOptions::Compact();
|
||||
|
|
@ -427,38 +1015,30 @@ void AnalyzeFile(const Options& options)
|
|||
|
||||
print_opts.show_references = options.print_references || options.verbose;
|
||||
|
||||
// 创建打印器
|
||||
symbol::debug::DebugPrinter printer(table, print_opts);
|
||||
|
||||
// 7. 执行查询或打印
|
||||
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)
|
||||
|
|
@ -491,7 +1071,6 @@ void AnalyzeFile(const Options& options)
|
|||
printed_anything = true;
|
||||
}
|
||||
|
||||
// 总是打印统计信息
|
||||
if (printed_anything)
|
||||
{
|
||||
*out << "\n";
|
||||
|
|
@ -499,24 +1078,22 @@ void AnalyzeFile(const Options& options)
|
|||
printer.PrintStatistics(*out);
|
||||
}
|
||||
|
||||
// 8. 完成
|
||||
if (file_out.is_open())
|
||||
{
|
||||
file_out.close();
|
||||
std::cout << "✓ Symbol table exported to: " << options.output_file << "\n";
|
||||
}
|
||||
|
||||
// 9. 打印摘要
|
||||
if (!options.compact_mode)
|
||||
{
|
||||
std::cout << "\n========================================\n";
|
||||
std::cout << "Summary:\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.GetAllDefinitions().size() << "\n";
|
||||
std::cout << " Scopes: " << table.GetScopeManager().GetAllScopes().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";
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue