diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 2f88c59..70644f4 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -151,14 +151,14 @@ - + - + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 6bc7020..6c533df 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -14,7 +14,7 @@ Header Files - + Header Files @@ -34,7 +34,7 @@ Source Files - + Source Files diff --git a/Common/LX-Common.h b/Common/LX-Common.h index 586efce..ec37e47 100644 --- a/Common/LX-Common.h +++ b/Common/LX-Common.h @@ -65,7 +65,7 @@ // Includes the rest of common // -#include #include #include #include +#include diff --git a/Common/inc/Console.h b/Common/inc/IO.h similarity index 57% rename from Common/inc/Console.h rename to Common/inc/IO.h index ef93f52..7900e0f 100644 --- a/Common/inc/Console.h +++ b/Common/inc/IO.h @@ -23,6 +23,25 @@ namespace LX // Prints a string to std::cout with a certain color using Win32 API // extern "C" void COMMON_API PrintStringAsColor(const std::string& str, Color c); + inline std::string ReadFileToString(const std::filesystem::path& path, const std::string errorName = "input file path") + { + // Verifies the file path is valid // + ThrowIf(std::filesystem::exists(path) == false, errorName, path); + + // Opens the file // + std::ifstream file(path, std::ios::binary | std::ios::ate); // Opens in binary and at the end (microptimsation) + ThrowIf(file.is_open() == false, errorName, path); + + // Stores the length of the string and goes back to the beginning // + const std::streamsize len = file.tellg(); // tellg returns length because it was opened at the end + file.seekg(0, std::ios::beg); + + // Transfers the file contents to the output // + std::string contents(len, '\0'); // Allocates an empty string which is the size of the file + file.read(&contents[0], len); + return contents; + } + // Util function for getting a line of the source at a given index (used for errors) // inline std::string GetLineAtIndexOf(const std::string& src, const std::streamsize index) // <- Has to be inline because of C++ types { diff --git a/Common/src/Console.cpp b/Common/src/IO.cpp similarity index 100% rename from Common/src/Console.cpp rename to Common/src/IO.cpp diff --git a/IR-Generator/inc/Parser.h b/IR-Generator/inc/Parser.h index e816d4d..9f51097 100644 --- a/IR-Generator/inc/Parser.h +++ b/IR-Generator/inc/Parser.h @@ -71,11 +71,10 @@ namespace LX { GENERATE_LX_ERROR_REQUIRED_FUNCTION_DECLARATIONS; - UnexpectedToken(Token::TokenType _expected, std::string _override, Token _got); + UnexpectedToken(Token::TokenType _expected, std::string _override, Token _got, const std::filesystem::path& _file); // - static std::string* s_Source; - static std::filesystem::path* s_SourceFile; + const std::filesystem::path file; // The token type that should be there // Token::TokenType expected; @@ -143,7 +142,7 @@ namespace LX }; // Turns the tokens of a file into it's abstract syntax tree equivalent // - FileAST TurnTokensIntoAbstractSyntaxTree(std::vector& tokens); + FileAST TurnTokensIntoAbstractSyntaxTree(std::vector& tokens, const std::filesystem::path& path); // Turns an abstract binary tree into LLVM intermediate representation // void GenerateIR(FileAST& ast, const std::string& name, const std::filesystem::path& IRPath); diff --git a/IR-Generator/src/Generator.cpp b/IR-Generator/src/Generator.cpp index 960f8f0..a967f2c 100644 --- a/IR-Generator/src/Generator.cpp +++ b/IR-Generator/src/Generator.cpp @@ -29,7 +29,7 @@ extern "C" int __declspec(dllexport) GenIR(const char* a_inpPath, const char* a_ std::vectortokens = LX::LexicalAnalyze(inpPath); // Turns the tokens into an AST // - LX::FileAST AST = LX::TurnTokensIntoAbstractSyntaxTree(tokens); + LX::FileAST AST = LX::TurnTokensIntoAbstractSyntaxTree(tokens, inpPath); // Turns the AST into LLVM IR // LX::GenerateIR(AST, inpPath.filename().string(), outPath); @@ -72,7 +72,7 @@ extern "C" int __declspec(dllexport) GenIR(const char* a_inpPath, const char* a_ std::cout << "An error occured. Please report this on the github page.\n" << std::endl; std::cout << e.what() << std::endl; - // Exit code -1 means an undefined error // + // Exit code -1 means an undefined error // But this isn't undefined and neither is LX::RuntimeError? return -1; } diff --git a/Lexer/src/Lexer.cpp b/Lexer/src/Lexer.cpp index 2a89c66..2b724e3 100644 --- a/Lexer/src/Lexer.cpp +++ b/Lexer/src/Lexer.cpp @@ -175,25 +175,6 @@ namespace LX } } - static std::string ReadFileToString(const std::filesystem::path& path) - { - // Verifies the file path is valid // - ThrowIf(std::filesystem::exists(path) == false, "input file path", path); - - // Opens the file // - std::ifstream file(path, std::ios::binary | std::ios::ate); // Opens in binary and at the end (microptimsation) - ThrowIf(file.is_open() == false, "input file path", path); - - // Stores the length of the string and goes back to the beginning // - const std::streamsize len = file.tellg(); // tellg returns length because it was opened at the end - file.seekg(0, std::ios::beg); - - // Transfers the file contents to the output // - std::string contents(len, '\0'); // Allocates an empty string which is the size of the file - file.read(&contents[0], len); - return contents; - } - const std::vector LX::LexicalAnalyze(const std::filesystem::path& path) { // Logs that the file is being read // diff --git a/Parser/src/Parser.cpp b/Parser/src/Parser.cpp index fbd9fbf..c7ce21b 100644 --- a/Parser/src/Parser.cpp +++ b/Parser/src/Parser.cpp @@ -6,9 +6,6 @@ namespace LX { - std::string* UnexpectedToken::s_Source = nullptr; - std::filesystem::path* UnexpectedToken::s_SourceFile = nullptr; - // Util function for working out if a token is a two sided operator // static bool IsTwoSidedOperator(Token::TokenType t) { @@ -29,10 +26,13 @@ namespace LX struct Parser { // Passes constructor args to members // - Parser(std::vector& _tokens) - : tokens(_tokens), index(0), len(_tokens.size()), scopeDepth(0) + Parser(std::vector& _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& tokens; @@ -69,7 +69,7 @@ namespace LX // TODO: Fix this // case Token::CLOSE_BRACE: - ThrowIf(p.scopeDepth == 0, Token::UNDEFINED, "need a different error", p.tokens[p.index]); + ThrowIf(p.scopeDepth == 0, Token::UNDEFINED, "need a different error", p.tokens[p.index], p.file); p.scopeDepth--; p.index++; return nullptr; @@ -92,7 +92,7 @@ namespace LX { // Parses the left hand side of the operation // std::unique_ptr lhs = ParsePrimary(p); - ThrowIf(lhs == nullptr, Token::UNDEFINED, "value", p.tokens[p.index - 1]); + ThrowIf(lhs == nullptr, Token::UNDEFINED, "value", p.tokens[p.index - 1], p.file); // Stores the operator to pass into the AST node // Token::TokenType op = p.tokens[p.index].type; @@ -100,7 +100,7 @@ namespace LX // Parses the right hand of the operation // std::unique_ptr rhs = ParseOperation(p); - ThrowIf(rhs == nullptr, Token::UNDEFINED, "value", p.tokens[p.index - 1]); + ThrowIf(rhs == nullptr, Token::UNDEFINED, "value", p.tokens[p.index - 1], p.file); // Returns an AST node as all of the components combined together // return std::make_unique(std::move(lhs), op, std::move(rhs)); @@ -137,7 +137,7 @@ namespace LX p.index++; // Checks for the variable name // - ThrowIf(p.tokens[p.index].type != Token::IDENTIFIER, Token::IDENTIFIER, "", p.tokens[p.index]); + ThrowIf(p.tokens[p.index].type != Token::IDENTIFIER, Token::IDENTIFIER, "", p.tokens[p.index], p.file); std::string name = p.tokens[p.index].GetContents(); p.index++; // <- Goes over the identifier token @@ -152,7 +152,7 @@ namespace LX // Gets the value to be assigned to the variable // std::unique_ptr defaultVal = ParsePrimary(p); - ThrowIf(defaultVal.get() == nullptr, Token::UNDEFINED, "value", p.tokens[p.index - 1]); + ThrowIf(defaultVal.get() == nullptr, Token::UNDEFINED, "value", p.tokens[p.index - 1], p.file); return std::make_unique(name); } @@ -170,7 +170,7 @@ namespace LX if (p.tokens[p.index + 1].type == Token::ASSIGN) { // Gets the variable that is being assigned too // - ThrowIf(p.tokens[p.index].type != Token::IDENTIFIER, Token::IDENTIFIER, "", p.tokens[p.index]); + ThrowIf(p.tokens[p.index].type != Token::IDENTIFIER, Token::IDENTIFIER, "", p.tokens[p.index], p.file); std::string name = p.tokens[p.index].GetContents(); // Skips over the assign token and name of the variable // @@ -195,19 +195,19 @@ namespace LX std::unique_ptr out = ParseVarAssignment(p); // Checks it is valid before returning // - ThrowIf(out == nullptr, Token::UNDEFINED, "top level statement", p.tokens[p.index - 1]); + ThrowIf(out == nullptr, Token::UNDEFINED, "top level statement", p.tokens[p.index - 1], p.file); return out; } // Turns the tokens of a file into it's abstract syntax tree equivalent // - FileAST TurnTokensIntoAbstractSyntaxTree(std::vector& tokens) + FileAST TurnTokensIntoAbstractSyntaxTree(std::vector& tokens, const std::filesystem::path& path) { // Logs the start of the parsing Log::LogNewSection("Started parsing tokens"); // Creates the output storer and the parser // FileAST output; - Parser p(tokens); + Parser 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 +225,11 @@ namespace LX FunctionDefinition& func = output.functions.back(); // Assigns the function name // - ThrowIf(p.tokens[p.index].type != Token::IDENTIFIER, Token::IDENTIFIER, "", p.tokens[p.index]); + ThrowIf(p.tokens[p.index].type != Token::IDENTIFIER, Token::IDENTIFIER, "", p.tokens[p.index], p.file); func.name = p.tokens[p.index++].GetContents(); // Checks for opening paren '(' // - ThrowIf(p.tokens[p.index].type != Token::OPEN_PAREN, Token::OPEN_PAREN, "", p.tokens[p.index]); + ThrowIf(p.tokens[p.index].type != Token::OPEN_PAREN, Token::OPEN_PAREN, "", p.tokens[p.index], p.file); p.index++; // Loops over all the arguments of the function // @@ -243,7 +243,7 @@ namespace LX p.index++; // Checks for opening bracket '{' // - ThrowIf(p.tokens[p.index].type != Token::OPEN_BRACKET, Token::OPEN_BRACKET, "", p.tokens[p.index]); + ThrowIf(p.tokens[p.index].type != Token::OPEN_BRACKET, Token::OPEN_BRACKET, "", p.tokens[p.index], p.file); p.index++; // Loops over the body until it reaches the end // diff --git a/Parser/src/ParserErrors.cpp b/Parser/src/ParserErrors.cpp index 54c37e1..1bde6a0 100644 --- a/Parser/src/ParserErrors.cpp +++ b/Parser/src/ParserErrors.cpp @@ -13,8 +13,8 @@ namespace LX return "IR Generation Error"; } - UnexpectedToken::UnexpectedToken(Token::TokenType _expected, std::string _override, Token _got) - : expected(_expected), override(_override), got(_got) + UnexpectedToken::UnexpectedToken(Token::TokenType _expected, std::string _override, Token _got, const std::filesystem::path& _file) + : expected(_expected), override(_override), got(_got), file(_file) {} void UnexpectedToken::PrintToConsole() const @@ -25,13 +25,16 @@ namespace LX size_t lineNumberWidthInConsole = std::max(oss.str().size(), (size_t)3); // Gets the line of the error // - std::string line = LX::GetLineAtIndexOf(*s_Source, got.index); + // As the file has been closed and the source has been deleted it needs to be reopened // + + std::string fileContents = ReadFileToString(file); + std::string line = LX::GetLineAtIndexOf(fileContents, got.index); // Prints the error to the console with the relevant info // std::cout << "\n"; LX::PrintStringAsColor("Error: ", LX::Color::LIGHT_RED); std::cout << "Incorrect syntax in "; - LX::PrintStringAsColor(s_SourceFile->filename().string(), LX::Color::WHITE); + LX::PrintStringAsColor(file.string(), LX::Color::WHITE); std::cout << ", found "; LX::PrintStringAsColor(LX::ToString(got.type).c_str(), LX::Color::WHITE); std::cout << " expected: ";