♻️ refactor(semantic): split semantic partitions
move semantic declarations into focused partitions and add new type_system.types/graph.types modules. reorder lsp server members so async executor shuts down before manager hub to avoid exit crash.
This commit is contained in:
parent
442bb5db7e
commit
f54050bad2
|
|
@ -60,13 +60,15 @@ set(SOURCES
|
|||
manager/symbol.cppm
|
||||
manager/detail/text_document.cppm
|
||||
manager/manager_hub.cppm
|
||||
language/semantic/graph/types.cppm
|
||||
language/semantic/graph/call.cppm
|
||||
language/semantic/graph/inheritance.cppm
|
||||
language/semantic/graph/reference.cppm
|
||||
language/semantic/interface.cppm
|
||||
language/semantic/semantic_model.cppm
|
||||
language/semantic/type_system.types.cppm
|
||||
language/semantic/type_system.cppm
|
||||
language/semantic/name_resolver.cppm
|
||||
language/semantic/semantic_model.cppm
|
||||
language/semantic/analyzer.cppm
|
||||
language/semantic/token_collector.cppm
|
||||
tree-sitter/parser.c
|
||||
|
|
@ -132,11 +134,13 @@ target_sources(
|
|||
language/symbol/symbol.cppm
|
||||
language/semantic/interface.cppm
|
||||
language/semantic/semantic.cppm
|
||||
language/semantic/type_system.types.cppm
|
||||
language/semantic/type_system.cppm
|
||||
language/semantic/name_resolver.cppm
|
||||
language/semantic/semantic_model.cppm
|
||||
language/semantic/analyzer.cppm
|
||||
language/semantic/token_collector.cppm
|
||||
language/semantic/graph/types.cppm
|
||||
language/semantic/graph/call.cppm
|
||||
language/semantic/graph/inheritance.cppm
|
||||
language/semantic/graph/reference.cppm
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@ export namespace lsp::core
|
|||
|
||||
private:
|
||||
RequestDispatcher dispatcher_;
|
||||
scheduler::AsyncExecutor async_executor_;
|
||||
manager::ManagerHub manager_hub_;
|
||||
scheduler::AsyncExecutor async_executor_;
|
||||
std::string interpreter_path_;
|
||||
|
||||
std::atomic<bool> is_initialized_ = false;
|
||||
|
|
@ -92,7 +92,8 @@ export namespace lsp::core
|
|||
|
||||
namespace lsp::core
|
||||
{
|
||||
LspServer::LspServer(std::size_t concurrency, std::string interpreter_path) : async_executor_(concurrency),
|
||||
LspServer::LspServer(std::size_t concurrency, std::string interpreter_path) : manager_hub_(),
|
||||
async_executor_(concurrency),
|
||||
interpreter_path_(std::move(interpreter_path))
|
||||
{
|
||||
spdlog::info("Initializing LSP server with {} worker threads", concurrency);
|
||||
|
|
|
|||
|
|
@ -4,11 +4,142 @@ export module lsp.language.semantic:analyzer;
|
|||
|
||||
import std;
|
||||
|
||||
import :interface;
|
||||
import :semantic_model;
|
||||
import :type_system;
|
||||
import lsp.language.ast;
|
||||
import lsp.language.symbol;
|
||||
import lsp.utils.string;
|
||||
|
||||
export namespace lsp::language::semantic
|
||||
{
|
||||
class Analyzer : public ast::ASTVisitor
|
||||
{
|
||||
public:
|
||||
explicit Analyzer(symbol::SymbolTable& symbol_table, SemanticModel& semantic_model);
|
||||
|
||||
using ExternalSymbolProvider = std::function<std::optional<symbol::Symbol>(const std::string&)>;
|
||||
void SetExternalSymbolProvider(ExternalSymbolProvider provider);
|
||||
|
||||
void Analyze(ast::ASTNode& root);
|
||||
|
||||
void VisitProgram(ast::Program& node) override;
|
||||
void VisitUnitDefinition(ast::UnitDefinition& node) override;
|
||||
void VisitClassDefinition(ast::ClassDefinition& node) override;
|
||||
void VisitFunctionDefinition(ast::FunctionDefinition& node) override;
|
||||
void VisitFunctionDeclaration(ast::FunctionDeclaration& node) override;
|
||||
void VisitMethodDeclaration(ast::MethodDeclaration& node) override;
|
||||
void VisitExternalMethodDefinition(ast::ExternalMethodDefinition& node) override;
|
||||
|
||||
void VisitVarDeclaration(ast::VarDeclaration& node) override;
|
||||
void VisitStaticDeclaration(ast::StaticDeclaration& node) override;
|
||||
void VisitGlobalDeclaration(ast::GlobalDeclaration& node) override;
|
||||
void VisitConstDeclaration(ast::ConstDeclaration& node) override;
|
||||
void VisitFieldDeclaration(ast::FieldDeclaration& node) override;
|
||||
void VisitClassMember(ast::ClassMember& node) override;
|
||||
void VisitPropertyDeclaration(ast::PropertyDeclaration& node) override;
|
||||
|
||||
void VisitBlockStatement(ast::BlockStatement& node) override;
|
||||
void VisitIfStatement(ast::IfStatement& node) override;
|
||||
void VisitForInStatement(ast::ForInStatement& node) override;
|
||||
void VisitForToStatement(ast::ForToStatement& node) override;
|
||||
void VisitWhileStatement(ast::WhileStatement& node) override;
|
||||
void VisitRepeatStatement(ast::RepeatStatement& node) override;
|
||||
void VisitCaseStatement(ast::CaseStatement& node) override;
|
||||
void VisitTryStatement(ast::TryStatement& node) override;
|
||||
void VisitLabelStatement(ast::LabelStatement& node) override;
|
||||
void VisitGotoStatement(ast::GotoStatement& node) override;
|
||||
|
||||
void VisitUsesStatement(ast::UsesStatement& node) override;
|
||||
|
||||
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& 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;
|
||||
|
||||
void VisitUnaryPlusExpression(ast::UnaryPlusExpression& node) override;
|
||||
void VisitUnaryMinusExpression(ast::UnaryMinusExpression& node) override;
|
||||
void VisitPrefixIncrementExpression(ast::PrefixIncrementExpression& node) override;
|
||||
void VisitPrefixDecrementExpression(ast::PrefixDecrementExpression& node) override;
|
||||
void VisitPostfixIncrementExpression(ast::PostfixIncrementExpression& node) override;
|
||||
void VisitPostfixDecrementExpression(ast::PostfixDecrementExpression& node) override;
|
||||
void VisitLogicalNotExpression(ast::LogicalNotExpression& node) override;
|
||||
void VisitBitwiseNotExpression(ast::BitwiseNotExpression& node) override;
|
||||
void VisitDerivativeExpression(ast::DerivativeExpression& node) override;
|
||||
void VisitMatrixTransposeExpression(ast::MatrixTransposeExpression& node) override;
|
||||
void VisitExprOperatorExpression(ast::ExprOperatorExpression& node) override;
|
||||
void VisitFunctionPointerExpression(ast::FunctionPointerExpression& node) override;
|
||||
|
||||
void VisitNewExpression(ast::NewExpression& node) override;
|
||||
void VisitEchoExpression(ast::EchoExpression& node) override;
|
||||
void VisitRaiseExpression(ast::RaiseExpression& node) override;
|
||||
void VisitInheritedExpression(ast::InheritedExpression& node) override;
|
||||
void VisitRdoExpression(ast::RdoExpression& node) override;
|
||||
void VisitParenthesizedExpression(ast::ParenthesizedExpression& node) override;
|
||||
|
||||
void VisitExpressionStatement(ast::ExpressionStatement& node) override;
|
||||
void VisitBreakStatement(ast::BreakStatement& node) override;
|
||||
void VisitContinueStatement(ast::ContinueStatement& node) override;
|
||||
void VisitReturnStatement(ast::ReturnStatement& node) override;
|
||||
void VisitTSSQLExpression(ast::TSSQLExpression& node) override;
|
||||
void VisitColumnReference(ast::ColumnReference& node) override;
|
||||
void VisitUnpackPattern(ast::UnpackPattern& node) override;
|
||||
void VisitMatrixIterationStatement(ast::MatrixIterationStatement& node) override;
|
||||
|
||||
void VisitCompilerDirective(ast::CompilerDirective& node) override;
|
||||
void VisitConditionalDirective(ast::ConditionalDirective& node) override;
|
||||
void VisitConditionalBlock(ast::ConditionalBlock& node) override;
|
||||
void VisitTSLXBlock(ast::TSLXBlock& node) override;
|
||||
|
||||
void VisitParameter(ast::Parameter& node) override;
|
||||
|
||||
private:
|
||||
void VisitStatements(const std::vector<ast::StatementPtr>& statements);
|
||||
void VisitExpression(ast::Expression& expr);
|
||||
void ProcessLValue(const ast::LValue& lvalue);
|
||||
std::optional<symbol::SymbolId> ResolveParentClass(const ast::ClassDefinition::ParentClass& parent);
|
||||
std::optional<symbol::ScopeId> ScopeAt(const ast::Location& location) const;
|
||||
std::optional<symbol::SymbolId> ResolveByName(const std::string& name);
|
||||
std::optional<symbol::SymbolId> FindMethodInClass(symbol::SymbolId class_id, const std::string& method_name) const;
|
||||
std::optional<symbol::ScopeId> FindScopeOwnedBy(symbol::SymbolId owner_id) const;
|
||||
|
||||
std::optional<symbol::SymbolId> ResolveIdentifier(const std::string& name, const ast::Location& location);
|
||||
std::optional<symbol::SymbolId> ResolveFromUses(const std::string& name);
|
||||
void TrackReference(symbol::SymbolId symbol_id, const ast::Location& location, bool is_write = false);
|
||||
void TrackCall(symbol::SymbolId callee, const ast::Location& location);
|
||||
std::shared_ptr<Type> InferExpressionType(ast::Expression& expr);
|
||||
std::shared_ptr<Type> GetDeclaredTypeForSymbol(symbol::SymbolId symbol_id);
|
||||
std::optional<symbol::SymbolId> ResolveClassSymbol(const std::string& name, const ast::Location& location);
|
||||
std::optional<symbol::SymbolId> ResolveLValueSymbol(const ast::LValue& lvalue);
|
||||
void RegisterParameterTypes(symbol::SymbolId function_id, const std::vector<std::unique_ptr<ast::Parameter>>& parameters);
|
||||
|
||||
private:
|
||||
struct UnitContext
|
||||
{
|
||||
std::string unit_name;
|
||||
std::vector<symbol::UnitImport> interface_imports;
|
||||
std::vector<symbol::UnitImport> implementation_imports;
|
||||
};
|
||||
|
||||
symbol::SymbolTable& symbol_table_;
|
||||
SemanticModel& semantic_model_;
|
||||
|
||||
std::optional<symbol::SymbolId> current_function_id_;
|
||||
std::optional<symbol::SymbolId> current_class_id_;
|
||||
std::optional<UnitContext> current_unit_context_;
|
||||
std::optional<symbol::UnitVisibility> current_unit_section_;
|
||||
std::vector<symbol::UnitImport> file_imports_;
|
||||
ExternalSymbolProvider external_symbol_provider_;
|
||||
std::unordered_map<std::string, symbol::SymbolId> imported_symbols_;
|
||||
};
|
||||
}
|
||||
|
||||
namespace lsp::language::semantic
|
||||
{
|
||||
namespace
|
||||
|
|
|
|||
|
|
@ -4,9 +4,31 @@ export module lsp.language.semantic:graph.call;
|
|||
|
||||
import std;
|
||||
|
||||
import :interface;
|
||||
import :graph.types;
|
||||
import lsp.language.ast;
|
||||
|
||||
export namespace lsp::language::semantic::graph
|
||||
{
|
||||
using symbol::SymbolId;
|
||||
|
||||
class Call : public ISemanticGraph
|
||||
{
|
||||
public:
|
||||
void OnSymbolRemoved(SymbolId id) override;
|
||||
void Clear() override;
|
||||
|
||||
void AddCall(SymbolId caller, SymbolId callee, const ast::Location& location);
|
||||
|
||||
const std::vector<semantic::Call>& callers(SymbolId id) const;
|
||||
|
||||
const std::vector<semantic::Call>& callees(SymbolId id) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<SymbolId, std::vector<semantic::Call>> callers_map_;
|
||||
std::unordered_map<SymbolId, std::vector<semantic::Call>> callees_map_;
|
||||
};
|
||||
}
|
||||
|
||||
namespace lsp::language::semantic::graph
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,31 @@ export module lsp.language.semantic:graph.inheritance;
|
|||
|
||||
import std;
|
||||
|
||||
import :interface;
|
||||
import :graph.types;
|
||||
export namespace lsp::language::semantic::graph
|
||||
{
|
||||
using symbol::SymbolId;
|
||||
|
||||
class Inheritance : public ISemanticGraph
|
||||
{
|
||||
public:
|
||||
void OnSymbolRemoved(SymbolId id) override;
|
||||
void Clear() override;
|
||||
|
||||
void AddInheritance(SymbolId derived, SymbolId base);
|
||||
|
||||
const std::vector<SymbolId>& base_classes(SymbolId id) const;
|
||||
|
||||
const std::vector<SymbolId>& derived_classes(SymbolId 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_;
|
||||
};
|
||||
}
|
||||
|
||||
namespace lsp::language::semantic::graph
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,30 @@ export module lsp.language.semantic:graph.reference;
|
|||
|
||||
import std;
|
||||
|
||||
import :interface;
|
||||
import :graph.types;
|
||||
import lsp.language.ast;
|
||||
|
||||
export namespace lsp::language::semantic::graph
|
||||
{
|
||||
using symbol::SymbolId;
|
||||
|
||||
class Reference : public ISemanticGraph
|
||||
{
|
||||
public:
|
||||
void OnSymbolRemoved(SymbolId id) override;
|
||||
void Clear() override;
|
||||
|
||||
void AddReference(SymbolId symbol_id, const ast::Location& location, bool is_definition = false, bool is_write = false);
|
||||
|
||||
const std::vector<semantic::Reference>& references(SymbolId id) const;
|
||||
|
||||
std::optional<ast::Location> FindDefinitionLocation(SymbolId id) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<SymbolId, std::vector<semantic::Reference>> references_;
|
||||
};
|
||||
}
|
||||
|
||||
namespace lsp::language::semantic::graph
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
module;
|
||||
|
||||
export module lsp.language.semantic:graph.types;
|
||||
|
||||
import std;
|
||||
|
||||
import lsp.language.ast;
|
||||
import lsp.language.symbol;
|
||||
|
||||
export namespace lsp::language::semantic
|
||||
{
|
||||
struct Reference
|
||||
{
|
||||
ast::Location location;
|
||||
symbol::SymbolId symbol_id;
|
||||
bool is_definition;
|
||||
bool is_write;
|
||||
};
|
||||
|
||||
struct Call
|
||||
{
|
||||
symbol::SymbolId caller;
|
||||
symbol::SymbolId callee;
|
||||
ast::Location call_site;
|
||||
};
|
||||
|
||||
struct Inheritance
|
||||
{
|
||||
symbol::SymbolId derived;
|
||||
symbol::SymbolId base;
|
||||
};
|
||||
|
||||
class ISemanticGraph
|
||||
{
|
||||
public:
|
||||
virtual ~ISemanticGraph() = default;
|
||||
|
||||
virtual void OnSymbolRemoved(symbol::SymbolId id) = 0;
|
||||
|
||||
virtual void Clear() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
export namespace lsp::language::semantic::graph
|
||||
{
|
||||
using symbol::SymbolId;
|
||||
}
|
||||
|
|
@ -1,815 +1,5 @@
|
|||
module;
|
||||
|
||||
export module lsp.language.semantic:interface;
|
||||
import tree_sitter;
|
||||
|
||||
import std;
|
||||
|
||||
import lsp.language.ast;
|
||||
import lsp.language.symbol;
|
||||
import lsp.utils.string;
|
||||
import lsp.protocol;
|
||||
|
||||
export namespace lsp::language::semantic
|
||||
{
|
||||
struct Reference
|
||||
{
|
||||
ast::Location location;
|
||||
symbol::SymbolId symbol_id;
|
||||
bool is_definition;
|
||||
bool is_write;
|
||||
};
|
||||
|
||||
struct Call
|
||||
{
|
||||
symbol::SymbolId caller;
|
||||
symbol::SymbolId callee;
|
||||
ast::Location call_site;
|
||||
};
|
||||
|
||||
struct Inheritance
|
||||
{
|
||||
symbol::SymbolId derived;
|
||||
symbol::SymbolId base;
|
||||
};
|
||||
|
||||
class ISemanticGraph
|
||||
{
|
||||
public:
|
||||
virtual ~ISemanticGraph() = default;
|
||||
|
||||
virtual void OnSymbolRemoved(symbol::SymbolId id) = 0;
|
||||
|
||||
virtual void Clear() = 0;
|
||||
};
|
||||
|
||||
enum class TypeKind
|
||||
{
|
||||
kPrimitive,
|
||||
kClass,
|
||||
kArray,
|
||||
kFunction,
|
||||
kOptional,
|
||||
kVoid,
|
||||
kUnknown,
|
||||
kError
|
||||
};
|
||||
|
||||
enum class PrimitiveTypeKind
|
||||
{
|
||||
kInt,
|
||||
kFloat,
|
||||
kString,
|
||||
kBool,
|
||||
kChar
|
||||
};
|
||||
|
||||
struct TypeCompatibility
|
||||
{
|
||||
bool is_compatible = false;
|
||||
int conversion_cost = -1;
|
||||
bool requires_cast = false;
|
||||
|
||||
static TypeCompatibility Exact()
|
||||
{
|
||||
return { true, 0, false };
|
||||
}
|
||||
|
||||
static TypeCompatibility Implicit(int cost)
|
||||
{
|
||||
return { true, cost, false };
|
||||
}
|
||||
|
||||
static TypeCompatibility ExplicitCast(int cost)
|
||||
{
|
||||
return { true, cost, true };
|
||||
}
|
||||
|
||||
static TypeCompatibility Incompatible()
|
||||
{
|
||||
return { false, -1, false };
|
||||
}
|
||||
};
|
||||
|
||||
class Type;
|
||||
|
||||
class PrimitiveType
|
||||
{
|
||||
public:
|
||||
explicit PrimitiveType(PrimitiveTypeKind kind) : kind_(kind) {}
|
||||
|
||||
PrimitiveTypeKind kind() const { return kind_; }
|
||||
std::string ToString() const;
|
||||
|
||||
bool operator==(const PrimitiveType& other) const
|
||||
{
|
||||
return kind_ == other.kind_;
|
||||
}
|
||||
|
||||
private:
|
||||
PrimitiveTypeKind kind_;
|
||||
};
|
||||
|
||||
class ClassType
|
||||
{
|
||||
public:
|
||||
explicit ClassType(symbol::SymbolId class_id) : class_id_(class_id) {}
|
||||
|
||||
symbol::SymbolId class_id() const { return class_id_; }
|
||||
|
||||
bool operator==(const ClassType& other) const
|
||||
{
|
||||
return class_id_ == other.class_id_;
|
||||
}
|
||||
|
||||
private:
|
||||
symbol::SymbolId class_id_;
|
||||
};
|
||||
|
||||
class ArrayType
|
||||
{
|
||||
public:
|
||||
explicit ArrayType(std::shared_ptr<Type> element_type) : element_type_(std::move(element_type)) {}
|
||||
|
||||
const Type& element_type() const { return *element_type_; }
|
||||
std::shared_ptr<Type> element_type_ptr() const { return element_type_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Type> element_type_;
|
||||
};
|
||||
|
||||
class FunctionType
|
||||
{
|
||||
public:
|
||||
FunctionType(std::vector<std::shared_ptr<Type>> param_types,
|
||||
std::shared_ptr<Type> return_type) : param_types_(std::move(param_types)),
|
||||
return_type_(std::move(return_type)) {}
|
||||
|
||||
const std::vector<std::shared_ptr<Type>>& param_types() const
|
||||
{
|
||||
return param_types_;
|
||||
}
|
||||
|
||||
const Type& return_type() const { return *return_type_; }
|
||||
std::shared_ptr<Type> return_type_ptr() const { return return_type_; }
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Type>> param_types_;
|
||||
std::shared_ptr<Type> return_type_;
|
||||
};
|
||||
|
||||
class OptionalType
|
||||
{
|
||||
public:
|
||||
explicit OptionalType(std::shared_ptr<Type> inner_type) : inner_type_(std::move(inner_type)) {}
|
||||
|
||||
const Type& inner_type() const { return *inner_type_; }
|
||||
std::shared_ptr<Type> inner_type_ptr() const { return inner_type_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Type> inner_type_;
|
||||
};
|
||||
|
||||
class VoidType
|
||||
{
|
||||
public:
|
||||
bool operator==(const VoidType&) const { return true; }
|
||||
};
|
||||
|
||||
class UnknownType
|
||||
{
|
||||
public:
|
||||
bool operator==(const UnknownType&) const { return true; }
|
||||
};
|
||||
|
||||
class ErrorType
|
||||
{
|
||||
public:
|
||||
explicit ErrorType(std::string message = "") : message_(std::move(message)) {}
|
||||
|
||||
const std::string& message() const { return message_; }
|
||||
|
||||
bool operator==(const ErrorType&) const { return true; }
|
||||
|
||||
private:
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
using TypeData = std::variant<
|
||||
PrimitiveType,
|
||||
ClassType,
|
||||
ArrayType,
|
||||
FunctionType,
|
||||
OptionalType,
|
||||
VoidType,
|
||||
UnknownType,
|
||||
ErrorType>;
|
||||
|
||||
class Type
|
||||
{
|
||||
public:
|
||||
explicit Type(TypeData data) : data_(std::move(data)) {}
|
||||
|
||||
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_);
|
||||
}
|
||||
|
||||
TypeKind kind() const;
|
||||
std::string ToString() const;
|
||||
|
||||
bool Equals(const Type& other) const;
|
||||
|
||||
const TypeData& data() const { return data_; }
|
||||
|
||||
private:
|
||||
TypeData data_;
|
||||
};
|
||||
|
||||
class TypeSystem
|
||||
{
|
||||
public:
|
||||
TypeSystem();
|
||||
|
||||
std::shared_ptr<Type> GetIntType() const { return int_type_; }
|
||||
std::shared_ptr<Type> GetFloatType() const { return float_type_; }
|
||||
std::shared_ptr<Type> GetStringType() const { return string_type_; }
|
||||
std::shared_ptr<Type> GetBoolType() const { return bool_type_; }
|
||||
std::shared_ptr<Type> GetCharType() const { return char_type_; }
|
||||
std::shared_ptr<Type> GetVoidType() const { return void_type_; }
|
||||
std::shared_ptr<Type> GetUnknownType() const { return unknown_type_; }
|
||||
std::shared_ptr<Type> GetErrorType() const { return error_type_; }
|
||||
|
||||
std::shared_ptr<Type> CreateClassType(symbol::SymbolId class_id);
|
||||
std::shared_ptr<Type> CreateArrayType(std::shared_ptr<Type> element_type);
|
||||
std::shared_ptr<Type> CreateFunctionType(
|
||||
std::vector<std::shared_ptr<Type>> param_types,
|
||||
std::shared_ptr<Type> return_type);
|
||||
std::shared_ptr<Type> CreateOptionalType(std::shared_ptr<Type> inner_type);
|
||||
|
||||
void RegisterClassType(const std::string& type_name, symbol::SymbolId class_id);
|
||||
std::shared_ptr<Type> GetTypeByName(const std::string& type_name) const;
|
||||
|
||||
std::shared_ptr<Type> GetSymbolType(symbol::SymbolId symbol_id) const;
|
||||
|
||||
void RegisterSymbolType(symbol::SymbolId symbol_id, std::shared_ptr<Type> type);
|
||||
|
||||
TypeCompatibility CheckCompatibility(const Type& from, const Type& to) const;
|
||||
|
||||
bool IsAssignable(const Type& from, const Type& to) const;
|
||||
|
||||
bool RequiresExplicitCast(const Type& from, const Type& to) const;
|
||||
|
||||
std::shared_ptr<Type> InferBinaryExpressionType(
|
||||
const Type& left,
|
||||
const Type& right,
|
||||
const std::string& op) const;
|
||||
|
||||
std::shared_ptr<Type> InferUnaryExpressionType(
|
||||
const Type& operand,
|
||||
const std::string& op) const;
|
||||
|
||||
std::shared_ptr<Type> InferLiteralType(const std::string& literal_value) const;
|
||||
|
||||
void SetInheritanceChecker(
|
||||
std::function<bool(symbol::SymbolId, symbol::SymbolId)> checker)
|
||||
{
|
||||
is_subclass_of_ = std::move(checker);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Type> int_type_;
|
||||
std::shared_ptr<Type> float_type_;
|
||||
std::shared_ptr<Type> string_type_;
|
||||
std::shared_ptr<Type> bool_type_;
|
||||
std::shared_ptr<Type> char_type_;
|
||||
std::shared_ptr<Type> void_type_;
|
||||
std::shared_ptr<Type> unknown_type_;
|
||||
std::shared_ptr<Type> error_type_;
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<Type>> type_by_name_;
|
||||
|
||||
std::unordered_map<symbol::SymbolId, std::shared_ptr<Type>> symbol_types_;
|
||||
|
||||
std::function<bool(symbol::SymbolId derived, symbol::SymbolId base)>
|
||||
is_subclass_of_;
|
||||
|
||||
TypeCompatibility CheckPrimitiveCompatibility(
|
||||
const PrimitiveType& from,
|
||||
const PrimitiveType& to) const;
|
||||
|
||||
TypeCompatibility CheckClassCompatibility(
|
||||
const ClassType& from,
|
||||
const ClassType& to) const;
|
||||
|
||||
TypeCompatibility CheckArrayCompatibility(
|
||||
const ArrayType& from,
|
||||
const ArrayType& to) const;
|
||||
|
||||
TypeCompatibility CheckFunctionCompatibility(
|
||||
const FunctionType& from,
|
||||
const FunctionType& to) const;
|
||||
};
|
||||
|
||||
struct NameResolutionResult
|
||||
{
|
||||
symbol::SymbolId symbol_id = symbol::kInvalidSymbolId;
|
||||
bool is_ambiguous = false;
|
||||
std::vector<symbol::SymbolId> candidates;
|
||||
|
||||
bool IsResolved() const
|
||||
{
|
||||
return symbol_id != symbol::kInvalidSymbolId;
|
||||
}
|
||||
|
||||
static NameResolutionResult Success(symbol::SymbolId id)
|
||||
{
|
||||
return { id, false, { id } };
|
||||
}
|
||||
|
||||
static NameResolutionResult Ambiguous(std::vector<symbol::SymbolId> symbols)
|
||||
{
|
||||
return {
|
||||
symbols.empty() ? symbol::kInvalidSymbolId : symbols[0],
|
||||
true,
|
||||
std::move(symbols)
|
||||
};
|
||||
}
|
||||
|
||||
static NameResolutionResult NotFound()
|
||||
{
|
||||
return { symbol::kInvalidSymbolId, false, {} };
|
||||
}
|
||||
};
|
||||
|
||||
struct OverloadCandidate
|
||||
{
|
||||
symbol::SymbolId symbol_id;
|
||||
int match_score = 0;
|
||||
std::vector<TypeCompatibility> arg_conversions;
|
||||
|
||||
bool operator<(const OverloadCandidate& other) const
|
||||
{
|
||||
return match_score > other.match_score;
|
||||
}
|
||||
};
|
||||
|
||||
class NameResolver
|
||||
{
|
||||
public:
|
||||
explicit NameResolver(const symbol::SymbolTable& symbol_table,
|
||||
const TypeSystem& type_system) : symbol_table_(symbol_table),
|
||||
type_system_(type_system) {}
|
||||
|
||||
NameResolutionResult ResolveName(
|
||||
const std::string& name,
|
||||
symbol::ScopeId scope_id,
|
||||
bool search_parent = true) const;
|
||||
|
||||
NameResolutionResult ResolveNameAtLocation(
|
||||
const std::string& name,
|
||||
const ast::Location& location) const;
|
||||
|
||||
NameResolutionResult ResolveMemberAccess(
|
||||
symbol::SymbolId object_symbol_id,
|
||||
const std::string& member_name) const;
|
||||
|
||||
NameResolutionResult ResolveClassMember(
|
||||
symbol::SymbolId class_id,
|
||||
const std::string& member_name,
|
||||
bool static_only = false) const;
|
||||
|
||||
NameResolutionResult ResolveFunctionCall(
|
||||
const std::string& function_name,
|
||||
const std::vector<std::shared_ptr<Type>>& arg_types,
|
||||
symbol::ScopeId scope_id) const;
|
||||
|
||||
NameResolutionResult ResolveMethodCall(
|
||||
symbol::SymbolId object_symbol_id,
|
||||
const std::string& method_name,
|
||||
const std::vector<std::shared_ptr<Type>>& arg_types) const;
|
||||
|
||||
NameResolutionResult ResolveQualifiedName(
|
||||
const std::string& qualifier,
|
||||
const std::string& name,
|
||||
symbol::ScopeId scope_id) const;
|
||||
|
||||
std::optional<symbol::ScopeId> GetSymbolScope(
|
||||
[[maybe_unused]] symbol::SymbolId symbol_id) const;
|
||||
|
||||
bool IsSymbolVisibleInScope(
|
||||
[[maybe_unused]] symbol::SymbolId symbol_id,
|
||||
[[maybe_unused]] symbol::ScopeId scope_id) const;
|
||||
|
||||
private:
|
||||
const symbol::SymbolTable& symbol_table_;
|
||||
const TypeSystem& type_system_;
|
||||
|
||||
std::vector<symbol::SymbolId> SearchScopeChain(
|
||||
const std::string& name,
|
||||
symbol::ScopeId start_scope) const;
|
||||
|
||||
std::optional<symbol::ScopeId> FindScopeOwnedBy(symbol::SymbolId owner) const;
|
||||
|
||||
NameResolutionResult SelectBestOverload(
|
||||
const std::vector<OverloadCandidate>& candidates) const;
|
||||
|
||||
OverloadCandidate CalculateOverloadScore(
|
||||
symbol::SymbolId candidate_id,
|
||||
const std::vector<std::shared_ptr<Type>>& arg_types) const;
|
||||
|
||||
std::vector<std::shared_ptr<Type>> GetParameterTypes(
|
||||
symbol::SymbolId symbol_id) const;
|
||||
|
||||
std::optional<symbol::SymbolId> GetOwnerClassId(
|
||||
[[maybe_unused]] symbol::SymbolId symbol_id) const;
|
||||
|
||||
bool CheckMemberAccessibility(
|
||||
[[maybe_unused]] const symbol::Symbol& member,
|
||||
[[maybe_unused]] symbol::ScopeId access_scope) const;
|
||||
};
|
||||
|
||||
namespace graph
|
||||
{
|
||||
using symbol::SymbolId;
|
||||
|
||||
class Reference : public ISemanticGraph
|
||||
{
|
||||
public:
|
||||
void OnSymbolRemoved(SymbolId id) override;
|
||||
void Clear() override;
|
||||
|
||||
void AddReference(SymbolId symbol_id, const ast::Location& location, bool is_definition = false, bool is_write = false);
|
||||
|
||||
const std::vector<semantic::Reference>& references(SymbolId id) const;
|
||||
|
||||
std::optional<ast::Location> FindDefinitionLocation(SymbolId id) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<SymbolId, std::vector<semantic::Reference>> references_;
|
||||
};
|
||||
|
||||
class Call : public ISemanticGraph
|
||||
{
|
||||
public:
|
||||
void OnSymbolRemoved(SymbolId id) override;
|
||||
void Clear() override;
|
||||
|
||||
void AddCall(SymbolId caller, SymbolId callee, const ast::Location& location);
|
||||
|
||||
const std::vector<semantic::Call>& callers(SymbolId id) const;
|
||||
|
||||
const std::vector<semantic::Call>& callees(SymbolId id) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<SymbolId, std::vector<semantic::Call>> callers_map_;
|
||||
std::unordered_map<SymbolId, std::vector<semantic::Call>> callees_map_;
|
||||
};
|
||||
|
||||
class Inheritance : public ISemanticGraph
|
||||
{
|
||||
public:
|
||||
void OnSymbolRemoved(SymbolId id) override;
|
||||
void Clear() override;
|
||||
|
||||
void AddInheritance(SymbolId derived, SymbolId base);
|
||||
|
||||
const std::vector<SymbolId>& base_classes(SymbolId id) const;
|
||||
|
||||
const std::vector<SymbolId>& derived_classes(SymbolId 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_;
|
||||
};
|
||||
} // namespace graph
|
||||
|
||||
class SemanticModel
|
||||
{
|
||||
public:
|
||||
struct UnitImportSet
|
||||
{
|
||||
std::vector<symbol::UnitImport> interface_imports;
|
||||
std::vector<symbol::UnitImport> implementation_imports;
|
||||
};
|
||||
|
||||
explicit SemanticModel(const symbol::SymbolTable& symbol_table);
|
||||
|
||||
void Clear();
|
||||
void OnSymbolRemoved(symbol::SymbolId id);
|
||||
void AddReference(symbol::SymbolId symbol_id, const ast::Location& location, bool is_definition = false, bool is_write = false);
|
||||
void AddInheritance(symbol::SymbolId derived, symbol::SymbolId base);
|
||||
void AddCall(symbol::SymbolId caller, symbol::SymbolId callee, const ast::Location& location);
|
||||
|
||||
graph::Reference& references() { return reference_graph_; }
|
||||
graph::Inheritance& inheritance() { return inheritance_graph_; }
|
||||
graph::Call& calls() { return call_graph_; }
|
||||
|
||||
const graph::Reference& references() const { return reference_graph_; }
|
||||
const graph::Inheritance& inheritance() const { return inheritance_graph_; }
|
||||
const graph::Call& calls() const { return call_graph_; }
|
||||
|
||||
TypeSystem& type_system() { return type_system_; }
|
||||
const TypeSystem& type_system() const { return type_system_; }
|
||||
|
||||
NameResolver& name_resolver() { return *name_resolver_; }
|
||||
const NameResolver& name_resolver() const { return *name_resolver_; }
|
||||
|
||||
void RegisterUnitImports(const std::string& unit_name, UnitImportSet imports);
|
||||
std::optional<UnitImportSet> GetUnitImports(const std::string& unit_name) const;
|
||||
|
||||
std::shared_ptr<Type> GetSymbolType(symbol::SymbolId symbol_id) const
|
||||
{
|
||||
return type_system_.GetSymbolType(symbol_id);
|
||||
}
|
||||
|
||||
void SetSymbolType(symbol::SymbolId symbol_id, std::shared_ptr<Type> type)
|
||||
{
|
||||
type_system_.RegisterSymbolType(symbol_id, std::move(type));
|
||||
}
|
||||
|
||||
bool IsSubclassOf(symbol::SymbolId derived, symbol::SymbolId base) const
|
||||
{
|
||||
return inheritance_graph_.IsSubclassOf(derived, base);
|
||||
}
|
||||
|
||||
private:
|
||||
const symbol::SymbolTable& symbol_table_;
|
||||
|
||||
graph::Reference reference_graph_;
|
||||
graph::Inheritance inheritance_graph_;
|
||||
graph::Call call_graph_;
|
||||
|
||||
TypeSystem type_system_;
|
||||
|
||||
std::unique_ptr<NameResolver> name_resolver_;
|
||||
std::unordered_map<std::string, UnitImportSet, utils::IHasher, utils::IEqualTo> unit_imports_;
|
||||
};
|
||||
|
||||
class Analyzer : public ast::ASTVisitor
|
||||
{
|
||||
public:
|
||||
explicit Analyzer(symbol::SymbolTable& symbol_table, SemanticModel& semantic_model);
|
||||
|
||||
using ExternalSymbolProvider = std::function<std::optional<symbol::Symbol>(const std::string&)>;
|
||||
void SetExternalSymbolProvider(ExternalSymbolProvider provider);
|
||||
|
||||
void Analyze(ast::ASTNode& root);
|
||||
|
||||
void VisitProgram(ast::Program& node) override;
|
||||
void VisitUnitDefinition(ast::UnitDefinition& node) override;
|
||||
void VisitClassDefinition(ast::ClassDefinition& node) override;
|
||||
void VisitFunctionDefinition(ast::FunctionDefinition& node) override;
|
||||
void VisitFunctionDeclaration(ast::FunctionDeclaration& node) override;
|
||||
void VisitMethodDeclaration(ast::MethodDeclaration& node) override;
|
||||
void VisitExternalMethodDefinition(ast::ExternalMethodDefinition& node) override;
|
||||
|
||||
void VisitVarDeclaration(ast::VarDeclaration& node) override;
|
||||
void VisitStaticDeclaration(ast::StaticDeclaration& node) override;
|
||||
void VisitGlobalDeclaration(ast::GlobalDeclaration& node) override;
|
||||
void VisitConstDeclaration(ast::ConstDeclaration& node) override;
|
||||
void VisitFieldDeclaration(ast::FieldDeclaration& node) override;
|
||||
void VisitClassMember(ast::ClassMember& node) override;
|
||||
void VisitPropertyDeclaration(ast::PropertyDeclaration& node) override;
|
||||
|
||||
void VisitBlockStatement(ast::BlockStatement& node) override;
|
||||
void VisitIfStatement(ast::IfStatement& node) override;
|
||||
void VisitForInStatement(ast::ForInStatement& node) override;
|
||||
void VisitForToStatement(ast::ForToStatement& node) override;
|
||||
void VisitWhileStatement(ast::WhileStatement& node) override;
|
||||
void VisitRepeatStatement(ast::RepeatStatement& node) override;
|
||||
void VisitCaseStatement(ast::CaseStatement& node) override;
|
||||
void VisitTryStatement(ast::TryStatement& node) override;
|
||||
void VisitLabelStatement(ast::LabelStatement& node) override;
|
||||
void VisitGotoStatement(ast::GotoStatement& node) override;
|
||||
|
||||
void VisitUsesStatement(ast::UsesStatement& node) override;
|
||||
|
||||
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& 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;
|
||||
|
||||
void VisitUnaryPlusExpression(ast::UnaryPlusExpression& node) override;
|
||||
void VisitUnaryMinusExpression(ast::UnaryMinusExpression& node) override;
|
||||
void VisitPrefixIncrementExpression(ast::PrefixIncrementExpression& node) override;
|
||||
void VisitPrefixDecrementExpression(ast::PrefixDecrementExpression& node) override;
|
||||
void VisitPostfixIncrementExpression(ast::PostfixIncrementExpression& node) override;
|
||||
void VisitPostfixDecrementExpression(ast::PostfixDecrementExpression& node) override;
|
||||
void VisitLogicalNotExpression(ast::LogicalNotExpression& node) override;
|
||||
void VisitBitwiseNotExpression(ast::BitwiseNotExpression& node) override;
|
||||
void VisitDerivativeExpression(ast::DerivativeExpression& node) override;
|
||||
void VisitMatrixTransposeExpression(ast::MatrixTransposeExpression& node) override;
|
||||
void VisitExprOperatorExpression(ast::ExprOperatorExpression& node) override;
|
||||
void VisitFunctionPointerExpression(ast::FunctionPointerExpression& node) override;
|
||||
|
||||
void VisitNewExpression(ast::NewExpression& node) override;
|
||||
void VisitEchoExpression(ast::EchoExpression& node) override;
|
||||
void VisitRaiseExpression(ast::RaiseExpression& node) override;
|
||||
void VisitInheritedExpression(ast::InheritedExpression& node) override;
|
||||
void VisitRdoExpression(ast::RdoExpression& node) override;
|
||||
void VisitParenthesizedExpression(ast::ParenthesizedExpression& node) override;
|
||||
|
||||
void VisitExpressionStatement(ast::ExpressionStatement& node) override;
|
||||
void VisitBreakStatement(ast::BreakStatement& node) override;
|
||||
void VisitContinueStatement(ast::ContinueStatement& node) override;
|
||||
void VisitReturnStatement(ast::ReturnStatement& node) override;
|
||||
void VisitTSSQLExpression(ast::TSSQLExpression& node) override;
|
||||
void VisitColumnReference(ast::ColumnReference& node) override;
|
||||
void VisitUnpackPattern(ast::UnpackPattern& node) override;
|
||||
void VisitMatrixIterationStatement(ast::MatrixIterationStatement& node) override;
|
||||
|
||||
void VisitCompilerDirective(ast::CompilerDirective& node) override;
|
||||
void VisitConditionalDirective(ast::ConditionalDirective& node) override;
|
||||
void VisitConditionalBlock(ast::ConditionalBlock& node) override;
|
||||
void VisitTSLXBlock(ast::TSLXBlock& node) override;
|
||||
|
||||
void VisitParameter(ast::Parameter& node) override;
|
||||
|
||||
private:
|
||||
void VisitStatements(const std::vector<ast::StatementPtr>& statements);
|
||||
void VisitExpression(ast::Expression& expr);
|
||||
void ProcessLValue(const ast::LValue& lvalue);
|
||||
std::optional<symbol::SymbolId> ResolveParentClass(const ast::ClassDefinition::ParentClass& parent);
|
||||
std::optional<symbol::ScopeId> ScopeAt(const ast::Location& location) const;
|
||||
std::optional<symbol::SymbolId> ResolveByName(const std::string& name);
|
||||
std::optional<symbol::SymbolId> FindMethodInClass(symbol::SymbolId class_id, const std::string& method_name) const;
|
||||
std::optional<symbol::ScopeId> FindScopeOwnedBy(symbol::SymbolId owner_id) const;
|
||||
|
||||
std::optional<symbol::SymbolId> ResolveIdentifier(const std::string& name, const ast::Location& location);
|
||||
std::optional<symbol::SymbolId> ResolveFromUses(const std::string& name);
|
||||
void TrackReference(symbol::SymbolId symbol_id, const ast::Location& location, bool is_write = false);
|
||||
void TrackCall(symbol::SymbolId callee, const ast::Location& location);
|
||||
std::shared_ptr<Type> InferExpressionType(ast::Expression& expr);
|
||||
std::shared_ptr<Type> GetDeclaredTypeForSymbol(symbol::SymbolId symbol_id);
|
||||
std::optional<symbol::SymbolId> ResolveClassSymbol(const std::string& name, const ast::Location& location);
|
||||
std::optional<symbol::SymbolId> ResolveLValueSymbol(const ast::LValue& lvalue);
|
||||
void RegisterParameterTypes(symbol::SymbolId function_id, const std::vector<std::unique_ptr<ast::Parameter>>& parameters);
|
||||
|
||||
private:
|
||||
struct UnitContext
|
||||
{
|
||||
std::string unit_name;
|
||||
std::vector<symbol::UnitImport> interface_imports;
|
||||
std::vector<symbol::UnitImport> implementation_imports;
|
||||
};
|
||||
|
||||
symbol::SymbolTable& symbol_table_;
|
||||
SemanticModel& semantic_model_;
|
||||
|
||||
std::optional<symbol::SymbolId> current_function_id_;
|
||||
std::optional<symbol::SymbolId> current_class_id_;
|
||||
std::optional<UnitContext> current_unit_context_;
|
||||
std::optional<symbol::UnitVisibility> current_unit_section_;
|
||||
std::vector<symbol::UnitImport> file_imports_;
|
||||
ExternalSymbolProvider external_symbol_provider_;
|
||||
std::unordered_map<std::string, symbol::SymbolId> imported_symbols_;
|
||||
};
|
||||
|
||||
struct TokenInfo
|
||||
{
|
||||
ast::Location location;
|
||||
std::string type;
|
||||
std::string text;
|
||||
bool is_named = false;
|
||||
std::uint32_t depth = 0;
|
||||
|
||||
std::string parent_type;
|
||||
std::uint32_t child_index = 0;
|
||||
std::uint32_t sibling_count = 0;
|
||||
|
||||
bool is_error = false;
|
||||
bool is_missing = false;
|
||||
};
|
||||
|
||||
struct TokenCollectionOptions
|
||||
{
|
||||
bool include_anonymous = true;
|
||||
bool include_comments = false;
|
||||
bool include_whitespace = false;
|
||||
bool include_errors = true;
|
||||
bool include_missing = true;
|
||||
|
||||
std::uint32_t max_depth = UINT32_MAX_VALUE;
|
||||
std::uint32_t min_depth = 0;
|
||||
|
||||
std::unordered_set<std::string> include_types;
|
||||
std::unordered_set<std::string> exclude_types;
|
||||
|
||||
bool skip_empty_text = false;
|
||||
std::uint32_t min_text_length = 0;
|
||||
std::uint32_t max_text_length = UINT32_MAX_VALUE;
|
||||
|
||||
bool has_location_filter = false;
|
||||
std::uint32_t filter_start_line = 0;
|
||||
std::uint32_t filter_end_line = UINT32_MAX_VALUE;
|
||||
|
||||
std::function<bool(const TokenInfo&)> custom_filter;
|
||||
|
||||
static TokenCollectionOptions OnlyNamed()
|
||||
{
|
||||
TokenCollectionOptions opts;
|
||||
opts.include_anonymous = false;
|
||||
return opts;
|
||||
}
|
||||
|
||||
static TokenCollectionOptions WithComments()
|
||||
{
|
||||
TokenCollectionOptions opts;
|
||||
opts.include_comments = true;
|
||||
return opts;
|
||||
}
|
||||
|
||||
static TokenCollectionOptions OnlyTypes(const std::vector<std::string>& types)
|
||||
{
|
||||
TokenCollectionOptions opts;
|
||||
opts.include_types = std::unordered_set<std::string>(types.begin(), types.end());
|
||||
return opts;
|
||||
}
|
||||
|
||||
static TokenCollectionOptions ExcludeTypes(const std::vector<std::string>& types)
|
||||
{
|
||||
TokenCollectionOptions opts;
|
||||
opts.exclude_types = std::unordered_set<std::string>(types.begin(), types.end());
|
||||
return opts;
|
||||
}
|
||||
|
||||
static TokenCollectionOptions InRange(std::uint32_t start_line, std::uint32_t end_line)
|
||||
{
|
||||
TokenCollectionOptions opts;
|
||||
opts.has_location_filter = true;
|
||||
opts.filter_start_line = start_line;
|
||||
opts.filter_end_line = end_line;
|
||||
return opts;
|
||||
}
|
||||
};
|
||||
|
||||
class TokenCollector
|
||||
{
|
||||
public:
|
||||
explicit TokenCollector(const TokenCollectionOptions& options = {});
|
||||
~TokenCollector() = default;
|
||||
TokenCollector(const TokenCollector&) = delete;
|
||||
TokenCollector& operator=(const TokenCollector&) = delete;
|
||||
TokenCollector(TokenCollector&&) = default;
|
||||
TokenCollector& operator=(TokenCollector&&) = default;
|
||||
|
||||
std::vector<TokenInfo> Collect(TSNode root, const std::string& source);
|
||||
std::vector<TokenInfo> CollectByType(TSNode root, const std::string& source, const std::vector<std::string>& types);
|
||||
std::vector<TokenInfo> CollectInRange(TSNode root, const std::string& source, std::uint32_t start_line, std::uint32_t end_line);
|
||||
std::vector<TokenInfo> CollectLeafNodes(TSNode root, const std::string& source);
|
||||
|
||||
std::vector<TokenInfo> FindTokensAtPosition(TSNode root, const std::string& source, std::uint32_t line, std::uint32_t column);
|
||||
std::vector<TokenInfo> FindTokensByText(TSNode root, const std::string& source, const std::string& text, bool exact_match = true);
|
||||
std::vector<TokenInfo> FindTokensBy(TSNode root, const std::string& source, std::function<bool(const TokenInfo&)> predicate);
|
||||
|
||||
std::uint32_t CountTokensByType(TSNode root, const std::string& source, const std::string& type);
|
||||
std::vector<std::string> GetUniqueTypes(TSNode root, const std::string& source);
|
||||
|
||||
void SetOptions(const TokenCollectionOptions& options) { options_ = options; }
|
||||
const TokenCollectionOptions& GetOptions() const { return options_; }
|
||||
|
||||
private:
|
||||
void CollectRecursive(TSNode node, const std::string& source, std::vector<TokenInfo>& tokens, std::uint32_t depth, TSNode parent);
|
||||
bool ShouldCollectNode(const TokenInfo& info) const;
|
||||
void FillTokenInfo(TokenInfo& info, TSNode node, const std::string& source, std::uint32_t depth, TSNode parent) const;
|
||||
bool IsCommentNode(const std::string& type) const;
|
||||
bool IsWhitespaceNode(const std::string& type) const;
|
||||
bool IsLocationInRange(const ast::Location& loc) const;
|
||||
|
||||
private:
|
||||
TokenCollectionOptions options_;
|
||||
};
|
||||
|
||||
inline std::vector<TokenInfo> CollectNamedTokens(TSNode root, const std::string& source)
|
||||
{
|
||||
return TokenCollector(TokenCollectionOptions::OnlyNamed()).Collect(root, source);
|
||||
}
|
||||
|
||||
inline std::vector<TokenInfo> CollectTokensOfType(TSNode root, const std::string& source, const std::string& type)
|
||||
{
|
||||
return TokenCollector().CollectByType(root, source, { type });
|
||||
}
|
||||
} // namespace lsp::language::semantic
|
||||
// Legacy placeholder kept for compatibility with existing imports.
|
||||
|
|
|
|||
|
|
@ -4,10 +4,131 @@ export module lsp.language.semantic:name_resolver;
|
|||
|
||||
import std;
|
||||
|
||||
import :interface;
|
||||
import :type_system;
|
||||
import lsp.language.ast;
|
||||
import lsp.language.symbol;
|
||||
|
||||
export namespace lsp::language::semantic
|
||||
{
|
||||
struct NameResolutionResult
|
||||
{
|
||||
symbol::SymbolId symbol_id = symbol::kInvalidSymbolId;
|
||||
bool is_ambiguous = false;
|
||||
std::vector<symbol::SymbolId> candidates;
|
||||
|
||||
bool IsResolved() const
|
||||
{
|
||||
return symbol_id != symbol::kInvalidSymbolId;
|
||||
}
|
||||
|
||||
static NameResolutionResult Success(symbol::SymbolId id)
|
||||
{
|
||||
return { id, false, { id } };
|
||||
}
|
||||
|
||||
static NameResolutionResult Ambiguous(std::vector<symbol::SymbolId> symbols)
|
||||
{
|
||||
return {
|
||||
symbols.empty() ? symbol::kInvalidSymbolId : symbols[0],
|
||||
true,
|
||||
std::move(symbols)
|
||||
};
|
||||
}
|
||||
|
||||
static NameResolutionResult NotFound()
|
||||
{
|
||||
return { symbol::kInvalidSymbolId, false, {} };
|
||||
}
|
||||
};
|
||||
|
||||
struct OverloadCandidate
|
||||
{
|
||||
symbol::SymbolId symbol_id;
|
||||
int match_score = 0;
|
||||
std::vector<TypeCompatibility> arg_conversions;
|
||||
|
||||
bool operator<(const OverloadCandidate& other) const
|
||||
{
|
||||
return match_score > other.match_score;
|
||||
}
|
||||
};
|
||||
|
||||
class NameResolver
|
||||
{
|
||||
public:
|
||||
explicit NameResolver(const symbol::SymbolTable& symbol_table,
|
||||
const TypeSystem& type_system) : symbol_table_(symbol_table),
|
||||
type_system_(type_system) {}
|
||||
|
||||
NameResolutionResult ResolveName(
|
||||
const std::string& name,
|
||||
symbol::ScopeId scope_id,
|
||||
bool search_parent = true) const;
|
||||
|
||||
NameResolutionResult ResolveNameAtLocation(
|
||||
const std::string& name,
|
||||
const ast::Location& location) const;
|
||||
|
||||
NameResolutionResult ResolveMemberAccess(
|
||||
symbol::SymbolId object_symbol_id,
|
||||
const std::string& member_name) const;
|
||||
|
||||
NameResolutionResult ResolveClassMember(
|
||||
symbol::SymbolId class_id,
|
||||
const std::string& member_name,
|
||||
bool static_only = false) const;
|
||||
|
||||
NameResolutionResult ResolveFunctionCall(
|
||||
const std::string& function_name,
|
||||
const std::vector<std::shared_ptr<Type>>& arg_types,
|
||||
symbol::ScopeId scope_id) const;
|
||||
|
||||
NameResolutionResult ResolveMethodCall(
|
||||
symbol::SymbolId object_symbol_id,
|
||||
const std::string& method_name,
|
||||
const std::vector<std::shared_ptr<Type>>& arg_types) const;
|
||||
|
||||
NameResolutionResult ResolveQualifiedName(
|
||||
const std::string& qualifier,
|
||||
const std::string& name,
|
||||
symbol::ScopeId scope_id) const;
|
||||
|
||||
std::optional<symbol::ScopeId> GetSymbolScope(
|
||||
[[maybe_unused]] symbol::SymbolId symbol_id) const;
|
||||
|
||||
bool IsSymbolVisibleInScope(
|
||||
[[maybe_unused]] symbol::SymbolId symbol_id,
|
||||
[[maybe_unused]] symbol::ScopeId scope_id) const;
|
||||
|
||||
private:
|
||||
const symbol::SymbolTable& symbol_table_;
|
||||
const TypeSystem& type_system_;
|
||||
|
||||
std::vector<symbol::SymbolId> SearchScopeChain(
|
||||
const std::string& name,
|
||||
symbol::ScopeId start_scope) const;
|
||||
|
||||
std::optional<symbol::ScopeId> FindScopeOwnedBy(symbol::SymbolId owner) const;
|
||||
|
||||
NameResolutionResult SelectBestOverload(
|
||||
const std::vector<OverloadCandidate>& candidates) const;
|
||||
|
||||
OverloadCandidate CalculateOverloadScore(
|
||||
symbol::SymbolId candidate_id,
|
||||
const std::vector<std::shared_ptr<Type>>& arg_types) const;
|
||||
|
||||
std::vector<std::shared_ptr<Type>> GetParameterTypes(
|
||||
symbol::SymbolId symbol_id) const;
|
||||
|
||||
std::optional<symbol::SymbolId> GetOwnerClassId(
|
||||
[[maybe_unused]] symbol::SymbolId symbol_id) const;
|
||||
|
||||
bool CheckMemberAccessibility(
|
||||
[[maybe_unused]] const symbol::Symbol& member,
|
||||
[[maybe_unused]] symbol::ScopeId access_scope) const;
|
||||
};
|
||||
}
|
||||
|
||||
namespace lsp::language::semantic
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ import std;
|
|||
|
||||
// 聚合导出语义模块的各个分区
|
||||
export import :interface;
|
||||
export import :type_system.types;
|
||||
export import :type_system;
|
||||
export import :name_resolver;
|
||||
export import :semantic_model;
|
||||
export import :analyzer;
|
||||
export import :token_collector;
|
||||
export import :graph.types;
|
||||
export import :graph.call;
|
||||
export import :graph.inheritance;
|
||||
export import :graph.reference;
|
||||
|
|
|
|||
|
|
@ -4,10 +4,80 @@ export module lsp.language.semantic:semantic_model;
|
|||
|
||||
import std;
|
||||
|
||||
import :interface;
|
||||
import :graph.call;
|
||||
import :graph.inheritance;
|
||||
import :graph.reference;
|
||||
import :name_resolver;
|
||||
import :type_system;
|
||||
import lsp.language.ast;
|
||||
import lsp.language.symbol;
|
||||
import lsp.protocol.types;
|
||||
import lsp.utils.string;
|
||||
|
||||
export namespace lsp::language::semantic
|
||||
{
|
||||
class SemanticModel
|
||||
{
|
||||
public:
|
||||
struct UnitImportSet
|
||||
{
|
||||
std::vector<symbol::UnitImport> interface_imports;
|
||||
std::vector<symbol::UnitImport> implementation_imports;
|
||||
};
|
||||
|
||||
explicit SemanticModel(const symbol::SymbolTable& symbol_table);
|
||||
|
||||
void Clear();
|
||||
void OnSymbolRemoved(symbol::SymbolId id);
|
||||
void AddReference(symbol::SymbolId symbol_id, const ast::Location& location, bool is_definition = false, bool is_write = false);
|
||||
void AddInheritance(symbol::SymbolId derived, symbol::SymbolId base);
|
||||
void AddCall(symbol::SymbolId caller, symbol::SymbolId callee, const ast::Location& location);
|
||||
|
||||
graph::Reference& references() { return reference_graph_; }
|
||||
graph::Inheritance& inheritance() { return inheritance_graph_; }
|
||||
graph::Call& calls() { return call_graph_; }
|
||||
|
||||
const graph::Reference& references() const { return reference_graph_; }
|
||||
const graph::Inheritance& inheritance() const { return inheritance_graph_; }
|
||||
const graph::Call& calls() const { return call_graph_; }
|
||||
|
||||
TypeSystem& type_system() { return type_system_; }
|
||||
const TypeSystem& type_system() const { return type_system_; }
|
||||
|
||||
NameResolver& name_resolver() { return *name_resolver_; }
|
||||
const NameResolver& name_resolver() const { return *name_resolver_; }
|
||||
|
||||
void RegisterUnitImports(const std::string& unit_name, UnitImportSet imports);
|
||||
std::optional<UnitImportSet> GetUnitImports(const std::string& unit_name) const;
|
||||
|
||||
std::shared_ptr<Type> GetSymbolType(symbol::SymbolId symbol_id) const
|
||||
{
|
||||
return type_system_.GetSymbolType(symbol_id);
|
||||
}
|
||||
|
||||
void SetSymbolType(symbol::SymbolId symbol_id, std::shared_ptr<Type> type)
|
||||
{
|
||||
type_system_.RegisterSymbolType(symbol_id, std::move(type));
|
||||
}
|
||||
|
||||
bool IsSubclassOf(symbol::SymbolId derived, symbol::SymbolId base) const
|
||||
{
|
||||
return inheritance_graph_.IsSubclassOf(derived, base);
|
||||
}
|
||||
|
||||
private:
|
||||
const symbol::SymbolTable& symbol_table_;
|
||||
|
||||
graph::Reference reference_graph_;
|
||||
graph::Inheritance inheritance_graph_;
|
||||
graph::Call call_graph_;
|
||||
|
||||
TypeSystem type_system_;
|
||||
|
||||
std::unique_ptr<NameResolver> name_resolver_;
|
||||
std::unordered_map<std::string, UnitImportSet, utils::IHasher, utils::IEqualTo> unit_imports_;
|
||||
};
|
||||
}
|
||||
|
||||
namespace lsp::language::semantic
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,9 +6,136 @@ import tree_sitter;
|
|||
|
||||
import std;
|
||||
|
||||
import :interface;
|
||||
import lsp.language.ast;
|
||||
|
||||
export namespace lsp::language::semantic
|
||||
{
|
||||
struct TokenInfo
|
||||
{
|
||||
ast::Location location;
|
||||
std::string type;
|
||||
std::string text;
|
||||
bool is_named = false;
|
||||
std::uint32_t depth = 0;
|
||||
|
||||
std::string parent_type;
|
||||
std::uint32_t child_index = 0;
|
||||
std::uint32_t sibling_count = 0;
|
||||
|
||||
bool is_error = false;
|
||||
bool is_missing = false;
|
||||
};
|
||||
|
||||
struct TokenCollectionOptions
|
||||
{
|
||||
bool include_anonymous = true;
|
||||
bool include_comments = false;
|
||||
bool include_whitespace = false;
|
||||
bool include_errors = true;
|
||||
bool include_missing = true;
|
||||
|
||||
std::uint32_t max_depth = UINT32_MAX_VALUE;
|
||||
std::uint32_t min_depth = 0;
|
||||
|
||||
std::unordered_set<std::string> include_types;
|
||||
std::unordered_set<std::string> exclude_types;
|
||||
|
||||
bool skip_empty_text = false;
|
||||
std::uint32_t min_text_length = 0;
|
||||
std::uint32_t max_text_length = UINT32_MAX_VALUE;
|
||||
|
||||
bool has_location_filter = false;
|
||||
std::uint32_t filter_start_line = 0;
|
||||
std::uint32_t filter_end_line = UINT32_MAX_VALUE;
|
||||
|
||||
std::function<bool(const TokenInfo&)> custom_filter;
|
||||
|
||||
static TokenCollectionOptions OnlyNamed()
|
||||
{
|
||||
TokenCollectionOptions opts;
|
||||
opts.include_anonymous = false;
|
||||
return opts;
|
||||
}
|
||||
|
||||
static TokenCollectionOptions WithComments()
|
||||
{
|
||||
TokenCollectionOptions opts;
|
||||
opts.include_comments = true;
|
||||
return opts;
|
||||
}
|
||||
|
||||
static TokenCollectionOptions OnlyTypes(const std::vector<std::string>& types)
|
||||
{
|
||||
TokenCollectionOptions opts;
|
||||
opts.include_types = std::unordered_set<std::string>(types.begin(), types.end());
|
||||
return opts;
|
||||
}
|
||||
|
||||
static TokenCollectionOptions ExcludeTypes(const std::vector<std::string>& types)
|
||||
{
|
||||
TokenCollectionOptions opts;
|
||||
opts.exclude_types = std::unordered_set<std::string>(types.begin(), types.end());
|
||||
return opts;
|
||||
}
|
||||
|
||||
static TokenCollectionOptions InRange(std::uint32_t start_line, std::uint32_t end_line)
|
||||
{
|
||||
TokenCollectionOptions opts;
|
||||
opts.has_location_filter = true;
|
||||
opts.filter_start_line = start_line;
|
||||
opts.filter_end_line = end_line;
|
||||
return opts;
|
||||
}
|
||||
};
|
||||
|
||||
class TokenCollector
|
||||
{
|
||||
public:
|
||||
explicit TokenCollector(const TokenCollectionOptions& options = {});
|
||||
~TokenCollector() = default;
|
||||
TokenCollector(const TokenCollector&) = delete;
|
||||
TokenCollector& operator=(const TokenCollector&) = delete;
|
||||
TokenCollector(TokenCollector&&) = default;
|
||||
TokenCollector& operator=(TokenCollector&&) = default;
|
||||
|
||||
std::vector<TokenInfo> Collect(TSNode root, const std::string& source);
|
||||
std::vector<TokenInfo> CollectByType(TSNode root, const std::string& source, const std::vector<std::string>& types);
|
||||
std::vector<TokenInfo> CollectInRange(TSNode root, const std::string& source, std::uint32_t start_line, std::uint32_t end_line);
|
||||
std::vector<TokenInfo> CollectLeafNodes(TSNode root, const std::string& source);
|
||||
|
||||
std::vector<TokenInfo> FindTokensAtPosition(TSNode root, const std::string& source, std::uint32_t line, std::uint32_t column);
|
||||
std::vector<TokenInfo> FindTokensByText(TSNode root, const std::string& source, const std::string& text, bool exact_match = true);
|
||||
std::vector<TokenInfo> FindTokensBy(TSNode root, const std::string& source, std::function<bool(const TokenInfo&)> predicate);
|
||||
|
||||
std::uint32_t CountTokensByType(TSNode root, const std::string& source, const std::string& type);
|
||||
std::vector<std::string> GetUniqueTypes(TSNode root, const std::string& source);
|
||||
|
||||
void SetOptions(const TokenCollectionOptions& options) { options_ = options; }
|
||||
const TokenCollectionOptions& GetOptions() const { return options_; }
|
||||
|
||||
private:
|
||||
void CollectRecursive(TSNode node, const std::string& source, std::vector<TokenInfo>& tokens, std::uint32_t depth, TSNode parent);
|
||||
bool ShouldCollectNode(const TokenInfo& info) const;
|
||||
void FillTokenInfo(TokenInfo& info, TSNode node, const std::string& source, std::uint32_t depth, TSNode parent) const;
|
||||
bool IsCommentNode(const std::string& type) const;
|
||||
bool IsWhitespaceNode(const std::string& type) const;
|
||||
bool IsLocationInRange(const ast::Location& loc) const;
|
||||
|
||||
private:
|
||||
TokenCollectionOptions options_;
|
||||
};
|
||||
|
||||
inline std::vector<TokenInfo> CollectNamedTokens(TSNode root, const std::string& source)
|
||||
{
|
||||
return TokenCollector(TokenCollectionOptions::OnlyNamed()).Collect(root, source);
|
||||
}
|
||||
|
||||
inline std::vector<TokenInfo> CollectTokensOfType(TSNode root, const std::string& source, const std::string& type)
|
||||
{
|
||||
return TokenCollector().CollectByType(root, source, { type });
|
||||
}
|
||||
}
|
||||
|
||||
namespace lsp::language::semantic
|
||||
{
|
||||
TokenCollector::TokenCollector(const TokenCollectionOptions& options) :
|
||||
|
|
|
|||
|
|
@ -4,11 +4,99 @@ export module lsp.language.semantic:type_system;
|
|||
|
||||
import std;
|
||||
|
||||
import :interface;
|
||||
export import :type_system.types;
|
||||
|
||||
import lsp.language.symbol;
|
||||
import lsp.utils.string;
|
||||
|
||||
export namespace lsp::language::semantic
|
||||
{
|
||||
class TypeSystem
|
||||
{
|
||||
public:
|
||||
TypeSystem();
|
||||
|
||||
std::shared_ptr<Type> GetIntType() const { return int_type_; }
|
||||
std::shared_ptr<Type> GetFloatType() const { return float_type_; }
|
||||
std::shared_ptr<Type> GetStringType() const { return string_type_; }
|
||||
std::shared_ptr<Type> GetBoolType() const { return bool_type_; }
|
||||
std::shared_ptr<Type> GetCharType() const { return char_type_; }
|
||||
std::shared_ptr<Type> GetVoidType() const { return void_type_; }
|
||||
std::shared_ptr<Type> GetUnknownType() const { return unknown_type_; }
|
||||
std::shared_ptr<Type> GetErrorType() const { return error_type_; }
|
||||
|
||||
std::shared_ptr<Type> CreateClassType(symbol::SymbolId class_id);
|
||||
std::shared_ptr<Type> CreateArrayType(std::shared_ptr<Type> element_type);
|
||||
std::shared_ptr<Type> CreateFunctionType(
|
||||
std::vector<std::shared_ptr<Type>> param_types,
|
||||
std::shared_ptr<Type> return_type);
|
||||
std::shared_ptr<Type> CreateOptionalType(std::shared_ptr<Type> inner_type);
|
||||
|
||||
void RegisterClassType(const std::string& type_name, symbol::SymbolId class_id);
|
||||
std::shared_ptr<Type> GetTypeByName(const std::string& type_name) const;
|
||||
|
||||
std::shared_ptr<Type> GetSymbolType(symbol::SymbolId symbol_id) const;
|
||||
|
||||
void RegisterSymbolType(symbol::SymbolId symbol_id, std::shared_ptr<Type> type);
|
||||
|
||||
TypeCompatibility CheckCompatibility(const Type& from, const Type& to) const;
|
||||
|
||||
bool IsAssignable(const Type& from, const Type& to) const;
|
||||
|
||||
bool RequiresExplicitCast(const Type& from, const Type& to) const;
|
||||
|
||||
std::shared_ptr<Type> InferBinaryExpressionType(
|
||||
const Type& left,
|
||||
const Type& right,
|
||||
const std::string& op) const;
|
||||
|
||||
std::shared_ptr<Type> InferUnaryExpressionType(
|
||||
const Type& operand,
|
||||
const std::string& op) const;
|
||||
|
||||
std::shared_ptr<Type> InferLiteralType(const std::string& literal_value) const;
|
||||
|
||||
void SetInheritanceChecker(
|
||||
std::function<bool(symbol::SymbolId, symbol::SymbolId)> checker)
|
||||
{
|
||||
is_subclass_of_ = std::move(checker);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Type> int_type_;
|
||||
std::shared_ptr<Type> float_type_;
|
||||
std::shared_ptr<Type> string_type_;
|
||||
std::shared_ptr<Type> bool_type_;
|
||||
std::shared_ptr<Type> char_type_;
|
||||
std::shared_ptr<Type> void_type_;
|
||||
std::shared_ptr<Type> unknown_type_;
|
||||
std::shared_ptr<Type> error_type_;
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<Type>> type_by_name_;
|
||||
|
||||
std::unordered_map<symbol::SymbolId, std::shared_ptr<Type>> symbol_types_;
|
||||
|
||||
std::function<bool(symbol::SymbolId derived, symbol::SymbolId base)>
|
||||
is_subclass_of_;
|
||||
|
||||
TypeCompatibility CheckPrimitiveCompatibility(
|
||||
const PrimitiveType& from,
|
||||
const PrimitiveType& to) const;
|
||||
|
||||
TypeCompatibility CheckClassCompatibility(
|
||||
const ClassType& from,
|
||||
const ClassType& to) const;
|
||||
|
||||
TypeCompatibility CheckArrayCompatibility(
|
||||
const ArrayType& from,
|
||||
const ArrayType& to) const;
|
||||
|
||||
TypeCompatibility CheckFunctionCompatibility(
|
||||
const FunctionType& from,
|
||||
const FunctionType& to) const;
|
||||
};
|
||||
}
|
||||
|
||||
namespace lsp::language::semantic
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,206 @@
|
|||
module;
|
||||
|
||||
export module lsp.language.semantic:type_system.types;
|
||||
|
||||
import std;
|
||||
|
||||
import lsp.language.symbol;
|
||||
|
||||
export namespace lsp::language::semantic
|
||||
{
|
||||
enum class TypeKind
|
||||
{
|
||||
kPrimitive,
|
||||
kClass,
|
||||
kArray,
|
||||
kFunction,
|
||||
kOptional,
|
||||
kVoid,
|
||||
kUnknown,
|
||||
kError
|
||||
};
|
||||
|
||||
enum class PrimitiveTypeKind
|
||||
{
|
||||
kInt,
|
||||
kFloat,
|
||||
kString,
|
||||
kBool,
|
||||
kChar
|
||||
};
|
||||
|
||||
struct TypeCompatibility
|
||||
{
|
||||
bool is_compatible = false;
|
||||
int conversion_cost = -1;
|
||||
bool requires_cast = false;
|
||||
|
||||
static TypeCompatibility Exact()
|
||||
{
|
||||
return { true, 0, false };
|
||||
}
|
||||
|
||||
static TypeCompatibility Implicit(int cost)
|
||||
{
|
||||
return { true, cost, false };
|
||||
}
|
||||
|
||||
static TypeCompatibility ExplicitCast(int cost)
|
||||
{
|
||||
return { true, cost, true };
|
||||
}
|
||||
|
||||
static TypeCompatibility Incompatible()
|
||||
{
|
||||
return { false, -1, false };
|
||||
}
|
||||
};
|
||||
|
||||
class Type;
|
||||
|
||||
class PrimitiveType
|
||||
{
|
||||
public:
|
||||
explicit PrimitiveType(PrimitiveTypeKind kind) : kind_(kind) {}
|
||||
|
||||
PrimitiveTypeKind kind() const { return kind_; }
|
||||
std::string ToString() const;
|
||||
|
||||
bool operator==(const PrimitiveType& other) const
|
||||
{
|
||||
return kind_ == other.kind_;
|
||||
}
|
||||
|
||||
private:
|
||||
PrimitiveTypeKind kind_;
|
||||
};
|
||||
|
||||
class ClassType
|
||||
{
|
||||
public:
|
||||
explicit ClassType(symbol::SymbolId class_id) : class_id_(class_id) {}
|
||||
|
||||
symbol::SymbolId class_id() const { return class_id_; }
|
||||
|
||||
bool operator==(const ClassType& other) const
|
||||
{
|
||||
return class_id_ == other.class_id_;
|
||||
}
|
||||
|
||||
private:
|
||||
symbol::SymbolId class_id_;
|
||||
};
|
||||
|
||||
class ArrayType
|
||||
{
|
||||
public:
|
||||
explicit ArrayType(std::shared_ptr<Type> element_type) : element_type_(std::move(element_type)) {}
|
||||
|
||||
const Type& element_type() const { return *element_type_; }
|
||||
std::shared_ptr<Type> element_type_ptr() const { return element_type_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Type> element_type_;
|
||||
};
|
||||
|
||||
class FunctionType
|
||||
{
|
||||
public:
|
||||
FunctionType(std::vector<std::shared_ptr<Type>> param_types,
|
||||
std::shared_ptr<Type> return_type) : param_types_(std::move(param_types)),
|
||||
return_type_(std::move(return_type)) {}
|
||||
|
||||
const std::vector<std::shared_ptr<Type>>& param_types() const
|
||||
{
|
||||
return param_types_;
|
||||
}
|
||||
|
||||
const Type& return_type() const { return *return_type_; }
|
||||
std::shared_ptr<Type> return_type_ptr() const { return return_type_; }
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Type>> param_types_;
|
||||
std::shared_ptr<Type> return_type_;
|
||||
};
|
||||
|
||||
class OptionalType
|
||||
{
|
||||
public:
|
||||
explicit OptionalType(std::shared_ptr<Type> inner_type) : inner_type_(std::move(inner_type)) {}
|
||||
|
||||
const Type& inner_type() const { return *inner_type_; }
|
||||
std::shared_ptr<Type> inner_type_ptr() const { return inner_type_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Type> inner_type_;
|
||||
};
|
||||
|
||||
class VoidType
|
||||
{
|
||||
public:
|
||||
bool operator==(const VoidType&) const { return true; }
|
||||
};
|
||||
|
||||
class UnknownType
|
||||
{
|
||||
public:
|
||||
bool operator==(const UnknownType&) const { return true; }
|
||||
};
|
||||
|
||||
class ErrorType
|
||||
{
|
||||
public:
|
||||
explicit ErrorType(std::string message = "") : message_(std::move(message)) {}
|
||||
|
||||
const std::string& message() const { return message_; }
|
||||
|
||||
bool operator==(const ErrorType&) const { return true; }
|
||||
|
||||
private:
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
using TypeData = std::variant<
|
||||
PrimitiveType,
|
||||
ClassType,
|
||||
ArrayType,
|
||||
FunctionType,
|
||||
OptionalType,
|
||||
VoidType,
|
||||
UnknownType,
|
||||
ErrorType>;
|
||||
|
||||
class Type
|
||||
{
|
||||
public:
|
||||
explicit Type(TypeData data) : data_(std::move(data)) {}
|
||||
|
||||
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_);
|
||||
}
|
||||
|
||||
TypeKind kind() const;
|
||||
std::string ToString() const;
|
||||
|
||||
bool Equals(const Type& other) const;
|
||||
|
||||
const TypeData& data() const { return data_; }
|
||||
|
||||
private:
|
||||
TypeData data_;
|
||||
};
|
||||
}
|
||||
|
|
@ -83,11 +83,13 @@ target_sources(
|
|||
../../src/language/symbol/symbol.cppm
|
||||
../../src/language/semantic/interface.cppm
|
||||
../../src/language/semantic/semantic.cppm
|
||||
../../src/language/semantic/type_system.types.cppm
|
||||
../../src/language/semantic/analyzer.cppm
|
||||
../../src/language/semantic/semantic_model.cppm
|
||||
../../src/language/semantic/type_system.cppm
|
||||
../../src/language/semantic/name_resolver.cppm
|
||||
../../src/language/semantic/token_collector.cppm
|
||||
../../src/language/semantic/graph/types.cppm
|
||||
../../src/language/semantic/graph/call.cppm
|
||||
../../src/language/semantic/graph/reference.cppm
|
||||
../../src/language/semantic/graph/inheritance.cppm
|
||||
|
|
|
|||
|
|
@ -27,10 +27,12 @@ set(SOURCES
|
|||
../../src/language/symbol/index/location.cppm
|
||||
../../src/language/symbol/index/scope.cppm
|
||||
../../src/language/semantic/interface.cppm
|
||||
../../src/language/semantic/type_system.types.cppm
|
||||
../../src/language/semantic/analyzer.cppm
|
||||
../../src/language/semantic/semantic_model.cppm
|
||||
../../src/language/semantic/type_system.cppm
|
||||
../../src/language/semantic/name_resolver.cppm
|
||||
../../src/language/semantic/graph/types.cppm
|
||||
../../src/language/semantic/graph/call.cppm
|
||||
../../src/language/semantic/graph/reference.cppm
|
||||
../../src/language/semantic/graph/inheritance.cppm
|
||||
|
|
@ -93,11 +95,13 @@ target_sources(
|
|||
../../src/language/symbol/symbol.cppm
|
||||
../../src/language/semantic/interface.cppm
|
||||
../../src/language/semantic/semantic.cppm
|
||||
../../src/language/semantic/type_system.types.cppm
|
||||
../../src/language/semantic/type_system.cppm
|
||||
../../src/language/semantic/name_resolver.cppm
|
||||
../../src/language/semantic/semantic_model.cppm
|
||||
../../src/language/semantic/analyzer.cppm
|
||||
../../src/language/semantic/token_collector.cppm
|
||||
../../src/language/semantic/graph/types.cppm
|
||||
../../src/language/semantic/graph/call.cppm
|
||||
../../src/language/semantic/graph/inheritance.cppm
|
||||
../../src/language/semantic/graph/reference.cppm
|
||||
|
|
|
|||
Loading…
Reference in New Issue