Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
alexzhirkevich committed Jul 29, 2024
1 parent 47db2f1 commit dda6ad4
Show file tree
Hide file tree
Showing 24 changed files with 423 additions and 229 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,134 +4,100 @@ import io.github.alexzhirkevich.compottie.internal.animation.expressions.operati
import io.github.alexzhirkevich.compottie.internal.animation.expressions.operations.unresolvedReference


internal enum class VariableScope {
Global, Block
internal enum class VariableType {
Var, Let, Const
}

internal interface EvaluationContext {

val randomSource : RandomSource
val random: RandomSource

fun registerFunction(function: OpFunction)
fun getVariable(name: String): Any?

fun getFunction(name: String) : OpFunction?

fun getVariable(name : String) : Any?

fun setVariable(name: String, value : Any, scope: VariableScope?)

fun withScope(extraVariables : Map<String ,Any>, block : (EvaluationContext) -> Any) : Any
fun setVariable(name: String, value: Any, type: VariableType?)

fun withScope(
extraVariables: Map<String, Pair<VariableType, Any>> = emptyMap(),
block : (EvaluationContext) -> Any
) : Any
}

internal class DefaultEvaluatorContext(
override val randomSource: RandomSource = RandomSource(),
) : EvaluationContext {

private val globalVariables: MutableMap<String, Any> = mutableMapOf()

private val blockVariables: MutableMap<String, Any> = mutableMapOf()
override val random: RandomSource = RandomSource(),
) : BaseEvaluationContext() {

private val functions: MutableMap<String, OpFunction> = mutableMapOf()

private val child by lazy {
BlockEvaluatorContext(this)
}

val result: Any? get() = getVariable("\$bm_rt")

fun reset() {
globalVariables.clear()
blockVariables.clear()
}

override fun setVariable(name: String, value: Any, scope: VariableScope?) {
val map = when {
scope == VariableScope.Global -> globalVariables
scope == VariableScope.Block -> blockVariables
name in blockVariables -> blockVariables
name in globalVariables -> globalVariables
else -> unresolvedReference(name)
}
map[name] = value
variables.clear()
functions.clear()
}
}

override fun registerFunction(function: OpFunction) {
functions[function.name] = function
}
private class BlockEvaluatorContext(
private val parent : EvaluationContext
) : BaseEvaluationContext() {

override fun getFunction(name: String): OpFunction? {
return functions[name]
}
override val random: RandomSource
get() = parent.random

override fun getVariable(name: String): Any? {
return when {
blockVariables.containsKey(name) -> blockVariables[name]
globalVariables.containsKey(name) -> globalVariables[name]
else -> null
return if (name in variables) {
super.getVariable(name)
} else {
parent.getVariable(name)
}
}

override fun withScope(
extraVariables: Map<String, Any>,
block: (EvaluationContext) -> Any
) : Any {
child.reset()
extraVariables.forEach { (n, v) ->
child.setVariable(n, v, VariableScope.Block)
override fun setVariable(name: String, value: Any, type: VariableType?) {
when {
type == VariableType.Var -> parent.setVariable(name, value, type)
type != null || name in variables -> super.setVariable(name, value, type)
else -> parent.setVariable(name, value, type)
}
return block(child)
}
}

private class BlockEvaluatorContext(
private val parent : EvaluationContext
) : EvaluationContext {

private val child by lazy {
BlockEvaluatorContext(this)
fun reset() {
variables.clear()
}
}

private val scopeVariables: MutableMap<String, Any> = mutableMapOf()
private val scopeFunctions: MutableMap<String, OpFunction> = mutableMapOf()

override val randomSource: RandomSource
get() = parent.randomSource
internal abstract class BaseEvaluationContext : EvaluationContext {

override fun registerFunction(function: OpFunction) {
scopeFunctions[function.name] = function
}
protected val variables: MutableMap<String, Pair<VariableType, Any>> = mutableMapOf()

override fun getFunction(name: String): OpFunction? {
return scopeFunctions[name] ?: parent.getFunction(name)
private val child by lazy {
BlockEvaluatorContext(this)
}

override fun getVariable(name: String): Any? {
return if (scopeVariables.containsKey(name)) {
scopeVariables[name]
} else {
parent.getVariable(name)
override fun setVariable(name: String, value: Any, type: VariableType?) {
if (type == null && name !in variables) {
unresolvedReference(name)
}
if (type != null && name in variables) {
error("Identifier '$name' is already declared")
}
if (type == null && variables[name]?.first == VariableType.Const) {
error("TypeError: Assignment to constant variable ('$name')")
}
variables[name] = (type ?: variables[name]?.first)!! to value
}

override fun setVariable(name: String, value: Any, scope: VariableScope?) {
when {
scope == VariableScope.Global -> parent.setVariable(name, value, scope)
scope == VariableScope.Block || name in scopeVariables -> scopeVariables[name] = value
else -> parent.setVariable(name, value, scope)
}
override fun getVariable(name: String): Any? {
return variables[name]?.second
}

override fun withScope(extraVariables: Map<String, Any>, block: (EvaluationContext) -> Any) : Any {
final override fun withScope(
extraVariables: Map<String, Pair<VariableType, Any>>,
block: (EvaluationContext) -> Any
) : Any {
child.reset()
extraVariables.forEach { (n, v) ->
child.setVariable(n, v, VariableScope.Block)
child.setVariable(n, v.second, v.first)
}
return block(child)
}

fun reset() {
scopeFunctions.clear()
scopeVariables.clear()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ internal interface ExpressionEvaluator {
fun RawProperty<*>.evaluate(state: AnimationState): Any
}


internal fun ExpressionEvaluator(expression: String) : ExpressionEvaluator =
ExpressionEvaluatorImpl(expression)
internal fun ExpressionEvaluator(expression: String, catchErrors : Boolean = true) : ExpressionEvaluator =
ExpressionEvaluatorImpl(expression, catchErrors)

internal object RawExpressionEvaluator : ExpressionEvaluator {
override fun RawProperty<*>.evaluate(state: AnimationState): Any = raw(state)
}


private class ExpressionEvaluatorImpl(expr : String) : ExpressionEvaluator {
private class ExpressionEvaluatorImpl(
expr : String,
private val catchErrors: Boolean = true
) : ExpressionEvaluator {

private val context = DefaultEvaluatorContext()

Expand All @@ -32,17 +34,22 @@ private class ExpressionEvaluatorImpl(expr : String) : ExpressionEvaluator {
return raw(state)

return try {
context.reset()
expression.invoke(this, context, state)
context.result?.toListOrThis()
} catch (t: Throwable) {
if (t.message !in errors) {
errors += t.message
Compottie.logger?.warn(
"Error occurred in a Lottie expression. Try to disable expressions for Painter using enableExpressions=false: ${t.message}"
)
if (catchErrors){
if (t.message !in errors) {
errors += t.message
Compottie.logger?.warn(
"Error occurred in a Lottie expression. Try to disable expressions for Painter using enableExpressions=false: ${t.message}"
)
}
} else {
throw t
}
null
} finally {
context.reset()
} ?: raw(state)
}
}
Expand Down
Loading

0 comments on commit dda6ad4

Please sign in to comment.