diff --git a/include/scratchcpp/dev/compiler.h b/include/scratchcpp/dev/compiler.h index 3b579023..d3700a88 100644 --- a/include/scratchcpp/dev/compiler.h +++ b/include/scratchcpp/dev/compiler.h @@ -61,6 +61,23 @@ class LIBSCRATCHCPP_EXPORT Compiler void createOr(); void createNot(); + void createMod(); + void createRound(); + void createAbs(); + void createFloor(); + void createCeil(); + void createSqrt(); + void createSin(); + void createCos(); + void createTan(); + void createAsin(); + void createAcos(); + void createAtan(); + void createLn(); + void createLog10(); + void createExp(); + void createExp10(); + void moveToIf(std::shared_ptr substack); void moveToIfElse(std::shared_ptr substack1, std::shared_ptr substack2); void moveToRepeatLoop(std::shared_ptr substack); diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp index 7ff6737e..1d036ba9 100644 --- a/src/dev/engine/compiler.cpp +++ b/src/dev/engine/compiler.cpp @@ -160,6 +160,102 @@ void Compiler::createNot() impl->builder->createNot(); } +/*! Creates a remainder operation using the last 2 values. */ +void Compiler::createMod() +{ + impl->builder->createMod(); +} + +/*! Creates a round operation using the last value. */ +void Compiler::createRound() +{ + impl->builder->createRound(); +} + +/*! Creates an abs operation using the last value. */ +void Compiler::createAbs() +{ + impl->builder->createAbs(); +} + +/*! Creates a floor operation using the last value. */ +void Compiler::createFloor() +{ + impl->builder->createFloor(); +} + +/*! Creates a ceiling operation using the last value. */ +void Compiler::createCeil() +{ + impl->builder->createCeil(); +} + +/*! Creates a square root operation using the last value. */ +void Compiler::createSqrt() +{ + impl->builder->createSqrt(); +} + +/*! Creates a sin operation using the last value. */ +void Compiler::createSin() +{ + impl->builder->createSin(); +} + +/*! Creates a cos operation using the last value. */ +void Compiler::createCos() +{ + impl->builder->createCos(); +} + +/*! Creates a tan operation using the last value. */ +void Compiler::createTan() +{ + impl->builder->createTan(); +} + +/*! Creates an asin operation using the last value. */ +void Compiler::createAsin() +{ + impl->builder->createAsin(); +} + +/*! Creates an acos operation using the last value. */ +void Compiler::createAcos() +{ + impl->builder->createAcos(); +} + +/*! Creates an atan operation using the last value. */ +void Compiler::createAtan() +{ + impl->builder->createAtan(); +} + +/*! Creates an ln operation using the last value. */ +void Compiler::createLn() +{ + impl->builder->createLn(); +} + +/*! Creates a log10 operation using the last value. */ +void Compiler::createLog10() +{ + impl->builder->createLog10(); +} + +/*! Creates an e^x operation using the last value. */ +void Compiler::createExp() +{ + impl->builder->createExp(); +} + +/*! Creates a 10^x operation using the last value. */ +void Compiler::createExp10() +{ + impl->builder->createExp10(); +} + /*! Jumps to the given if substack. */ void Compiler::moveToIf(std::shared_ptr substack) { diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h index f5852098..824471bf 100644 --- a/src/dev/engine/internal/icodebuilder.h +++ b/src/dev/engine/internal/icodebuilder.h @@ -37,6 +37,23 @@ class ICodeBuilder virtual void createOr() = 0; virtual void createNot() = 0; + virtual void createMod() = 0; + virtual void createRound() = 0; + virtual void createAbs() = 0; + virtual void createFloor() = 0; + virtual void createCeil() = 0; + virtual void createSqrt() = 0; + virtual void createSin() = 0; + virtual void createCos() = 0; + virtual void createTan() = 0; + virtual void createAsin() = 0; + virtual void createAcos() = 0; + virtual void createAtan() = 0; + virtual void createLn() = 0; + virtual void createLog10() = 0; + virtual void createExp() = 0; + virtual void createExp10() = 0; + virtual void beginIfStatement() = 0; virtual void beginElseBranch() = 0; virtual void endIf() = 0; diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp index 22f3d552..6d1c5872 100644 --- a/src/dev/engine/internal/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvmcodebuilder.cpp @@ -180,6 +180,220 @@ std::shared_ptr LLVMCodeBuilder::finalize() break; } + case Step::Type::Mod: { + assert(step.args.size() == 2); + const auto &arg1 = step.args[0]; + const auto &arg2 = step.args[1]; + // rem(a, b) / b < 0.0 ? rem(a, b) + b : rem(a, b) + llvm::Constant *zero = llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)); + llvm::Value *num1 = removeNaN(castValue(arg1.second, arg1.first)); + llvm::Value *num2 = removeNaN(castValue(arg2.second, arg2.first)); + llvm::Value *value = m_builder.CreateFRem(num1, num2); // rem(a, b) + llvm::Value *cond = m_builder.CreateFCmpOLT(m_builder.CreateFDiv(value, num2), zero); // rem(a, b) / b < 0.0 // rem(a, b) + step.functionReturnReg->value = m_builder.CreateSelect(cond, m_builder.CreateFAdd(value, num2), value); + break; + } + + case Step::Type::Round: { + assert(step.args.size() == 1); + const auto &arg = step.args[0]; + // x >= 0.0 ? round(x) : (x >= -0.5 ? -0.0 : floor(x + 0.5)) + llvm::Constant *zero = llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)); + llvm::Constant *negativeZero = llvm::ConstantFP::get(m_ctx, llvm::APFloat(-0.0)); + llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::round, m_builder.getDoubleTy()); + llvm::Function *floorFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::floor, m_builder.getDoubleTy()); + llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); + llvm::Value *notNegative = m_builder.CreateFCmpOGE(num, zero); // num >= 0.0 + llvm::Value *roundNum = m_builder.CreateCall(roundFunc, num); // round(num) + llvm::Value *negativeCond = m_builder.CreateFCmpOGE(num, llvm::ConstantFP::get(m_ctx, llvm::APFloat(-0.5))); // num >= -0.5 + llvm::Value *negativeRound = m_builder.CreateCall(floorFunc, m_builder.CreateFAdd(num, llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.5)))); // floor(x + 0.5) + step.functionReturnReg->value = m_builder.CreateSelect(notNegative, roundNum, m_builder.CreateSelect(negativeCond, negativeZero, negativeRound)); + break; + } + + case Step::Type::Abs: { + assert(step.args.size() == 1); + const auto &arg = step.args[0]; + llvm::Function *absFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::fabs, m_builder.getDoubleTy()); + llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); + step.functionReturnReg->value = m_builder.CreateCall(absFunc, num); + break; + } + + case Step::Type::Floor: { + assert(step.args.size() == 1); + const auto &arg = step.args[0]; + llvm::Function *floorFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::floor, m_builder.getDoubleTy()); + llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); + step.functionReturnReg->value = m_builder.CreateCall(floorFunc, num); + break; + } + + case Step::Type::Ceil: { + assert(step.args.size() == 1); + const auto &arg = step.args[0]; + llvm::Function *ceilFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::ceil, m_builder.getDoubleTy()); + llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); + step.functionReturnReg->value = m_builder.CreateCall(ceilFunc, num); + break; + } + + case Step::Type::Sqrt: { + assert(step.args.size() == 1); + const auto &arg = step.args[0]; + // sqrt(x) + 0.0 + // This avoids negative zero + llvm::Constant *zero = llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)); + llvm::Function *sqrtFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::sqrt, m_builder.getDoubleTy()); + llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); + step.functionReturnReg->value = m_builder.CreateFAdd(m_builder.CreateCall(sqrtFunc, num), zero); + break; + } + + case Step::Type::Sin: { + assert(step.args.size() == 1); + const auto &arg = step.args[0]; + // round(sin(x * pi / 180.0) * 1e10) / 1e10 + 0.0 + // +0.0 to avoid -0.0 + llvm::Constant *zero = llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)); + llvm::Constant *pi = llvm::ConstantFP::get(m_ctx, llvm::APFloat(std::acos(-1.0))); + llvm::Constant *piDeg = llvm::ConstantFP::get(m_ctx, llvm::APFloat(180.0)); + llvm::Constant *factor = llvm::ConstantFP::get(m_ctx, llvm::APFloat(1e10)); + llvm::Function *sinFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::sin, m_builder.getDoubleTy()); + llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::round, m_builder.getDoubleTy()); + llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); + llvm::Value *sinResult = m_builder.CreateCall(sinFunc, m_builder.CreateFDiv(m_builder.CreateFMul(num, pi), piDeg)); // sin(x * pi / 180) + llvm::Value *rounded = m_builder.CreateCall(roundFunc, m_builder.CreateFMul(sinResult, factor)); // round(sin(x * 180) * 1e10) + step.functionReturnReg->value = m_builder.CreateFAdd(m_builder.CreateFDiv(rounded, factor), zero); + break; + } + + case Step::Type::Cos: { + assert(step.args.size() == 1); + const auto &arg = step.args[0]; + // round(cos(x * pi / 180.0) * 1e10) / 1e10 + llvm::Constant *pi = llvm::ConstantFP::get(m_ctx, llvm::APFloat(std::acos(-1.0))); + llvm::Constant *piDeg = llvm::ConstantFP::get(m_ctx, llvm::APFloat(180.0)); + llvm::Constant *factor = llvm::ConstantFP::get(m_ctx, llvm::APFloat(1e10)); + llvm::Function *cosFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::cos, m_builder.getDoubleTy()); + llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::round, m_builder.getDoubleTy()); + llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); + llvm::Value *cosResult = m_builder.CreateCall(cosFunc, m_builder.CreateFDiv(m_builder.CreateFMul(num, pi), piDeg)); // cos(x * pi / 180) + llvm::Value *rounded = m_builder.CreateCall(roundFunc, m_builder.CreateFMul(cosResult, factor)); // round(cos(x * 180) * 1e10) + step.functionReturnReg->value = m_builder.CreateFDiv(rounded, factor); + break; + } + + case Step::Type::Tan: { + assert(step.args.size() == 1); + const auto &arg = step.args[0]; + // ((mod = rem(x, 360.0)) == -270.0 || mod == 90.0) ? inf : ((mod == -90.0 || mod == 270.0) ? -inf : round(tan(x * pi / 180.0) * 1e10) / 1e10 + 0.0) + // +0.0 to avoid -0.0 + llvm::Constant *zero = llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)); + llvm::Constant *full = llvm::ConstantFP::get(m_ctx, llvm::APFloat(360.0)); + llvm::Constant *posInf = llvm::ConstantFP::getInfinity(m_builder.getDoubleTy(), false); + llvm::Constant *negInf = llvm::ConstantFP::getInfinity(m_builder.getDoubleTy(), true); + llvm::Constant *undefined1 = llvm::ConstantFP::get(m_ctx, llvm::APFloat(-270.0)); + llvm::Constant *undefined2 = llvm::ConstantFP::get(m_ctx, llvm::APFloat(90.0)); + llvm::Constant *undefined3 = llvm::ConstantFP::get(m_ctx, llvm::APFloat(-90.0)); + llvm::Constant *undefined4 = llvm::ConstantFP::get(m_ctx, llvm::APFloat(270.0)); + llvm::Constant *pi = llvm::ConstantFP::get(m_ctx, llvm::APFloat(std::acos(-1.0))); + llvm::Constant *piDeg = llvm::ConstantFP::get(m_ctx, llvm::APFloat(180.0)); + llvm::Constant *factor = llvm::ConstantFP::get(m_ctx, llvm::APFloat(1e10)); + llvm::Function *tanFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::tan, m_builder.getDoubleTy()); + llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::round, m_builder.getDoubleTy()); + llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); + llvm::Value *mod = m_builder.CreateFRem(num, full); + llvm::Value *isUndefined1 = m_builder.CreateFCmpOEQ(mod, undefined1); // rem(x, 360.0) == -270.0 + llvm::Value *isUndefined2 = m_builder.CreateFCmpOEQ(mod, undefined2); // rem(x, 360.0) == 90.0 + llvm::Value *isUndefined3 = m_builder.CreateFCmpOEQ(mod, undefined3); // rem(x, 360.0) == -90.0 + llvm::Value *isUndefined4 = m_builder.CreateFCmpOEQ(mod, undefined4); // rem(x, 360.0) == 270.0 + llvm::Value *tanResult = m_builder.CreateCall(tanFunc, m_builder.CreateFDiv(m_builder.CreateFMul(num, pi), piDeg)); // tan(x * pi / 180) + llvm::Value *rounded = m_builder.CreateCall(roundFunc, m_builder.CreateFMul(tanResult, factor)); // round(tan(x * 180) * 1e10) + llvm::Value *result = m_builder.CreateFAdd(m_builder.CreateFDiv(rounded, factor), zero); // round(tan(x * pi / 180.0) * 1e10) / 1e10 + 0.0 + llvm::Value *inner = m_builder.CreateSelect(m_builder.CreateOr(isUndefined3, isUndefined4), negInf, result); + step.functionReturnReg->value = m_builder.CreateSelect(m_builder.CreateOr(isUndefined1, isUndefined2), posInf, inner); + break; + } + + case Step::Type::Asin: { + assert(step.args.size() == 1); + const auto &arg = step.args[0]; + // asin(x) * 180.0 / pi + 0.0 + // +0.0 to avoid -0.0 + llvm::Constant *zero = llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)); + llvm::Constant *pi = llvm::ConstantFP::get(m_ctx, llvm::APFloat(std::acos(-1.0))); + llvm::Constant *piDeg = llvm::ConstantFP::get(m_ctx, llvm::APFloat(180.0)); + llvm::Function *asinFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::asin, m_builder.getDoubleTy()); + llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); + step.functionReturnReg->value = m_builder.CreateFAdd(m_builder.CreateFDiv(m_builder.CreateFMul(m_builder.CreateCall(asinFunc, num), piDeg), pi), zero); + break; + } + + case Step::Type::Acos: { + assert(step.args.size() == 1); + const auto &arg = step.args[0]; + // acos(x) * 180.0 / pi + llvm::Constant *pi = llvm::ConstantFP::get(m_ctx, llvm::APFloat(std::acos(-1.0))); + llvm::Constant *piDeg = llvm::ConstantFP::get(m_ctx, llvm::APFloat(180.0)); + llvm::Function *acosFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::acos, m_builder.getDoubleTy()); + llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); + step.functionReturnReg->value = m_builder.CreateFDiv(m_builder.CreateFMul(m_builder.CreateCall(acosFunc, num), piDeg), pi); + break; + } + + case Step::Type::Atan: { + assert(step.args.size() == 1); + const auto &arg = step.args[0]; + // atan(x) * 180.0 / pi + llvm::Constant *pi = llvm::ConstantFP::get(m_ctx, llvm::APFloat(std::acos(-1.0))); + llvm::Constant *piDeg = llvm::ConstantFP::get(m_ctx, llvm::APFloat(180.0)); + llvm::Function *atanFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::atan, m_builder.getDoubleTy()); + llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); + step.functionReturnReg->value = m_builder.CreateFDiv(m_builder.CreateFMul(m_builder.CreateCall(atanFunc, num), piDeg), pi); + break; + } + + case Step::Type::Ln: { + assert(step.args.size() == 1); + const auto &arg = step.args[0]; + // log(x) + llvm::Function *logFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::log, m_builder.getDoubleTy()); + llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); + step.functionReturnReg->value = m_builder.CreateCall(logFunc, num); + break; + } + + case Step::Type::Log10: { + assert(step.args.size() == 1); + const auto &arg = step.args[0]; + // log10(x) + llvm::Function *log10Func = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::log10, m_builder.getDoubleTy()); + llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); + step.functionReturnReg->value = m_builder.CreateCall(log10Func, num); + break; + } + + case Step::Type::Exp: { + assert(step.args.size() == 1); + const auto &arg = step.args[0]; + // exp(x) + llvm::Function *expFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::exp, m_builder.getDoubleTy()); + llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); + step.functionReturnReg->value = m_builder.CreateCall(expFunc, num); + break; + } + + case Step::Type::Exp10: { + assert(step.args.size() == 1); + const auto &arg = step.args[0]; + // exp10(x) + llvm::Function *expFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::exp10, m_builder.getDoubleTy()); + llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); + step.functionReturnReg->value = m_builder.CreateCall(expFunc, num); + break; + } + case Step::Type::Yield: if (!m_warp) { freeHeap(); @@ -548,6 +762,86 @@ void LLVMCodeBuilder::createNot() createOp(Step::Type::Not, Compiler::StaticType::Bool, Compiler::StaticType::Bool, 1); } +void LLVMCodeBuilder::createMod() +{ + createOp(Step::Type::Mod, Compiler::StaticType::Number, Compiler::StaticType::Number, 2); +} + +void LLVMCodeBuilder::createRound() +{ + createOp(Step::Type::Round, Compiler::StaticType::Number, Compiler::StaticType::Number, 1); +} + +void LLVMCodeBuilder::createAbs() +{ + createOp(Step::Type::Abs, Compiler::StaticType::Number, Compiler::StaticType::Number, 1); +} + +void LLVMCodeBuilder::createFloor() +{ + createOp(Step::Type::Floor, Compiler::StaticType::Number, Compiler::StaticType::Number, 1); +} + +void LLVMCodeBuilder::createCeil() +{ + createOp(Step::Type::Ceil, Compiler::StaticType::Number, Compiler::StaticType::Number, 1); +} + +void LLVMCodeBuilder::createSqrt() +{ + createOp(Step::Type::Sqrt, Compiler::StaticType::Number, Compiler::StaticType::Number, 1); +} + +void LLVMCodeBuilder::createSin() +{ + createOp(Step::Type::Sin, Compiler::StaticType::Number, Compiler::StaticType::Number, 1); +} + +void LLVMCodeBuilder::createCos() +{ + createOp(Step::Type::Cos, Compiler::StaticType::Number, Compiler::StaticType::Number, 1); +} + +void LLVMCodeBuilder::createTan() +{ + createOp(Step::Type::Tan, Compiler::StaticType::Number, Compiler::StaticType::Number, 1); +} + +void LLVMCodeBuilder::createAsin() +{ + createOp(Step::Type::Asin, Compiler::StaticType::Number, Compiler::StaticType::Number, 1); +} + +void LLVMCodeBuilder::createAcos() +{ + createOp(Step::Type::Acos, Compiler::StaticType::Number, Compiler::StaticType::Number, 1); +} + +void LLVMCodeBuilder::createAtan() +{ + createOp(Step::Type::Atan, Compiler::StaticType::Number, Compiler::StaticType::Number, 1); +} + +void LLVMCodeBuilder::createLn() +{ + createOp(Step::Type::Ln, Compiler::StaticType::Number, Compiler::StaticType::Number, 1); +} + +void LLVMCodeBuilder::createLog10() +{ + createOp(Step::Type::Log10, Compiler::StaticType::Number, Compiler::StaticType::Number, 1); +} + +void LLVMCodeBuilder::createExp() +{ + createOp(Step::Type::Exp, Compiler::StaticType::Number, Compiler::StaticType::Number, 1); +} + +void LLVMCodeBuilder::createExp10() +{ + createOp(Step::Type::Exp10, Compiler::StaticType::Number, Compiler::StaticType::Number, 1); +} + void LLVMCodeBuilder::beginIfStatement() { Step step(Step::Type::BeginIf); diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h index 5fb17df5..9401e048 100644 --- a/src/dev/engine/internal/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvmcodebuilder.h @@ -39,6 +39,23 @@ class LLVMCodeBuilder : public ICodeBuilder void createOr() override; void createNot() override; + void createMod() override; + void createRound() override; + void createAbs() override; + void createFloor() override; + void createCeil() override; + void createSqrt() override; + void createSin() override; + void createCos() override; + void createTan() override; + void createAsin() override; + void createAcos() override; + void createAtan() override; + void createLn() override; + void createLog10() override; + void createExp() override; + void createExp10() override; + void beginIfStatement() override; void beginElseBranch() override; void endIf() override; @@ -81,6 +98,22 @@ class LLVMCodeBuilder : public ICodeBuilder And, Or, Not, + Mod, + Round, + Abs, + Floor, + Ceil, + Sqrt, + Sin, + Cos, + Tan, + Asin, + Acos, + Atan, + Ln, + Log10, + Exp, + Exp10, Yield, BeginIf, BeginElse, diff --git a/src/scratch/value_functions.cpp b/src/scratch/value_functions.cpp index b35092d0..710facd5 100644 --- a/src/scratch/value_functions.cpp +++ b/src/scratch/value_functions.cpp @@ -354,17 +354,9 @@ extern "C" /*! Calculates the modulo the given values and writes the result to dst. */ void value_mod(const libscratchcpp::ValueData *v1, const libscratchcpp::ValueData *v2, ValueData *dst) { - double a = value_toDouble(v1); - double b = value_toDouble(v2); - - if ((b == 0) || std::isinf(a)) - value_assign_double(dst, std::numeric_limits::quiet_NaN()); - else if (std::isinf(b)) - value_assign_double(dst, value_toDouble(v1)); - else if (value_isNegative(v1) || value_isNegative(v2)) - value_assign_double(dst, fmod(value_toDouble(v2) + fmod(value_toDouble(v1), -value_toDouble(v2)), value_toDouble(v2))); - else - value_assign_double(dst, fmod(value_toDouble(v1), value_toDouble(v2))); + const double a = value_toDouble(v1); + const double b = value_toDouble(v2); + value_assign_double(dst, fmod(a, b) / b < 0.0 ? fmod(a, b) + b : fmod(a, b)); } /* comparison */ diff --git a/src/scratch/value_functions_p.h b/src/scratch/value_functions_p.h index ca497e99..01958ab5 100644 --- a/src/scratch/value_functions_p.h +++ b/src/scratch/value_functions_p.h @@ -50,7 +50,7 @@ extern "C" return v < 0 && std::isinf(v); } - inline long value_convert_int_str(const char *str, int n, bool *ok) + inline double value_convert_int_str(const char *str, int n, bool *ok) { if (ok) *ok = false; @@ -80,7 +80,7 @@ extern "C" *ok = true; if (isNegative) - return -ret; + return -static_cast(ret); // for negative zero else return ret; } diff --git a/test/dev/compiler/compiler_test.cpp b/test/dev/compiler/compiler_test.cpp index 0ebe69c5..fc59fb86 100644 --- a/test/dev/compiler/compiler_test.cpp +++ b/test/dev/compiler/compiler_test.cpp @@ -349,6 +349,214 @@ TEST_F(CompilerTest, CreateNot) compile(compiler, block); } +TEST_F(CompilerTest, CreateMod) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createMod); + compiler->createMod(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateRound) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createRound); + compiler->createRound(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateAbs) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createAbs); + compiler->createAbs(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateFloor) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createFloor); + compiler->createFloor(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateCeil) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createCeil); + compiler->createCeil(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateSqrt) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createSqrt); + compiler->createSqrt(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateSin) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createSin); + compiler->createSin(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateCos) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createCos); + compiler->createCos(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateTan) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createTan); + compiler->createTan(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateAsin) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createAsin); + compiler->createAsin(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateAcos) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createAcos); + compiler->createAcos(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateAtan) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createAtan); + compiler->createAtan(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateLn) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createLn); + compiler->createLn(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateLog10) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createLog10); + compiler->createLog10(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateExp) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createExp); + compiler->createExp(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateExp10) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createExp10); + compiler->createExp10(); + }); + + compile(compiler, block); +} + TEST_F(CompilerTest, MoveToIf) { Compiler compiler(&m_engine, &m_target); diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index 6715cc0e..7c357278 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -226,7 +226,7 @@ TEST_F(LLVMCodeBuilderTest, Add) { std::string expected; - auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) { + auto addOpTest = [this, &expected](Value v1, Value v2) { createBuilder(true); m_builder->addConstValue(v1); @@ -241,7 +241,7 @@ TEST_F(LLVMCodeBuilderTest, Add) m_builder->createAdd(); m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); - std::string str = Value(expectedResult).toString() + '\n'; + std::string str = (v1 + v2).toString() + '\n'; std::string expected = str + str; auto code = m_builder->finalize(); @@ -254,25 +254,25 @@ TEST_F(LLVMCodeBuilderTest, Add) ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; }; - addOpTest(50, 25, 75); - addOpTest(-500, 25, -475); - addOpTest(-500, -25, -525); - addOpTest("2.54", "6.28", 8.82); - addOpTest(2.54, "-6.28", -3.74); - addOpTest(true, true, 2); - addOpTest("Infinity", "Infinity", std::numeric_limits::infinity()); - addOpTest("Infinity", "-Infinity", std::numeric_limits::quiet_NaN()); - addOpTest("-Infinity", "Infinity", std::numeric_limits::quiet_NaN()); - addOpTest("-Infinity", "-Infinity", -std::numeric_limits::infinity()); - addOpTest(1, "NaN", 1); - addOpTest("NaN", 1, 1); + addOpTest(50, 25); + addOpTest(-500, 25); + addOpTest(-500, -25); + addOpTest("2.54", "6.28"); + addOpTest(2.54, "-6.28"); + addOpTest(true, true); + addOpTest("Infinity", "Infinity"); + addOpTest("Infinity", "-Infinity"); + addOpTest("-Infinity", "Infinity"); + addOpTest("-Infinity", "-Infinity"); + addOpTest(1, "NaN"); + addOpTest("NaN", 1); } TEST_F(LLVMCodeBuilderTest, Subtract) { std::string expected; - auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) { + auto addOpTest = [this, &expected](Value v1, Value v2) { createBuilder(true); m_builder->addConstValue(v1); @@ -287,7 +287,7 @@ TEST_F(LLVMCodeBuilderTest, Subtract) m_builder->createSub(); m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); - std::string str = Value(expectedResult).toString() + '\n'; + std::string str = (v1 - v2).toString() + '\n'; std::string expected = str + str; auto code = m_builder->finalize(); @@ -300,25 +300,25 @@ TEST_F(LLVMCodeBuilderTest, Subtract) ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; }; - addOpTest(50, 25, 25); - addOpTest(-500, 25, -525); - addOpTest(-500, -25, -475); - addOpTest("2.54", "6.28", -3.74); - addOpTest(2.54, "-6.28", 8.82); - addOpTest(true, true, 0); - addOpTest("Infinity", "Infinity", std::numeric_limits::quiet_NaN()); - addOpTest("Infinity", "-Infinity", std::numeric_limits::infinity()); - addOpTest("-Infinity", "Infinity", -std::numeric_limits::infinity()); - addOpTest("-Infinity", "-Infinity", std::numeric_limits::quiet_NaN()); - addOpTest(1, "NaN", 1); - addOpTest("NaN", 1, -1); + addOpTest(50, 25); + addOpTest(-500, 25); + addOpTest(-500, -25); + addOpTest("2.54", "6.28"); + addOpTest(2.54, "-6.28"); + addOpTest(true, true); + addOpTest("Infinity", "Infinity"); + addOpTest("Infinity", "-Infinity"); + addOpTest("-Infinity", "Infinity"); + addOpTest("-Infinity", "-Infinity"); + addOpTest(1, "NaN"); + addOpTest("NaN", 1); } TEST_F(LLVMCodeBuilderTest, Multiply) { std::string expected; - auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) { + auto addOpTest = [this, &expected](Value v1, Value v2) { createBuilder(true); m_builder->addConstValue(v1); @@ -333,7 +333,7 @@ TEST_F(LLVMCodeBuilderTest, Multiply) m_builder->createMul(); m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); - std::string str = Value(expectedResult).toString() + '\n'; + std::string str = (v1 * v2).toString() + '\n'; std::string expected = str + str; auto code = m_builder->finalize(); @@ -346,30 +346,30 @@ TEST_F(LLVMCodeBuilderTest, Multiply) ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; }; - addOpTest(50, 2, 100); - addOpTest(-500, 25, -12500); - addOpTest("-500", -25, 12500); - addOpTest("2.54", "6.28", 15.9512); - addOpTest(true, true, 1); - addOpTest("Infinity", "Infinity", std::numeric_limits::infinity()); - addOpTest("Infinity", 0, std::numeric_limits::quiet_NaN()); - addOpTest("Infinity", 2, std::numeric_limits::infinity()); - addOpTest("Infinity", -2, -std::numeric_limits::infinity()); - addOpTest("Infinity", "-Infinity", -std::numeric_limits::infinity()); - addOpTest("-Infinity", "Infinity", -std::numeric_limits::infinity()); - addOpTest("-Infinity", 0, std::numeric_limits::quiet_NaN()); - addOpTest("-Infinity", 2, -std::numeric_limits::infinity()); - addOpTest("-Infinity", -2, std::numeric_limits::infinity()); - addOpTest("-Infinity", "-Infinity", std::numeric_limits::infinity()); - addOpTest(1, "NaN", 0); - addOpTest("NaN", 1, 0); + addOpTest(50, 2); + addOpTest(-500, 25); + addOpTest("-500", -25); + addOpTest("2.54", "6.28"); + addOpTest(true, true); + addOpTest("Infinity", "Infinity"); + addOpTest("Infinity", 0); + addOpTest("Infinity", 2); + addOpTest("Infinity", -2); + addOpTest("Infinity", "-Infinity"); + addOpTest("-Infinity", "Infinity"); + addOpTest("-Infinity", 0); + addOpTest("-Infinity", 2); + addOpTest("-Infinity", -2); + addOpTest("-Infinity", "-Infinity"); + addOpTest(1, "NaN"); + addOpTest("NaN", 1); } TEST_F(LLVMCodeBuilderTest, Divide) { std::string expected; - auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) { + auto addOpTest = [this, &expected](Value v1, Value v2) { createBuilder(true); m_builder->addConstValue(v1); @@ -384,7 +384,7 @@ TEST_F(LLVMCodeBuilderTest, Divide) m_builder->createDiv(); m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); - std::string str = Value(expectedResult).toString() + '\n'; + std::string str = (v1 / v2).toString() + '\n'; std::string expected = str + str; auto code = m_builder->finalize(); @@ -397,32 +397,32 @@ TEST_F(LLVMCodeBuilderTest, Divide) ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; }; - addOpTest(50, 2, 25); - addOpTest(-500, 25, -20); - addOpTest("-500", -25, 20); - addOpTest("3.5", "2.5", 1.4); - addOpTest(true, true, 1); - addOpTest("Infinity", "Infinity", std::numeric_limits::quiet_NaN()); - addOpTest("Infinity", 0, std::numeric_limits::infinity()); - addOpTest("Infinity", 2, std::numeric_limits::infinity()); - addOpTest("Infinity", -2, -std::numeric_limits::infinity()); - addOpTest("Infinity", "-Infinity", std::numeric_limits::quiet_NaN()); - addOpTest("-Infinity", "Infinity", std::numeric_limits::quiet_NaN()); - addOpTest("-Infinity", 0, -std::numeric_limits::infinity()); - addOpTest("-Infinity", 2, -std::numeric_limits::infinity()); - addOpTest("-Infinity", -2, std::numeric_limits::infinity()); - addOpTest("-Infinity", "-Infinity", std::numeric_limits::quiet_NaN()); - addOpTest(0, "Infinity", 0); - addOpTest(2, "Infinity", 0); - addOpTest(-2, "Infinity", 0); - addOpTest(0, "-Infinity", 0); - addOpTest(2, "-Infinity", 0); - addOpTest(-2, "-Infinity", 0); - addOpTest(1, "NaN", std::numeric_limits::infinity()); - addOpTest("NaN", 1, 0); - addOpTest(5, 0, std::numeric_limits::infinity()); - addOpTest(-5, 0, -std::numeric_limits::infinity()); - addOpTest(0, 0, std::numeric_limits::quiet_NaN()); + addOpTest(50, 2); + addOpTest(-500, 25); + addOpTest("-500", -25); + addOpTest("3.5", "2.5"); + addOpTest(true, true); + addOpTest("Infinity", "Infinity"); + addOpTest("Infinity", 0); + addOpTest("Infinity", 2); + addOpTest("Infinity", -2); + addOpTest("Infinity", "-Infinity"); + addOpTest("-Infinity", "Infinity"); + addOpTest("-Infinity", 0); + addOpTest("-Infinity", 2); + addOpTest("-Infinity", -2); + addOpTest("-Infinity", "-Infinity"); + addOpTest(0, "Infinity"); + addOpTest(2, "Infinity"); + addOpTest(-2, "Infinity"); + addOpTest(0, "-Infinity"); + addOpTest(2, "-Infinity"); + addOpTest(-2, "-Infinity"); + addOpTest(1, "NaN"); + addOpTest("NaN", 1); + addOpTest(5, 0); + addOpTest(-5, 0); + addOpTest(0, 0); } TEST_F(LLVMCodeBuilderTest, EqualComparison) @@ -1024,6 +1024,759 @@ TEST_F(LLVMCodeBuilderTest, Not) addOpTest("nan"); } +TEST_F(LLVMCodeBuilderTest, Mod) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, Value v2) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->addConstValue(v2); + m_builder->createMod(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->addConstValue(v2); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createMod(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + std::string str = (v1 % v2).toString() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + const std::string quotes2 = v2.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; + }; + + addOpTest(4, 3); + addOpTest(3, 3); + addOpTest(2, 3); + addOpTest(1, 3); + addOpTest(0, 3); + addOpTest(-1, 3); + addOpTest(-2, 3); + addOpTest(-3, 3); + addOpTest(-4, 3); + addOpTest(4.75, 2); + addOpTest(-4.75, 2); + addOpTest(-4.75, -2); + addOpTest(4.75, -2); + addOpTest(5, 0); + addOpTest(-5, 0); + addOpTest(-2.5, "Infinity"); + addOpTest(-1.2, "-Infinity"); + addOpTest(2.5, "Infinity"); + addOpTest(1.2, "-Infinity"); + addOpTest("Infinity", 2); + addOpTest("-Infinity", 2); + addOpTest("Infinity", -2); + addOpTest("-Infinity", -2); + addOpTest(3, "NaN"); + addOpTest(-3, "NaN"); + addOpTest("NaN", 5); + addOpTest("NaN", -5); +} + +TEST_F(LLVMCodeBuilderTest, Round) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, double expectedResult) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->createRound(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createRound(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + std::stringstream stream; + stream << expectedResult; + std::string str = stream.str() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1; + }; + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(4.0, 4.0); + addOpTest(3.2, 3.0); + addOpTest(3.5, 4.0); + addOpTest(3.6, 4.0); + addOpTest(-2.4, -2.0); + addOpTest(-2.5, -2.0); + addOpTest(-2.6, -3.0); + addOpTest(-0.4, -0.0); + addOpTest(-0.5, -0.0); + addOpTest(-0.51, -1.0); + addOpTest(inf, inf); + addOpTest(-inf, -inf); + addOpTest(nan, 0); +} + +TEST_F(LLVMCodeBuilderTest, Abs) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, double expectedResult) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->createAbs(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createAbs(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + std::stringstream stream; + stream << expectedResult; + std::string str = stream.str() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1; + }; + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(4.0, 4.0); + addOpTest(3.2, 3.2); + addOpTest(-2.0, 2.0); + addOpTest(-2.5, 2.5); + addOpTest(-2.6, 2.6); + addOpTest(0.0, 0.0); + addOpTest(-0.0, 0.0); + addOpTest(inf, inf); + addOpTest(-inf, inf); + addOpTest(nan, 0); +} + +TEST_F(LLVMCodeBuilderTest, Floor) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, double expectedResult) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->createFloor(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createFloor(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + std::stringstream stream; + stream << expectedResult; + std::string str = stream.str() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1; + }; + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(4.0, 4.0); + addOpTest(3.2, 3.0); + addOpTest(3.5, 3.0); + addOpTest(3.6, 3.0); + addOpTest(0.0, 0.0); + addOpTest(-0.0, -0.0); + addOpTest(-2.4, -3.0); + addOpTest(-2.5, -3.0); + addOpTest(-2.6, -3.0); + addOpTest(-0.4, -1.0); + addOpTest(-0.5, -1.0); + addOpTest(-0.51, -1.0); + addOpTest(inf, inf); + addOpTest(-inf, -inf); + addOpTest(nan, 0); +} + +TEST_F(LLVMCodeBuilderTest, Ceil) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, double expectedResult) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->createCeil(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createCeil(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + std::stringstream stream; + stream << expectedResult; + std::string str = stream.str() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1; + }; + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(8.0, 8.0); + addOpTest(3.2, 4.0); + addOpTest(3.5, 4.0); + addOpTest(3.6, 4.0); + addOpTest(0.4, 1.0); + addOpTest(0.0, 0.0); + addOpTest(-0.0, -0.0); + addOpTest(-2.4, -2.0); + addOpTest(-2.5, -2.0); + addOpTest(-2.6, -2.0); + addOpTest(-0.4, -0.0); + addOpTest(-0.5, -0.0); + addOpTest(-0.51, -0.0); + addOpTest(inf, inf); + addOpTest(-inf, -inf); + addOpTest(nan, 0); +} + +TEST_F(LLVMCodeBuilderTest, Sqrt) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, double expectedResult) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->createSqrt(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createSqrt(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + std::stringstream stream; + stream << expectedResult; + std::string str = stream.str() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1; + }; + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(16.0, 4.0); + addOpTest(0.04, 0.2); + addOpTest(0.0, 0.0); + addOpTest(-0.0, 0.0); + addOpTest(-4.0, -nan); // negative NaN shouldn't be a problem + addOpTest(inf, inf); + addOpTest(-inf, -nan); + addOpTest(nan, 0); +} + +TEST_F(LLVMCodeBuilderTest, Sin) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, double expectedResult) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->createSin(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createSin(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + std::stringstream stream; + stream << expectedResult; + std::string str = stream.str() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1; + }; + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(30.0, 0.5); + addOpTest(90.0, 1.0); + addOpTest(2.8e-9, 0.0); + addOpTest(2.9e-9, 1e-10); + addOpTest(570.0, -0.5); + addOpTest(-30.0, -0.5); + addOpTest(-90.0, -1.0); + addOpTest(0.0, 0.0); + addOpTest(-0.0, 0.0); + addOpTest(inf, -nan); // negative NaN shouldn't be a problem + addOpTest(-inf, -nan); + addOpTest(nan, 0); +} + +TEST_F(LLVMCodeBuilderTest, Cos) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, double expectedResult) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->createCos(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createCos(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + std::stringstream stream; + stream << expectedResult; + std::string str = stream.str() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1; + }; + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(60.0, 0.5); + addOpTest(90.0, 0.0); + addOpTest(600.0, -0.5); + addOpTest(89.9999999971352, 1e-10); + addOpTest(89.999999999, 0.0); + addOpTest(-60.0, 0.5); + addOpTest(-90.0, 0.0); + addOpTest(0.0, 1.0); + addOpTest(-0.0, 1.0); + addOpTest(inf, -nan); // negative NaN shouldn't be a problem + addOpTest(-inf, -nan); + addOpTest(nan, 1.0); +} + +TEST_F(LLVMCodeBuilderTest, Tan) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, double expectedResult) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->createTan(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createTan(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + std::stringstream stream; + stream << expectedResult; + std::string str = stream.str() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1; + }; + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(45.0, 1.0); + addOpTest(90.0, inf); + addOpTest(270.0, -inf); + addOpTest(450.0, inf); + addOpTest(-90.0, -inf); + addOpTest(-270.0, inf); + addOpTest(-450.0, -inf); + addOpTest(180.0, 0.0); + addOpTest(-180.0, 0.0); + addOpTest(2.87e-9, 1e-10); + addOpTest(2.8647e-9, 0.0); + addOpTest(0.0, 0.0); + addOpTest(-0.0, 0.0); + addOpTest(inf, -nan); // negative NaN shouldn't be a problem + addOpTest(-inf, -nan); + addOpTest(nan, 0.0); +} + +TEST_F(LLVMCodeBuilderTest, Asin) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, double expectedResult) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->createAsin(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createAsin(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + std::stringstream stream; + stream << expectedResult; + std::string str = stream.str() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1; + }; + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(1.0, 90.0); + addOpTest(0.5, 30.0); + addOpTest(0.0, 0.0); + addOpTest(-0.0, 0.0); + addOpTest(-0.5, -30.0); + addOpTest(-1.0, -90.0); + addOpTest(1.1, nan); + addOpTest(-1.2, nan); + addOpTest(inf, nan); + addOpTest(-inf, nan); + addOpTest(nan, 0.0); +} + +TEST_F(LLVMCodeBuilderTest, Acos) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, double expectedResult) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->createAcos(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createAcos(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + std::stringstream stream; + stream << expectedResult; + std::string str = stream.str() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1; + }; + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(1.0, 0.0); + addOpTest(0.5, 60.0); + addOpTest(0.0, 90.0); + addOpTest(-0.0, 90.0); + addOpTest(-0.5, 120.0); + addOpTest(-1.0, 180.0); + addOpTest(1.1, nan); + addOpTest(-1.2, nan); + addOpTest(inf, nan); + addOpTest(-inf, nan); + addOpTest(nan, 90.0); +} + +TEST_F(LLVMCodeBuilderTest, Atan) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, double expectedResult) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->createAtan(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createAtan(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + std::stringstream stream; + stream << expectedResult; + std::string str = stream.str() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1; + }; + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(1.0, 45.0); + addOpTest(0.0, 0.0); + addOpTest(-0.0, -0.0); + addOpTest(-1.0, -45.0); + addOpTest(inf, 90.0); + addOpTest(-inf, -90.0); + addOpTest(nan, 0.0); +} + +TEST_F(LLVMCodeBuilderTest, Ln) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, double expectedResult) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->createLn(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createLn(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + std::stringstream stream; + stream << expectedResult; + std::string str = stream.str() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1; + }; + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(std::exp(1.0), 1.0); + addOpTest(std::exp(2.0), 2.0); + addOpTest(std::exp(0.3), 0.3); + addOpTest(1.0, 0.0); + addOpTest(0.0, -inf); + addOpTest(-0.0, -inf); + addOpTest(-0.7, -nan); // negative NaN shouldn't be a problem + addOpTest(inf, inf); + addOpTest(-inf, -nan); + addOpTest(nan, -inf); +} + +TEST_F(LLVMCodeBuilderTest, Log10) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, double expectedResult) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->createLog10(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createLog10(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + std::stringstream stream; + stream << expectedResult; + std::string str = stream.str() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1; + }; + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(10.0, 1.0); + addOpTest(1000.0, 3.0); + addOpTest(0.01, -2.0); + addOpTest(0.0, -inf); + addOpTest(-0.0, -inf); + addOpTest(-0.7, nan); + addOpTest(inf, inf); + addOpTest(-inf, nan); + addOpTest(nan, -inf); +} + +TEST_F(LLVMCodeBuilderTest, Exp) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, double expectedResult) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->createExp(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createExp(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + std::stringstream stream; + stream << expectedResult; + std::string str = stream.str() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1; + }; + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(1.0, std::exp(1.0)); + addOpTest(0.5, std::exp(0.5)); + addOpTest(0.0, 1.0); + addOpTest(-0.0, 1.0); + addOpTest(-0.7, std::exp(-0.7)); + addOpTest(inf, inf); + addOpTest(-inf, 0.0); + addOpTest(nan, 1.0); +} + +TEST_F(LLVMCodeBuilderTest, Exp10) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, double expectedResult) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->createExp10(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createExp10(); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }); + + std::stringstream stream; + stream << expectedResult; + std::string str = stream.str() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1; + }; + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(1.0, 10.0); + addOpTest(3.0, 1000.0); + addOpTest(0.0, 1.0); + addOpTest(-0.0, 1.0); + addOpTest(-1.0, 0.1); + addOpTest(-5.0, 0.00001); + addOpTest(inf, inf); + addOpTest(-inf, 0.0); + addOpTest(nan, 1.0); +} + TEST_F(LLVMCodeBuilderTest, Yield) { auto build = [this]() { diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h index 3271451d..3de5cf4c 100644 --- a/test/mocks/codebuildermock.h +++ b/test/mocks/codebuildermock.h @@ -27,6 +27,23 @@ class CodeBuilderMock : public ICodeBuilder MOCK_METHOD(void, createOr, (), (override)); MOCK_METHOD(void, createNot, (), (override)); + MOCK_METHOD(void, createMod, (), (override)); + MOCK_METHOD(void, createRound, (), (override)); + MOCK_METHOD(void, createAbs, (), (override)); + MOCK_METHOD(void, createFloor, (), (override)); + MOCK_METHOD(void, createCeil, (), (override)); + MOCK_METHOD(void, createSqrt, (), (override)); + MOCK_METHOD(void, createSin, (), (override)); + MOCK_METHOD(void, createCos, (), (override)); + MOCK_METHOD(void, createTan, (), (override)); + MOCK_METHOD(void, createAsin, (), (override)); + MOCK_METHOD(void, createAcos, (), (override)); + MOCK_METHOD(void, createAtan, (), (override)); + MOCK_METHOD(void, createLn, (), (override)); + MOCK_METHOD(void, createLog10, (), (override)); + MOCK_METHOD(void, createExp, (), (override)); + MOCK_METHOD(void, createExp10, (), (override)); + MOCK_METHOD(void, beginIfStatement, (), (override)); MOCK_METHOD(void, beginElseBranch, (), (override)); MOCK_METHOD(void, endIf, (), (override)); diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp index 359bc946..04cce470 100644 --- a/test/scratch_classes/value_test.cpp +++ b/test/scratch_classes/value_test.cpp @@ -938,6 +938,15 @@ TEST(ValueTest, ToDouble) v = "-0.15"; ASSERT_EQ(v.toDouble(), -0.15); + v = "0"; + ASSERT_EQ(v.toDouble(), 0.0); + v = "-0"; + ASSERT_EQ(v.toDouble(), -0.0); + v = "0.0"; + ASSERT_EQ(v.toDouble(), 0.0); + v = "-0.0"; + ASSERT_EQ(v.toDouble(), -0.0); + v = "+.15"; ASSERT_EQ(v.toDouble(), 0.15); v = ".15"; @@ -2879,6 +2888,11 @@ TEST(ValueTest, StringToDouble) ASSERT_EQ(value_stringToDouble("0.15"), 0.15); ASSERT_EQ(value_stringToDouble("-0.15"), -0.15); + ASSERT_EQ(value_stringToDouble("0"), 0.0); + ASSERT_EQ(value_stringToDouble("-0"), -0.0); + ASSERT_EQ(value_stringToDouble("0.0"), 0.0); + ASSERT_EQ(value_stringToDouble("-0.0"), -0.0); + ASSERT_EQ(value_stringToDouble("+.15"), 0.15); ASSERT_EQ(value_stringToDouble(".15"), 0.15); ASSERT_EQ(value_stringToDouble("-.15"), -0.15);