[orx-expression-evaluator-typed] Add range operators
This commit is contained in:
@@ -7,6 +7,10 @@ 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.KeyLangLexer.Tokens.RANGE_DOWNTO
|
||||
import org.openrndr.extra.expressions.parser.KeyLangLexer.Tokens.RANGE_EXCLUSIVE
|
||||
import org.openrndr.extra.expressions.parser.KeyLangLexer.Tokens.RANGE_EXCLUSIVE_UNTIL
|
||||
import org.openrndr.extra.expressions.parser.KeyLangLexer.Tokens.RANGE_INCLUSIVE
|
||||
import org.openrndr.extra.expressions.parser.KeyLangParser
|
||||
import org.openrndr.extra.expressions.parser.KeyLangParserBaseListener
|
||||
import org.openrndr.extra.noise.uniform
|
||||
@@ -94,12 +98,53 @@ abstract class TypedExpressionListenerBase(
|
||||
s.reset()
|
||||
}
|
||||
|
||||
override fun exitRangeExpression(ctx: KeyLangParser.RangeExpressionContext) {
|
||||
val s = state
|
||||
if (s.inFunctionLiteral > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
val step: Any?
|
||||
if (ctx.step != null) {
|
||||
step = s.valueStack.pop()
|
||||
} else {
|
||||
step = null
|
||||
}
|
||||
|
||||
val right = s.valueStack.pop()
|
||||
val left = s.valueStack.pop()
|
||||
|
||||
|
||||
val lower = (left as Double).toInt()
|
||||
val upper = (right as Double).toInt()
|
||||
val list = if (step == null) {
|
||||
when (ctx.operator?.type) {
|
||||
RANGE_INCLUSIVE -> (lower..upper).toList().map { it.toDouble() }
|
||||
RANGE_EXCLUSIVE -> (lower..<upper).toList().map { it.toDouble() }
|
||||
RANGE_EXCLUSIVE_UNTIL -> (lower until upper).toList().map { it.toDouble() }
|
||||
RANGE_DOWNTO -> (lower downTo upper).toList().map { it.toDouble() }
|
||||
else -> error("unsupported operator: '${ctx.operator?.text}'")
|
||||
}
|
||||
} else {
|
||||
val stepSize = (step as Double).toInt()
|
||||
when (ctx.operator?.type) {
|
||||
RANGE_INCLUSIVE -> (lower..upper step stepSize).toList().map { it.toDouble() }
|
||||
RANGE_EXCLUSIVE -> (lower..<upper step stepSize).toList().map { it.toDouble() }
|
||||
RANGE_EXCLUSIVE_UNTIL -> (lower until upper step stepSize).toList().map { it.toDouble() }
|
||||
RANGE_DOWNTO -> (lower downTo upper step stepSize).toList().map { it.toDouble() }
|
||||
else -> error("unsupported operator: '${ctx.operator?.text}'")
|
||||
}
|
||||
}
|
||||
s.valueStack.push(list)
|
||||
|
||||
}
|
||||
|
||||
override fun exitListLiteral(ctx: KeyLangParser.ListLiteralContext) {
|
||||
val s = state
|
||||
if (s.inFunctionLiteral > 0) {
|
||||
return
|
||||
}
|
||||
val list = (0 until ctx.expression().size).map { s.valueStack.pop() }
|
||||
val list = (0 until ctx.expressionRoot().size).map { s.valueStack.pop() }
|
||||
s.valueStack.push(list.reversed())
|
||||
}
|
||||
|
||||
@@ -113,7 +158,7 @@ abstract class TypedExpressionListenerBase(
|
||||
|
||||
val value = when (listValue) {
|
||||
is List<*> -> listValue[index] ?: error("got null")
|
||||
is Function<*> -> (listValue as (Int)->Any)(index)
|
||||
is Function<*> -> (listValue as (Int) -> Any)(index)
|
||||
else -> error("can't index on '$listValue'")
|
||||
}
|
||||
s.valueStack.push(value)
|
||||
@@ -545,10 +590,10 @@ abstract class TypedExpressionListenerBase(
|
||||
if (s.inFunctionLiteral > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
s.idTypeStack.push(IDType.FUNCTION2)
|
||||
}
|
||||
|
||||
|
||||
override fun exitFunctionCall2Expression(ctx: KeyLangParser.FunctionCall2ExpressionContext) {
|
||||
val s = state
|
||||
if (s.inFunctionLiteral > 0) {
|
||||
@@ -819,7 +864,11 @@ abstract class TypedExpressionListenerBase(
|
||||
}
|
||||
|
||||
|
||||
else -> error("receiver for '$name' '${receiver.toString().take(30)}' ${receiver::class} not supported")
|
||||
else -> error(
|
||||
"receiver for '$name' '${
|
||||
receiver.toString().take(30)
|
||||
}' ${receiver::class} not supported"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package typed
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.openrndr.extra.expressions.typed.evaluateTypedExpression
|
||||
import org.openrndr.math.Vector2
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class TestListLiteralExpression {
|
||||
|
||||
@Test
|
||||
fun testSimpleList() {
|
||||
val r = evaluateTypedExpression("[0, 1, 2]")
|
||||
assertTrue(r is List<*>)
|
||||
assertEquals(3, r.size )
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRangesList() {
|
||||
val r = evaluateTypedExpression("[0..1, 1..2, 2..3]")
|
||||
assertTrue(r is List<*>)
|
||||
assertEquals(3, r.size )
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package typed
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.openrndr.extra.expressions.typed.evaluateTypedExpression
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class TestRangeExpression {
|
||||
@Test
|
||||
fun testRangeInclusive() {
|
||||
val r = evaluateTypedExpression("(0..10)")
|
||||
assertTrue(r is List<*>)
|
||||
assertEquals(11, r.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRangeDownTo() {
|
||||
val r = evaluateTypedExpression("(10 downTo 0)")
|
||||
assertTrue(r is List<*>)
|
||||
assertEquals(11, r.size)
|
||||
assertEquals(0.0, r.last())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRangeExclusive() {
|
||||
val r = evaluateTypedExpression("(0..<10)")
|
||||
assertTrue(r is List<*>)
|
||||
assertEquals(10, r.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRangeInclusivePrecedenceRight() {
|
||||
val r = evaluateTypedExpression("(0..10+2)")
|
||||
assertTrue(r is List<*>)
|
||||
assertEquals(13, r.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRangeInclusivePrecedenceLeft() {
|
||||
val r = evaluateTypedExpression("1+2..10")
|
||||
assertTrue(r is List<*>)
|
||||
assertEquals(8, r.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRangeInclusivePrecedenceLeftRight() {
|
||||
val r = evaluateTypedExpression("1+2..10+2")
|
||||
assertTrue(r is List<*>)
|
||||
assertEquals(10, r.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRangeUntilPrecedenceLeftRight() {
|
||||
val r = evaluateTypedExpression("1+2 until 10+2")
|
||||
assertTrue(r is List<*>)
|
||||
assertEquals(9, r.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRangeUntilPrecedenceLeftRightMap() {
|
||||
val r = evaluateTypedExpression("(1+2 until 10+2).map { x -> x * 2 }")
|
||||
assertTrue(r is List<*>)
|
||||
assertEquals(9, r.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRangeExclusiveStep() {
|
||||
val r = evaluateTypedExpression("(0..10 step 2)")
|
||||
assertTrue(r is List<*>)
|
||||
assertEquals(6, r.size)
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,14 @@ NEWLINE : '\r\n' | '\r' | '\n' ;
|
||||
WS : [\t ]+ -> channel(WHITESPACE) ;
|
||||
|
||||
|
||||
RANGE_INCLUSIVE : '..' ;
|
||||
RANGE_EXCLUSIVE_UNTIL : 'until' ;
|
||||
RANGE_EXCLUSIVE : '..<' ;
|
||||
RANGE_DOWNTO : 'downTo' ;
|
||||
|
||||
STEP : 'step' ;
|
||||
|
||||
|
||||
// Identifiers
|
||||
ID : [$_]*[a-zA-Z][A-Za-z0-9_]* | '`'[$_]*[A-Za-z0-9_-]*'`';
|
||||
FUNCTION_ID : [$_]*[a-z][A-Za-z0-9_]* ;
|
||||
|
||||
@@ -8,13 +8,19 @@ keyLangFile : lines=line+ ;
|
||||
line : statement (NEWLINE | EOF) ;
|
||||
|
||||
statement :
|
||||
expression # expressionStatement ;
|
||||
expressionRoot # expressionStatement ;
|
||||
|
||||
rangeExpression: expression operator=(RANGE_INCLUSIVE|RANGE_EXCLUSIVE|RANGE_EXCLUSIVE_UNTIL|RANGE_DOWNTO) expression (step=STEP expression)?;
|
||||
|
||||
expressionRoot: rangeExpression
|
||||
| expression
|
||||
;
|
||||
|
||||
lambda: LCURLY (ID ( COMMA ID )* ARROW )? expression RCURLY # functionLiteral;
|
||||
|
||||
expression : INTLIT # intLiteral
|
||||
| DECLIT # decimalLiteral
|
||||
| LBRACKET (expression ( COMMA expression )*)? RBRACKET # listLiteral
|
||||
| LBRACKET (expressionRoot ( COMMA expressionRoot )*)? RBRACKET # listLiteral
|
||||
| expression DOT ID lambda # memberFunctionCall0LambdaExpression
|
||||
| lambda # lambdaExpression
|
||||
| expression DOT ID LPAREN RPAREN # memberFunctionCall0Expression
|
||||
@@ -32,7 +38,7 @@ expression : INTLIT # int
|
||||
| ID # valueReference
|
||||
| STRING_OPEN (parts+=stringLiteralContent)* STRING_CLOSE # stringLiteral
|
||||
| expression DOT ID # propReference
|
||||
| LPAREN expression RPAREN # parenExpression
|
||||
| LPAREN expressionRoot RPAREN # parenExpression
|
||||
| MINUS expression # minusExpression
|
||||
| NOT expression # negateExpression
|
||||
| expression operator=(DIVISION|ASTERISK|PERCENTAGE) expression # binaryOperation1
|
||||
|
||||
Reference in New Issue
Block a user