Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Zhirkevich Alexander Y authored and Zhirkevich Alexander Y committed Jul 26, 2024
1 parent aeff2bf commit e5214d5
Show file tree
Hide file tree
Showing 33 changed files with 349 additions and 135 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.alexzhirkevich.compottie.internal.animation.expressions

import io.github.alexzhirkevich.compottie.internal.animation.expressions.operations.keywords.OpFunction
import io.github.alexzhirkevich.compottie.internal.animation.expressions.operations.unresolvedReference


internal enum class VariableScope {
Expand All @@ -17,7 +18,7 @@ internal interface EvaluationContext {

fun getVariable(name : String) : Any?

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

fun withScope(extraVariables : Map<String ,Any>, block : (EvaluationContext) -> Any) : Any

Expand All @@ -44,10 +45,13 @@ internal class DefaultEvaluatorContext(
blockVariables.clear()
}

override fun setVariable(name: String, value: Any, scope: VariableScope) {
val map = when (scope) {
VariableScope.Global -> globalVariables
VariableScope.Block -> blockVariables
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
}
Expand Down Expand Up @@ -110,10 +114,11 @@ private class BlockEvaluatorContext(
}
}

override fun setVariable(name: String, value: Any, scope: VariableScope) {
when (scope) {
VariableScope.Global -> parent.setVariable(name, value, scope)
VariableScope.Block -> scopeVariables[name] = 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)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import io.github.alexzhirkevich.compottie.internal.AnimationState
import io.github.alexzhirkevich.compottie.internal.animation.expressions.operations.time.OpGetTime
import io.github.alexzhirkevich.compottie.internal.animation.expressions.operations.value.OpAssign

internal enum class LogicalContext {
And, Or, Compare
}

internal interface ExpressionContext<T> : Expression {

fun interpret(callable: String?, args: List<Expression>?): Expression?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ private class ExpressionEvaluatorImpl(expr : String) : ExpressionEvaluator {
null
} ?: raw(state)
}
}
}

private fun Any.toListOrThis() : Any{
return when (this){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,29 @@ import io.github.alexzhirkevich.compottie.internal.animation.expressions.operati
import io.github.alexzhirkevich.compottie.internal.animation.expressions.operations.unresolvedReference
import io.github.alexzhirkevich.compottie.internal.animation.expressions.operations.value.OpAssign
import io.github.alexzhirkevich.compottie.internal.animation.expressions.operations.value.OpAssignByIndex
import io.github.alexzhirkevich.compottie.internal.animation.expressions.operations.value.OpCompare
import io.github.alexzhirkevich.compottie.internal.animation.expressions.operations.value.OpConstant
import io.github.alexzhirkevich.compottie.internal.animation.expressions.operations.value.OpEqualsComparator
import io.github.alexzhirkevich.compottie.internal.animation.expressions.operations.value.OpGetVariable
import io.github.alexzhirkevich.compottie.internal.animation.expressions.operations.value.OpGreaterComparator
import io.github.alexzhirkevich.compottie.internal.animation.expressions.operations.value.OpIndex
import io.github.alexzhirkevich.compottie.internal.animation.expressions.operations.value.OpLessComparator
import io.github.alexzhirkevich.compottie.internal.animation.expressions.operations.value.OpMakeArray
import io.github.alexzhirkevich.compottie.internal.animation.expressions.operations.value.OpVar
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract

internal class SingleExpressionInterpreter(
private val expr : String,
expr : String,
private val context: EvaluationContext
) : ExpressionInterpreter {
private var pos = -1
private var ch: Char = ' '

private val expr = expr
.replace("{\n","{")
.replace("\n}"," }")

override fun interpret(): Expression {
pos = -1
ch = ' '
Expand All @@ -54,6 +62,12 @@ internal class SingleExpressionInterpreter(
}
}

private fun prepareNextChar(){
while (ch.skip() && pos < expr.length){
nextChar()
}
}

private fun nextChar() {
ch = if (++pos < expr.length) expr[pos] else ';'
}
Expand All @@ -62,7 +76,7 @@ internal class SingleExpressionInterpreter(
ch = if (--pos > 0 && pos < expr.length) expr[pos] else ';'
}

fun Char.skip() : Boolean = this == ' ' || this == '\n'
private fun Char.skip() : Boolean = this == ' ' || this == '\n'

private fun eat(charToEat: Char): Boolean {
while (ch.skip()) nextChar()
Expand Down Expand Up @@ -139,6 +153,7 @@ internal class SingleExpressionInterpreter(
println("Parsed ${x::class.simpleName} as assignment")
}
while (true) {
prepareNextChar()
x = when {
eatSequence("+=") -> parseAssignmentValue(x, ::OpAdd)
eatSequence("-=") -> parseAssignmentValue(x, ::OpSub)
Expand Down Expand Up @@ -166,7 +181,7 @@ internal class SingleExpressionInterpreter(
x is OpGetVariable -> OpAssign(
variableName = x.name,
assignableValue = parseExpressionOp(OpGlobalContext),
scope = x.assignInScope ?: VariableScope.Global,
scope = x.assignInScope,
merge = merge
).also {
if (EXPR_DEBUG_PRINT_ENABLED) {
Expand All @@ -177,18 +192,29 @@ internal class SingleExpressionInterpreter(
else -> error("Invalid assignment")
}

private fun parseExpressionOp(context: Expression): Expression {
private fun parseExpressionOp(context: Expression, logicalContext: LogicalContext? = null): Expression {
var x = parseTermOp(context)
while (true) {
prepareNextChar()
x = when {
eatSequence("&&") -> OpBoolean(parseExpressionOp(OpGlobalContext),x, Boolean::and, "and")
eatSequence("||") -> OpBoolean(parseExpressionOp(OpGlobalContext),x, Boolean::or, "or")
logicalContext != LogicalContext.Compare && eatSequence("&&") ->
OpBoolean(parseExpressionOp(OpGlobalContext, LogicalContext.And),x, Boolean::and)
logicalContext == null && eatSequence("||") ->
OpBoolean(parseExpressionOp(OpGlobalContext, LogicalContext.Or),x, Boolean::or)
eatSequence("<=") -> OpCompare(x, parseExpressionOp(OpGlobalContext, LogicalContext.Compare)) { a, b ->
OpLessComparator(a, b) || OpEqualsComparator(a, b)
}
eatSequence("<") -> OpCompare(x, parseExpressionOp(OpGlobalContext, LogicalContext.Compare), OpLessComparator)
eatSequence(">=") -> OpCompare(x, parseExpressionOp(OpGlobalContext, LogicalContext.Compare)) { a, b ->
OpGreaterComparator(a, b) || OpEqualsComparator(a, b)
}
eatSequence(">") -> OpCompare(x, parseExpressionOp(OpGlobalContext, LogicalContext.Compare), OpGreaterComparator)
eatSequence("===") -> OpEquals(x, parseExpressionOp(OpGlobalContext, LogicalContext.Compare), true)
eatSequence("==") -> OpEquals(x, parseExpressionOp(OpGlobalContext, LogicalContext.Compare), false)
eatSequence("!==") -> OpNot(OpEquals(x, parseExpressionOp(OpGlobalContext, LogicalContext.Compare), false))
eatSequence("!=") -> OpNot(OpEquals(x, parseExpressionOp(OpGlobalContext, LogicalContext.Compare), true))
!nextSequenceIs("+=") && eat('+') -> OpAdd(x, parseTermOp(OpGlobalContext))
!nextSequenceIs("-=") && eat('-') -> OpSub(x, parseTermOp(OpGlobalContext))
eatSequence("===") -> OpEquals(x, parseExpressionOp(OpGlobalContext), true)
eatSequence("==") -> OpEquals(x, parseExpressionOp(OpGlobalContext), false)
eatSequence("!==") -> OpNot(OpEquals(x, parseExpressionOp(OpGlobalContext), false))
eatSequence("!=") -> OpNot(OpEquals(x, parseExpressionOp(OpGlobalContext), true))
else -> return x
}
}
Expand All @@ -197,6 +223,7 @@ internal class SingleExpressionInterpreter(
private fun parseTermOp(context: Expression): Expression {
var x = parseFactorOp(context)
while (true) {
prepareNextChar()
x = when {
!nextSequenceIs("*=") && eat('*') -> OpMul(x, parseFactorOp(OpGlobalContext))
!nextSequenceIs("/=") && eat('/') -> OpDiv(x, parseFactorOp(OpGlobalContext))
Expand Down Expand Up @@ -418,58 +445,64 @@ internal class SingleExpressionInterpreter(

private fun parseFunction(context: Expression, func : String?) : Expression {

if (func == "function") {
return parseFunctionDefinition()
}
return when (func) {
"function" -> parseFunctionDefinition()
"while" -> {
if (EXPR_DEBUG_PRINT_ENABLED) {
println("making while loop")
}

if (func == "while"){
return OpWhileLoop(
condition = checkNotNull(parseFunctionArgs("while")?.singleOrNull()){
"missing 'while' condition"
},
body = parseBlock()
)
}
check(eat('(')){
"Missing while loop condition"
}

if (func == "return"){
val expr = parseExpressionOp(OpGlobalContext)
if (EXPR_DEBUG_PRINT_ENABLED) {
println("making return with $expr")
}
return OpReturn(expr)
}
val condition = parseExpressionOp(OpGlobalContext)

val args = parseFunctionArgs(func)
check(eat(')')){
"Missing closing ')' in loop condition"
}

if (EXPR_DEBUG_PRINT_ENABLED) {
println("making fun $func")
}
OpWhileLoop(
condition = condition,
body = parseBlock()
)
}

return when (context) {
is ExpressionContext<*> -> context.interpret(func, args)
?: (if (args != null && func != null && this.context.getFunction(func) != null) {
if (EXPR_DEBUG_PRINT_ENABLED) {
println("parsed call for defined function $func")
}
OpFunctionExec(func, args)
"return" -> {
val expr = parseExpressionOp(OpGlobalContext)
if (EXPR_DEBUG_PRINT_ENABLED) {
println("making return with $expr")
}
else null)
?: unresolvedReference(
ref = func ?: "null",
obj = context::class.simpleName
?.substringAfter("Op")
?.substringBefore("Context")
)
OpReturn(expr)
}

else -> {
JsContext.interpret(context, func, args)
?: error("Unsupported Lottie expression function: $func")
// when {
// func != null && this.context.getFunction(func) != null ->
// OpFunctionExec(func, args)
// else -> JsContext.interpret(context, func, args)
// ?: error("Unsupported Lottie expression function: $func")
// }
val args = parseFunctionArgs(func)

if (EXPR_DEBUG_PRINT_ENABLED) {
println("making fun $func")
}

return when (context) {
is ExpressionContext<*> -> context.interpret(func, args)
?: (if (args != null && func != null && this.context.getFunction(func) != null) {
if (EXPR_DEBUG_PRINT_ENABLED) {
println("parsed call for defined function $func")
}
OpFunctionExec(func, args)
} else null)
?: unresolvedReference(
ref = func ?: "null",
obj = context::class.simpleName
?.substringAfter("Op")
?.substringBefore("Context")
)

else -> {
JsContext.interpret(context, func, args)
?: error("Unsupported Lottie expression function: $func")
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.github.alexzhirkevich.compottie.internal.animation.expressions.operations

import io.github.alexzhirkevich.compottie.internal.AnimationState
import io.github.alexzhirkevich.compottie.internal.animation.RawProperty
import io.github.alexzhirkevich.compottie.internal.animation.expressions.EvaluationContext
import io.github.alexzhirkevich.compottie.internal.animation.expressions.Expression

internal class LazyConstExpression(
val init: Expression
) : Expression {

private var value : Any? = null
private var initialized : Boolean = false

override fun invoke(
property: RawProperty<Any>,
context: EvaluationContext,
state: AnimationState
): Any {
if (initialized){
return value!!
}

value = init(property, context, state)
initialized = true
return value!!
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//package io.github.alexzhirkevich.compottie.internal.animation.expressions.operations
//
//import io.github.alexzhirkevich.compottie.internal.AnimationState
//import io.github.alexzhirkevich.compottie.internal.animation.RawProperty
//import io.github.alexzhirkevich.compottie.internal.animation.expressions.EvaluationContext
//import io.github.alexzhirkevich.compottie.internal.animation.expressions.Expression
//import io.github.alexzhirkevich.compottie.internal.animation.expressions.ExpressionContext
//import io.github.alexzhirkevich.compottie.internal.animation.expressions.Undefined
//
//internal class Object(
// private val properties : Map<String, LazyConstExpression>
//) : ExpressionContext<Any> {
//
// private val obj = mutableMapOf<String, Any?>()
//
// override fun interpret(callable: String?, args: List<Expression>?): Expression? {
// return when {
// callable == null -> null
// args == null -> field(callable)
// else -> func(callable, args)
// }
// }
//
// override fun invoke(
// property: RawProperty<Any>,
// context: EvaluationContext,
// state: AnimationState
// ): Any = this
//
// private fun field(name: String): Expression {
// return Expression { _, _, _ -> obj[name] ?: Undefined }
// }
//
// private fun func(name: String, args: List<Expression>): Expression {
// return Expression { property, context, state ->
// obj[name]
// }
// }
//}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import io.github.alexzhirkevich.compottie.internal.animation.expressions.Express
internal fun unresolvedReference(ref : String, obj : String? = null) : Nothing =
if (obj != null)
error("Unresolved reference '$ref' for $obj")
else error("Unresolved reference")
else error("Unresolved reference: $ref")


internal fun <T, R : Any> Expression.cast(block: (T) -> R) : Expression =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import io.github.alexzhirkevich.compottie.internal.animation.expressions.Undefin

internal class OpBlock(
val expressions: List<Expression>,
var scoped : Boolean
var scoped : Boolean,
) : Expression {

override fun invoke(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@ internal fun OpBoolean(
a : Expression,
b : Expression,
op : (Boolean, Boolean) -> Boolean,
name: String,
) = Expression { property, context, state ->

println(name)
op(!a(property, context, state).isFalse(), !b(property, context, state).isFalse())
}

Loading

0 comments on commit e5214d5

Please sign in to comment.