From 88ce75ceb184faf3ecffc9395a1ae67a27a4f50b Mon Sep 17 00:00:00 2001 From: Pasha Bibko <156938226+PashaBibko@users.noreply.github.com> Date: Sun, 27 Apr 2025 21:48:16 +0100 Subject: [PATCH] Changed how scopes work Also added logging and GenIR functions for VariableDeclaration --- IR-Generator/inc/Parser.h | 26 ++++----------- Parser/inc/AST.h | 3 ++ Parser/src/AST-Constructors.cpp | 7 +++- Parser/src/AST-LLVM.cpp | 5 +++ Parser/src/AST-Loggers.cpp | 6 ++++ Parser/src/GenIR.cpp | 1 + Parser/src/Scope.cpp | 58 ++++++++------------------------- example/main.lx | 2 +- 8 files changed, 41 insertions(+), 67 deletions(-) diff --git a/IR-Generator/inc/Parser.h b/IR-Generator/inc/Parser.h index f66cfa6..530eafa 100644 --- a/IR-Generator/inc/Parser.h +++ b/IR-Generator/inc/Parser.h @@ -3,7 +3,7 @@ // Lexer foward declares fstream components so we can use them here // #include -#include +#include #include // Foward declares STD stuff that is passed around // @@ -30,6 +30,7 @@ namespace LX::AST NUMBER_LITERAL, OPERATION, + VARIABLE_DECLARATION, // Control flow Nodes // @@ -81,18 +82,6 @@ namespace LX class Scope { public: - // Struct to store all the info of a variable (excluding name as that is stored in the map) // - struct __declspec(novtable) Variable final - { - // Default constructor // - Variable() {} - - // Will hold the type as part of the type here // - - // The value of the variable // - llvm::Value* val = nullptr; - }; - // Error thrown if the user tried to create a variable that already existed // struct __declspec(novtable) VariableAlreadyExists final {}; @@ -105,17 +94,14 @@ namespace LX {} // Gets a variable from the scope by it's name // - Variable* GetVarOfName(const std::string& name); + bool DoesVarExist(const std::string& name); - // Creates a variable of the given name, returns nullptr if a variable with that name already exists // - Variable* CreateVar(const std::string& name); + // Creates a variable of the given name // + void CreateVar(const std::string& name); private: - // Base logic for getting a variable by it's name without any error checking // - Variable* GetVarOfNameImpl(const std::string& name); - // Holds all the variables in the scope (excluding ones owned by the children // - std::unordered_map m_LocalVariables; + std::unordered_set m_LocalVariables; // Holds a section of the scope, for example the variables created in a while loop // std::unique_ptr m_Child; diff --git a/Parser/inc/AST.h b/Parser/inc/AST.h index 9df97b8..9544602 100644 --- a/Parser/inc/AST.h +++ b/Parser/inc/AST.h @@ -15,6 +15,9 @@ namespace LX llvm::LLVMContext context; llvm::Module module; llvm::IRBuilder<> builder; + + // Not LLVM I just cba to add this parameter to the functions that needed it // + Scope* scope; }; } diff --git a/Parser/src/AST-Constructors.cpp b/Parser/src/AST-Constructors.cpp index 2652900..58650fe 100644 --- a/Parser/src/AST-Constructors.cpp +++ b/Parser/src/AST-Constructors.cpp @@ -6,7 +6,7 @@ namespace LX { // Default constructor that just initalises LLVM variables that it holds // InfoLLVM::InfoLLVM(std::string name) - : context{}, builder(context), module(name, context) + : context{}, builder(context), module(name, context), scope(nullptr) {} // Reserves space for nodes (stops excess allocations) // @@ -41,4 +41,9 @@ namespace LX::AST ReturnStatement::ReturnStatement(std::unique_ptr val) : Node(Node::RETURN_STATEMENT), m_Val(std::move(val)) {} + + // Passes constructor args to values and sets type // + VariableDeclaration::VariableDeclaration(const std::string& name) + : Node(Node::VARIABLE_DECLARATION), m_Name(name) + {} } diff --git a/Parser/src/AST-LLVM.cpp b/Parser/src/AST-LLVM.cpp index 5524cff..9d6f6e8 100644 --- a/Parser/src/AST-LLVM.cpp +++ b/Parser/src/AST-LLVM.cpp @@ -70,5 +70,10 @@ namespace LX::AST // Function for generating LLVN IR (Intermediate representation) // llvm::Value* VariableDeclaration::GenIR(InfoLLVM& LLVM) { + // Creates the variable within the scope // + LLVM.scope->CreateVar(m_Name); + + // Creates the declaration within the IR // + return LLVM.builder.CreateAlloca(LLVM.builder.getInt32Ty(), nullptr, m_Name); } } diff --git a/Parser/src/AST-Loggers.cpp b/Parser/src/AST-Loggers.cpp index 20b3512..6f9c20b 100644 --- a/Parser/src/AST-Loggers.cpp +++ b/Parser/src/AST-Loggers.cpp @@ -40,4 +40,10 @@ namespace LX::AST m_Val->Log(log, depth + 1); } } + + void VariableDeclaration::Log(std::ofstream* log, unsigned depth) + { + (*log) << std::string(depth, '\t'); + (*log) << "Variable declaration: " << m_Name << "\n"; + } } diff --git a/Parser/src/GenIR.cpp b/Parser/src/GenIR.cpp index cbb3aa5..053b439 100644 --- a/Parser/src/GenIR.cpp +++ b/Parser/src/GenIR.cpp @@ -55,6 +55,7 @@ namespace LX // Loops over the functions to generate their LLVM IR // for (auto& func : ast.functions) { + LLVM.scope = &func.scope; // Sets the current scope for the builder GenerateFunctionIR(func, LLVM); } diff --git a/Parser/src/Scope.cpp b/Parser/src/Scope.cpp index 263f6e2..42cdbf4 100644 --- a/Parser/src/Scope.cpp +++ b/Parser/src/Scope.cpp @@ -4,64 +4,32 @@ namespace LX { - // Util function for getting a pointer to an item from a map // - static inline Scope::Variable* GetFromMap(const std::string& name, std::unordered_map& map) + bool Scope::DoesVarExist(const std::string& name) { - // Checks if it is in a map and if so returns it // - if (auto it = map.find(name); it != map.end()) { return &it->second; } - - // Else returns null // - else { return nullptr; } - } - - // Base logic for getting a variable by it's name without any error checking // - Scope::Variable* Scope::GetVarOfNameImpl(const std::string& name) - { - // Stores the current scope that is being checked for the variable // + // Stores a pointer to the current scope // Scope* current = this; - // Loops over the scope and it's child to find the variable // do { - // Gets the variable (if it exists) // - Variable* var = GetFromMap(name, current->m_LocalVariables); + // Checks if the variable exists in the current scope // + bool exists = current->m_LocalVariables.contains(name); + if (exists) { return true; } - // Returns the variable if it exists // - if (var != nullptr) { return var; } - - // Assigns current to the child to recursively check // - // Doing it like this avoids recursive functions // + // Travels to the next scope // current = current->m_Child.get(); } while (current != nullptr); - // Else returns a nullptr and lets the caller handle the error checking // - return nullptr; + // If it gets here it means it couldnt find the variable so it doesnt exist in the current context // + return false; } - // Gets a variable from the scope by it's name // - Scope::Variable* Scope::GetVarOfName(const std::string& name) + void Scope::CreateVar(const std::string& name) { - // Gets the variable (if it exists) // - Variable* var = GetVarOfNameImpl(name); + // Checks variable of the same name doesn't exist // + ThrowIf(DoesVarExist(name)); - // Throws an error if the variable doesn't exist // - ThrowIf(var == nullptr); - - // Else it can return the variable // - return var; - } - - // Creates a variable of the given name, returns nullptr if a variable with that name already exists // - Scope::Variable* Scope::CreateVar(const std::string& name) - { - // Checks if a variable with the same name already exists // - Variable* alreadyExist = GetVarOfNameImpl(name); - - // Throws an error if the variable already exists // - ThrowIf(alreadyExist != nullptr); - - // Else creates the variable and returns it // - return &m_LocalVariables[name]; + // Else inserts it into the local set // + m_LocalVariables.insert(name); } } diff --git a/example/main.lx b/example/main.lx index 63ad11d..50cf183 100644 --- a/example/main.lx +++ b/example/main.lx @@ -1,4 +1,4 @@ func main() { - int a = 5 + int a }