Skip to content

Commit

Permalink
feat: Add validation for calcul
Browse files Browse the repository at this point in the history
Part of #25

- check left operand
- check right operand
- check calcul can be done between types

Also add early return when type given by sub-expression is nullptr: it
means there is an error, so no need to go further in this expression
  • Loading branch information
Gashmob committed Sep 19, 2024
1 parent 91703c9 commit 812bebb
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ coverage.info
.idea
FilLexer.tokens
node_modules
vgcore.*
46 changes: 46 additions & 0 deletions include/filc/validation/CalculValidator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* MIT License
*
* Copyright (c) 2024-Present Kevin Traini
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef FILC_CALCULVALIDATOR_H
#define FILC_CALCULVALIDATOR_H

#include "filc/grammar/Type.h"
#include <memory>
#include <string>

namespace filc {
class CalculValidator {
public:
[[nodiscard]] static auto isCalculValid(const std::shared_ptr<AbstractType> &left_type, const std::string &op,
const std::shared_ptr<AbstractType> &right_type) -> bool;

private:
[[nodiscard]] static auto isNumericOperatorValid(const std::string &op) -> bool;

[[nodiscard]] static auto isBoolOperatorValid(const std::string &op) -> bool;

[[nodiscard]] static auto isPointerOperatorValid(const std::string &op) -> bool;
};
}

#endif // FILC_CALCULVALIDATOR_H
7 changes: 3 additions & 4 deletions src/grammar/FilParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,7 @@ assignation returns[std::shared_ptr<filc::Assignation> tree]
$tree = std::make_shared<filc::Assignation>($i1.text, $e1.tree);
}
| i2=IDENTIFIER op=(PLUS_EQ | MINUS_EQ | STAR_EQ | DIV_EQ | MOD_EQ | AND_EQ | OR_EQ) e2=expression {
$tree = std::make_shared<filc::Assignation>(
$i2.text,
std::make_shared<filc::BinaryCalcul>(std::make_shared<filc::Identifier>($i2.text), $op.text.substr(0, $op.text.size() - 1), $e2.tree)
);
const auto calcul = std::make_shared<filc::BinaryCalcul>(std::make_shared<filc::Identifier>($i2.text), $op.text.substr(0, $op.text.size() - 1), $e2.tree);
calcul->setPosition(filc::Position($op, $e2.stop));
$tree = std::make_shared<filc::Assignation>($i2.text, calcul);
};
67 changes: 67 additions & 0 deletions src/validation/CalculValidator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* MIT License
*
* Copyright (c) 2024-Present Kevin Traini
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "filc/validation/CalculValidator.h"
#include <algorithm>
#include <vector>

using namespace filc;

auto CalculValidator::isCalculValid(const std::shared_ptr<AbstractType> &left_type, const std::string &op,
const std::shared_ptr<AbstractType> &right_type) -> bool {
if (left_type != right_type) {
return false;
}
const auto type = left_type->getName();

const std::vector<std::string> numeric_type = {
"i8", "i16", "i32", "i64", "i128", "u8", "u16", "u32", "u64", "u128", "f32", "f64",
};
if (std::find(numeric_type.begin(), numeric_type.end(), type) != numeric_type.end()) {
return isNumericOperatorValid(op);
}

if (type == "bool") {
return isBoolOperatorValid(op);
}

if (type[type.length() - 1] == '*') { // A pointer
return isPointerOperatorValid(op);
}

// We don't know what it is, so we assert it cannot be done
return false;
}

auto CalculValidator::isNumericOperatorValid(const std::string &op) -> bool {
const std::vector<std::string> valid_op = {
"%", "+", "-", "/", "*", "<", "<=", ">", ">=", "==", "!=",
};
return std::find(valid_op.begin(), valid_op.end(), op) != valid_op.end();
}

auto CalculValidator::isBoolOperatorValid(const std::string &op) -> bool {
return op == "&&" || op == "||" || op == "==" || op == "!=";
}

auto CalculValidator::isPointerOperatorValid(const std::string &op) -> bool { return op == "==" || op == "!="; }
42 changes: 41 additions & 1 deletion src/validation/ValidationVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@
*/
#include "filc/validation/ValidationVisitor.h"
#include "filc/grammar/assignation/Assignation.h"
#include "filc/grammar/calcul/Calcul.h"
#include "filc/grammar/identifier/Identifier.h"
#include "filc/grammar/literal/Literal.h"
#include "filc/grammar/program/Program.h"
#include "filc/grammar/variable/Variable.h"
#include "filc/utils/Message.h"
#include "filc/validation/CalculValidator.h"
#include <stdexcept>

