Hnalding parsing errors
This commit is contained in:
parent
8835751449
commit
53b626fd2f
68
.vscode/settings.json
vendored
68
.vscode/settings.json
vendored
@ -1,6 +1,72 @@
|
|||||||
{
|
{
|
||||||
"files.associations": {
|
"files.associations": {
|
||||||
"*.tcc": "cpp",
|
"*.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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7,7 +7,11 @@ ADD_LIBRARY(hoocore STATIC
|
|||||||
Visitor.cpp
|
Visitor.cpp
|
||||||
Compiler.cpp
|
Compiler.cpp
|
||||||
Compiler.hpp
|
Compiler.hpp
|
||||||
Node.hpp)
|
Node.hpp
|
||||||
|
ParseError.hpp
|
||||||
|
ParseErrorHandler.hpp
|
||||||
|
ParseErrorHandler.cpp
|
||||||
|
ParseErrorException.hpp)
|
||||||
ADD_EXECUTABLE(hoo
|
ADD_EXECUTABLE(hoo
|
||||||
Hoo.cpp Visitor.cpp
|
Hoo.cpp Visitor.cpp
|
||||||
${ANTLR_GENERATED_DIR}/HooBaseVisitor.cpp
|
${ANTLR_GENERATED_DIR}/HooBaseVisitor.cpp
|
||||||
|
|||||||
@ -1,12 +1,18 @@
|
|||||||
#include "Compiler.hpp"
|
#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);
|
_input_stream = new antlr4::ANTLRInputStream(_input);
|
||||||
_lexer = new HooLexer(_input_stream);
|
_lexer = new HooLexer(_input_stream);
|
||||||
_tokens = new antlr4::CommonTokenStream(_lexer);
|
_tokens = new antlr4::CommonTokenStream(_lexer);
|
||||||
_parser = new HooParser(_tokens);
|
_parser = new HooParser(_tokens);
|
||||||
_visitor = new Visitor(_moduleName);
|
_visitor = new Visitor(_moduleName);
|
||||||
|
_parser->setErrorHandler(std::make_shared<ParseErrorHandler>());
|
||||||
}
|
}
|
||||||
|
|
||||||
Compiler::~Compiler()
|
Compiler::~Compiler()
|
||||||
@ -22,14 +28,30 @@ std::any Compiler::compile()
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
auto error_handler = std::dynamic_pointer_cast<ParseErrorHandler>(_parser->getErrorHandler());
|
||||||
antlr4::tree::ParseTree *tree = parse();
|
antlr4::tree::ParseTree *tree = parse();
|
||||||
if (tree == nullptr)
|
if (tree == nullptr)
|
||||||
{
|
{
|
||||||
std::cout << "Parse failed" << std::endl;
|
std::cerr << "Parse failed" << std::endl;
|
||||||
}
|
}
|
||||||
auto result = _visitor->visit(tree);
|
auto result = _visitor->visit(tree);
|
||||||
|
auto parseErrors = error_handler->getErrors();
|
||||||
|
if (parseErrors.size() > 0)
|
||||||
|
{
|
||||||
|
throw ParseCollectiveErrorException(parseErrors);
|
||||||
|
}
|
||||||
return result;
|
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)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
std::cerr << "Compilation Error: " << e.what() << std::endl;
|
std::cerr << "Compilation Error: " << e.what() << std::endl;
|
||||||
|
|||||||
@ -4,8 +4,10 @@
|
|||||||
#include "HooLexer.h"
|
#include "HooLexer.h"
|
||||||
#include "HooParser.h"
|
#include "HooParser.h"
|
||||||
#include "Visitor.hpp"
|
#include "Visitor.hpp"
|
||||||
|
#include "ParseErrorHandler.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
class Compiler
|
class Compiler
|
||||||
{
|
{
|
||||||
|
|||||||
10
src/Node.hpp
10
src/Node.hpp
@ -26,8 +26,14 @@ private:
|
|||||||
llvm::Value *_value;
|
llvm::Value *_value;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Node(NodeType nodeType, DataType dataType, llvm::Value *value) : _nodeType(nodeType), _dataType(dataType), _value(value) {}
|
Node(NodeType nodeType, DataType dataType, llvm::Value *value)
|
||||||
Node(const Node &other) : _nodeType(other._nodeType), _dataType(other._dataType), _value(other._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; }
|
NodeType getNodeType() const { return _nodeType; }
|
||||||
DataType getDataType() const { return _dataType; }
|
DataType getDataType() const { return _dataType; }
|
||||||
|
|||||||
21
src/ParseError.hpp
Normal file
21
src/ParseError.hpp
Normal 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; }
|
||||||
|
};
|
||||||
61
src/ParseErrorException.hpp
Normal file
61
src/ParseErrorException.hpp
Normal 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
39
src/ParseErrorHandler.cpp
Normal 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
24
src/ParseErrorHandler.hpp
Normal 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;
|
||||||
|
};
|
||||||
@ -1,5 +1,6 @@
|
|||||||
#include "Visitor.hpp"
|
#include "Visitor.hpp"
|
||||||
#include "Node.hpp"
|
#include "Node.hpp"
|
||||||
|
#include "ParseErrorException.hpp"
|
||||||
|
|
||||||
#include <llvm/IR/Constants.h>
|
#include <llvm/IR/Constants.h>
|
||||||
#include <llvm/IR/Type.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_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)
|
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)
|
std::any Visitor::visitStatement(HooParser::StatementContext *ctx)
|
||||||
{
|
{
|
||||||
auto listeral_stmt_ctx = ctx->literalStatement();
|
auto listeral_stmt_ctx = ctx->literalStatement();
|
||||||
|
std::cout << "Statement: " << ctx->getText() << std::endl;
|
||||||
if (listeral_stmt_ctx != nullptr)
|
if (listeral_stmt_ctx != nullptr)
|
||||||
{
|
{
|
||||||
auto result = visitLiteralStatement(listeral_stmt_ctx);
|
auto result = visitLiteralStatement(listeral_stmt_ctx);
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
class Visitor : public HooBaseVisitor
|
class Visitor : public HooBaseVisitor
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
const std::string &_moduleName;
|
std::string _moduleName;
|
||||||
std::shared_ptr<llvm::LLVMContext> _context;
|
std::shared_ptr<llvm::LLVMContext> _context;
|
||||||
std::shared_ptr<llvm::Module> _module;
|
std::shared_ptr<llvm::Module> _module;
|
||||||
std::shared_ptr<llvm::IRBuilder<>> _builder;
|
std::shared_ptr<llvm::IRBuilder<>> _builder;
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
#include "Compiler.hpp"
|
#include "Compiler.hpp"
|
||||||
#include "Node.hpp"
|
#include "Node.hpp"
|
||||||
|
#include "ParseErrorException.hpp"
|
||||||
|
|
||||||
#include "llvm/IR/Constants.h"
|
#include "llvm/IR/Constants.h"
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
@ -10,19 +12,17 @@ void testBoolLiteral(const std::string &input, bool expectedValue)
|
|||||||
auto result = compiler->compile();
|
auto result = compiler->compile();
|
||||||
auto boolNode = std::any_cast<Node>(result);
|
auto boolNode = std::any_cast<Node>(result);
|
||||||
|
|
||||||
// Validate node type and data type
|
|
||||||
ASSERT_EQ(boolNode.getNodeType(), NODE_LITERAL);
|
ASSERT_EQ(boolNode.getNodeType(), NODE_LITERAL);
|
||||||
ASSERT_EQ(boolNode.getDataType(), DATATYPE_BOOL);
|
ASSERT_EQ(boolNode.getDataType(), DATATYPE_BOOL);
|
||||||
|
|
||||||
// Validate LLVM value
|
|
||||||
auto value = boolNode.getValue();
|
auto value = boolNode.getValue();
|
||||||
ASSERT_NE(value, nullptr);
|
ASSERT_NE(value, nullptr);
|
||||||
|
|
||||||
auto value_constant = llvm::dyn_cast<llvm::ConstantInt>(value);
|
auto value_constant = llvm::dyn_cast<llvm::ConstantInt>(value);
|
||||||
ASSERT_NE(value_constant, nullptr);
|
ASSERT_NE(value_constant, nullptr);
|
||||||
|
|
||||||
// Check the boolean value (1 for true, 0 for false)
|
ASSERT_EQ(value_constant->getValue().getLimitedValue(),
|
||||||
ASSERT_EQ(value_constant->getValue().getLimitedValue(), static_cast<uint64_t>(expectedValue));
|
static_cast<uint64_t>(expectedValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test fixture for boolean tests
|
// Test fixture for boolean tests
|
||||||
@ -55,17 +55,17 @@ TEST_F(BoolTest, LiteralFalse)
|
|||||||
TEST_F(BoolTest, InvalidLiteral)
|
TEST_F(BoolTest, InvalidLiteral)
|
||||||
{
|
{
|
||||||
auto compiler = std::make_unique<Compiler>("notbool;", "main");
|
auto compiler = std::make_unique<Compiler>("notbool;", "main");
|
||||||
ASSERT_THROW(compiler->compile(), std::invalid_argument);
|
ASSERT_THROW(compiler->compile(), ParseErrorException);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BoolTest, MissingSemicolon)
|
TEST_F(BoolTest, MissingSemicolon)
|
||||||
{
|
{
|
||||||
auto compiler = std::make_unique<Compiler>("true", "main");
|
auto compiler = std::make_unique<Compiler>("true", "main");
|
||||||
ASSERT_THROW(compiler->compile(), std::runtime_error);
|
ASSERT_THROW(compiler->compile(), ParseCollectiveErrorException);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BoolTest, MixedCaseLiteral)
|
TEST_F(BoolTest, MixedCaseLiteral)
|
||||||
{
|
{
|
||||||
auto compiler = std::make_unique<Compiler>("True;", "main");
|
auto compiler = std::make_unique<Compiler>("True;", "main");
|
||||||
ASSERT_THROW(compiler->compile(), std::invalid_argument); // Expect strict case-sensitivity
|
ASSERT_THROW(compiler->compile(), ParseErrorException);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user