Now supports function calling

This commit is contained in:
Pasha Bibko
2025-05-11 15:17:07 +01:00
parent c47889a4ff
commit 2a47fd1756
9 changed files with 206 additions and 44 deletions

View File

@@ -30,6 +30,7 @@ namespace LX::AST
NUMBER_LITERAL, NUMBER_LITERAL,
OPERATION, OPERATION,
FUNCTION_CALL,
// Variable manipulation // // Variable manipulation //
@@ -57,6 +58,9 @@ namespace LX::AST
// Function to log the node to a file // // Function to log the node to a file //
virtual void Log(unsigned depth) = 0; virtual void Log(unsigned depth) = 0;
// Function to get the node's type name //
virtual const char* TypeName() = 0;
// Function for generating C/C++ code (Currently not implemented) // // Function for generating C/C++ code (Currently not implemented) //
//virtual void GenC() = 0; //virtual void GenC() = 0;

View File

@@ -49,6 +49,7 @@ namespace LX_Build
{ {
// Quits if the IR Generation fails // // Quits if the IR Generation fails //
// The C++ script handles all of the error message outputting // // The C++ script handles all of the error message outputting //
Console.WriteLine("LX_API.GenIR threw an error");
return; return;
} }

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;
// All IR functions that have been generated //
std::unordered_map<std::string, llvm::Function*> functions;
}; };
} }
@@ -32,6 +35,9 @@ namespace LX::AST
// Function to log the node to a file, will throw an error if called on this class // // Function to log the node to a file, will throw an error if called on this class //
void Log(unsigned depth) override; void Log(unsigned depth) override;
// Function to get the node's type name, will throw an error if called on this class //
virtual const char* TypeName() override;
// The nodes that are contained within this node // // The nodes that are contained within this node //
std::vector<std::unique_ptr<Node>> nodes; std::vector<std::unique_ptr<Node>> nodes;
}; };
@@ -49,6 +55,9 @@ namespace LX::AST
// Function to log the node to a file // // Function to log the node to a file //
void Log(unsigned depth) override; void Log(unsigned depth) override;
// Function to get the node's type name //
const char* TypeName() override;
private: private:
// The number it stores // // The number it stores //
// Yes the number is stored as a string, It's horrible I know // // Yes the number is stored as a string, It's horrible I know //
@@ -68,6 +77,9 @@ namespace LX::AST
// Function to log the node to a file // // Function to log the node to a file //
void Log(unsigned depth) override; void Log(unsigned depth) override;
// Function to get the node's type name //
const char* TypeName() override;
private: private:
// The sides of the operation // // The sides of the operation //
// Unary operations are handled by a different class // // Unary operations are handled by a different class //
@@ -90,6 +102,9 @@ namespace LX::AST
// Function to log the node to a file // // Function to log the node to a file //
void Log(unsigned depth) override; void Log(unsigned depth) override;
// Function to get the node's type name //
const char* TypeName() override;
private: private:
// What it is returning (can be null) // // What it is returning (can be null) //
std::unique_ptr<Node> m_Val; std::unique_ptr<Node> m_Val;
@@ -108,6 +123,9 @@ namespace LX::AST
// Function to log the node to a file // // Function to log the node to a file //
void Log(unsigned depth) override; void Log(unsigned depth) override;
// Function to get the node's type name //
const char* TypeName() override;
private: private:
// Name of the variable // // Name of the variable //
std::string m_Name; std::string m_Name;
@@ -128,6 +146,9 @@ namespace LX::AST
// Function to log the node to a file // // Function to log the node to a file //
void Log(unsigned depth) override; void Log(unsigned depth) override;
// Function to get the node's type name //
const char* TypeName() override;
private: private:
// Name of the variable // // Name of the variable //
std::string m_Name; std::string m_Name;
@@ -149,8 +170,35 @@ namespace LX::AST
// Function to log the node to a file // // Function to log the node to a file //
void Log(unsigned depth) override; void Log(unsigned depth) override;
// Function to get the node's type name //
const char* TypeName() override;
private: private:
// The name of the variable // // The name of the variable //
std::string m_Name; std::string m_Name;
}; };
// Node to represent calling a function within the AST //
class FunctionCall : public Node
{
public:
// Constructor to set the name of the function and any args it may have //
FunctionCall(const std::string& funcName, std::vector<std::unique_ptr<Node>>& args);
// Function for generating LLVM IR (Intermediate representation) //
llvm::Value* GenIR(InfoLLVM& LLVM, FunctionScope& func) override;
// Function to log the niode to a file //
void Log(unsigned depth) override;
// Function to get the node's type name //
const char* TypeName() override;
private:
// The name of the function //
std::string m_Name;
// Any arguments to pass into the function //
std::vector<std::unique_ptr<Node>> m_Args;
};
} }

View File