using namespace filc;
Expand All @@ -47,6 +49,10 @@ auto ValidationVisitor::visitProgram(Program *program) -> void {
if (it + 1 == expressions.end()) {
const auto expected = _environment->getType("int");
const auto found_type = (*it)->getType();
if (found_type == nullptr) {
return;
}

if (found_type != expected) {
auto found_type_name = found_type->getDisplayName() != found_type->getName()
? found_type->getDisplayName() + " aka " + found_type->getName()
Expand Down Expand Up @@ -121,6 +127,9 @@ auto ValidationVisitor::visitVariableDeclaration(VariableDeclaration *variable)
return;
}
variable_type = _environment->getType(variable->getTypeName());
if (variable_type == nullptr) {
return;
}
}

if (variable->getValue() != nullptr) {
Expand Down Expand Up @@ -175,7 +184,35 @@ auto ValidationVisitor::visitIdentifier(Identifier *identifier) -> void {
}

auto ValidationVisitor::visitBinaryCalcul(BinaryCalcul *calcul) -> void {
throw std::logic_error("Not implemented yet");
_context->stack();
_context->set("return", true);
calcul->getLeftExpression()->accept(this);
const auto left_type = calcul->getLeftExpression()->getType();
_context->unstack();

_context->stack();
_context->set("return", true);
calcul->getRightExpression()->accept(this);
const auto right_type = calcul->getRightExpression()->getType();
_context->unstack();

if (left_type == nullptr || right_type == nullptr) {
return;
}

if (!CalculValidator::isCalculValid(left_type, calcul->getOperator(), right_type)) {
_out << Message(ERROR,
"You cannot use operator " + calcul->getOperator() + " with " + left_type->getDisplayName() +
" and " + right_type->getDisplayName(),
calcul->getPosition(), ERROR_COLOR);
return;
}

calcul->setType(left_type);

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

auto ValidationVisitor::visitAssignation(Assignation *assignation) -> void {
Expand All @@ -195,6 +232,9 @@ auto ValidationVisitor::visitAssignation(Assignation *assignation) -> void {
assignation->getValue()->accept(this);
_context->unstack();
const auto value_type = assignation->getValue()->getType();
if (value_type == nullptr) {
return;
}
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()
Expand Down
6 changes: 6 additions & 0 deletions tests/unit/FilCompilerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,9 @@ TEST(FilCompiler, dumpAST) {
"=== End AST dump ===\n",
result.c_str());
}

TEST(FilCompiler, fullRun) {
std::stringstream ss;
auto compiler = filc::FilCompiler(filc::OptionsParser(), filc::DumpVisitor(ss), filc::ValidationVisitor(std::cout));
ASSERT_EQ(1, compiler.run(2, toStringArray({"filc", FIXTURES_PATH "/sample.fil"}).data()));
}
52 changes: 52 additions & 0 deletions tests/unit/validation/CalculValidatorTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* MIT License
*
* Copyright (c) 2024-Present Kevin Traini
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <filc/grammar/Type.h>
#include <filc/validation/CalculValidator.h>
#include <gtest/gtest.h>
#include <memory>

TEST(CalculValidator, invalidDifferentType) {
ASSERT_FALSE(
filc::CalculValidator::isCalculValid(std::make_shared<filc::Type>("a"), "", std::make_shared<filc::Type>("b")));
}

TEST(CalculValidator, validNumeric) {
ASSERT_TRUE(filc::CalculValidator::isCalculValid(std::make_shared<filc::Type>("i32"), "+",
std::make_shared<filc::Type>("i32")));
}

TEST(CalculValidator, validBool) {
ASSERT_TRUE(filc::CalculValidator::isCalculValid(std::make_shared<filc::Type>("bool"), "&&",
std::make_shared<filc::Type>("bool")));
}

TEST(CalculValidator, validPointer) {
ASSERT_TRUE(filc::CalculValidator::isCalculValid(std::make_shared<filc::Type>("i32*"),
"==", std::make_shared<filc::Type>("i32*")));
}

TEST(CalculValidator, invalidUnknown) {
ASSERT_FALSE(filc::CalculValidator::isCalculValid(std::make_shared<filc::Type>("foo"), "+",
std::make_shared<filc::Type>("foo")));
}
4 changes: 3 additions & 1 deletion tests/unit/validation/ValidationVisitorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ TEST(ValidationVisitor, visitIdentifier) {
TEST(ValidationVisitor, visitBinaryCalcul) {
VISITOR;
const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/binary.fil");
ASSERT_THROW(program->accept(&visitor), std::logic_error);
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}), IsEmpty());
ASSERT_STREQ("int", program->getExpressions()[0]->getType()->getDisplayName().c_str());
}

TEST(ValidationVisitor, visitAssignation) {
Expand Down

0 comments on commit 812bebb

Please sign in to comment.