Hnalding parsing errors

This commit is contained in:
Benoy Bose 2025-02-20 00:14:34 +05:30
parent 8835751449
commit 53b626fd2f
12 changed files with 268 additions and 15 deletions

68
.vscode/settings.json vendored
View File

@ -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"
}
}

View File

@ -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

View File

@ -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 <iostream>
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<ParseErrorHandler>());
}
Compiler::~Compiler()
@ -22,14 +28,30 @@ std::any Compiler::compile()
{
try
{
auto error_handler = std::dynamic_pointer_cast<ParseErrorHandler>(_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;

View File

@ -4,8 +4,10 @@
#include "HooLexer.h"
#include "HooParser.h"
#include "Visitor.hpp"
#include "ParseErrorHandler.hpp"
#include <string>
#include <memory>
class Compiler
{

View File

@ -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; }

21
src/ParseError.hpp Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <cstddef>
#include <string>
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; }
};

View File

@ -0,0 +1,61 @@
#pragma once
#include "ParseErrorException.hpp"
#include "ParseError.hpp"
#include <stdexcept>
#include <string>
#include <vector>
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<ParseError> _errors;
public:
ParseCollectiveErrorException(const std::vector<ParseError> &errors)
: _errors(errors)
{
}
const std::vector<ParseError> &getErrors() const { return _errors; }
std::string getMessage() const
{
std::string result;
for (const auto &error : _errors)
{
result += error.getMessage() + "\n";
}
return result;
}
};

39
src/ParseErrorHandler.cpp Normal file
View File

@ -0,0 +1,39 @@
#include "ParseErrorHandler.hpp"
#include "antlr4-runtime.h"
#include <string>
#include <list>
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<ParseError> &ParseErrorHandler::getErrors() const
{
return errors;
}

24
src/ParseErrorHandler.hpp Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include "ParseError.hpp"
#include "antlr4-runtime.h"
#include <string>
#include <list>
class ParseErrorHandler : public antlr4::DefaultErrorStrategy
{
private:
std::vector<ParseError> 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<ParseError> &getErrors() const;
};

View File

@ -1,5 +1,6 @@
#include "Visitor.hpp"
#include "Node.hpp"
#include "ParseErrorException.hpp"
#include <llvm/IR/Constants.h>
#include <llvm/IR/Type.h>
@ -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);

View File

@ -11,7 +11,7 @@
class Visitor : public HooBaseVisitor
{
private:
const std::string &_moduleName;
std::string _moduleName;
std::shared_ptr<llvm::LLVMContext> _context;
std::shared_ptr<llvm::Module> _module;
std::shared_ptr<llvm::IRBuilder<>> _builder;

View File

@ -1,5 +1,7 @@
#include "Compiler.hpp"
#include "Node.hpp"
#include "ParseErrorException.hpp"
#include "llvm/IR/Constants.h"
#include <gtest/gtest.h>
@ -10,19 +12,17 @@ void testBoolLiteral(const std::string &input, bool expectedValue)
auto result = compiler->compile();
auto boolNode = std::any_cast<Node>(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<llvm::ConstantInt>(value);
ASSERT_NE(value_constant, nullptr);
// Check the boolean value (1 for true, 0 for false)
ASSERT_EQ(value_constant->getValue().getLimitedValue(), static_cast<uint64_t>(expectedValue));
ASSERT_EQ(value_constant->getValue().getLimitedValue(),
static_cast<uint64_t>(expectedValue));
}
// Test fixture for boolean tests
@ -55,17 +55,17 @@ TEST_F(BoolTest, LiteralFalse)
TEST_F(BoolTest, InvalidLiteral)
{
auto compiler = std::make_unique<Compiler>("notbool;", "main");
ASSERT_THROW(compiler->compile(), std::invalid_argument);
ASSERT_THROW(compiler->compile(), ParseErrorException);
}
TEST_F(BoolTest, MissingSemicolon)
{
auto compiler = std::make_unique<Compiler>("true", "main");
ASSERT_THROW(compiler->compile(), std::runtime_error);
ASSERT_THROW(compiler->compile(), ParseCollectiveErrorException);
}
TEST_F(BoolTest, MixedCaseLiteral)
{
auto compiler = std::make_unique<Compiler>("True;", "main");
ASSERT_THROW(compiler->compile(), std::invalid_argument); // Expect strict case-sensitivity
ASSERT_THROW(compiler->compile(), ParseErrorException);
}