diff --git a/src/grammar/Position.cpp b/src/grammar/Position.cpp index 5efe1f8..2ea5030 100644 --- a/src/grammar/Position.cpp +++ b/src/grammar/Position.cpp @@ -47,8 +47,8 @@ auto Position::getStartPosition() const -> std::pair auto Position::getEndPosition() const -> std::pair { return _end_position; } auto Position::getContent() const -> std::vector { - if (_filename.empty()) { - throw std::logic_error("Filename is empty"); + if (_filename.empty() || _filename == "") { + return {}; } std::ifstream file(_filename); @@ -85,6 +85,9 @@ auto Position::dump(const std::string &color) const -> std::string { const auto end_line = _end_position.first; const auto end_column = _end_position.second; const auto content = getContent(); + if (content.empty()) { + return ""; + } if (content.size() == 1) { // Single line const auto nth = " " + std::to_string(start_line) + " "; diff --git a/src/validation/ValidationVisitor.cpp b/src/validation/ValidationVisitor.cpp index 8c3074f..07a2a88 100644 --- a/src/validation/ValidationVisitor.cpp +++ b/src/validation/ValidationVisitor.cpp @@ -132,9 +132,6 @@ auto ValidationVisitor::visitVariableDeclaration(VariableDeclaration *variable) return; } variable_type = _environment->getType(variable->getTypeName()); - if (variable_type == nullptr) { - return; - } } if (variable->getValue() != nullptr) { @@ -144,7 +141,7 @@ auto ValidationVisitor::visitVariableDeclaration(VariableDeclaration *variable) _context->unstack(); const auto value_type = variable->getValue()->getType(); if (value_type == nullptr) { - throw std::logic_error("Variable value has no type"); + return; } if (variable_type != nullptr && variable_type->getName() != value_type->getName()) { displayError("Cannot assign value of type " + value_type->toDisplay() + " to a variable of type " + diff --git a/tests/unit/Fixtures/validation/assignation.fil b/tests/unit/Fixtures/validation/assignation.fil deleted file mode 100644 index 4fb17b8..0000000 --- a/tests/unit/Fixtures/validation/assignation.fil +++ /dev/null @@ -1,2 +0,0 @@ -var id = 3 -id = 478 diff --git a/tests/unit/Fixtures/validation/binary.fil b/tests/unit/Fixtures/validation/binary.fil deleted file mode 100644 index 5b5a327..0000000 --- a/tests/unit/Fixtures/validation/binary.fil +++ /dev/null @@ -1 +0,0 @@ -2 + 3 diff --git a/tests/unit/Fixtures/validation/boolean.fil b/tests/unit/Fixtures/validation/boolean.fil deleted file mode 100644 index 033cb67..0000000 --- a/tests/unit/Fixtures/validation/boolean.fil +++ /dev/null @@ -1,2 +0,0 @@ -true -0 diff --git a/tests/unit/Fixtures/validation/character.fil b/tests/unit/Fixtures/validation/character.fil deleted file mode 100644 index a6c96f8..0000000 --- a/tests/unit/Fixtures/validation/character.fil +++ /dev/null @@ -1,2 +0,0 @@ -'g' -0 diff --git a/tests/unit/Fixtures/validation/float.fil b/tests/unit/Fixtures/validation/float.fil deleted file mode 100644 index 064a417..0000000 --- a/tests/unit/Fixtures/validation/float.fil +++ /dev/null @@ -1,2 +0,0 @@ -4.6 -0 diff --git a/tests/unit/Fixtures/validation/identifier.fil b/tests/unit/Fixtures/validation/identifier.fil deleted file mode 100644 index e5d676c..0000000 --- a/tests/unit/Fixtures/validation/identifier.fil +++ /dev/null @@ -1,2 +0,0 @@ -val foo = 2 -foo diff --git a/tests/unit/Fixtures/validation/integer.fil b/tests/unit/Fixtures/validation/integer.fil deleted file mode 100644 index 368283e..0000000 --- a/tests/unit/Fixtures/validation/integer.fil +++ /dev/null @@ -1,2 +0,0 @@ -12 -0 diff --git a/tests/unit/Fixtures/validation/invalid_program.fil b/tests/unit/Fixtures/validation/invalid_program.fil deleted file mode 100644 index 908d586..0000000 --- a/tests/unit/Fixtures/validation/invalid_program.fil +++ /dev/null @@ -1,2 +0,0 @@ -// Its last expression is not int -"Hello" diff --git a/tests/unit/Fixtures/validation/string.fil b/tests/unit/Fixtures/validation/string.fil deleted file mode 100644 index fd91746..0000000 --- a/tests/unit/Fixtures/validation/string.fil +++ /dev/null @@ -1,2 +0,0 @@ -"hello" -0 diff --git a/tests/unit/Fixtures/validation/valid_program.fil b/tests/unit/Fixtures/validation/valid_program.fil deleted file mode 100644 index 44fb636..0000000 --- a/tests/unit/Fixtures/validation/valid_program.fil +++ /dev/null @@ -1,2 +0,0 @@ -// Its last expression is int -0 diff --git a/tests/unit/Fixtures/validation/variable.fil b/tests/unit/Fixtures/validation/variable.fil deleted file mode 100644 index 6834e3c..0000000 --- a/tests/unit/Fixtures/validation/variable.fil +++ /dev/null @@ -1 +0,0 @@ -val name: i32 = 45 diff --git a/tests/unit/grammar/PositionTest.cpp b/tests/unit/grammar/PositionTest.cpp index 7d2e9ac..7bc5d90 100644 --- a/tests/unit/grammar/PositionTest.cpp +++ b/tests/unit/grammar/PositionTest.cpp @@ -35,8 +35,8 @@ TEST(Position, defaultConstructor) { ASSERT_THAT(position.getStartPosition(), Pair(0, 0)); ASSERT_THAT(position.getEndPosition(), Pair(0, 0)); ASSERT_STREQ("", position.getFilename().c_str()); - ASSERT_THROW(position.getContent(), std::logic_error); - ASSERT_THROW(position.dump(""), std::logic_error); + ASSERT_THAT(position.getContent(), IsEmpty()); + ASSERT_STREQ("", position.dump("").c_str()); } TEST(Position, tokenConstructor) { diff --git a/tests/unit/validation/ValidationVisitorTest.cpp b/tests/unit/validation/ValidationVisitorTest.cpp index 3590d67..b3b2d01 100644 --- a/tests/unit/validation/ValidationVisitorTest.cpp +++ b/tests/unit/validation/ValidationVisitorTest.cpp @@ -21,9 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include "test_tools.h" #include #include -#include #include #include #include @@ -37,97 +37,224 @@ using namespace ::testing; #define VALIDATION_FIXTURES FIXTURES_PATH "/validation" -TEST(ValidationVisitor, visitProgram_valid) { +TEST(ValidationVisitor, program_valid) { VISITOR; - const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/valid_program.fil"); + const auto program = parseString("0"); program->accept(&visitor); ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); ASSERT_FALSE(visitor.hasError()); } -TEST(ValidationVisitor, visitProgram_invalid) { +TEST(ValidationVisitor, program_invalid) { VISITOR; - const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/invalid_program.fil"); + const auto program = parseString("'a'"); program->accept(&visitor); ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), - HasSubstr("Expected type int aka i32 but got char* aka u8*")); + HasSubstr("Expected type int aka i32 but got char")); ASSERT_TRUE(visitor.hasError()); } -TEST(ValidationVisitor, visitBooleanLiteral) { +TEST(ValidationVisitor, boolean) { VISITOR; - const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/boolean.fil"); + const auto program = parseString("true\n0"); program->accept(&visitor); ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Boolean value not used")); ASSERT_FALSE(visitor.hasError()); } -TEST(ValidationVisitor, visitIntegerLiteral) { +TEST(ValidationVisitor, integer) { VISITOR; - const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/integer.fil"); + const auto program = parseString("2\n0"); 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()); } -TEST(ValidationVisitor, visitFloatLiteral) { +TEST(ValidationVisitor, float) { VISITOR; - const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/float.fil"); + const auto program = parseString("3.14\n0"); 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()); } -TEST(ValidationVisitor, visitCharacterLiteral) { +TEST(ValidationVisitor, character) { VISITOR; - const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/character.fil"); + const auto program = parseString("'a'\n0"); 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()); } -TEST(ValidationVisitor, visitStringLiteral) { +TEST(ValidationVisitor, string) { VISITOR; - const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/string.fil"); + const auto program = parseString("\"hello\"\n0"); 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()); } -TEST(ValidationVisitor, visitVariableDeclaration) { +TEST(ValidationVisitor, variable_alreadyDefined) { VISITOR; - const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/variable.fil"); + const auto program = parseString("val my_constant = 2\nval my_constant = 3"); + program->accept(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("my_constant is already defined")); + ASSERT_TRUE(visitor.hasError()); + ASSERT_STREQ("int", program->getExpressions()[0]->getType()->getDisplayName().c_str()); + ASSERT_EQ(nullptr, program->getExpressions()[1]->getType()); +} + +TEST(ValidationVisitor, variable_constantWithoutValue) { + VISITOR; + const auto program = parseString("val my_constant"); + program->accept(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), + HasSubstr("When declaring a constant, you must provide it a value")); + ASSERT_TRUE(visitor.hasError()); + ASSERT_EQ(nullptr, program->getExpressions()[0]->getType()); +} + +TEST(ValidationVisitor, variable_unknowType) { + VISITOR; + const auto program = parseString("var my_var: foo"); + program->accept(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Unknown type: foo")); + ASSERT_TRUE(visitor.hasError()); + ASSERT_EQ(nullptr, program->getExpressions()[0]->getType()); +} + +TEST(ValidationVisitor, variable_differentValueType) { + VISITOR; + const auto program = parseString("var my_var: i32 = 'a'"); + program->accept(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), + HasSubstr("Cannot assign value of type char aka u8 to a variable of type i32")); + ASSERT_TRUE(visitor.hasError()); + ASSERT_EQ(nullptr, program->getExpressions()[0]->getType()); +} + +TEST(ValidationVisitor, variable_assignAliasType) { + VISITOR; + const auto program = parseString("var my_var: u8 = 'a'\n0"); + program->accept(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); + ASSERT_FALSE(visitor.hasError()); + ASSERT_STREQ("u8", program->getExpressions()[0]->getType()->getDisplayName().c_str()); +} + +TEST(ValidationVisitor, variable_withoutTypeAndValue) { + VISITOR; + const auto program = parseString("var my_var"); + program->accept(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), + HasSubstr("When declaring a variable, you must provide at least a type or a value")); + ASSERT_TRUE(visitor.hasError()); + ASSERT_EQ(nullptr, program->getExpressions()[0]->getType()); +} + +TEST(ValidationVisitor, variable_valid) { + VISITOR; + const auto program = parseString("val foo: i32 = 45"); 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()); } -TEST(ValidationVisitor, visitIdentifier) { +TEST(ValidationVisitor, identifier_nonExisting) { + VISITOR; + const auto program = parseString("bar"); + program->accept(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), + HasSubstr("Unknown name, don't know what it refers to: bar")); + ASSERT_TRUE(visitor.hasError()); + ASSERT_EQ(nullptr, program->getExpressions()[0]->getType()); +} + +TEST(ValidationVisitor, identifier_valid) { VISITOR; - const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/identifier.fil"); + const auto program = parseString("val foo = 1\nfoo"); 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()); } -TEST(ValidationVisitor, visitBinaryCalcul) { +TEST(ValidationVisitor, calcul_invalidLeft) { + VISITOR; + const auto program = parseString("(val foo) + 2"); + program->accept(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), + HasSubstr("When declaring a constant, you must provide it a value")); + ASSERT_TRUE(visitor.hasError()); + ASSERT_EQ(nullptr, program->getExpressions()[0]->getType()); +} + +TEST(ValidationVisitor, calcul_invalidRight) { + VISITOR; + const auto program = parseString("2 + (val foo)"); + program->accept(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), + HasSubstr("When declaring a constant, you must provide it a value")); + ASSERT_TRUE(visitor.hasError()); + ASSERT_EQ(nullptr, program->getExpressions()[0]->getType()); +} + +TEST(ValidationVisitor, calcul_invalid) { + VISITOR; + const auto program = parseString("'a' && 3"); + program->accept(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), + HasSubstr("You cannot use operator && with char aka u8 and int aka i32")); + ASSERT_TRUE(visitor.hasError()); + ASSERT_EQ(nullptr, program->getExpressions()[0]->getType()); +} + +TEST(ValidationVisitor, calcul_valid) { VISITOR; - const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/binary.fil"); + const auto program = parseString("2 + 2"); 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()); } -TEST(ValidationVisitor, visitAssignation) { +TEST(ValidationVisitor, assignation_nonExisting) { + VISITOR; + const auto program = parseString("foo = 3"); + program->accept(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), + HasSubstr("Unknown name, don't know what it refers to: foo")); + ASSERT_TRUE(visitor.hasError()); + ASSERT_EQ(nullptr, program->getExpressions()[0]->getType()); +} + +TEST(ValidationVisitor, assignation_constant) { + VISITOR; + const auto program = parseString("val foo = 3\nfoo = 4"); + program->accept(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), HasSubstr("Cannot modify a constant")); + ASSERT_TRUE(visitor.hasError()); + ASSERT_EQ(nullptr, program->getExpressions()[1]->getType()); +} + +TEST(ValidationVisitor, assignation_differentType) { + VISITOR; + const auto program = parseString("var foo = 3\nfoo = false"); + program->accept(&visitor); + ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), + HasSubstr("Cannot assign value of type bool to a variable of type int aka i32")); + ASSERT_TRUE(visitor.hasError()); + ASSERT_EQ(nullptr, program->getExpressions()[1]->getType()); +} + +TEST(ValidationVisitor, assignation_valid) { VISITOR; - const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/assignation.fil"); + const auto program = parseString("var foo = 3\nfoo = 2"); program->accept(&visitor); ASSERT_THAT(std::string(std::istreambuf_iterator(ss), {}), IsEmpty()); ASSERT_FALSE(visitor.hasError());