[orx-keyframer] Add support for backticked identifier names, replace spek tests
This commit is contained in:
@@ -38,6 +38,8 @@ dependencies {
|
|||||||
implementation libs.gson
|
implementation libs.gson
|
||||||
implementation(libs.kotlin.reflect)
|
implementation(libs.kotlin.reflect)
|
||||||
|
|
||||||
|
testImplementation(libs.kotlin.test)
|
||||||
|
|
||||||
demoImplementation(project(":orx-camera"))
|
demoImplementation(project(":orx-camera"))
|
||||||
demoImplementation(project(":orx-jvm:orx-panel"))
|
demoImplementation(project(":orx-jvm:orx-panel"))
|
||||||
|
|
||||||
@@ -50,4 +52,8 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tasks.getByName("compileKotlin").dependsOn("generateGrammarSource")
|
tasks.getByName("compileKotlin").dependsOn("generateGrammarSource")
|
||||||
tasks.getByName("compileTestKotlin").dependsOn("generateTestGrammarSource")
|
tasks.getByName("compileTestKotlin").dependsOn("generateTestGrammarSource")
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ DECIMAL : 'Decimal';
|
|||||||
STRING : 'String';
|
STRING : 'String';
|
||||||
|
|
||||||
// Identifiers
|
// Identifiers
|
||||||
ID : [$_]*[a-zA-Z][A-Za-z0-9_]* ;
|
ID : [$_]*[a-zA-Z][A-Za-z0-9_]* | '`'[$_]*[A-Za-z0-9_-]*'`';
|
||||||
FUNCTION_ID : [$_]*[a-z][A-Za-z0-9_]* ;
|
FUNCTION_ID : [$_]*[a-z][A-Za-z0-9_]* ;
|
||||||
|
|
||||||
// Literals
|
// Literals
|
||||||
@@ -38,6 +38,7 @@ ASSIGN : '=' ;
|
|||||||
LPAREN : '(' ;
|
LPAREN : '(' ;
|
||||||
RPAREN : ')' ;
|
RPAREN : ')' ;
|
||||||
|
|
||||||
|
|
||||||
COMMA : ',' ;
|
COMMA : ',' ;
|
||||||
|
|
||||||
STRING_OPEN : '"' -> pushMode(MODE_IN_STRING);
|
STRING_OPEN : '"' -> pushMode(MODE_IN_STRING);
|
||||||
@@ -51,40 +52,5 @@ ESCAPE_SLASH : '\\\\' ;
|
|||||||
ESCAPE_NEWLINE : '\\n' ;
|
ESCAPE_NEWLINE : '\\n' ;
|
||||||
ESCAPE_SHARP : '\\#' ;
|
ESCAPE_SHARP : '\\#' ;
|
||||||
STRING_CLOSE : '"' -> popMode ;
|
STRING_CLOSE : '"' -> popMode ;
|
||||||
INTERPOLATION_OPEN : '#{' -> pushMode(MODE_IN_INTERPOLATION) ;
|
|
||||||
STRING_CONTENT : ~["\n\r\t\\#]+ ;
|
STRING_CONTENT : ~["\n\r\t\\#]+ ;
|
||||||
|
|
||||||
STR_UNMATCHED : . -> type(UNMATCHED) ;
|
|
||||||
|
|
||||||
mode MODE_IN_INTERPOLATION;
|
|
||||||
|
|
||||||
INTERPOLATION_CLOSE : '}' -> popMode ;
|
|
||||||
|
|
||||||
INTERP_WS : [\t ]+ -> channel(WHITESPACE), type(WS) ;
|
|
||||||
|
|
||||||
// Keywords
|
|
||||||
INTERP_AS : 'as'-> type(AS) ;
|
|
||||||
INTERP_INT : 'Int'-> type(INT) ;
|
|
||||||
INTERP_DECIMAL : 'Decimal'-> type(DECIMAL) ;
|
|
||||||
INTERP_STRING : 'String'-> type(STRING) ;
|
|
||||||
|
|
||||||
// Literals
|
|
||||||
INTERP_INTLIT : ('0'|[1-9][0-9]*) -> type(INTLIT) ;
|
|
||||||
INTERP_DECLIT : ('0'|[1-9][0-9]*) '.' [0-9]+ -> type(DECLIT) ;
|
|
||||||
|
|
||||||
// Operators
|
|
||||||
INTERP_PLUS : '+' -> type(PLUS) ;
|
|
||||||
INTERP_MINUS : '-' -> type(MINUS) ;
|
|
||||||
INTERP_ASTERISK : '*' -> type(ASTERISK) ;
|
|
||||||
INTERP_DIVISION : '/' -> type(DIVISION) ;
|
|
||||||
INTERP_PERCENTAGE : '%' -> type(PERCENTAGE) ;
|
|
||||||
INTERP_ASSIGN : '=' -> type(ASSIGN) ;
|
|
||||||
INTERP_LPAREN : '(' -> type(LPAREN) ;
|
|
||||||
INTERP_RPAREN : ')' -> type(RPAREN) ;
|
|
||||||
|
|
||||||
// Identifiers
|
|
||||||
INTERP_ID : [_]*[a-z][A-Za-z0-9_]* -> type(ID);
|
|
||||||
|
|
||||||
INTERP_STRING_OPEN : '"' -> type(STRING_OPEN), pushMode(MODE_IN_STRING);
|
|
||||||
|
|
||||||
INTERP_UNMATCHED : . -> type(UNMATCHED) ;
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package org.openrndr.extra.keyframer.antlr;
|
|||||||
|
|
||||||
options { tokenVocab=KeyLangLexer; }
|
options { tokenVocab=KeyLangLexer; }
|
||||||
|
|
||||||
miniCalcFile : lines=line+ ;
|
keyLangFile : lines=line+ ;
|
||||||
|
|
||||||
line : statement (NEWLINE | EOF) ;
|
line : statement (NEWLINE | EOF) ;
|
||||||
|
|
||||||
|
|||||||
@@ -248,11 +248,11 @@ internal class ExpressionListener(val functions: FunctionExtensions = FunctionEx
|
|||||||
doubleStack.push(node.text.toDouble())
|
doubleStack.push(node.text.toDouble())
|
||||||
}
|
}
|
||||||
if (type == KeyLangParser.ID) {
|
if (type == KeyLangParser.ID) {
|
||||||
|
val name = node.text.replace("`","")
|
||||||
@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 (name) {
|
||||||
"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)
|
||||||
}
|
}
|
||||||
@@ -260,11 +260,11 @@ internal class ExpressionListener(val functions: FunctionExtensions = FunctionEx
|
|||||||
|
|
||||||
IDType.FUNCTION0 -> {
|
IDType.FUNCTION0 -> {
|
||||||
val function: (DoubleArray) -> Double =
|
val function: (DoubleArray) -> Double =
|
||||||
when (val candidate = node.text) {
|
when (name) {
|
||||||
"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[name]?.let { { _: DoubleArray -> it.invoke() } }
|
||||||
?: errorValue(
|
?: errorValue(
|
||||||
"unresolved function: '${candidate}()'"
|
"unresolved function: '${name}()'"
|
||||||
) { _ -> error("this is the error function") }
|
) { _ -> error("this is the error function") }
|
||||||
}
|
}
|
||||||
functionStack.push(function)
|
functionStack.push(function)
|
||||||
@@ -272,7 +272,7 @@ internal class ExpressionListener(val functions: FunctionExtensions = FunctionEx
|
|||||||
|
|
||||||
IDType.FUNCTION1 -> {
|
IDType.FUNCTION1 -> {
|
||||||
val function: (DoubleArray) -> Double =
|
val function: (DoubleArray) -> Double =
|
||||||
when (val candidate = node.text) {
|
when (name) {
|
||||||
"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]) }
|
||||||
@@ -287,48 +287,54 @@ internal class ExpressionListener(val functions: FunctionExtensions = FunctionEx
|
|||||||
"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[name]?.let { { x: DoubleArray -> it.invoke(x[0]) } }
|
||||||
?: errorValue(
|
?: errorValue(
|
||||||
"unresolved function: '${candidate}(x0)'"
|
"unresolved function: '${name}(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 (name) {
|
||||||
"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]) }
|
||||||
"length" -> { x -> Vector2(x[0], x[1]).length }
|
"length" -> { x -> Vector2(x[0], x[1]).length }
|
||||||
else -> functions.functions2[candidate]?.let { { x: DoubleArray -> it.invoke(x[0], x[1]) } }
|
else -> functions.functions2[name]?.let { { x: DoubleArray -> it.invoke(x[0], x[1]) } }
|
||||||
?: errorValue(
|
?: errorValue(
|
||||||
"unresolved function: '${candidate}(x0, x1)'"
|
"unresolved function: '${name}(x0, x1)'"
|
||||||
) { _ -> error("this is the error function") }
|
) { _ -> 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 (name) {
|
||||||
"mix" -> { x -> mix(x[0], x[1], x[2]) }
|
"mix" -> { x -> mix(x[0], x[1], x[2]) }
|
||||||
|
"min" -> { x -> x.minOrNull()!! }
|
||||||
|
"max" -> { x -> x.maxOrNull()!! }
|
||||||
|
"sum" -> { x -> x.sum() }
|
||||||
"smoothstep" -> { x -> smoothstep(x[0], x[1], x[2]) }
|
"smoothstep" -> { x -> smoothstep(x[0], x[1], x[2]) }
|
||||||
"length" -> { x -> Vector3(x[0], x[1], x[2]).length }
|
"length" -> { x -> Vector3(x[0], x[1], x[2]).length }
|
||||||
else -> functions.functions3[candidate]?.let { { x: DoubleArray -> it.invoke(x[0], x[1], x[2]) } }
|
else -> functions.functions3[name]?.let { { x: DoubleArray -> it.invoke(x[0], x[1], x[2]) } }
|
||||||
?: errorValue(
|
?: errorValue(
|
||||||
"unresolved function: '${candidate}(x0, x1, x2)'"
|
"unresolved function: '${name}(x0, x1, x2)'"
|
||||||
) { _ -> error("this is the error function") }
|
) { _ -> 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 (name) {
|
||||||
else -> functions.functions4[candidate]?.let { { x: DoubleArray -> it.invoke(x[0], x[1], x[2], x[3]) } }
|
"min" -> { x -> x.minOrNull()!! }
|
||||||
|
"max" -> { x -> x.maxOrNull()!! }
|
||||||
|
"sum" -> { x -> x.sum() }
|
||||||
|
else -> functions.functions4[name]?.let { { x: DoubleArray -> it.invoke(x[0], x[1], x[2], x[3]) } }
|
||||||
?: errorValue(
|
?: errorValue(
|
||||||
"unresolved function: '${candidate}(x0, x1, x2, x3)'"
|
"unresolved function: '${name}(x0, x1, x2, x3)'"
|
||||||
) { _ -> error("this is the error function") }
|
) { _ -> error("this is the error function") }
|
||||||
}
|
}
|
||||||
functionStack.push(function)
|
functionStack.push(function)
|
||||||
@@ -336,11 +342,14 @@ internal class ExpressionListener(val functions: FunctionExtensions = FunctionEx
|
|||||||
|
|
||||||
IDType.FUNCTION5 -> {
|
IDType.FUNCTION5 -> {
|
||||||
val function: (DoubleArray) -> Double =
|
val function: (DoubleArray) -> Double =
|
||||||
when (val candidate = node.text) {
|
when (name) {
|
||||||
|
"min" -> { x -> x.minOrNull()!! }
|
||||||
|
"max" -> { x -> x.maxOrNull()!! }
|
||||||
|
"sum" -> { x -> x.sum() }
|
||||||
"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[name]?.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: '${name}(x0, x1, x2, x3, x4)'"
|
||||||
) { _ -> error("this is the error function") }
|
) { _ -> error("this is the error function") }
|
||||||
}
|
}
|
||||||
functionStack.push(function)
|
functionStack.push(function)
|
||||||
@@ -374,7 +383,7 @@ fun evaluateExpression(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
val root = parser.miniCalcFile()
|
val root = parser.keyLangFile()
|
||||||
val listener = ExpressionListener(functions)
|
val listener = ExpressionListener(functions)
|
||||||
listener.variables.putAll(variables)
|
listener.variables.putAll(variables)
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -3,55 +3,50 @@ import org.amshove.kluent.`with message`
|
|||||||
import org.amshove.kluent.invoking
|
import org.amshove.kluent.invoking
|
||||||
import org.openrndr.extra.keyframer.ExpressionException
|
import org.openrndr.extra.keyframer.ExpressionException
|
||||||
import org.openrndr.extra.keyframer.evaluateExpression
|
import org.openrndr.extra.keyframer.evaluateExpression
|
||||||
import org.spekframework.spek2.Spek
|
import kotlin.test.Test
|
||||||
import org.spekframework.spek2.style.specification.describe
|
|
||||||
import java.lang.IllegalStateException
|
|
||||||
|
|
||||||
object TestExpressionErrors : Spek({
|
class TestExpressionErrors {
|
||||||
|
|
||||||
describe("an expression with non-sensible writing") {
|
@Test
|
||||||
|
fun `an expression with non-sensible writing`() {
|
||||||
val expression = ")("
|
val expression = ")("
|
||||||
it("should cause an exception to be thrown when evaluated") {
|
invoking {
|
||||||
invoking {
|
evaluateExpression(expression)
|
||||||
evaluateExpression(expression)
|
} `should throw` ExpressionException::class `with message` "parser error in expression: ')('; [line: 1, character: 0 , near: [@0,0:0=')',<21>,1:0] ]"
|
||||||
} `should throw` ExpressionException::class `with message` "parser error in expression: ')('; [line: 1, character: 0 , near: [@0,0:0=')',<21>,1:0] ]"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("an expression with equality instead of assign") {
|
@Test
|
||||||
|
fun `an expression with equality instead of assign`() {
|
||||||
val expression = "a == 5"
|
val expression = "a == 5"
|
||||||
it("should cause an exception to be thrown when evaluated") {
|
invoking {
|
||||||
invoking {
|
evaluateExpression(expression)
|
||||||
evaluateExpression(expression)
|
} `should throw` ExpressionException::class `with message` "parser error in expression: 'a == 5'; [line: 1, character: 3 , near: [@3,3:3='=',<19>,1:3] ]"
|
||||||
} `should throw` ExpressionException::class `with message` "parser error in expression: 'a == 5'; [line: 1, character: 3 , near: [@3,3:3='=',<19>,1:3] ]"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("an expression trying to reassign a number") {
|
@Test
|
||||||
|
fun `an expression trying to reassign a number`() {
|
||||||
val expression = "3 = 5"
|
val expression = "3 = 5"
|
||||||
it("should cause an exception to be thrown when evaluated") {
|
invoking {
|
||||||
invoking {
|
evaluateExpression(expression)
|
||||||
evaluateExpression(expression)
|
} `should throw` ExpressionException::class `with message` "parser error in expression: '3 = 5'; [line: 1, character: 2 , near: [@2,2:2='=',<19>,1:2] ]"
|
||||||
} `should throw` ExpressionException::class `with message` "parser error in expression: '3 = 5'; [line: 1, character: 2 , near: [@2,2:2='=',<19>,1:2] ]"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("an expression that uses non-existing functions") {
|
@Test
|
||||||
|
fun `an expression that uses non-existing functions`() {
|
||||||
val expression = "notExisting(5)"
|
val expression = "notExisting(5)"
|
||||||
it("should cause an exception to be thrown when evaluated") {
|
invoking {
|
||||||
invoking {
|
evaluateExpression(expression)
|
||||||
evaluateExpression(expression)
|
} `should throw` ExpressionException::class `with message` "error in evaluation of 'notExisting(5)': unresolved function: 'notExisting(x0)'"
|
||||||
} `should throw` ExpressionException::class `with message` "error in evaluation of 'notExisting(5)': unresolved function: 'notExisting(x0)'"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("an expression that uses non-existing variables") {
|
@Test
|
||||||
|
fun `an expression that uses non-existing variables`() {
|
||||||
val expression = "notExisting + 4"
|
val expression = "notExisting + 4"
|
||||||
it("should cause an exception to be thrown when evaluated") {
|
invoking {
|
||||||
invoking {
|
evaluateExpression(expression)
|
||||||
evaluateExpression(expression)
|
} `should throw` ExpressionException::class `with message` "error in evaluation of 'notExisting+4': unresolved variable: 'notExisting'"
|
||||||
} `should throw` ExpressionException::class `with message` "error in evaluation of 'notExisting+4': unresolved variable: 'notExisting'"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
|
||||||
|
|||||||
@@ -1,92 +1,138 @@
|
|||||||
|
import org.amshove.kluent.shouldBe
|
||||||
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
import org.amshove.kluent.shouldBeNear
|
import org.amshove.kluent.shouldBeNear
|
||||||
import org.openrndr.extra.keyframer.FunctionExtensions
|
import org.openrndr.extra.keyframer.FunctionExtensions
|
||||||
import org.openrndr.extra.keyframer.evaluateExpression
|
import org.openrndr.extra.keyframer.evaluateExpression
|
||||||
import org.spekframework.spek2.Spek
|
import org.openrndr.math.map
|
||||||
import org.spekframework.spek2.style.specification.describe
|
|
||||||
|
|
||||||
object TestFunctionCall : Spek({
|
import kotlin.test.Test
|
||||||
describe("a function call") {
|
class TestExpressions {
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `a value reference`() {
|
||||||
|
val expression = "someValue"
|
||||||
|
val result = evaluateExpression(expression, variables= mapOf("someValue" to 5.0))
|
||||||
|
result?.shouldBeEqualTo(5.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `a backticked value reference`() {
|
||||||
|
val expression = "`some-value`"
|
||||||
|
val result = evaluateExpression(expression, variables= mapOf("some-value" to 5.0))
|
||||||
|
result?.shouldBeEqualTo(5.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `a function call`() {
|
||||||
val expression = "sqrt(4.0)"
|
val expression = "sqrt(4.0)"
|
||||||
val result = evaluateExpression(expression)
|
val result = evaluateExpression(expression)
|
||||||
result?.shouldBeNear(2.0, 10E-6)
|
result?.shouldBeNear(2.0, 10E-6)
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("two function calls") {
|
@Test
|
||||||
|
fun `a function call with the name in backticks`() {
|
||||||
|
val expression = "`sqrt`(4.0)"
|
||||||
|
val result = evaluateExpression(expression)
|
||||||
|
result?.shouldBeNear(2.0, 10E-6)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `two function calls`() {
|
||||||
val expression = "sqrt(4.0) * sqrt(4.0)"
|
val expression = "sqrt(4.0) * sqrt(4.0)"
|
||||||
val result = evaluateExpression(expression)
|
val result = evaluateExpression(expression)
|
||||||
result?.shouldBeNear(4.0, 10E-6)
|
result?.shouldBeNear(4.0, 10E-6)
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("two argument function call") {
|
@Test
|
||||||
|
fun `two argument max function call`() {
|
||||||
val expression = "max(0.0, 4.0)"
|
val expression = "max(0.0, 4.0)"
|
||||||
val result = evaluateExpression(expression)
|
val result = evaluateExpression(expression)
|
||||||
result?.shouldBeNear(4.0, 10E-6)
|
result?.shouldBeNear(4.0, 10E-6)
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("two argument function call") {
|
@Test
|
||||||
|
fun `two argument min function call`() {
|
||||||
val expression = "min(8.0, 4.0)"
|
val expression = "min(8.0, 4.0)"
|
||||||
val result = evaluateExpression(expression)
|
val result = evaluateExpression(expression)
|
||||||
result?.shouldBeNear(4.0, 10E-6)
|
result?.shouldBeNear(4.0, 10E-6)
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("three argument function call") {
|
@Test
|
||||||
|
fun `three argument function call`() {
|
||||||
val expression = "mix(8.0, 4.0, 0.5)"
|
val expression = "mix(8.0, 4.0, 0.5)"
|
||||||
val result = evaluateExpression(expression)
|
val result = evaluateExpression(expression)
|
||||||
result?.shouldBeNear(6.0, 10E-6)
|
result?.shouldBeNear(6.0, 10E-6)
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("five argument function call") {
|
@Test
|
||||||
|
fun `five argument function call`() {
|
||||||
val expression = "map(0.0, 1.0, 0.0, 8.0, 0.5)"
|
val expression = "map(0.0, 1.0, 0.0, 8.0, 0.5)"
|
||||||
val result = evaluateExpression(expression)
|
val result = evaluateExpression(expression)
|
||||||
result?.shouldBeNear(4.0, 10E-6)
|
result?.shouldBeNear(4.0, 10E-6)
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("two argument function call, where argument order matters") {
|
@Test
|
||||||
|
fun `two argument function call, where argument order matters`() {
|
||||||
val expression = "pow(2.0, 3.0)"
|
val expression = "pow(2.0, 3.0)"
|
||||||
val result = evaluateExpression(expression)
|
val result = evaluateExpression(expression)
|
||||||
result?.shouldBeNear(8.0, 10E-6)
|
result?.shouldBeNear(8.0, 10E-6)
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("nested function call") {
|
@Test
|
||||||
|
fun `nested function call`() {
|
||||||
val expression = "sqrt(min(8.0, 4.0))"
|
val expression = "sqrt(min(8.0, 4.0))"
|
||||||
val result = evaluateExpression(expression)
|
val result = evaluateExpression(expression)
|
||||||
result?.shouldBeNear(2.0, 10E-6)
|
result?.shouldBeNear(2.0, 10E-6)
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("extension function0 call") {
|
@Test
|
||||||
|
fun `extension function0 call`() {
|
||||||
val expression = "extension()"
|
val expression = "extension()"
|
||||||
val result = evaluateExpression(expression, functions = FunctionExtensions(functions0 = mapOf("extension" to { 2.0 })))
|
val result = evaluateExpression(expression, functions = FunctionExtensions(functions0 = mapOf("extension" to { 2.0 })))
|
||||||
result?.shouldBeNear(2.0, 10E-6)
|
result?.shouldBeNear(2.0, 10E-6)
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("extension function1 call") {
|
@Test
|
||||||
|
fun `extension function1 call`(){
|
||||||
val expression = "extension(1.0)"
|
val expression = "extension(1.0)"
|
||||||
val result = evaluateExpression(expression, functions = FunctionExtensions(functions1 = mapOf("extension" to { x -> x * 2.0 })))
|
val result = evaluateExpression(expression, functions = FunctionExtensions(functions1 = mapOf("extension" to { x -> x * 2.0 })))
|
||||||
result?.shouldBeNear(2.0, 10E-6)
|
result?.shouldBeNear(2.0, 10E-6)
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("extension function2 call") {
|
@Test
|
||||||
|
fun `extension function1 call with dashed name in backticks`(){
|
||||||
|
val expression = "`extension-function`(1.0)"
|
||||||
|
val result = evaluateExpression(expression, functions = FunctionExtensions(functions1 = mapOf("extension-function" to { x -> x * 2.0 })))
|
||||||
|
result?.shouldBeNear(2.0, 10E-6)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `extension function2 call`() {
|
||||||
val expression = "extension(1.0, 1.0)"
|
val expression = "extension(1.0, 1.0)"
|
||||||
val result = evaluateExpression(expression, functions = FunctionExtensions(functions2 = mapOf("extension" to { x, y -> x + y })))
|
val result = evaluateExpression(expression, functions = FunctionExtensions(functions2 = mapOf("extension" to { x, y -> x + y })))
|
||||||
result?.shouldBeNear(2.0, 10E-6)
|
result?.shouldBeNear(2.0, 10E-6)
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("extension function3 call") {
|
@Test
|
||||||
|
fun `extension function3 call`() {
|
||||||
val expression = "extension(1.0, 1.0, 1.0)"
|
val expression = "extension(1.0, 1.0, 1.0)"
|
||||||
val result = evaluateExpression(expression, functions = FunctionExtensions(functions3 = mapOf("extension" to { x, y, z -> x + y + z})))
|
val result = evaluateExpression(expression, functions = FunctionExtensions(functions3 = mapOf("extension" to { x, y, z -> x + y + z})))
|
||||||
result?.shouldBeNear(3.0, 10E-6)
|
result?.shouldBeNear(3.0, 10E-6)
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("extension function4 call") {
|
@Test
|
||||||
|
fun `extension function4 call`() {
|
||||||
val expression = "extension(1.0, 1.0, 1.0, 1.0)"
|
val expression = "extension(1.0, 1.0, 1.0, 1.0)"
|
||||||
val result = evaluateExpression(expression, functions = FunctionExtensions(functions4 = mapOf("extension" to { x, y, z, w -> x + y + z + w})))
|
val result = evaluateExpression(expression, functions = FunctionExtensions(functions4 = mapOf("extension" to { x, y, z, w -> x + y + z + w})))
|
||||||
result?.shouldBeNear(4.0, 10E-6)
|
result?.shouldBeNear(4.0, 10E-6)
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("extension function5 call") {
|
@Test
|
||||||
|
fun `extension function5 call`() {
|
||||||
val expression = "extension(1.0, 1.0, 1.0, 1.0, 1.0)"
|
val expression = "extension(1.0, 1.0, 1.0, 1.0, 1.0)"
|
||||||
val result = evaluateExpression(expression, functions = FunctionExtensions(functions5 = mapOf("extension" to { x, y, z, w, u -> x + y + z + w + u})))
|
val result = evaluateExpression(expression, functions = FunctionExtensions(functions5 = mapOf("extension" to { x, y, z, w, u -> x + y + z + w + u})))
|
||||||
result?.shouldBeNear(5.0, 10E-6)
|
result?.shouldBeNear(5.0, 10E-6)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
|
||||||
@@ -2,34 +2,31 @@ import org.amshove.kluent.`should be`
|
|||||||
import org.amshove.kluent.shouldBeNear
|
import org.amshove.kluent.shouldBeNear
|
||||||
import org.openrndr.extra.keyframer.KeyframerChannel
|
import org.openrndr.extra.keyframer.KeyframerChannel
|
||||||
import org.openrndr.extras.easing.Easing
|
import org.openrndr.extras.easing.Easing
|
||||||
import org.spekframework.spek2.Spek
|
|
||||||
import org.spekframework.spek2.style.specification.describe
|
|
||||||
|
|
||||||
object TestKeyframerChannel : Spek({
|
import kotlin.test.Test
|
||||||
|
|
||||||
describe("a keyframer channel without keys") {
|
class TestKeyframerChannel {
|
||||||
|
@Test
|
||||||
|
fun `a keyframer channel without keys`() {
|
||||||
val kfc = KeyframerChannel()
|
val kfc = KeyframerChannel()
|
||||||
it ("should return null when asking for value before first key time") {
|
kfc.value(0.0) `should be` null
|
||||||
kfc.value(0.0) `should be` null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
describe("a keyframer channel with a single key") {
|
|
||||||
|
@Test
|
||||||
|
fun `a keyframer channel with a single key`() {
|
||||||
val kfc = KeyframerChannel()
|
val kfc = KeyframerChannel()
|
||||||
|
|
||||||
kfc.add(0.0, 1.0, Easing.Linear.function)
|
kfc.add(0.0, 1.0, Easing.Linear.function)
|
||||||
kfc.value(0.0)?.shouldBeNear(1.0, 10E-6)
|
kfc.value(0.0)?.shouldBeNear(1.0, 10E-6)
|
||||||
|
kfc.value(-1.0) `should be` null
|
||||||
it ("should return null when asking for value before first key time") {
|
|
||||||
kfc.value(-1.0) `should be` null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
describe("a keyframer channel with two keys") {
|
|
||||||
|
@Test
|
||||||
|
fun `a keyframer channel with two keys`() {
|
||||||
val kfc = KeyframerChannel()
|
val kfc = KeyframerChannel()
|
||||||
kfc.add(0.0, 1.0, Easing.Linear.function)
|
kfc.add(0.0, 1.0, Easing.Linear.function)
|
||||||
kfc.add(1.0, 2.0, Easing.Linear.function)
|
kfc.add(1.0, 2.0, Easing.Linear.function)
|
||||||
kfc.value(0.0)?.shouldBeNear(1.0, 10E-6)
|
kfc.value(0.0)?.shouldBeNear(1.0, 10E-6)
|
||||||
|
kfc.value(-1.0) `should be` null
|
||||||
it ("should return null when asking for value before first key time") {
|
|
||||||
kfc.value(-1.0) `should be` null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
@@ -1,110 +1,96 @@
|
|||||||
//import org.amshove.kluent.`should throw`
|
import org.amshove.kluent.`should throw`
|
||||||
//import org.amshove.kluent.`with message`
|
import org.amshove.kluent.invoking
|
||||||
//import org.amshove.kluent.invoking
|
import kotlin.test.Test
|
||||||
//import org.openrndr.extra.keyframer.ExpressionException
|
import org.openrndr.extra.keyframer.ExpressionException
|
||||||
//import org.openrndr.extra.keyframer.Keyframer
|
import org.openrndr.extra.keyframer.Keyframer
|
||||||
//import org.openrndr.extra.keyframer.KeyframerFormat
|
import org.openrndr.extra.keyframer.KeyframerFormat
|
||||||
//import org.spekframework.spek2.Spek
|
import java.io.File
|
||||||
//import org.spekframework.spek2.style.specification.describe
|
import kotlin.IllegalStateException
|
||||||
//import java.io.File
|
|
||||||
//import kotlin.IllegalStateException
|
private fun testFile(path: String): File {
|
||||||
//
|
val test = File(".")
|
||||||
//
|
return if (test.absolutePath.replace("\\","/").endsWith("orx-keyframer/.")) {
|
||||||
//private fun testFile(path: String) : File {
|
File(path)
|
||||||
// val test = File(".")
|
} else {
|
||||||
// return if (test.absolutePath.endsWith("orx-keyframer/.")) {
|
File("orx-keyframer/$path")
|
||||||
// File(path)
|
}
|
||||||
// } else {
|
}
|
||||||
// File("orx-keyframer/$path")
|
|
||||||
// }
|
class TestKeyframerErrors {
|
||||||
//}
|
class Animation : Keyframer() {
|
||||||
//private fun testName(path: String) : String {
|
val position by Vector2Channel(arrayOf("x", "y"))
|
||||||
// val test = File(".")
|
}
|
||||||
// return (if (test.absolutePath.endsWith("orx-keyframer/.")) {
|
|
||||||
// path
|
@Test
|
||||||
// } else {
|
fun `loading a faulty json`() {
|
||||||
// "orx-keyframer/$path"
|
val animation = Animation()
|
||||||
// }).replace("/", File.separator)
|
val json = """
|
||||||
//}
|
"""
|
||||||
//
|
invoking { animation.loadFromJsonString(json) } `should throw` (IllegalStateException::class)
|
||||||
//
|
}
|
||||||
//object TestKeyframerErrors : Spek({
|
|
||||||
// class Animation : Keyframer() {
|
@Test
|
||||||
// val position by Vector2Channel(arrayOf("x", "y"))
|
fun `loading a non existing json`() {
|
||||||
// }
|
val animation = Animation()
|
||||||
//
|
|
||||||
// describe("loading a faulty json") {
|
invoking { animation.loadFromJson(testFile("this-does-not-exist")) } `should throw` (IllegalArgumentException::class)
|
||||||
// val animation = Animation()
|
|
||||||
// val json = """
|
}
|
||||||
// """
|
@Test
|
||||||
// it("should throw an exception") {
|
fun `loading a json with a faulty time expression (1)`() {
|
||||||
// invoking { animation.loadFromJsonString(json) } `should throw` (IllegalStateException::class)
|
|
||||||
// }
|
File(".").apply {
|
||||||
// }
|
println(this.absolutePath)
|
||||||
//
|
}
|
||||||
// describe("loading a non existing json") {
|
|
||||||
// val animation = Animation()
|
|
||||||
// it("should throw an exception") {
|
val animation = Animation()
|
||||||
// invoking { animation.loadFromJson(testFile("this-does-not-exist")) } `should throw` (IllegalArgumentException::class)
|
|
||||||
// }
|
invoking {
|
||||||
// }
|
animation.loadFromJson(
|
||||||
//
|
testFile("src/test/resources/error-reporting/time-01.json"),
|
||||||
// describe("loading a json with a faulty time expression (1)") {
|
format = KeyframerFormat.SIMPLE
|
||||||
//
|
)
|
||||||
// File(".").apply {
|
} `should throw` ExpressionException::class //`with message` "Error loading from '${testName("src/test/resources/error-reporting/time-01.json")}': error in keys[0].'time': parser error in expression: ')('; [line: 1, character: 0 , near: [@0,0:0=')',<21>,1:0] ]"
|
||||||
// println(this.absolutePath)
|
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
//
|
// Paths.sep
|
||||||
//
|
//
|
||||||
// val animation = Animation()
|
//Expected <Error loading from 'orx-keyframer/src\test\resources\error-reporting\time-01.json': error in keys[0].'time': parser error in expression: ')('; [line: 1, character: 0 , near: [@0,0:0=')',<21>,1:0] ]>,
|
||||||
// it("should throw an exception") {
|
// actual <Error loading from 'orx-keyframer\src\test\resources\error-reporting\time-01.json': error in keys[0].'time': parser error in expression: ')('; [line: 1, character: 0 , near: [@0,0:0=')',<21>,1:0] ]>.
|
||||||
// invoking {
|
|
||||||
// animation.loadFromJson(
|
@Test
|
||||||
// testFile("src/test/resources/error-reporting/time-01.json"),
|
fun `loading a json with a faulty time expression (2) `() {
|
||||||
// format = KeyframerFormat.SIMPLE
|
val animation = Animation()
|
||||||
// )
|
invoking {
|
||||||
// } `should throw` ExpressionException::class //`with message` "Error loading from '${testName("src/test/resources/error-reporting/time-01.json")}': error in keys[0].'time': parser error in expression: ')('; [line: 1, character: 0 , near: [@0,0:0=')',<21>,1:0] ]"
|
animation.loadFromJson(
|
||||||
// }
|
testFile("src/test/resources/error-reporting/time-02.json"),
|
||||||
// }
|
format = KeyframerFormat.SIMPLE
|
||||||
// // Paths.sep
|
)
|
||||||
// //
|
} `should throw` ExpressionException::class //`with message` "Error loading from '${testName("src/test/resources/error-reporting/time-02.json")}': error in keys[0].'time': error in evaluation of 'doesNotExist': unresolved variable: 'doesNotExist'"
|
||||||
// //Expected <Error loading from 'orx-keyframer/src\test\resources\error-reporting\time-01.json': error in keys[0].'time': parser error in expression: ')('; [line: 1, character: 0 , near: [@0,0:0=')',<21>,1:0] ]>,
|
|
||||||
// // actual <Error loading from 'orx-keyframer\src\test\resources\error-reporting\time-01.json': error in keys[0].'time': parser error in expression: ')('; [line: 1, character: 0 , near: [@0,0:0=')',<21>,1:0] ]>.
|
}
|
||||||
// describe("loading a json with a faulty time expression (2) ") {
|
@Test
|
||||||
// val animation = Animation()
|
fun `loading a json with a non-existing easing`() {
|
||||||
// it("should throw an exception") {
|
val animation = Animation()
|
||||||
// invoking {
|
invoking {
|
||||||
// animation.loadFromJson(
|
animation.loadFromJson(
|
||||||
// testFile("src/test/resources/error-reporting/time-02.json"),
|
testFile("src/test/resources/error-reporting/easing.json"),
|
||||||
// format = KeyframerFormat.SIMPLE
|
format = KeyframerFormat.SIMPLE
|
||||||
// )
|
)
|
||||||
// } `should throw` ExpressionException::class //`with message` "Error loading from '${testName("src/test/resources/error-reporting/time-02.json")}': error in keys[0].'time': error in evaluation of 'doesNotExist': unresolved variable: 'doesNotExist'"
|
} `should throw` ExpressionException::class //`with message` "Error loading from '${testName("src/test/resources/error-reporting/easing.json")}': error in keys[0].'easing': unknown easing name 'garble'"
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
//
|
@Test
|
||||||
// describe("loading a json with a non-existing easing") {
|
fun `loading a json with a faulty value (1)`() {
|
||||||
// val animation = Animation()
|
val animation = Animation()
|
||||||
// it("should throw an exception") {
|
|
||||||
// invoking {
|
invoking {
|
||||||
// animation.loadFromJson(
|
animation.loadFromJson(
|
||||||
// testFile("src/test/resources/error-reporting/easing.json"),
|
testFile("src/test/resources/error-reporting/value-01.json"),
|
||||||
// format = KeyframerFormat.SIMPLE
|
format = KeyframerFormat.SIMPLE
|
||||||
// )
|
)
|
||||||
// } `should throw` ExpressionException::class //`with message` "Error loading from '${testName("src/test/resources/error-reporting/easing.json")}': error in keys[0].'easing': unknown easing name 'garble'"
|
} `should throw` ExpressionException::class //`with message` "Error loading from '${testName("src/test/resources/error-reporting/value-01.json")}': error in keys[0].'x': error in evaluation of 'garble': unresolved variable: 'garble'"
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// describe("loading a json with a faulty value (1)") {
|
|
||||||
// val animation = Animation()
|
|
||||||
//
|
|
||||||
// it("should throw an exception") {
|
|
||||||
// invoking {
|
|
||||||
// animation.loadFromJson(
|
|
||||||
// testFile("src/test/resources/error-reporting/value-01.json"),
|
|
||||||
// format = KeyframerFormat.SIMPLE
|
|
||||||
// )
|
|
||||||
// } `should throw` ExpressionException::class //`with message` "Error loading from '${testName("src/test/resources/error-reporting/value-01.json")}': error in keys[0].'x': error in evaluation of 'garble': unresolved variable: 'garble'"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//})
|
|
||||||
|
|||||||
@@ -1,39 +1,53 @@
|
|||||||
import org.amshove.kluent.shouldBeNear
|
import org.amshove.kluent.shouldBeNear
|
||||||
import org.openrndr.extra.keyframer.evaluateExpression
|
import org.openrndr.extra.keyframer.evaluateExpression
|
||||||
import org.spekframework.spek2.Spek
|
import kotlin.test.Test
|
||||||
import org.spekframework.spek2.style.specification.describe
|
|
||||||
|
|
||||||
object TestOperators : Spek({
|
class TestOperators {
|
||||||
describe("an addition operation") {
|
@Test
|
||||||
|
fun `an addition operation`() {
|
||||||
val result = evaluateExpression("1 + 2")
|
val result = evaluateExpression("1 + 2")
|
||||||
result?.shouldBeNear(3.0, 10E-6)
|
result?.shouldBeNear(3.0, 10E-6)
|
||||||
}
|
}
|
||||||
describe("a subtraction operation") {
|
|
||||||
|
@Test
|
||||||
|
fun `a subtraction operation`() {
|
||||||
val result = evaluateExpression("1 - 2")
|
val result = evaluateExpression("1 - 2")
|
||||||
result?.shouldBeNear(-1.0, 10E-6)
|
result?.shouldBeNear(-1.0, 10E-6)
|
||||||
}
|
}
|
||||||
describe("a modulus operation") {
|
|
||||||
|
@Test
|
||||||
|
fun `a modulus operation`() {
|
||||||
val result = evaluateExpression("4 % 2")
|
val result = evaluateExpression("4 % 2")
|
||||||
result?.shouldBeNear(0.0, 10E-6)
|
result?.shouldBeNear(0.0, 10E-6)
|
||||||
}
|
}
|
||||||
describe("a multiplication operation") {
|
|
||||||
|
@Test
|
||||||
|
fun `a multiplication operation`() {
|
||||||
val result = evaluateExpression("4 * 2")
|
val result = evaluateExpression("4 * 2")
|
||||||
result?.shouldBeNear(8.0, 10E-6)
|
result?.shouldBeNear(8.0, 10E-6)
|
||||||
}
|
}
|
||||||
describe("a division operation") {
|
|
||||||
|
@Test
|
||||||
|
fun `a division operation`() {
|
||||||
val result = evaluateExpression("4 / 2")
|
val result = evaluateExpression("4 / 2")
|
||||||
result?.shouldBeNear(2.0, 10E-6)
|
result?.shouldBeNear(2.0, 10E-6)
|
||||||
}
|
}
|
||||||
describe("a multiplication/addition operation") {
|
|
||||||
|
@Test
|
||||||
|
fun `a multiplication and addition operation`() {
|
||||||
val result = evaluateExpression("4 * 2 + 1")
|
val result = evaluateExpression("4 * 2 + 1")
|
||||||
result?.shouldBeNear(9.0, 10E-6)
|
result?.shouldBeNear(9.0, 10E-6)
|
||||||
}
|
}
|
||||||
describe("an addition/multiplication") {
|
|
||||||
|
@Test
|
||||||
|
fun `an addition and multiplication`() {
|
||||||
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") {
|
|
||||||
|
@Test
|
||||||
|
fun `unary minus`() {
|
||||||
val result = evaluateExpression("-4.0")
|
val result = evaluateExpression("-4.0")
|
||||||
result?.shouldBeNear(-4.0, 10E-6)
|
result?.shouldBeNear(-4.0, 10E-6)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user