mirror of
https://github.com/PashaBibko/LX.git
synced 2026-04-03 17:39:02 +00:00
Now supports function calling
This commit is contained in:
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -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))
|
||||||
|
{}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,36 +30,52 @@ 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
|
||||||
|
|
||||||
std::cout << funcAST.params.size() << std::endl;
|
|
||||||
|
|
||||||
std::vector<llvm::Type*> funcParams(funcAST.params.size(), LLVM.builder.getInt32Ty());
|
|
||||||
|
|
||||||
llvm::FunctionType* retType = llvm::FunctionType::get(llvm::Type::getInt32Ty(LLVM.context), funcParams, false); // <- Defaults to int currently
|
|
||||||
llvm::Function* func = llvm::Function::Create(retType, GetLinkageType(funcAST.name), funcAST.name, LLVM.module);
|
|
||||||
llvm::BasicBlock* entry = llvm::BasicBlock::Create(LLVM.context, funcAST.name + "-entry", func);
|
|
||||||
LLVM.builder.SetInsertPoint(entry);
|
|
||||||
|
|
||||||
// Creates the storer of the variables/parameters //
|
|
||||||
|
|
||||||
FunctionScope funcScope(funcAST.params, func);
|
|
||||||
|
|
||||||
// Generates the IR within the function by looping over the nodes //
|
|
||||||
for (auto& node : funcAST.body)
|
|
||||||
{
|
{
|
||||||
ThrowIf<IRGenerationError>(IsValidTopLevelNode(node->m_Type) == false); // <- TODO: replace with actual error type
|
Log::LogNewSection("Generating ", funcAST.name, " LLVM IR");
|
||||||
node->GenIR(LLVM, funcScope);
|
|
||||||
|
// Creates the functions signature and return type //
|
||||||
|
|
||||||
|
std::vector<llvm::Type*> funcParams(funcAST.params.size(), LLVM.builder.getInt32Ty());
|
||||||
|
|
||||||
|
llvm::FunctionType* retType = llvm::FunctionType::get(llvm::Type::getInt32Ty(LLVM.context), funcParams, false); // <- Defaults to int currently
|
||||||
|
llvm::Function* func = llvm::Function::Create(retType, GetLinkageType(funcAST.name), funcAST.name, LLVM.module);
|
||||||
|
llvm::BasicBlock* entry = llvm::BasicBlock::Create(LLVM.context, funcAST.name + "-entry", func);
|
||||||
|
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 //
|
||||||
|
|
||||||
|
FunctionScope funcScope(funcAST.params, func);
|
||||||
|
|
||||||
|
// Generates the IR within the function by looping over the nodes //
|
||||||
|
for (auto& node : funcAST.body)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a terminator if there is none //
|
||||||
|
if (entry->getTerminator() == nullptr)
|
||||||
|
{
|
||||||
|
LLVM.builder.CreateRet(llvm::ConstantInt::get(llvm::Type::getInt32Ty(LLVM.context), 0, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifies the function works //
|
||||||
|
ThrowIf<IRGenerationError>(llvm::verifyFunction(*func, &llvm::errs())); // <- TODO: Make error type
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a terminator if there is none //
|
catch (...)
|
||||||
if (entry->getTerminator() == nullptr)
|
|
||||||
{
|
{
|
||||||
LLVM.builder.CreateRet(llvm::ConstantInt::get(llvm::Type::getInt32Ty(LLVM.context), 0, true));
|
__debugbreak();
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verifies the function works //
|
|
||||||
ThrowIf<IRGenerationError>(llvm::verifyFunction(*func)); // <- TODO: Make error type
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turns an abstract binary tree into LLVM intermediate representation //
|
// Turns an abstract binary tree into LLVM intermediate representation //
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user