Skip to content

Commit

Permalink
feat: add ValidationVisitor::hasError
Browse files Browse the repository at this point in the history
Part of 25

This public method tells if there was an error during validation. To do
so it just checks if a call to displayError was made (if so it means
there was an error). displayWarning is not considered as an error
  • Loading branch information
Gashmob committed Sep 20, 2024
1 parent ad38945 commit 7c3b22a
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 34 deletions.
8 changes: 8 additions & 0 deletions include/filc/validation/ValidationVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#define FILC_VALIDATIONVISITOR_H

#include "filc/grammar/Visitor.h"
#include "filc/grammar/Position.h"
#include "filc/validation/Environment.h"
#include <memory>
#include <stack>
Expand Down Expand Up @@ -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;
Expand All @@ -89,6 +92,11 @@ class ValidationVisitor final : public Visitor {
std::unique_ptr<ValidationContext> _context;
std::unique_ptr<Environment> _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;
};
}

Expand Down
5 changes: 4 additions & 1 deletion src/filc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
71 changes: 38 additions & 33 deletions src/validation/ValidationVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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());
}
}

Expand All @@ -67,58 +78,57 @@ auto ValidationVisitor::visitBooleanLiteral(BooleanLiteral *literal) -> void {
literal->setType(_environment->getType("bool"));

if (!_context->has("return") || !_context->get<bool>("return")) {
_out << Message(WARNING, "Boolean value not used", literal->getPosition(), WARNING_COLOR);
displayWarning("Boolean value not used", literal->getPosition());
}
}

auto ValidationVisitor::visitIntegerLiteral(IntegerLiteral *literal) -> void {
literal->setType(_environment->getType("int"));

if (!_context->has("return") || !_context->get<bool>("return")) {
_out << Message(WARNING, "Integer value not used", literal->getPosition(), WARNING_COLOR);
displayWarning("Integer value not used", literal->getPosition());
}
}

auto ValidationVisitor::visitFloatLiteral(FloatLiteral *literal) -> void {
literal->setType(_environment->getType("f64"));

if (!_context->has("return") || !_context->get<bool>("return")) {
_out << Message(WARNING, "Float value not used", literal->getPosition(), WARNING_COLOR);
displayWarning("Float value not used", literal->getPosition());
}
}

auto ValidationVisitor::visitCharacterLiteral(CharacterLiteral *literal) -> void {
literal->setType(_environment->getType("char"));

if (!_context->has("return") || !_context->get<bool>("return")) {
_out << Message(WARNING, "Character value not used", literal->getPosition(), WARNING_COLOR);
displayWarning("Character value not used", literal->getPosition());
}
}

auto ValidationVisitor::visitStringLiteral(StringLiteral *literal) -> void {
literal->setType(_environment->getType("char*"));

if (!_context->has("return") || !_context->get<bool>("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<AbstractType> 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());
Expand All @@ -137,19 +147,17 @@ 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;
}
}

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

Expand All @@ -159,16 +167,15 @@ 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;
}

const auto name = _environment->getName(identifier->getName());
identifier->setType(name.getType());

if (!_context->has("return") || !_context->get<bool>("return")) {
_out << Message(WARNING, "Value not used", identifier->getPosition(), WARNING_COLOR);
displayWarning("Value not used", identifier->getPosition());
}
}

Expand All @@ -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<bool>("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;
}

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

Expand Down
11 changes: 11 additions & 0 deletions tests/unit/validation/ValidationVisitorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<char>(ss), {}), IsEmpty());
ASSERT_FALSE(visitor.hasError());
}

TEST(ValidationVisitor, visitProgram_invalid) {
Expand All @@ -50,20 +51,23 @@ TEST(ValidationVisitor, visitProgram_invalid) {
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}),
HasSubstr("Expected type int aka i32 but got char* aka u8*"));
ASSERT_TRUE(visitor.hasError());
}

TEST(ValidationVisitor, visitBooleanLiteral) {
VISITOR;
const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/boolean.fil");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}), HasSubstr("Boolean value not used"));
ASSERT_FALSE(visitor.hasError());
}

TEST(ValidationVisitor, visitIntegerLiteral) {
VISITOR;
const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/integer.fil");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}), HasSubstr("Integer value not used"));
ASSERT_FALSE(visitor.hasError());
ASSERT_STREQ("int", program->getExpressions()[0]->getType()->getDisplayName().c_str());
}

Expand All @@ -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<char>(ss), {}), HasSubstr("Float value not used"));
ASSERT_FALSE(visitor.hasError());
ASSERT_STREQ("f64", program->getExpressions()[0]->getType()->getDisplayName().c_str());
}

Expand All @@ -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<char>(ss), {}), HasSubstr("Character value not used"));
ASSERT_FALSE(visitor.hasError());
ASSERT_STREQ("char", program->getExpressions()[0]->getType()->getDisplayName().c_str());
}

Expand All @@ -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<char>(ss), {}), HasSubstr("String value not used"));
ASSERT_FALSE(visitor.hasError());
ASSERT_STREQ("char*", program->getExpressions()[0]->getType()->getDisplayName().c_str());
}

Expand All @@ -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<char>(ss), {}), IsEmpty());
ASSERT_FALSE(visitor.hasError());
ASSERT_STREQ("i32", program->getExpressions()[0]->getType()->getDisplayName().c_str());
}

Expand All @@ -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<char>(ss), {}), IsEmpty());
ASSERT_FALSE(visitor.hasError());
ASSERT_STREQ("int", program->getExpressions()[1]->getType()->getDisplayName().c_str());
}

Expand All @@ -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<char>(ss), {}), IsEmpty());
ASSERT_FALSE(visitor.hasError());
ASSERT_STREQ("int", program->getExpressions()[0]->getType()->getDisplayName().c_str());
}

Expand All @@ -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<char>(ss), {}), IsEmpty());
ASSERT_FALSE(visitor.hasError());
ASSERT_STREQ("int", program->getExpressions()[1]->getType()->getDisplayName().c_str());
}

0 comments on commit 7c3b22a

Please sign in to comment.