Changed how scopes work

Also added logging and GenIR functions for VariableDeclaration
This commit is contained in:
Pasha Bibko
2025-04-27 21:48:16 +01:00
parent a64aa28432
commit 88ce75ceb1
8 changed files with 41 additions and 67 deletions

View File

@@ -3,7 +3,7 @@
// Lexer foward declares fstream components so we can use them here // // Lexer foward declares fstream components so we can use them here //
#include <Lexer.h> #include <Lexer.h>
#include <unordered_map> #include <unordered_set>
#include <memory> #include <memory>
// Foward declares STD stuff that is passed around // // Foward declares STD stuff that is passed around //
@@ -30,6 +30,7 @@ namespace LX::AST
NUMBER_LITERAL, NUMBER_LITERAL,
OPERATION, OPERATION,
VARIABLE_DECLARATION,
// Control flow Nodes // // Control flow Nodes //
@@ -81,18 +82,6 @@ namespace LX
class Scope class Scope
{ {
public: 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 // // Error thrown if the user tried to create a variable that already existed //
struct __declspec(novtable) VariableAlreadyExists final {}; struct __declspec(novtable) VariableAlreadyExists final {};
@@ -105,17 +94,14 @@ namespace LX
{} {}
// Gets a variable from the scope by it's name // // 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 // // Creates a variable of the given name //
Variable* CreateVar(const std::string& name); void CreateVar(const std::string& name);
private: 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 // // Holds all the variables in the scope (excluding ones owned by the children //
std::unordered_map<std::string, Variable> m_LocalVariables; std::unordered_set<std::string> m_LocalVariables;
// Holds a section of the scope, for example the variables created in a while loop // // Holds a section of the scope, for example the variables created in a while loop //
std::unique_ptr<Scope> m_Child; std::unique_ptr<Scope> m_Child;

View File

@@ -15,6 +15,9 @@ namespace LX
llvm::LLVMContext context; llvm::LLVMContext context;
llvm::Module module; llvm::Module module;
llvm::IRBuilder<> builder; llvm::IRBuilder<> builder;
// Not LLVM I just cba to add this parameter to the functions that needed it //
Scope* scope;
}; };
} }

View File

@@ -6,7 +6,7 @@ namespace LX
{ {
// Default constructor that just initalises LLVM variables that it holds // // Default constructor that just initalises LLVM variables that it holds //
InfoLLVM::InfoLLVM(std::string name) 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) // // Reserves space for nodes (stops excess allocations) //
@@ -41,4 +41,9 @@ namespace LX::AST
ReturnStatement::ReturnStatement(std::unique_ptr<Node> val) ReturnStatement::ReturnStatement(std::unique_ptr<Node> val)
: Node(Node::RETURN_STATEMENT), m_Val(std::move(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)
{}
} }

View File

@@ -70,5 +70,10 @@ namespace LX::AST
// Function for generating LLVN IR (Intermediate representation) // // Function for generating LLVN IR (Intermediate representation) //
llvm::Value* VariableDeclaration::GenIR(InfoLLVM& LLVM) 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);
} }
} }

View File

@@ -40,4 +40,10 @@ namespace LX::AST
m_Val->Log(log, depth + 1); 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";
}
} }

View File

@@ -55,6 +55,7 @@ namespace LX
// Loops over the functions to generate their LLVM IR // // Loops over the functions to generate their LLVM IR //
for (auto& func : ast.functions) for (auto& func : ast.functions)
{ {
LLVM.scope = &func.scope; // Sets the current scope for the builder
GenerateFunctionIR(func, LLVM); GenerateFunctionIR(func, LLVM);
} }

View File

@@ -4,64 +4,32 @@
namespace LX namespace LX
{ {
// Util function for getting a pointer to an item from a map // bool Scope::DoesVarExist(const std::string& name)
static inline Scope::Variable* GetFromMap(const std::string& name, std::unordered_map<std::string, Scope::Variable>& map)
{ {
// Checks if it is in a map and if so returns it // // Stores a pointer to the current scope //
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 //
Scope* current = this; Scope* current = this;
// Loops over the scope and it's child to find the variable //
do do
{ {
// Gets the variable (if it exists) // // Checks if the variable exists in the current scope //
Variable* var = GetFromMap(name, current->m_LocalVariables); bool exists = current->m_LocalVariables.contains(name);
if (exists) { return true; }
// Returns the variable if it exists // // Travels to the next scope //
if (var != nullptr) { return var; }
// Assigns current to the child to recursively check //
// Doing it like this avoids recursive functions //
current = current->m_Child.get(); current = current->m_Child.get();
} while (current != nullptr); } while (current != nullptr);
// Else returns a nullptr and lets the caller handle the error checking // // If it gets here it means it couldnt find the variable so it doesnt exist in the current context //
return nullptr; return false;
} }
// Gets a variable from the scope by it's name // void Scope::CreateVar(const std::string& name)
Scope::Variable* Scope::GetVarOfName(const std::string& name)
{ {
// Gets the variable (if it exists) // // Checks variable of the same name doesn't exist //
Variable* var = GetVarOfNameImpl(name); ThrowIf<Scope::VariableAlreadyExists>(DoesVarExist(name));
// Throws an error if the variable doesn't exist // // Else inserts it into the local set //
ThrowIf<VariableDoesntExist>(var == nullptr); m_LocalVariables.insert(name);
// 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<VariableAlreadyExists>(alreadyExist != nullptr);
// Else creates the variable and returns it //
return &m_LocalVariables[name];
} }
} }

View File

@@ -1,4 +1,4 @@
func main() func main()
{ {
int a = 5 int a
} }