You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2021/04/12 09:23:17 UTC

[groovy] branch master updated (8980443 -> e642159)

This is an automated email from the ASF dual-hosted git repository.

paulk pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git.


    from 8980443  minor refactor: formatting/imports and adjust ports to experiment with flakey CI RMI test
     new e3ba8e6  GROOVY-9649: - Added left and full exclusive patterns to parser - Added exclusiveLeft and exclusiveRight class variables to RangeExpression
     new 75b7133  GROOVY-9649: Rework range creation to also allow left- and full-open ranges
     new 5983f66  GROOVY-9649: Added tests for left and full-exclusive range operator
     new acf9423  GROOVY-9649: left open and full open range support for IntRange
     new f571a25  GROOVY-9649: Started implementing left- and full-open range support for NumberRange.
     new 16d12fc  GROOVY-9649: Fix subListBorders call in IntRange
     new f6644ff  GROOVY-9649: Added more test cases for empty ranges
     new f71a1b7  GROOVY-9649: Make createRange aware of left side exclusivity
     new f786bc0  GROOVY-9649: Finalize NumberRange functionalities for left- and full-open ranges.
     new e620177  GROOVY-9649: Fixed NumberRange.get not throwing at certain conditions
     new e89b552  GROOVY-9649: Added test cases for left- and full-exclusive IntRanges
     new 1a8f109  GROOVY-9649: Fix IntRange size being negative on some occasions
     new cca4515  GROOVY-9649: Make createRange create EmptyRanges when from != to
     new 8d76395  GROOVY-9649: Fixed getAt for primitive arrays by introducing a new helper method, added few test cases for getAt
     new 0a8a8a7  GROOVY-9649: Fixed NumberRange size calculation with full-exclusive ranges where from equals to.
     new f8fcd96  GROOVY-9649: Added documentation for left-open and full-open ranges
     new c1d2c7f  GROOVY-9649: Amended documentation for IntRange
     new 4cbaec4  GROOVY-9649: Sonar refactoring
     new f364b33  GROOVY-9649: Add test case for NumberRange size edge cases
     new 7dac3e7  GROOVY-9649: Sonar refactoring
     new 8aa8cba  GROOVY-9649: Sonar refactoring
     new 4d84b69  GROOVY-9649: Minor refactor: remove redundant if clause
     new 8e54d6c  GROOVY-9649: Fix bug in IntRange.equals
     new 06b56f2  GROOVY-9649: Add test cases for IntRange.equals
     new e642159  GROOVY-9649: Refactored RangeExpression

The 25 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/antlr/GroovyLexer.g4                           |  36 ++---
 src/antlr/GroovyParser.g4                          |   4 +-
 src/main/java/groovy/lang/IntRange.java            | 124 +++++++++++-----
 src/main/java/groovy/lang/NumberRange.java         | 105 ++++++++++----
 .../apache/groovy/parser/antlr4/AstBuilder.java    |   8 +-
 .../codehaus/groovy/ast/expr/RangeExpression.java  |  32 ++++-
 .../groovy/classgen/AsmClassGenerator.java         |   9 +-
 .../codehaus/groovy/classgen/asm/MethodCaller.java |  29 +++-
 .../groovy/runtime/DefaultGroovyMethods.java       |  16 +--
 .../runtime/DefaultGroovyMethodsSupport.java       |  28 ++++
 .../org/codehaus/groovy/runtime/InvokerHelper.java |   9 +-
 .../groovy/runtime/ScriptBytecodeAdapter.java      |  54 +++++--
 src/spec/doc/_working-with-collections.adoc        |   6 +
 src/spec/doc/core-operators.adoc                   |   6 +-
 src/spec/doc/core-semantics.adoc                   |   2 +-
 src/spec/test/OperatorsTest.groovy                 |   8 +-
 src/test/groovy/GroovyMethodsTest.groovy           |   3 +
 src/test/groovy/ListTest.groovy                    |  16 ++-
 src/test/groovy/RangeTest.groovy                   | 124 ++++++++++++++++
 src/test/groovy/lang/IntRangeTest.groovy           | 158 +++++++++++++++++----
 src/test/groovy/lang/NumberRangeTest.groovy        |  16 ++-
 .../powerassert/AssertionRenderingTest.groovy      |  20 +++
 .../runtime/powerassert/EvaluationTest.groovy      |   2 +
 .../completion/antlr4/ReflectionCompleter.groovy   |   8 +-
 24 files changed, 659 insertions(+), 164 deletions(-)

[groovy] 20/25: GROOVY-9649: Sonar refactoring

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 7dac3e7aea61a24922b6c44b17e7897c47be996c
Author: Esko Toivonen <es...@tuni.fi>
AuthorDate: Fri Apr 9 12:16:18 2021 +0300

    GROOVY-9649: Sonar refactoring
    
    Avoid NPE by not using a Boolean as an expression
---
 .../org/codehaus/groovy/runtime/DefaultGroovyMethodsSupport.java  | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethodsSupport.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethodsSupport.java
