/* * EEZ Modular Firmware * Copyright (C) 2021-present, Envox d.o.o. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include // https://howardhinnant.github.io/date/date.html #include #include #include #include namespace eez { namespace flow { using namespace gui; Value op_add(const Value& a1, const Value& b1) { auto a = a1.getValue(); auto b = b1.getValue(); if (a.isAnyStringType() || b.isAnyStringType()) { Value value1 = a.toString(0x84eafaa8); Value value2 = b.toString(0xd273cab6); auto res = Value::concatenateString(value1, value2); char str1[128]; res.toText(str1, sizeof(str1)); return res; } if (a.isDouble() || b.isDouble()) { return Value(a.toDouble() + b.toDouble(), VALUE_TYPE_DOUBLE); } if (a.isFloat() || b.isFloat()) { return Value(a.toFloat() + b.toFloat(), VALUE_TYPE_FLOAT); } if (a.isInt64() || b.isInt64()) { return Value(a.toInt64() + b.toInt64(), VALUE_TYPE_INT64); } if (a.isInt32OrLess() && b.isInt32OrLess()) { return Value((int)(a.int32Value + b.int32Value), VALUE_TYPE_INT32); } return Value(); } Value op_sub(const Value& a1, const Value& b1) { auto a = a1.getValue(); auto b = b1.getValue(); if (a.isDouble() || b.isDouble()) { return Value(a.toDouble() - b.toDouble(), VALUE_TYPE_DOUBLE); } if (a.isFloat() || b.isFloat()) { return Value(a.toFloat() - b.toFloat(), VALUE_TYPE_FLOAT); } if (a.isInt64() || b.isInt64()) { return Value(a.toInt64() - b.toInt64(), VALUE_TYPE_INT64); } if (a.isInt32OrLess() && b.isInt32OrLess()) { return Value((int)(a.int32Value - b.int32Value), VALUE_TYPE_INT32); } return Value(); } Value op_mul(const Value& a1, const Value& b1) { auto a = a1.getValue(); auto b = b1.getValue(); if (a.isDouble() || b.isDouble()) { return Value(a.toDouble() * b.toDouble(), VALUE_TYPE_DOUBLE); } if (a.isFloat() || b.isFloat()) { return Value(a.toFloat() * b.toFloat(), VALUE_TYPE_FLOAT); } if (a.isInt64() || b.isInt64()) { return Value(a.toInt64() * b.toInt64(), VALUE_TYPE_INT64); } if (a.isInt32OrLess() && b.isInt32OrLess()) { return Value((int)(a.int32Value * b.int32Value), VALUE_TYPE_INT32); } return Value(); } Value op_div(const Value& a1, const Value& b1) { auto a = a1.getValue(); auto b = b1.getValue(); if (a.isDouble() || b.isDouble()) { return Value(a.toDouble() / b.toDouble(), VALUE_TYPE_DOUBLE); } if (a.isFloat() || b.isFloat()) { return Value(a.toFloat() / b.toFloat(), VALUE_TYPE_FLOAT); } if (a.isInt64() || b.isInt64()) { return Value(1.0 * a.toInt64() / b.toInt64(), VALUE_TYPE_DOUBLE); } if (a.isInt32OrLess() && b.isInt32OrLess()) { return Value(1.0 * a.int32Value / b.int32Value, VALUE_TYPE_DOUBLE); } return Value(); } Value op_mod(const Value& a1, const Value& b1) { auto a = a1.getValue(); auto b = b1.getValue(); return Value(a.toDouble() - floor(a.toDouble() / b.toDouble()) * b.toDouble(), VALUE_TYPE_DOUBLE); } Value op_left_shift(const Value& a1, const Value& b1) { auto a = a1.getValue(); auto b = b1.getValue(); return Value((int)(a.toInt32() << b.toInt32()), VALUE_TYPE_INT32); } Value op_right_shift(const Value& a1, const Value& b1) { auto a = a1.getValue(); auto b = b1.getValue(); return Value((int)(a.toInt32() >> b.toInt32()), VALUE_TYPE_INT32); } Value op_binary_and(const Value& a1, const Value& b1) { auto a = a1.getValue(); auto b = b1.getValue(); return Value((int)(a.toBool() & b.toBool()), VALUE_TYPE_INT32); } Value op_binary_or(const Value& a1, const Value& b1) { auto a = a1.getValue(); auto b = b1.getValue(); return Value((int)(a.toBool() | b.toBool()), VALUE_TYPE_INT32); } Value op_binary_xor(const Value& a1, const Value& b1) { auto a = a1.getValue(); auto b = b1.getValue(); return Value((int)(a.toBool() ^ b.toBool()), VALUE_TYPE_INT32); } bool is_equal(const Value& a1, const Value& b1) { auto a = a1.getValue(); auto b = b1.getValue(); auto aIsUndefinedOrNull = a.getType() == VALUE_TYPE_UNDEFINED || a.getType() == VALUE_TYPE_NULL; auto bIsUndefinedOrNull = b.getType() == VALUE_TYPE_UNDEFINED || b.getType() == VALUE_TYPE_NULL; if (aIsUndefinedOrNull) { return bIsUndefinedOrNull; } else if (bIsUndefinedOrNull) { return false; } if (a.isAnyStringType() && b.isAnyStringType()) { const char *aStr = a.getString(); const char *bStr = b.getString(); if (!aStr && !aStr) { return true; } if (!aStr || !bStr) { return false; } return strcmp(aStr, bStr) == 0; } return a.toDouble() == b.toDouble(); } bool is_less(const Value& a1, const Value& b1) { auto a = a1.getValue(); auto b = b1.getValue(); if (a.isAnyStringType() && b.isAnyStringType()) { const char *aStr = a.getString(); const char *bStr = b.getString(); if (!aStr || !bStr) { return false; } return strcmp(aStr, bStr) < 0; } return a.toDouble() < b.toDouble(); } Value op_eq(const Value& a1, const Value& b1) { return Value(is_equal(a1, b1), VALUE_TYPE_BOOLEAN); } Value op_neq(const Value& a1, const Value& b1) { return Value(!is_equal(a1, b1), VALUE_TYPE_BOOLEAN); } Value op_less(const Value& a1, const Value& b1) { return Value(is_less(a1, b1), VALUE_TYPE_BOOLEAN); } Value op_great(const Value& a1, const Value& b1) { return Value(!is_less(a1, b1) && !is_equal(a1, b1), VALUE_TYPE_BOOLEAN); } Value op_less_eq(const Value& a1, const Value& b1) { return Value(is_less(a1, b1) || is_equal(a1, b1), VALUE_TYPE_BOOLEAN); } Value op_great_eq(const Value& a1, const Value& b1) { return Value(!is_less(a1, b1), VALUE_TYPE_BOOLEAN); } bool do_OPERATION_TYPE_ADD(EvalStack &stack) { auto b = stack.pop(); auto a = stack.pop(); auto result = op_add(a, b); if (result.getType() == VALUE_TYPE_UNDEFINED) { return false; } if (!stack.push(result)) { return false; } return true; } bool do_OPERATION_TYPE_SUB(EvalStack &stack) { auto b = stack.pop(); auto a = stack.pop(); auto result = op_sub(a, b); if (result.getType() == VALUE_TYPE_UNDEFINED) { return false; } if (!stack.push(result)) { return false; } return true; } bool do_OPERATION_TYPE_MUL(EvalStack &stack) { auto b = stack.pop(); auto a = stack.pop(); auto result = op_mul(a, b); if (result.getType() == VALUE_TYPE_UNDEFINED) { return false; } if (!stack.push(result)) { return false; } return true; } bool do_OPERATION_TYPE_DIV(EvalStack &stack) { auto b = stack.pop(); auto a = stack.pop(); auto result = op_div(a, b); if (result.getType() == VALUE_TYPE_UNDEFINED) { return false; } if (!stack.push(result)) { return false; } return true; } bool do_OPERATION_TYPE_MOD(EvalStack &stack) { auto b = stack.pop(); auto a = stack.pop(); auto result = op_mod(a, b); if (result.getType() == VALUE_TYPE_UNDEFINED) { return false; } if (!stack.push(result)) { return false; } return true; } bool do_OPERATION_TYPE_LEFT_SHIFT(EvalStack &stack) { auto b = stack.pop(); auto a = stack.pop(); auto result = op_left_shift(a, b); if (result.getType() == VALUE_TYPE_UNDEFINED) { return false; } if (!stack.push(result)) { return false; } return true; } bool do_OPERATION_TYPE_RIGHT_SHIFT(EvalStack &stack) { auto b = stack.pop(); auto a = stack.pop(); auto result = op_right_shift(a, b); if (result.getType() == VALUE_TYPE_UNDEFINED) { return false; } if (!stack.push(result)) { return false; } return true; } bool do_OPERATION_TYPE_BINARY_AND(EvalStack &stack) { auto b = stack.pop(); auto a = stack.pop(); auto result = op_binary_and(a, b); if (result.getType() == VALUE_TYPE_UNDEFINED) { return false; } if (!stack.push(result)) { return false; } return true; } bool do_OPERATION_TYPE_BINARY_OR(EvalStack &stack) { auto b = stack.pop(); auto a = stack.pop(); auto result = op_binary_or(a, b); if (result.getType() == VALUE_TYPE_UNDEFINED) { return false; } if (!stack.push(result)) { return false; } return true; } bool do_OPERATION_TYPE_BINARY_XOR(EvalStack &stack) { auto b = stack.pop(); auto a = stack.pop(); auto result = op_binary_xor(a, b); if (result.getType() == VALUE_TYPE_UNDEFINED) { return false; } if (!stack.push(result)) { return false; } return true; } bool do_OPERATION_TYPE_EQUAL(EvalStack &stack) { auto b = stack.pop(); auto a = stack.pop(); if (!stack.push(op_eq(a, b))) { return false; } return true; } bool do_OPERATION_TYPE_NOT_EQUAL(EvalStack &stack) { auto b = stack.pop(); auto a = stack.pop(); if (!stack.push(op_neq(a, b))) { return false; } return true;} bool do_OPERATION_TYPE_LESS(EvalStack &stack) { auto b = stack.pop(); auto a = stack.pop(); if (!stack.push(op_less(a, b))) { return false; } return true; } bool do_OPERATION_TYPE_GREATER(EvalStack &stack) { auto b = stack.pop(); auto a = stack.pop(); if (!stack.push(op_great(a, b))) { return false; } return true; } bool do_OPERATION_TYPE_LESS_OR_EQUAL(EvalStack &stack) { auto b = stack.pop(); auto a = stack.pop(); if (!stack.push(op_less_eq(a, b))) { return false; } return true; } bool do_OPERATION_TYPE_GREATER_OR_EQUAL(EvalStack &stack) { auto b = stack.pop(); auto a = stack.pop(); if (!stack.push(op_great_eq(a, b))) { return false; } return true; } bool do_OPERATION_TYPE_LOGICAL_AND(EvalStack &stack) { auto b = stack.pop().getValue(); auto a = stack.pop().getValue(); if (!stack.push(Value(a.toBool() && b.toBool(), VALUE_TYPE_BOOLEAN))) { return false; } return true; } bool do_OPERATION_TYPE_LOGICAL_OR(EvalStack &stack) { auto b = stack.pop().getValue(); auto a = stack.pop().getValue(); if (!stack.push(Value(a.toBool() || b.toBool(), VALUE_TYPE_BOOLEAN))) { return false; } return true; } bool do_OPERATION_TYPE_UNARY_PLUS(EvalStack &stack) { auto a = stack.pop().getValue(); if (a.isDouble()) { if (!stack.push(Value(a.getDouble(), VALUE_TYPE_DOUBLE))) { return false; } return true; } if (a.isFloat()) { if (!stack.push(Value(a.toFloat(), VALUE_TYPE_FLOAT))) { return false; } return true; } if (a.isInt64()) { if (!stack.push(Value((int64_t)a.getInt64(), VALUE_TYPE_INT64))) { return false; } return true; } if (a.isInt32()) { if (!stack.push(Value((int)a.getInt32(), VALUE_TYPE_INT32))) { return false; } return true; } if (a.isInt16()) { if (!stack.push(Value((int16_t)a.getInt16(), VALUE_TYPE_INT16))) { return false; } return true; } if (a.isInt8()) { if (!stack.push(Value((int8_t)a.getInt8(), VALUE_TYPE_INT8))) { return false; } return true; } return false; } bool do_OPERATION_TYPE_UNARY_MINUS(EvalStack &stack) { auto a = stack.pop().getValue(); if (a.isDouble()) { if (!stack.push(Value(-a.getDouble(), VALUE_TYPE_DOUBLE))) { return false; } return true; } if (a.isFloat()) { if (!stack.push(Value(-a.toFloat(), VALUE_TYPE_FLOAT))) { return false; } return true; } if (a.isInt64()) { if (!stack.push(Value((int64_t)-a.getInt64(), VALUE_TYPE_INT64))) { return false; } return true; } if (a.isInt32()) { if (!stack.push(Value((int)-a.getInt32(), VALUE_TYPE_INT32))) { return false; } return true; } if (a.isInt16()) { if (!stack.push(Value((int16_t)-a.getInt16(), VALUE_TYPE_INT16))) { return false; } return true; } if (a.isInt8()) { if (!stack.push(Value((int8_t)-a.getInt8(), VALUE_TYPE_INT8))) { return false; } return true; } return false; } bool do_OPERATION_TYPE_BINARY_ONE_COMPLEMENT(EvalStack &stack) { auto a = stack.pop().getValue(); if (a.isInt64()) { if (!stack.push(Value(~a.uint64Value, VALUE_TYPE_UINT64))) { return false; } return true; } if (a.isInt32()) { if (!stack.push(Value(~a.uint32Value, VALUE_TYPE_UINT32))) { return false; } return true; } if (a.isInt16()) { if (!stack.push(Value(~a.uint16Value, VALUE_TYPE_UINT16))) { return false; } return true; } if (a.isInt8()) { if (!stack.push(Value(~a.uint8Value, VALUE_TYPE_UINT8))) { return false; } return true; } return false; } bool do_OPERATION_TYPE_NOT(EvalStack &stack) { auto aValue = stack.pop(); int err; auto a = aValue.toBool(&err); if (err != 0) { return false; } if (!stack.push(Value(!a, VALUE_TYPE_BOOLEAN))) { return false; } return true; } bool do_OPERATION_TYPE_CONDITIONAL(EvalStack &stack) { auto alternate = stack.pop(); auto consequent = stack.pop(); auto conditionValue = stack.pop(); int err; auto condition = conditionValue.toBool(&err); if (err != 0) { return false; } if (!stack.push(condition ? consequent : alternate)) { return false; } return true; } bool do_OPERATION_TYPE_SYSTEM_GET_TICK(EvalStack &stack) { if (!stack.push(Value(millis(), VALUE_TYPE_UINT32))) { return false; } return true; } bool do_OPERATION_TYPE_FLOW_INDEX(EvalStack &stack) { if (!stack.iterators) { return false; } auto a = stack.pop(); int err; auto iteratorIndex = a.toInt32(&err); if (err != 0) { return false; } using eez::gui::MAX_ITERATORS; iteratorIndex = iteratorIndex; if (iteratorIndex < 0 || iteratorIndex >= (int)MAX_ITERATORS) { return false; } if (!stack.push(stack.iterators[iteratorIndex])) { return false; } return true; } bool do_OPERATION_TYPE_FLOW_IS_PAGE_ACTIVE(EvalStack &stack) { bool isActive = false; auto pageIndex = getPageIndex(stack.flowState); if (pageIndex >= 0) { int16_t pageId = (int16_t)(pageIndex + 1); if (stack.flowState->assets == g_externalAssets) { pageId = -pageId; } for (int16_t appContextId = 0; ; appContextId++) { auto appContext = getAppContextFromId(appContextId); if (!appContext) { break; } if (appContext->isPageOnStack(pageId)) { isActive = true; break; } } } if (!stack.push(Value(isActive, VALUE_TYPE_BOOLEAN))) { return false; } return true; } bool do_OPERATION_TYPE_FLOW_PAGE_TIMELINE_POSITION(EvalStack &stack) { if (!stack.push(Value(stack.flowState->timelinePosition, VALUE_TYPE_FLOAT))) { return false; } return true; } bool do_OPERATION_TYPE_FLOW_MAKE_ARRAY_VALUE(EvalStack &stack) { auto arrayTypeValue = stack.pop(); auto arraySizeValue = stack.pop(); int arrayType = arrayTypeValue.getInt(); int arraySize = arraySizeValue.getInt(); auto arrayValue = Value::makeArrayRef(arraySize, arrayType, 0x837260d4); auto array = arrayValue.getArray(); for (int i = 0; i < arraySize; i++) { array->values[i] = stack.pop().getValue(); } if (!stack.push(arrayValue)) { return false; } return true; } bool do_OPERATION_TYPE_FLOW_LANGUAGES(EvalStack &stack) { auto languages = stack.flowState->assets->languages; auto arrayValue = Value::makeArrayRef(languages.count, VALUE_TYPE_STRING, 0xff4787fc); auto array = arrayValue.getArray(); for (uint32_t i = 0; i < languages.count; i++) { array->values[i] = Value((const char *)(languages[i]->languageID)); } if (!stack.push(arrayValue)) { return false; } return true; } bool do_OPERATION_TYPE_FLOW_TRANSLATE(EvalStack &stack) { auto textResourceIndexValue = stack.pop(); int textResourceIndex = textResourceIndexValue.getInt(); int languageIndex = g_selectedLanguage; auto languages = stack.flowState->assets->languages; if (languageIndex >= 0 && languageIndex < (int)languages.count) { auto translations = languages[languageIndex]->translations; if (textResourceIndex >= 0 && textResourceIndex < (int)translations.count) { if (!stack.push(translations[textResourceIndex])) { return false; } return true; } } if (!stack.push("")) { return false; } return true; } bool do_OPERATION_TYPE_FLOW_PARSE_INTEGER(EvalStack &stack) { auto str = stack.pop(); int err; auto value = str.toInt32(&err); if (err) { return false; } if (!stack.push(Value((int)value, VALUE_TYPE_INT32))) { return false; } return true; } bool do_OPERATION_TYPE_FLOW_PARSE_FLOAT(EvalStack &stack) { auto str = stack.pop(); int err; auto value = str.toFloat(&err); if (err) { return false; } if (!stack.push(Value(value, VALUE_TYPE_FLOAT))) { return false; } return true; } bool do_OPERATION_TYPE_FLOW_PARSE_DOUBLE(EvalStack &stack) { auto str = stack.pop(); int err; auto value = str.toDouble(&err); if (err) { return false; } if (!stack.push(Value(value, VALUE_TYPE_DOUBLE))) { return false; } return true; } bool do_OPERATION_TYPE_DATE_NOW(EvalStack &stack) { using namespace std::chrono; milliseconds ms = duration_cast(system_clock::now().time_since_epoch()); if (!stack.push(Value((double)ms.count(), VALUE_TYPE_DATE))) { return false; } return true; } bool do_OPERATION_TYPE_DATE_TO_STRING(EvalStack &stack) { auto a = stack.pop().getValue(); if (a.getType() != VALUE_TYPE_DATE) { return false; } using namespace std; using namespace std::chrono; using namespace date; auto tp = system_clock::time_point(milliseconds((long long)a.getDouble())); stringstream out; out << tp << endl; if (!stack.push(Value::makeStringRef(out.str().c_str(), -1, 0xbe440ec8))) { return false; } return true; } bool do_OPERATION_TYPE_DATE_FROM_STRING(EvalStack &stack) { auto a = stack.pop().getValue(); Value dateStrValue = a.toString(0x99cb1a93); using namespace std; using namespace std::chrono; using namespace date; istringstream in{dateStrValue.getString()}; system_clock::time_point tp; in >> date::parse("%Y-%m-%d %T", tp); milliseconds ms = duration_cast(tp.time_since_epoch()); if (!stack.push(Value((double)ms.count(), VALUE_TYPE_DATE))) { return false; } return true; } bool do_OPERATION_TYPE_MATH_SIN(EvalStack &stack) { auto a = stack.pop().getValue(); if (a.isDouble()) { if (!stack.push(Value(sin(a.getDouble()), VALUE_TYPE_DOUBLE))) { return false; } return true; } if (a.isFloat()) { if (!stack.push(Value(sinf(a.toFloat()), VALUE_TYPE_FLOAT))) { return false; } return true; } if (a.isInt64()) { if (!stack.push(Value(sin(a.toInt64()), VALUE_TYPE_FLOAT))) { return false; } return true; } if (a.isInt32OrLess()) { if (!stack.push(Value(sinf(a.int32Value), VALUE_TYPE_FLOAT))) { return false; } return true; } return false; } bool do_OPERATION_TYPE_MATH_COS(EvalStack &stack) { auto a = stack.pop().getValue(); if (a.isDouble()) { if (!stack.push(Value(cos(a.getDouble()), VALUE_TYPE_DOUBLE))) { return false; } return true; } if (a.isFloat()) { if (!stack.push(Value(cosf(a.toFloat()), VALUE_TYPE_FLOAT))) { return false; } return true; } if (a.isInt64()) { if (!stack.push(Value(cos(a.toInt64()), VALUE_TYPE_FLOAT))) { return false; } return true; } if (a.isInt32OrLess()) { if (!stack.push(Value(cosf(a.int32Value), VALUE_TYPE_FLOAT))) { return false; } return true; } return false; } bool do_OPERATION_TYPE_MATH_LOG(EvalStack &stack) { auto a = stack.pop().getValue(); if (a.isDouble()) { if (!stack.push(Value(log(a.getDouble()), VALUE_TYPE_DOUBLE))) { return false; } return true; } if (a.isFloat()) { if (!stack.push(Value(logf(a.toFloat()), VALUE_TYPE_FLOAT))) { return false; } return true; } if (a.isInt64()) { if (!stack.push(Value(log(a.toInt64()), VALUE_TYPE_FLOAT))) { return false; } return true; } if (a.isInt32OrLess()) { if (!stack.push(Value(logf(a.int32Value), VALUE_TYPE_FLOAT))) { return false; } return true; } return false; } bool do_OPERATION_TYPE_MATH_LOG10(EvalStack &stack) { auto a = stack.pop().getValue(); if (a.isDouble()) { if (!stack.push(Value(log10(a.getDouble()), VALUE_TYPE_DOUBLE))) { return false; } return true; } if (a.isFloat()) { if (!stack.push(Value(log10f(a.toFloat()), VALUE_TYPE_FLOAT))) { return false; } return true; } if (a.isInt64()) { if (!stack.push(Value(log10(a.toInt64()), VALUE_TYPE_FLOAT))) { return false; } return true; } if (a.isInt32OrLess()) { if (!stack.push(Value(log10f(a.int32Value), VALUE_TYPE_FLOAT))) { return false; } return true; } return false; } bool do_OPERATION_TYPE_MATH_ABS(EvalStack &stack) { auto a = stack.pop().getValue(); if (a.isDouble()) { if (!stack.push(Value(abs(a.getDouble()), VALUE_TYPE_DOUBLE))) { return false; } return true; } if (a.isFloat()) { if (!stack.push(Value(abs(a.toFloat()), VALUE_TYPE_FLOAT))) { return false; } return true; } if (a.isInt64()) { if (!stack.push(Value((int64_t)abs(a.getInt64()), VALUE_TYPE_INT64))) { return false; } return true; } if (a.isInt32()) { if (!stack.push(Value((int)abs(a.getInt32()), VALUE_TYPE_INT32))) { return false; } return true; } if (a.isInt16()) { if (!stack.push(Value(abs(a.getInt16()), VALUE_TYPE_INT16))) { return false; } return true; } if (a.isInt8()) { if (!stack.push(Value(abs(a.getInt8()), VALUE_TYPE_INT8))) { return false; } return true; } return false; } bool do_OPERATION_TYPE_MATH_FLOOR(EvalStack &stack) { auto a = stack.pop().getValue(); if (a.isDouble()) { if (!stack.push(Value(floor(a.getDouble()), VALUE_TYPE_DOUBLE))) { return false; } return true; } if (a.isFloat()) { if (!stack.push(Value(floorf(a.toFloat()), VALUE_TYPE_FLOAT))) { return false; } return true; } return false; } bool do_OPERATION_TYPE_MATH_CEIL(EvalStack &stack) { auto a = stack.pop().getValue(); if (a.isDouble()) { if (!stack.push(Value(ceil(a.getDouble()), VALUE_TYPE_DOUBLE))) { return false; } return true; } if (a.isFloat()) { if (!stack.push(Value(ceilf(a.toFloat()), VALUE_TYPE_FLOAT))) { return false; } return true; } return false; } float roundN(float value, unsigned int numDigits) { float pow_10 = pow(10.0f, numDigits); return round(value * pow_10) / pow_10; } double roundN(double value, unsigned int numDigits) { float pow_10 = pow(10.0f, numDigits); return round(value * pow_10) / pow_10; } bool do_OPERATION_TYPE_MATH_ROUND(EvalStack &stack) { auto numArgs = stack.pop().getInt(); auto a = stack.pop().getValue(); unsigned int numDigits; if (numArgs > 1) { auto b = stack.pop().getValue(); numDigits = b.toInt32(); } else { numDigits = 0; } if (a.isDouble()) { if (!stack.push(Value(roundN(a.getDouble(), numDigits), VALUE_TYPE_DOUBLE))) { return false; } return true; } if (a.isFloat()) { if (!stack.push(Value(roundN(a.toFloat(), numDigits), VALUE_TYPE_FLOAT))) { return false; } return true; } if (a.isInt32OrLess()) { if (!stack.push(a)) { return false; } return true; } return false; } bool do_OPERATION_TYPE_MATH_MIN(EvalStack &stack) { auto numArgs = stack.pop().getInt(); double minValue = INFINITY; for (int i = 0; i < numArgs; i++) { auto value = stack.pop().getValue().toDouble(); if (value < minValue) { minValue = value; } } if (!stack.push(Value(minValue, VALUE_TYPE_DOUBLE))) { return false; } return true; } bool do_OPERATION_TYPE_MATH_MAX(EvalStack &stack) { auto numArgs = stack.pop().getInt(); double maxValue = -INFINITY; for (int i = 0; i < numArgs; i++) { auto value = stack.pop().getValue().toDouble(); if (value > maxValue) { maxValue = value; } } if (!stack.push(Value(maxValue, VALUE_TYPE_DOUBLE))) { return false; } return true; } bool do_OPERATION_TYPE_STRING_FIND(EvalStack &stack) { auto b = stack.pop().getValue(); auto a = stack.pop().getValue(); Value aStr = a.toString(0xf616bf4d); Value bStr = b.toString(0x81229133); if (!aStr.getString() || !bStr.getString()) { if (!stack.push(Value(-1, VALUE_TYPE_INT32))) { return false; } } else { const char *pos = strstr(aStr.getString(), bStr.getString()); if (!pos) { if (!stack.push(Value((int)(pos - aStr.getString()), VALUE_TYPE_INT32))) { return false; } } else { if (!stack.push(Value(-1, VALUE_TYPE_INT32))) { return false; } } } return true; } bool do_OPERATION_TYPE_STRING_PAD_START(EvalStack &stack) { auto a = stack.pop().getValue(); auto b = stack.pop().getValue(); auto c = stack.pop().getValue(); auto str = a.toString(0xcf6aabe6); if (!str.getString()) { return false; } int strLen = strlen(str.getString()); int err; int targetLength = b.toInt32(&err); if (err) { return false; } if (targetLength < strLen) { targetLength = strLen; } auto padStr = c.toString(0x81353bd7); if (!padStr.getString()) { return false; } int padStrLen = strlen(padStr.getString()); Value resultValue = eez::gui::Value::makeStringRef("", targetLength, 0xf43b14dd); if (resultValue.type == VALUE_TYPE_NULL) { return false; } char *resultStr = (char *)resultValue.getString(); auto n = targetLength - strLen; stringCopy(resultStr + (targetLength - strLen), strLen + 1, str.getString()); for (int i = 0; i < n; i++) { resultStr[i] = padStr.getString()[i % padStrLen]; } if (!stack.push(resultValue)) { return false; } return true; } bool do_OPERATION_TYPE_STRING_SPLIT(EvalStack &stack) { auto strValue = stack.pop().getValue(); auto delimValue = stack.pop().getValue(); auto str = strValue.getString(); if (!str) { return false; } auto delim = delimValue.getString(); if (!delim) { return false; } auto strLen = strlen(str); char *strCopy = (char *)eez::alloc(strLen + 1, 0xea9d0bc0); stringCopy(strCopy, strLen + 1, str); // get num parts size_t arraySize = 0; char *token = strtok(strCopy, delim); while (token != NULL) { arraySize++; token = strtok(NULL, delim); } eez::free(strCopy); strCopy = (char *)eez::alloc(strLen + 1, 0xea9d0bc1); stringCopy(strCopy, strLen + 1, str); // make array auto arrayValue = Value::makeArrayRef(arraySize, VALUE_TYPE_STRING, 0xe82675d4); auto array = arrayValue.getArray(); int i = 0; token = strtok(strCopy, delim); while (token != NULL) { array->values[i++] = Value::makeStringRef(token, -1, 0x45209ec0); token = strtok(NULL, delim); } eez::free(strCopy); if (!stack.push(arrayValue)) { return false; } return true; } bool do_OPERATION_TYPE_ARRAY_LENGTH(EvalStack &stack) { auto a = stack.pop().getValue(); if (a.getType() == VALUE_TYPE_ARRAY || a.getType() == VALUE_TYPE_ARRAY_REF) { auto array = a.getArray(); if (!stack.push(Value(array->arraySize, VALUE_TYPE_UINT32))) { return false; } return true; } return false; } bool do_OPERATION_TYPE_ARRAY_SLICE(EvalStack &stack) { // TODO return false; } EvalOperation g_evalOperations[] = { do_OPERATION_TYPE_ADD, do_OPERATION_TYPE_SUB, do_OPERATION_TYPE_MUL, do_OPERATION_TYPE_DIV, do_OPERATION_TYPE_MOD, do_OPERATION_TYPE_LEFT_SHIFT, do_OPERATION_TYPE_RIGHT_SHIFT, do_OPERATION_TYPE_BINARY_AND, do_OPERATION_TYPE_BINARY_OR, do_OPERATION_TYPE_BINARY_XOR, do_OPERATION_TYPE_EQUAL, do_OPERATION_TYPE_NOT_EQUAL, do_OPERATION_TYPE_LESS, do_OPERATION_TYPE_GREATER, do_OPERATION_TYPE_LESS_OR_EQUAL, do_OPERATION_TYPE_GREATER_OR_EQUAL, do_OPERATION_TYPE_LOGICAL_AND, do_OPERATION_TYPE_LOGICAL_OR, do_OPERATION_TYPE_UNARY_PLUS, do_OPERATION_TYPE_UNARY_MINUS, do_OPERATION_TYPE_BINARY_ONE_COMPLEMENT, do_OPERATION_TYPE_NOT, do_OPERATION_TYPE_CONDITIONAL, do_OPERATION_TYPE_SYSTEM_GET_TICK, do_OPERATION_TYPE_FLOW_INDEX, do_OPERATION_TYPE_FLOW_IS_PAGE_ACTIVE, do_OPERATION_TYPE_FLOW_PAGE_TIMELINE_POSITION, do_OPERATION_TYPE_FLOW_MAKE_ARRAY_VALUE, do_OPERATION_TYPE_FLOW_MAKE_ARRAY_VALUE, do_OPERATION_TYPE_FLOW_LANGUAGES, do_OPERATION_TYPE_FLOW_TRANSLATE, do_OPERATION_TYPE_FLOW_PARSE_INTEGER, do_OPERATION_TYPE_FLOW_PARSE_FLOAT, do_OPERATION_TYPE_FLOW_PARSE_DOUBLE, do_OPERATION_TYPE_DATE_NOW, do_OPERATION_TYPE_DATE_TO_STRING, do_OPERATION_TYPE_DATE_FROM_STRING, do_OPERATION_TYPE_MATH_SIN, do_OPERATION_TYPE_MATH_COS, do_OPERATION_TYPE_MATH_LOG, do_OPERATION_TYPE_MATH_LOG10, do_OPERATION_TYPE_MATH_ABS, do_OPERATION_TYPE_MATH_FLOOR, do_OPERATION_TYPE_MATH_CEIL, do_OPERATION_TYPE_MATH_ROUND, do_OPERATION_TYPE_MATH_MIN, do_OPERATION_TYPE_MATH_MAX, do_OPERATION_TYPE_STRING_FIND, do_OPERATION_TYPE_STRING_PAD_START, do_OPERATION_TYPE_STRING_SPLIT, do_OPERATION_TYPE_ARRAY_LENGTH, do_OPERATION_TYPE_ARRAY_SLICE }; } // namespace flow } // namespace eez