From f5ea7be16d1f39b76feaccede74ac802ac275e53 Mon Sep 17 00:00:00 2001 From: tayrinn Date: Wed, 22 Mar 2023 13:52:04 +0300 Subject: [PATCH] add getValue functions --- .../java/com/yandex/div/evaluable/Function.kt | 2 +- .../function/BuiltinFunctionProvider.kt | 14 +- .../evaluable/function/VariableFunctions.kt | 146 ++++++++++++++++++ .../com/yandex/div/evaluable/EvaluableTest.kt | 2 +- .../EvaluableMultiplatformTest.kt | 2 +- .../SignaturesMultiplatformTest.kt | 6 +- .../evaluable/repl/EvaluableReplRuntime.kt | 2 +- .../main/java/com/yandex/div/core/DivKit.kt | 2 +- .../expression/ExpressionsRuntimeProvider.kt | 4 +- .../expression/ExpressionResolverImplTest.kt | 6 +- .../function_signatures_std.json | 12 +- .../functions_variables.json | 19 +++ 12 files changed, 198 insertions(+), 19 deletions(-) create mode 100644 client/android/div-evaluable/src/main/java/com/yandex/div/evaluable/function/VariableFunctions.kt diff --git a/client/android/div-evaluable/src/main/java/com/yandex/div/evaluable/Function.kt b/client/android/div-evaluable/src/main/java/com/yandex/div/evaluable/Function.kt index 7a1aa50ac..ba305ca1c 100644 --- a/client/android/div-evaluable/src/main/java/com/yandex/div/evaluable/Function.kt +++ b/client/android/div-evaluable/src/main/java/com/yandex/div/evaluable/Function.kt @@ -1,6 +1,6 @@ package com.yandex.div.evaluable -abstract class Function { +abstract class Function(open val variableProvider: VariableProvider? = null) { abstract val name: String abstract val declaredArgs: List diff --git a/client/android/div-evaluable/src/main/java/com/yandex/div/evaluable/function/BuiltinFunctionProvider.kt b/client/android/div-evaluable/src/main/java/com/yandex/div/evaluable/function/BuiltinFunctionProvider.kt index 6d6007844..68dde4b01 100644 --- a/client/android/div-evaluable/src/main/java/com/yandex/div/evaluable/function/BuiltinFunctionProvider.kt +++ b/client/android/div-evaluable/src/main/java/com/yandex/div/evaluable/function/BuiltinFunctionProvider.kt @@ -1,11 +1,9 @@ package com.yandex.div.evaluable.function -import com.yandex.div.evaluable.EvaluableType +import com.yandex.div.evaluable.* import com.yandex.div.evaluable.Function -import com.yandex.div.evaluable.FunctionArgument -import com.yandex.div.evaluable.FunctionProvider -object BuiltinFunctionProvider : FunctionProvider { +class BuiltinFunctionProvider(variableProvider: VariableProvider) : FunctionProvider { private val registry = FunctionRegistry() @@ -139,6 +137,14 @@ object BuiltinFunctionProvider : FunctionProvider { registry.register(NumberToString) registry.register(BooleanToString) registry.register(ColorToString) + + // Variable functions + registry.register(GetIntegerValue(variableProvider)) + registry.register(GetNumberValue(variableProvider)) + registry.register(GetStringValue(variableProvider)) + registry.register(GetColorValueString(variableProvider)) + registry.register(GetColorValue(variableProvider)) + registry.register(GetBooleanValue(variableProvider)) } override fun get(name: String, args: List): Function { diff --git a/client/android/div-evaluable/src/main/java/com/yandex/div/evaluable/function/VariableFunctions.kt b/client/android/div-evaluable/src/main/java/com/yandex/div/evaluable/function/VariableFunctions.kt new file mode 100644 index 000000000..ee6b89f0f --- /dev/null +++ b/client/android/div-evaluable/src/main/java/com/yandex/div/evaluable/function/VariableFunctions.kt @@ -0,0 +1,146 @@ +package com.yandex.div.evaluable.function + +import com.yandex.div.evaluable.* +import com.yandex.div.evaluable.Function +import com.yandex.div.evaluable.types.Color + +internal class GetIntegerValue(override val variableProvider: VariableProvider) : Function(variableProvider) { + + override val name = "getIntegerValue" + + override val declaredArgs = listOf( + FunctionArgument(type = EvaluableType.STRING), // variable name + FunctionArgument(type = EvaluableType.INTEGER), // fallback + ) + + override val resultType = EvaluableType.INTEGER + + override val isPure = false + + override fun evaluate(args: List): Any { + val variableName = args[0] as String + val fallbackValue = args[1] as Int + val variableValue = variableProvider.get(variableName) as? Int + + return variableValue ?: fallbackValue + } + +} + +internal class GetNumberValue(override val variableProvider: VariableProvider) : Function(variableProvider) { + + override val name = "getNumberValue" + + override val declaredArgs = listOf( + FunctionArgument(type = EvaluableType.STRING), // variable name + FunctionArgument(type = EvaluableType.NUMBER), // fallback + ) + + override val resultType = EvaluableType.NUMBER + + override val isPure = false + + override fun evaluate(args: List): Any { + val variableName = args[0] as String + val fallbackValue = args[1] as Number + val variableValue = if (variableProvider.get(variableName) is Int) + null + else + variableProvider.get(variableName) as? Number + + return variableValue ?: fallbackValue + } + +} + +internal class GetStringValue(override val variableProvider: VariableProvider) : Function(variableProvider) { + + override val name = "getStringValue" + + override val declaredArgs = listOf( + FunctionArgument(type = EvaluableType.STRING), // variable name + FunctionArgument(type = EvaluableType.STRING), // fallback + ) + + override val resultType = EvaluableType.STRING + + override val isPure = false + + override fun evaluate(args: List): Any { + val variableName = args[0] as String + val fallbackValue = args[1] as String + val variableValue = variableProvider.get(variableName) as? String + + return variableValue ?: fallbackValue + } + +} + +internal class GetColorValueString(override val variableProvider: VariableProvider) : Function(variableProvider) { + + override val name = "getColorValue" + + override val declaredArgs = listOf( + FunctionArgument(type = EvaluableType.STRING), // variable name + FunctionArgument(type = EvaluableType.STRING), // fallback + ) + + override val resultType = EvaluableType.COLOR + + override val isPure = false + + override fun evaluate(args: List): Any { + val variableName = args[0] as String + val fallbackValue = Color.parse(args[1] as String) + val variableValue = variableProvider.get(variableName) as? Color + + return variableValue ?: fallbackValue + } + +} + +internal class GetColorValue(override val variableProvider: VariableProvider) : Function(variableProvider) { + + override val name = "getColorValue" + + override val declaredArgs = listOf( + FunctionArgument(type = EvaluableType.STRING), // variable name + FunctionArgument(type = EvaluableType.COLOR), // fallback + ) + + override val resultType = EvaluableType.COLOR + + override val isPure = false + + override fun evaluate(args: List): Any { + val variableName = args[0] as String + val fallbackValue = args[1] as Color + val variableValue = variableProvider.get(variableName) as? Color + + return variableValue ?: fallbackValue + } + +} + +internal class GetBooleanValue(override val variableProvider: VariableProvider) : Function(variableProvider) { + + override val name = "getBooleanValue" + + override val declaredArgs = listOf( + FunctionArgument(type = EvaluableType.STRING), // variable name + FunctionArgument(type = EvaluableType.BOOLEAN), // fallback + ) + + override val resultType = EvaluableType.BOOLEAN + + override val isPure = false + + override fun evaluate(args: List): Any { + val variableName = args[0] as String + val fallbackValue = args[1] as Boolean + val variableValue = variableProvider.get(variableName) as? Boolean + + return variableValue ?: fallbackValue + } + +} diff --git a/client/android/div-evaluable/src/test/java/com/yandex/div/evaluable/EvaluableTest.kt b/client/android/div-evaluable/src/test/java/com/yandex/div/evaluable/EvaluableTest.kt index a8d0f1d96..7969e894a 100644 --- a/client/android/div-evaluable/src/test/java/com/yandex/div/evaluable/EvaluableTest.kt +++ b/client/android/div-evaluable/src/test/java/com/yandex/div/evaluable/EvaluableTest.kt @@ -14,7 +14,7 @@ private typealias NumOfInvokes = Int class EvaluableTest { private val variableProvider = mock() - private val functionProvider = BuiltinFunctionProvider + private val functionProvider = BuiltinFunctionProvider(variableProvider) private val evaluator = Evaluator(variableProvider, functionProvider) // Ternary Operator Test diff --git a/client/android/div-evaluable/src/test/java/com/yandex/div/evaluable/multiplatform/EvaluableMultiplatformTest.kt b/client/android/div-evaluable/src/test/java/com/yandex/div/evaluable/multiplatform/EvaluableMultiplatformTest.kt index 19ed39737..e7345f39d 100644 --- a/client/android/div-evaluable/src/test/java/com/yandex/div/evaluable/multiplatform/EvaluableMultiplatformTest.kt +++ b/client/android/div-evaluable/src/test/java/com/yandex/div/evaluable/multiplatform/EvaluableMultiplatformTest.kt @@ -25,7 +25,7 @@ import java.io.File class EvaluableMultiplatformTest(private val caseOrError: TestCaseOrError) { private val variableProvider = mock() - private val evaluator = Evaluator(variableProvider, BuiltinFunctionProvider) + private val evaluator = Evaluator(variableProvider, BuiltinFunctionProvider(variableProvider)) private lateinit var testCase: ExpressionTestCase @Before diff --git a/client/android/div-evaluable/src/test/java/com/yandex/div/evaluable/multiplatform/SignaturesMultiplatformTest.kt b/client/android/div-evaluable/src/test/java/com/yandex/div/evaluable/multiplatform/SignaturesMultiplatformTest.kt index 1b7a30a9c..f0b82e428 100644 --- a/client/android/div-evaluable/src/test/java/com/yandex/div/evaluable/multiplatform/SignaturesMultiplatformTest.kt +++ b/client/android/div-evaluable/src/test/java/com/yandex/div/evaluable/multiplatform/SignaturesMultiplatformTest.kt @@ -3,6 +3,7 @@ package com.yandex.div.evaluable.multiplatform import com.yandex.div.evaluable.EvaluableException import com.yandex.div.evaluable.EvaluableType import com.yandex.div.evaluable.FunctionArgument +import com.yandex.div.evaluable.VariableProvider import com.yandex.div.evaluable.function.BuiltinFunctionProvider import com.yandex.div.evaluable.multiplatform.MultiplatformTestUtils.isForAndroidPlatform import com.yandex.div.evaluable.multiplatform.MultiplatformTestUtils.parsePlatform @@ -12,12 +13,15 @@ import org.json.JSONObject import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized +import org.mockito.Mockito.mock +import org.mockito.kotlin.mock import java.io.File import java.lang.RuntimeException @RunWith(Parameterized::class) class SignaturesMultiplatformTest(caseOrError: TestCaseOrError) { - private val functionProvider = BuiltinFunctionProvider + private val variableProvider = mock() + private val functionProvider = BuiltinFunctionProvider(variableProvider) private val signature = caseOrError.getCaseOrThrow() @Test diff --git a/client/android/div-evaluable/src/test/java/com/yandex/div/evaluable/repl/EvaluableReplRuntime.kt b/client/android/div-evaluable/src/test/java/com/yandex/div/evaluable/repl/EvaluableReplRuntime.kt index 50b56a63c..c9b28c7ea 100644 --- a/client/android/div-evaluable/src/test/java/com/yandex/div/evaluable/repl/EvaluableReplRuntime.kt +++ b/client/android/div-evaluable/src/test/java/com/yandex/div/evaluable/repl/EvaluableReplRuntime.kt @@ -13,7 +13,7 @@ import com.yandex.div.evaluable.types.DateTime internal object EvaluableReplRuntime { private val variableProvider = VariableProvider { variableName -> variableList[variableName] } - private val functionProvider = BuiltinFunctionProvider + private val functionProvider = BuiltinFunctionProvider(variableProvider) private val evaluator = Evaluator(variableProvider, functionProvider) private val variableList = mutableMapOf() diff --git a/client/android/div/src/main/java/com/yandex/div/core/DivKit.kt b/client/android/div/src/main/java/com/yandex/div/core/DivKit.kt index 996413ded..8abc36b58 100644 --- a/client/android/div/src/main/java/com/yandex/div/core/DivKit.kt +++ b/client/android/div/src/main/java/com/yandex/div/core/DivKit.kt @@ -80,7 +80,7 @@ class DivKit private constructor( @AnyThread fun warmUpStatics() { // Bootstraps all built in expression functions. - BuiltinFunctionProvider + BuiltinFunctionProvider { _ -> null } } @JvmStatic diff --git a/client/android/div/src/main/java/com/yandex/div/core/expression/ExpressionsRuntimeProvider.kt b/client/android/div/src/main/java/com/yandex/div/core/expression/ExpressionsRuntimeProvider.kt index be2f1f5dd..47ce41cbc 100644 --- a/client/android/div/src/main/java/com/yandex/div/core/expression/ExpressionsRuntimeProvider.kt +++ b/client/android/div/src/main/java/com/yandex/div/core/expression/ExpressionsRuntimeProvider.kt @@ -83,7 +83,9 @@ internal class ExpressionsRuntimeProvider @Inject constructor( addSource(globalVariableController.variableSource) } - val evaluatorFactory = ExpressionEvaluatorFactory(BuiltinFunctionProvider) + val evaluatorFactory = ExpressionEvaluatorFactory(BuiltinFunctionProvider { + variableName -> variableController.getMutableVariable(variableName)?.getValue() + }) val errorCollector = errorCollectors.getOrCreate(tag, data) val expressionResolver = ExpressionResolverImpl( variableController, diff --git a/client/android/div/src/test/java/com/yandex/div/core/expression/ExpressionResolverImplTest.kt b/client/android/div/src/test/java/com/yandex/div/core/expression/ExpressionResolverImplTest.kt index d398a0122..a1c92ef2a 100644 --- a/client/android/div/src/test/java/com/yandex/div/core/expression/ExpressionResolverImplTest.kt +++ b/client/android/div/src/test/java/com/yandex/div/core/expression/ExpressionResolverImplTest.kt @@ -7,6 +7,7 @@ import com.yandex.div.data.Variable import com.yandex.div.evaluable.EvaluableType import com.yandex.div.evaluable.Function import com.yandex.div.evaluable.FunctionProvider +import com.yandex.div.evaluable.VariableProvider import com.yandex.div.evaluable.function.BuiltinFunctionProvider import com.yandex.div.evaluable.types.DateTime import com.yandex.div.internal.parser.* @@ -28,6 +29,7 @@ import java.util.* class ExpressionResolverImplTest { @get:Rule val rule = EnableAssertsRule(false) + private val variableProvider = mock() private val variables = mutableMapOf().also { map -> listOf( @@ -66,7 +68,7 @@ class ExpressionResolverImplTest { private val underTest = ExpressionResolverImpl( externalVariables, - ExpressionEvaluatorFactory(BuiltinFunctionProvider), + ExpressionEvaluatorFactory(BuiltinFunctionProvider(variableProvider)), mock(), ) @@ -76,7 +78,7 @@ class ExpressionResolverImplTest { ExpressionEvaluatorFactory(object : FunctionProvider { override fun get(name: String, args: List): Function { callback() - return BuiltinFunctionProvider.get(name, args) + return BuiltinFunctionProvider(variableProvider).get(name, args) } }), mock() diff --git a/test_data/expression_test_data/function_signatures_std.json b/test_data/expression_test_data/function_signatures_std.json index cc315c5a3..39b735170 100644 --- a/test_data/expression_test_data/function_signatures_std.json +++ b/test_data/expression_test_data/function_signatures_std.json @@ -197,7 +197,7 @@ } ], "result_type": "integer", - "platforms": ["web"] + "platforms": [ "android", "web" ] }, { "name": "getNumberValue(string, number) number", @@ -214,7 +214,7 @@ } ], "result_type": "number", - "platforms": ["web"] + "platforms": [ "android", "web" ] }, { "name": "getStringValue(string, string) string", @@ -231,7 +231,7 @@ } ], "result_type": "string", - "platforms": ["web"] + "platforms": [ "android", "web" ] }, { "name": "getUrlValue(string, url) url", @@ -282,7 +282,7 @@ } ], "result_type": "color", - "platforms": ["web"] + "platforms": [ "android", "web" ] }, { "name": "getColorValue(string, string) color", @@ -299,7 +299,7 @@ } ], "result_type": "color", - "platforms": ["web"] + "platforms": [ "android", "web" ] }, { "name": "getBooleanValue(string, boolean) boolean", @@ -316,7 +316,7 @@ } ], "result_type": "boolean", - "platforms": ["web"] + "platforms": [ "android", "web" ] } ] } diff --git a/test_data/expression_test_data/functions_variables.json b/test_data/expression_test_data/functions_variables.json index e4e772f54..45245b3fe 100644 --- a/test_data/expression_test_data/functions_variables.json +++ b/test_data/expression_test_data/functions_variables.json @@ -15,6 +15,7 @@ } ], "platforms": [ + "android", "web" ] }, @@ -33,6 +34,7 @@ } ], "platforms": [ + "android", "web" ] }, @@ -45,6 +47,7 @@ }, "variables": [], "platforms": [ + "android", "web" ] }, @@ -63,6 +66,7 @@ } ], "platforms": [ + "android", "web" ] }, @@ -81,6 +85,7 @@ } ], "platforms": [ + "android", "web" ] }, @@ -93,6 +98,7 @@ }, "variables": [], "platforms": [ + "android", "web" ] }, @@ -111,6 +117,7 @@ } ], "platforms": [ + "android", "web" ] }, @@ -129,6 +136,7 @@ } ], "platforms": [ + "android", "web" ] }, @@ -141,6 +149,7 @@ }, "variables": [], "platforms": [ + "android", "web" ] }, @@ -159,6 +168,7 @@ } ], "platforms": [ + "android", "web" ] }, @@ -177,6 +187,7 @@ } ], "platforms": [ + "android", "web" ] }, @@ -189,6 +200,7 @@ }, "variables": [], "platforms": [ + "android", "web" ] }, @@ -212,6 +224,7 @@ } ], "platforms": [ + "android", "web" ] }, @@ -235,6 +248,7 @@ } ], "platforms": [ + "android", "web" ] }, @@ -253,6 +267,7 @@ } ], "platforms": [ + "android", "web" ] }, @@ -271,6 +286,7 @@ } ], "platforms": [ + "android", "web" ] }, @@ -289,6 +305,7 @@ } ], "platforms": [ + "android", "web" ] }, @@ -319,6 +336,7 @@ } ], "platforms": [ + "android", "web" ] }, @@ -349,6 +367,7 @@ }, "variables": [], "platforms": [ + "android", "web" ] },