diff --git a/core/src/main/kotlin/eu/iamgio/quarkdown/function/value/ValueFactory.kt b/core/src/main/kotlin/eu/iamgio/quarkdown/function/value/ValueFactory.kt index 3414115f..bc42b9f5 100644 --- a/core/src/main/kotlin/eu/iamgio/quarkdown/function/value/ValueFactory.kt +++ b/core/src/main/kotlin/eu/iamgio/quarkdown/function/value/ValueFactory.kt @@ -82,8 +82,8 @@ object ValueFactory { // 2..5 maps to Range(1, 4) val range = Range( - start?.toIntOrNull()?.minus(1), - end?.toIntOrNull()?.minus(1), + start?.toIntOrNull(), + end?.toIntOrNull(), ) return ObjectValue(range) diff --git a/core/src/main/kotlin/eu/iamgio/quarkdown/function/value/data/Range.kt b/core/src/main/kotlin/eu/iamgio/quarkdown/function/value/data/Range.kt index 6ce7b3af..3a5be20f 100644 --- a/core/src/main/kotlin/eu/iamgio/quarkdown/function/value/data/Range.kt +++ b/core/src/main/kotlin/eu/iamgio/quarkdown/function/value/data/Range.kt @@ -4,6 +4,13 @@ import eu.iamgio.quarkdown.function.value.GeneralCollectionValue import eu.iamgio.quarkdown.function.value.IterableValue import eu.iamgio.quarkdown.function.value.NumberValue +/** + * Default lower bound index of a [Range] whose `start` value is `null`, when converted to a collection. + * This can also be seen as `N` in _arrays start from `N`_. + * @see Range.toCollection + */ +private const val DEFAULT_LOWER_BOUND_INDEX = 1 + /** * Represents a range of numbers, which can also be iterated through. * @property start start of the range (inclusive). If `null`, the range is infinite on the left end @@ -21,16 +28,19 @@ data class Range(val start: Int?, val end: Int?) : Iterable { * @param upperBound upper bound of the range, in case [end] is `null` * @return this range as an [IntRange], with [start] and [end] replaced by [lowerBound] and [upperBound] respectively if they are `null` */ - fun toIntRange( + private fun toIntRange( lowerBound: Int, upperBound: Int, ) = IntRange(start ?: lowerBound, end ?: upperBound) + /** + * @return this range as an iterable collection value + */ fun toCollection(): IterableValue = GeneralCollectionValue(this) /** * @return a new iterator for this range. - * If this is open on the left end, it starts from 0. + * If this is open on the left end, it starts from [DEFAULT_LOWER_BOUND_INDEX]. * @throws IllegalStateException if [end] is `null` */ override fun iterator(): Iterator { @@ -38,7 +48,7 @@ data class Range(val start: Int?, val end: Int?) : Iterable { throw IllegalStateException("Cannot iterate through an endless range.") } - return toIntRange(lowerBound = 0, upperBound = end).asSequence() + return toIntRange(lowerBound = DEFAULT_LOWER_BOUND_INDEX, upperBound = end).asSequence() .map(::NumberValue) .iterator() } @@ -55,9 +65,9 @@ data class Range(val start: Int?, val end: Int?) : Iterable { /** * @param range range to get the sublist from - * @return sublist of [this] list, starting from [range]'s start and ending at [range]'s end (both inclusive). + * @return sublist of [this] list, starting from [range]'s start (starting from 1) and ending at [range]'s end (both inclusive). * If any of the bounds is `null`, it is replaced by the list's start or end index respectively */ fun List.subList(range: Range): List { - return subList(range.start ?: 0, range.end?.plus(1) ?: this.size) + return subList(range.start?.minus(DEFAULT_LOWER_BOUND_INDEX) ?: 0, range.end ?: this.size) } diff --git a/core/src/test/kotlin/eu/iamgio/quarkdown/ValueFactoryTest.kt b/core/src/test/kotlin/eu/iamgio/quarkdown/ValueFactoryTest.kt index a0438cc7..5947adb1 100644 --- a/core/src/test/kotlin/eu/iamgio/quarkdown/ValueFactoryTest.kt +++ b/core/src/test/kotlin/eu/iamgio/quarkdown/ValueFactoryTest.kt @@ -48,10 +48,9 @@ class ValueFactoryTest { @Test fun range() { - // TODO x..y should actually map to Range(x, y) instead of Range(x - 1, y - 1) - assertEquals(Range(0, 8), ValueFactory.range("1..9").unwrappedValue) - assertEquals(Range(null, 10), ValueFactory.range("..11").unwrappedValue) - assertEquals(Range(13, null), ValueFactory.range("14..").unwrappedValue) + assertEquals(Range(1, 9), ValueFactory.range("1..9").unwrappedValue) + assertEquals(Range(null, 11), ValueFactory.range("..11").unwrappedValue) + assertEquals(Range(14, null), ValueFactory.range("14..").unwrappedValue) assertEquals(Range(null, null), ValueFactory.range("..").unwrappedValue) } diff --git a/stdlib/src/test/kotlin/eu/iamgio/quarkdown/stdlib/DataTest.kt b/stdlib/src/test/kotlin/eu/iamgio/quarkdown/stdlib/DataTest.kt index 2f50372c..fe190bb5 100644 --- a/stdlib/src/test/kotlin/eu/iamgio/quarkdown/stdlib/DataTest.kt +++ b/stdlib/src/test/kotlin/eu/iamgio/quarkdown/stdlib/DataTest.kt @@ -41,17 +41,17 @@ class DataTest { assertEquals( "Line 2${LINE_SEPARATOR}${LINE_SEPARATOR}Line 4", - fileContent(context, path, Range(1, 3)).unwrappedValue, + fileContent(context, path, Range(2, 4)).unwrappedValue, ) assertEquals( "Line 1${LINE_SEPARATOR}Line 2", - fileContent(context, path, Range(null, 1)).unwrappedValue, + fileContent(context, path, Range(null, 2)).unwrappedValue, ) assertEquals( "Line 4${LINE_SEPARATOR}Line 5", - fileContent(context, path, Range(3, null)).unwrappedValue, + fileContent(context, path, Range(4, null)).unwrappedValue, ) } diff --git a/stdlib/src/test/kotlin/eu/iamgio/quarkdown/stdlib/FlowTest.kt b/stdlib/src/test/kotlin/eu/iamgio/quarkdown/stdlib/FlowTest.kt index 21252ed7..0cf6e8f5 100644 --- a/stdlib/src/test/kotlin/eu/iamgio/quarkdown/stdlib/FlowTest.kt +++ b/stdlib/src/test/kotlin/eu/iamgio/quarkdown/stdlib/FlowTest.kt @@ -179,10 +179,10 @@ class FlowTest { assertEquals( listOf( - DynamicValue("N: 0"), DynamicValue("N: 1"), DynamicValue("N: 2"), DynamicValue("N: 3"), + DynamicValue("N: 4"), ), loop3.unwrappedValue, ) 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 1a0f88e9..07faf7ef 100644 --- a/test/src/test/kotlin/eu/iamgio/quarkdown/test/FullPipelineTest.kt +++ b/test/src/test/kotlin/eu/iamgio/quarkdown/test/FullPipelineTest.kt @@ -174,7 +174,7 @@ class FullPipelineTest { } execute(".foreach {..3}\n **N:** .1") { - assertEquals("

N: 0

N: 1

N: 2

", it) + assertEquals("

N: 1

N: 2

N: 3

", it) } execute( @@ -185,7 +185,7 @@ class FullPipelineTest { Hi .n """.trimIndent(), ) { - assertEquals("

Title

Hi 0

Hi 1

", it) + assertEquals("

Title

Hi 1

Hi 2

", it) } execute( @@ -368,7 +368,7 @@ class FullPipelineTest { .var {t2} {1} .table - .foreach {..4} + .foreach {0..4} | $ F_{.1} $ | |:-------------:| | .t1 | @@ -389,7 +389,7 @@ class FullPipelineTest { | .t1 | .table - .foreach {..4} + .foreach {0..4} .tablecolumn {.1} .var {tmp} {.sum {.t1} {.t2}} .var {t1} {.t2} @@ -411,7 +411,7 @@ class FullPipelineTest { } .table - .foreach {..4} + .foreach {0..4} | $ F_{.1} $ | |:------------:| | .fib {.1} | @@ -422,10 +422,15 @@ class FullPipelineTest { "__QD_INLINE_MATH__\$F_{0}\$__QD_INLINE_MATH__" + "__QD_INLINE_MATH__\$F_{1}\$__QD_INLINE_MATH__" + "__QD_INLINE_MATH__\$F_{2}\$__QD_INLINE_MATH__" + - "__QD_INLINE_MATH__\$F_{3}\$__QD_INLINE_MATH__" + - "01" + + "__QD_INLINE_MATH__\$F_{3}\$__QD_INLINE_MATH__" + + "__QD_INLINE_MATH__\$F_{4}\$__QD_INLINE_MATH__" + + "" + + "0" + + "1" + "1" + - "2" + "2" + + "3" + + "" execute(iterative) { assertEquals(out, it) @@ -473,10 +478,10 @@ class FullPipelineTest { """.trimIndent(), ) { assertEquals( - "

Hello, world!

0__QD_INLINE_MATH__$\\times\$__QD_INLINE_MATH__0 is 0

End

" + - "

Hello, world!

1__QD_INLINE_MATH__$\\times\$__QD_INLINE_MATH__1 is 1

End

" + + "

Hello, world!

1__QD_INLINE_MATH__$\\times\$__QD_INLINE_MATH__1 is 1

End

" + "

Hello, world!

2__QD_INLINE_MATH__$\\times\$__QD_INLINE_MATH__2 is 4

End

" + - "

Hello, world!

3__QD_INLINE_MATH__$\\times\$__QD_INLINE_MATH__3 is 9

End

", + "

Hello, world!

3__QD_INLINE_MATH__$\\times\$__QD_INLINE_MATH__3 is 9

End

" + + "

Hello, world!

4__QD_INLINE_MATH__$\\times\$__QD_INLINE_MATH__4 is 16

End

", it, ) }