[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.collections.push
|
||||||
import org.openrndr.color.ColorRGBa
|
import org.openrndr.color.ColorRGBa
|
||||||
import org.openrndr.extra.expressions.parser.KeyLangLexer
|
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.KeyLangParser
|
||||||
import org.openrndr.extra.expressions.parser.KeyLangParserBaseListener
|
import org.openrndr.extra.expressions.parser.KeyLangParserBaseListener
|
||||||
import org.openrndr.extra.noise.uniform
|
import org.openrndr.extra.noise.uniform
|
||||||
@@ -94,12 +98,53 @@ abstract class TypedExpressionListenerBase(
|
|||||||
s.reset()
|
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) {
|
override fun exitListLiteral(ctx: KeyLangParser.ListLiteralContext) {
|
||||||
val s = state
|
val s = state
|
||||||
if (s.inFunctionLiteral > 0) {
|
if (s.inFunctionLiteral > 0) {
|
||||||
return
|
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())
|
s.valueStack.push(list.reversed())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +158,7 @@ abstract class TypedExpressionListenerBase(
|
|||||||
|
|
||||||
val value = when (listValue) {
|
val value = when (listValue) {
|
||||||
is List<*> -> listValue[index] ?: error("got null")
|
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'")
|
else -> error("can't index on '$listValue'")
|
||||||
}
|
}
|
||||||
s.valueStack.push(value)
|
s.valueStack.push(value)
|
||||||
@@ -545,10 +590,10 @@ abstract class TypedExpressionListenerBase(
|
|||||||
if (s.inFunctionLiteral > 0) {
|
if (s.inFunctionLiteral > 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.idTypeStack.push(IDType.FUNCTION2)
|
s.idTypeStack.push(IDType.FUNCTION2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun exitFunctionCall2Expression(ctx: KeyLangParser.FunctionCall2ExpressionContext) {
|
override fun exitFunctionCall2Expression(ctx: KeyLangParser.FunctionCall2ExpressionContext) {
|
||||||
val s = state
|
val s = state
|
||||||
if (s.inFunctionLiteral > 0) {
|
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) ;
|
WS : [\t ]+ -> channel(WHITESPACE) ;
|
||||||
|
|
||||||
|
|
||||||
|
RANGE_INCLUSIVE : '..' ;
|
||||||
|
RANGE_EXCLUSIVE_UNTIL : 'until' ;
|
||||||
|
RANGE_EXCLUSIVE : '..<' ;
|
||||||
|
RANGE_DOWNTO : 'downTo' ;
|
||||||
|
|
||||||
|
STEP : 'step' ;
|
||||||
|
|
||||||
|
|
||||||
// Identifiers
|
// Identifiers
|
||||||
ID : [$_]*[a-zA-Z][A-Za-z0-9_]* | '`'[$_]*[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_]* ;
|
||||||
|
|||||||
@@ -8,13 +8,19 @@ keyLangFile : lines=line+ ;
|
|||||||
line : statement (NEWLINE | EOF) ;
|
line : statement (NEWLINE | EOF) ;
|
||||||
|
|
||||||
statement :
|
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;
|
lambda: LCURLY (ID ( COMMA ID )* ARROW )? expression RCURLY # functionLiteral;
|
||||||
|
|
||||||
expression : INTLIT # intLiteral
|
expression : INTLIT # intLiteral
|
||||||
| DECLIT # decimalLiteral
|
| DECLIT # decimalLiteral
|
||||||
| LBRACKET (expression ( COMMA expression )*)? RBRACKET # listLiteral
|
| LBRACKET (expressionRoot ( COMMA expressionRoot )*)? RBRACKET # listLiteral
|
||||||
| expression DOT ID lambda # memberFunctionCall0LambdaExpression
|
| expression DOT ID lambda # memberFunctionCall0LambdaExpression
|
||||||
| lambda # lambdaExpression
|
| lambda # lambdaExpression
|
||||||
| expression DOT ID LPAREN RPAREN # memberFunctionCall0Expression
|
| expression DOT ID LPAREN RPAREN # memberFunctionCall0Expression
|
||||||
@@ -32,7 +38,7 @@ expression : INTLIT # int
|
|||||||
| ID # valueReference
|
| ID # valueReference
|
||||||
| STRING_OPEN (parts+=stringLiteralContent)* STRING_CLOSE # stringLiteral
|
| STRING_OPEN (parts+=stringLiteralContent)* STRING_CLOSE # stringLiteral
|
||||||
| expression DOT ID # propReference
|
| expression DOT ID # propReference
|
||||||
| LPAREN expression RPAREN # parenExpression
|
| LPAREN expressionRoot RPAREN # parenExpression
|
||||||
| MINUS expression # minusExpression
|
| MINUS expression # minusExpression
|
||||||
| NOT expression # negateExpression
|
| NOT expression # negateExpression
|
||||||
| expression operator=(DIVISION|ASTERISK|PERCENTAGE) expression # binaryOperation1
|
| expression operator=(DIVISION|ASTERISK|PERCENTAGE) expression # binaryOperation1
|
||||||
|
|||||||
Reference in New Issue
Block a user