#include #include "./builder.hpp" namespace lsp::language::symbol { Builder::Builder(SymbolTable& table) : table_(table), current_scope_id_(kInvalidScopeId), in_interface_section_(false) { } void Builder::Build(ast::ASTNode& root) { root.Accept(*this); } // ===== Helper Methods ===== SymbolId Builder::CreateSymbol(const std::string& name, SymbolKind kind, const ast::Location& location, const std::optional& type_hint) { std::optional visibility; if (in_interface_section_) visibility = UnitVisibility::kInterface; Symbol symbol = [&]() -> Symbol { switch (kind) { case SymbolKind::Class: { Class cls; cls.name = name; cls.selection_range = location; cls.range = location; cls.unit_visibility = visibility; return Symbol(std::move(cls)); } case SymbolKind::Function: { Function fn; fn.name = name; fn.selection_range = location; fn.range = location; fn.declaration_range = location; fn.return_type = type_hint; fn.unit_visibility = visibility; return Symbol(std::move(fn)); } case SymbolKind::Method: { Method method; method.name = name; method.selection_range = location; method.range = location; method.declaration_range = location; method.return_type = type_hint; return Symbol(std::move(method)); } case SymbolKind::Property: { Property property; property.name = name; property.selection_range = location; property.range = location; property.type = type_hint; return Symbol(std::move(property)); } case SymbolKind::Field: { Field field; field.name = name; field.selection_range = location; field.range = location; field.type = type_hint; return Symbol(std::move(field)); } case SymbolKind::Variable: { Variable var; var.name = name; var.selection_range = location; var.range = location; var.type = type_hint; var.unit_visibility = visibility; return Symbol(std::move(var)); } case SymbolKind::Constant: { Constant constant; constant.name = name; constant.selection_range = location; constant.range = location; constant.type = type_hint; return Symbol(std::move(constant)); } default: { Variable fallback; fallback.name = name; fallback.selection_range = location; fallback.range = location; fallback.type = type_hint; fallback.unit_visibility = visibility; return Symbol(std::move(fallback)); } } }(); SymbolId id = table_.CreateSymbol(std::move(symbol)); if (current_scope_id_ != kInvalidScopeId) { table_.AddSymbolToScope(current_scope_id_, name, id); } return id; } SymbolId Builder::CreateSymbol(const std::string& name, SymbolKind kind, const ast::ASTNode& node, const std::optional& type_hint) { return CreateSymbol(name, kind, node.span, type_hint); } SymbolId Builder::CreateFunctionSymbol( const std::string& name, const ast::Location& location, const std::vector>& parameters, const std::optional& return_type) { std::optional visibility; if (in_interface_section_) visibility = UnitVisibility::kInterface; Function fn; fn.name = name; fn.selection_range = location; fn.range = location; fn.declaration_range = location; fn.return_type = ExtractTypeName(return_type); fn.unit_visibility = visibility; fn.parameters = BuildParameters(parameters); Symbol symbol(std::move(fn)); SymbolId id = table_.CreateSymbol(std::move(symbol)); if (current_scope_id_ != kInvalidScopeId) { table_.AddSymbolToScope(current_scope_id_, name, id); } return id; } SymbolId Builder::CreateMethodSymbol( const std::string& name, const ast::Location& location, const std::vector>& parameters, const std::optional& return_type) { Method method; method.name = name; method.selection_range = location; method.range = location; method.declaration_range = location; method.return_type = ExtractTypeName(return_type); method.parameters = BuildParameters(parameters); Symbol symbol(std::move(method)); SymbolId id = table_.CreateSymbol(std::move(symbol)); if (current_scope_id_ != kInvalidScopeId) { table_.AddSymbolToScope(current_scope_id_, name, id); } return id; } ScopeId Builder::EnterScopeWithSymbol(ScopeKind kind, SymbolId symbol_id, const ast::Location& range) { current_scope_id_ = table_.CreateScope(kind, range, current_scope_id_, symbol_id); return current_scope_id_; } ScopeId Builder::EnterScope(ScopeKind kind, const ast::Location& range) { current_scope_id_ = table_.CreateScope(kind, range, current_scope_id_, std::nullopt); return current_scope_id_; } void Builder::ExitScope() { const auto* scope_info = table_.scopes().scope(current_scope_id_); if (scope_info && scope_info->parent) { current_scope_id_ = *scope_info->parent; } } void Builder::VisitStatements(const std::vector& statements) { for (const auto& stmt : statements) { if (stmt) stmt->Accept(*this); } } void Builder::VisitExpression(ast::Expression& expr) { expr.Accept(*this); } std::optional Builder::ExtractTypeName(const std::optional& type) const { if (type) return type->name; return std::nullopt; } std::vector Builder::BuildParameters(const std::vector>& parameters) const { std::vector result; result.reserve(parameters.size()); for (const auto& param : parameters) { if (!param) continue; language::symbol::Parameter p; p.name = param->name; if (param->type) p.type = param->type->name; if (param->default_value) p.default_value = ""; result.push_back(std::move(p)); } return result; } // ===== Node Visitation ===== void Builder::VisitProgram(ast::Program& node) { current_scope_id_ = table_.CreateScope(ScopeKind::kGlobal, node.span, std::nullopt, std::nullopt); VisitStatements(node.statements); } void Builder::VisitUnitDefinition(ast::UnitDefinition& node) { auto unit_scope = EnterScope(ScopeKind::kUnit, node.span); // Process interface section in_interface_section_ = true; VisitStatements(node.interface_statements); // Process implementation section in_interface_section_ = false; VisitStatements(node.implementation_statements); ExitScope(); } void Builder::VisitClassDefinition(ast::ClassDefinition& node) { auto class_id = CreateSymbol(node.name, SymbolKind::Class, node.location); auto class_scope = EnterScopeWithSymbol(ScopeKind::kClass, class_id, node.span); auto prev_parent = current_parent_symbol_id_; current_parent_symbol_id_ = class_id; for (auto& member : node.members) { if (member) member->Accept(*this); } current_parent_symbol_id_ = prev_parent; ExitScope(); } void Builder::VisitFunctionDeclaration(ast::FunctionDeclaration& node) { auto location = ast::Location{}; if (!node.name.empty() && !node.parameters.empty()) { location = node.span; } CreateFunctionSymbol(node.name, location, node.parameters, node.return_type); } void Builder::VisitFunctionDefinition(ast::FunctionDefinition& node) { auto func_id = CreateFunctionSymbol(node.name, node.location, node.parameters, node.return_type); if (node.body) { auto func_scope = EnterScopeWithSymbol(ScopeKind::kFunction, func_id, node.body->span); auto prev_function = current_function_id_; current_function_id_ = func_id; for (auto& param : node.parameters) { if (param) { CreateSymbol(param->name, SymbolKind::Variable, param->location); } } VisitStatements(node.body->statements); current_function_id_ = prev_function; ExitScope(); } } void Builder::VisitMethodDeclaration(ast::MethodDeclaration& node) { auto method_id = CreateMethodSymbol(node.name, node.location, node.parameters, node.return_type); if (node.body) { auto method_scope = EnterScopeWithSymbol(ScopeKind::kFunction, method_id, node.body->span); auto prev_function = current_function_id_; current_function_id_ = method_id; for (auto& param : node.parameters) { if (param) { CreateSymbol(param->name, SymbolKind::Variable, param->location); } } VisitStatements(node.body->statements); current_function_id_ = prev_function; ExitScope(); } } void Builder::VisitPropertyDeclaration(ast::PropertyDeclaration& node) { auto type = ExtractTypeName(node.type); CreateSymbol(node.name, SymbolKind::Property, node.location, type); } void Builder::VisitExternalMethodDefinition(ast::ExternalMethodDefinition& node) { std::string method_name = node.name; size_t dot_pos = method_name.find_last_of('.'); if (dot_pos != std::string::npos) { method_name = method_name.substr(dot_pos + 1); } std::optional method_id; if (current_parent_symbol_id_) { const auto* scope_info = table_.scopes().scope(current_scope_id_); if (scope_info) { method_id = table_.scopes().FindSymbolInScope(current_scope_id_, method_name); } } if (!method_id) { method_id = CreateMethodSymbol(method_name, node.location, node.parameters, node.return_type); } if (node.body) { auto method_scope = EnterScopeWithSymbol(ScopeKind::kFunction, *method_id, node.body->span); auto prev_function = current_function_id_; current_function_id_ = *method_id; for (auto& param : node.parameters) { if (param) { CreateSymbol(param->name, SymbolKind::Variable, param->location); } } VisitStatements(node.body->statements); current_function_id_ = prev_function; ExitScope(); } } void Builder::VisitClassMember(ast::ClassMember& node) { std::visit([this](auto& member) { if (member) member->Accept(*this); }, node.member); } void Builder::VisitVarDeclaration(ast::VarDeclaration& node) { CreateSymbol(node.name, SymbolKind::Variable, node.location, ExtractTypeName(node.type)); if (node.initializer) { VisitExpression(*node.initializer.value()); } } void Builder::VisitStaticDeclaration(ast::StaticDeclaration& node) { CreateSymbol(node.name, SymbolKind::Variable, node.location, ExtractTypeName(node.type)); if (node.initializer) { VisitExpression(*node.initializer.value()); } } void Builder::VisitGlobalDeclaration(ast::GlobalDeclaration& node) { CreateSymbol(node.name, SymbolKind::Variable, node.location, ExtractTypeName(node.type)); if (node.initializer) { VisitExpression(*node.initializer.value()); } } void Builder::VisitConstDeclaration(ast::ConstDeclaration& node) { CreateSymbol(node.name, SymbolKind::Constant, node.location, ExtractTypeName(node.type)); if (node.value) { VisitExpression(*node.value); } } void Builder::VisitFieldDeclaration(ast::FieldDeclaration& node) { CreateSymbol(node.name, SymbolKind::Field, node.location, ExtractTypeName(node.type)); if (node.initializer) { VisitExpression(*node.initializer.value()); } } void Builder::VisitUsesStatement(ast::UsesStatement& node) { // Unit imports would be stored in the Unit symbol // For now just skip since we don't have a way to store them } // ===== Expression Processing (references) ===== void Builder::VisitIdentifier(ast::Identifier& node) { auto symbol_id = table_.scopes().FindSymbolInScopeChain(current_scope_id_, node.name); if (symbol_id) { table_.AddReference(*symbol_id, node.location, false, false); } } void Builder::VisitCallExpression(ast::CallExpression& node) { if (node.callee) { if (auto* id = dynamic_cast(node.callee.get())) { auto symbol_id = table_.scopes().FindSymbolInScopeChain(current_scope_id_, id->name); if (symbol_id) { table_.AddReference(*symbol_id, id->location, false, false); if (current_function_id_) { table_.AddCall(*current_function_id_, *symbol_id, node.span); } } } else { node.callee->Accept(*this); } } for (auto& arg : node.arguments) { if (arg.value) VisitExpression(*arg.value); } } void Builder::VisitAttributeExpression(ast::AttributeExpression& node) { if (node.object) { node.object->Accept(*this); } if (node.attribute) { node.attribute->Accept(*this); } } void Builder::VisitAssignmentExpression(ast::AssignmentExpression& node) { ProcessLValue(node.left, true); if (node.right) { VisitExpression(*node.right); } } void Builder::ProcessLValue(const ast::LValue& lvalue, bool is_write) { std::visit([this, is_write](auto& val) { using T = std::decay_t; if constexpr (std::is_same_v>) { if (val) { auto symbol_id = table_.scopes().FindSymbolInScopeChain(current_scope_id_, val->name); if (symbol_id) { table_.AddReference(*symbol_id, val->location, false, is_write); } } } else if constexpr (std::is_same_v>) { if (val) { if (val->object) { val->object->Accept(*this); } if (val->attribute) { val->attribute->Accept(*this); } } } else if constexpr (std::is_same_v>) { if (val) { if (val->base) { val->base->Accept(*this); } for (auto& idx : val->indices) { if (idx.start) idx.start->Accept(*this); if (idx.end) idx.end->Accept(*this); if (idx.step) idx.step->Accept(*this); } } } else if constexpr (std::is_same_v>) { if (val && val->value) { val->value->Accept(*this); } } }, lvalue); } // ===== Statement Processing ===== void Builder::VisitBlockStatement(ast::BlockStatement& node) { auto block_scope = EnterScope(ScopeKind::kBlock, node.span); VisitStatements(node.statements); ExitScope(); } void Builder::VisitIfStatement(ast::IfStatement& node) { for (auto& branch : node.branches) { if (branch.condition) VisitExpression(*branch.condition); if (branch.body) branch.body->Accept(*this); } if (node.else_body && *node.else_body) { (*node.else_body)->Accept(*this); } } void Builder::VisitForInStatement(ast::ForInStatement& node) { auto for_scope = EnterScope(ScopeKind::kBlock, node.span); if (!node.key.empty()) { CreateSymbol(node.key, SymbolKind::Variable, node.key_location); } if (!node.value.empty()) { CreateSymbol(node.value, SymbolKind::Variable, node.value_location); } if (node.collection) VisitExpression(*node.collection); if (node.body) node.body->Accept(*this); ExitScope(); } void Builder::VisitForToStatement(ast::ForToStatement& node) { auto for_scope = EnterScope(ScopeKind::kBlock, node.span); CreateSymbol(node.counter, SymbolKind::Variable, node.counter_location); if (node.start) VisitExpression(*node.start); if (node.end) VisitExpression(*node.end); if (node.step) VisitExpression(*node.step); if (node.body) node.body->Accept(*this); ExitScope(); } void Builder::VisitWhileStatement(ast::WhileStatement& node) { if (node.condition) VisitExpression(*node.condition); if (node.body) node.body->Accept(*this); } void Builder::VisitRepeatStatement(ast::RepeatStatement& node) { VisitStatements(node.body); if (node.condition) VisitExpression(*node.condition); } void Builder::VisitCaseStatement(ast::CaseStatement& node) { if (node.discriminant) VisitExpression(*node.discriminant); for (auto& branch : node.branches) { for (auto& value : branch.values) { if (value) VisitExpression(*value); } if (branch.body) branch.body->Accept(*this); } if (node.else_body && *node.else_body) { (*node.else_body)->Accept(*this); } } void Builder::VisitTryStatement(ast::TryStatement& node) { if (node.try_body) { VisitStatements(node.try_body->statements); } if (node.except_body) { VisitStatements(node.except_body->statements); } } void Builder::VisitMatrixIterationStatement(ast::MatrixIterationStatement& node) { auto iter_scope = EnterScope(ScopeKind::kBlock, node.span); if (node.target) VisitExpression(*node.target); if (node.body) VisitStatements(node.body->statements); ExitScope(); } void Builder::VisitExpressionStatement(ast::ExpressionStatement& node) { if (node.expression) VisitExpression(*node.expression); } void Builder::VisitReturnStatement(ast::ReturnStatement& node) { if (node.value && *node.value) VisitExpression(**node.value); } // ===== Other Expressions ===== void Builder::VisitBinaryExpression(ast::BinaryExpression& node) { if (node.left) VisitExpression(*node.left); if (node.right) VisitExpression(*node.right); } void Builder::VisitTernaryExpression(ast::TernaryExpression& node) { if (node.condition) VisitExpression(*node.condition); if (node.consequence) VisitExpression(*node.consequence); if (node.alternative) VisitExpression(*node.alternative); } void Builder::VisitSubscriptExpression(ast::SubscriptExpression& node) { if (node.base) VisitExpression(*node.base); for (auto& idx : node.indices) { if (idx.start) VisitExpression(*idx.start); if (idx.end) VisitExpression(*idx.end); if (idx.step) VisitExpression(*idx.step); } } void Builder::VisitArrayExpression(ast::ArrayExpression& node) { for (auto& elem : node.elements) { if (elem.key) VisitExpression(*elem.key.value()); if (elem.value) VisitExpression(*elem.value); } } void Builder::VisitAnonymousFunctionExpression(ast::AnonymousFunctionExpression& node) { auto func_id = CreateFunctionSymbol("", node.span, node.parameters, node.return_type); if (node.body) { auto func_scope = EnterScopeWithSymbol(ScopeKind::kAnonymousFunction, func_id, node.body->span); auto prev_function = current_function_id_; current_function_id_ = func_id; for (auto& param : node.parameters) { if (param) { CreateSymbol(param->name, SymbolKind::Variable, param->location); } } VisitStatements(node.body->statements); current_function_id_ = prev_function; ExitScope(); } } void Builder::VisitUnaryPlusExpression(ast::UnaryPlusExpression& node) { if (node.argument) VisitExpression(*node.argument); } void Builder::VisitUnaryMinusExpression(ast::UnaryMinusExpression& node) { if (node.argument) VisitExpression(*node.argument); } void Builder::VisitPrefixIncrementExpression(ast::PrefixIncrementExpression& node) { if (node.argument) VisitExpression(*node.argument); } void Builder::VisitPrefixDecrementExpression(ast::PrefixDecrementExpression& node) { if (node.argument) VisitExpression(*node.argument); } void Builder::VisitPostfixIncrementExpression(ast::PostfixIncrementExpression& node) { if (node.argument) VisitExpression(*node.argument); } void Builder::VisitPostfixDecrementExpression(ast::PostfixDecrementExpression& node) { if (node.argument) VisitExpression(*node.argument); } void Builder::VisitLogicalNotExpression(ast::LogicalNotExpression& node) { if (node.argument) VisitExpression(*node.argument); } void Builder::VisitBitwiseNotExpression(ast::BitwiseNotExpression& node) { if (node.argument) VisitExpression(*node.argument); } void Builder::VisitDerivativeExpression(ast::DerivativeExpression& node) { if (node.argument) VisitExpression(*node.argument); } void Builder::VisitMatrixTransposeExpression(ast::MatrixTransposeExpression& node) { if (node.argument) VisitExpression(*node.argument); } void Builder::VisitExprOperatorExpression(ast::ExprOperatorExpression& node) { if (node.argument) VisitExpression(*node.argument); } void Builder::VisitFunctionPointerExpression([[maybe_unused]] ast::FunctionPointerExpression& node) { if (node.argument) VisitExpression(*node.argument); } void Builder::VisitNewExpression(ast::NewExpression& node) { if (node.target) VisitExpression(*node.target); } void Builder::VisitEchoExpression(ast::EchoExpression& node) { for (auto& expr : node.expressions) { if (expr) VisitExpression(*expr); } } void Builder::VisitRaiseExpression(ast::RaiseExpression& node) { if (node.exception) VisitExpression(*node.exception); } void Builder::VisitInheritedExpression(ast::InheritedExpression& node) { if (node.call && *node.call) { (*node.call)->Accept(*this); } } void Builder::VisitParenthesizedExpression(ast::ParenthesizedExpression& node) { for (auto& elem : node.elements) { if (elem.key) VisitExpression(*elem.key.value()); if (elem.value) VisitExpression(*elem.value); } } void Builder::VisitColumnReference(ast::ColumnReference& node) { if (node.value) { VisitExpression(*node.value); } } void Builder::VisitUnpackPattern([[maybe_unused]] ast::UnpackPattern& node) { // Unpack pattern processing - would need to handle LValue variants } void Builder::VisitCompilerDirective([[maybe_unused]] ast::CompilerDirective& node) { // Compiler directive processing } void Builder::VisitConditionalDirective([[maybe_unused]] ast::ConditionalDirective& node) { // Conditional compilation directive - no statements to visit } void Builder::VisitConditionalBlock(ast::ConditionalBlock& node) { VisitStatements(node.consequence); VisitStatements(node.alternative); } } // namespace lsp::language::symbol