@@ -63,4 +63,9 @@ namespace LX::AST
VariableAccess::VariableAccess(const std::string& name) VariableAccess::VariableAccess(const std::string& name)
: Node(Node::VARIABLE_ACCESS), m_Name(name) : Node(Node::VARIABLE_ACCESS), m_Name(name)
{} {}
// Passes constructor args to values and sets type //
FunctionCall::FunctionCall(const std::string& name, std::vector<std::unique_ptr<AST::Node>>& args)
: Node(Node::FUNCTION_CALL), m_Name(name), m_Args(std::move(args))
{}
} }

View File

@@ -113,4 +113,16 @@ namespace LX::AST
{ {
return func.AccessVar(m_Name, LLVM); return func.AccessVar(m_Name, LLVM);
} }
llvm::Value* FunctionCall::GenIR(InfoLLVM& LLVM, FunctionScope& func)
{
std::vector<llvm::Value*> evaluatedArgs;
for (std::unique_ptr<Node>& node : m_Args)
{
evaluatedArgs.push_back(node->GenIR(LLVM, func));
}
return LLVM.builder.CreateCall(LLVM.functions[m_Name], evaluatedArgs, "call_tmp");
}
} }

View File

@@ -9,11 +9,21 @@ namespace LX::AST
throw int(); // <- TODO: Make an error for this throw int(); // <- TODO: Make an error for this
} }
const char* MultiNode::TypeName()
{
throw int(); // <- TODO: Make an error for this
}
void NumberLiteral::Log(unsigned depth) void NumberLiteral::Log(unsigned depth)
{ {
Log::out<Log::Priority::HIGH>(std::string(depth, '\t'), "Number: ", m_Number); Log::out<Log::Priority::HIGH>(std::string(depth, '\t'), "Number: ", m_Number);
} }
const char* NumberLiteral::TypeName()
{
return "Number Literal";
}
void Operation::Log(unsigned depth) void Operation::Log(unsigned depth)
{ {
Log::out<Log::Priority::HIGH>(std::string(depth, '\t'), "Operation {", ToString(m_Operand), "}:"); Log::out<Log::Priority::HIGH>(std::string(depth, '\t'), "Operation {", ToString(m_Operand), "}:");
@@ -25,6 +35,11 @@ namespace LX::AST
m_Rhs->Log(depth + 2); m_Rhs->Log(depth + 2);
} }
const char* Operation::TypeName()
{
return "Operation";
}
void ReturnStatement::Log(unsigned depth) void ReturnStatement::Log(unsigned depth)
{ {
Log::out<Log::Priority::HIGH, Log::Format::NONE>(std::string(depth, '\t'), "Return"); Log::out<Log::Priority::HIGH, Log::Format::NONE>(std::string(depth, '\t'), "Return");
@@ -41,11 +56,21 @@ namespace LX::AST
} }
} }
const char* ReturnStatement::TypeName()
{
return "Return";
}
void VariableDeclaration::Log(unsigned depth) void VariableDeclaration::Log(unsigned depth)
{ {
Log::out<Log::Priority::HIGH>(std::string(depth, '\t'), "Variable declaration: ", m_Name); Log::out<Log::Priority::HIGH>(std::string(depth, '\t'), "Variable declaration: ", m_Name);
} }
const char* VariableDeclaration::TypeName()
{
return "Variable declaration";
}
void VariableAssignment::Log(unsigned depth) void VariableAssignment::Log(unsigned depth)
{ {
Log::out<Log::Priority::HIGH>(std::string(depth, '\t'), "Variable assignment:"); Log::out<Log::Priority::HIGH>(std::string(depth, '\t'), "Variable assignment:");
@@ -55,8 +80,35 @@ namespace LX::AST
m_Value->Log(depth + 2); m_Value->Log(depth + 2);
} }
const char* VariableAssignment::TypeName()
{
return "Variable assignment";
}
void VariableAccess::Log(unsigned depth) void VariableAccess::Log(unsigned depth)
{ {
Log::out<Log::Priority::HIGH>(std::string(depth, '\t'), "Variable: ", m_Name); Log::out<Log::Priority::HIGH>(std::string(depth, '\t'), "Variable: ", m_Name);
} }
const char* VariableAccess::TypeName()
{
return "Variable access";
}
void FunctionCall::Log(unsigned depth)
{
Log::out<Log::Priority::HIGH>(std::string(depth, '\t'), "Function call{", m_Name, "}:");
if (m_Args.size() != 0)
{
Log::out<Log::Priority::HIGH>(std::string(depth + 1, '\t'), "Args:");
for (auto& arg : m_Args) { arg->Log(depth + 2); }
}
}
const char* FunctionCall::TypeName()
{
return "Function call";
}
} }

View File

