diff --git a/src/Binder/Binder.cpp b/src/Binder/Binder.cpp index bdb1149..d6540d1 100644 --- a/src/Binder/Binder.cpp +++ b/src/Binder/Binder.cpp @@ -585,14 +585,12 @@ namespace Porygon::Binder { } auto functionType = std::dynamic_pointer_cast(type); auto givenParameters = expression->GetParameters(); - auto givenParameterTypes = vector>(givenParameters->size()); vector boundParameters = vector(givenParameters->size()); for (size_t i = 0; i < givenParameters->size(); i++){ boundParameters[i] = this -> BindExpression(givenParameters->at(i)); - givenParameterTypes[i] = boundParameters[i]->GetType(); } - auto functionOption = functionType->GetFunctionOption(givenParameterTypes); + auto functionOption = functionType->GetFunctionOption(this->_scriptData->Diagnostics, boundParameters); if (functionOption == nullptr){ this->_scriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::InvalidFunctionParameters, expression->GetStartPosition(), diff --git a/src/Binder/BoundExpressions/BoundExpression.hpp b/src/Binder/BoundExpressions/BoundExpression.hpp index 5a16baa..a6f55d8 100644 --- a/src/Binder/BoundExpressions/BoundExpression.hpp +++ b/src/Binder/BoundExpressions/BoundExpression.hpp @@ -7,7 +7,6 @@ #include "../../ScriptTypes/ScriptType.hpp" #include "../BoundOperators.hpp" #include "../BoundVariables/BoundVariableKey.hpp" -#include "../../ScriptTypes/FunctionScriptType.hpp" using namespace std; diff --git a/src/Binder/BoundExpressions/BoundFunctionCallExpression.hpp b/src/Binder/BoundExpressions/BoundFunctionCallExpression.hpp index 7342bf4..19f94c8 100644 --- a/src/Binder/BoundExpressions/BoundFunctionCallExpression.hpp +++ b/src/Binder/BoundExpressions/BoundFunctionCallExpression.hpp @@ -2,6 +2,7 @@ #define PORYGONLANG_BOUNDFUNCTIONCALLEXPRESSION_HPP #include "BoundExpression.hpp" +#include "../../ScriptTypes/FunctionScriptType.hpp" namespace Porygon::Binder { class BoundFunctionCallExpression : public Porygon::Binder::BoundExpression { diff --git a/src/Diagnostics/DiagnosticCode.hpp b/src/Diagnostics/DiagnosticCode.hpp index 8a4464c..639443c 100644 --- a/src/Diagnostics/DiagnosticCode.hpp +++ b/src/Diagnostics/DiagnosticCode.hpp @@ -11,7 +11,7 @@ namespace Porygon::Diagnostics { // Parse diagnostics UnexpectedToken, - // Bind diagnostics + // Bind errors NoBinaryOperationFound, NoUnaryOperationFound, CantAssignVariable, @@ -27,7 +27,10 @@ namespace Porygon::Diagnostics { NumericalForArgumentNotANumber, CantIterateExpression, InvalidFunctionParameters, - ModuleDoesntExist + ModuleDoesntExist, + + // Bind warnings + DataLossOnImplicitCast, }; } #endif //PORYGONLANG_DIAGNOSTICCODE_HPP diff --git a/src/ScriptTypes/CastResult.hpp b/src/ScriptTypes/CastResult.hpp new file mode 100644 index 0000000..b8dc379 --- /dev/null +++ b/src/ScriptTypes/CastResult.hpp @@ -0,0 +1,12 @@ +#ifndef PORYGONLANG_CASTRESULT_HPP +#define PORYGONLANG_CASTRESULT_HPP + +namespace Porygon{ + enum class CastResult{ + Success, + Failure, + DataLoss, + }; +} + +#endif //PORYGONLANG_CASTRESULT_HPP diff --git a/src/ScriptTypes/FunctionScriptType.hpp b/src/ScriptTypes/FunctionScriptType.hpp index e1659d0..2d7b44f 100644 --- a/src/ScriptTypes/FunctionScriptType.hpp +++ b/src/ScriptTypes/FunctionScriptType.hpp @@ -4,6 +4,8 @@ #include #include "ScriptType.hpp" +#include "../Binder/BoundExpressions/BoundExpression.hpp" +#include "../Diagnostics/DiagnosticsHolder.hpp" namespace Porygon { class GenericFunctionOption{ @@ -33,15 +35,25 @@ namespace Porygon { return _parameterTypes; } - bool IsValid(const vector>& parameters){ + bool IsValid(const shared_ptr& diagnostics, + const vector& parameters){ if (parameters.size() != _parameterTypes.size()){ return false; } for (size_t i = 0; i < parameters.size(); i++){ if (_parameterTypes[i]->GetClass() == TypeClass::All) continue; - if (parameters[i]->operator!=(_parameterTypes[i].get())){ - return false; + auto parameter = parameters[i]; + const auto& parameterType = parameter->GetType(); + if (parameterType->operator!=(_parameterTypes[i].get())){ + auto castResult = parameterType->CastableTo(_parameterTypes[i], false); + if (castResult == CastResult::Failure){ + return false; + } + else if (castResult == CastResult::DataLoss){ + diagnostics->LogWarning(Diagnostics::DiagnosticCode::DataLossOnImplicitCast, parameter->GetStartPosition(), + parameter->GetLength()); + } } } return true; @@ -91,9 +103,10 @@ namespace Porygon { return this; } - GenericFunctionOption* GetFunctionOption(const vector>& parameters) const{ + GenericFunctionOption* GetFunctionOption(const shared_ptr& diagnostics, + const vector& parameters) const{ for (auto o: *_options){ - if (o->IsValid(parameters)){ + if (o->IsValid(diagnostics, parameters)){ return o; } } diff --git a/src/ScriptTypes/ScriptType.hpp b/src/ScriptTypes/ScriptType.hpp index 4eb71b6..b1e35eb 100644 --- a/src/ScriptTypes/ScriptType.hpp +++ b/src/ScriptTypes/ScriptType.hpp @@ -9,6 +9,7 @@ #include #include "../Binder/BoundVariables/BoundVariableKey.hpp" #include "../Utilities/HashedString.hpp" +#include "CastResult.hpp" using namespace std; @@ -34,7 +35,7 @@ namespace Porygon{ virtual ~ScriptType() = default; - [[nodiscard]] const TypeClass GetClass() const{ + [[nodiscard]] inline TypeClass GetClass() const{ return _class; } @@ -74,6 +75,10 @@ namespace Porygon{ virtual shared_ptr GetIteratorKeyType() const{ throw "This type told the binder it can be iterated, but it does not implement the resulting type."; } + + [[nodiscard]] virtual CastResult CastableTo(const shared_ptr& castType, bool explicitCast) const{ + return CastResult::Failure; + } }; class NumericScriptType : public ScriptType{ @@ -94,6 +99,34 @@ namespace Porygon{ [[nodiscard]] inline bool IsFloat() const{ return _isFloat; } + + bool operator ==(const ScriptType& b) const final{ + if (b.GetClass() != TypeClass::Number) + return false; + auto bNum = dynamic_cast(b); + if (bNum.IsAwareOfFloat() && IsAwareOfFloat()){ + return bNum.IsFloat() == IsFloat(); + } + return true; + }; + + bool operator !=(const ScriptType& b) const final{ + return ! (operator==(b)); + } + bool operator !=(const ScriptType* b) const final{ + return ! (operator==(*b)); + } + + [[nodiscard]] CastResult CastableTo(const shared_ptr& castType, bool explicitCast) const final{ + if (!explicitCast){ + if (castType->GetClass() != TypeClass::Number ) + return CastResult::Failure; + auto bNum = dynamic_pointer_cast(castType); + if (bNum->IsFloat() && !IsFloat()) return CastResult::Success; + if (!bNum->IsFloat() && IsFloat()) return CastResult::DataLoss; + } + return CastResult::Success; + } }; class StringScriptType : public ScriptType{ diff --git a/src/StandardLibraries/MathLibrary.hpp b/src/StandardLibraries/MathLibrary.hpp index 04210e9..b2863f9 100644 --- a/src/StandardLibraries/MathLibrary.hpp +++ b/src/StandardLibraries/MathLibrary.hpp @@ -165,15 +165,13 @@ namespace Porygon::StandardLibraries { HashedString::ConstHash("acos"), new UserData::UserDataField( (new GenericFunctionScriptType()) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE})) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE})) + ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE})) , FUNCTION(_acos)) }, { HashedString::ConstHash("asin"), new UserData::UserDataField( (new GenericFunctionScriptType()) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE})) ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE})) , FUNCTION(_asin)) }, @@ -181,11 +179,7 @@ namespace Porygon::StandardLibraries { HashedString::ConstHash("atan"), new UserData::UserDataField( (new GenericFunctionScriptType()) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE})) ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE})) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE, INTEGER_TYPE})) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE, FLOAT_TYPE})) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE, INTEGER_TYPE})) ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE, FLOAT_TYPE})) , FUNCTION(_atan)) }, @@ -193,14 +187,13 @@ namespace Porygon::StandardLibraries { HashedString::ConstHash("ceil"), new UserData::UserDataField( (new GenericFunctionScriptType()) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE})) + ->RegisterFunctionOption(0, CreateFunctionOption(INTEGER_TYPE, {FLOAT_TYPE})) , FUNCTION(_ceil)) }, { HashedString::ConstHash("cos"), new UserData::UserDataField( (new GenericFunctionScriptType()) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE})) ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE})) , FUNCTION(_cos)) }, @@ -208,7 +201,6 @@ namespace Porygon::StandardLibraries { HashedString::ConstHash("deg"), new UserData::UserDataField( (new GenericFunctionScriptType()) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE})) ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE})) , FUNCTION(_deg)) }, @@ -216,7 +208,6 @@ namespace Porygon::StandardLibraries { HashedString::ConstHash("exp"), new UserData::UserDataField( (new GenericFunctionScriptType()) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE})) ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE})) , FUNCTION(_exp)) }, @@ -224,16 +215,13 @@ namespace Porygon::StandardLibraries { HashedString::ConstHash("floor"), new UserData::UserDataField( (new GenericFunctionScriptType()) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE})) + ->RegisterFunctionOption(0, CreateFunctionOption(INTEGER_TYPE, {FLOAT_TYPE})) , FUNCTION(_floor)) }, { HashedString::ConstHash("fmod"), new UserData::UserDataField( (new GenericFunctionScriptType()) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE, INTEGER_TYPE})) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE, INTEGER_TYPE})) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE, FLOAT_TYPE})) ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE, FLOAT_TYPE})) , FUNCTION(_fmod)) }, @@ -247,11 +235,7 @@ namespace Porygon::StandardLibraries { HashedString::ConstHash("log"), new UserData::UserDataField( (new GenericFunctionScriptType()) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE})) ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE})) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE, INTEGER_TYPE})) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE, FLOAT_TYPE})) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE, INTEGER_TYPE})) ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE, FLOAT_TYPE})) , FUNCTION(_log)) }, @@ -277,7 +261,6 @@ namespace Porygon::StandardLibraries { HashedString::ConstHash("rad"), new UserData::UserDataField( (new GenericFunctionScriptType()) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE})) ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE})) , FUNCTION(_rad)) }, @@ -285,7 +268,6 @@ namespace Porygon::StandardLibraries { HashedString::ConstHash("sin"), new UserData::UserDataField( (new GenericFunctionScriptType()) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE})) ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE})) , FUNCTION(_sin)) }, @@ -293,7 +275,6 @@ namespace Porygon::StandardLibraries { HashedString::ConstHash("sqrt"), new UserData::UserDataField( (new GenericFunctionScriptType()) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE})) ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE})) , FUNCTION(_sqrt)) }, @@ -301,7 +282,6 @@ namespace Porygon::StandardLibraries { HashedString::ConstHash("tan"), new UserData::UserDataField( (new GenericFunctionScriptType()) - ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {INTEGER_TYPE})) ->RegisterFunctionOption(0, CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE})) , FUNCTION(_tan)) },