Tided up Parser project

This commit is contained in:
Pasha Bibko
2025-05-07 20:33:39 +01:00
parent c472cb5fc5
commit 4509250c4e
12 changed files with 142 additions and 95 deletions

View File

@@ -2,7 +2,7 @@
#include <LX-Common.h>
// Lexer foward declares fstream components so we can use them here //
// Includes Lexer so it can use LX::Token //
#include <Lexer.h>
// Foward declares the wrapper around the LLVM objects we need to pass around //
@@ -63,39 +63,9 @@ namespace LX::AST
namespace LX
{
// Thrown if there was an error during IR Generation //
CREATE_EMPTY_LX_ERROR_TYPE(IRGenerationError);
// Thrown if there was an unexpected (incorrect) token //
struct UnexpectedToken : public RuntimeError
{
GENERATE_LX_ERROR_REQUIRED_FUNCTION_DECLARATIONS;
UnexpectedToken(Token::TokenType _expected, std::string _override, Token _got, const std::filesystem::path& _file);
//
const std::filesystem::path file;
// The token type that should be there //
Token::TokenType expected;
// If there are multiple expected types there is an option for a custom message //
std::string override;
// What token was actually at that position //
// Stored as Token not TokenType to store the location of it within the source //
Token got;
};
class Scope
{
public:
// Error thrown if the user tried to create a variable that already existed //
CREATE_EMPTY_LX_ERROR_TYPE(VariableAlreadyExists);
// Error thrown if user tries to access variable that does not exist //
CREATE_EMPTY_LX_ERROR_TYPE(VariableDoesntExist);
// Default constructor //
Scope()
: m_LocalVariables{}, m_Child(nullptr)

View File

@@ -147,9 +147,9 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="src\AST-Constructors.cpp" />
<ClCompile Include="src\AST-LLVM.cpp" />
<ClCompile Include="src\AST-Loggers.cpp" />
<ClCompile Include="src\AST\AST-Constructors.cpp" />
<ClCompile Include="src\AST\AST-LLVM.cpp" />
<ClCompile Include="src\AST\AST-Loggers.cpp" />
<ClCompile Include="src\GenIR.cpp" />
<ClCompile Include="src\Parser.cpp" />
<ClCompile Include="src\ParserErrors.cpp" />
@@ -157,6 +157,8 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="inc\AST.h" />
<ClInclude Include="inc\ParserErrors.h" />
<ClInclude Include="inc\ParserInfo.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View File

@@ -9,23 +9,17 @@
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Source Files\AST">
<UniqueIdentifier>{344a1f33-e6b1-4bf7-b3b4-ec5b8c726d40}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\Parser.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\AST-Constructors.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\AST-LLVM.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\GenIR.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\AST-Loggers.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Scope.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -35,10 +29,25 @@
<ClCompile Include="inc\pch.cpp">
<Filter>Header Files</Filter>
</ClCompile>
<ClCompile Include="src\AST\AST-Constructors.cpp">
<Filter>Source Files\AST</Filter>
</ClCompile>
<ClCompile Include="src\AST\AST-LLVM.cpp">
<Filter>Source Files\AST</Filter>
</ClCompile>
<ClCompile Include="src\AST\AST-Loggers.cpp">
<Filter>Source Files\AST</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="inc\AST.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="inc\ParserErrors.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="inc\ParserInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

43
Parser/inc/ParserErrors.h Normal file
View File

@@ -0,0 +1,43 @@
#pragma once
#include <LX-Common.h>
#include <ParserInfo.h>
#include <Lexer.h>
namespace LX
{
// Thrown if there was an error during IR Generation //
CREATE_EMPTY_LX_ERROR_TYPE(IRGenerationError);
// Error thrown if the user tried to create a variable that already existed //
CREATE_EMPTY_LX_ERROR_TYPE(VariableAlreadyExists);
// Error thrown if user tries to access variable that does not exist //
CREATE_EMPTY_LX_ERROR_TYPE(VariableDoesntExist);
// Thrown if there was an unexpected (incorrect) token //
struct UnexpectedToken : public RuntimeError
{
GENERATE_LX_ERROR_REQUIRED_FUNCTION_DECLARATIONS;
// Constructor to set the members of the error //
UnexpectedToken(Token::TokenType _expected, const ParserInfo& p);
// Constructor for custom messages in the cmd //
UnexpectedToken(Token::TokenType _expected, Token _got, const std::string& message, const ParserInfo& p);
// The file that the tokens come from //
const std::filesystem::path file;
// The token type that should be there //
const Token::TokenType expected;
// If there are multiple expected types there is an option for a custom message //
const std::string custom;
// What token was actually at that position //
// Stored as Token not TokenType to store the location of it within the source //
const Token got;
};
}

33
Parser/inc/ParserInfo.h Normal file
View File

@@ -0,0 +1,33 @@
#pragma once
#include <LX-Common.h>
// Includes Lexer so it can use LX::Token //
#include <Lexer.h>
namespace LX
{
// Local struct so everything can be public //
struct ParserInfo
{
// Passes constructor args to members //
ParserInfo(const std::vector<Token>& _tokens, const std::filesystem::path& path)
: tokens(_tokens), index(0), len(_tokens.size()), scopeDepth(0), file(path)
{}
// The file that the tokens were generated from //
const std::filesystem::path file;
// Tokens created by the lexer //
const std::vector<Token>& tokens;
// Length of the the token vector //
const size_t len;
// Current index within the token vector //
size_t index;
// Current scope depth //
size_t scopeDepth;
};
}

View File

@@ -2,6 +2,7 @@
#include <Parser.h>
#include <ParserErrors.h>
#include <AST.h>
namespace LX::AST
@@ -108,7 +109,7 @@ namespace LX::AST
{
// Gets the variable from the current scope //
llvm::AllocaInst* asignee = LLVM.scope->GetVar(m_Name);
ThrowIf<Scope::VariableDoesntExist>(asignee == nullptr);
ThrowIf<VariableDoesntExist>(asignee == nullptr);
// Creates the assignment //
return LLVM.builder.CreateStore(m_Value->GenIR(LLVM), asignee);
@@ -118,7 +119,7 @@ namespace LX::AST
{
// Loads the variable from the current scope //
llvm::AllocaInst* var = LLVM.scope->GetVar(m_Name);
ThrowIf<Scope::VariableDoesntExist>(var == nullptr);
ThrowIf<VariableDoesntExist>(var == nullptr);
// Creates the load within the AST //
return LLVM.builder.CreateLoad(llvm::Type::getInt32Ty(LLVM.context), var, m_Name + "_val");

View File

@@ -2,6 +2,7 @@
#include <Parser.h>
#include <ParserErrors.h>
#include <AST.h>
namespace LX

View File

@@ -2,6 +2,8 @@
#include <Parser.h>
#include <ParserErrors.h>
#include <ParserInfo.h>
#include <AST.h>
namespace LX
@@ -22,32 +24,8 @@ namespace LX
}
}
// Local struct so everything can be public //
struct Parser
{
// Passes constructor args to members //
Parser(std::vector<Token>& _tokens, const std::filesystem::path& path)
: tokens(_tokens), index(0), len(_tokens.size()), scopeDepth(0), file(path)
{}
// The file that the tokens were generated from //
const std::filesystem::path file;
// Tokens created by the lexer //
std::vector<Token>& tokens;
// Length of the the token vector //
const size_t len;
// Current index within the token vector //
size_t index;
// Current scope depth //
size_t scopeDepth;
};
// Base of the call stack to handle the simplest of tokens //
static std::unique_ptr<AST::Node> ParsePrimary(Parser& p)
static std::unique_ptr<AST::Node> ParsePrimary(ParserInfo& p)
{
// There are lots of possible token's that can be here so a switch is used //
switch (p.tokens[p.index].type)
@@ -69,7 +47,7 @@ namespace LX
// TODO: Fix this //
case Token::CLOSE_BRACE:
ThrowIf<UnexpectedToken>(p.scopeDepth == 0, Token::UNDEFINED, "need a different error", p.tokens[p.index], p.file);
ThrowIf<UnexpectedToken>(p.scopeDepth == 0, Token::UNDEFINED, p.tokens[p.index], "need a different error", p);
p.scopeDepth--;
p.index++;
return nullptr;
@@ -82,7 +60,7 @@ namespace LX
}
// Handles operations, if it is not currently at an operation goes to ParsePrimary //
static std::unique_ptr<AST::Node> ParseOperation(Parser& p)
static std::unique_ptr<AST::Node> ParseOperation(ParserInfo& p)
{
// Checks if the next token is an operator //
// TODO: Add more than just add //
@@ -92,7 +70,7 @@ namespace LX
{
// Parses the left hand side of the operation //
std::unique_ptr<AST::Node> lhs = ParsePrimary(p);
ThrowIf<UnexpectedToken>(lhs == nullptr, Token::UNDEFINED, "value", p.tokens[p.index - 1], p.file);
ThrowIf<UnexpectedToken>(lhs == nullptr, Token::UNDEFINED, p.tokens[p.index - 1], "value", p);
// Stores the operator to pass into the AST node //
Token::TokenType op = p.tokens[p.index].type;
@@ -100,7 +78,7 @@ namespace LX
// Parses the right hand of the operation //
std::unique_ptr<AST::Node> rhs = ParseOperation(p);
ThrowIf<UnexpectedToken>(rhs == nullptr, Token::UNDEFINED, "value", p.tokens[p.index - 1], p.file);
ThrowIf<UnexpectedToken>(rhs == nullptr, Token::UNDEFINED, p.tokens[p.index - 1], "value", p);
// Returns an AST node as all of the components combined together //
return std::make_unique<AST::Operation>(std::move(lhs), op, std::move(rhs));
@@ -112,7 +90,7 @@ namespace LX
}
// Handles return statements, if not calls ParseOperation //
static std::unique_ptr<AST::Node> ParseReturn(Parser& p)
static std::unique_ptr<AST::Node> ParseReturn(ParserInfo& p)
{
// Checks if the current token is a return //
if (p.tokens[p.index].type == Token::RETURN)
@@ -128,7 +106,7 @@ namespace LX
}
// Handles variable declarations, if not calls ParseReturn //
static std::unique_ptr<AST::Node> ParseVarDeclaration(Parser& p)
static std::unique_ptr<AST::Node> ParseVarDeclaration(ParserInfo& p)
{
// Checks if the current token is a declaration //
if (p.tokens[p.index].type == Token::INT_DEC)
@@ -137,7 +115,7 @@ namespace LX
p.index++;
// Checks for the variable name //
ThrowIf<UnexpectedToken>(p.tokens[p.index].type != Token::IDENTIFIER, Token::IDENTIFIER, "", p.tokens[p.index], p.file);
ThrowIf<UnexpectedToken>(p.tokens[p.index].type != Token::IDENTIFIER, Token::IDENTIFIER, p);
std::string name = p.tokens[p.index].GetContents();
p.index++; // <- Goes over the identifier token
@@ -152,7 +130,7 @@ namespace LX
// Gets the value to be assigned to the variable //
std::unique_ptr<AST::Node> defaultVal = ParsePrimary(p);
ThrowIf<UnexpectedToken>(defaultVal.get() == nullptr, Token::UNDEFINED, "value", p.tokens[p.index - 1], p.file);
ThrowIf<UnexpectedToken>(defaultVal.get() == nullptr, Token::UNDEFINED, p.tokens[p.index - 1], "value", p);
return std::make_unique<AST::VariableDeclaration>(name);
}
@@ -162,7 +140,7 @@ namespace LX
}
// Handles variable assignments, if not calls ParseVarDeclaration //
static std::unique_ptr<AST::Node> ParseVarAssignment(Parser& p)
static std::unique_ptr<AST::Node> ParseVarAssignment(ParserInfo& p)
{
// Checks if the next token is an equals //
if (p.index + 1 < p.len) [[likely]]
@@ -170,7 +148,7 @@ namespace LX
if (p.tokens[p.index + 1].type == Token::ASSIGN)
{
// Gets the variable that is being assigned too //
ThrowIf<UnexpectedToken>(p.tokens[p.index].type != Token::IDENTIFIER, Token::IDENTIFIER, "", p.tokens[p.index], p.file);
ThrowIf<UnexpectedToken>(p.tokens[p.index].type != Token::IDENTIFIER, Token::IDENTIFIER, p);
std::string name = p.tokens[p.index].GetContents();
// Skips over the assign token and name of the variable //
@@ -189,13 +167,13 @@ namespace LX
}
// Helper function to call the top of the Parse-Call-Stack //
static inline std::unique_ptr<AST::Node> Parse(Parser& p)
static inline std::unique_ptr<AST::Node> Parse(ParserInfo& p)
{
// Parses the current token //
std::unique_ptr<AST::Node> out = ParseVarAssignment(p);
// Checks it is valid before returning //
ThrowIf<UnexpectedToken>(out == nullptr, Token::UNDEFINED, "top level statement", p.tokens[p.index - 1], p.file);
ThrowIf<UnexpectedToken>(out == nullptr, Token::UNDEFINED, p.tokens[p.index - 1], "top level statement", p);
return out;
}
@@ -207,7 +185,7 @@ namespace LX
// Creates the output storer and the parser //
FileAST output;
Parser p(tokens, path);
ParserInfo p(tokens, path);
// Loops over the tokens and calls the correct parsing function //
// Which depends on their type and current state of the parser //
@@ -225,11 +203,11 @@ namespace LX
FunctionDefinition& func = output.functions.back();
// Assigns the function name //
ThrowIf<UnexpectedToken>(p.tokens[p.index].type != Token::IDENTIFIER, Token::IDENTIFIER, "", p.tokens[p.index], p.file);
ThrowIf<UnexpectedToken>(p.tokens[p.index].type != Token::IDENTIFIER, Token::IDENTIFIER, p);
func.name = p.tokens[p.index++].GetContents();
// Checks for opening paren '(' //
ThrowIf<UnexpectedToken>(p.tokens[p.index].type != Token::OPEN_PAREN, Token::OPEN_PAREN, "", p.tokens[p.index], p.file);
ThrowIf<UnexpectedToken>(p.tokens[p.index].type != Token::OPEN_PAREN, Token::OPEN_PAREN, p);
p.index++;
// Loops over all the arguments of the function //
@@ -243,7 +221,7 @@ namespace LX
p.index++;
// Checks for opening bracket '{' //
ThrowIf<UnexpectedToken>(p.tokens[p.index].type != Token::OPEN_BRACKET, Token::OPEN_BRACKET, "", p.tokens[p.index], p.file);
ThrowIf<UnexpectedToken>(p.tokens[p.index].type != Token::OPEN_BRACKET, Token::OPEN_BRACKET, p);
p.index++;
// Loops over the body until it reaches the end //

View File

@@ -1,6 +1,9 @@
#include <LX-Common.h>
#include <Parser.h>
#include <ParserErrors.h>
#include <ParserInfo.h>
#include <Lexer.h>
namespace LX
{
@@ -13,8 +16,14 @@ namespace LX
return "IR Generation Error";
}
UnexpectedToken::UnexpectedToken(Token::TokenType _expected, std::string _override, Token _got, const std::filesystem::path& _file)
: expected(_expected), override(_override), got(_got), file(_file)
// Constructor to set the members of the error //
UnexpectedToken::UnexpectedToken(Token::TokenType _expected, const ParserInfo& p)
: file(p.file), expected(Token::UNDEFINED), custom(""), got(p.tokens[p.index])
{}
// Constructor for custom messages in the cmd //
UnexpectedToken::UnexpectedToken(Token::TokenType _expected, Token _got, const std::string& message, const ParserInfo& p)
: file(p.file), expected(_expected), custom(message), got(_got)
{}
void UnexpectedToken::PrintToConsole() const
@@ -40,7 +49,7 @@ namespace LX
std::cout << " expected: ";
// Allows the error to have a custom type that is printed to the console //
if (expected == LX::Token::UNDEFINED) { PrintAsColor<Color::WHITE>(override.c_str()); }
if (expected == LX::Token::UNDEFINED) { PrintAsColor<Color::WHITE>(custom); }
else { PrintAsColor<Color::WHITE>(ToString(expected).c_str()); }
std::cout << "\n";
@@ -57,20 +66,20 @@ namespace LX
return "Unexpected Token";
}
void Scope::VariableAlreadyExists::PrintToConsole() const
void VariableAlreadyExists::PrintToConsole() const
{
}
const char* Scope::VariableAlreadyExists::ErrorType() const
const char* VariableAlreadyExists::ErrorType() const
{
return "Variable Already Exists";
}
void Scope::VariableDoesntExist::PrintToConsole() const
void VariableDoesntExist::PrintToConsole() const
{
}
const char* Scope::VariableDoesntExist::ErrorType() const
const char* VariableDoesntExist::ErrorType() const
{
return "Variable Doesn't exist";
}

View File

@@ -2,6 +2,7 @@
#include <Parser.h>
#include <ParserErrors.h>
#include <AST.h>
namespace LX
@@ -29,7 +30,7 @@ namespace LX
llvm::AllocaInst* Scope::CreateVar(const std::string& name, InfoLLVM& LLVM)
{
// Checks variable of the same name doesn't exist //
ThrowIf<Scope::VariableAlreadyExists>(GetVar(name) != nullptr);
ThrowIf<VariableAlreadyExists>(GetVar(name) != nullptr);
// Else inserts it into the local set //
m_LocalVariables[name] = LLVM.builder.CreateAlloca(LLVM.builder.getInt32Ty(), nullptr, name);