--- linuxsampler/trunk/src/scriptvm/CoreVMFunctions.cpp 2019/08/28 11:12:04 3575 +++ linuxsampler/trunk/src/scriptvm/CoreVMFunctions.cpp 2019/08/30 12:23:40 3582 @@ -19,6 +19,13 @@ namespace LinuxSampler { +inline bool _fEqualX(vmfloat a, vmfloat b) { + if (sizeof(vmfloat) == sizeof(float)) + return RTMath::fEqual32(a, b); + else + return RTMath::fEqual64(a, b); +} + /////////////////////////////////////////////////////////////////////////// // class VMEmptyResultFunction @@ -38,12 +45,28 @@ VMFnResult* VMIntResultFunction::errorResult(vmint i) { result.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); result.value = i; + result.unitPrefixFactor = VM_NO_FACTOR; return &result; } VMFnResult* VMIntResultFunction::successResult(vmint i) { result.flags = STMT_SUCCESS; result.value = i; + result.unitPrefixFactor = VM_NO_FACTOR; + return &result; +} + +VMFnResult* VMIntResultFunction::errorResult(VMIntFnResDef res) { + result.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); + result.value = res.value; + result.unitPrefixFactor = res.unitFactor; + return &result; +} + +VMFnResult* VMIntResultFunction::successResult(VMIntFnResDef res) { + result.flags = STMT_SUCCESS; + result.value = res.value; + result.unitPrefixFactor = res.unitFactor; return &result; } @@ -53,12 +76,28 @@ VMFnResult* VMRealResultFunction::errorResult(vmfloat f) { result.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); result.value = f; + result.unitPrefixFactor = VM_NO_FACTOR; + return &result; +} + +VMFnResult* VMRealResultFunction::errorResult(VMRealFnResDef res) { + result.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); + result.value = res.value; + result.unitPrefixFactor = res.unitFactor; return &result; } VMFnResult* VMRealResultFunction::successResult(vmfloat f) { result.flags = STMT_SUCCESS; result.value = f; + result.unitPrefixFactor = VM_NO_FACTOR; + return &result; +} + +VMFnResult* VMRealResultFunction::successResult(VMRealFnResDef res) { + result.flags = STMT_SUCCESS; + result.value = res.value; + result.unitPrefixFactor = res.unitFactor; return &result; } @@ -78,6 +117,65 @@ } /////////////////////////////////////////////////////////////////////////// +// class VMNumberResultFunction + +VMFnResult* VMNumberResultFunction::errorResult(vmint i) { + intResult.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); + intResult.value = i; + intResult.unitPrefixFactor = VM_NO_FACTOR; + return &intResult; +} + +VMFnResult* VMNumberResultFunction::errorResult(vmfloat f) { + realResult.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); + realResult.value = f; + realResult.unitPrefixFactor = VM_NO_FACTOR; + return &realResult; +} + +VMFnResult* VMNumberResultFunction::successResult(vmint i) { + intResult.flags = STMT_SUCCESS; + intResult.value = i; + intResult.unitPrefixFactor = VM_NO_FACTOR; + return &intResult; +} + +VMFnResult* VMNumberResultFunction::successResult(vmfloat f) { + realResult.flags = STMT_SUCCESS; + realResult.value = f; + realResult.unitPrefixFactor = VM_NO_FACTOR; + return &realResult; +} + +VMFnResult* VMNumberResultFunction::errorIntResult(VMIntFnResDef res) { + intResult.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); + intResult.value = res.value; + intResult.unitPrefixFactor = res.unitFactor; + return &intResult; +} + +VMFnResult* VMNumberResultFunction::errorRealResult(VMRealFnResDef res) { + realResult.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); + realResult.value = res.value; + realResult.unitPrefixFactor = res.unitFactor; + return &realResult; +} + +VMFnResult* VMNumberResultFunction::successIntResult(VMIntFnResDef res) { + intResult.flags = STMT_SUCCESS; + intResult.value = res.value; + intResult.unitPrefixFactor = res.unitFactor; + return &intResult; +} + +VMFnResult* VMNumberResultFunction::successRealResult(VMRealFnResDef res) { + realResult.flags = STMT_SUCCESS; + realResult.value = res.value; + realResult.unitPrefixFactor = res.unitFactor; + return &realResult; +} + +/////////////////////////////////////////////////////////////////////////// // built-in script function: message() bool CoreVMFunction_message::acceptsArgType(vmint iArg, ExprType_t type) const { @@ -122,21 +220,51 @@ return type == INT_EXPR || type == REAL_EXPR || type == STRING_EXPR; } +bool CoreVMFunction_exit::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { + if (!vm->isExitResultEnabled()) return false; + return true; +} + +bool CoreVMFunction_exit::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const { + if (!vm->isExitResultEnabled()) return false; + return true; +} +bool CoreVMFunction_exit::acceptsArgFinal(vmint iArg) const { + if (!vm->isExitResultEnabled()) return false; + return true; +} + VMFnResult* CoreVMFunction_exit::exec(VMFnArgs* args) { this->result.flags = STMT_ABORT_SIGNALLED; if (vm->isExitResultEnabled() && args->argsCount()) { ExecContext* ctx = dynamic_cast(vm->currentVMExecContext()); switch (args->arg(0)->exprType()) { - case INT_EXPR: - ctx->exitRes.intLiteral.value = args->arg(0)->asInt()->evalInt(); + case INT_EXPR: { + VMIntExpr* expr = args->arg(0)->asInt(); + ctx->exitRes.intLiteral = IntLiteral({ + .value = expr->evalInt(), + .unitFactor = expr->unitFactor(), + .unitType = expr->unitType(), + .isFinal = expr->isFinal() + }); ctx->exitRes.value = &ctx->exitRes.intLiteral; break; - case REAL_EXPR: - ctx->exitRes.realLiteral.value = args->arg(0)->asReal()->evalReal(); + } + case REAL_EXPR: { + VMRealExpr* expr = args->arg(0)->asReal(); + ctx->exitRes.realLiteral = RealLiteral({ + .value = expr->evalReal(), + .unitFactor = expr->unitFactor(), + .unitType = expr->unitType(), + .isFinal = expr->isFinal() + }); ctx->exitRes.value = &ctx->exitRes.realLiteral; break; + } case STRING_EXPR: - ctx->exitRes.stringLiteral.value = args->arg(0)->asString()->evalStr(); + ctx->exitRes.stringLiteral = StringLiteral( + args->arg(0)->asString()->evalStr() + ); ctx->exitRes.value = &ctx->exitRes.stringLiteral; break; default: @@ -181,87 +309,302 @@ /////////////////////////////////////////////////////////////////////////// // built-in script function: abs() +ExprType_t CoreVMFunction_abs::returnType(VMFnArgs* args) { + return args->arg(0)->exprType(); +} + +StdUnit_t CoreVMFunction_abs::returnUnitType(VMFnArgs* args) { + return args->arg(0)->asNumber()->unitType(); +} + +bool CoreVMFunction_abs::returnsFinal(VMFnArgs* args) { + return args->arg(0)->asNumber()->isFinal(); +} + bool CoreVMFunction_abs::acceptsArgType(vmint iArg, ExprType_t type) const { - return type == INT_EXPR; + return type == INT_EXPR || type == REAL_EXPR; } VMFnResult* CoreVMFunction_abs::exec(VMFnArgs* args) { - return successResult( ::abs(args->arg(0)->asInt()->evalInt()) ); + VMExpr* arg = args->arg(0); + if (arg->exprType() == REAL_EXPR) { + VMRealExpr* expr = arg->asReal(); + return successRealResult({ + .value = ::fabs(expr->evalReal()), + .unitFactor = expr->unitFactor() + }); + } else { + VMIntExpr* expr = arg->asInt(); + return successIntResult({ + .value = ::abs(expr->evalInt()), + .unitFactor = expr->unitFactor() + }); + } } /////////////////////////////////////////////////////////////////////////// // built-in script function: random() +ExprType_t CoreVMFunction_random::returnType(VMFnArgs* args) { + return (args->arg(0)->exprType() == INT_EXPR && + args->arg(1)->exprType() == INT_EXPR) ? INT_EXPR : REAL_EXPR; +} + +StdUnit_t CoreVMFunction_random::returnUnitType(VMFnArgs* args) { + // we ensure in checkArgs() below (which is called before this method here) + // that both arguments must be of same unit type, so either one is fine here + return args->arg(0)->asNumber()->unitType(); +} + +bool CoreVMFunction_random::returnsFinal(VMFnArgs* args) { + return args->arg(0)->asNumber()->isFinal() || + args->arg(1)->asNumber()->isFinal(); +} + bool CoreVMFunction_random::acceptsArgType(vmint iArg, ExprType_t type) const { - return type == INT_EXPR; + return type == INT_EXPR || type == REAL_EXPR; +} + +void CoreVMFunction_random::checkArgs(VMFnArgs* args, + std::function err, + std::function wrn) +{ + // super class checks + Super::checkArgs(args, err, wrn); + + // own checks ... + if (args->arg(0)->asNumber()->unitType() != + args->arg(1)->asNumber()->unitType()) + { + String a = unitTypeStr(args->arg(0)->asNumber()->unitType()); + String b = unitTypeStr(args->arg(1)->asNumber()->unitType()); + err("Argument 1 has unit type " + a + ", whereas argument 2 has unit type " + b + "."); + return; + } + if (args->arg(0)->asNumber()->isFinal() != + args->arg(1)->asNumber()->isFinal()) + { + String a = args->arg(0)->asNumber()->isFinal() ? "'final'" : "not 'final'"; + String b = args->arg(1)->asNumber()->isFinal() ? "'final'" : "not 'final'"; + wrn("Argument 1 is " + a + ", whereas argument 2 is " + b + ", function result will be final."); + } } VMFnResult* CoreVMFunction_random::exec(VMFnArgs* args) { - vmint iMin = args->arg(0)->asInt()->evalInt(); - vmint iMax = args->arg(1)->asInt()->evalInt(); float f = float(::rand()) / float(RAND_MAX); - return successResult( - iMin + roundf( f * float(iMax - iMin) ) - ); + + VMNumberExpr* arg0 = args->arg(0)->asNumber(); + VMNumberExpr* arg1 = args->arg(1)->asNumber(); + + if (arg0->exprType() == INT_EXPR && arg1->exprType() == INT_EXPR) { + vmint iMin = args->arg(0)->asInt()->evalInt(); + vmint iMax = args->arg(1)->asInt()->evalInt(); + if (arg0->unitFactor() == arg1->unitFactor()) { + return successIntResult({ + .value = vmint( iMin + roundf( f * float(iMax - iMin) ) ), + .unitFactor = arg0->unitFactor() + }); + } else if (arg0->unitFactor() < arg1->unitFactor()) { + iMax = Unit::convIntToUnitFactor(iMax, arg1, arg0); + return successIntResult({ + .value = vmint( iMin + roundf( f * float(iMax - iMin) ) ), + .unitFactor = arg0->unitFactor() + }); + } else { // arg0->unitFactor() > arg1->unitFactor() ... + iMin = Unit::convIntToUnitFactor(iMin, arg0, arg1); + return successIntResult({ + .value = vmint( iMin + roundf( f * float(iMax - iMin) ) ), + .unitFactor = arg1->unitFactor() + }); + } + } else { + vmfloat fMin = arg0->evalCastReal(); + vmfloat fMax = arg1->evalCastReal(); + if (arg0->unitFactor() == arg1->unitFactor()) { + return successRealResult({ + .value = fMin + f * (fMax - fMin), + .unitFactor = arg0->unitFactor() + }); + } else if (arg0->unitFactor() < arg1->unitFactor()) { + fMax = Unit::convRealToUnitFactor(fMax, arg1, arg0); + return successRealResult({ + .value = fMin + f * (fMax - fMin), + .unitFactor = arg0->unitFactor() + }); + } else { // arg0->unitFactor() > arg1->unitFactor() ... + fMin = Unit::convRealToUnitFactor(fMin, arg0, arg1); + return successRealResult({ + .value = fMin + f * (fMax - fMin), + .unitFactor = arg1->unitFactor() + }); + } + } } /////////////////////////////////////////////////////////////////////////// // built-in script function: num_elements() bool CoreVMFunction_num_elements::acceptsArgType(vmint iArg, ExprType_t type) const { - return type == INT_ARR_EXPR; + return isArray(type); } VMFnResult* CoreVMFunction_num_elements::exec(VMFnArgs* args) { - return successResult( args->arg(0)->asIntArray()->arraySize() ); + return successResult( args->arg(0)->asArray()->arraySize() ); } /////////////////////////////////////////////////////////////////////////// // built-in script function: inc() +StdUnit_t CoreVMFunction_inc::returnUnitType(VMFnArgs* args) { + return args->arg(0)->asNumber()->unitType(); +} + +bool CoreVMFunction_inc::returnsFinal(VMFnArgs* args) { + return args->arg(0)->asNumber()->isFinal(); +} + +void CoreVMFunction_inc::checkArgs(VMFnArgs* args, + std::function err, + std::function wrn) +{ + // super class checks + Super::checkArgs(args, err, wrn); + + // own checks ... + if (args->arg(0)->asNumber()->unitType()) { + String unitType = unitTypeStr(args->arg(0)->asNumber()->unitType()); + wrn("Argument has a unit type (" + unitType + "), only the number before the unit will be incremented by one."); + } +} + VMFnResult* CoreVMFunction_inc::exec(VMFnArgs* args) { VMExpr* arg = args->arg(0); VMIntExpr* in = dynamic_cast(arg); VMVariable* out = dynamic_cast(arg); - if (!in || !out) successResult(0); vmint i = in->evalInt() + 1; - IntLiteral tmp(i); + IntLiteral tmp({ + .value = i, + .unitFactor = in->unitFactor() + }); out->assignExpr(&tmp); - return successResult(i); + return successResult({ + .value = i, + .unitFactor = in->unitFactor() + }); } /////////////////////////////////////////////////////////////////////////// // built-in script function: dec() +StdUnit_t CoreVMFunction_dec::returnUnitType(VMFnArgs* args) { + return args->arg(0)->asNumber()->unitType(); +} + +bool CoreVMFunction_dec::returnsFinal(VMFnArgs* args) { + return args->arg(0)->asNumber()->isFinal(); +} + +void CoreVMFunction_dec::checkArgs(VMFnArgs* args, + std::function err, + std::function wrn) +{ + // super class checks + Super::checkArgs(args, err, wrn); + + // own checks ... + if (args->arg(0)->asNumber()->unitType()) { + String unitType = unitTypeStr(args->arg(0)->asNumber()->unitType()); + wrn("Argument has a unit type (" + unitType + "), only the number before the unit will be decremented by one."); + } +} + VMFnResult* CoreVMFunction_dec::exec(VMFnArgs* args) { VMExpr* arg = args->arg(0); VMIntExpr* in = dynamic_cast(arg); VMVariable* out = dynamic_cast(arg); - if (!in || !out) successResult(0); vmint i = in->evalInt() - 1; - IntLiteral tmp(i); + IntLiteral tmp({ + .value = i, + .unitFactor = in->unitFactor() + }); out->assignExpr(&tmp); - return successResult(i); + return successResult({ + .value = i, + .unitFactor = in->unitFactor() + }); } /////////////////////////////////////////////////////////////////////////// // built-in script function: in_range() -VMFnResult* CoreVMFunction_in_range::exec(VMFnArgs* args) { - vmint i = args->arg(0)->asInt()->evalInt(); - vmint lo = args->arg(1)->asInt()->evalInt(); - vmint hi = args->arg(2)->asInt()->evalInt(); - if (lo > hi) { // swap lo and hi - vmint tmp = lo; - lo = hi; - hi = tmp; +bool CoreVMFunction_in_range::acceptsArgType(vmint iArg, ExprType_t type) const { + return type == INT_EXPR || type == REAL_EXPR; +} + +void CoreVMFunction_in_range::checkArgs(VMFnArgs* args, + std::function err, + std::function wrn) +{ + // super class checks + Super::checkArgs(args, err, wrn); + + // own checks ... + if (args->arg(0)->asNumber()->unitType() != + args->arg(1)->asNumber()->unitType() || + args->arg(1)->asNumber()->unitType() != + args->arg(2)->asNumber()->unitType()) + { + String a = unitTypeStr(args->arg(0)->asNumber()->unitType()); + String b = unitTypeStr(args->arg(1)->asNumber()->unitType()); + String c = unitTypeStr(args->arg(2)->asNumber()->unitType()); + err("Arguments must all have same unit, however argument 1 is " + a + + ", argument 2 is " + b + ", argument 3 is " + c + "."); + return; + } + if (args->arg(0)->exprType() != args->arg(1)->exprType() || + args->arg(1)->exprType() != args->arg(2)->exprType()) + { + String a = typeStr(args->arg(0)->exprType()); + String b = typeStr(args->arg(1)->exprType()); + String c = typeStr(args->arg(2)->exprType()); + String r = typeStr(REAL_EXPR); + wrn("Argument 1 is " + a + ", argument 2 is " + b + + ", argument 3 is " + c + ", function result will be " + r + "."); } - return successResult(i >= lo && i <= hi); +} + +template +inline void _swapByValue(T& a, T& b) { + T tmp = a; + a = b; + b = tmp; +} + +VMFnResult* CoreVMFunction_in_range::exec(VMFnArgs* args) { + VMNumberExpr* argNeedle = args->arg(0)->asNumber(); + VMNumberExpr* argLo = args->arg(1)->asNumber(); + VMNumberExpr* argHi = args->arg(2)->asNumber(); + + vmfloat needle = argNeedle->evalCastReal(); + vmfloat lo = argLo->evalCastReal(); + vmfloat hi = argHi->evalCastReal(); + + needle *= argNeedle->unitFactor(); + lo *= argLo->unitFactor(); + hi *= argHi->unitFactor(); + + if (lo > hi) _swapByValue(lo, hi); + + return successResult(needle >= lo && needle <= hi); } /////////////////////////////////////////////////////////////////////////// // built-in script function: sh_left() +bool CoreVMFunction_sh_left::returnsFinal(VMFnArgs* args) { + return args->arg(0)->asNumber()->isFinal(); +} + VMFnResult* CoreVMFunction_sh_left::exec(VMFnArgs* args) { vmint i = args->arg(0)->asInt()->evalInt(); vmint n = args->arg(1)->asInt()->evalInt(); @@ -271,6 +614,10 @@ /////////////////////////////////////////////////////////////////////////// // built-in script function: sh_right() +bool CoreVMFunction_sh_right::returnsFinal(VMFnArgs* args) { + return args->arg(0)->asNumber()->isFinal(); +} + VMFnResult* CoreVMFunction_sh_right::exec(VMFnArgs* args) { vmint i = args->arg(0)->asInt()->evalInt(); vmint n = args->arg(1)->asInt()->evalInt(); @@ -280,35 +627,284 @@ /////////////////////////////////////////////////////////////////////////// // built-in script function: min() +ExprType_t CoreVMFunction_min::returnType(VMFnArgs* args) { + return (args->arg(0)->exprType() == REAL_EXPR || + args->arg(1)->exprType() == REAL_EXPR) ? REAL_EXPR : INT_EXPR; +} + +StdUnit_t CoreVMFunction_min::returnUnitType(VMFnArgs* args) { + return args->arg(0)->asNumber()->unitType(); +} + +bool CoreVMFunction_min::returnsFinal(VMFnArgs* args) { + return args->arg(0)->asNumber()->isFinal() || + args->arg(1)->asNumber()->isFinal(); +} + +bool CoreVMFunction_min::acceptsArgType(vmint iArg, ExprType_t type) const { + return type == INT_EXPR || type == REAL_EXPR; +} + +void CoreVMFunction_min::checkArgs(VMFnArgs* args, + std::function err, + std::function wrn) +{ + // super class checks + Super::checkArgs(args, err, wrn); + + // own checks ... + if (args->arg(0)->asNumber()->unitType() != + args->arg(1)->asNumber()->unitType()) + { + String a = unitTypeStr(args->arg(0)->asNumber()->unitType()); + String b = unitTypeStr(args->arg(1)->asNumber()->unitType()); + err("Argument 1 has unit type " + a + ", whereas argument 2 has unit type " + b + "."); + return; + } + if (args->arg(0)->exprType() != args->arg(1)->exprType()) { + String a = typeStr(args->arg(0)->exprType()); + String b = typeStr(args->arg(1)->exprType()); + String c = typeStr(REAL_EXPR); + wrn("Argument 1 is " + a + ", whereas argument 2 is " + b + ", function result will be " + c + "."); + return; + } + if (args->arg(0)->asNumber()->isFinal() != + args->arg(1)->asNumber()->isFinal()) + { + String a = args->arg(0)->asNumber()->isFinal() ? "'final'" : "not 'final'"; + String b = args->arg(1)->asNumber()->isFinal() ? "'final'" : "not 'final'"; + wrn("Argument 1 is " + a + ", whereas argument 2 is " + b + ", function result will be final."); + } +} + VMFnResult* CoreVMFunction_min::exec(VMFnArgs* args) { - vmint l = args->arg(0)->asInt()->evalInt(); - vmint r = args->arg(1)->asInt()->evalInt(); - return successResult(l < r ? l : r); + VMNumberExpr* lhs = args->arg(0)->asNumber(); + VMNumberExpr* rhs = args->arg(1)->asNumber(); + if (lhs->exprType() == REAL_EXPR && rhs->exprType() == REAL_EXPR) { + vmfloat lm = lhs->asReal()->evalReal(); + vmfloat rm = rhs->asReal()->evalReal(); + vmfloat lprod = lm * lhs->unitFactor(); + vmfloat rprod = rm * rhs->unitFactor(); + return successRealResult({ + .value = (lprod < rprod) ? lm : rm, + .unitFactor = (lprod < rprod) ? lhs->unitFactor() : rhs->unitFactor() + }); + } else if (lhs->exprType() == REAL_EXPR && rhs->exprType() == INT_EXPR) { + vmfloat lm = lhs->asReal()->evalReal(); + vmint rm = rhs->asInt()->evalInt(); + vmfloat lprod = lm * lhs->unitFactor(); + vmfloat rprod = rm * rhs->unitFactor(); + return successRealResult({ + .value = (lprod < rprod) ? lm : rm, + .unitFactor = (lprod < rprod) ? lhs->unitFactor() : rhs->unitFactor() + }); + } else if (lhs->exprType() == INT_EXPR && rhs->exprType() == REAL_EXPR) { + vmint lm = lhs->asInt()->evalInt(); + vmfloat rm = rhs->asReal()->evalReal(); + vmfloat lprod = lm * lhs->unitFactor(); + vmfloat rprod = rm * rhs->unitFactor(); + return successRealResult({ + .value = (lprod < rprod) ? lm : rm, + .unitFactor = (lprod < rprod) ? lhs->unitFactor() : rhs->unitFactor() + }); + } else { + vmint lm = lhs->asInt()->evalInt(); + vmint rm = rhs->asInt()->evalInt(); + vmfloat lprod = lm * lhs->unitFactor(); + vmfloat rprod = rm * rhs->unitFactor(); + return successIntResult({ + .value = (lprod < rprod) ? lm : rm, + .unitFactor = (lprod < rprod) ? lhs->unitFactor() : rhs->unitFactor() + }); + } } /////////////////////////////////////////////////////////////////////////// // built-in script function: max() +ExprType_t CoreVMFunction_max::returnType(VMFnArgs* args) { + return (args->arg(0)->exprType() == REAL_EXPR || + args->arg(1)->exprType() == REAL_EXPR) ? REAL_EXPR : INT_EXPR; +} + +StdUnit_t CoreVMFunction_max::returnUnitType(VMFnArgs* args) { + return args->arg(0)->asNumber()->unitType(); +} + +bool CoreVMFunction_max::returnsFinal(VMFnArgs* args) { + return args->arg(0)->asNumber()->isFinal() || + args->arg(1)->asNumber()->isFinal(); +} + +bool CoreVMFunction_max::acceptsArgType(vmint iArg, ExprType_t type) const { + return type == INT_EXPR || type == REAL_EXPR; +} + +void CoreVMFunction_max::checkArgs(VMFnArgs* args, + std::function err, + std::function wrn) +{ + // super class checks + Super::checkArgs(args, err, wrn); + + // own checks ... + if (args->arg(0)->asNumber()->unitType() != + args->arg(1)->asNumber()->unitType()) + { + String a = unitTypeStr(args->arg(0)->asNumber()->unitType()); + String b = unitTypeStr(args->arg(1)->asNumber()->unitType()); + err("Argument 1 has unit type " + a + ", whereas argument 2 has unit type " + b + "."); + return; + } + if (args->arg(0)->exprType() != args->arg(1)->exprType()) { + String a = typeStr(args->arg(0)->exprType()); + String b = typeStr(args->arg(1)->exprType()); + String c = typeStr(REAL_EXPR); + wrn("Argument 1 is " + a + ", whereas argument 2 is " + b + ", function result will be " + c + "."); + return; + } + if (args->arg(0)->asNumber()->isFinal() != + args->arg(1)->asNumber()->isFinal()) + { + String a = args->arg(0)->asNumber()->isFinal() ? "'final'" : "not 'final'"; + String b = args->arg(1)->asNumber()->isFinal() ? "'final'" : "not 'final'"; + wrn("Argument 1 is " + a + ", whereas argument 2 is " + b + ", function result will be final."); + } +} + VMFnResult* CoreVMFunction_max::exec(VMFnArgs* args) { - vmint l = args->arg(0)->asInt()->evalInt(); - vmint r = args->arg(1)->asInt()->evalInt(); - return successResult(l > r ? l : r); + VMNumberExpr* lhs = args->arg(0)->asNumber(); + VMNumberExpr* rhs = args->arg(1)->asNumber(); + if (lhs->exprType() == REAL_EXPR && rhs->exprType() == REAL_EXPR) { + vmfloat lm = lhs->asReal()->evalReal(); + vmfloat rm = rhs->asReal()->evalReal(); + vmfloat lprod = lm * lhs->unitFactor(); + vmfloat rprod = rm * rhs->unitFactor(); + return successRealResult({ + .value = (lprod > rprod) ? lm : rm, + .unitFactor = (lprod > rprod) ? lhs->unitFactor() : rhs->unitFactor() + }); + } else if (lhs->exprType() == REAL_EXPR && rhs->exprType() == INT_EXPR) { + vmfloat lm = lhs->asReal()->evalReal(); + vmint rm = rhs->asInt()->evalInt(); + vmfloat lprod = lm * lhs->unitFactor(); + vmfloat rprod = rm * rhs->unitFactor(); + return successRealResult({ + .value = (lprod > rprod) ? lm : rm, + .unitFactor = (lprod > rprod) ? lhs->unitFactor() : rhs->unitFactor() + }); + } else if (lhs->exprType() == INT_EXPR && rhs->exprType() == REAL_EXPR) { + vmint lm = lhs->asInt()->evalInt(); + vmfloat rm = rhs->asReal()->evalReal(); + vmfloat lprod = lm * lhs->unitFactor(); + vmfloat rprod = rm * rhs->unitFactor(); + return successRealResult({ + .value = (lprod > rprod) ? lm : rm, + .unitFactor = (lprod > rprod) ? lhs->unitFactor() : rhs->unitFactor() + }); + } else { + vmint lm = lhs->asInt()->evalInt(); + vmint rm = rhs->asInt()->evalInt(); + vmfloat lprod = lm * lhs->unitFactor(); + vmfloat rprod = rm * rhs->unitFactor(); + return successIntResult({ + .value = (lprod > rprod) ? lm : rm, + .unitFactor = (lprod > rprod) ? lhs->unitFactor() : rhs->unitFactor() + }); + } } /////////////////////////////////////////////////////////////////////////// // built-in script function: array_equal() +bool CoreVMFunction_array_equal::acceptsArgType(vmint iArg, ExprType_t type) const { + return isArray(type); +} + +void CoreVMFunction_array_equal::checkArgs(VMFnArgs* args, + std::function err, + std::function wrn) +{ + // super class checks + Super::checkArgs(args, err, wrn); + + // own checks ... + if (args->arg(0)->exprType() != args->arg(1)->exprType()) { + String a = typeStr(args->arg(0)->exprType()); + String b = typeStr(args->arg(1)->exprType()); + err("Argument 1 is " + a + ", whereas argument 2 is " + b + "."); + return; + } + if (args->arg(0)->asArray()->arraySize() != + args->arg(1)->asArray()->arraySize()) + { + wrn("Result of function call is always false, since the passed two arrays were declared with different array sizes."); + } +} + VMFnResult* CoreVMFunction_array_equal::exec(VMFnArgs* args) { - VMIntArrayExpr* l = args->arg(0)->asIntArray(); - VMIntArrayExpr* r = args->arg(1)->asIntArray(); + VMArrayExpr* l = args->arg(0)->asArray(); + VMArrayExpr* r = args->arg(1)->asArray(); if (l->arraySize() != r->arraySize()) { - wrnMsg("array_equal(): the two arrays differ in size"); + //wrnMsg("array_equal(): the two arrays differ in size"); return successResult(0); // false } const vmint n = l->arraySize(); - for (vmint i = 0; i < n; ++i) - if (l->evalIntElement(i) != r->evalIntElement(i)) - return successResult(0); // false + // checkArgs() above ensured that we either have INT_ARR_EXPR on both sides + // or REAL_ARR_EXPR on both sides, so we can simplify here (a bit) + if (l->exprType() == INT_ARR_EXPR) { + VMIntArrayExpr* lia = l->asIntArray(); + VMIntArrayExpr* ria = r->asIntArray(); + for (vmint i = 0; i < n; ++i) { + vmint lvalue = lia->evalIntElement(i); + vmint rvalue = ria->evalIntElement(i); + vmfloat lfactor = lia->unitFactorOfElement(i); + vmfloat rfactor = ria->unitFactorOfElement(i); + if (lfactor == rfactor) { + if (lvalue != rvalue) + return successResult(0); // false + else + continue; + } + if (lfactor < rfactor) { + if (lvalue != Unit::convIntToUnitFactor(rvalue, rfactor, lfactor)) + return successResult(0); // false + else + continue; + } else { + if (rvalue != Unit::convIntToUnitFactor(lvalue, lfactor, rfactor)) + return successResult(0); // false + else + continue; + } + } + } else { + VMRealArrayExpr* lra = l->asRealArray(); + VMRealArrayExpr* rra = r->asRealArray(); + for (vmint i = 0; i < n; ++i) { + vmfloat lvalue = lra->evalRealElement(i); + vmfloat rvalue = rra->evalRealElement(i); + vmfloat lfactor = lra->unitFactorOfElement(i); + vmfloat rfactor = rra->unitFactorOfElement(i); + if (lfactor == rfactor) { + if (!_fEqualX(lvalue, rvalue)) + return successResult(0); // false + else + continue; + } + if (lfactor < rfactor) { + if (!_fEqualX(lvalue, Unit::convRealToUnitFactor(rvalue, rfactor, lfactor))) + return successResult(0); // false + else + continue; + } else { + if (!_fEqualX(rvalue, Unit::convRealToUnitFactor(lvalue, lfactor, rfactor))) + return successResult(0); // false + else + continue; + } + } + } return successResult(1); // true } @@ -321,18 +917,57 @@ bool CoreVMFunction_search::acceptsArgType(vmint iArg, ExprType_t type) const { if (iArg == 0) - return type == INT_ARR_EXPR; + return isArray(type); else - return type == INT_EXPR; + return type == INT_EXPR || type == REAL_EXPR; +} + +void CoreVMFunction_search::checkArgs(VMFnArgs* args, + std::function err, + std::function wrn) +{ + // super class checks + Super::checkArgs(args, err, wrn); + + // own checks ... + if (args->arg(0)->exprType() == INT_ARR_EXPR && + args->arg(1)->exprType() != INT_EXPR) + { + String a = typeStr(INT_ARR_EXPR); + String bIs = typeStr(args->arg(1)->exprType()); + String bShould = typeStr(INT_EXPR); + err("Argument 1 is " + a + ", hence argument 2 should be " + bShould + ", is " + bIs + " though."); + return; + } + if (args->arg(0)->exprType() == REAL_ARR_EXPR && + args->arg(1)->exprType() != REAL_EXPR) + { + String a = typeStr(REAL_ARR_EXPR); + String bIs = typeStr(args->arg(1)->exprType()); + String bShould = typeStr(REAL_EXPR); + err("Argument 1 is " + a + ", hence argument 2 should be " + bShould + ", is " + bIs + " though."); + return; + } } VMFnResult* CoreVMFunction_search::exec(VMFnArgs* args) { - VMIntArrayExpr* a = args->arg(0)->asIntArray(); - const vmint needle = args->arg(1)->asInt()->evalInt(); + VMArrayExpr* a = args->arg(0)->asArray(); const vmint n = a->arraySize(); - for (vmint i = 0; i < n; ++i) - if (a->evalIntElement(i) == needle) - return successResult(i); + if (a->exprType() == INT_ARR_EXPR) { + const vmint needle = args->arg(1)->asInt()->evalInt(); + VMIntArrayExpr* intArray = a->asIntArray(); + for (vmint i = 0; i < n; ++i) + if (intArray->evalIntElement(i) == needle) + return successResult(i); + } else { // real array ... + const vmfloat needle = args->arg(1)->asReal()->evalReal(); + VMRealArrayExpr* realArray = a->asRealArray(); + for (vmint i = 0; i < n; ++i) { + const vmfloat value = realArray->evalRealElement(i); + if (_fEqualX(value, needle)) + return successResult(i); + } + } return successResult(-1); // not found } @@ -345,230 +980,349 @@ bool CoreVMFunction_sort::acceptsArgType(vmint iArg, ExprType_t type) const { if (iArg == 0) - return type == INT_ARR_EXPR; + return isArray(type); else return type == INT_EXPR; } +// The following structs and template classes act as adapters for allowing to +// use std sort algorithms on our arrays. It might look a bit more complicated +// than it ought to be, but there is one reason for the large amount of +// 'adapter' code below: the STL std algorithms rely on 'lvalues' to do their +// e.g. sorting) jobs, that is they expect containers to have 'localizeable' +// data which essentially means their data should reside somewhere in memory and +// directly be accessible (readable and writable) there, which is not the case +// with our VM interfaces which actually always require virtual getter and +// setter methods to be called instead. So we must emulate lvalues by custom +// classes/structs which forward between our getters/setters and the lvalue +// access operators used by the STL std algorithms. + +struct IntArrayAccessor { + static inline vmint getPrimaryValue(VMIntArrayExpr* arr, vmint index) { + return arr->evalIntElement(index); + } + static inline void setPrimaryValue(VMIntArrayExpr* arr, vmint index, vmint value) { + arr->assignIntElement(index, value); + } +}; + +struct RealArrayAccessor { + static inline vmfloat getPrimaryValue(VMRealArrayExpr* arr, vmint index) { + return arr->evalRealElement(index); + } + static inline void setPrimaryValue(VMRealArrayExpr* arr, vmint index, vmfloat value) { + arr->assignRealElement(index, value); + } +}; + +template // i.e. T_array is either VMIntArrayExpr or VMRealArrayExpr struct ArrElemPOD { - VMIntArrayExpr* m_array; + T_array* m_array; vmint m_index; }; -static inline void swap(class ArrElemRef a, class ArrElemRef b); +// This class is used for temporary values by std::sort(). +template // i.e. T_value is either vmint or vmfloat +struct ScalarNmbrVal { + T_value primValue; + vmfloat unitFactor; + + inline bool operator<(const ScalarNmbrVal& other) const { + return getProdValue() < other.getProdValue(); + } + inline bool operator>(const ScalarNmbrVal& other) const { + return getProdValue() > other.getProdValue(); + } + inline vmfloat getProdValue() const { + // simple solution for both vmint and vmfloat, should be fine for just sorting + return primValue * unitFactor; + } +}; -class ArrElemRef : protected ArrElemPOD { +// This class emulates lvalue access (access by reference) which is used by ArrExprIter::operator*() below. +template // T_accessor is either IntArrayAccessor or RealArrayAccessor +class ArrElemRef : protected ArrElemPOD { public: - ArrElemRef() { - m_array = NULL; - m_index = 0; - } - ArrElemRef(VMIntArrayExpr* a, vmint index) { - m_array = a; - m_index = index; + typedef ScalarNmbrVal ScalarNmbrVal; + + inline ArrElemRef(T_array* a, vmint index) { + this->m_array = a; + this->m_index = index; + } + inline ArrElemRef(const ArrElemRef& ref) { + this->m_array = ref.m_array; + this->m_index = ref.m_index; } inline ArrElemRef& operator=(const ArrElemRef& e) { - setValue(e.getValue()); + setPrimValue(e.getPrimValue()); + setUnitFactor(e.getUnitFactor()); return *this; } - inline ArrElemRef& operator=(vmint val) { - setValue(val); + inline ArrElemRef& operator=(ScalarNmbrVal value) { + setPrimValue(value.primValue); + setUnitFactor(value.unitFactor); return *this; } inline bool operator==(const ArrElemRef& e) const { - if (m_index == e.m_index) - return true; - return getValue() == e.getValue(); - } - inline bool operator==(vmint val) const { - return getValue() == val; + return getProdValue() == e.getProdValue(); } inline bool operator!=(const ArrElemRef& e) const { return !(operator==(e)); } - inline bool operator!=(vmint val) const { - return !(operator==(val)); - } inline bool operator<(const ArrElemRef& e) const { - if (m_index == e.m_index) - return false; - return getValue() < e.getValue(); - } - inline bool operator<(vmint val) const { - return getValue() < val; + return getProdValue() < e.getProdValue(); } inline bool operator>(const ArrElemRef& e) const { - if (m_index == e.m_index) - return false; - return getValue() > e.getValue(); - } - inline bool operator>(vmint val) const { - return getValue() > val; + return getProdValue() > e.getProdValue(); } inline bool operator<=(const ArrElemRef& e) const { - if (m_index == e.m_index) - return true; - return getValue() <= e.getValue(); - } - inline bool operator<=(vmint val) const { - return getValue() <= val; + return getProdValue() <= e.getProdValue(); } inline bool operator>=(const ArrElemRef& e) const { - if (m_index == e.m_index) - return true; - return getValue() >= e.getValue(); + return getProdValue() >= e.getProdValue(); + } + inline bool operator==(const ScalarNmbrVal& s) const { + return getProdValue() == s.getProdValue(); + } + inline bool operator!=(const ScalarNmbrVal& s) const { + return !(operator==(s)); + } + inline bool operator<(const ScalarNmbrVal& s) const { + return getProdValue() < s.getProdValue(); + } + inline bool operator>(const ScalarNmbrVal& s) const { + return getProdValue() > s.getProdValue(); + } + inline bool operator<=(const ScalarNmbrVal& s) const { + return getProdValue() <= s.getProdValue(); } - inline bool operator>=(vmint val) const { - return getValue() >= val; + inline bool operator>=(const ScalarNmbrVal& s) const { + return getProdValue() >= s.getProdValue(); } - inline operator vmint() const { - return getValue(); + inline operator ScalarNmbrVal() { + return { + .primValue = getPrimValue() , + .unitFactor = getUnitFactor() + }; } protected: - inline vmint getValue() const { - return m_array->evalIntElement(m_index); + inline T_value getPrimValue() const { + return T_accessor::getPrimaryValue( this->m_array, this->m_index ); } - inline void setValue(vmint value) { - m_array->assignIntElement(m_index, value); + inline void setPrimValue(T_value value) { + T_accessor::setPrimaryValue( this->m_array, this->m_index, value ); } - - friend void swap(class ArrElemRef a, class ArrElemRef b); -}; - -class ArrElemPtr : protected ArrElemPOD { -public: - ArrElemPtr() { - m_array = NULL; - m_index = 0; - } - ArrElemPtr(VMIntArrayExpr* a, vmint index) { - m_array = a; - m_index = index; + inline vmfloat getUnitFactor() const { + return this->m_array->unitFactorOfElement(this->m_index); } - inline ArrElemRef operator*() { - return *(ArrElemRef*)this; + inline void setUnitFactor(vmfloat factor) { + this->m_array->assignElementUnitFactor(this->m_index, factor); + } + inline vmfloat getProdValue() const { + // simple solution for both vmint and vmfloat, should be fine for just sorting + vmfloat primary = (vmfloat) getPrimValue(); + vmfloat factor = getUnitFactor(); + return primary * factor; } -}; -static inline void swap(ArrElemRef a, ArrElemRef b) { - vmint valueA = a.getValue(); - vmint valueB = b.getValue(); - a.setValue(valueB); - b.setValue(valueA); -} + // allow swap() functions below to access protected methods here + friend void swap(class ArrElemRef a, + class ArrElemRef b); +}; -class ArrExprIter : public ArrElemPOD { +// custom iterator class to be used by std:sort() on our VM arrays +template +class ArrExprIter : public ArrElemPOD { public: typedef std::random_access_iterator_tag iterator_category; - typedef vmint value_type; typedef ssize_t difference_type; - typedef ArrElemPtr pointer; - typedef ArrElemRef reference; - - ArrExprIter(VMIntArrayExpr* a, vmint index) { - m_array = a; - m_index = index; + typedef ArrElemRef ArrElemRef; + typedef ArrElemRef reference; // type used by STL for access by reference + typedef void pointer; // type used by STL for -> operator result, we don't use that operator at all so just void it + typedef ScalarNmbrVal value_type; // type used by STL for temporary values + + ArrExprIter(T_array* a, vmint index) { + this->m_array = a; + this->m_index = index; + } + ArrExprIter(const ArrElemRef& ref) { + this->m_array = ref.m_array; + this->m_index = ref.m_index; } inline ArrElemRef operator*() { - return *(ArrElemRef*)this; + return ArrElemRef(this->m_array, this->m_index); } inline ArrExprIter& operator++() { // prefix increment - ++m_index; + ++(this->m_index); return *this; } inline ArrExprIter& operator--() { // prefix decrement - --m_index; + --(this->m_index); return *this; } inline ArrExprIter operator++(int) { // postfix increment ArrExprIter it = *this; - ++m_index; + ++(this->m_index); return it; } inline ArrExprIter operator--(int) { // postfix decrement ArrExprIter it = *this; - --m_index; + --(this->m_index); return it; } inline ArrExprIter& operator+=(difference_type d) { - m_index += d; + this->m_index += d; return *this; } inline ArrExprIter& operator-=(difference_type d) { - m_index -= d; + this->m_index -= d; return *this; } inline bool operator==(const ArrExprIter& other) const { - return m_index == other.m_index; + return this->m_index == other.m_index; } inline bool operator!=(const ArrExprIter& other) const { - return m_index != other.m_index; + return this->m_index != other.m_index; } inline bool operator<(const ArrExprIter& other) const { - return m_index < other.m_index; + return this->m_index < other.m_index; } inline bool operator>(const ArrExprIter& other) const { - return m_index > other.m_index; + return this->m_index > other.m_index; } inline bool operator<=(const ArrExprIter& other) const { - return m_index <= other.m_index; + return this->m_index <= other.m_index; } inline bool operator>=(const ArrExprIter& other) const { - return m_index >= other.m_index; + return this->m_index >= other.m_index; } inline difference_type operator+(const ArrExprIter& other) const { - return m_index + other.m_index; + return this->m_index + other.m_index; } inline difference_type operator-(const ArrExprIter& other) const { - return m_index - other.m_index; + return this->m_index - other.m_index; } inline ArrExprIter operator-(difference_type d) const { - return ArrExprIter(m_array, m_index - d); + return ArrExprIter(this->m_array, this->m_index - d); } inline ArrExprIter operator+(difference_type d) const { - return ArrExprIter(m_array, m_index + d); + return ArrExprIter(this->m_array, this->m_index + d); } inline ArrExprIter operator*(difference_type factor) const { - return ArrExprIter(m_array, m_index * factor); + return ArrExprIter(this->m_array, this->m_index * factor); } }; +typedef ArrExprIter IntArrExprIter; +typedef ArrExprIter RealArrExprIter; + +// intentionally not a template function to avoid potential clashes with other (i.e. system's) swap() functions +static inline void swap(IntArrExprIter::ArrElemRef a, + IntArrExprIter::ArrElemRef b) +{ + vmint valueA = a.getPrimValue(); + vmint valueB = b.getPrimValue(); + vmfloat factorA = a.getUnitFactor(); + vmfloat factorB = b.getUnitFactor(); + a.setPrimValue(valueB); + a.setUnitFactor(factorB); + b.setPrimValue(valueA); + b.setUnitFactor(factorA); +} + +// intentionally not a template function to avoid potential clashes with other (i.e. system's) swap() functions +static inline void swap(RealArrExprIter::ArrElemRef a, + RealArrExprIter::ArrElemRef b) +{ + vmfloat valueA = a.getPrimValue(); + vmfloat valueB = b.getPrimValue(); + vmfloat factorA = a.getUnitFactor(); + vmfloat factorB = b.getUnitFactor(); + a.setPrimValue(valueB); + a.setUnitFactor(factorB); + b.setPrimValue(valueA); + b.setUnitFactor(factorA); +} + +// used to sort in descending order (unlike the default behaviour of std::sort() which is ascending order) +template // T is either IntArrExprIter or RealArrExprIter struct DescArrExprSorter { - inline bool operator()(const vmint& a, const vmint& b) const { + inline bool operator()(const typename T::value_type a, const typename T::value_type b) const { return a > b; } }; VMFnResult* CoreVMFunction_sort::exec(VMFnArgs* args) { - VMIntArrayExpr* a = args->arg(0)->asIntArray(); - bool bAscending = + const bool bAscending = (args->argsCount() < 2) ? true : !args->arg(1)->asInt()->evalInt(); - vmint n = a->arraySize(); - ArrExprIter itBegin(a, 0); - ArrExprIter itEnd(a, n); - if (bAscending) { - std::sort(itBegin, itEnd); + + if (args->arg(0)->exprType() == INT_ARR_EXPR) { + VMIntArrayExpr* a = args->arg(0)->asIntArray(); + vmint n = a->arraySize(); + IntArrExprIter itBegin(a, 0); + IntArrExprIter itEnd(a, n); + if (bAscending) { + std::sort(itBegin, itEnd); + } else { + DescArrExprSorter sorter; + std::sort(itBegin, itEnd, sorter); + } } else { - DescArrExprSorter sorter; - std::sort(itBegin, itEnd, sorter); + VMRealArrayExpr* a = args->arg(0)->asRealArray(); + vmint n = a->arraySize(); + RealArrExprIter itBegin(a, 0); + RealArrExprIter itEnd(a, n); + if (bAscending) { + std::sort(itBegin, itEnd); + } else { + DescArrExprSorter sorter; + std::sort(itBegin, itEnd, sorter); + } } + return successResult(); } /////////////////////////////////////////////////////////////////////////// // built-in script function: real_to_int() and int() +StdUnit_t CoreVMFunction_real_to_int::returnUnitType(VMFnArgs* args) { + return args->arg(0)->asNumber()->unitType(); +} + +bool CoreVMFunction_real_to_int::returnsFinal(VMFnArgs* args) { + return args->arg(0)->asNumber()->isFinal(); +} + VMFnResult* CoreVMFunction_real_to_int::exec(VMFnArgs* args) { - vmfloat f = args->arg(0)->asReal()->evalReal(); - return successResult(vmint(f)); + VMRealExpr* realExpr = args->arg(0)->asReal(); + vmfloat f = realExpr->evalReal(); + return successResult({ + .value = vmint(f), + .unitFactor = realExpr->unitFactor() + }); } /////////////////////////////////////////////////////////////////////////// // built-in script function: int_to_real() and real() +StdUnit_t CoreVMFunction_int_to_real::returnUnitType(VMFnArgs* args) { + return args->arg(0)->asNumber()->unitType(); +} + +bool CoreVMFunction_int_to_real::returnsFinal(VMFnArgs* args) { + return args->arg(0)->asNumber()->isFinal(); +} + VMFnResult* CoreVMFunction_int_to_real::exec(VMFnArgs* args) { - vmint i = args->arg(0)->asInt()->evalInt(); - return successResult(i); + VMIntExpr* intExpr = args->arg(0)->asInt(); + vmint i = intExpr->evalInt(); + return successResult({ + .value = vmfloat(i), + .unitFactor = intExpr->unitFactor() + }); } } // namespace LinuxSampler