diff --git a/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Math.kt b/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Math.kt index 090e2443..754ff26e 100644 --- a/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Math.kt +++ b/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Math.kt @@ -3,6 +3,8 @@ package eu.iamgio.quarkdown.stdlib import eu.iamgio.quarkdown.function.reflect.annotation.Name import eu.iamgio.quarkdown.function.value.BooleanValue import eu.iamgio.quarkdown.function.value.NumberValue +import eu.iamgio.quarkdown.function.value.ObjectValue +import eu.iamgio.quarkdown.function.value.data.Range import kotlin.math.PI import kotlin.math.pow @@ -22,6 +24,7 @@ val Math: Module = ::cos, ::tan, ::isEven, + ::range, ) // Basic operations @@ -105,3 +108,22 @@ fun tan(x: Number) = NumberValue(kotlin.math.tan(x.toFloat())) */ @Name("iseven") fun isEven(x: Number) = BooleanValue(x.toInt() % 2 == 0) + +/** + * Creates a range of numbers, which can also be iterated through. + * The behavior of an open range is delegated to the consumer. + * For instance, using a left-open range with [forEach] will make the loop start from 1. + * The difference between this function and the built-in `..` operator is that the latter + * does not allow for dynamic evaluation, hence both ends must be literals. + * This function allows evaluating ends dynamically: for instance, `.range from:{1} to:{.sum {1} {2}}`. + * Floating-point numbers are truncated to integers. + * @property start start of the range (inclusive). If `null`, the range is infinite on the left end + * @property end end of the range (inclusive). If `null`, the range is infinite on the right end. [end] > [start] + */ +fun range( + @Name("from") start: Number? = null, + @Name("to") end: Number? = null, +): ObjectValue = + ObjectValue( + Range(start?.toInt(), end?.toInt()), + ) diff --git a/test/src/test/kotlin/eu/iamgio/quarkdown/test/FullPipelineTest.kt b/test/src/test/kotlin/eu/iamgio/quarkdown/test/FullPipelineTest.kt index c569908c..1306e273 100644 --- a/test/src/test/kotlin/eu/iamgio/quarkdown/test/FullPipelineTest.kt +++ b/test/src/test/kotlin/eu/iamgio/quarkdown/test/FullPipelineTest.kt @@ -610,6 +610,14 @@ class FullPipelineTest { assertEquals("

N: 1

N: 2

N: 3

", it) } + execute(".foreach {2..4}\n **N:** .1") { + assertEquals("

N: 2

N: 3

N: 4

", it) + } + + execute(".foreach {.range from:{2} to:{4}}\n **N:** .1") { + assertEquals("

N: 2

N: 3

N: 4

", it) + } + execute( """ ## Title @@ -623,7 +631,7 @@ class FullPipelineTest { execute( """ - .foreach {..2} + .foreach {.range to:{.sum {1} {1}}} ## Hello .1 .foreach {..1} **Hi**! @@ -646,7 +654,7 @@ class FullPipelineTest { execute( """ .foreach {..2} - .foreach {..2} + .foreach {.range to:{2}} .foreach {..2} ## Title 2 # Title 1