diff --git a/.vscode/settings.json b/.vscode/settings.json index f33d6be..ee3f40c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,72 @@ { "files.associations": { "*.tcc": "cpp", - "istream": "cpp" + "istream": "cpp", + "any": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "map": "cpp", + "set": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "format": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "semaphore": "cpp", + "shared_mutex": "cpp", + "span": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "text_encoding": "cpp", + "thread": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp", + "variant": "cpp" } } \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b4300d2..dab7bd8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,7 +7,11 @@ ADD_LIBRARY(hoocore STATIC Visitor.cpp Compiler.cpp Compiler.hpp - Node.hpp) + Node.hpp + ParseError.hpp + ParseErrorHandler.hpp + ParseErrorHandler.cpp + ParseErrorException.hpp) ADD_EXECUTABLE(hoo Hoo.cpp Visitor.cpp ${ANTLR_GENERATED_DIR}/HooBaseVisitor.cpp diff --git a/src/Compiler.cpp b/src/Compiler.cpp index 8b38fb2..796614d 100644 --- a/src/Compiler.cpp +++ b/src/Compiler.cpp @@ -1,12 +1,18 @@ #include "Compiler.hpp" +#include "ParseErrorException.hpp" +#include "ParseError.hpp" -Compiler::Compiler(const std::string &input, const std::string &moduleName) : _input(input), _moduleName(moduleName) +#include + +Compiler::Compiler(const std::string &input, + const std::string &moduleName) : _input(input), _moduleName(moduleName) { _input_stream = new antlr4::ANTLRInputStream(_input); _lexer = new HooLexer(_input_stream); _tokens = new antlr4::CommonTokenStream(_lexer); _parser = new HooParser(_tokens); _visitor = new Visitor(_moduleName); + _parser->setErrorHandler(std::make_shared()); } Compiler::~Compiler() @@ -22,14 +28,30 @@ std::any Compiler::compile() { try { + auto error_handler = std::dynamic_pointer_cast(_parser->getErrorHandler()); antlr4::tree::ParseTree *tree = parse(); if (tree == nullptr) { - std::cout << "Parse failed" << std::endl; + std::cerr << "Parse failed" << std::endl; } auto result = _visitor->visit(tree); + auto parseErrors = error_handler->getErrors(); + if (parseErrors.size() > 0) + { + throw ParseCollectiveErrorException(parseErrors); + } return result; } + catch (const ParseCollectiveErrorException &e) + { + std::cerr << e.getMessage() << std::endl; + throw; + } + catch (const ParseErrorException &e) + { + std::cerr << e.what() << std::endl; + throw; + } catch (const std::exception &e) { std::cerr << "Compilation Error: " << e.what() << std::endl; diff --git a/src/Compiler.hpp b/src/Compiler.hpp index ae6516c..266e92c 100644 --- a/src/Compiler.hpp +++ b/src/Compiler.hpp @@ -4,8 +4,10 @@ #include "HooLexer.h" #include "HooParser.h" #include "Visitor.hpp" +#include "ParseErrorHandler.hpp" #include +#include class Compiler { diff --git a/src/Node.hpp b/src/Node.hpp index 32e8400..ff987cc 100644 --- a/src/Node.hpp +++ b/src/Node.hpp @@ -26,8 +26,14 @@ private: llvm::Value *_value; public: - Node(NodeType nodeType, DataType dataType, llvm::Value *value) : _nodeType(nodeType), _dataType(dataType), _value(value) {} - Node(const Node &other) : _nodeType(other._nodeType), _dataType(other._dataType), _value(other._value) {} + Node(NodeType nodeType, DataType dataType, llvm::Value *value) + : _nodeType(nodeType), + _dataType(dataType), + _value(value) {} + Node(const Node &other) + : _nodeType(other._nodeType), + _dataType(other._dataType), + _value(other._value) {} NodeType getNodeType() const { return _nodeType; } DataType getDataType() const { return _dataType; } diff --git a/src/ParseError.hpp b/src/ParseError.hpp new file mode 100644 index 0000000..5220d9c --- /dev/null +++ b/src/ParseError.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +class ParseError +{ +private: + size_t line_no; + size_t char_pos; + std::string message; + +public: + ParseError(size_t line, size_t pos, const std::string &msg) + : line_no(line), char_pos(pos), message(msg) {} + ParseError(const ParseError &other) : ParseError(other.line_no, other.char_pos, other.message) {} + + size_t getLineNo() const { return line_no; } + size_t getCharPos() const { return char_pos; } + std::string getMessage() const { return message; } +}; diff --git a/src/ParseErrorException.hpp b/src/ParseErrorException.hpp new file mode 100644 index 0000000..f577137 --- /dev/null +++ b/src/ParseErrorException.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include "ParseErrorException.hpp" +#include "ParseError.hpp" + +#include +#include +#include + +class ParseErrorException : public std::exception +{ + std::string _message; + size_t _line_number; + size_t _character_position; + std::string _module_name; + +public: + ParseErrorException(const std::string &module_name, + size_t line_number, + size_t character_position, + const std::string &message) + : _message(message), + _line_number(line_number), + _character_position(character_position), + _module_name(module_name) + { + } + + size_t getLineNumber() const { return _line_number; } + size_t getCharacterPosition() const { return _character_position; } + std::string getModuleName() const { return _module_name; } + + ParseErrorException(const ParseErrorException &other) : std::exception(other), + _module_name(other._module_name), + _line_number(other._line_number), + _character_position(other._character_position) {} + + const char *what() const noexcept override { return _message.c_str(); } +}; + +class ParseCollectiveErrorException : public std::exception +{ + std::vector _errors; + +public: + ParseCollectiveErrorException(const std::vector &errors) + : _errors(errors) + { + } + + const std::vector &getErrors() const { return _errors; } + std::string getMessage() const + { + std::string result; + for (const auto &error : _errors) + { + result += error.getMessage() + "\n"; + } + return result; + } +}; diff --git a/src/ParseErrorHandler.cpp b/src/ParseErrorHandler.cpp new file mode 100644 index 0000000..10a9e59 --- /dev/null +++ b/src/ParseErrorHandler.cpp @@ -0,0 +1,39 @@ +#include "ParseErrorHandler.hpp" + +#include "antlr4-runtime.h" +#include +#include + +void ParseErrorHandler::reportError(antlr4::Parser *recognizer, const antlr4::RecognitionException &e) +{ + auto offendingToken = e.getOffendingToken(); + auto line_no = offendingToken->getLine(); + auto char_pos = offendingToken->getLine(); + ParseError error(line_no, char_pos, "Parse error"); + errors.push_back(error); +} + +void ParseErrorHandler::recover(antlr4::Parser *recognizer, std::exception_ptr e) +{ + recognizer->consume(); +} + +antlr4::Token *ParseErrorHandler::recoverInline(antlr4::Parser *recognizer) +{ + auto offendingToken = recognizer->getCurrentToken(); + auto line_no = offendingToken->getLine(); + auto char_pos = offendingToken->getLine(); + ParseError error(line_no, char_pos, "Parse error"); + errors.push_back(error); + recognizer->consume(); + return recognizer->getCurrentToken(); +} + +void ParseErrorHandler::sync(antlr4::Parser *recognizer) +{ +} + +const std::vector &ParseErrorHandler::getErrors() const +{ + return errors; +} diff --git a/src/ParseErrorHandler.hpp b/src/ParseErrorHandler.hpp new file mode 100644 index 0000000..a3a2080 --- /dev/null +++ b/src/ParseErrorHandler.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "ParseError.hpp" + +#include "antlr4-runtime.h" +#include +#include + +class ParseErrorHandler : public antlr4::DefaultErrorStrategy +{ +private: + std::vector errors; + +public: + void reportError(antlr4::Parser *recognizer, const antlr4::RecognitionException &e) override; + + void recover(antlr4::Parser *recognizer, std::exception_ptr e) override; + + antlr4::Token *recoverInline(antlr4::Parser *recognizer) override; + + void sync(antlr4::Parser *recognizer) override; + + const std::vector &getErrors() const; +}; diff --git a/src/Visitor.cpp b/src/Visitor.cpp index 2bbfbd6..b98f5ac 100644 --- a/src/Visitor.cpp +++ b/src/Visitor.cpp @@ -1,5 +1,6 @@ #include "Visitor.hpp" #include "Node.hpp" +#include "ParseErrorException.hpp" #include #include @@ -72,7 +73,13 @@ std::any Visitor::visitLiteral(HooParser::LiteralContext *ctx) return std::any{Node(NODE_LITERAL, DATATYPE_STRING, stringConstant)}; } - return std::any{Node(NODE_IGNORE, DATATYPE_VOID, nullptr)}; + auto line_no = ctx->getStart()->getLine(); + auto char_pos = ctx->getStart()->getCharPositionInLine(); + auto message = "Invalid literal near line " + std::to_string(line_no) + ", character " + std::to_string(char_pos) + " on " + _moduleName + "."; +#ifndef NDDEBUG + std::cerr << message << std::endl; +#endif + throw ParseErrorException(_moduleName, line_no, char_pos, message); } std::any Visitor::visitLiteralStatement(HooParser::LiteralStatementContext *ctx) @@ -85,6 +92,7 @@ std::any Visitor::visitLiteralStatement(HooParser::LiteralStatementContext *ctx) std::any Visitor::visitStatement(HooParser::StatementContext *ctx) { auto listeral_stmt_ctx = ctx->literalStatement(); + std::cout << "Statement: " << ctx->getText() << std::endl; if (listeral_stmt_ctx != nullptr) { auto result = visitLiteralStatement(listeral_stmt_ctx); diff --git a/src/Visitor.hpp b/src/Visitor.hpp index f86ebc3..6c6229f 100644 --- a/src/Visitor.hpp +++ b/src/Visitor.hpp @@ -11,7 +11,7 @@ class Visitor : public HooBaseVisitor { private: - const std::string &_moduleName; + std::string _moduleName; std::shared_ptr _context; std::shared_ptr _module; std::shared_ptr> _builder; diff --git a/tests/bool_tests.cpp b/tests/bool_tests.cpp index b55bc11..be5b118 100644 --- a/tests/bool_tests.cpp +++ b/tests/bool_tests.cpp @@ -1,5 +1,7 @@ #include "Compiler.hpp" #include "Node.hpp" +#include "ParseErrorException.hpp" + #include "llvm/IR/Constants.h" #include @@ -10,19 +12,17 @@ void testBoolLiteral(const std::string &input, bool expectedValue) auto result = compiler->compile(); auto boolNode = std::any_cast(result); - // Validate node type and data type ASSERT_EQ(boolNode.getNodeType(), NODE_LITERAL); ASSERT_EQ(boolNode.getDataType(), DATATYPE_BOOL); - // Validate LLVM value auto value = boolNode.getValue(); ASSERT_NE(value, nullptr); auto value_constant = llvm::dyn_cast(value); ASSERT_NE(value_constant, nullptr); - // Check the boolean value (1 for true, 0 for false) - ASSERT_EQ(value_constant->getValue().getLimitedValue(), static_cast(expectedValue)); + ASSERT_EQ(value_constant->getValue().getLimitedValue(), + static_cast(expectedValue)); } // Test fixture for boolean tests @@ -55,17 +55,17 @@ TEST_F(BoolTest, LiteralFalse) TEST_F(BoolTest, InvalidLiteral) { auto compiler = std::make_unique("notbool;", "main"); - ASSERT_THROW(compiler->compile(), std::invalid_argument); + ASSERT_THROW(compiler->compile(), ParseErrorException); } TEST_F(BoolTest, MissingSemicolon) { auto compiler = std::make_unique("true", "main"); - ASSERT_THROW(compiler->compile(), std::runtime_error); + ASSERT_THROW(compiler->compile(), ParseCollectiveErrorException); } TEST_F(BoolTest, MixedCaseLiteral) { auto compiler = std::make_unique("True;", "main"); - ASSERT_THROW(compiler->compile(), std::invalid_argument); // Expect strict case-sensitivity + ASSERT_THROW(compiler->compile(), ParseErrorException); }