From 91703c9b715c36347bbd70e00e77542b9b799a2b Mon Sep 17 00:00:00 2001 From: Kevin Traini Date: Sun, 15 Sep 2024 10:53:02 +0200 Subject: [PATCH] feat: Add validation of assignation Part of #25 - check name exists - check it's not a constant - check type equality --- include/filc/validation/Name.h | 5 ++- src/validation/Name.cpp | 7 +++- src/validation/ValidationVisitor.cpp | 37 +++++++++++++++++-- .../unit/Fixtures/validation/assignation.fil | 1 + tests/unit/validation/EnvironmentTest.cpp | 5 ++- tests/unit/validation/NameTest.cpp | 4 +- .../unit/validation/ValidationVisitorTest.cpp | 4 +- 7 files changed, 53 insertions(+), 10 deletions(-) diff --git a/include/filc/validation/Name.h b/include/filc/validation/Name.h index 1abc49d..982ec92 100644 --- a/include/filc/validation/Name.h +++ b/include/filc/validation/Name.h @@ -33,13 +33,16 @@ class Name { public: Name(); - Name(std::string name, std::shared_ptr type); + Name(bool constant, std::string name, std::shared_ptr type); + + [[nodiscard]] auto isConstant() const -> bool; [[nodiscard]] auto getName() const -> const std::string&; [[nodiscard]] auto getType() const -> std::shared_ptr; private: + bool _constant; std::string _name; std::shared_ptr _type; }; diff --git a/src/validation/Name.cpp b/src/validation/Name.cpp index 93ca8b5..8c3c835 100644 --- a/src/validation/Name.cpp +++ b/src/validation/Name.cpp @@ -26,9 +26,12 @@ using namespace filc; -Name::Name() = default; +Name::Name() : _constant(true) {} -Name::Name(std::string name, std::shared_ptr type) : _name(std::move(name)), _type(std::move(type)) {} +Name::Name(bool constant, std::string name, std::shared_ptr type) + : _constant(constant), _name(std::move(name)), _type(std::move(type)) {} + +auto Name::isConstant() const -> bool { return _constant; } auto Name::getName() const -> const std::string & { return _name; } diff --git a/src/validation/ValidationVisitor.cpp b/src/validation/ValidationVisitor.cpp index 1580c3d..2a78b7e 100644 --- a/src/validation/ValidationVisitor.cpp +++ b/src/validation/ValidationVisitor.cpp @@ -22,6 +22,7 @@ * SOFTWARE. */ #include "filc/validation/ValidationVisitor.h" +#include "filc/grammar/assignation/Assignation.h" #include "filc/grammar/identifier/Identifier.h" #include "filc/grammar/literal/Literal.h" #include "filc/grammar/program/Program.h" @@ -126,6 +127,7 @@ auto ValidationVisitor::visitVariableDeclaration(VariableDeclaration *variable) _context->stack(); _context->set("return", true); variable->getValue()->accept(this); + _context->unstack(); const auto value_type = variable->getValue()->getType(); if (value_type == nullptr) { throw std::logic_error("Variable value has no type"); @@ -154,12 +156,12 @@ auto ValidationVisitor::visitVariableDeclaration(VariableDeclaration *variable) } variable->setType(variable_type); - _environment->addName(Name(variable->getName(), variable_type)); + _environment->addName(Name(variable->isConstant(), variable->getName(), variable_type)); } auto ValidationVisitor::visitIdentifier(Identifier *identifier) -> void { if (!_environment->hasName(identifier->getName())) { - _out << Message(ERROR, "Unknown name, don't what it refers to: " + identifier->getName(), + _out << Message(ERROR, "Unknown name, don't know what it refers to: " + identifier->getName(), identifier->getPosition(), ERROR_COLOR); return; } @@ -177,5 +179,34 @@ auto ValidationVisitor::visitBinaryCalcul(BinaryCalcul *calcul) -> void { } auto ValidationVisitor::visitAssignation(Assignation *assignation) -> void { - throw std::logic_error("Not implemented yet"); + if (!_environment->hasName(assignation->getIdentifier())) { + _out << Message(ERROR, "Unknown name, don't know what it refers to: " + assignation->getIdentifier(), + assignation->getPosition(), ERROR_COLOR); + return; + } + const auto name = _environment->getName(assignation->getIdentifier()); + if (name.isConstant()) { + _out << Message(ERROR, "Cannot modify a constant", assignation->getPosition(), ERROR_COLOR); + return; + } + + _context->stack(); + _context->set("return", true); + assignation->getValue()->accept(this); + _context->unstack(); + const auto value_type = assignation->getValue()->getType(); + if (value_type->getName() != name.getType()->getName()) { + const auto variable_type_dump = name.getType()->getDisplayName() != name.getType()->getName() + ? name.getType()->getDisplayName() + " aka " + name.getType()->getName() + : name.getType()->getDisplayName(); + const auto value_type_dump = value_type->getDisplayName() != value_type->getName() + ? value_type->getDisplayName() + " aka " + value_type->getName() + : value_type->getDisplayName(); + _out << Message( + ERROR, "Cannot assign value of type " + value_type_dump + " to a variable of type " + variable_type_dump, + assignation->getPosition(), ERROR_COLOR); + return; + } + + assignation->setType(name.getType()); } diff --git a/tests/unit/Fixtures/validation/assignation.fil b/tests/unit/Fixtures/validation/assignation.fil index deae3df..4fb17b8 100644 --- a/tests/unit/Fixtures/validation/assignation.fil +++ b/tests/unit/Fixtures/validation/assignation.fil @@ -1 +1,2 @@ +var id = 3 id = 478 diff --git a/tests/unit/validation/EnvironmentTest.cpp b/tests/unit/validation/EnvironmentTest.cpp index d663046..5b74cdf 100644 --- a/tests/unit/validation/EnvironmentTest.cpp +++ b/tests/unit/validation/EnvironmentTest.cpp @@ -60,9 +60,10 @@ TEST(Environment, Name) { filc::Environment env; ASSERT_FALSE(env.hasName("my_name")); ASSERT_THROW(env.getName("my_name"), std::logic_error); - env.addName(filc::Name("my_name", env.getType("i32"))); - ASSERT_THROW(env.addName(filc::Name("my_name", env.getType("i64"))), std::logic_error); + env.addName(filc::Name(false, "my_name", env.getType("i32"))); + ASSERT_THROW(env.addName(filc::Name(true, "my_name", env.getType("i64"))), std::logic_error); ASSERT_TRUE(env.hasName("my_name")); + ASSERT_FALSE(env.getName("my_name").isConstant()); ASSERT_STREQ("my_name", env.getName("my_name").getName().c_str()); ASSERT_STREQ("i32", env.getName("my_name").getType()->getName().c_str()); } diff --git a/tests/unit/validation/NameTest.cpp b/tests/unit/validation/NameTest.cpp index 5e9d018..cd54f4f 100644 --- a/tests/unit/validation/NameTest.cpp +++ b/tests/unit/validation/NameTest.cpp @@ -26,12 +26,14 @@ TEST(Name, defaultConstructor) { filc::Name name; + ASSERT_TRUE(name.isConstant()); ASSERT_STREQ("", name.getName().c_str()); ASSERT_EQ(nullptr, name.getType()); } TEST(Name, constructor) { - filc::Name name("my_var", std::make_shared("i32")); + filc::Name name(false, "my_var", std::make_shared("i32")); + ASSERT_FALSE(name.isConstant()); ASSERT_STREQ("my_var", name.getName().c_str()); ASSERT_STREQ("i32", name.getType()->getName().c_str()); } diff --git a/tests/unit/validation/ValidationVisitorTest.cpp b/tests/unit/validation/ValidationVisitorTest.cpp index 2995b57..8349423 100644 --- a/tests/unit/validation/ValidationVisitorTest.cpp +++ b/tests/unit/validation/ValidationVisitorTest.cpp @@ -116,5 +116,7 @@ TEST(ValidationVisitor, visitBinaryCalcul) { TEST(ValidationVisitor, visitAssignation) { VISITOR; const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/assignation.fil"); - ASSERT_THROW(program->accept(&visitor), std::logic_error); + program->accept(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_STREQ("int", program->getExpressions()[1]->getType()->getDisplayName().c_str()); }