index 14f2b9d..0becd0a 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethodsSupport.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethodsSupport.java
@@ -108,17 +108,17 @@ public class DefaultGroovyMethodsSupport {
 
         // Undo inclusiveness effects done by subListBorders()
         if (!info.reverse) {
-            if (!range.getInclusiveLeft()) {
+            if (Boolean.FALSE.equals(range.getInclusiveLeft())) {
                 from--;
             }
-            if (!range.getInclusiveRight()) {
+            if (Boolean.FALSE.equals(range.getInclusiveRight())) {
                 to++;
             }
         } else {
-            if (!range.getInclusiveLeft()) {
+            if (Boolean.FALSE.equals(range.getInclusiveLeft())) {
                 to++;
             }
-            if (!range.getInclusiveRight()) {
+            if (Boolean.FALSE.equals(range.getInclusiveRight())) {
                 from--;
             }
         }

[groovy] 04/25: GROOVY-9649: left open and full open range support for IntRange

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit acf9423f27f7512b5be767f2ca7d71756ea7e416
Author: Eerik Voimanen <ee...@tuni.fi>
AuthorDate: Thu Apr 1 16:45:58 2021 +0300

    GROOVY-9649: left open and full open range support for IntRange
---
 src/main/java/groovy/lang/IntRange.java            | 77 ++++++++++++++--------
 .../groovy/runtime/ScriptBytecodeAdapter.java      |  2 +-
 2 files changed, 52 insertions(+), 27 deletions(-)

diff --git a/src/main/java/groovy/lang/IntRange.java b/src/main/java/groovy/lang/IntRange.java
index c1a4bc3..f06f7a7 100644
--- a/src/main/java/groovy/lang/IntRange.java
+++ b/src/main/java/groovy/lang/IntRange.java
@@ -132,7 +132,9 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
      * <p>
      * If true or false, the reverse flag is discarded.
      */
-    private final Boolean inclusive;
+    private final Boolean inclusiveRight;
+
+    private final Boolean inclusiveLeft;
 
     /**
      * Creates a new non-inclusive aware <code>IntRange</code>. If <code>from</code> is greater than
@@ -143,7 +145,8 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
      * @throws IllegalArgumentException if the range would contain more than {@link Integer#MAX_VALUE} values.
      */
     public IntRange(int from, int to) {
-        this.inclusive = null;
+        this.inclusiveRight = null;
+        this.inclusiveLeft = null;
         if (from > to) {
             this.from = to;
             this.to = from;
@@ -166,7 +169,8 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
      * @throws IllegalArgumentException if <code>from</code> is greater than <code>to</code>.
      */
     protected IntRange(int from, int to, boolean reverse) {
-        this.inclusive = null;
+        this.inclusiveRight = null;
+        this.inclusiveLeft = null;
         if (from > to) {
             throw new IllegalArgumentException("'from' must be less than or equal to 'to'");
         }
@@ -182,12 +186,17 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
      *
      * @param from      the first value in the range.
      * @param to        the last value in the range.
-     * @param inclusive <code>true</code> if the to value is included in the range.
+     * @param inclusiveRight <code>true</code> if the to value is included in the range.
      */
-    public IntRange(boolean inclusive, int from, int to) {
+    public IntRange(boolean inclusiveRight, int from, int to) {
+        this(true, inclusiveRight, from, to);
+    }
+
+    public IntRange(boolean inclusiveLeft, boolean inclusiveRight, int from, int to) {
         this.from = from;
         this.to = to;
-        this.inclusive = inclusive;
+        this.inclusiveRight = inclusiveRight;
+        this.inclusiveLeft = inclusiveLeft;
         this.reverse = false; // range may still be reversed, this value is ignored for inclusive-aware ranges
         checkSize();
     }
@@ -201,7 +210,8 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
      * @since 2.5.0
      */
     public <T extends Number & Comparable> NumberRange by(T stepSize) {
-        return new NumberRange(NumberRange.comparableNumber((Number)from), NumberRange.comparableNumber((Number)to), stepSize, inclusive);
+        // TODO edit NumberRange
+        return new NumberRange(NumberRange.comparableNumber((Number)from), NumberRange.comparableNumber((Number)to), stepSize, inclusiveRight);
     }
 
     private void checkSize() {
@@ -220,13 +230,17 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
      * @return the calculated range information (with 1 added to the to value, ready for providing to subList
      */
     public RangeInfo subListBorders(int size) {
-        if (inclusive == null) {
+        if (inclusiveRight == null || inclusiveLeft == null) {
             throw new IllegalStateException("Should not call subListBorders on a non-inclusive aware IntRange");
         }
-        return subListBorders(from, to, inclusive, size);
+        return subListBorders(from, to, inclusiveRight, size);
     }
 
-    static RangeInfo subListBorders(int from, int to, boolean inclusive, int size) {
+    static RangeInfo subListBorders(int from, int to, boolean inclusiveRight, int size) {
+        return subListBorders(from, to, true, inclusiveRight, size);
+    }
+
+    static RangeInfo subListBorders(int from, int to, boolean inclusiveLeft, boolean inclusiveRight, int size) {
         int tempFrom = from;
         if (tempFrom < 0) {
             tempFrom += size;
@@ -236,9 +250,9 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
             tempTo += size;
         }
         if (tempFrom > tempTo) {
-            return new RangeInfo(inclusive ? tempTo : tempTo + 1, tempFrom + 1, true);
+            return new RangeInfo(inclusiveRight ? tempTo : tempTo + 1, inclusiveLeft ? tempFrom + 1 : tempFrom, true);
         }
-        return new RangeInfo(tempFrom, inclusive ? tempTo + 1 : tempTo, false);
+        return new RangeInfo(inclusiveLeft ? tempFrom : tempFrom + 1, inclusiveRight ? tempTo + 1 : tempTo, false);
     }
 
     /**
@@ -267,34 +281,44 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
      * @return <code>true</code> if the ranges are equal
      */
     public boolean equals(IntRange that) {
-        return that != null && ((inclusive == null && reverse == that.reverse && from == that.from && to == that.to)
-                || (inclusive != null && Objects.equals(inclusive, that.inclusive) && from == that.from && to == that.to));
+        return that != null && from == that.from && to == that.to && (
+                (inclusiveRight == null && inclusiveLeft == null && reverse == that.reverse)
+                || (
+                        (inclusiveLeft == null || Objects.equals(inclusiveLeft, that.inclusiveLeft))
+                        && (inclusiveRight == null || Objects.equals(inclusiveRight, that.inclusiveRight))
+                )
+        );
     }
 
     @Override
     public Integer getFrom() {
-        if (inclusive == null || from <= to) {
-            return from;
+        if (from <= to) {
+            return (inclusiveLeft == null || inclusiveLeft) ? from : from + 1;
         }
-        return inclusive ? to : to + 1;
+        return (inclusiveRight == null || inclusiveRight) ? to : to + 1;
     }
 
     @Override
     public Integer getTo() {
-        if (inclusive == null) {
-            return to;
-        }
         if (from <= to) {
-            return inclusive ? to : to - 1;
+            return (inclusiveRight == null || inclusiveRight) ? to : to - 1;
         }
-        return from;
+        return (inclusiveLeft == null || inclusiveLeft) ? from : from - 1;
     }
 
     /**
      * Returns the inclusive flag. Null for non-inclusive aware ranges or non-null for inclusive aware ranges.
      */
     public Boolean getInclusive() {
-        return inclusive;
+        return inclusiveRight;
+    }
+
+    public Boolean getInclusiveRight() {
+        return inclusiveRight;
+    }
+
+    public Boolean getInclusiveLeft() {
+        return inclusiveLeft;
     }
 
     /**
@@ -317,7 +341,7 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
 
     @Override
     public boolean isReverse() {
-        return inclusive == null ? reverse : (from > to);
+        return (inclusiveRight == null && inclusiveLeft == null) ? reverse : (from > to);
     }
 
     @Override
@@ -367,8 +391,9 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
 
     @Override
     public String toString() {
-        return inclusive != null ? ("" + from + ".." + (inclusive ? "" : "<") + to)
-                : (reverse ? "" + to + ".." + from : "" + from + ".." + to);
+        return (inclusiveRight == null && inclusiveLeft == null) ? (reverse ? "" + to + ".." + from : "" + from + ".." + to)
+                : ("" + from + ((inclusiveLeft != null && inclusiveLeft) ? "" : "<") + ".."
+                             + ((inclusiveRight != null && inclusiveRight) ? "" : "<") + to);
     }
 
     @Override
diff --git a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
index e02bc62..a273c61 100644
--- a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
+++ b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
@@ -641,7 +641,7 @@ public class ScriptBytecodeAdapter {
             int ifrom = (Integer) from;
             int ito = (Integer) to;
             if (inclusive || ifrom != ito) {
-                return new IntRange(inclusive, ifrom, ito);
+                return new IntRange(!exclusiveLeft, !exclusiveRight, ifrom, ito);
             } // else fall through for EmptyRange
         }
         if (!inclusive && compareEqual(from, to)) {

[groovy] 07/25: GROOVY-9649: Added more test cases for empty ranges

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit f6644ffac2505f671ff80aef32046ed325074775
Author: Esko Toivonen <es...@tuni.fi>
AuthorDate: Mon Apr 5 14:38:47 2021 +0300

    GROOVY-9649: Added more test cases for empty ranges
---
 src/test/groovy/RangeTest.groovy | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/src/test/groovy/RangeTest.groovy b/src/test/groovy/RangeTest.groovy
index 82798f9..3bb7609 100644
--- a/src/test/groovy/RangeTest.groovy
+++ b/src/test/groovy/RangeTest.groovy
@@ -329,11 +329,29 @@ class RangeTest extends GroovyTestCase {
         assertSize(0..<0, 0)
         assertSize(1..<1, 0)
         assertSize(-1..<-1, 0)
+        assertSize(-1<..-1, 0)
+        assertSize(-1<..<-1, 0)
+        assertSize(-1<..<-2, 0)
         assertSize('a'..<'a', 0)
+        assertSize('a'<..'a', 0)
+        assertSize('a'<..<'a', 0)
+        assertSize('a'<..<'b', 0)
         assertSize(0.0G..<0.0G, 0)
+        assertSize(0.0G<..0.0G, 0)
+        assertSize(0.0G<..<0.0G, 0)
+        assertSize(0.0G<..<1.0G, 0)
         (0..<0).each { assert false }
+        (0<..0).each { assert false }
+        (0<..<0).each { assert false }
+        (0<..<1).each { assert false }
         (0..<0).step(1) { assert false }
+        (0<..0).step(1) { assert false }
+        (0<..<0).step(1) { assert false }
+        (0<..<1).step(1) { assert false }
         for (i in 0..<0) assert false
+        for (i in 0<..0) assert false
+        for (i in 0<..<0) assert false
+        for (i in 0<..<1) assert false
         assertToString(0..<0, '0..<0', '0..<0')
         assertToString('a'..<'a', 'a..<a', "'a'..<'a'")
         assertToString(null..<null, 'null..<null', 'null..<null')

[groovy] 10/25: GROOVY-9649: Fixed NumberRange.get not throwing at certain conditions

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit e620177d07f93c0af07e56afc93509e777accd9c
Author: Esko Toivonen <es...@tuni.fi>
AuthorDate: Tue Apr 6 17:59:44 2021 +0300

    GROOVY-9649: Fixed NumberRange.get not throwing at certain conditions
    
    With ranges like 0G<..<1G, the get method would erroneously return 1G
    instead of throwing an exception. This commit fixes that by directly
    incrementing the current value in the iterator instead of next() call.
---
 src/main/java/groovy/lang/NumberRange.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/java/groovy/lang/NumberRange.java b/src/main/java/groovy/lang/NumberRange.java
index 8a7c846..ece85c9 100644
--- a/src/main/java/groovy/lang/NumberRange.java
+++ b/src/main/java/groovy/lang/NumberRange.java
@@ -644,7 +644,7 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
                     // make the first fetch lazy too
                     next = isAscending ? range.getFrom() : range.getTo();
                     if (!range.inclusiveLeft) {
-                        next = next();
+                        next = isAscending ? increment(next, step) : decrement(next, step);
                     }
                 } else {
                     next = isAscending ? increment(next, step) : decrement(next, step);

[groovy] 24/25: GROOVY-9649: Add test cases for IntRange.equals

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 06b56f2e93c4f75a692b4155a11eca7aa087d431
Author: Esko Toivonen <es...@tuni.fi>
AuthorDate: Fri Apr 9 16:31:05 2021 +0300

    GROOVY-9649: Add test cases for IntRange.equals
---
 src/test/groovy/lang/IntRangeTest.groovy | 37 ++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/src/test/groovy/lang/IntRangeTest.groovy b/src/test/groovy/lang/IntRangeTest.groovy
index ac495b9..ec68b91 100644
--- a/src/test/groovy/lang/IntRangeTest.groovy
+++ b/src/test/groovy/lang/IntRangeTest.groovy
@@ -218,4 +218,41 @@ class IntRangeTest extends GroovyTestCase {
         bais.withObjectInputStream { ois -> assert ois.readObject() == [4..1, 2..<5] }
     }
 
+    void testEquals() {
+        IntRange r1 = new IntRange(0, 10)
+        IntRange r2 = new IntRange(0, 10)
+        assert r1.equals(r2)
+        assert r2.equals(r1)
+
+        r1 = new IntRange(true, false, 0, 10)
+        r2 = new IntRange(true, false, 0, 10)
+        assert r1.equals(r2)
+        assert r2.equals(r1)
+
+        r1 = new IntRange(false, 1, 11)
+        r2 = new IntRange(1, 10)
+        assert !r1.equals(r2)
+        assert !r2.equals(r1)
+
+        r1 = new IntRange(false, 1, 10)
+        r2 = new IntRange(1, 10)
+        assert !r1.equals(r2)
+        // As before GROOVY-9649
+        assert r2.equals(r1)
+
+        r1 = new IntRange(false, true, -1, 10)
+        r2 = new IntRange(1, 10)
+        assert !r1.equals(r2)
+        assert !r2.equals(r1)
+
+        r1 = new IntRange(true, true, 10, 0)
+        r2 = new IntRange(0, 10, true)
+        assert !r1.equals(r2)
+        assert !r2.equals(r1)
+
+        r1 = new IntRange(0, 10, true)
+        r2 = new IntRange(0, 10, false)
+        assert !r1.equals(r2)
+        assert !r2.equals(r1)
+    }
 }

[groovy] 23/25: GROOVY-9649: Fix bug in IntRange.equals

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 8e54d6c8fc3b5bb2b6ee10c5908776b1f05e3af3
Author: Eerik Voimanen <ee...@tuni.fi>
AuthorDate: Fri Apr 9 15:53:19 2021 +0300

    GROOVY-9649: Fix bug in IntRange.equals
---
 src/main/java/groovy/lang/IntRange.java | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/src/main/java/groovy/lang/IntRange.java b/src/main/java/groovy/lang/IntRange.java
index a605a4b..cb5d172 100644
--- a/src/main/java/groovy/lang/IntRange.java
+++ b/src/main/java/groovy/lang/IntRange.java
@@ -298,11 +298,10 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
      */
     public boolean equals(IntRange that) {
         return that != null && from == that.from && to == that.to && (
-                (inclusiveRight == null && inclusiveLeft == null && reverse == that.reverse)
-                || (
-                        (inclusiveLeft == null || Objects.equals(inclusiveLeft, that.inclusiveLeft))
-                        && (inclusiveRight == null || Objects.equals(inclusiveRight, that.inclusiveRight))
-                )
+                // If inclusiveRight is null, then inclusive left is also null (see constructor)
+                (inclusiveRight == null) ? reverse == that.reverse:
+                        (Objects.equals(inclusiveLeft, that.inclusiveLeft)
+                                && Objects.equals(inclusiveRight, that.inclusiveRight))
         );
     }
 
@@ -417,8 +416,7 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
         if (inclusiveRight == null && inclusiveLeft == null)  {
                return reverse ? "" + to + ".." + from : "" + from + ".." + to;
         }
-        return "" + from + ((inclusiveLeft != null && inclusiveLeft) ? "" : "<") + ".."
-                  + ((inclusiveRight != null && inclusiveRight) ? "" : "<") + to;
+        return "" + from + (inclusiveLeft ? "" : "<") + ".." + (inclusiveRight ? "" : "<") + to;
     }
 
     @Override

[groovy] 06/25: GROOVY-9649: Fix subListBorders call in IntRange

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 16d12fc6823f6502000738fa60b6b1589c55f939
Author: Eerik Voimanen <ee...@tuni.fi>
AuthorDate: Sat Apr 3 16:22:48 2021 +0300

    GROOVY-9649: Fix subListBorders call in IntRange
---
 src/main/java/groovy/lang/IntRange.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/java/groovy/lang/IntRange.java b/src/main/java/groovy/lang/IntRange.java
index f06f7a7..c11fd94 100644
--- a/src/main/java/groovy/lang/IntRange.java
+++ b/src/main/java/groovy/lang/IntRange.java
@@ -233,7 +233,7 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
         if (inclusiveRight == null || inclusiveLeft == null) {
             throw new IllegalStateException("Should not call subListBorders on a non-inclusive aware IntRange");
         }
-        return subListBorders(from, to, inclusiveRight, size);
+        return subListBorders(from, to, inclusiveLeft, inclusiveRight, size);
     }
 
     static RangeInfo subListBorders(int from, int to, boolean inclusiveRight, int size) {

[groovy] 22/25: GROOVY-9649: Minor refactor: remove redundant if clause

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 4d84b6927a14cf719fba154b771f5bbcb597a076
Author: Esko Toivonen <es...@tuni.fi>
AuthorDate: Fri Apr 9 13:21:07 2021 +0300

    GROOVY-9649: Minor refactor: remove redundant if clause
---
 .../org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java   | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
index fdd593d..9c19475 100644
--- a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
+++ b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
@@ -661,14 +661,10 @@ public class ScriptBytecodeAdapter {
             return new EmptyRange((Comparable) from);
         }
         if (from instanceof Integer && to instanceof Integer) {
-            int ifrom = (Integer) from;
-            int ito = (Integer) to;
-            if ((!exclusiveLeft && !exclusiveRight) || ifrom != ito) {
-                // Currently, empty ranges where from != to, the range is full exclusive (e.g. 0<..<-1) and from and to
-                // have a different sign are constructed as IntRanges. This is because these ranges can still be used to
-                // index into lists.
-                return new IntRange(!exclusiveLeft, !exclusiveRight, ifrom, ito);
-            }
+            // Currently, empty ranges where from != to, the range is full exclusive (e.g. 0<..<-1) and from and to
+            // have a different sign are constructed as IntRanges. This is because t3hese ranges can still be used to
+            // index into lists.
+            return new IntRange(!exclusiveLeft, !exclusiveRight, (Integer) from, (Integer) to);
         }
         if (from instanceof Number && to instanceof Number) {
             return new NumberRange(comparableNumber((Number) from), comparableNumber((Number) to), !exclusiveLeft, !exclusiveRight);

[groovy] 16/25: GROOVY-9649: Added documentation for left-open and full-open ranges

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit f8fcd96a99fd7dbada9f250d0a45dbd0d2e51c0e
Author: Otto Vayrynen <ot...@tuni.fi>
AuthorDate: Wed Apr 7 18:57:51 2021 +0300

    GROOVY-9649: Added documentation for left-open and full-open ranges
---
 src/spec/doc/_working-with-collections.adoc | 6 ++++++
 src/spec/doc/core-operators.adoc            | 6 ++++--
 src/spec/doc/core-semantics.adoc            | 2 +-
 src/spec/test/OperatorsTest.groovy          | 8 ++++----
 4 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/src/spec/doc/_working-with-collections.adoc b/src/spec/doc/_working-with-collections.adoc
index 9faf413..4e93da0 100644
--- a/src/spec/doc/_working-with-collections.adoc
+++ b/src/spec/doc/_working-with-collections.adoc
@@ -341,6 +341,12 @@ contains the from and to value).
 Ranges defined with the `..<` notation are half-open, they include the
 first value but not the last value.
 
+Ranges defined with the `<..` notation are also half-open, they include the
+last value but not the first value.
+
+Ranges defined with the `<..<` notation are full-open, they do not include the
+first value nor the last value.
+
 [source,groovy]
 ----------------------------------------------------------------------------
 include::../test/gdk/WorkingWithCollectionsTest.groovy[tags=intrange,indent=0]
diff --git a/src/spec/doc/core-operators.adoc b/src/spec/doc/core-operators.adoc
index e9a5f04..78726bf 100644
--- a/src/spec/doc/core-operators.adoc
+++ b/src/spec/doc/core-operators.adoc
@@ -705,8 +705,10 @@ include::../test/OperatorsTest.groovy[tags=intrange,indent=0]
 <1> a simple range of integers, stored into a local variable
 <2> an `IntRange`, with inclusive bounds
 <3> an `IntRange`, with exclusive upper bound
-<4> a `groovy.lang.Range` implements the `List` interface
-<5> meaning that you can call the `size` method on it
+<4> an `IntRange`, with exclusive lower bound
+<5> an `IntRange`, with exclusive lower and upper bounds
+<6> a `groovy.lang.Range` implements the `List` interface
+<7> meaning that you can call the `size` method on it
 
 Ranges implementation is lightweight, meaning that only the lower and upper bounds are stored. You can create a range
 from any `Comparable` object that has `next()` and `previous()` methods to determine the next / previous item in the range.
diff --git a/src/spec/doc/core-semantics.adoc b/src/spec/doc/core-semantics.adoc
index a86bf02..45e7e42 100644
--- a/src/spec/doc/core-semantics.adoc
+++ b/src/spec/doc/core-semantics.adoc
@@ -1545,7 +1545,7 @@ Groovy provides a syntax for various type literals. There are three native colle
 
 * lists, using the `[]` literal
 * maps, using the `[:]` literal
-* ranges, using `from..to` (inclusive) and `from..<to` (exclusive)
+* ranges, using `from..to` (inclusive), `from..<to` (right exclusive),`from<..to` (left exclusive) and `from<..<to` (full exclusive)
 
 The inferred type of a literal depends on the elements of the literal, as illustrated in the following table:
 
diff --git a/src/spec/test/OperatorsTest.groovy b/src/spec/test/OperatorsTest.groovy
index 3f9746c..94f0d29 100644
--- a/src/spec/test/OperatorsTest.groovy
+++ b/src/spec/test/OperatorsTest.groovy
@@ -543,10 +543,10 @@ assert function(*args,5,6) == 26
         def range = 0..5                                    // <1>
         assert (0..5).collect() == [0, 1, 2, 3, 4, 5]       // <2>
         assert (0..<5).collect() == [0, 1, 2, 3, 4]         // <3>
-        assert (0<..5).collect() == [1, 2, 3, 4, 5]
-        assert (0<..<5).collect() == [1, 2, 3, 4]
-        assert (0..5) instanceof List                       // <4>
-        assert (0..5).size() == 6                           // <5>
+        assert (0<..5).collect() == [1, 2, 3, 4, 5]         // <4>
+        assert (0<..<5).collect() == [1, 2, 3, 4]           // <5>
+        assert (0..5) instanceof List                       // <6>
+        assert (0..5).size() == 6                           // <7>
         // end::intrange[]
         '''
         assertScript '''

[groovy] 14/25: GROOVY-9649: Fixed getAt for primitive arrays by introducing a new helper method, added few test cases for getAt

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 8d76395455a689d058b2428b00e4eaf391ad31c2
Author: Esko Toivonen <es...@tuni.fi>
AuthorDate: Wed Apr 7 17:52:47 2021 +0300

    GROOVY-9649: Fixed getAt for primitive arrays by introducing a new helper method, added few test cases for getAt
---
 .../groovy/runtime/DefaultGroovyMethods.java       | 16 ++++++-------
 .../runtime/DefaultGroovyMethodsSupport.java       | 28 ++++++++++++++++++++++
 src/test/groovy/GroovyMethodsTest.groovy           |  3 +++
 3 files changed, 39 insertions(+), 8 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
index 1ce0ea6..c756d84 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -13944,7 +13944,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
     @SuppressWarnings("unchecked")
     public static List<Byte> getAt(byte[] array, IntRange range) {
         RangeInfo info = subListBorders(array.length, range);
-        List<Byte> answer = primitiveArrayGet(array, new IntRange(true, info.from, info.to - 1));
+        List<Byte> answer = primitiveArrayGet(array, subListRange(info, range));
         return info.reverse ? reverse(answer) : answer;
     }
 
@@ -13959,7 +13959,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
     @SuppressWarnings("unchecked")
     public static List<Character> getAt(char[] array, IntRange range) {
         RangeInfo info = subListBorders(array.length, range);
-        List<Character> answer = primitiveArrayGet(array, new IntRange(true, info.from, info.to - 1));
+        List<Character> answer = primitiveArrayGet(array, subListRange(info, range));
         return info.reverse ? reverse(answer) : answer;
     }
 
@@ -13974,7 +13974,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
     @SuppressWarnings("unchecked")
     public static List<Short> getAt(short[] array, IntRange range) {
         RangeInfo info = subListBorders(array.length, range);
-        List<Short> answer = primitiveArrayGet(array, new IntRange(true, info.from, info.to - 1));
+        List<Short> answer = primitiveArrayGet(array, subListRange(info, range));
         return info.reverse ? reverse(answer) : answer;
     }
 
@@ -13989,7 +13989,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
     @SuppressWarnings("unchecked")
     public static List<Integer> getAt(int[] array, IntRange range) {
         RangeInfo info = subListBorders(array.length, range);
-        List<Integer> answer = primitiveArrayGet(array, new IntRange(true, info.from, info.to - 1));
+        List<Integer> answer = primitiveArrayGet(array, subListRange(info, range));
         return info.reverse ? reverse(answer) : answer;
     }
 
@@ -14004,7 +14004,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
     @SuppressWarnings("unchecked")
     public static List<Long> getAt(long[] array, IntRange range) {
         RangeInfo info = subListBorders(array.length, range);
-        List<Long> answer = primitiveArrayGet(array, new IntRange(true, info.from, info.to - 1));
+        List<Long> answer = primitiveArrayGet(array, subListRange(info, range));
         return info.reverse ? reverse(answer) : answer;
     }
 
@@ -14019,7 +14019,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
     @SuppressWarnings("unchecked")
     public static List<Float> getAt(float[] array, IntRange range) {
         RangeInfo info = subListBorders(array.length, range);
-        List<Float> answer = primitiveArrayGet(array, new IntRange(true, info.from, info.to - 1));
+        List<Float> answer = primitiveArrayGet(array, subListRange(info, range));
         return info.reverse ? reverse(answer) : answer;
     }
 
@@ -14034,7 +14034,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
     @SuppressWarnings("unchecked")
     public static List<Double> getAt(double[] array, IntRange range) {
         RangeInfo info = subListBorders(array.length, range);
-        List<Double> answer = primitiveArrayGet(array, new IntRange(true, info.from, info.to - 1));
+        List<Double> answer = primitiveArrayGet(array, subListRange(info, range));
         return info.reverse ? reverse(answer) : answer;
     }
 
@@ -14049,7 +14049,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
     @SuppressWarnings("unchecked")
     public static List<Boolean> getAt(boolean[] array, IntRange range) {
         RangeInfo info = subListBorders(array.length, range);
-        List<Boolean> answer = primitiveArrayGet(array, new IntRange(true, info.from, info.to - 1));
+        List<Boolean> answer = primitiveArrayGet(array, subListRange(info, range));
         return info.reverse ? reverse(answer) : answer;
     }
 
diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethodsSupport.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethodsSupport.java
index 43136d5..14f2b9d 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethodsSupport.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethodsSupport.java
@@ -101,6 +101,34 @@ public class DefaultGroovyMethodsSupport {
         return new RangeInfo(from, from, false);
     }
 
+    // Helper method for primitive array getAt
+    protected static IntRange subListRange(RangeInfo info, IntRange range) {
+        int from = info.from;
+        int to = info.to - 1;
+
+        // Undo inclusiveness effects done by subListBorders()
+        if (!info.reverse) {
+            if (!range.getInclusiveLeft()) {
+                from--;
+            }
+            if (!range.getInclusiveRight()) {
+                to++;
+            }
+        } else {
+            if (!range.getInclusiveLeft()) {
+                to++;
+            }
+            if (!range.getInclusiveRight()) {
+                from--;
+            }
+        }
+
+        boolean inclusiveLeft = info.reverse ? range.getInclusiveRight() : range.getInclusiveLeft();
+        boolean inclusiveRight = info.reverse ? range.getInclusiveLeft() : range.getInclusiveRight();
+
+        return new IntRange(inclusiveLeft, inclusiveRight, from, to);
+    }
+
     /**
      * This converts a possibly negative index to a real index into the array.
      *
diff --git a/src/test/groovy/GroovyMethodsTest.groovy b/src/test/groovy/GroovyMethodsTest.groovy
index 0c9d5d3..63ac9df 100644
--- a/src/test/groovy/GroovyMethodsTest.groovy
+++ b/src/test/groovy/GroovyMethodsTest.groovy
@@ -295,6 +295,9 @@ class GroovyMethodsTest extends GroovyTestCase {
         def list = ['a', 'b', 'c']
         assert list[1..2] == ['b', 'c']
         assert list[0..<0] == []
+        assert list[0<..0] == []
+        assert list[0<..<0] == []
+        assert list[0<..<1] == []
     }
 
     void testCharSequenceGetAt() {

[groovy] 15/25: GROOVY-9649: Fixed NumberRange size calculation with full-exclusive ranges where from equals to.

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 0a8a8a7ad590c4610973454e15ce5ba767459540
Author: Iiro Kiviluoma <ii...@outlook.com>
AuthorDate: Wed Apr 7 18:07:07 2021 +0300

    GROOVY-9649: Fixed NumberRange size calculation with full-exclusive ranges where from equals to.
---
 src/main/java/groovy/lang/NumberRange.java | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/main/java/groovy/lang/NumberRange.java b/src/main/java/groovy/lang/NumberRange.java
index ece85c9..8677740 100644
--- a/src/main/java/groovy/lang/NumberRange.java
+++ b/src/main/java/groovy/lang/NumberRange.java
@@ -448,6 +448,10 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
     }
 
     void calcSize(Comparable from, Comparable to, Number stepSize) {
+        if (from == to && !inclusiveLeft && !inclusiveRight) {
+            size = 0;
+            return;
+        }
         int tempsize = 0;
         boolean shortcut = false;
         if (isIntegral(stepSize)) {

[groovy] 25/25: GROOVY-9649: Refactored RangeExpression

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit e64215983ea4b7c277eb01a1ed3fcac43976be9c
Author: Iiro Kiviluoma <ii...@outlook.com>
AuthorDate: Sun Apr 11 12:05:17 2021 +0300

    GROOVY-9649: Refactored RangeExpression
    
    - Old constructor now delegates to the new constructor
    - Dropped inclusive class variable, isInclusive is handled via right exclusivity.
---
 .../java/org/codehaus/groovy/ast/expr/RangeExpression.java   | 12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/expr/RangeExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/RangeExpression.java
index a03cecf..b1aba80 100644
--- a/src/main/java/org/codehaus/groovy/ast/expr/RangeExpression.java
+++ b/src/main/java/org/codehaus/groovy/ast/expr/RangeExpression.java
@@ -29,26 +29,18 @@ import org.codehaus.groovy.ast.GroovyCodeVisitor;
 public class RangeExpression extends Expression {
     private final Expression from;
     private final Expression to;
-    private final boolean inclusive; // Kept to keep old code depending on this working
-    // GROOVY-9649
     private final boolean exclusiveLeft;
     private final boolean exclusiveRight;
 
     // Kept until sure this can be removed
     public RangeExpression(Expression from, Expression to, boolean inclusive) {
-        this.from = from;
-        this.to = to;
-        this.inclusive = inclusive;
-        this.exclusiveLeft = false;
-        this.exclusiveRight = !inclusive;
-        setType(ClassHelper.RANGE_TYPE);
+        this(from, to, false, !inclusive);
     }
 
     // GROOVY-9649
     public RangeExpression(Expression from, Expression to, boolean exclusiveLeft, boolean exclusiveRight) {
         this.from = from;
         this.to = to;
-        this.inclusive = !exclusiveRight; // Old code depends on this
         this.exclusiveLeft = exclusiveLeft;
         this.exclusiveRight = exclusiveRight;
         setType(ClassHelper.RANGE_TYPE);
@@ -77,7 +69,7 @@ public class RangeExpression extends Expression {
     }
 
     public boolean isInclusive() {
-        return inclusive;
+        return !isExclusiveRight();
     }
 
     public boolean isExclusiveLeft() {

[groovy] 08/25: GROOVY-9649: Make createRange aware of left side exclusivity

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit f71a1b7366ac304c6b7e1660e5a4a59f83382b6f
Author: Esko Toivonen <es...@tuni.fi>
AuthorDate: Mon Apr 5 14:41:53 2021 +0300

    GROOVY-9649: Make createRange aware of left side exclusivity
    
    Currently empty ranges are created only when to == from. This is
    because range indexing in lists behaves differently: while 0<..<-1
    would normally be an empty range, we can index into lists with it and
    expect some output, since negative indices count from the end of the
    list towards its beginning. Note that while this is true for IntRanges,
    object ranges like 'a'<..<'b' become EmptyRanges.
---
 .../groovy/runtime/ScriptBytecodeAdapter.java      | 27 +++++++++++++++++-----
 1 file changed, 21 insertions(+), 6 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
index 5041862..5161f65 100644
--- a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
+++ b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
@@ -636,28 +636,43 @@ public class ScriptBytecodeAdapter {
     }
 
     public static List createRange(Object from, Object to, boolean exclusiveLeft, boolean exclusiveRight) throws Throwable {
-        boolean inclusive = !exclusiveRight;
         if (from instanceof Integer && to instanceof Integer) {
             int ifrom = (Integer) from;
             int ito = (Integer) to;
-            if (inclusive || ifrom != ito) {
+            if ((!exclusiveLeft && !exclusiveRight) || ifrom != ito) {
+                // Currently, empty ranges where from != to and the range is full exclusive (e.g. 0<..<-1) are
+                // constructed as IntRanges. This is because these ranges can still be used to index into lists.
                 return new IntRange(!exclusiveLeft, !exclusiveRight, ifrom, ito);
             } // else fall through for EmptyRange
         }
-        if (!inclusive && compareEqual(from, to)) {
+        if ((exclusiveLeft || exclusiveRight) && compareEqual(from, to)) {
             return new EmptyRange((Comparable) from);
         }
         if (from instanceof Number && to instanceof Number) {
             return new NumberRange(comparableNumber((Number) from), comparableNumber((Number) to), !exclusiveLeft, !exclusiveRight);
         }
-        if (!inclusive) {
-            if (compareGreaterThan(from, to)) {
+        Boolean greater = null;
+        if (exclusiveRight) {
+            greater = compareGreaterThan(from, to);
+            if (greater) {
                 to = invokeMethod0(ScriptBytecodeAdapter.class, to, "next");
             } else {
                 to = invokeMethod0(ScriptBytecodeAdapter.class, to, "previous");
             }
         }
-
+        if (exclusiveLeft) {
+            if (greater == null) {
+                greater = compareGreaterThan(from, to);
+            }
+            if (compareEqual(from, to)) {
+                return new EmptyRange((Comparable) from);
+            }
+            if (greater) {
+                from = invokeMethod0(ScriptBytecodeAdapter.class, from, "previous");
+            } else {
+                from = invokeMethod0(ScriptBytecodeAdapter.class, from, "next");
+            }
+        }
         return new ObjectRange((Comparable) from, (Comparable) to);
     }
 

[groovy] 09/25: GROOVY-9649: Finalize NumberRange functionalities for left- and full-open ranges.

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit f786bc032b1339139557bf200518589596f5deac
Author: Iiro Kiviluoma <ii...@outlook.com>
AuthorDate: Mon Apr 5 15:41:19 2021 +0300

    GROOVY-9649: Finalize NumberRange functionalities for left- and full-open ranges.
---
 src/main/java/groovy/lang/NumberRange.java | 24 ++++++++++--------------
 1 file changed, 10 insertions(+), 14 deletions(-)

diff --git a/src/main/java/groovy/lang/NumberRange.java b/src/main/java/groovy/lang/NumberRange.java
index eb41699..8a7c846 100644
--- a/src/main/java/groovy/lang/NumberRange.java
+++ b/src/main/java/groovy/lang/NumberRange.java
@@ -86,11 +86,6 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
     private final boolean reverse;
 
     /**
-     * <code>true</code> if the range includes the upper bound.
-     */
-    private final boolean inclusive;
-
-    /**
      * <code>true</code> if the range includes the lower bound.
      */
     private final boolean inclusiveLeft;
@@ -218,7 +213,6 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
         this.from = (Comparable) tempFrom;
         this.to = (Comparable) tempTo;
         this.stepSize = stepSize == null ? 1 : stepSize;
-        this.inclusive = inclusiveRight;
         this.inclusiveLeft = inclusiveLeft;
         this.inclusiveRight = inclusiveRight;
     }
@@ -234,7 +228,7 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
         if (stepSize.intValue() != 1) {
             throw new IllegalStateException("Step must be 1 when used by subList!");
         }
-        return IntRange.subListBorders(((Number) from).intValue(), ((Number) to).intValue(), inclusive, size);
+        return IntRange.subListBorders(((Number) from).intValue(), ((Number) to).intValue(), inclusiveLeft, inclusiveRight, size);
     }
 
     /**
@@ -249,7 +243,7 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
         if (!Integer.valueOf(1).equals(this.stepSize)) {
             throw new IllegalStateException("by only allowed on ranges with original stepSize = 1 but found " + this.stepSize);
         }
-        return new NumberRange(comparableNumber(from), comparableNumber(to), stepSize, inclusive);
+        return new NumberRange(comparableNumber(from), comparableNumber(to), stepSize, inclusiveLeft, inclusiveRight);
     }
 
     @SuppressWarnings("unchecked")
@@ -352,7 +346,8 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
     public boolean fastEquals(NumberRange that) {
         return that != null
                 && reverse == that.reverse
-                && inclusive == that.inclusive
+                && inclusiveLeft == that.inclusiveLeft
+                && inclusiveRight == that.inclusiveRight
                 && compareEqual(from, that.from)
                 && compareEqual(to, that.to)
                 && compareEqual(stepSize, that.stepSize);
@@ -452,7 +447,6 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
         return size;
     }
 
-    // TODO: Update to work with inclusiveLeft! (GROOVY-9649)
     void calcSize(Comparable from, Comparable to, Number stepSize) {
         int tempsize = 0;
         boolean shortcut = false;
@@ -460,18 +454,20 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
             if ((from instanceof Integer || from instanceof Long)
                     && (to instanceof Integer || to instanceof Long)) {
                 // let's fast calculate the size
-                final BigInteger fromNum = new BigInteger(from.toString());
+                final BigInteger fromTemp = new BigInteger(from.toString());
+                final BigInteger fromNum = inclusiveLeft ? fromTemp : fromTemp.add(BigInteger.ONE);
                 final BigInteger toTemp = new BigInteger(to.toString());
-                final BigInteger toNum = inclusive ? toTemp : toTemp.subtract(BigInteger.ONE);
+                final BigInteger toNum = inclusiveRight ? toTemp : toTemp.subtract(BigInteger.ONE);
                 final BigInteger sizeNum = new BigDecimal(toNum.subtract(fromNum)).divide(BigDecimal.valueOf(stepSize.longValue()), RoundingMode.DOWN).toBigInteger().add(BigInteger.ONE);
                 tempsize = sizeNum.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) < 0 ? sizeNum.intValue() : Integer.MAX_VALUE;
                 shortcut = true;
             } else if (((from instanceof BigDecimal || from instanceof BigInteger) && to instanceof Number) ||
                     ((to instanceof BigDecimal || to instanceof BigInteger) && from instanceof Number)) {
                 // let's fast calculate the size
-                final BigDecimal fromNum = NumberMath.toBigDecimal((Number) from);
+                final BigDecimal fromTemp = NumberMath.toBigDecimal((Number) from);
+                final BigDecimal fromNum = inclusiveLeft ? fromTemp : fromTemp.add(BigDecimal.ONE);
                 final BigDecimal toTemp = NumberMath.toBigDecimal((Number) to);
-                final BigDecimal toNum = inclusive ? toTemp : toTemp.subtract(BigDecimal.ONE);
+                final BigDecimal toNum = inclusiveRight ? toTemp : toTemp.subtract(BigDecimal.ONE);
                 final BigInteger sizeNum = toNum.subtract(fromNum).divide(BigDecimal.valueOf(stepSize.longValue()), RoundingMode.DOWN).toBigInteger().add(BigInteger.ONE);
                 tempsize = sizeNum.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) < 0 ? sizeNum.intValue() : Integer.MAX_VALUE;
                 shortcut = true;

[groovy] 18/25: GROOVY-9649: Sonar refactoring

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 4cbaec4dd65329570887ba4218438c8bcbe4483f
Author: Iiro Kiviluoma <ii...@outlook.com>
AuthorDate: Fri Apr 9 11:49:29 2021 +0300

    GROOVY-9649: Sonar refactoring
    
    - NumberRange: Extracted nested ternary operations of hasNext into independent statements
    - NumberRange: Inverted if-condition in fetchNextIfNeeded to reduce its cognitive complexity
---
 src/main/java/groovy/lang/NumberRange.java | 35 +++++++++++++++++++-----------
 1 file changed, 22 insertions(+), 13 deletions(-)

diff --git a/src/main/java/groovy/lang/NumberRange.java b/src/main/java/groovy/lang/NumberRange.java
index 8677740..e19a367 100644
--- a/src/main/java/groovy/lang/NumberRange.java
+++ b/src/main/java/groovy/lang/NumberRange.java
@@ -624,9 +624,18 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
         @Override
         public boolean hasNext() {
             fetchNextIfNeeded();
-            return (next != null) && (isAscending
-                    ? (range.inclusiveRight ? compareLessThanEqual(next, range.getTo()) : compareLessThan(next, range.getTo()))
-                    : (range.inclusiveRight ? compareGreaterThanEqual(next, range.getFrom()) : compareGreaterThan(next, range.getFrom())));
+            if (next == null) {
+                return false;
+            }
+            if (isAscending) {
+                return range.inclusiveRight
+                        ? compareLessThanEqual(next, range.getTo())
+                        : compareLessThan(next, range.getTo());
+            }
+            return range.inclusiveRight
+                    ? compareGreaterThanEqual(next, range.getFrom())
+                    : compareGreaterThan(next, range.getFrom());
+
         }
 
         @Override
@@ -641,18 +650,18 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
         }
 
         private void fetchNextIfNeeded() {
-            if (!isNextFetched) {
-                isNextFetched = true;
-
-                if (next == null) {
-                    // make the first fetch lazy too
-                    next = isAscending ? range.getFrom() : range.getTo();
-                    if (!range.inclusiveLeft) {
-                        next = isAscending ? increment(next, step) : decrement(next, step);
-                    }
-                } else {
+            if (isNextFetched) {
+                return;
+            }
+            isNextFetched = true;
+            if (next == null) {
+                // make the first fetch lazy too
+                next = isAscending ? range.getFrom() : range.getTo();
+                if (!range.inclusiveLeft) {
                     next = isAscending ? increment(next, step) : decrement(next, step);
                 }
+            } else {
+                next = isAscending ? increment(next, step) : decrement(next, step);
             }
         }
 

[groovy] 12/25: GROOVY-9649: Fix IntRange size being negative on some occasions

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 1a8f109b6bfc073dc81bf4ea63a3b0878aa70360
Author: Eerik Voimanen <ee...@tuni.fi>
AuthorDate: Wed Apr 7 16:39:32 2021 +0300

    GROOVY-9649: Fix IntRange size being negative on some occasions
---
 src/main/java/groovy/lang/IntRange.java | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/main/java/groovy/lang/IntRange.java b/src/main/java/groovy/lang/IntRange.java
index c11fd94..7a701ad 100644
--- a/src/main/java/groovy/lang/IntRange.java
+++ b/src/main/java/groovy/lang/IntRange.java
@@ -362,7 +362,8 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
 
     @Override
     public int size() {
-        return getTo() - getFrom() + 1;
+        // If fully exclusive and borders are one apart, the size would be negative, take that into account
+        return Math.max(getTo() - getFrom() + 1, 0);
     }
 
     @Override

[groovy] 01/25: GROOVY-9649: - Added left and full exclusive patterns to parser - Added exclusiveLeft and exclusiveRight class variables to RangeExpression

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit e3ba8e65e0b3b9cb0068bb64f3123be52a8442dd
Author: Iiro Kiviluoma <ii...@outlook.com>
AuthorDate: Wed Mar 24 15:10:17 2021 +0200

    GROOVY-9649: - Added left and full exclusive patterns to parser - Added exclusiveLeft and exclusiveRight class variables to RangeExpression
---
 src/antlr/GroovyLexer.g4                           | 36 ++++++++++++----------
 src/antlr/GroovyParser.g4                          |  4 ++-
 .../apache/groovy/parser/antlr4/AstBuilder.java    |  8 +++--
 .../codehaus/groovy/ast/expr/RangeExpression.java  | 36 +++++++++++++++++++---
 .../completion/antlr4/ReflectionCompleter.groovy   |  8 +++--
 5 files changed, 64 insertions(+), 28 deletions(-)

diff --git a/src/antlr/GroovyLexer.g4 b/src/antlr/GroovyLexer.g4
index 6989d6d..147e126 100644
--- a/src/antlr/GroovyLexer.g4
+++ b/src/antlr/GroovyLexer.g4
@@ -808,23 +808,25 @@ NullLiteral
 
 // Groovy Operators
 
-RANGE_INCLUSIVE     : '..';
-RANGE_EXCLUSIVE     : '..<';
-SPREAD_DOT          : '*.';
-SAFE_DOT            : '?.';
-SAFE_INDEX          : '?[';
-SAFE_CHAIN_DOT      : '??.';
-ELVIS               : '?:';
-METHOD_POINTER      : '.&';
-METHOD_REFERENCE    : '::';
-REGEX_FIND          : '=~';
-REGEX_MATCH         : '==~';
-POWER               : '**';
-POWER_ASSIGN        : '**=';
-SPACESHIP           : '<=>';
-IDENTICAL           : '===';
-NOT_IDENTICAL       : '!==';
-ARROW               : '->';
+RANGE_INCLUSIVE         : '..';
+RANGE_EXCLUSIVE_LEFT    : '<..';
+RANGE_EXCLUSIVE_RIGHT   : '..<';
+RANGE_EXCLUSIVE_FULL    : '<..<';
+SPREAD_DOT              : '*.';
+SAFE_DOT                : '?.';
+SAFE_INDEX              : '?[';
+SAFE_CHAIN_DOT          : '??.';
+ELVIS                   : '?:';
+METHOD_POINTER          : '.&';
+METHOD_REFERENCE        : '::';
+REGEX_FIND              : '=~';
+REGEX_MATCH             : '==~';
+POWER                   : '**';
+POWER_ASSIGN            : '**=';
+SPACESHIP               : '<=>';
+IDENTICAL               : '===';
+NOT_IDENTICAL           : '!==';
+ARROW                   : '->';
 
 // !internalPromise will be parsed as !in ternalPromise, so semantic predicates are necessary
 NOT_INSTANCEOF      : '!instanceof' { isFollowedBy(_input, ' ', '\t', '\r', '\n') }?;
diff --git a/src/antlr/GroovyParser.g4 b/src/antlr/GroovyParser.g4
index 16024a6..bff566b 100644
--- a/src/antlr/GroovyParser.g4
+++ b/src/antlr/GroovyParser.g4
@@ -766,7 +766,9 @@ expression
                         |   dgOp=GT GT
                         )
             |   rangeOp=(    RANGE_INCLUSIVE
-                        |    RANGE_EXCLUSIVE
+                        |    RANGE_EXCLUSIVE_LEFT
+                        |    RANGE_EXCLUSIVE_RIGHT
+                        |    RANGE_EXCLUSIVE_FULL
                         )
             ) nls
         right=expression                                                                    #shiftExprAlt
diff --git a/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java b/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
index 9ede47f..1fe7595 100644
--- a/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
+++ b/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
@@ -330,7 +330,9 @@ import static org.apache.groovy.parser.antlr4.GroovyParser.LT;
 import static org.apache.groovy.parser.antlr4.GroovyParser.NOT_IN;
 import static org.apache.groovy.parser.antlr4.GroovyParser.NOT_INSTANCEOF;
 import static org.apache.groovy.parser.antlr4.GroovyParser.PRIVATE;
-import static org.apache.groovy.parser.antlr4.GroovyParser.RANGE_EXCLUSIVE;
+import static org.apache.groovy.parser.antlr4.GroovyParser.RANGE_EXCLUSIVE_FULL;
+import static org.apache.groovy.parser.antlr4.GroovyParser.RANGE_EXCLUSIVE_LEFT;
+import static org.apache.groovy.parser.antlr4.GroovyParser.RANGE_EXCLUSIVE_RIGHT;
 import static org.apache.groovy.parser.antlr4.GroovyParser.RANGE_INCLUSIVE;
 import static org.apache.groovy.parser.antlr4.GroovyParser.SAFE_INDEX;
 import static org.apache.groovy.parser.antlr4.GroovyParser.STATIC;
@@ -2870,7 +2872,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
         Expression right = (Expression) this.visit(ctx.right);
 
         if (asBoolean(ctx.rangeOp)) {
-            return configureAST(new RangeExpression(left, right, !ctx.rangeOp.getText().endsWith("<")), ctx);
+            return configureAST(new RangeExpression(left, right, ctx.rangeOp.getText().startsWith("<"), ctx.rangeOp.getText().endsWith("<")), ctx);
         }
 
         org.codehaus.groovy.syntax.Token op;
@@ -4473,7 +4475,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
         int tokenType = token.getType();
         String text = 1 == cardinality ? tokenText : StringGroovyMethods.multiply(tokenText, cardinality);
         return new org.codehaus.groovy.syntax.Token(
-                RANGE_EXCLUSIVE == tokenType || RANGE_INCLUSIVE == tokenType
+                RANGE_EXCLUSIVE_FULL == tokenType || RANGE_EXCLUSIVE_LEFT == tokenType || RANGE_EXCLUSIVE_RIGHT == tokenType || RANGE_INCLUSIVE == tokenType
                         ? Types.RANGE_OPERATOR
                         : SAFE_INDEX == tokenType
                         ? Types.LEFT_SQUARE_BRACKET
diff --git a/src/main/java/org/codehaus/groovy/ast/expr/RangeExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/RangeExpression.java
index 04a4923..a03cecf 100644
--- a/src/main/java/org/codehaus/groovy/ast/expr/RangeExpression.java
+++ b/src/main/java/org/codehaus/groovy/ast/expr/RangeExpression.java
@@ -27,18 +27,34 @@ import org.codehaus.groovy.ast.GroovyCodeVisitor;
  * <pre>for i in 0..10 {...}</pre>
  */
 public class RangeExpression extends Expression {
-
     private final Expression from;
     private final Expression to;
-    private final boolean inclusive;
+    private final boolean inclusive; // Kept to keep old code depending on this working
+    // GROOVY-9649
+    private final boolean exclusiveLeft;
+    private final boolean exclusiveRight;
 
+    // Kept until sure this can be removed
     public RangeExpression(Expression from, Expression to, boolean inclusive) {
         this.from = from;
         this.to = to;
         this.inclusive = inclusive;
+        this.exclusiveLeft = false;
+        this.exclusiveRight = !inclusive;
+        setType(ClassHelper.RANGE_TYPE);
+    }
+
+    // GROOVY-9649
+    public RangeExpression(Expression from, Expression to, boolean exclusiveLeft, boolean exclusiveRight) {
+        this.from = from;
+        this.to = to;
+        this.inclusive = !exclusiveRight; // Old code depends on this
+        this.exclusiveLeft = exclusiveLeft;
+        this.exclusiveRight = exclusiveRight;
         setType(ClassHelper.RANGE_TYPE);
     }
 
+
     @Override
     public void visit(GroovyCodeVisitor visitor) {
         visitor.visitRangeExpression(this);
@@ -46,7 +62,7 @@ public class RangeExpression extends Expression {
 
     @Override
     public Expression transformExpression(ExpressionTransformer transformer) {
-        Expression ret = new RangeExpression(transformer.transform(from), transformer.transform(to), inclusive);
+        Expression ret = new RangeExpression(transformer.transform(from), transformer.transform(to), exclusiveLeft, exclusiveRight);
         ret.setSourcePosition(this);
         ret.copyNodeMetaData(this);
         return ret;
@@ -64,10 +80,20 @@ public class RangeExpression extends Expression {
         return inclusive;
     }
 
+    public boolean isExclusiveLeft() {
+        return exclusiveLeft;
+    }
+
+    public boolean isExclusiveRight() {
+        return exclusiveRight;
+    }
+
     @Override
     public String getText() {
         return "(" + from.getText() +
-               (!isInclusive()? "..<" : ".." ) +
-               to.getText() + ")";
+                (this.exclusiveLeft ? "<" : "") +
+                ".." +
+                (this.exclusiveRight ? "<" : "") +
+                to.getText() + ")";
     }
 }
diff --git a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/completion/antlr4/ReflectionCompleter.groovy b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/completion/antlr4/ReflectionCompleter.groovy
index 74b182a..610e279 100644
--- a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/completion/antlr4/ReflectionCompleter.groovy
+++ b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/completion/antlr4/ReflectionCompleter.groovy
@@ -69,7 +69,9 @@ import static org.apache.groovy.parser.antlr4.GroovyLexer.NOT
 import static org.apache.groovy.parser.antlr4.GroovyLexer.NOTEQUAL
 import static org.apache.groovy.parser.antlr4.GroovyLexer.OR
 import static org.apache.groovy.parser.antlr4.GroovyLexer.OR_ASSIGN
-import static org.apache.groovy.parser.antlr4.GroovyLexer.RANGE_EXCLUSIVE
+import static org.apache.groovy.parser.antlr4.GroovyLexer.RANGE_EXCLUSIVE_FULL
+import static org.apache.groovy.parser.antlr4.GroovyLexer.RANGE_EXCLUSIVE_LEFT
+import static org.apache.groovy.parser.antlr4.GroovyLexer.RANGE_EXCLUSIVE_RIGHT
 import static org.apache.groovy.parser.antlr4.GroovyLexer.RANGE_INCLUSIVE
 import static org.apache.groovy.parser.antlr4.GroovyLexer.RBRACK
 import static org.apache.groovy.parser.antlr4.GroovyLexer.RPAREN
@@ -349,7 +351,9 @@ class ReflectionCompleter {
                     break
             // may begin expression when outside brackets (from back)
                 case RANGE_INCLUSIVE:
-                case RANGE_EXCLUSIVE:
+                case RANGE_EXCLUSIVE_LEFT:
+                case RANGE_EXCLUSIVE_RIGHT:
+                case RANGE_EXCLUSIVE_FULL:
                 case COLON:
                 case COMMA:
                     if (expectedOpeners.empty()) {

[groovy] 17/25: GROOVY-9649: Amended documentation for IntRange

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit c1d2c7fb59d8b147a2b7121087b741daf90733c6
Author: Eerik Voimanen <ee...@tuni.fi>
AuthorDate: Wed Apr 7 19:37:01 2021 +0300

    GROOVY-9649: Amended documentation for IntRange
---
 src/main/java/groovy/lang/IntRange.java | 46 ++++++++++++++++++++++++---------
 1 file changed, 34 insertions(+), 12 deletions(-)

diff --git a/src/main/java/groovy/lang/IntRange.java b/src/main/java/groovy/lang/IntRange.java
index 7a701ad..aeb97fe 100644
--- a/src/main/java/groovy/lang/IntRange.java
+++ b/src/main/java/groovy/lang/IntRange.java
@@ -31,8 +31,8 @@ import java.util.NoSuchElementException;
 import java.util.Objects;
 
 /**
- * Represents a list of Integer objects starting at a specified {@code from} value up (or down)
- * to and potentially including a given {@code to} value.
+ * Represents a list of Integer objects starting at and potentially including a specified
+ * {@code from} value up (or down) to and potentially including a given {@code to} value.
  * <p>
  * Instances of this class may be either inclusive aware or non-inclusive aware. See the
  * relevant constructors for creating each type. Inclusive aware IntRange instances are
@@ -40,14 +40,15 @@ import java.util.Objects;
  * might be negative. This normally happens underneath the covers but is worth keeping
  * in mind if creating these ranges yourself explicitly.
  * <p>
- * Note: the design of this class might seem a little strange at first. It contains a Boolean
- * field, {@code inclusive}, which can be {@code true}, {@code false} or {@code null}. This
- * design is for backwards compatibility reasons. Groovy uses this class under the covers
- * to represent range indexing, e.g. {@code someList[x..y]} and {@code someString[x..<y]}.
- * In early versions of Groovy the ranges in these expressions were represented under the
- * covers by the {@code new IntRange(x, y)} and {@code new IntRange(x, y-1)}. This turns
- * out to be a lossy abstraction when x and/or y are negative values. Now the latter case
- * is represented by {@code new IntRange(false, x, y)}.
+ * Note: the design of this class might seem a little strange at first. It contains Boolean
+ * flags, {@code inclusiveLeft} and {@code inclusiveRight}, which can be {@code true},
+ * {@code false} or {@code null}. This design is for backwards compatibility reasons.
+ * Groovy uses this class under the covers to represent range indexing, e.g.
+ * {@code someList[x..y]} and {@code someString[x..<y]}. In early versions of Groovy the
+ * ranges in these expressions were represented under the covers by the
+ * {@code new IntRange(x, y)} and {@code new IntRange(x, y-1)}. This turns out to be a
+ * lossy abstraction when x and/or y are negative values. Now the latter case is
+ * represented by {@code new IntRange(false, x, y)}.
  * <p>
  * Note: This class is a copy of {@link ObjectRange} optimized for <code>int</code>. If you make any
  * changes to this class, you might consider making parallel changes to {@link ObjectRange}.
@@ -134,6 +135,14 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
      */
     private final Boolean inclusiveRight;
 
+    /**
+     * If <code>true</code> or null, <code>from</code> is included in the range.
+     * If <code>false</code>, the range begins after the <code>from</code> value.
+     * <p>
+     * Null for non-inclusive-aware ranges (which are inclusive).
+     * <p>
+     * If true or false, the reverse flag is discarded.
+     */
     private final Boolean inclusiveLeft;
 
     /**
@@ -192,6 +201,14 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
         this(true, inclusiveRight, from, to);
     }
 
+    /**
+     * Creates a new inclusive aware <code>IntRange</code>
+     *
+     * @param inclusiveLeft     <code>true</code> if the from value is included in the range.
+     * @param inclusiveRight    <code>true</code> if the to value is included in the range.
+     * @param from              the first value in the range.
+     * @param to                the last value in the range.
+     */
     public IntRange(boolean inclusiveLeft, boolean inclusiveRight, int from, int to) {
         this.from = from;
         this.to = to;
@@ -210,7 +227,6 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
      * @since 2.5.0
      */
     public <T extends Number & Comparable> NumberRange by(T stepSize) {
-        // TODO edit NumberRange
         return new NumberRange(NumberRange.comparableNumber((Number)from), NumberRange.comparableNumber((Number)to), stepSize, inclusiveRight);
     }
 
@@ -307,16 +323,22 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
     }
 
     /**
-     * Returns the inclusive flag. Null for non-inclusive aware ranges or non-null for inclusive aware ranges.
+     * Returns the same as <code>getInclusiveRight</code>, kept here for backwards compatibility.
      */
     public Boolean getInclusive() {
         return inclusiveRight;
     }
 
+    /**
+     * Returns the inclusiveRight flag. Null for non-inclusive aware ranges or non-null for inclusive aware ranges.
+     */
     public Boolean getInclusiveRight() {
         return inclusiveRight;
     }
 
+    /**
+     * Returns the inclusiveLeft flag. Null for non-inclusive aware ranges or non-null for inclusive aware ranges.
+     */
     public Boolean getInclusiveLeft() {
         return inclusiveLeft;
     }

[groovy] 21/25: GROOVY-9649: Sonar refactoring

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 8aa8cbab344362025288910d9b86cc270cbb7063
Author: Otto Vayrynen <ot...@tuni.fi>
AuthorDate: Fri Apr 9 12:49:37 2021 +0300

    GROOVY-9649: Sonar refactoring
    
    Refactor getInclusive and toString ternary operations
---
 src/main/java/groovy/lang/IntRange.java | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/main/java/groovy/lang/IntRange.java b/src/main/java/groovy/lang/IntRange.java
index aeb97fe..a605a4b 100644
--- a/src/main/java/groovy/lang/IntRange.java
+++ b/src/main/java/groovy/lang/IntRange.java
@@ -326,7 +326,7 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
      * Returns the same as <code>getInclusiveRight</code>, kept here for backwards compatibility.
      */
     public Boolean getInclusive() {
-        return inclusiveRight;
+        return getInclusiveRight();
     }
 
     /**
@@ -414,9 +414,11 @@ public class IntRange extends AbstractList<Integer> implements Range<Integer>, S
 
     @Override
     public String toString() {
-        return (inclusiveRight == null && inclusiveLeft == null) ? (reverse ? "" + to + ".." + from : "" + from + ".." + to)
-                : ("" + from + ((inclusiveLeft != null && inclusiveLeft) ? "" : "<") + ".."
-                             + ((inclusiveRight != null && inclusiveRight) ? "" : "<") + to);
+        if (inclusiveRight == null && inclusiveLeft == null)  {
+               return reverse ? "" + to + ".." + from : "" + from + ".." + to;
+        }
+        return "" + from + ((inclusiveLeft != null && inclusiveLeft) ? "" : "<") + ".."
+                  + ((inclusiveRight != null && inclusiveRight) ? "" : "<") + to;
     }
 
     @Override

[groovy] 02/25: GROOVY-9649: Rework range creation to also allow left- and full-open ranges

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 75b71337571bcf3517aec7b3a89b3603fac1d329
Author: Esko Toivonen <es...@tuni.fi>
AuthorDate: Tue Mar 30 11:12:26 2021 +0300

    GROOVY-9649: Rework range creation to also allow left- and full-open ranges
    
    This commit also adds a new parameter to MethodCaller which is used in
    AsmClassGenerator to access the createRange method. This new parameter is needed
    because the three-parameter version has to be left in for backwards
    compatibility, and without the additional parameter in MethodCaller the wrong
    method would be found.
---
 .../groovy/classgen/AsmClassGenerator.java         |  9 ++++---
 .../codehaus/groovy/classgen/asm/MethodCaller.java | 29 ++++++++++++++++++----
 .../org/codehaus/groovy/runtime/InvokerHelper.java |  9 +++++--
 .../groovy/runtime/ScriptBytecodeAdapter.java      |  8 +++++-
 4 files changed, 44 insertions(+), 11 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
index bfbd856..be5047d 100644
--- a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
+++ b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
@@ -216,7 +216,9 @@ public class AsmClassGenerator extends ClassGenerator {
     // type conversions
     private static final MethodCaller createMapMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createMap");
     private static final MethodCaller createListMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createList");
-    private static final MethodCaller createRangeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createRange");
+    // The 3-parameter version of createRange is kept in for backwards compatibility, so we need to specify the
+    // parameter count here
+    private static final MethodCaller createRangeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createRange", 4);
     private static final MethodCaller createPojoWrapperMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createPojoWrapper");
     private static final MethodCaller createGroovyObjectWrapperMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createGroovyObjectWrapper");
 
@@ -1526,10 +1528,11 @@ public class AsmClassGenerator extends ClassGenerator {
         operandStack.box();
         expression.getTo().visit(this);
         operandStack.box();
-        operandStack.pushBool(expression.isInclusive());
+        operandStack.pushBool(expression.isExclusiveLeft());
+        operandStack.pushBool(expression.isExclusiveRight());
 
         createRangeMethod.call(controller.getMethodVisitor());
-        operandStack.replace(ClassHelper.RANGE_TYPE, 3);
+        operandStack.replace(ClassHelper.RANGE_TYPE, 4);
     }
 
     @Override
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/MethodCaller.java b/src/main/java/org/codehaus/groovy/classgen/asm/MethodCaller.java
index 91a5988..faf3390 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/MethodCaller.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/MethodCaller.java
@@ -38,11 +38,17 @@ public class MethodCaller {
     private String name;
     private Class theClass;
     private String methodDescriptor;
+    private int parameterCount;
+    private static final int ANY_PARAMETER_COUNT = -1;
 
     public static MethodCaller newStatic(Class theClass, String name) {
         return new MethodCaller(INVOKESTATIC, theClass, name);
     }
 
+    public static MethodCaller newStatic(Class theClass, String name, int parameterCount) {
+        return new MethodCaller(INVOKESTATIC, theClass, name, parameterCount);
+    }
+
     public static MethodCaller newInterface(Class theClass, String name) {
         return new MethodCaller(INVOKEINTERFACE, theClass, name);
     }
@@ -57,11 +63,15 @@ public class MethodCaller {
     protected MethodCaller() {}
 
     public MethodCaller(int opcode, Class theClass, String name) {
+        this(opcode, theClass, name, ANY_PARAMETER_COUNT);
+    }
+
+    public MethodCaller(int opcode, Class theClass, String name, int parameterCount) {
         this.opcode = opcode;
         this.internalName = Type.getInternalName(theClass);
         this.theClass = theClass;
         this.name = name;
-
+        this.parameterCount = parameterCount;
     }
 
     public void call(MethodVisitor methodVisitor) {
@@ -78,11 +88,20 @@ public class MethodCaller {
 
     protected Method getMethod() {
         Method[] methods = theClass.getMethods();
-        for (Method method : methods) {
-            if (method.getName().equals(name)) {
-                return method;
+        if (parameterCount != ANY_PARAMETER_COUNT) {
+            for (Method method : methods) {
+                if (method.getName().equals(name) && method.getParameterCount() == parameterCount) {
+                    return method;
+                }
+            }
+        } else {
+            for (Method method : methods) {
+                if (method.getName().equals(name)) {
+                    return method;
+                }
             }
         }
-        throw new ClassGeneratorException("Could not find method: " + name + " on class: " + theClass);
+        throw new ClassGeneratorException("Could not find method: " + name +
+                (parameterCount >= 0 ? " with parameter count " + parameterCount : "") + " on class: " + theClass);
     }
 }
diff --git a/src/main/java/org/codehaus/groovy/runtime/InvokerHelper.java b/src/main/java/org/codehaus/groovy/runtime/InvokerHelper.java
index 82a8868..6d64276 100644
--- a/src/main/java/org/codehaus/groovy/runtime/InvokerHelper.java
+++ b/src/main/java/org/codehaus/groovy/runtime/InvokerHelper.java
@@ -933,9 +933,9 @@ public class InvokerHelper {
         return toArrayString(arguments, false, maxSize, safe);
     }
 
-    public static List createRange(Object from, Object to, boolean inclusive) {
+    public static List createRange(Object from, Object to, boolean exclusiveLeft, boolean exclusiveRight) {
         try {
-            return ScriptBytecodeAdapter.createRange(from, to, inclusive);
+            return ScriptBytecodeAdapter.createRange(from, to, exclusiveLeft, exclusiveRight);
         } catch (RuntimeException | Error re) {
             throw re;
         } catch (Throwable t) {
@@ -943,6 +943,11 @@ public class InvokerHelper {
         }
     }
 
+    // Kept in for backwards compatibility
+    public static List createRange(Object from, Object to, boolean inclusive) {
+        return createRange(from, to, false, !inclusive);
+    }
+
     public static Object bitwiseNegate(Object value) {
         if (value instanceof Integer) {
             Integer number = (Integer) value;
diff --git a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
index 69cd50f..e02bc62 100644
--- a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
+++ b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
@@ -635,7 +635,8 @@ public class ScriptBytecodeAdapter {
         return InvokerHelper.createMap(values);
     }
 
-    public static List createRange(Object from, Object to, boolean inclusive) throws Throwable {
+    public static List createRange(Object from, Object to, boolean exclusiveLeft, boolean exclusiveRight) throws Throwable {
+        boolean inclusive = !exclusiveRight;
         if (from instanceof Integer && to instanceof Integer) {
             int ifrom = (Integer) from;
             int ito = (Integer) to;
@@ -660,6 +661,11 @@ public class ScriptBytecodeAdapter {
         return new ObjectRange((Comparable) from, (Comparable) to);
     }
 
+    // Kept in for backwards compatibility
+    public static List createRange(Object from, Object to, boolean inclusive) throws Throwable {
+        return createRange(from, to, false, !inclusive);
+    }
+
     @SuppressWarnings("unchecked")
     private static <T extends Number & Comparable> T comparableNumber(Number n) {
         return (T) n;

[groovy] 13/25: GROOVY-9649: Make createRange create EmptyRanges when from != to

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit cca4515dc11ab13a8225f010cc7bc83a403b4e53
Author: Esko Toivonen <es...@tuni.fi>
AuthorDate: Wed Apr 7 17:48:08 2021 +0300

    GROOVY-9649: Make createRange create EmptyRanges when from != to
    
    Ranges like 0<..<1 should be EmptyRanges. One complicating fact is that
    ranges like 0<..<-1 can be used for list indexing, thus only ranges
    where from and to share the same sign (zeros and positives are
    considered equal) and their difference is one are treated as EmptyRanges.
---
 .../groovy/runtime/ScriptBytecodeAdapter.java      | 47 ++++++++++++++--------
 1 file changed, 31 insertions(+), 16 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
index 5161f65..fdd593d 100644
--- a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
+++ b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
@@ -636,38 +636,53 @@ public class ScriptBytecodeAdapter {
     }
 
     public static List createRange(Object from, Object to, boolean exclusiveLeft, boolean exclusiveRight) throws Throwable {
+        if (exclusiveLeft && exclusiveRight) {
+            if (compareEqual(from, to)) {
+                return new EmptyRange((Comparable) from);
+            }
+            Object tmpFrom;
+            if (compareLessThan(from, to)) {
+                tmpFrom = invokeMethod0(ScriptBytecodeAdapter.class, from, "next");
+            } else {
+                tmpFrom = invokeMethod0(ScriptBytecodeAdapter.class, from, "previous");
+            }
+            // Create an empty range if the difference between from and to is one and they have the same sign. This
+            // means that range syntaxes like 5<..<6 will result in an empty range, but 0<..<-1 won't, since the latter
+            // is used in list indexing where negative indices count from the end towards the beginning. Note that
+            // positive numbers and zeros are considered to have the same sign to make ranges like 0<..<1 be EmptyRanges
+            int fromComp = compareTo(from, 0);
+            int toComp = compareTo(to, 0);
+            boolean sameSign = (fromComp >= 0 && toComp >= 0) || (fromComp < 0 && toComp < 0);
+            if (compareEqual(tmpFrom, to) && sameSign) {
+                return new EmptyRange((Comparable) from);
+            }
+        }
+        if ((exclusiveLeft || exclusiveRight) && compareEqual(from, to)) {
+            return new EmptyRange((Comparable) from);
+        }
         if (from instanceof Integer && to instanceof Integer) {
             int ifrom = (Integer) from;
             int ito = (Integer) to;
             if ((!exclusiveLeft && !exclusiveRight) || ifrom != ito) {
-                // Currently, empty ranges where from != to and the range is full exclusive (e.g. 0<..<-1) are
-                // constructed as IntRanges. This is because these ranges can still be used to index into lists.
+                // Currently, empty ranges where from != to, the range is full exclusive (e.g. 0<..<-1) and from and to
+                // have a different sign are constructed as IntRanges. This is because these ranges can still be used to
+                // index into lists.
                 return new IntRange(!exclusiveLeft, !exclusiveRight, ifrom, ito);
-            } // else fall through for EmptyRange
-        }
-        if ((exclusiveLeft || exclusiveRight) && compareEqual(from, to)) {
-            return new EmptyRange((Comparable) from);
+            }
         }
         if (from instanceof Number && to instanceof Number) {
             return new NumberRange(comparableNumber((Number) from), comparableNumber((Number) to), !exclusiveLeft, !exclusiveRight);
         }
-        Boolean greater = null;
+        // ObjectRange does not include information about inclusivity, so we need to consider it here
         if (exclusiveRight) {
-            greater = compareGreaterThan(from, to);
-            if (greater) {
+            if (compareGreaterThan(from, to)) {
                 to = invokeMethod0(ScriptBytecodeAdapter.class, to, "next");
             } else {
                 to = invokeMethod0(ScriptBytecodeAdapter.class, to, "previous");
             }
         }
         if (exclusiveLeft) {
-            if (greater == null) {
-                greater = compareGreaterThan(from, to);
-            }
-            if (compareEqual(from, to)) {
-                return new EmptyRange((Comparable) from);
-            }
-            if (greater) {
+            if (compareGreaterThan(from, to)) {
                 from = invokeMethod0(ScriptBytecodeAdapter.class, from, "previous");
             } else {
                 from = invokeMethod0(ScriptBytecodeAdapter.class, from, "next");

[groovy] 03/25: GROOVY-9649: Added tests for left and full-exclusive range operator

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 5983f66eca55956cc67f9c7993d92e61313a5bbe
Author: Esko Toivonen <es...@tuni.fi>
AuthorDate: Wed Mar 31 16:27:12 2021 +0300

    GROOVY-9649: Added tests for left and full-exclusive range operator
---
 src/spec/test/OperatorsTest.groovy                 |   4 +-
 src/test/groovy/ListTest.groovy                    |  16 +++-
 src/test/groovy/RangeTest.groovy                   | 106 +++++++++++++++++++++
 .../powerassert/AssertionRenderingTest.groovy      |  20 ++++
 .../runtime/powerassert/EvaluationTest.groovy      |   2 +
 5 files changed, 143 insertions(+), 5 deletions(-)

diff --git a/src/spec/test/OperatorsTest.groovy b/src/spec/test/OperatorsTest.groovy
index 0393a47..3f9746c 100644
--- a/src/spec/test/OperatorsTest.groovy
+++ b/src/spec/test/OperatorsTest.groovy
@@ -543,6 +543,8 @@ assert function(*args,5,6) == 26
         def range = 0..5                                    // <1>
         assert (0..5).collect() == [0, 1, 2, 3, 4, 5]       // <2>
         assert (0..<5).collect() == [0, 1, 2, 3, 4]         // <3>
+        assert (0<..5).collect() == [1, 2, 3, 4, 5]
+        assert (0<..<5).collect() == [1, 2, 3, 4]
         assert (0..5) instanceof List                       // <4>
         assert (0..5).size() == 6                           // <5>
         // end::intrange[]
@@ -781,4 +783,4 @@ assert !(falseValue2 ^= false)
 assert !(falseValue3 ^= null)
 '''
     }
-}
\ No newline at end of file
+}
diff --git a/src/test/groovy/ListTest.groovy b/src/test/groovy/ListTest.groovy
index 5e3860d..8a92f54 100644
--- a/src/test/groovy/ListTest.groovy
+++ b/src/test/groovy/ListTest.groovy
@@ -312,13 +312,21 @@ class ListTest extends GroovyTestCase {
         assert list[0..0] == [0]          , 'one element range'
         assert list[0..<0] == []          , 'empty range'
         assert list[3..0] == [3, 2, 1, 0] , 'reverse range'
-        assert list[3..<0] == [3, 2, 1]   , 'reverse exclusive range'
+        assert list[3..<0] == [3, 2, 1]   , 'reverse right exclusive range'
+        assert list[3<..0] == [2, 1, 0]   , 'reverse left exclusive range'
+        assert list[3<..<0] == [2, 1]     , 'reverse full exclusive range'
         assert list[-2..-1] == [2, 3]     , 'negative index range'
-        assert list[-2..<-1] == [2]       , 'negative index range exclusive'
+        assert list[-2..<-1] == [2]       , 'negative index range right exclusive'
+        assert list[-2<..-1] == [3]       , 'negative index range left exclusive'
+        assert list[-2<..<-1] == []       , 'negative index range full exclusive'
         assert list[-1..-2] == [3, 2]     , 'negative index range reversed'
-        assert list[-1..<-2] == [3]       , 'negative index range reversed exclusive'  // aaaahhhhh !
+        assert list[-1..<-2] == [3]       , 'negative index range reversed right exclusive'
+        assert list[-1<..-2] == [2]       , 'negative index range reversed left exclusive'
+        assert list[-1<..<-2] == []       , 'negative index range reversed full exclusive'  // aaaaaaahhhhh !
         assert list[0..-1] == list        , 'pos - neg value'
-        assert list[0..<-1] == [0, 1, 2]  , 'pos - neg value exclusive'
+        assert list[0..<-1] == [0, 1, 2]  , 'pos - neg value right exclusive'
+        assert list[0<..-1] == [1, 2, 3]  , 'pos - neg value left exclusive'
+        assert list[0<..<-1] == [1, 2]    , 'pos - neg value full exclusive'
         assert list[0..<-2] == [0, 1]     , 'pos - neg value exclusive'
         shouldFail(GroovyRuntimeException) { list[null] }
         shouldFail(IndexOutOfBoundsException) { list[5..6] }
diff --git a/src/test/groovy/RangeTest.groovy b/src/test/groovy/RangeTest.groovy
index dc2cb45..82798f9 100644
--- a/src/test/groovy/RangeTest.groovy
+++ b/src/test/groovy/RangeTest.groovy
@@ -36,6 +36,18 @@ class RangeTest extends GroovyTestCase {
         assert x == 45
 
         x = 0
+        for (i in 1<..10) {
+            x = x + i
+        }
+        assert x == 54
+
+        x = 0
+        for (i in 1<..<10) {
+            x = x + i
+        }
+        assert x == 44
+
+        x = 0
         for (i in 0..'\u0009') {
             x = x + i
         }
@@ -54,13 +66,29 @@ class RangeTest extends GroovyTestCase {
             x = x + it
         }
         assert x == 45
+
+        x = 0
+        (1<..10).each {
+            x = x + it
+        }
+        assert x == 54
+
+        x = 0
+        (1<..<10).each {
+            x = x + it
+        }
+        assert x == 44
     }
 
     void testIntStep() {
         assertStep(0..9, 3, [0, 3, 6, 9])
         assertStep(0..<10, 3, [0, 3, 6, 9])
+        assertStep(0<..10, 3, [1, 4, 7, 10])
+        assertStep(0<..<10, 3, [1, 4, 7])
         assertStep(9..0, 3, [9, 6, 3, 0])
         assertStep(9..<0, 3, [9, 6, 3])
+        assertStep(9<..-1, 3, [8, 5, 2, -1])
+        assertStep(9<..<-1, 3, [8, 5, 2])
     }
 
 
@@ -75,8 +103,12 @@ class RangeTest extends GroovyTestCase {
         assertStep('a'..'f', 2, ['a', 'c', 'e'])
         assertStep('f'..'a', 2, ['f', 'd', 'b'])
         assertStep('a'..<'e', 2, ['a', 'c'])
+        assertStep('a'<..'f', 2, ['b', 'd', 'f'])
+        assertStep('a'<..<'f', 2, ['b', 'd'])
         assertStep('z'..'v', 2, ['z', 'x', 'v'])
         assertStep('z'..<'v', 2, ['z', 'x'])
+        assertStep('z'<..'u', 2, ['y', 'w', 'u'])
+        assertStep('z'<..<'u', 2, ['y', 'w'])
     }
 
     void testNegativeObjectStep() {
@@ -87,15 +119,23 @@ class RangeTest extends GroovyTestCase {
     void testIterateIntRange() {
         assertIterate(0..9, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
         assertIterate(1..<8, [1, 2, 3, 4, 5, 6, 7])
+        assertIterate(1<..8, [2, 3, 4, 5, 6, 7, 8])
+        assertIterate(1<..<8, [2, 3, 4, 5, 6, 7])
         assertIterate(7..1, [7, 6, 5, 4, 3, 2, 1])
         assertIterate(6..<1, [6, 5, 4, 3, 2])
+        assertIterate(6<..1, [5, 4, 3, 2, 1])
+        assertIterate(6<..<1, [5, 4, 3, 2])
     }
 
     void testIterateObjectRange() {
         assertIterate('a'..'d', ['a', 'b', 'c', 'd'])
         assertIterate('a'..<'d', ['a', 'b', 'c'])
+        assertIterate('a'<..'d', ['b', 'c', 'd'])
+        assertIterate('a'<..<'d', ['b', 'c'])
         assertIterate('z'..'x', ['z', 'y', 'x'])
         assertIterate('z'..<'x', ['z', 'y'])
+        assertIterate('z'<..'x', ['y', 'x'])
+        assertIterate('z'<..<'x', ['y'])
     }
 
     enum RomanNumber {
@@ -146,6 +186,14 @@ class RangeTest extends GroovyTestCase {
         range = 0..<5
         assert range.contains(0) && 0 in range
         assert !range.contains(5) && !(5 in range)
+
+        range = 0<..5
+        assert !range.contains(0) && !(0 in range)
+        assert range.contains(5) && 5 in range
+
+        range = 0<..<5
+        assert !range.contains(0) && !(0 in range)
+        assert !range.contains(5) && !(5 in range)
     }
 
     void testBackwardsRangeContains() {
@@ -156,6 +204,14 @@ class RangeTest extends GroovyTestCase {
         range = 5..<1
         assert range.contains(5) && 5 in range
         assert !range.contains(1) && !(1 in range)
+
+        range = 5<..1
+        assert !range.contains(5) && !(5 in range)
+        assert range.contains(1) && 1 in range
+
+        range = 5<..<1
+        assert !range.contains(5) && !(5 in range)
+        assert !range.contains(1) && !(1 in range)
     }
 
     void testObjectRangeContains() {
@@ -169,6 +225,19 @@ class RangeTest extends GroovyTestCase {
         assert !range.contains('g')
         assert !range.contains('f')
         assert !range.contains('a')
+
+        range = 'b'<..'f'
+        assert range.contains('f')
+        assert !range.contains('b')
+        assert !range.contains('g')
+        assert !range.contains('a')
+
+        range = 'b'<..<'f'
+        assert !range.contains('b')
+        assert !range.contains('f')
+        assert !range.contains('a')
+        assert !range.contains('g')
+        assert range.contains('c')
     }
 
     void testBackwardsObjectRangeContains() {
@@ -182,6 +251,19 @@ class RangeTest extends GroovyTestCase {
         assert range.contains('f')
         assert range.contains('c')
         assert !range.contains('b')
+
+        range = 'f'<..'b'
+        assert !range.contains('f')
+        assert range.contains('b')
+        assert !range.contains('g')
+        assert !range.contains('a')
+
+        range = 'f'<..<'b'
+        assert !range.contains('b')
+        assert !range.contains('f')
+        assert !range.contains('a')
+        assert !range.contains('g')
+        assert range.contains('c')
     }
 
     void testIntRangeToString() {
@@ -191,25 +273,47 @@ class RangeTest extends GroovyTestCase {
         assertToString(0..<11, "0..<11")
         assertToString([1, 4..<11, 9], "[1, 4..<11, 9]")
 
+        assertToString(0<..11, "0<..11")
+        assertToString([1, 4<..11, 9], "[1, 4<..11, 9]")
+
+        assertToString(0<..<11, "0<..<11")
+        assertToString([1, 4<..<11, 9], "[1, 4<..<11, 9]")
+
         assertToString(10..0, "10..0")
         assertToString([1, 10..4, 9], "[1, 10..4, 9]")
 
         assertToString(11..<0, "11..<0")
         assertToString([1, 11..<4, 9], "[1, 11..<4, 9]")
+
+        assertToString(11<..0, "11<..0")
+        assertToString([1, 11<..4, 9], "[1, 11<..4, 9]")
+
+        assertToString(11<..<0, "11<..<0")
+        assertToString([1, 11<..<4, 9], "[1, 11<..<4, 9]")
     }
 
     void testObjectRangeToString() {
         assertToString('a'..'d', 'a..d', "'a'..'d'")
         assertToString('a'..<'d', 'a..c', "'a'..'c'")
+        assertToString('a'<..'d', 'b..d', "'b'..'d'")
+        assertToString('a'<..<'d', 'b..c', "'b'..'c'")
+
         assertToString('z'..'x', 'z..x', "'z'..'x'")
         assertToString('z'..<'x', 'z..y', "'z'..'y'")
+        assertToString('z'<..'x', 'y..x', "'y'..'x'")
+        assertToString('z'<..<'x', 'y..y', "'y'..'y'")
     }
 
     void testRangeSize() {
         assertSize(1..10, 10)
         assertSize(11..<21, 10)
+        assertSize(11<..21, 10)
+        assertSize(11<..<22, 10)
+
         assertSize(30..21, 10)
         assertSize(40..<30, 10)
+        assertSize(40<..30, 10)
+        assertSize(41<..<30, 10)
     }
 
     void testBorderCases() {
@@ -217,6 +321,8 @@ class RangeTest extends GroovyTestCase {
         assertIterate(0..0, [0])
         assertIterate(0..-1, [0, -1])
         assertIterate(0..<-1, [0])
+        assertIterate(0<..-1, [-1])
+        assertIterate(0<..<-1, [])
     }
 
     void testEmptyRanges() {
diff --git a/src/test/org/codehaus/groovy/runtime/powerassert/AssertionRenderingTest.groovy b/src/test/org/codehaus/groovy/runtime/powerassert/AssertionRenderingTest.groovy
index f34d762..ee8a791 100644
--- a/src/test/org/codehaus/groovy/runtime/powerassert/AssertionRenderingTest.groovy
+++ b/src/test/org/codehaus/groovy/runtime/powerassert/AssertionRenderingTest.groovy
@@ -363,6 +363,26 @@ assert (a..<b) == null
             def b = 2
             assert (a..<b) == null
         }
+
+        isRendered '''
+assert (a<..b) == null
+        |   |  |
+        1   2  false
+        ''', { ->
+            def a = 1
+            def b = 2
+            assert (a<..b) == null
+        }
+
+        isRendered '''
+assert (a<..<b) == null
+        |    |  |
+        1    2  false
+        ''', { ->
+            def a = 1
+            def b = 2
+            assert (a<..<b) == null
+        }
     }
 
     @Test
diff --git a/src/test/org/codehaus/groovy/runtime/powerassert/EvaluationTest.groovy b/src/test/org/codehaus/groovy/runtime/powerassert/EvaluationTest.groovy
index 38bee33..945df42 100644
--- a/src/test/org/codehaus/groovy/runtime/powerassert/EvaluationTest.groovy
+++ b/src/test/org/codehaus/groovy/runtime/powerassert/EvaluationTest.groovy
@@ -156,6 +156,8 @@ final class EvaluationTest extends GroovyTestCase {
     void testRangeExpression() {
         assert (1..3).contains(3)
         assert !((1..<3).contains(3))
+        assert !((1<..3).contains(1))
+        assert (!(1<..<3).contains(1) && !(1<..<3).contains(3))
     }
 
     void testPropertyExpression() {

[groovy] 19/25: GROOVY-9649: Add test case for NumberRange size edge cases

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit f364b33791a323f884b82fa4f93ad5c520d421b2
Author: Esko Toivonen <es...@tuni.fi>
AuthorDate: Fri Apr 9 12:00:40 2021 +0300

    GROOVY-9649: Add test case for NumberRange size edge cases
---
 src/test/groovy/lang/NumberRangeTest.groovy | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/src/test/groovy/lang/NumberRangeTest.groovy b/src/test/groovy/lang/NumberRangeTest.groovy
index 89d0ddd..b6f221b 100644
--- a/src/test/groovy/lang/NumberRangeTest.groovy
+++ b/src/test/groovy/lang/NumberRangeTest.groovy
@@ -23,9 +23,8 @@ import junit.framework.TestCase
 /**
  * Provides unit tests for the <code>NumberRange</code> class.
  */
-public class NumberRangeTest extends TestCase {
-
-    public void testStep() {
+class NumberRangeTest extends TestCase {
+    void testStep() {
         Range n = new NumberRange(1, 3)
         assert n.step(1) == [1, 2, 3]
         assert n.size() == 3
@@ -72,4 +71,15 @@ public class NumberRangeTest extends TestCase {
         assert Integer.MAX_VALUE == new NumberRange(new BigInteger("-10"), new BigInteger(Long.toString((long) Integer.MAX_VALUE) + 1L)).size()
     }
 
+    void testSizeEdgeCases() {
+        assert new NumberRange(0, 0, false).size() == 0
+        assert new NumberRange(0, 0, true).size() == 1
+        assert new NumberRange(0, 1, false).size() == 1
+        assert new NumberRange(0, 1, true).size() == 2
+        assert new NumberRange(0, 0, true, true).size() == 1
+        assert new NumberRange(0, 0, false, true).size() == 0
+        assert new NumberRange(0, 1, false, true).size() == 1
+        assert new NumberRange(0, 0, false, false).size() == 0
+        assert new NumberRange(0, 1, false, false).size() == 0
+    }
 }

[groovy] 05/25: GROOVY-9649: Started implementing left- and full-open range support for NumberRange.

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit f571a25c0117247af7bfb96072e69a049af25dc3
Author: Iiro Kiviluoma <ii...@outlook.com>
AuthorDate: Sat Apr 3 15:34:28 2021 +0300

    GROOVY-9649: Started implementing left- and full-open range support for NumberRange.
---
 src/main/java/groovy/lang/NumberRange.java         | 64 +++++++++++++++++++---
 .../groovy/runtime/ScriptBytecodeAdapter.java      |  2 +-
 2 files changed, 57 insertions(+), 9 deletions(-)

diff --git a/src/main/java/groovy/lang/NumberRange.java b/src/main/java/groovy/lang/NumberRange.java
index 11f2ab3..eb41699 100644
--- a/src/main/java/groovy/lang/NumberRange.java
+++ b/src/main/java/groovy/lang/NumberRange.java
@@ -91,6 +91,16 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
     private final boolean inclusive;
 
     /**
+     * <code>true</code> if the range includes the lower bound.
+     */
+    private final boolean inclusiveLeft;
+
+    /**
+     * <code>true</code> if the range includes the upper bound.
+     */
+    private final boolean inclusiveRight;
+
+    /**
      * Creates an inclusive {@link NumberRange} with step size 1.
      * Creates a reversed range if <code>from</code> &lt; <code>to</code>.
      *
@@ -99,7 +109,7 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
      */
     public <T extends Number & Comparable, U extends Number & Comparable>
     NumberRange(T from, U to) {
-        this(from, to, null, true);
+        this(from, to, null, true, true);
     }
 
     /**
@@ -112,7 +122,7 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
      */
     public <T extends Number & Comparable, U extends Number & Comparable>
     NumberRange(T from, U to, boolean inclusive) {
-        this(from, to, null, inclusive);
+        this(from, to, null, true, inclusive);
     }
 
     /**
@@ -126,7 +136,7 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
     public <T extends Number & Comparable, U extends Number & Comparable, V extends
             Number & Comparable<? super Number>>
     NumberRange(T from, U to, V stepSize) {
-        this(from, to, stepSize, true);
+        this(from, to, stepSize, true, true);
     }
 
     /**
@@ -136,11 +146,42 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
      * @param from start of the range
      * @param to   end of the range
      * @param stepSize the gap between discrete elements in the range
-     * @param inclusive whether the range is inclusive
+     * @param inclusive whether the range is inclusive (upper bound)
      */
     public <T extends Number & Comparable, U extends Number & Comparable, V extends
             Number & Comparable>
     NumberRange(T from, U to, V stepSize, boolean inclusive) {
+        this(from, to, stepSize, true, inclusive);
+    }
+
+    /**
+     * Creates a {@link NumberRange}.
+     * Creates a reversed range if <code>from</code> &lt; <code>to</code>.
+     *
+     * @param from start of the range
+     * @param to   end of the range
+     * @param inclusiveLeft whether the range is includes the lower bound
+     * @param inclusiveRight whether the range is includes the upper bound
+     */
+    public <T extends Number & Comparable, U extends Number & Comparable, V extends
+            Number & Comparable>
+    NumberRange(T from, U to, boolean inclusiveLeft, boolean inclusiveRight) {
+        this(from, to, null, inclusiveLeft, inclusiveRight);
+    }
+
+    /**
+     * Creates a {@link NumberRange}.
+     * Creates a reversed range if <code>from</code> &lt; <code>to</code>.
+     *
+     * @param from start of the range
+     * @param to   end of the range
+     * @param stepSize the gap between discrete elements in the range
+     * @param inclusiveLeft whether the range is includes the lower bound
+     * @param inclusiveRight whether the range is includes the upper bound
+     */
+    public <T extends Number & Comparable, U extends Number & Comparable, V extends
+            Number & Comparable>
+    NumberRange(T from, U to, V stepSize, boolean inclusiveLeft, boolean inclusiveRight) {
         if (from == null) {
             throw new IllegalArgumentException("Must specify a non-null value for the 'from' index in a Range");
         }
@@ -177,7 +218,9 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
         this.from = (Comparable) tempFrom;
         this.to = (Comparable) tempTo;
         this.stepSize = stepSize == null ? 1 : stepSize;
-        this.inclusive = inclusive;
+        this.inclusive = inclusiveRight;
+        this.inclusiveLeft = inclusiveLeft;
+        this.inclusiveRight = inclusiveRight;
     }
 
     /**
@@ -409,6 +452,7 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
         return size;
     }
 
+    // TODO: Update to work with inclusiveLeft! (GROOVY-9649)
     void calcSize(Comparable from, Comparable to, Number stepSize) {
         int tempsize = 0;
         boolean shortcut = false;
@@ -504,7 +548,8 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
     }
 
     private String getToString(String toText, String fromText) {
-        String sep = inclusive ? ".." : "..<";
+        String sepLeft = inclusiveLeft ? ".." : "<..";
+        String sep = inclusiveRight ? sepLeft : sepLeft + "<";
         String base = reverse ? "" + toText + sep + fromText : "" + fromText + sep + toText;
         return Integer.valueOf(1).equals(stepSize) ? base : base + ".by(" + stepSize + ")";
     }
@@ -580,8 +625,8 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
         public boolean hasNext() {
             fetchNextIfNeeded();
             return (next != null) && (isAscending
-                    ? (range.inclusive ? compareLessThanEqual(next, range.getTo()) : compareLessThan(next, range.getTo()))
-                    : (range.inclusive ? compareGreaterThanEqual(next, range.getFrom()) : compareGreaterThan(next, range.getFrom())));
+                    ? (range.inclusiveRight ? compareLessThanEqual(next, range.getTo()) : compareLessThan(next, range.getTo()))
+                    : (range.inclusiveRight ? compareGreaterThanEqual(next, range.getFrom()) : compareGreaterThan(next, range.getFrom())));
         }
 
         @Override
@@ -602,6 +647,9 @@ public class NumberRange extends AbstractList<Comparable> implements Range<Compa
                 if (next == null) {
                     // make the first fetch lazy too
                     next = isAscending ? range.getFrom() : range.getTo();
+                    if (!range.inclusiveLeft) {
+                        next = next();
+                    }
                 } else {
                     next = isAscending ? increment(next, step) : decrement(next, step);
                 }
diff --git a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
index a273c61..5041862 100644
--- a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
+++ b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
@@ -648,7 +648,7 @@ public class ScriptBytecodeAdapter {
             return new EmptyRange((Comparable) from);
         }
         if (from instanceof Number && to instanceof Number) {
-            return new NumberRange(comparableNumber((Number) from), comparableNumber((Number) to), inclusive);
+            return new NumberRange(comparableNumber((Number) from), comparableNumber((Number) to), !exclusiveLeft, !exclusiveRight);
         }
         if (!inclusive) {
             if (compareGreaterThan(from, to)) {

[groovy] 11/25: GROOVY-9649: Added test cases for left- and full-exclusive IntRanges

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit e89b5523ba7656b5b5d74432559bd192181fe7fa
Author: Esko Toivonen <es...@tuni.fi>
AuthorDate: Wed Apr 7 11:21:13 2021 +0300

    GROOVY-9649: Added test cases for left- and full-exclusive IntRanges
---
 src/test/groovy/lang/IntRangeTest.groovy | 121 +++++++++++++++++++++++--------
 1 file changed, 91 insertions(+), 30 deletions(-)

diff --git a/src/test/groovy/lang/IntRangeTest.groovy b/src/test/groovy/lang/IntRangeTest.groovy
index c7fa51c..ac495b9 100644
--- a/src/test/groovy/lang/IntRangeTest.groovy
+++ b/src/test/groovy/lang/IntRangeTest.groovy
@@ -54,6 +54,11 @@ class IntRangeTest extends GroovyTestCase {
         assert new IntRange(true, 0, 0).size() == 1
         assert new IntRange(false, 0, 1).size() == 1
         assert new IntRange(true, 0, 1).size() == 2
+        assert new IntRange(true, true, 0, 0).size() == 1
+        assert new IntRange(false, true, 0, 0).size() == 0
+        assert new IntRange(false, true, 0, 1).size() == 1
+        assert new IntRange(false, false, 0, 0).size() == 0
+        assert new IntRange(false, false, 0, 1).size() == 0
     }
 
     /**
@@ -90,37 +95,81 @@ class IntRangeTest extends GroovyTestCase {
 
     void testInclusiveRangesWithNegativesAndPositives() {
         final a = [1, 2, 3, 4]
-        assert a[-3..-2] == [2, 3]
-        assert a[-3..<-2] == [2]
-        assert a[2..-3] == [3, 2]
-        assert a[1..-1] == [2, 3, 4]
-        assert a[1..<-1] == [2, 3]
-        assert a[-2..<1] == [3]
-        assert a[-2..<-3] == [3]
-        assert a[5..<5] == []
-        assert a[-5..<-5] == []
+        assert a[-3..-2]   == [2, 3]
+        assert a[-3..<-2]  == [2]
+        assert a[-3<..2]   == [3]
+        assert a[-3<..<-2] == []
+
+        assert a[2..-3]    == [3, 2]
+
+        assert a[1..-1]    == [2, 3, 4]
+        assert a[1..<-1]   == [2, 3]
+        assert a[1<..-1]   == [3, 4]
+        assert a[1<..<-1]  == [3]
+
+        assert a[-2..<1]   == [3]
+        assert a[-2<..1]   == [2]
+        assert a[-2<..<1]  == []
+
+        assert a[-2..<-3]  == [3]
+        assert a[-2<..-3]  == [2]
+        assert a[-2<..<-3] == []
+
+        assert a[5..<5]    == []
+        assert a[5<..5]    == []
+        assert a[5<..<5]   == []
+        assert a[5<..<6]   == []
+        assert a[-5..<-5]  == []
     }
 
     void testInclusiveRangesWithNegativesAndPositivesStrings() {
         def items = 'abcde'
-        assert items[1..-2]   == 'bcd'
-        assert items[1..<-2]  == 'bc'
-        assert items[-3..<-2] == 'c'
-        assert items[-2..-4]  == 'dcb'
-        assert items[-2..<-4] == 'dc'
-        assert items[2..<2] == ''
-        assert items[-2..<-2] == ''
+        assert items[1..-2]    == 'bcd'
+        assert items[1..<-2]   == 'bc'
+        assert items[1<..-2]   == 'cd'
+        assert items[1<..<-2]  == 'c'
+
+        assert items[-3..<-2]  == 'c'
+        assert items[-3<..-2]  == 'd'
+        assert items[-3<..<-2] == ''
+
+        assert items[-2..-4]   == 'dcb'
+        assert items[-2..<-4]  == 'dc'
+        assert items[-2<..-4]  == 'cb'
+        assert items[-2<..<-4] == 'c'
+
+        assert items[2..<2]    == ''
+        assert items[2<..2]    == ''
+        assert items[2<..<2]   == ''
+        assert items[2<..<3]   == ''
+
+        assert items[-2..<-2]  == ''
+        assert items[-2<..-2]  == ''
+        assert items[-2<..<-2] == ''
+        assert items[-2<..<-3] == ''
     }
 
     void testInclusiveRangesWithNegativesAndPositivesPrimBoolArray() {
         boolean[] bs = [true, false, true, true]
-        assert bs[-3..-2]  == [false, true]
-        assert bs[-3..<-2] == [false]
-        assert bs[2..-3]   == [true, false]
-        assert bs[1..-1]   == [false, true, true]
-        assert bs[1..<-1]  == [false, true]
-        assert bs[-2..<1]  == [true]
-        assert bs[-2..<-3] == [true]
+        assert bs[-3..-2]   == [false, true]
+        assert bs[-3..<-2]  == [false]
+        assert bs[-3<..-2]  == [true]
+        assert bs[-3<..<-2] == []
+
+        assert bs[2..-3]    == [true, false]
+
+        assert bs[1..-1]    == [false, true, true]
+        assert bs[1..<-1]   == [false, true]
+        assert bs[1<..-1]   == [true, true]
+        assert bs[1<..<-1]  == [true]
+
+        assert bs[-2..<1]   == [true]
+        assert bs[-2<..1]   == [false]
+        assert bs[-2<..<1]  == []
+
+        assert bs[-2..<-3]  == [true]
+        assert bs[-2<..-3]  == [false]
+        assert bs[-2<..<-3] == []
     }
 
     void testInclusiveRangesWithNegativesAndPositivesBitset() {
@@ -132,13 +181,25 @@ class IntRangeTest extends GroovyTestCase {
         assert bs.toString() == '{1, 2, 6, 10, 14, 16, 17, 18, 23}'
         assert bs[bs.length()-1] == true
         assert bs[-1] == true
-        assert bs[6..17].toString() == '{0, 4, 8, 10, 11}'
-        assert bs[6..<17].toString() == '{0, 4, 8, 10}'
-        assert bs[17..6].toString() == '{0, 1, 3, 7, 11}'
-        assert bs[17..<6].toString() == '{0, 1, 3, 7}'
-        assert bs[-1..-7].toString() == '{0, 5, 6}'
-        assert bs[-1..<-7].toString() == '{0, 5}'
-        assert bs[20..<-8].toString() == '{2, 3}'
+
+        assert bs[6..17].toString()    == '{0, 4, 8, 10, 11}'
+        assert bs[6..<17].toString()   == '{0, 4, 8, 10}'
+        assert bs[6<..17].toString()   == '{3, 7, 9, 10}'
+        assert bs[6<..<17].toString()  == '{3, 7, 9}'
+
+        assert bs[17..6].toString()    == '{0, 1, 3, 7, 11}'
+        assert bs[17..<6].toString()   == '{0, 1, 3, 7}'
+        assert bs[17<..6].toString()   == '{0, 2, 6, 10}'
+        assert bs[17<..<6].toString()  == '{0, 2, 6}'
+
+        assert bs[-1..-7].toString()   == '{0, 5, 6}'
+        assert bs[-1..<-7].toString()  == '{0, 5}'
+        assert bs[-1<..-7].toString()  == '{4, 5}'
+        assert bs[-1<..<-7].toString() == '{4}'
+
+        assert bs[20..<-8].toString()  == '{2, 3}'
+        assert bs[20<..-8].toString()  == '{1, 2, 3}'
+        assert bs[20<..<-8].toString() == '{1, 2}'
     }
 
     void testHashCode(){