diff --git a/include/filc/validation/ValidationVisitor.h b/include/filc/validation/ValidationVisitor.h index 265ee4b..0444bda 100644 --- a/include/filc/validation/ValidationVisitor.h +++ b/include/filc/validation/ValidationVisitor.h @@ -25,6 +25,7 @@ #define FILC_VALIDATIONVISITOR_H #include "filc/grammar/Visitor.h" +#include "filc/grammar/Position.h" #include "filc/validation/Environment.h" #include #include @@ -65,6 +66,8 @@ class ValidationVisitor final : public Visitor { public: explicit ValidationVisitor(std::ostream &out); + [[nodiscard]] auto hasError() const -> bool; + auto visitProgram(Program *program) -> void override; auto visitBooleanLiteral(BooleanLiteral *literal) -> void override; @@ -89,6 +92,11 @@ class ValidationVisitor final : public Visitor { std::unique_ptr _context; std::unique_ptr _environment; std::ostream &_out; + bool _error; + + auto displayError(const std::string &message, const Position &position) -> void; + + auto displayWarning(const std::string &message, const Position &position) const -> void; }; } diff --git a/src/filc.cpp b/src/filc.cpp index 94df477..cfadb34 100644 --- a/src/filc.cpp +++ b/src/filc.cpp @@ -61,6 +61,9 @@ auto FilCompiler::run(int argc, char **argv) -> int { } program->accept(&_validation_visitor); + if (_validation_visitor.hasError()) { + return 1; + } - return 1; + return 0; } diff --git a/src/validation/ValidationVisitor.cpp b/src/validation/ValidationVisitor.cpp index 61c4ff2..8c3074f 100644 --- a/src/validation/ValidationVisitor.cpp +++ b/src/validation/ValidationVisitor.cpp @@ -35,7 +35,18 @@ using namespace filc; ValidationVisitor::ValidationVisitor(std::ostream &out) - : _context(new ValidationContext()), _environment(new Environment()), _out(out) {} + : _context(new ValidationContext()), _environment(new Environment()), _out(out), _error(false) {} + +auto ValidationVisitor::hasError() const -> bool { return _error; } + +auto ValidationVisitor::displayError(const std::string &message, const Position &position) -> void { + _error = true; + _out << Message(ERROR, message, position, ERROR_COLOR); +} + +auto ValidationVisitor::displayWarning(const std::string &message, const Position &position) const -> void { + _out << Message(WARNING, message, position, WARNING_COLOR); +} auto ValidationVisitor::visitProgram(Program *program) -> void { auto expressions = program->getExpressions(); @@ -54,8 +65,8 @@ auto ValidationVisitor::visitProgram(Program *program) -> void { } if (found_type != expected) { - _out << Message(ERROR, "Expected type " + expected->toDisplay() + " but got " + found_type->toDisplay(), - (*it)->getPosition(), ERROR_COLOR); + displayError("Expected type " + expected->toDisplay() + " but got " + found_type->toDisplay(), + (*it)->getPosition()); } } @@ -67,7 +78,7 @@ auto ValidationVisitor::visitBooleanLiteral(BooleanLiteral *literal) -> void { literal->setType(_environment->getType("bool")); if (!_context->has("return") || !_context->get("return")) { - _out << Message(WARNING, "Boolean value not used", literal->getPosition(), WARNING_COLOR); + displayWarning("Boolean value not used", literal->getPosition()); } } @@ -75,7 +86,7 @@ auto ValidationVisitor::visitIntegerLiteral(IntegerLiteral *literal) -> void { literal->setType(_environment->getType("int")); if (!_context->has("return") || !_context->get("return")) { - _out << Message(WARNING, "Integer value not used", literal->getPosition(), WARNING_COLOR); + displayWarning("Integer value not used", literal->getPosition()); } } @@ -83,7 +94,7 @@ auto ValidationVisitor::visitFloatLiteral(FloatLiteral *literal) -> void { literal->setType(_environment->getType("f64")); if (!_context->has("return") || !_context->get("return")) { - _out << Message(WARNING, "Float value not used", literal->getPosition(), WARNING_COLOR); + displayWarning("Float value not used", literal->getPosition()); } } @@ -91,7 +102,7 @@ auto ValidationVisitor::visitCharacterLiteral(CharacterLiteral *literal) -> void literal->setType(_environment->getType("char")); if (!_context->has("return") || !_context->get("return")) { - _out << Message(WARNING, "Character value not used", literal->getPosition(), WARNING_COLOR); + displayWarning("Character value not used", literal->getPosition()); } } @@ -99,26 +110,25 @@ auto ValidationVisitor::visitStringLiteral(StringLiteral *literal) -> void { literal->setType(_environment->getType("char*")); if (!_context->has("return") || !_context->get("return")) { - _out << Message(WARNING, "String value not used", literal->getPosition(), WARNING_COLOR); + displayWarning("String value not used", literal->getPosition()); } } auto ValidationVisitor::visitVariableDeclaration(VariableDeclaration *variable) -> void { if (_environment->hasName(variable->getName())) { - _out << Message(ERROR, variable->getName() + " is already defined", variable->getPosition(), ERROR_COLOR); + displayError(variable->getName() + " is already defined", variable->getPosition()); return; } if (variable->isConstant() && variable->getValue() == nullptr) { - _out << Message(ERROR, "When declaring a constant, you must provide it a value", variable->getPosition(), - ERROR_COLOR); + displayError("When declaring a constant, you must provide it a value", variable->getPosition()); return; } std::shared_ptr variable_type = nullptr; if (!variable->getTypeName().empty()) { if (!_environment->hasType(variable->getTypeName())) { - _out << Message(ERROR, "Unknown type: " + variable->getTypeName(), variable->getPosition(), ERROR_COLOR); + displayError("Unknown type: " + variable->getTypeName(), variable->getPosition()); return; } variable_type = _environment->getType(variable->getTypeName()); @@ -137,10 +147,9 @@ auto ValidationVisitor::visitVariableDeclaration(VariableDeclaration *variable) throw std::logic_error("Variable value has no type"); } if (variable_type != nullptr && variable_type->getName() != value_type->getName()) { - _out << Message(ERROR, - "Cannot assign value of type " + value_type->toDisplay() + " to a variable of type " + - variable_type->toDisplay(), - variable->getPosition(), ERROR_COLOR); + displayError("Cannot assign value of type " + value_type->toDisplay() + " to a variable of type " + + variable_type->toDisplay(), + variable->getPosition()); return; } else if (variable_type == nullptr) { variable_type = value_type; @@ -148,8 +157,7 @@ auto ValidationVisitor::visitVariableDeclaration(VariableDeclaration *variable) } if (variable_type == nullptr) { - _out << Message(ERROR, "When declaring a variable, you must provide at least a type or a value", - variable->getPosition(), ERROR_COLOR); + displayError("When declaring a variable, you must provide at least a type or a value", variable->getPosition()); return; } @@ -159,8 +167,7 @@ auto ValidationVisitor::visitVariableDeclaration(VariableDeclaration *variable) auto ValidationVisitor::visitIdentifier(Identifier *identifier) -> void { if (!_environment->hasName(identifier->getName())) { - _out << Message(ERROR, "Unknown name, don't know what it refers to: " + identifier->getName(), - identifier->getPosition(), ERROR_COLOR); + displayError("Unknown name, don't know what it refers to: " + identifier->getName(), identifier->getPosition()); return; } @@ -168,7 +175,7 @@ auto ValidationVisitor::visitIdentifier(Identifier *identifier) -> void { identifier->setType(name.getType()); if (!_context->has("return") || !_context->get("return")) { - _out << Message(WARNING, "Value not used", identifier->getPosition(), WARNING_COLOR); + displayWarning("Value not used", identifier->getPosition()); } } @@ -190,29 +197,28 @@ auto ValidationVisitor::visitBinaryCalcul(BinaryCalcul *calcul) -> void { } if (!CalculValidator::isCalculValid(left_type, calcul->getOperator(), right_type)) { - _out << Message(ERROR, - "You cannot use operator " + calcul->getOperator() + " with " + left_type->toDisplay() + - " and " + right_type->toDisplay(), - calcul->getPosition(), ERROR_COLOR); + displayError("You cannot use operator " + calcul->getOperator() + " with " + left_type->toDisplay() + " and " + + right_type->toDisplay(), + calcul->getPosition()); return; } calcul->setType(left_type); if (!_context->has("return") || !_context->get("return")) { - _out << Message(WARNING, "Value not used", calcul->getPosition(), WARNING_COLOR); + displayWarning("Value not used", calcul->getPosition()); } } auto ValidationVisitor::visitAssignation(Assignation *assignation) -> void { if (!_environment->hasName(assignation->getIdentifier())) { - _out << Message(ERROR, "Unknown name, don't know what it refers to: " + assignation->getIdentifier(), - assignation->getPosition(), ERROR_COLOR); + displayError("Unknown name, don't know what it refers to: " + assignation->getIdentifier(), + assignation->getPosition()); return; } const auto name = _environment->getName(assignation->getIdentifier()); if (name.isConstant()) { - _out << Message(ERROR, "Cannot modify a constant", assignation->getPosition(), ERROR_COLOR); + displayError("Cannot modify a constant", assignation->getPosition()); return; } @@ -225,10 +231,9 @@ auto ValidationVisitor::visitAssignation(Assignation *assignation) -> void { return; } if (value_type->getName() != name.getType()->getName()) { - _out << Message(ERROR, - "Cannot assign value of type " + value_type->toDisplay() + " to a variable of type " + - name.getType()->toDisplay(), - assignation->getPosition(), ERROR_COLOR); + displayError("Cannot assign value of type " + value_type->toDisplay() + " to a variable of type " + + name.getType()->toDisplay(), + assignation->getPosition()); return; } diff --git a/tests/unit/validation/ValidationVisitorTest.cpp b/tests/unit/validation/ValidationVisitorTest.cpp index c283646..3590d67 100644 --- a/tests/unit/validation/ValidationVisitorTest.cpp +++ b/tests/unit/validation/ValidationVisitorTest.cpp @@ -42,6 +42,7 @@ TEST(ValidationVisitor, visitProgram_valid) { const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/valid_program.fil"); program->accept(&visitor); ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_FALSE(visitor.hasError()); } TEST(ValidationVisitor, visitProgram_invalid) { @@ -50,6 +51,7 @@ TEST(ValidationVisitor, visitProgram_invalid) { program->accept(&visitor); ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Expected type int aka i32 but got char* aka u8*")); + ASSERT_TRUE(visitor.hasError()); } TEST(ValidationVisitor, visitBooleanLiteral) { @@ -57,6 +59,7 @@ TEST(ValidationVisitor, visitBooleanLiteral) { const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/boolean.fil"); program->accept(&visitor); ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Boolean value not used")); + ASSERT_FALSE(visitor.hasError()); } TEST(ValidationVisitor, visitIntegerLiteral) { @@ -64,6 +67,7 @@ TEST(ValidationVisitor, visitIntegerLiteral) { const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/integer.fil"); program->accept(&visitor); ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Integer value not used")); + ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("int", program->getExpressions()[0]->getType()->getDisplayName().c_str()); } @@ -72,6 +76,7 @@ TEST(ValidationVisitor, visitFloatLiteral) { const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/float.fil"); program->accept(&visitor); ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Float value not used")); + ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("f64", program->getExpressions()[0]->getType()->getDisplayName().c_str()); } @@ -80,6 +85,7 @@ TEST(ValidationVisitor, visitCharacterLiteral) { const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/character.fil"); program->accept(&visitor); ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Character value not used")); + ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("char", program->getExpressions()[0]->getType()->getDisplayName().c_str()); } @@ -88,6 +94,7 @@ TEST(ValidationVisitor, visitStringLiteral) { const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/string.fil"); program->accept(&visitor); ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("String value not used")); + ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("char*", program->getExpressions()[0]->getType()->getDisplayName().c_str()); } @@ -96,6 +103,7 @@ TEST(ValidationVisitor, visitVariableDeclaration) { const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/variable.fil"); program->accept(&visitor); ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("i32", program->getExpressions()[0]->getType()->getDisplayName().c_str()); } @@ -104,6 +112,7 @@ TEST(ValidationVisitor, visitIdentifier) { const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/identifier.fil"); program->accept(&visitor); ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("int", program->getExpressions()[1]->getType()->getDisplayName().c_str()); } @@ -112,6 +121,7 @@ TEST(ValidationVisitor, visitBinaryCalcul) { const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/binary.fil"); program->accept(&visitor); ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("int", program->getExpressions()[0]->getType()->getDisplayName().c_str()); } @@ -120,5 +130,6 @@ TEST(ValidationVisitor, visitAssignation) { const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/assignation.fil"); program->accept(&visitor); ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_FALSE(visitor.hasError()); ASSERT_STREQ("int", program->getExpressions()[1]->getType()->getDisplayName().c_str()); }