[orx-expression-evaluator] Thread safety for TypedExpressionListener
This commit is contained in:
@@ -1,37 +1,171 @@
|
||||
package org.openrndr.extra.expressions.typed
|
||||
|
||||
import org.antlr.v4.kotlinruntime.tree.ParseTreeWalker
|
||||
import org.openrndr.extra.expressions.ExpressionException
|
||||
|
||||
/**
|
||||
* Compile a function
|
||||
*/
|
||||
fun <T0, R> compileFunction1(
|
||||
expression: String,
|
||||
parameter0: String,
|
||||
constants: (String) -> Any? = { null },
|
||||
functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY
|
||||
): ((T0) -> R) {
|
||||
require(constants(parameter0) == null) {
|
||||
"${parameter0} is in constants with value '${constants(parameter0)}"
|
||||
}
|
||||
val root = expressionRoot(expression)
|
||||
|
||||
var varP0: T0? = null
|
||||
val constantValues = fun(p: String): Any? {
|
||||
return if (p == parameter0) {
|
||||
varP0
|
||||
} else {
|
||||
constants(p)
|
||||
}
|
||||
}
|
||||
val listener = TypedExpressionListener(functions, constantValues)
|
||||
|
||||
return { p0 ->
|
||||
varP0 = p0
|
||||
ParseTreeWalker.DEFAULT.walk(listener, root)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
listener.state.lastExpressionResult as? R ?: error("no result")
|
||||
}
|
||||
}
|
||||
|
||||
fun <T0, R> compileFunction1OrNull(
|
||||
expression: String,
|
||||
parameter0: String,
|
||||
constants: (String)->Any? = { null },
|
||||
functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY
|
||||
constants: (String) -> Any? = { null },
|
||||
functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY,
|
||||
onError: (Throwable) -> Unit = { }
|
||||
): ((T0) -> R)? {
|
||||
require(constants(parameter0) == null) {
|
||||
"${parameter0} is in constants with value '${constants(parameter0)}"
|
||||
}
|
||||
try {
|
||||
val root = org.openrndr.extra.expressions.typed.expressionRoot(expression)
|
||||
|
||||
var varP0: T0? = null
|
||||
val variables = fun(p : String) : Any? {
|
||||
return if (p == parameter0) {
|
||||
varP0
|
||||
} else {
|
||||
constants(p)
|
||||
}
|
||||
}
|
||||
val listener = TypedExpressionListener(functions, variables)
|
||||
|
||||
return { p0 ->
|
||||
varP0 = p0
|
||||
ParseTreeWalker.DEFAULT.walk(listener, root)
|
||||
listener.lastExpressionResult as? R ?: error("no result")
|
||||
|
||||
}
|
||||
} catch (e: ExpressionException) {
|
||||
return compileFunction1(expression, parameter0, constants, functions)
|
||||
} catch (e: Throwable) {
|
||||
onError(e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
fun <T0, T1, R> compileFunction2(
|
||||
expression: String,
|
||||
parameter0: String,
|
||||
parameter1: String,
|
||||
constants: (String) -> Any? = { null },
|
||||
functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY
|
||||
): ((T0, T1) -> R) {
|
||||
require(constants(parameter0) == null) {
|
||||
"${parameter0} is in constants with value '${constants(parameter0)}"
|
||||
}
|
||||
require(constants(parameter1) == null) {
|
||||
"${parameter1} is in constants with value '${constants(parameter1)}"
|
||||
}
|
||||
|
||||
val root = expressionRoot(expression)
|
||||
|
||||
var varP0: T0? = null
|
||||
var varP1: T1? = null
|
||||
val constantValues = fun(p: String): Any? {
|
||||
return if (p == parameter0) {
|
||||
varP0
|
||||
} else if (p == parameter1) {
|
||||
varP1
|
||||
} else {
|
||||
constants(p)
|
||||
}
|
||||
}
|
||||
val listener = TypedExpressionListener(functions, constantValues)
|
||||
|
||||
return { p0, p1 ->
|
||||
varP0 = p0
|
||||
varP1 = p1
|
||||
ParseTreeWalker.DEFAULT.walk(listener, root)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
listener.state.lastExpressionResult as? R ?: error("no result")
|
||||
}
|
||||
}
|
||||
|
||||
fun <T0, T1, R> compileFunction2OrNull(
|
||||
expression: String,
|
||||
parameter0: String,
|
||||
parameter1: String,
|
||||
constants: (String) -> Any? = { null },
|
||||
functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY,
|
||||
onError: (Throwable) -> Unit = { }
|
||||
): ((T0, T1) -> R)? {
|
||||
try {
|
||||
return compileFunction2(expression, parameter0, parameter1, constants, functions)
|
||||
} catch (e: Throwable) {
|
||||
onError(e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
fun <T0, T1, T2, R> compileFunction3(
|
||||
expression: String,
|
||||
parameter0: String,
|
||||
parameter1: String,
|
||||
parameter2: String,
|
||||
constants: (String) -> Any? = { null },
|
||||
functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY
|
||||
): ((T0, T1, T2) -> R) {
|
||||
require(constants(parameter0) == null) {
|
||||
"${parameter0} is in constants with value '${constants(parameter0)}"
|
||||
}
|
||||
require(constants(parameter1) == null) {
|
||||
"${parameter1} is in constants with value '${constants(parameter1)}"
|
||||
}
|
||||
require(constants(parameter2) == null) {
|
||||
"${parameter2} is in constants with value '${constants(parameter2)}"
|
||||
}
|
||||
|
||||
val root = expressionRoot(expression)
|
||||
|
||||
var varP0: T0? = null
|
||||
var varP1: T1? = null
|
||||
var varP2: T2? = null
|
||||
val constantValues = fun(p: String): Any? {
|
||||
return if (p == parameter0) {
|
||||
varP0
|
||||
} else if (p == parameter1) {
|
||||
varP1
|
||||
} else if (p == parameter2) {
|
||||
varP2
|
||||
} else {
|
||||
constants(p)
|
||||
}
|
||||
}
|
||||
val listener = TypedExpressionListener(functions, constantValues)
|
||||
|
||||
return { p0, p1, p2 ->
|
||||
varP0 = p0
|
||||
varP1 = p1
|
||||
varP2 = p2
|
||||
ParseTreeWalker.DEFAULT.walk(listener, root)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
listener.state.lastExpressionResult as? R ?: error("no result")
|
||||
}
|
||||
}
|
||||
|
||||
fun <T0, T1, T2, R> compileFunction3OrNull(
|
||||
expression: String,
|
||||
parameter0: String,
|
||||
parameter1: String,
|
||||
parameter2: String,
|
||||
constants: (String) -> Any? = { null },
|
||||
functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY,
|
||||
onError: (Throwable) -> Unit = { }
|
||||
): ((T0, T1, T2) -> R)? {
|
||||
try {
|
||||
return compileFunction3(expression, parameter0, parameter1, parameter2, constants, functions)
|
||||
} catch (e: Throwable) {
|
||||
onError(e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.extra.expressions.parser.KeyLangLexer
|
||||
import org.openrndr.extra.expressions.parser.KeyLangParser
|
||||
import org.openrndr.extra.expressions.parser.KeyLangParserBaseListener
|
||||
import org.openrndr.extra.expressions.parser.KeyLangParserVisitor
|
||||
|
||||
import org.openrndr.extra.noise.uniform
|
||||
import org.openrndr.math.*
|
||||
@@ -43,7 +44,7 @@ class TypedFunctionExtensions(
|
||||
}
|
||||
}
|
||||
|
||||
internal enum class IDType {
|
||||
enum class IDType {
|
||||
VARIABLE,
|
||||
PROPERTY,
|
||||
MEMBER_FUNCTION0,
|
||||
@@ -58,31 +59,39 @@ internal enum class IDType {
|
||||
FUNCTION5
|
||||
}
|
||||
|
||||
internal class TypedExpressionListener(
|
||||
abstract class TypedExpressionListenerBase(
|
||||
val functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY,
|
||||
val constants: (String) -> Any? = { null }
|
||||
) :
|
||||
KeyLangParserBaseListener() {
|
||||
val valueStack = ArrayDeque<Any>()
|
||||
val functionStack = ArrayDeque<(Array<Any>) -> Any>()
|
||||
val propertyStack = ArrayDeque<String>()
|
||||
|
||||
val idTypeStack = ArrayDeque<IDType>()
|
||||
var lastExpressionResult: Any? = null
|
||||
class State {
|
||||
val valueStack = ArrayDeque<Any>()
|
||||
val functionStack = ArrayDeque<(Array<Any>) -> Any>()
|
||||
val propertyStack = ArrayDeque<String>()
|
||||
|
||||
val exceptionStack = ArrayDeque<ExpressionException>()
|
||||
val idTypeStack = ArrayDeque<IDType>()
|
||||
var lastExpressionResult: Any? = null
|
||||
|
||||
val exceptionStack = ArrayDeque<ExpressionException>()
|
||||
}
|
||||
|
||||
abstract val state: State
|
||||
|
||||
override fun exitExpressionStatement(ctx: KeyLangParser.ExpressionStatementContext) {
|
||||
ifError {
|
||||
throw ExpressionException("error in evaluation of '${ctx.text}': ${it.message ?: ""}")
|
||||
}
|
||||
val result = valueStack.pop()
|
||||
lastExpressionResult = result
|
||||
val result = state.valueStack.pop()
|
||||
state.lastExpressionResult = result
|
||||
}
|
||||
|
||||
override fun exitMinusExpression(ctx: KeyLangParser.MinusExpressionContext) {
|
||||
val op = valueStack.pop()
|
||||
valueStack.pushChecked(
|
||||
val s = state
|
||||
|
||||
|
||||
val op = s.valueStack.pop()
|
||||
s.valueStack.pushChecked(
|
||||
when (op) {
|
||||
is Double -> -op
|
||||
is Vector3 -> -op
|
||||
@@ -95,13 +104,15 @@ internal class TypedExpressionListener(
|
||||
}
|
||||
|
||||
override fun exitBinaryOperation1(ctx: KeyLangParser.BinaryOperation1Context) {
|
||||
val s = state
|
||||
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
|
||||
val right = valueStack.pop()
|
||||
val left = valueStack.pop()
|
||||
val right = s.valueStack.pop()
|
||||
val left = s.valueStack.pop()
|
||||
|
||||
val result = when (val operator = ctx.operator?.type) {
|
||||
KeyLangLexer.Tokens.ASTERISK -> when {
|
||||
@@ -142,18 +153,19 @@ internal class TypedExpressionListener(
|
||||
|
||||
else -> error("operator '$operator' not implemented")
|
||||
}
|
||||
valueStack.pushChecked(result)
|
||||
s.valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
@Suppress("IMPLICIT_CAST_TO_ANY")
|
||||
override fun exitBinaryOperation2(ctx: KeyLangParser.BinaryOperation2Context) {
|
||||
val s = state
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
|
||||
val right = valueStack.pop()
|
||||
val left = valueStack.pop()
|
||||
val right = s.valueStack.pop()
|
||||
val left = s.valueStack.pop()
|
||||
|
||||
val result = when (val operator = ctx.operator?.type) {
|
||||
KeyLangLexer.Tokens.PLUS -> when {
|
||||
@@ -179,24 +191,27 @@ internal class TypedExpressionListener(
|
||||
|
||||
else -> error("operator '$operator' not implemented")
|
||||
}
|
||||
valueStack.pushChecked(result)
|
||||
s.valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
override fun exitJoinOperation(ctx: KeyLangParser.JoinOperationContext) {
|
||||
val right = (valueStack.pop() as Double).roundToInt()
|
||||
val left = (valueStack.pop() as Double).roundToInt()
|
||||
val s = state
|
||||
val right = (s.valueStack.pop() as Double).roundToInt()
|
||||
val left = (s.valueStack.pop() as Double).roundToInt()
|
||||
|
||||
val result = when (val operator = ctx.operator?.type) {
|
||||
KeyLangLexer.Tokens.AND -> right != 0 && left != 0
|
||||
KeyLangLexer.Tokens.OR -> right != 0 || left != 0
|
||||
else -> error("operator '$operator' not implemented")
|
||||
}
|
||||
valueStack.pushChecked(if (result) 1.0 else 0.0)
|
||||
s.valueStack.pushChecked(if (result) 1.0 else 0.0)
|
||||
}
|
||||
|
||||
override fun exitComparisonOperation(ctx: KeyLangParser.ComparisonOperationContext) {
|
||||
val right = valueStack.pop()
|
||||
val left = valueStack.pop()
|
||||
val s = state
|
||||
|
||||
val right = s.valueStack.pop()
|
||||
val left = s.valueStack.pop()
|
||||
|
||||
val result = when (val operator = ctx.operator?.type) {
|
||||
KeyLangLexer.Tokens.EQ -> when {
|
||||
@@ -208,18 +223,22 @@ internal class TypedExpressionListener(
|
||||
left is String && right is String -> left == right
|
||||
else -> error("unsupported operands for == operator left:${left::class} right:${right::class}")
|
||||
}
|
||||
|
||||
KeyLangLexer.Tokens.LTEQ -> when {
|
||||
left is Double && right is Double -> left <= right
|
||||
else -> error("unsupported operands for <= operator left:${left::class} right:${right::class}")
|
||||
}
|
||||
|
||||
KeyLangLexer.Tokens.LT -> when {
|
||||
left is Double && right is Double -> left < right
|
||||
else -> error("unsupported operands for < operator left:${left::class} right:${right::class}")
|
||||
}
|
||||
|
||||
KeyLangLexer.Tokens.GTEQ -> when {
|
||||
left is Double && right is Double -> left >= right
|
||||
else -> error("unsupported operands for >= operator left:${left::class} right:${right::class}")
|
||||
}
|
||||
|
||||
KeyLangLexer.Tokens.GT -> when {
|
||||
left is Double && right is Double -> left > right
|
||||
else -> error("unsupported operands for > operator left:${left::class} right:${right::class}")
|
||||
@@ -227,195 +246,218 @@ internal class TypedExpressionListener(
|
||||
|
||||
else -> error("operator '$operator' not implemented")
|
||||
}
|
||||
valueStack.pushChecked(if (result) 1.0 else 0.0)
|
||||
s.valueStack.pushChecked(if (result) 1.0 else 0.0)
|
||||
}
|
||||
|
||||
override fun exitNegateExpression(ctx: KeyLangParser.NegateExpressionContext) {
|
||||
val operand = (valueStack.pop() as Double).roundToInt()
|
||||
valueStack.pushChecked(if (operand == 0) 1.0 else 0.0)
|
||||
val s = state
|
||||
val operand = (s.valueStack.pop() as Double).roundToInt()
|
||||
s.valueStack.pushChecked(if (operand == 0) 1.0 else 0.0)
|
||||
}
|
||||
|
||||
override fun exitTernaryExpression(ctx: KeyLangParser.TernaryExpressionContext) {
|
||||
val right = valueStack.pop()
|
||||
val left = valueStack.pop()
|
||||
val comp = valueStack.pop()
|
||||
val s = state
|
||||
val right = s.valueStack.pop()
|
||||
val left = s.valueStack.pop()
|
||||
val comp = s.valueStack.pop()
|
||||
|
||||
val result = when (comp) {
|
||||
is Double -> if (comp.roundToInt() != 0) left else right
|
||||
else -> error("can't compare")
|
||||
}
|
||||
valueStack.pushChecked(result)
|
||||
s.valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
override fun enterValueReference(ctx: KeyLangParser.ValueReferenceContext) {
|
||||
idTypeStack.push(IDType.VARIABLE)
|
||||
val s = state
|
||||
s.idTypeStack.push(IDType.VARIABLE)
|
||||
}
|
||||
|
||||
override fun enterMemberFunctionCall0Expression(ctx: KeyLangParser.MemberFunctionCall0ExpressionContext) {
|
||||
idTypeStack.push(IDType.MEMBER_FUNCTION1)
|
||||
val s = state
|
||||
s.idTypeStack.push(IDType.MEMBER_FUNCTION1)
|
||||
}
|
||||
|
||||
override fun exitMemberFunctionCall0Expression(ctx: KeyLangParser.MemberFunctionCall0ExpressionContext) {
|
||||
val s = state
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
valueStack.pushChecked(functionStack.pop().invoke(emptyArray()))
|
||||
s.valueStack.pushChecked(s.functionStack.pop().invoke(emptyArray()))
|
||||
}
|
||||
|
||||
override fun enterMemberFunctionCall1Expression(ctx: KeyLangParser.MemberFunctionCall1ExpressionContext) {
|
||||
idTypeStack.push(IDType.MEMBER_FUNCTION1)
|
||||
val s = state
|
||||
s.idTypeStack.push(IDType.MEMBER_FUNCTION1)
|
||||
}
|
||||
|
||||
override fun exitMemberFunctionCall1Expression(ctx: KeyLangParser.MemberFunctionCall1ExpressionContext) {
|
||||
val s = state
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
valueStack.pushChecked(functionStack.pop().invoke(arrayOf(valueStack.pop())))
|
||||
s.valueStack.pushChecked(s.functionStack.pop().invoke(arrayOf(s.valueStack.pop())))
|
||||
}
|
||||
|
||||
override fun enterMemberFunctionCall2Expression(ctx: KeyLangParser.MemberFunctionCall2ExpressionContext) {
|
||||
idTypeStack.push(IDType.MEMBER_FUNCTION2)
|
||||
val s = state
|
||||
s.idTypeStack.push(IDType.MEMBER_FUNCTION2)
|
||||
}
|
||||
|
||||
override fun exitMemberFunctionCall2Expression(ctx: KeyLangParser.MemberFunctionCall2ExpressionContext) {
|
||||
val s = state
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
val argument1 = valueStack.pop()
|
||||
val argument0 = valueStack.pop()
|
||||
val argument1 = s.valueStack.pop()
|
||||
val argument0 = s.valueStack.pop()
|
||||
|
||||
valueStack.pushChecked(functionStack.pop().invoke(arrayOf(argument0, argument1)))
|
||||
s.valueStack.pushChecked(s.functionStack.pop().invoke(arrayOf(argument0, argument1)))
|
||||
}
|
||||
|
||||
override fun enterMemberFunctionCall3Expression(ctx: KeyLangParser.MemberFunctionCall3ExpressionContext) {
|
||||
idTypeStack.push(IDType.MEMBER_FUNCTION3)
|
||||
val s = state
|
||||
s.idTypeStack.push(IDType.MEMBER_FUNCTION3)
|
||||
}
|
||||
|
||||
override fun exitMemberFunctionCall3Expression(ctx: KeyLangParser.MemberFunctionCall3ExpressionContext) {
|
||||
val s = state
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
val argument2 = valueStack.pop()
|
||||
val argument1 = valueStack.pop()
|
||||
val argument0 = valueStack.pop()
|
||||
val argument2 = s.valueStack.pop()
|
||||
val argument1 = s.valueStack.pop()
|
||||
val argument0 = s.valueStack.pop()
|
||||
|
||||
valueStack.pushChecked(functionStack.pop().invoke(arrayOf(argument0, argument1, argument2)))
|
||||
s.valueStack.pushChecked(s.functionStack.pop().invoke(arrayOf(argument0, argument1, argument2)))
|
||||
}
|
||||
|
||||
|
||||
override fun enterFunctionCall0Expression(ctx: KeyLangParser.FunctionCall0ExpressionContext) {
|
||||
idTypeStack.push(IDType.FUNCTION0)
|
||||
val s = state
|
||||
s.idTypeStack.push(IDType.FUNCTION0)
|
||||
}
|
||||
|
||||
override fun exitFunctionCall0Expression(ctx: KeyLangParser.FunctionCall0ExpressionContext) {
|
||||
val s = state
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
|
||||
val function = functionStack.pop()
|
||||
val function = s.functionStack.pop()
|
||||
val result = function.invoke(arrayOf())
|
||||
valueStack.pushChecked(result)
|
||||
s.valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
override fun enterFunctionCall1Expression(ctx: KeyLangParser.FunctionCall1ExpressionContext) {
|
||||
idTypeStack.push(IDType.FUNCTION1)
|
||||
val s = state
|
||||
s.idTypeStack.push(IDType.FUNCTION1)
|
||||
}
|
||||
|
||||
override fun exitFunctionCall1Expression(ctx: KeyLangParser.FunctionCall1ExpressionContext) {
|
||||
val s = state
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
|
||||
val function = functionStack.pop()
|
||||
val argument = valueStack.pop()
|
||||
val function = s.functionStack.pop()
|
||||
val argument = s.valueStack.pop()
|
||||
|
||||
val result = function.invoke(arrayOf(argument))
|
||||
valueStack.pushChecked(result)
|
||||
s.valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
override fun enterFunctionCall2Expression(ctx: KeyLangParser.FunctionCall2ExpressionContext) {
|
||||
idTypeStack.push(IDType.FUNCTION2)
|
||||
val s = state
|
||||
s.idTypeStack.push(IDType.FUNCTION2)
|
||||
}
|
||||
|
||||
override fun exitFunctionCall2Expression(ctx: KeyLangParser.FunctionCall2ExpressionContext) {
|
||||
val s = state
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
|
||||
val function = functionStack.pop()
|
||||
val argument1 = valueStack.pop()
|
||||
val argument0 = valueStack.pop()
|
||||
val function = s.functionStack.pop()
|
||||
val argument1 = s.valueStack.pop()
|
||||
val argument0 = s.valueStack.pop()
|
||||
|
||||
val result = function.invoke(arrayOf(argument0, argument1))
|
||||
valueStack.pushChecked(result)
|
||||
s.valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
override fun enterFunctionCall3Expression(ctx: KeyLangParser.FunctionCall3ExpressionContext) {
|
||||
idTypeStack.push(IDType.FUNCTION3)
|
||||
val s = state
|
||||
s.idTypeStack.push(IDType.FUNCTION3)
|
||||
}
|
||||
|
||||
override fun exitFunctionCall3Expression(ctx: KeyLangParser.FunctionCall3ExpressionContext) {
|
||||
val s = state
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
|
||||
val function = functionStack.pop()
|
||||
val argument2 = valueStack.pop()
|
||||
val argument1 = valueStack.pop()
|
||||
val argument0 = valueStack.pop()
|
||||
val function = s.functionStack.pop()
|
||||
val argument2 = s.valueStack.pop()
|
||||
val argument1 = s.valueStack.pop()
|
||||
val argument0 = s.valueStack.pop()
|
||||
|
||||
val result = function.invoke(arrayOf(argument0, argument1, argument2))
|
||||
valueStack.pushChecked(result)
|
||||
s.valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
override fun enterFunctionCall4Expression(ctx: KeyLangParser.FunctionCall4ExpressionContext) {
|
||||
idTypeStack.push(IDType.FUNCTION4)
|
||||
val s = state
|
||||
s.idTypeStack.push(IDType.FUNCTION4)
|
||||
}
|
||||
|
||||
override fun exitFunctionCall4Expression(ctx: KeyLangParser.FunctionCall4ExpressionContext) {
|
||||
val s = state
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
|
||||
val function = functionStack.pop()
|
||||
val argument3 = valueStack.pop()
|
||||
val argument2 = valueStack.pop()
|
||||
val argument1 = valueStack.pop()
|
||||
val argument0 = valueStack.pop()
|
||||
val function = s.functionStack.pop()
|
||||
val argument3 = s.valueStack.pop()
|
||||
val argument2 = s.valueStack.pop()
|
||||
val argument1 = s.valueStack.pop()
|
||||
val argument0 = s.valueStack.pop()
|
||||
|
||||
val result = function.invoke(arrayOf(argument0, argument1, argument2, argument3))
|
||||
valueStack.pushChecked(result)
|
||||
s.valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
|
||||
override fun enterFunctionCall5Expression(ctx: KeyLangParser.FunctionCall5ExpressionContext) {
|
||||
idTypeStack.push(IDType.FUNCTION5)
|
||||
val s = state
|
||||
s.idTypeStack.push(IDType.FUNCTION5)
|
||||
}
|
||||
|
||||
override fun exitFunctionCall5Expression(ctx: KeyLangParser.FunctionCall5ExpressionContext) {
|
||||
val s = state
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
|
||||
val function = functionStack.pop()
|
||||
val argument4 = valueStack.pop()
|
||||
val argument3 = valueStack.pop()
|
||||
val argument2 = valueStack.pop()
|
||||
val argument1 = valueStack.pop()
|
||||
val argument0 = valueStack.pop()
|
||||
val function = s.functionStack.pop()
|
||||
val argument4 = s.valueStack.pop()
|
||||
val argument3 = s.valueStack.pop()
|
||||
val argument2 = s.valueStack.pop()
|
||||
val argument1 = s.valueStack.pop()
|
||||
val argument0 = s.valueStack.pop()
|
||||
|
||||
val result = function.invoke(arrayOf(argument0, argument1, argument2, argument3, argument4))
|
||||
valueStack.pushChecked(result)
|
||||
s.valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
private fun <T> errorValue(message: String, value: T): T {
|
||||
@@ -424,24 +466,28 @@ internal class TypedExpressionListener(
|
||||
}
|
||||
|
||||
private fun pushError(message: String) {
|
||||
exceptionStack.push(ExpressionException(message))
|
||||
val s = state
|
||||
s.exceptionStack.push(ExpressionException(message))
|
||||
}
|
||||
|
||||
private inline fun ifError(f: (e: Throwable) -> Unit) {
|
||||
if (exceptionStack.isNotEmpty()) {
|
||||
val e = exceptionStack.pop()
|
||||
val s = state
|
||||
if (s.exceptionStack.isNotEmpty()) {
|
||||
val e = s.exceptionStack.pop()
|
||||
f(e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun enterPropReference(ctx: KeyLangParser.PropReferenceContext) {
|
||||
idTypeStack.push(IDType.PROPERTY)
|
||||
val s = state
|
||||
s.idTypeStack.push(IDType.PROPERTY)
|
||||
}
|
||||
|
||||
override fun exitPropReference(ctx: KeyLangParser.PropReferenceContext) {
|
||||
val root = valueStack.pop()
|
||||
val s = state
|
||||
val root = s.valueStack.pop()
|
||||
var current = root
|
||||
val property = propertyStack.pop()
|
||||
val property = s.propertyStack.pop()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
current = when (current) {
|
||||
is Map<*, *> -> current[property] ?: error("property '$property' not found")
|
||||
@@ -455,31 +501,31 @@ internal class TypedExpressionListener(
|
||||
is Matrix44 -> current.property(property)
|
||||
else -> error("can't look up: ${current::class} '$current', root:'$root' ${ctx.text} ")
|
||||
}
|
||||
valueStack.push(current)
|
||||
s.valueStack.push(current)
|
||||
}
|
||||
|
||||
|
||||
override fun visitTerminal(node: TerminalNode) {
|
||||
|
||||
val s = state
|
||||
val type = node.symbol.type
|
||||
if (type == KeyLangParser.Tokens.INTLIT) {
|
||||
valueStack.pushChecked(node.text.toDouble())
|
||||
s.valueStack.pushChecked(node.text.toDouble())
|
||||
} else if (type == KeyLangParser.Tokens.DECLIT) {
|
||||
valueStack.pushChecked(node.text.toDouble())
|
||||
s.valueStack.pushChecked(node.text.toDouble())
|
||||
} else if (type == KeyLangParser.Tokens.STRING_CONTENT) {
|
||||
valueStack.pushChecked(node.text)
|
||||
s.valueStack.pushChecked(node.text)
|
||||
} else if (type == KeyLangParser.Tokens.ID) {
|
||||
val name = node.text.replace("`", "")
|
||||
@Suppress("DIVISION_BY_ZERO")
|
||||
when (val idType = idTypeStack.pop()) {
|
||||
IDType.VARIABLE -> valueStack.pushChecked(
|
||||
when (val idType = s.idTypeStack.pop()) {
|
||||
IDType.VARIABLE -> s.valueStack.pushChecked(
|
||||
when (name) {
|
||||
"PI" -> PI
|
||||
else -> constants(name) ?: errorValue("unresolved variable: '${name}'", 0.0 / 0.0)
|
||||
}
|
||||
)
|
||||
|
||||
IDType.PROPERTY -> propertyStack.push(name)
|
||||
IDType.PROPERTY -> s.propertyStack.push(name)
|
||||
|
||||
IDType.FUNCTION0 -> {
|
||||
val function: (Array<Any>) -> Any =
|
||||
@@ -490,17 +536,17 @@ internal class TypedExpressionListener(
|
||||
"unresolved function: '${name}()'"
|
||||
) { _ -> error("this is the error function") }
|
||||
}
|
||||
functionStack.push(function)
|
||||
s.functionStack.push(function)
|
||||
}
|
||||
|
||||
IDType.MEMBER_FUNCTION0,
|
||||
IDType.MEMBER_FUNCTION1,
|
||||
IDType.MEMBER_FUNCTION2,
|
||||
IDType.MEMBER_FUNCTION3 -> {
|
||||
val receiver = valueStack.pop()
|
||||
val receiver = s.valueStack.pop()
|
||||
when (receiver) {
|
||||
is String -> {
|
||||
functionStack.push(
|
||||
s.functionStack.push(
|
||||
receiver.memberFunctions(name)
|
||||
?: error("no member function '$receiver.$name()'")
|
||||
)
|
||||
@@ -509,7 +555,7 @@ internal class TypedExpressionListener(
|
||||
is ColorRGBa -> {
|
||||
when (idType) {
|
||||
IDType.MEMBER_FUNCTION1 -> {
|
||||
functionStack.push(when (name) {
|
||||
s.functionStack.push(when (name) {
|
||||
"shade" -> { x -> receiver.shade(x[0] as Double) }
|
||||
"opacify" -> { x -> receiver.opacify(x[0] as Double) }
|
||||
else -> error("no member function '$receiver.$name()'")
|
||||
@@ -530,22 +576,22 @@ internal class TypedExpressionListener(
|
||||
when (idType) {
|
||||
IDType.MEMBER_FUNCTION0 -> {
|
||||
function as () -> Any
|
||||
functionStack.push({ function() })
|
||||
s.functionStack.push({ function() })
|
||||
}
|
||||
|
||||
IDType.MEMBER_FUNCTION1 -> {
|
||||
function as (Any) -> Any
|
||||
functionStack.push({ x -> function(x[0]) })
|
||||
s.functionStack.push({ x -> function(x[0]) })
|
||||
}
|
||||
|
||||
IDType.MEMBER_FUNCTION2 -> {
|
||||
function as (Any, Any) -> Any
|
||||
functionStack.push({ x -> function(x[0], x[1]) })
|
||||
s.functionStack.push({ x -> function(x[0], x[1]) })
|
||||
}
|
||||
|
||||
IDType.MEMBER_FUNCTION3 -> {
|
||||
function as (Any, Any, Any) -> Any
|
||||
functionStack.push({ x -> function(x[0], x[1], x[2]) })
|
||||
s.functionStack.push({ x -> function(x[0], x[1], x[2]) })
|
||||
}
|
||||
|
||||
else -> error("unreachable")
|
||||
@@ -557,12 +603,13 @@ internal class TypedExpressionListener(
|
||||
}
|
||||
|
||||
IDType.FUNCTION1 -> {
|
||||
val s = state
|
||||
val function: (Array<Any>) -> Any =
|
||||
dispatchFunction1(name, functions.functions1)
|
||||
?: errorValue(
|
||||
"unresolved function: '${name}(x0)'"
|
||||
) { _ -> error("this is the error function") }
|
||||
functionStack.push(function)
|
||||
s.functionStack.push(function)
|
||||
}
|
||||
|
||||
IDType.FUNCTION2 -> {
|
||||
@@ -571,7 +618,7 @@ internal class TypedExpressionListener(
|
||||
?: errorValue(
|
||||
"unresolved function: '${name}(x0, x1)'"
|
||||
) { _ -> error("this is the error function") }
|
||||
functionStack.push(function)
|
||||
s.functionStack.push(function)
|
||||
}
|
||||
|
||||
IDType.FUNCTION3 -> {
|
||||
@@ -580,7 +627,7 @@ internal class TypedExpressionListener(
|
||||
?: errorValue(
|
||||
"unresolved function: '${name}(x0)'"
|
||||
) { _ -> error("this is the error function") }
|
||||
functionStack.push(function)
|
||||
s.functionStack.push(function)
|
||||
}
|
||||
|
||||
IDType.FUNCTION4 -> {
|
||||
@@ -589,7 +636,7 @@ internal class TypedExpressionListener(
|
||||
?: errorValue(
|
||||
"unresolved function: '${name}(x0)'"
|
||||
) { _ -> error("this is the error function") }
|
||||
functionStack.push(function)
|
||||
s.functionStack.push(function)
|
||||
}
|
||||
|
||||
else -> error("unsupported id-type $idType")
|
||||
@@ -598,6 +645,14 @@ internal class TypedExpressionListener(
|
||||
}
|
||||
}
|
||||
|
||||
expect class TypedExpressionListener(
|
||||
functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY,
|
||||
constants: (String) -> Any? = { null }
|
||||
) : TypedExpressionListenerBase {
|
||||
|
||||
override val state: State
|
||||
}
|
||||
|
||||
class ExpressionException(message: String) : RuntimeException(message)
|
||||
|
||||
fun evaluateTypedExpression(
|
||||
@@ -628,7 +683,8 @@ fun evaluateTypedExpression(
|
||||
} catch (e: ExpressionException) {
|
||||
throw ExpressionException(e.message ?: "")
|
||||
}
|
||||
return listener.lastExpressionResult
|
||||
|
||||
return listener.state.lastExpressionResult
|
||||
}
|
||||
|
||||
fun compileTypedExpression(
|
||||
@@ -661,7 +717,7 @@ fun compileTypedExpression(
|
||||
} catch (e: ExpressionException) {
|
||||
throw ExpressionException(e.message ?: "")
|
||||
}
|
||||
listener.lastExpressionResult ?: error("no result")
|
||||
listener.state.lastExpressionResult ?: error("no result")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package org.openrndr.extra.expressions.typed
|
||||
|
||||
import org.antlr.v4.kotlinruntime.tree.ParseTreeWalker
|
||||
import org.openrndr.extra.expressions.ExpressionException
|
||||
|
||||
fun <T0, R> compileFunction1OrNull(
|
||||
expression: String,
|
||||
parameter0: String,
|
||||
constants: (String)->Any? = { null },
|
||||
functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY
|
||||
): ((T0) -> R)? {
|
||||
require(constants(parameter0) == null) {
|
||||
"${parameter0} is in constants with value '${constants(parameter0)}"
|
||||
}
|
||||
try {
|
||||
val root = org.openrndr.extra.expressions.typed.expressionRoot(expression)
|
||||
|
||||
var varP0: T0? = null
|
||||
val variables = fun(p : String) : Any? {
|
||||
return if (p == parameter0) {
|
||||
varP0
|
||||
} else {
|
||||
constants(p)
|
||||
}
|
||||
}
|
||||
val listener = TypedExpressionListener(functions, variables)
|
||||
|
||||
return { p0 ->
|
||||
varP0 = p0
|
||||
ParseTreeWalker.DEFAULT.walk(listener, root)
|
||||
listener.lastExpressionResult as? R ?: error("no result")
|
||||
|
||||
}
|
||||
} catch (e: ExpressionException) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
package org.openrndr.extra.expressions.typed
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.math.Matrix44
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.Vector3
|
||||
import org.openrndr.math.Vector4
|
||||
import org.openrndr.math.transforms.scale
|
||||
import org.openrndr.math.transforms.translate
|
||||
import kotlin.math.abs as abs_
|
||||
import kotlin.math.cos as cos_
|
||||
import kotlin.math.sin as sin_
|
||||
import kotlin.math.sqrt as sqrt_
|
||||
|
||||
internal fun vec2(x: Any): Vector2 {
|
||||
require(x is Double)
|
||||
return Vector2(x, x)
|
||||
}
|
||||
|
||||
internal fun vec3(x: Any): Vector3 {
|
||||
require(x is Double)
|
||||
return Vector3(x, x, x)
|
||||
}
|
||||
|
||||
internal fun vec4(x: Any): Vector4 {
|
||||
require(x is Double)
|
||||
return Vector4(x, x, x, x)
|
||||
}
|
||||
|
||||
internal fun rgba(x: Any): ColorRGBa {
|
||||
return when (x) {
|
||||
is Double -> ColorRGBa(x, x, x, 1.0)
|
||||
is Vector3 -> ColorRGBa(x.x, x.y, x.z, 1.0)
|
||||
is Vector4 -> ColorRGBa(x.x, x.y, x.z, x.w)
|
||||
else -> error("type not supported ${x::class.simpleName}")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun cos(x: Any): Any {
|
||||
return when (x) {
|
||||
is Double -> cos_(x)
|
||||
is Vector2 -> x.map { cos_(it) }
|
||||
is Vector3 -> x.map { cos_(it) }
|
||||
is Vector4 -> x.map { cos_(it) }
|
||||
else -> error("type not supported ${x::class.simpleName}")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun sin(x: Any): Any {
|
||||
return when (x) {
|
||||
is Double -> sin_(x)
|
||||
is Vector2 -> x.map { sin_(it) }
|
||||
is Vector3 -> x.map { sin_(it) }
|
||||
is Vector4 -> x.map { sin_(it) }
|
||||
else -> error("type not supported ${x::class.simpleName}")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun normalize(x: Any): Any {
|
||||
return when (x) {
|
||||
is Vector2 -> x.normalized
|
||||
is Vector3 -> x.normalized
|
||||
is Vector4 -> x.normalized
|
||||
else -> error("type not supported ${x::class.simpleName}")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun inverse(x: Any): Any {
|
||||
return when (x) {
|
||||
is Matrix44 -> x.inversed
|
||||
else -> error("type not supported ${x::class.simpleName}")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun transpose(x: Any): Any {
|
||||
return when (x) {
|
||||
is Matrix44 -> x.transposed
|
||||
else -> error("type not supported ${x::class.simpleName}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun abs(x: Any): Any {
|
||||
return when (x) {
|
||||
is Double -> abs_(x)
|
||||
is Vector2 -> x.map { abs_(it) }
|
||||
is Vector3 -> x.map { abs_(it) }
|
||||
is Vector4 -> x.map { abs_(it) }
|
||||
else -> error("type not supported ${x::class.simpleName}")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun scale(scale: Any): Matrix44 {
|
||||
@Suppress("NAME_SHADOWING") val scale = when (scale) {
|
||||
is Double -> Vector3(scale, scale, scale)
|
||||
is Vector2 -> scale.xy1
|
||||
is Vector3 -> scale
|
||||
else -> error("unsupported axis argument")
|
||||
}
|
||||
return Matrix44.scale(scale)
|
||||
}
|
||||
|
||||
|
||||
internal fun sqrt(x: Any): Any {
|
||||
return when (x) {
|
||||
is Double -> sqrt_(x)
|
||||
is Vector2 -> x.map { sqrt_(it) }
|
||||
is Vector3 -> x.map { sqrt_(it) }
|
||||
is Vector4 -> x.map { sqrt_(it) }
|
||||
else -> error("type not supported ${x::class.simpleName}")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun translate(translation: Any): Matrix44 {
|
||||
@Suppress("NAME_SHADOWING") val translation = when (translation) {
|
||||
is Vector2 -> translation.xy0
|
||||
is Vector3 -> translation
|
||||
else -> error("unsupported axis argument")
|
||||
}
|
||||
return Matrix44.translate(translation)
|
||||
}
|
||||
|
||||
internal fun dispatchFunction1(name: String, functions: Map<String, TypedFunction1>): ((Array<Any>) -> Any)? {
|
||||
return when (name) {
|
||||
"vec2" -> { x -> vec2(x[0]) }
|
||||
"vec3" -> { x -> vec3(x[0]) }
|
||||
"vec4" -> { x -> vec4(x[0]) }
|
||||
|
||||
"cos" -> { x -> cos(x[0]) }
|
||||
"sin" -> { x -> sin(x[0]) }
|
||||
"sqrt" -> { v -> sqrt(v[0]) }
|
||||
"abs" -> { v -> abs(v[0]) }
|
||||
"scale" -> { x -> scale(x[0]) }
|
||||
"translate" -> { x -> translate(x[0]) }
|
||||
"transpose" -> { x -> transpose(x[0]) }
|
||||
"inverse" -> { x -> inverse(x[0]) }
|
||||
"normalize" -> { x -> normalize(x[0]) }
|
||||
else -> functions[name]?.let { { x: Array<Any> -> it.invoke(x[0]) } }
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package org.openrndr.extra.expressions.typed
|
||||
|
||||
import org.openrndr.math.Matrix44
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.Vector3
|
||||
import org.openrndr.math.Vector4
|
||||
import org.openrndr.math.transforms.rotate
|
||||
import org.openrndr.math.transforms.translate
|
||||
import org.openrndr.math.min as min_
|
||||
import org.openrndr.math.max as max_
|
||||
|
||||
import kotlin.math.max as max_
|
||||
import kotlin.math.min as min_
|
||||
|
||||
internal fun rotate(axis: Any, angleInDegrees:Any): Matrix44 {
|
||||
require(angleInDegrees is Double)
|
||||
@Suppress("NAME_SHADOWING") val axis = when(axis) {
|
||||
is Vector2 -> axis.xy0
|
||||
is Vector3 -> axis
|
||||
else -> error("unsupported axis argument")
|
||||
}
|
||||
return Matrix44.rotate(axis, angleInDegrees)
|
||||
}
|
||||
|
||||
|
||||
internal fun min(x: Any, y: Any): Any {
|
||||
return when {
|
||||
x is Double && y is Double -> min_(x, y)
|
||||
x is Vector2 && y is Vector2 -> min_(x, y)
|
||||
x is Vector3 && y is Vector3 -> min_(x, y)
|
||||
x is Vector4 && y is Vector4 -> min_(x, y)
|
||||
else -> error("unsupported arguments")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun max(x: Any, y: Any): Any {
|
||||
return when {
|
||||
x is Double && y is Double -> max_(x, y)
|
||||
x is Vector2 && y is Vector2 -> max_(x, y)
|
||||
x is Vector3 && y is Vector3 -> max_(x, y)
|
||||
x is Vector4 && y is Vector4 -> max_(x, y)
|
||||
else -> error("unsupported arguments")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun vec2(x: Any, y: Any): Vector2 {
|
||||
require(x is Double)
|
||||
require(y is Double)
|
||||
return Vector2(x, y)
|
||||
}
|
||||
|
||||
internal fun vec3(x: Any, y: Any): Vector3 = when {
|
||||
x is Double && y is Vector2 -> {
|
||||
Vector3(x, y.x, y.y)
|
||||
}
|
||||
x is Vector2 && y is Double -> {
|
||||
Vector3(x.x, x.y, y)
|
||||
}
|
||||
else -> {
|
||||
error("unsupported arguments, '$x' (${x::class}) '$y' (${y::class}")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun dispatchFunction2(name: String, functions: Map<String, TypedFunction2>): ((Array<Any>) -> Any)? {
|
||||
return when (name) {
|
||||
"min" -> { x -> min(x[0], x[1]) }
|
||||
"max" -> { x -> max(x[0], x[1]) }
|
||||
"vec2" -> { x -> vec2(x[0], x[1]) }
|
||||
"vec3" -> { x -> vec3(x[0], x[1]) }
|
||||
"rotate" -> { x -> rotate(x[0], x[1]) }
|
||||
else -> functions[name]?.let { { x: Array<Any> -> it.invoke(x[0], x[1]) } }
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package org.openrndr.extra.expressions.typed
|
||||
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.Vector3
|
||||
import org.openrndr.math.Vector4
|
||||
import org.openrndr.math.mix as mix_
|
||||
|
||||
internal fun mix(x: Any, y: Any, f: Any): Any {
|
||||
return when {
|
||||
x is Double && y is Double && f is Double -> mix_(x, y, f)
|
||||
x is Vector2 && y is Vector2 && f is Double -> mix_(x, y, f)
|
||||
x is Vector3 && y is Vector3 && f is Double -> mix_(x, y, f)
|
||||
x is Vector4 && y is Vector4 && f is Double -> mix_(x, y, f)
|
||||
else -> error("unsupported arguments")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun vec3(x: Any, y: Any, z: Any): Vector3 {
|
||||
require(x is Double && y is Double && z is Double)
|
||||
return Vector3(x, y, z)
|
||||
}
|
||||
|
||||
internal fun vec4(x: Any, y: Any, z: Any): Vector4 {
|
||||
return when {
|
||||
x is Vector2 && y is Double && z is Double -> Vector4(x.x, x.y, y, z)
|
||||
x is Double && y is Vector2 && z is Double -> Vector4(x, y.x, y.y, z)
|
||||
x is Double && y is Double && z is Vector2 -> Vector4(x, y, z.x, z.y)
|
||||
else -> error("unsupported arguments")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun dispatchFunction3(name: String, functions: Map<String, TypedFunction3>): ((Array<Any>) -> Any)? {
|
||||
return when (name) {
|
||||
"vec3" -> { x -> vec3(x[0], x[1], x[2]) }
|
||||
"vec4" -> { x -> vec4(x[0], x[1], x[2]) }
|
||||
"mix" -> { x -> mix(x[0], x[1], x[2]) }
|
||||
else -> functions[name]?.let {
|
||||
{ x: Array<Any> ->
|
||||
it.invoke(x[0], x[1], x[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package org.openrndr.extra.expressions.typed
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.math.Matrix44
|
||||
import org.openrndr.math.Vector4
|
||||
|
||||
internal fun vec4(x: Any, y: Any, z: Any, w: Any): Vector4 {
|
||||
require(x is Double && y is Double && z is Double && w is Double)
|
||||
return Vector4(x, y, z, w)
|
||||
}
|
||||
|
||||
internal fun mat4(x: Any, y: Any, z: Any, w: Any): Matrix44 {
|
||||
require(x is Vector4 && y is Vector4 && z is Vector4 && w is Vector4)
|
||||
return Matrix44.fromColumnVectors(x, y, z, w)
|
||||
}
|
||||
|
||||
internal fun rgba(r: Any, g: Any, b: Any, a: Any): ColorRGBa {
|
||||
require(r is Double && g is Double && b is Double && a is Double)
|
||||
return ColorRGBa(r, g, b, a)
|
||||
}
|
||||
|
||||
|
||||
internal fun dispatchFunction4(name: String, functions: Map<String, TypedFunction4>): ((Array<Any>) -> Any)? {
|
||||
return when (name) {
|
||||
"vec4" -> { x -> vec4(x[0], x[1], x[2], x[3]) }
|
||||
"mat4" -> { x -> mat4(x[0], x[1], x[2], x[3]) }
|
||||
"rgba" -> { x -> rgba(x[0], x[1], x[2], x[3]) }
|
||||
else -> functions[name]?.let { { x: Array<Any> -> it.invoke(x[0], x[1], x[2], x[3]) } }
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package org.openrndr.extra.expressions.typed
|
||||
|
||||
fun String.memberFunctions(n: String): ((Array<Any>) -> Any)? {
|
||||
return when (n) {
|
||||
"take" -> { n -> this.take((n[0] as Number).toInt()) }
|
||||
"drop" -> { n -> this.drop((n[0] as Number).toInt()) }
|
||||
"takeLast" -> { n -> this.takeLast((n[0] as Number).toInt()) }
|
||||
"dropLast" -> { n -> this.takeLast((n[0] as Number).toInt()) }
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package org.openrndr.extra.expressions.typed
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.math.Matrix44
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.Vector3
|
||||
import org.openrndr.math.Vector4
|
||||
|
||||
internal fun String.property(property: String): Any {
|
||||
return when (property) {
|
||||
"length" -> this.length.toDouble()
|
||||
"uppercase" -> this.uppercase()
|
||||
"lowercase" -> this.lowercase()
|
||||
"reversed" -> this.reversed()
|
||||
else -> error("unknown property '$property'")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Vector2.property(property: String): Any {
|
||||
return when (property) {
|
||||
"x" -> x
|
||||
"y" -> y
|
||||
"xx" -> xx
|
||||
"yx" -> yx
|
||||
"yy" -> yy
|
||||
"xy" -> this
|
||||
"xxx" -> Vector3(x, x, x)
|
||||
"xxy" -> Vector3(x, x, y)
|
||||
"length" -> length
|
||||
"normalized" -> normalized
|
||||
else -> error("unknown property '$property")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Vector3.property(property: String): Any {
|
||||
return when (property) {
|
||||
"x" -> x
|
||||
"y" -> y
|
||||
"z" -> z
|
||||
"xx" -> Vector2(x, x)
|
||||
"yx" -> Vector2(y, x)
|
||||
"yy" -> Vector2(y, y)
|
||||
"xy" -> Vector2(x, y)
|
||||
"zx" -> Vector2(z, x)
|
||||
"xz" -> Vector2(x, z)
|
||||
"xxx" -> Vector3(x, x, x)
|
||||
"xxy" -> Vector3(x, x, y)
|
||||
"length" -> length
|
||||
"normalized" -> normalized
|
||||
|
||||
else -> error("unknown property '$property")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Vector4.property(property: String): Any {
|
||||
return when (property) {
|
||||
"x" -> x
|
||||
"y" -> y
|
||||
"z" -> z
|
||||
"xx" -> Vector2(x, x)
|
||||
"yx" -> Vector2(y, x)
|
||||
"yy" -> Vector2(y, y)
|
||||
"xy" -> Vector2(x, y)
|
||||
"zx" -> Vector2(z, x)
|
||||
"xz" -> Vector2(x, z)
|
||||
"xyz" -> Vector3(x, y, z)
|
||||
"xxy" -> Vector3(x, x, y)
|
||||
"length" -> length
|
||||
"normalized" -> normalized
|
||||
else -> error("unknown property '$property")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun ColorRGBa.property(property: String): Any {
|
||||
return when (property) {
|
||||
"r" -> r
|
||||
"g" -> g
|
||||
"b" -> b
|
||||
"a" -> alpha
|
||||
"linear" -> toLinear()
|
||||
"srgb" -> toSRGB()
|
||||
else -> error("unknown property '$property")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Matrix44.property(property: String): Any {
|
||||
return when (property) {
|
||||
"inversed" -> inversed
|
||||
"transposed" -> transposed
|
||||
else -> error("unknown property '$property")
|
||||
}
|
||||
}
|
||||
@@ -1,686 +0,0 @@
|
||||
package org.openrndr.extra.expressions.typed
|
||||
|
||||
import org.antlr.v4.kotlinruntime.*
|
||||
import org.antlr.v4.kotlinruntime.tree.ParseTreeWalker
|
||||
import org.antlr.v4.kotlinruntime.tree.TerminalNode
|
||||
import org.openrndr.collections.pop
|
||||
import org.openrndr.collections.push
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.extra.expressions.parser.KeyLangLexer
|
||||
import org.openrndr.extra.expressions.parser.KeyLangParser
|
||||
import org.openrndr.extra.expressions.parser.KeyLangParserBaseListener
|
||||
|
||||
import org.openrndr.extra.noise.uniform
|
||||
import org.openrndr.math.*
|
||||
import kotlin.math.*
|
||||
|
||||
typealias TypedFunction0 = () -> Any
|
||||
typealias TypedFunction1 = (Any) -> Any
|
||||
typealias TypedFunction2 = (Any, Any) -> Any
|
||||
typealias TypedFunction3 = (Any, Any, Any) -> Any
|
||||
typealias TypedFunction4 = (Any, Any, Any, Any) -> Any
|
||||
typealias TypedFunction5 = (Any, Any, Any, Any, Any) -> Any
|
||||
|
||||
|
||||
private fun ArrayDeque<Any>.pushChecked(item: Any) {
|
||||
// require(item is Double || item is Vector2 || item is Vector3 || item is Vector4 || item is Map<*, *> || item is Matrix44) {
|
||||
//
|
||||
// "$item ${item::class}"
|
||||
// }
|
||||
push(item)
|
||||
}
|
||||
|
||||
class TypedFunctionExtensions(
|
||||
val functions0: Map<String, TypedFunction0> = emptyMap(),
|
||||
val functions1: Map<String, TypedFunction1> = emptyMap(),
|
||||
val functions2: Map<String, TypedFunction2> = emptyMap(),
|
||||
val functions3: Map<String, TypedFunction3> = emptyMap(),
|
||||
val functions4: Map<String, TypedFunction4> = emptyMap(),
|
||||
val functions5: Map<String, TypedFunction5> = emptyMap()
|
||||
) {
|
||||
companion object {
|
||||
val EMPTY = TypedFunctionExtensions()
|
||||
}
|
||||
}
|
||||
|
||||
internal enum class IDType {
|
||||
VARIABLE,
|
||||
PROPERTY,
|
||||
MEMBER_FUNCTION0,
|
||||
MEMBER_FUNCTION1,
|
||||
MEMBER_FUNCTION2,
|
||||
MEMBER_FUNCTION3,
|
||||
FUNCTION0,
|
||||
FUNCTION1,
|
||||
FUNCTION2,
|
||||
FUNCTION3,
|
||||
FUNCTION4,
|
||||
FUNCTION5
|
||||
}
|
||||
|
||||
internal class TypedExpressionListener(
|
||||
val functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY,
|
||||
val constants: (String) -> Any? = { null }
|
||||
) :
|
||||
KeyLangParserBaseListener() {
|
||||
val valueStack = ArrayDeque<Any>()
|
||||
val functionStack = ArrayDeque<(Array<Any>) -> Any>()
|
||||
val propertyStack = ArrayDeque<String>()
|
||||
|
||||
val idTypeStack = ArrayDeque<IDType>()
|
||||
var lastExpressionResult: Any? = null
|
||||
|
||||
val exceptionStack = ArrayDeque<ExpressionException>()
|
||||
|
||||
override fun exitExpressionStatement(ctx: KeyLangParser.ExpressionStatementContext) {
|
||||
ifError {
|
||||
throw ExpressionException("error in evaluation of '${ctx.text}': ${it.message ?: ""}")
|
||||
}
|
||||
val result = valueStack.pop()
|
||||
lastExpressionResult = result
|
||||
}
|
||||
|
||||
override fun exitMinusExpression(ctx: KeyLangParser.MinusExpressionContext) {
|
||||
val op = valueStack.pop()
|
||||
valueStack.pushChecked(
|
||||
when (op) {
|
||||
is Double -> -op
|
||||
is Vector3 -> -op
|
||||
is Vector2 -> -op
|
||||
is Vector4 -> -op
|
||||
is Matrix44 -> op * -1.0
|
||||
else -> error("unsupported type")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun exitBinaryOperation1(ctx: KeyLangParser.BinaryOperation1Context) {
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
|
||||
val right = valueStack.pop()
|
||||
val left = valueStack.pop()
|
||||
|
||||
val result = when (val operator = ctx.operator?.type) {
|
||||
KeyLangLexer.Tokens.ASTERISK -> when {
|
||||
left is Double && right is Double -> left * right
|
||||
left is Vector2 && right is Vector2 -> left * right
|
||||
left is Vector2 && right is Double -> left * right
|
||||
left is Vector3 && right is Vector3 -> left * right
|
||||
left is Vector3 && right is Double -> left * right
|
||||
left is Vector4 && right is Vector4 -> left * right
|
||||
left is Vector4 && right is Double -> left * right
|
||||
left is Matrix44 && right is Matrix44 -> left * right
|
||||
left is Matrix44 && right is Vector4 -> left * right
|
||||
left is Matrix44 && right is Double -> left * right
|
||||
left is ColorRGBa && right is Double -> left * right
|
||||
left is String && right is Double -> left.repeat(right.roundToInt())
|
||||
else -> error("unsupported operands for * operator left:${left::class} right:${right::class}")
|
||||
}
|
||||
|
||||
KeyLangLexer.Tokens.DIVISION -> when {
|
||||
left is Double && right is Double -> left / right
|
||||
left is Vector2 && right is Vector2 -> left / right
|
||||
left is Vector2 && right is Double -> left / right
|
||||
left is Vector3 && right is Vector3 -> left / right
|
||||
left is Vector3 && right is Double -> left / right
|
||||
left is Vector4 && right is Vector4 -> left / right
|
||||
left is Vector4 && right is Double -> left / right
|
||||
left is ColorRGBa && right is Double -> left / right
|
||||
else -> error("unsupported operands for - operator left:${left::class} right:${right::class}")
|
||||
}
|
||||
|
||||
KeyLangLexer.Tokens.PERCENTAGE -> when {
|
||||
left is Double && right is Double -> left.mod(right)
|
||||
left is Vector2 && right is Vector2 -> left.mod(right)
|
||||
left is Vector3 && right is Vector3 -> left.mod(right)
|
||||
left is Vector4 && right is Vector4 -> left.mod(right)
|
||||
else -> error("unsupported operands for - operator left:${left::class} right:${right::class}")
|
||||
}
|
||||
|
||||
else -> error("operator '$operator' not implemented")
|
||||
}
|
||||
valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
@Suppress("IMPLICIT_CAST_TO_ANY")
|
||||
override fun exitBinaryOperation2(ctx: KeyLangParser.BinaryOperation2Context) {
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
|
||||
val right = valueStack.pop()
|
||||
val left = valueStack.pop()
|
||||
|
||||
val result = when (val operator = ctx.operator?.type) {
|
||||
KeyLangLexer.Tokens.PLUS -> when {
|
||||
left is Double && right is Double -> left + right
|
||||
left is Vector2 && right is Vector2 -> left + right
|
||||
left is Vector3 && right is Vector3 -> left + right
|
||||
left is Vector4 && right is Vector4 -> left + right
|
||||
left is Matrix44 && right is Matrix44 -> left + right
|
||||
left is ColorRGBa && right is ColorRGBa -> left + right
|
||||
left is String && right is String -> left + right
|
||||
else -> error("unsupported operands for + operator left:${left::class} right:${right::class}")
|
||||
}
|
||||
|
||||
KeyLangLexer.Tokens.MINUS -> when {
|
||||
left is Double && right is Double -> left - right
|
||||
left is Vector2 && right is Vector2 -> left - right
|
||||
left is Vector3 && right is Vector3 -> left - right
|
||||
left is Vector4 && right is Vector4 -> left - right
|
||||
left is Matrix44 && right is Matrix44 -> left - right
|
||||
left is ColorRGBa && right is ColorRGBa -> left - right
|
||||
else -> error("unsupported operands for - operator left:${left::class} right:${right::class}")
|
||||
}
|
||||
|
||||
else -> error("operator '$operator' not implemented")
|
||||
}
|
||||
valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
override fun exitJoinOperation(ctx: KeyLangParser.JoinOperationContext) {
|
||||
val right = (valueStack.pop() as Double).roundToInt()
|
||||
val left = (valueStack.pop() as Double).roundToInt()
|
||||
|
||||
val result = when (val operator = ctx.operator?.type) {
|
||||
KeyLangLexer.Tokens.AND -> right != 0 && left != 0
|
||||
KeyLangLexer.Tokens.OR -> right != 0 || left != 0
|
||||
else -> error("operator '$operator' not implemented")
|
||||
}
|
||||
valueStack.pushChecked(if (result) 1.0 else 0.0)
|
||||
}
|
||||
|
||||
override fun exitComparisonOperation(ctx: KeyLangParser.ComparisonOperationContext) {
|
||||
val right = valueStack.pop()
|
||||
val left = valueStack.pop()
|
||||
|
||||
val result = when (val operator = ctx.operator?.type) {
|
||||
KeyLangLexer.Tokens.EQ -> when {
|
||||
left is Double && right is Double -> left == right
|
||||
left is Vector2 && right is Vector2 -> left == right
|
||||
left is Vector3 && right is Vector3 -> left == right
|
||||
left is Vector4 && right is Vector4 -> left == right
|
||||
left is ColorRGBa && right is ColorRGBa -> left == right
|
||||
left is String && right is String -> left == right
|
||||
else -> error("unsupported operands for == operator left:${left::class} right:${right::class}")
|
||||
}
|
||||
KeyLangLexer.Tokens.LTEQ -> when {
|
||||
left is Double && right is Double -> left <= right
|
||||
else -> error("unsupported operands for <= operator left:${left::class} right:${right::class}")
|
||||
}
|
||||
KeyLangLexer.Tokens.LT -> when {
|
||||
left is Double && right is Double -> left < right
|
||||
else -> error("unsupported operands for < operator left:${left::class} right:${right::class}")
|
||||
}
|
||||
KeyLangLexer.Tokens.GTEQ -> when {
|
||||
left is Double && right is Double -> left >= right
|
||||
else -> error("unsupported operands for >= operator left:${left::class} right:${right::class}")
|
||||
}
|
||||
KeyLangLexer.Tokens.GT -> when {
|
||||
left is Double && right is Double -> left > right
|
||||
else -> error("unsupported operands for > operator left:${left::class} right:${right::class}")
|
||||
}
|
||||
|
||||
else -> error("operator '$operator' not implemented")
|
||||
}
|
||||
valueStack.pushChecked(if (result) 1.0 else 0.0)
|
||||
}
|
||||
|
||||
override fun exitNegateExpression(ctx: KeyLangParser.NegateExpressionContext) {
|
||||
val operand = (valueStack.pop() as Double).roundToInt()
|
||||
valueStack.pushChecked(if (operand == 0) 1.0 else 0.0)
|
||||
}
|
||||
|
||||
override fun exitTernaryExpression(ctx: KeyLangParser.TernaryExpressionContext) {
|
||||
val right = valueStack.pop()
|
||||
val left = valueStack.pop()
|
||||
val comp = valueStack.pop()
|
||||
|
||||
val result = when (comp) {
|
||||
is Double -> if (comp.roundToInt() != 0) left else right
|
||||
else -> error("can't compare")
|
||||
}
|
||||
valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
override fun enterValueReference(ctx: KeyLangParser.ValueReferenceContext) {
|
||||
idTypeStack.push(IDType.VARIABLE)
|
||||
}
|
||||
|
||||
override fun enterMemberFunctionCall0Expression(ctx: KeyLangParser.MemberFunctionCall0ExpressionContext) {
|
||||
idTypeStack.push(IDType.MEMBER_FUNCTION1)
|
||||
}
|
||||
|
||||
override fun exitMemberFunctionCall0Expression(ctx: KeyLangParser.MemberFunctionCall0ExpressionContext) {
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
valueStack.pushChecked(functionStack.pop().invoke(emptyArray()))
|
||||
}
|
||||
|
||||
override fun enterMemberFunctionCall1Expression(ctx: KeyLangParser.MemberFunctionCall1ExpressionContext) {
|
||||
idTypeStack.push(IDType.MEMBER_FUNCTION1)
|
||||
}
|
||||
|
||||
override fun exitMemberFunctionCall1Expression(ctx: KeyLangParser.MemberFunctionCall1ExpressionContext) {
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
valueStack.pushChecked(functionStack.pop().invoke(arrayOf(valueStack.pop())))
|
||||
}
|
||||
|
||||
override fun enterMemberFunctionCall2Expression(ctx: KeyLangParser.MemberFunctionCall2ExpressionContext) {
|
||||
idTypeStack.push(IDType.MEMBER_FUNCTION2)
|
||||
}
|
||||
|
||||
override fun exitMemberFunctionCall2Expression(ctx: KeyLangParser.MemberFunctionCall2ExpressionContext) {
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
val argument1 = valueStack.pop()
|
||||
val argument0 = valueStack.pop()
|
||||
|
||||
valueStack.pushChecked(functionStack.pop().invoke(arrayOf(argument0, argument1)))
|
||||
}
|
||||
|
||||
override fun enterMemberFunctionCall3Expression(ctx: KeyLangParser.MemberFunctionCall3ExpressionContext) {
|
||||
idTypeStack.push(IDType.MEMBER_FUNCTION3)
|
||||
}
|
||||
|
||||
override fun exitMemberFunctionCall3Expression(ctx: KeyLangParser.MemberFunctionCall3ExpressionContext) {
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
val argument2 = valueStack.pop()
|
||||
val argument1 = valueStack.pop()
|
||||
val argument0 = valueStack.pop()
|
||||
|
||||
valueStack.pushChecked(functionStack.pop().invoke(arrayOf(argument0, argument1, argument2)))
|
||||
}
|
||||
|
||||
|
||||
override fun enterFunctionCall0Expression(ctx: KeyLangParser.FunctionCall0ExpressionContext) {
|
||||
idTypeStack.push(IDType.FUNCTION0)
|
||||
}
|
||||
|
||||
override fun exitFunctionCall0Expression(ctx: KeyLangParser.FunctionCall0ExpressionContext) {
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
|
||||
val function = functionStack.pop()
|
||||
val result = function.invoke(arrayOf())
|
||||
valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
override fun enterFunctionCall1Expression(ctx: KeyLangParser.FunctionCall1ExpressionContext) {
|
||||
idTypeStack.push(IDType.FUNCTION1)
|
||||
}
|
||||
|
||||
override fun exitFunctionCall1Expression(ctx: KeyLangParser.FunctionCall1ExpressionContext) {
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
|
||||
val function = functionStack.pop()
|
||||
val argument = valueStack.pop()
|
||||
|
||||
val result = function.invoke(arrayOf(argument))
|
||||
valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
override fun enterFunctionCall2Expression(ctx: KeyLangParser.FunctionCall2ExpressionContext) {
|
||||
idTypeStack.push(IDType.FUNCTION2)
|
||||
}
|
||||
|
||||
override fun exitFunctionCall2Expression(ctx: KeyLangParser.FunctionCall2ExpressionContext) {
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
|
||||
val function = functionStack.pop()
|
||||
val argument1 = valueStack.pop()
|
||||
val argument0 = valueStack.pop()
|
||||
|
||||
val result = function.invoke(arrayOf(argument0, argument1))
|
||||
valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
override fun enterFunctionCall3Expression(ctx: KeyLangParser.FunctionCall3ExpressionContext) {
|
||||
idTypeStack.push(IDType.FUNCTION3)
|
||||
}
|
||||
|
||||
override fun exitFunctionCall3Expression(ctx: KeyLangParser.FunctionCall3ExpressionContext) {
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
|
||||
val function = functionStack.pop()
|
||||
val argument2 = valueStack.pop()
|
||||
val argument1 = valueStack.pop()
|
||||
val argument0 = valueStack.pop()
|
||||
|
||||
val result = function.invoke(arrayOf(argument0, argument1, argument2))
|
||||
valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
override fun enterFunctionCall4Expression(ctx: KeyLangParser.FunctionCall4ExpressionContext) {
|
||||
idTypeStack.push(IDType.FUNCTION4)
|
||||
}
|
||||
|
||||
override fun exitFunctionCall4Expression(ctx: KeyLangParser.FunctionCall4ExpressionContext) {
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
|
||||
val function = functionStack.pop()
|
||||
val argument3 = valueStack.pop()
|
||||
val argument2 = valueStack.pop()
|
||||
val argument1 = valueStack.pop()
|
||||
val argument0 = valueStack.pop()
|
||||
|
||||
val result = function.invoke(arrayOf(argument0, argument1, argument2, argument3))
|
||||
valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
|
||||
override fun enterFunctionCall5Expression(ctx: KeyLangParser.FunctionCall5ExpressionContext) {
|
||||
idTypeStack.push(IDType.FUNCTION5)
|
||||
}
|
||||
|
||||
override fun exitFunctionCall5Expression(ctx: KeyLangParser.FunctionCall5ExpressionContext) {
|
||||
ifError {
|
||||
pushError(it.message ?: "")
|
||||
return
|
||||
}
|
||||
|
||||
val function = functionStack.pop()
|
||||
val argument4 = valueStack.pop()
|
||||
val argument3 = valueStack.pop()
|
||||
val argument2 = valueStack.pop()
|
||||
val argument1 = valueStack.pop()
|
||||
val argument0 = valueStack.pop()
|
||||
|
||||
val result = function.invoke(arrayOf(argument0, argument1, argument2, argument3, argument4))
|
||||
valueStack.pushChecked(result)
|
||||
}
|
||||
|
||||
private fun <T> errorValue(message: String, value: T): T {
|
||||
pushError(message)
|
||||
return value
|
||||
}
|
||||
|
||||
private fun pushError(message: String) {
|
||||
exceptionStack.push(ExpressionException(message))
|
||||
}
|
||||
|
||||
private inline fun ifError(f: (e: Throwable) -> Unit) {
|
||||
if (exceptionStack.isNotEmpty()) {
|
||||
val e = exceptionStack.pop()
|
||||
f(e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun enterPropReference(ctx: KeyLangParser.PropReferenceContext) {
|
||||
idTypeStack.push(IDType.PROPERTY)
|
||||
}
|
||||
|
||||
override fun exitPropReference(ctx: KeyLangParser.PropReferenceContext) {
|
||||
val root = valueStack.pop()
|
||||
var current = root
|
||||
val property = propertyStack.pop()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
current = when (current) {
|
||||
is Map<*, *> -> current[property] ?: error("property '$property' not found")
|
||||
is Function<*> -> (current as ((String) -> Any?)).invoke(property)
|
||||
?: error("property '$property' not found")
|
||||
|
||||
is Vector2 -> current.property(property)
|
||||
is Vector3 -> current.property(property)
|
||||
is Vector4 -> current.property(property)
|
||||
is ColorRGBa -> current.property(property)
|
||||
is Matrix44 -> current.property(property)
|
||||
else -> error("can't look up: ${current::class} '$current', root:'$root' ${ctx.text} ")
|
||||
}
|
||||
valueStack.push(current)
|
||||
}
|
||||
|
||||
|
||||
override fun visitTerminal(node: TerminalNode) {
|
||||
|
||||
val type = node.symbol.type
|
||||
if (type == KeyLangParser.Tokens.INTLIT) {
|
||||
valueStack.pushChecked(node.text.toDouble())
|
||||
} else if (type == KeyLangParser.Tokens.DECLIT) {
|
||||
valueStack.pushChecked(node.text.toDouble())
|
||||
} else if (type == KeyLangParser.Tokens.STRING_CONTENT) {
|
||||
valueStack.pushChecked(node.text)
|
||||
} else if (type == KeyLangParser.Tokens.ID) {
|
||||
val name = node.text.replace("`", "")
|
||||
@Suppress("DIVISION_BY_ZERO")
|
||||
when (val idType = idTypeStack.pop()) {
|
||||
IDType.VARIABLE -> valueStack.pushChecked(
|
||||
when (name) {
|
||||
"PI" -> PI
|
||||
else -> constants(name) ?: errorValue("unresolved variable: '${name}'", 0.0 / 0.0)
|
||||
}
|
||||
)
|
||||
|
||||
IDType.PROPERTY -> propertyStack.push(name)
|
||||
|
||||
IDType.FUNCTION0 -> {
|
||||
val function: (Array<Any>) -> Any =
|
||||
when (name) {
|
||||
"random" -> { _ -> Double.uniform(0.0, 1.0) }
|
||||
else -> functions.functions0[name]?.let { { _: Array<Any> -> it.invoke() } }
|
||||
?: errorValue(
|
||||
"unresolved function: '${name}()'"
|
||||
) { _ -> error("this is the error function") }
|
||||
}
|
||||
functionStack.push(function)
|
||||
}
|
||||
|
||||
IDType.MEMBER_FUNCTION0,
|
||||
IDType.MEMBER_FUNCTION1,
|
||||
IDType.MEMBER_FUNCTION2,
|
||||
IDType.MEMBER_FUNCTION3 -> {
|
||||
val receiver = valueStack.pop()
|
||||
when (receiver) {
|
||||
is String -> {
|
||||
functionStack.push(
|
||||
receiver.memberFunctions(name)
|
||||
?: error("no member function '$receiver.$name()'")
|
||||
)
|
||||
}
|
||||
|
||||
is ColorRGBa -> {
|
||||
when (idType) {
|
||||
IDType.MEMBER_FUNCTION1 -> {
|
||||
functionStack.push(when (name) {
|
||||
"shade" -> { x -> receiver.shade(x[0] as Double) }
|
||||
"opacify" -> { x -> receiver.opacify(x[0] as Double) }
|
||||
else -> error("no member function '$receiver.$name()'")
|
||||
})
|
||||
}
|
||||
|
||||
else -> error("no member function $idType '$receiver.$name()")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
is Function<*> -> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
receiver as (String) -> Any
|
||||
@Suppress("UNCHECKED_CAST") val function =
|
||||
receiver.invoke(name) ?: error("no such function $name")
|
||||
|
||||
when (idType) {
|
||||
IDType.MEMBER_FUNCTION0 -> {
|
||||
function as () -> Any
|
||||
functionStack.push({ function() })
|
||||
}
|
||||
|
||||
IDType.MEMBER_FUNCTION1 -> {
|
||||
function as (Any) -> Any
|
||||
functionStack.push({ x -> function(x[0]) })
|
||||
}
|
||||
|
||||
IDType.MEMBER_FUNCTION2 -> {
|
||||
function as (Any, Any) -> Any
|
||||
functionStack.push({ x -> function(x[0], x[1]) })
|
||||
}
|
||||
|
||||
IDType.MEMBER_FUNCTION3 -> {
|
||||
function as (Any, Any, Any) -> Any
|
||||
functionStack.push({ x -> function(x[0], x[1], x[2]) })
|
||||
}
|
||||
|
||||
else -> error("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
else -> error("receiver '${receiver}' not supported")
|
||||
}
|
||||
}
|
||||
|
||||
IDType.FUNCTION1 -> {
|
||||
val function: (Array<Any>) -> Any =
|
||||
dispatchFunction1(name, functions.functions1)
|
||||
?: errorValue(
|
||||
"unresolved function: '${name}(x0)'"
|
||||
) { _ -> error("this is the error function") }
|
||||
functionStack.push(function)
|
||||
}
|
||||
|
||||
IDType.FUNCTION2 -> {
|
||||
val function: (Array<Any>) -> Any =
|
||||
dispatchFunction2(name, functions.functions2)
|
||||
?: errorValue(
|
||||
"unresolved function: '${name}(x0, x1)'"
|
||||
) { _ -> error("this is the error function") }
|
||||
functionStack.push(function)
|
||||
}
|
||||
|
||||
IDType.FUNCTION3 -> {
|
||||
val function: (Array<Any>) -> Any =
|
||||
dispatchFunction3(name, functions.functions3)
|
||||
?: errorValue(
|
||||
"unresolved function: '${name}(x0)'"
|
||||
) { _ -> error("this is the error function") }
|
||||
functionStack.push(function)
|
||||
}
|
||||
|
||||
IDType.FUNCTION4 -> {
|
||||
val function: (Array<Any>) -> Any =
|
||||
dispatchFunction4(name, functions.functions4)
|
||||
?: errorValue(
|
||||
"unresolved function: '${name}(x0)'"
|
||||
) { _ -> error("this is the error function") }
|
||||
functionStack.push(function)
|
||||
}
|
||||
|
||||
else -> error("unsupported id-type $idType")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ExpressionException(message: String) : RuntimeException(message)
|
||||
|
||||
fun evaluateTypedExpression(
|
||||
expression: String,
|
||||
constants: (String) -> Any? = { null },
|
||||
functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY
|
||||
): Any? {
|
||||
val lexer = KeyLangLexer(CharStreams.fromString(expression))
|
||||
val parser = KeyLangParser(CommonTokenStream(lexer))
|
||||
parser.removeErrorListeners()
|
||||
parser.addErrorListener(object : BaseErrorListener() {
|
||||
override fun syntaxError(
|
||||
recognizer: Recognizer<*, *>,
|
||||
offendingSymbol: Any?,
|
||||
line: Int,
|
||||
charPositionInLine: Int,
|
||||
msg: String,
|
||||
e: RecognitionException?
|
||||
) {
|
||||
throw ExpressionException("parser error in expression: '$expression'; [line: $line, character: $charPositionInLine ${offendingSymbol?.let { ", near: $it" } ?: ""} ]")
|
||||
}
|
||||
})
|
||||
|
||||
val root = parser.keyLangFile()
|
||||
val listener = TypedExpressionListener(functions, constants)
|
||||
try {
|
||||
ParseTreeWalker.DEFAULT.walk(listener, root)
|
||||
} catch (e: ExpressionException) {
|
||||
throw ExpressionException(e.message ?: "")
|
||||
}
|
||||
return listener.lastExpressionResult
|
||||
}
|
||||
|
||||
fun compileTypedExpression(
|
||||
expression: String,
|
||||
constants: (String) -> Any? = { null },
|
||||
functions: TypedFunctionExtensions = TypedFunctionExtensions.EMPTY
|
||||
): () -> Any {
|
||||
val lexer = KeyLangLexer(CharStreams.fromString(expression))
|
||||
val parser = KeyLangParser(CommonTokenStream(lexer))
|
||||
parser.removeErrorListeners()
|
||||
parser.addErrorListener(object : BaseErrorListener() {
|
||||
override fun syntaxError(
|
||||
recognizer: Recognizer<*, *>,
|
||||
offendingSymbol: Any?,
|
||||
line: Int,
|
||||
charPositionInLine: Int,
|
||||
msg: String,
|
||||
e: RecognitionException?
|
||||
) {
|
||||
throw ExpressionException("parser error in expression: '$expression'; [line: $line, character: $charPositionInLine ${offendingSymbol?.let { ", near: $it" } ?: ""} ]")
|
||||
}
|
||||
})
|
||||
val root = parser.keyLangFile()
|
||||
val listener = TypedExpressionListener(functions, constants)
|
||||
|
||||
|
||||
return {
|
||||
try {
|
||||
ParseTreeWalker.DEFAULT.walk(listener, root)
|
||||
} catch (e: ExpressionException) {
|
||||
throw ExpressionException(e.message ?: "")
|
||||
}
|
||||
listener.lastExpressionResult ?: error("no result")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun expressionRoot(expression: String): KeyLangParser.KeyLangFileContext {
|
||||
val lexer = KeyLangLexer(CharStreams.fromString(expression))
|
||||
val parser = KeyLangParser(CommonTokenStream(lexer))
|
||||
parser.removeErrorListeners()
|
||||
parser.addErrorListener(object : BaseErrorListener() {
|
||||
override fun syntaxError(
|
||||
recognizer: Recognizer<*, *>,
|
||||
offendingSymbol: Any?,
|
||||
line: Int,
|
||||
charPositionInLine: Int,
|
||||
msg: String,
|
||||
e: RecognitionException?
|
||||
) {
|
||||
throw ExpressionException("parser error in expression: '$expression'; [line: $line, character: $charPositionInLine ${offendingSymbol?.let { ", near: $it" } ?: ""} ]")
|
||||
}
|
||||
})
|
||||
return parser.keyLangFile()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user