Fix unary minus operators

This commit is contained in:
Edwin Jakobs
2020-04-21 11:46:01 +02:00
parent dafd309924
commit 6a29853c71
2 changed files with 88 additions and 84 deletions

View File

@@ -5,10 +5,7 @@ import org.antlr.v4.runtime.tree.ParseTreeWalker
import org.antlr.v4.runtime.tree.TerminalNode import org.antlr.v4.runtime.tree.TerminalNode
import org.openrndr.extra.keyframer.antlr.* import org.openrndr.extra.keyframer.antlr.*
import org.openrndr.extra.noise.uniform import org.openrndr.extra.noise.uniform
import org.openrndr.math.map import org.openrndr.math.*
import org.openrndr.math.mix
import org.openrndr.math.mod
import org.openrndr.math.smoothstep
import java.util.* import java.util.*
import kotlin.math.* import kotlin.math.*
@@ -20,12 +17,12 @@ typealias Function4 = (Double, Double, Double, Double) -> Double
typealias Function5 = (Double, Double, Double, Double, Double) -> Double typealias Function5 = (Double, Double, Double, Double, Double) -> Double
class FunctionExtensions( class FunctionExtensions(
val functions0: Map<String, Function0> = emptyMap(), val functions0: Map<String, Function0> = emptyMap(),
val functions1: Map<String, Function1> = emptyMap(), val functions1: Map<String, Function1> = emptyMap(),
val functions2: Map<String, Function2> = emptyMap(), val functions2: Map<String, Function2> = emptyMap(),
val functions3: Map<String, Function3> = emptyMap(), val functions3: Map<String, Function3> = emptyMap(),
val functions4: Map<String, Function4> = emptyMap(), val functions4: Map<String, Function4> = emptyMap(),
val functions5: Map<String, Function5> = emptyMap() val functions5: Map<String, Function5> = emptyMap()
) { ) {
companion object { companion object {
val EMPTY = FunctionExtensions() val EMPTY = FunctionExtensions()
@@ -43,7 +40,7 @@ internal enum class IDType {
} }
internal class ExpressionListener(val functions: FunctionExtensions = FunctionExtensions.EMPTY) : internal class ExpressionListener(val functions: FunctionExtensions = FunctionExtensions.EMPTY) :
KeyLangParserBaseListener() { KeyLangParserBaseListener() {
val doubleStack = Stack<Double>() val doubleStack = Stack<Double>()
val functionStack = Stack<(DoubleArray) -> Double>() val functionStack = Stack<(DoubleArray) -> Double>()
val variables = mutableMapOf<String, Double>() val variables = mutableMapOf<String, Double>()
@@ -68,7 +65,8 @@ internal class ExpressionListener(val functions: FunctionExtensions = FunctionEx
} }
override fun exitMinusExpression(ctx: KeyLangParser.MinusExpressionContext) { override fun exitMinusExpression(ctx: KeyLangParser.MinusExpressionContext) {
super.exitMinusExpression(ctx) val op = doubleStack.pop()
doubleStack.push(-op)
} }
override fun exitBinaryOperation1(ctx: KeyLangParser.BinaryOperation1Context) { override fun exitBinaryOperation1(ctx: KeyLangParser.BinaryOperation1Context) {
@@ -252,95 +250,97 @@ internal class ExpressionListener(val functions: FunctionExtensions = FunctionEx
@Suppress("DIVISION_BY_ZERO") @Suppress("DIVISION_BY_ZERO")
when (val idType = idTypeStack.pop()) { when (val idType = idTypeStack.pop()) {
IDType.VARIABLE -> doubleStack.push( IDType.VARIABLE -> doubleStack.push(
when (val name = node.text) { when (val name = node.text) {
"PI" -> PI "PI" -> PI
else -> variables[name] ?: errorValue("unresolved variable: '${name}'", 0.0 / 0.0) else -> variables[name] ?: errorValue("unresolved variable: '${name}'", 0.0 / 0.0)
} }
) )
IDType.FUNCTION0 -> { IDType.FUNCTION0 -> {
val function: (DoubleArray) -> Double = val function: (DoubleArray) -> Double =
when (val candidate = node.text) { when (val candidate = node.text) {
"random" -> { _ -> Double.uniform(0.0, 1.0) } "random" -> { _ -> Double.uniform(0.0, 1.0) }
else -> functions.functions0[candidate]?.let { { _:DoubleArray -> it.invoke() } } else -> functions.functions0[candidate]?.let { { _: DoubleArray -> it.invoke() } }
?: errorValue( ?: errorValue(
"unresolved function: '${candidate}()'" "unresolved function: '${candidate}()'"
) { _ -> error("this is the error function") } ) { _ -> error("this is the error function") }
} }
functionStack.push(function) functionStack.push(function)
} }
IDType.FUNCTION1 -> { IDType.FUNCTION1 -> {
val function: (DoubleArray) -> Double = val function: (DoubleArray) -> Double =
when (val candidate = node.text) { when (val candidate = node.text) {
"sqrt" -> { x -> sqrt(x[0]) } "sqrt" -> { x -> sqrt(x[0]) }
"radians" -> { x -> Math.toRadians(x[0]) } "radians" -> { x -> Math.toRadians(x[0]) }
"degrees" -> { x -> Math.toDegrees(x[0]) } "degrees" -> { x -> Math.toDegrees(x[0]) }
"cos" -> { x -> cos(x[0]) } "cos" -> { x -> cos(x[0]) }
"sin" -> { x -> sin(x[0]) } "sin" -> { x -> sin(x[0]) }
"tan" -> { x -> tan(x[0]) } "tan" -> { x -> tan(x[0]) }
"atan" -> { x -> atan(x[0]) } "atan" -> { x -> atan(x[0]) }
"acos" -> { x -> acos(x[0]) } "acos" -> { x -> acos(x[0]) }
"asin" -> { x -> asin(x[0]) } "asin" -> { x -> asin(x[0]) }
"exp" -> { x -> exp(x[0]) } "exp" -> { x -> exp(x[0]) }
"abs" -> { x -> abs(x[0]) } "abs" -> { x -> abs(x[0]) }
"floor" -> { x -> floor(x[0]) } "floor" -> { x -> floor(x[0]) }
"ceil" -> { x -> ceil(x[0]) } "ceil" -> { x -> ceil(x[0]) }
"saturate" -> { x -> x[0].coerceIn(0.0, 1.0) } "saturate" -> { x -> x[0].coerceIn(0.0, 1.0) }
else -> functions.functions1[candidate]?.let { { x:DoubleArray -> it.invoke(x[0]) } } else -> functions.functions1[candidate]?.let { { x: DoubleArray -> it.invoke(x[0]) } }
?: errorValue( ?: errorValue(
"unresolved function: '${candidate}(x0)'" "unresolved function: '${candidate}(x0)'"
) { _ -> error("this is the error function") } ) { _ -> error("this is the error function") }
} }
functionStack.push(function) functionStack.push(function)
} }
IDType.FUNCTION2 -> { IDType.FUNCTION2 -> {
val function: (DoubleArray) -> Double = val function: (DoubleArray) -> Double =
when (val candidate = node.text) { when (val candidate = node.text) {
"max" -> { x -> max(x[0], x[1]) } "max" -> { x -> max(x[0], x[1]) }
"min" -> { x -> min(x[0], x[1]) } "min" -> { x -> min(x[0], x[1]) }
"pow" -> { x -> x[0].pow(x[1]) } "pow" -> { x -> x[0].pow(x[1]) }
"atan2" -> { x -> atan2(x[0], x[1]) } "atan2" -> { x -> atan2(x[0], x[1]) }
"random" -> { x -> Double.uniform(x[0], x[1]) } "random" -> { x -> Double.uniform(x[0], x[1]) }
else -> functions.functions2[candidate]?.let { { x:DoubleArray -> it.invoke(x[0], x[1]) } } "length" -> { x -> Vector2(x[0], x[1]).length }
?: errorValue( else -> functions.functions2[candidate]?.let { { x: DoubleArray -> it.invoke(x[0], x[1]) } }
"unresolved function: '${candidate}(x0, x1)'" ?: errorValue(
) { _ -> error("this is the error function") } "unresolved function: '${candidate}(x0, x1)'"
} ) { _ -> error("this is the error function") }
}
functionStack.push(function) functionStack.push(function)
} }
IDType.FUNCTION3 -> { IDType.FUNCTION3 -> {
val function: (DoubleArray) -> Double = val function: (DoubleArray) -> Double =
when (val candidate = node.text) { when (val candidate = node.text) {
"mix" -> { x -> mix(x[0], x[1], x[2]) } "mix" -> { x -> mix(x[0], x[1], x[2]) }
"smoothstep" -> { x -> smoothstep(x[0], x[1], x[2]) } "smoothstep" -> { x -> smoothstep(x[0], x[1], x[2]) }
else -> functions.functions3[candidate]?.let { { x:DoubleArray -> it.invoke(x[0], x[1], x[2]) } } "length" -> { x -> Vector3(x[0], x[1], x[2]).length }
?: errorValue( else -> functions.functions3[candidate]?.let { { x: DoubleArray -> it.invoke(x[0], x[1], x[2]) } }
"unresolved function: '${candidate}(x0, x1, x2)'" ?: errorValue(
) { _ -> error("this is the error function") } "unresolved function: '${candidate}(x0, x1, x2)'"
} ) { _ -> error("this is the error function") }
}
functionStack.push(function) functionStack.push(function)
} }
IDType.FUNCTION4 -> { IDType.FUNCTION4 -> {
val function: (DoubleArray) -> Double = val function: (DoubleArray) -> Double =
when (val candidate = node.text) { when (val candidate = node.text) {
else -> functions.functions4[candidate]?.let { { x:DoubleArray -> it.invoke(x[0], x[1], x[2], x[3]) } } else -> functions.functions4[candidate]?.let { { x: DoubleArray -> it.invoke(x[0], x[1], x[2], x[3]) } }
?: errorValue( ?: errorValue(
"unresolved function: '${candidate}(x0, x1, x2, x3)'" "unresolved function: '${candidate}(x0, x1, x2, x3)'"
) { _ -> error("this is the error function") } ) { _ -> error("this is the error function") }
} }
functionStack.push(function) functionStack.push(function)
} }
IDType.FUNCTION5 -> { IDType.FUNCTION5 -> {
val function: (DoubleArray) -> Double = val function: (DoubleArray) -> Double =
when (val candidate = node.text) { when (val candidate = node.text) {
"map" -> { x -> map(x[0], x[1], x[2], x[3], x[4]) } "map" -> { x -> map(x[0], x[1], x[2], x[3], x[4]) }
else -> functions.functions5[candidate]?.let { { x:DoubleArray -> it.invoke(x[0], x[1], x[2], x[3], x[4]) } } else -> functions.functions5[candidate]?.let { { x: DoubleArray -> it.invoke(x[0], x[1], x[2], x[3], x[4]) } }
?: errorValue( ?: errorValue(
"unresolved function: '${candidate}(x0, x1, x2, x3, x4)'" "unresolved function: '${candidate}(x0, x1, x2, x3, x4)'"
) { _ -> error("this is the error function") } ) { _ -> error("this is the error function") }
} }
functionStack.push(function) functionStack.push(function)
} }
else -> error("unsupported id-type $idType") else -> error("unsupported id-type $idType")
@@ -352,9 +352,9 @@ internal class ExpressionListener(val functions: FunctionExtensions = FunctionEx
class ExpressionException(message: String) : RuntimeException(message) class ExpressionException(message: String) : RuntimeException(message)
fun evaluateExpression( fun evaluateExpression(
input: String, input: String,
variables: Map<String, Double> = emptyMap(), variables: Map<String, Double> = emptyMap(),
functions: FunctionExtensions = FunctionExtensions.EMPTY functions: FunctionExtensions = FunctionExtensions.EMPTY
): Double? { ): Double? {
val lexer = KeyLangLexer(CharStreams.fromString(input)) val lexer = KeyLangLexer(CharStreams.fromString(input))
// //
@@ -375,12 +375,12 @@ fun evaluateExpression(
parser.removeErrorListeners() parser.removeErrorListeners()
parser.addErrorListener(object : BaseErrorListener() { parser.addErrorListener(object : BaseErrorListener() {
override fun syntaxError( override fun syntaxError(
recognizer: Recognizer<*, *>?, recognizer: Recognizer<*, *>?,
offendingSymbol: Any?, offendingSymbol: Any?,
line: Int, line: Int,
charPositionInLine: Int, charPositionInLine: Int,
msg: String?, msg: String?,
e: RecognitionException? e: RecognitionException?
) { ) {
throw ExpressionException("parser error in expression: '$input'; [line: $line, character: $charPositionInLine ${offendingSymbol?.let { ", near: $it" } ?: ""} ]") throw ExpressionException("parser error in expression: '$input'; [line: $line, character: $charPositionInLine ${offendingSymbol?.let { ", near: $it" } ?: ""} ]")
} }

View File

@@ -32,4 +32,8 @@ object TestOperators : Spek({
val result = evaluateExpression("4 + 2 * 3") val result = evaluateExpression("4 + 2 * 3")
result?.shouldBeNear(10.0, 10E-6) result?.shouldBeNear(10.0, 10E-6)
} }
describe("unary minus") {
val result = evaluateExpression("-4.0")
result?.shouldBeNear(-4.0, 10E-6)
}
}) })