@@ -30,9 +30,11 @@ namespace LX
// Generates the LLVM IR for the given function // // Generates the LLVM IR for the given function //
static void GenerateFunctionIR(FunctionDefinition& funcAST, InfoLLVM& LLVM) static void GenerateFunctionIR(FunctionDefinition& funcAST, InfoLLVM& LLVM)
{ {
// Creates the functions signature and return type // try
{
Log::LogNewSection("Generating ", funcAST.name, " LLVM IR");
std::cout << funcAST.params.size() << std::endl; // Creates the functions signature and return type //
std::vector<llvm::Type*> funcParams(funcAST.params.size(), LLVM.builder.getInt32Ty()); std::vector<llvm::Type*> funcParams(funcAST.params.size(), LLVM.builder.getInt32Ty());
@@ -41,6 +43,10 @@ namespace LX
llvm::BasicBlock* entry = llvm::BasicBlock::Create(LLVM.context, funcAST.name + "-entry", func); llvm::BasicBlock* entry = llvm::BasicBlock::Create(LLVM.context, funcAST.name + "-entry", func);
LLVM.builder.SetInsertPoint(entry); LLVM.builder.SetInsertPoint(entry);
// Stores the function for other functions to call it //
LLVM.functions[funcAST.name] = func;
// Creates the storer of the variables/parameters // // Creates the storer of the variables/parameters //
FunctionScope funcScope(funcAST.params, func); FunctionScope funcScope(funcAST.params, func);
@@ -49,6 +55,9 @@ namespace LX
for (auto& node : funcAST.body) for (auto& node : funcAST.body)
{ {
ThrowIf<IRGenerationError>(IsValidTopLevelNode(node->m_Type) == false); // <- TODO: replace with actual error type ThrowIf<IRGenerationError>(IsValidTopLevelNode(node->m_Type) == false); // <- TODO: replace with actual error type
Log::out<Log::Priority::HIGH>("Generating: ", node->TypeName());
node->GenIR(LLVM, funcScope); node->GenIR(LLVM, funcScope);
} }
@@ -59,7 +68,14 @@ namespace LX
} }
// Verifies the function works // // Verifies the function works //
ThrowIf<IRGenerationError>(llvm::verifyFunction(*func)); // <- TODO: Make error type ThrowIf<IRGenerationError>(llvm::verifyFunction(*func, &llvm::errs())); // <- TODO: Make error type
}
catch (...)
{
__debugbreak();
throw;
}
} }
// Turns an abstract binary tree into LLVM intermediate representation // // Turns an abstract binary tree into LLVM intermediate representation //

View File

@@ -24,6 +24,36 @@ namespace LX
} }
} }
std::unique_ptr<AST::Node> ParseOperation(ParserInfo& p);
// Part of ParsePrimary //
static std::unique_ptr<AST::Node> ParseIdentifier(ParserInfo& p)
{
if (p.tokens[p.index + 1].type == Token::OPEN_PAREN)
{
std::string funcName = p.tokens[p.index].GetContents();
p.index = p.index + 2; // Skips over open paren and func name
std::vector<std::unique_ptr<AST::Node>> args;
while (true)
{
args.push_back(ParseOperation(p));
if (p.tokens[p.index].type == Token::CLOSE_PAREN)
{
p.index++;
return std::make_unique<AST::FunctionCall>(funcName, args);
}
ThrowIf<UnexpectedToken>(p.tokens[p.index].type != Token::COMMA, Token::COMMA, p);
p.index++;
}
}
return std::make_unique<AST::VariableAccess>(p.tokens[p.index++].GetContents());
}
// Base of the call stack to handle the simplest of tokens // // Base of the call stack to handle the simplest of tokens //
static std::unique_ptr<AST::Node> ParsePrimary(ParserInfo& p) static std::unique_ptr<AST::Node> ParsePrimary(ParserInfo& p)
{ {
@@ -37,20 +67,7 @@ namespace LX
// If an Identifier has got here it means a variable is being accessed // // If an Identifier has got here it means a variable is being accessed //
case Token::IDENTIFIER: case Token::IDENTIFIER:
return std::make_unique<AST::VariableAccess>(p.tokens[p.index++].GetContents()); return ParseIdentifier(p);
// TODO: Fix this //
case Token::OPEN_BRACKET:
p.scopeDepth++;
p.index++;
return nullptr;
// TODO: Fix this //
case Token::CLOSE_BRACE:
ThrowIf<UnexpectedToken>(p.scopeDepth == 0, Token::UNDEFINED, p.tokens[p.index], "need a different error", p);
p.scopeDepth--;
p.index++;
return nullptr;
// Returns nullptr, the parsing function that recives that value will decide if that is valid // // Returns nullptr, the parsing function that recives that value will decide if that is valid //
default: default:

View File

@@ -3,12 +3,19 @@ func add(int a, int b)
return a + b return a + b
} }
func sub(int c, int d)
{
return add(c, 0 - d)
}
func mul(int e, int f)
{
return e * f
}
func main() func main()
{ {
int a int g = mul(add(7, 5), sub(5, 3))
a = 5 + 2
int b = 6 + 1 return g
return a + b
} }