Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Report the number of elements in an infinite range correctly #525

Closed
p6rt opened this issue Dec 22, 2008 · 12 comments
Closed

Report the number of elements in an infinite range correctly #525

p6rt opened this issue Dec 22, 2008 · 12 comments
Labels

Comments

@p6rt
Copy link

p6rt commented Dec 22, 2008

Migrated from rt.perl.org#61618 (status was 'resolved')

Searchable as RT61618$

@p6rt
Copy link
Author

p6rt commented Dec 22, 2008

From @cspencer

The attatched patch lets an infinite Range (ie. (1..Inf)) report its
length as Inf.

It also adds temporary !FAIL cases when an infinite range is converted
into a list or string context.

@p6rt
Copy link
Author

p6rt commented Dec 22, 2008

From @cspencer

infinite-range.patch
Index: src/classes/Range.pir
===================================================================
--- src/classes/Range.pir	(revision 34246)
+++ src/classes/Range.pir	(working copy)
@@ -72,7 +72,21 @@
      .return ($P0)
 .end
 
+=item elems()
 
+Returns in the number of elements in the Range.
+
+=cut
+
+.sub 'elems' :method
+    # TODO: Returning a Num for the moment, so Inf gets reported correctly.
+    # TODO: Should be returning an Int.
+    $N0 = self
+    
+    .return ($N0)
+.end
+
+
 =item from()
 
 =item to()
@@ -114,16 +128,27 @@
 =cut
 
 .sub 'list' :method
-    .local pmc range_it, result
-    range_it = self.'iterator'()
-    result = new 'List'
-  range_loop:
-    unless range_it goto range_end
-    $P0 = shift range_it
-    push result, $P0
-    goto range_loop
-  range_end:
-    .return (result)
+    .local int test
+    .local pmc it
+    .local pmc rv
+
+    test = self.'!infinite'()
+    if test goto infinite
+    
+    it = self.'iterator'()
+    rv = new 'List'
+    
+  loop:
+    unless it goto end
+    $P0 = shift it
+    push rv, $P0
+    goto loop
+
+  infinite:
+    rv = '!FAIL'("Can't convert an infinite range to a list (yet)")
+    
+  end:
+    .return (rv)
 .end
 
 
@@ -242,7 +267,6 @@
     .return ($I0)
 .end
 
-
 =back
 
 =head2 Operators
@@ -381,6 +405,22 @@
     .return ($I0)
 .end
 
+.sub '!infinite' :method
+    .local pmc val
+
+    val = self.'from'()
+    if val == "-inf" goto infinite
+    
+    val = self.'to'()
+    if val == "inf" goto infinite
+
+  finite:
+    .return (0)
+
+  infinite:
+    .return (1)
+.end
+
 =back
 
 =head2 Vtable functions
@@ -396,21 +436,65 @@
 =cut
 
 .sub 'VTABLE_get_integer' :method :vtable('get_integer')
+    .local int test
+    .local int rv
+
+    test = self.'!infinite'()
+    if test goto infinite
+
+  finite:
     $P0 = self.'list'()
-    $I0 = $P0
-    .return ($I0)
+    rv  = $P0.'elems'()
+    goto done
+
+  infinite:	
+    # TODO: This isn't returning the correct value yet, should be Inf but isn't
+    rv = "inf"
+    goto done
+
+  done:	
+    .return (rv)
 .end
 
 .sub 'VTABLE_get_number' :method :vtable('get_number')
+    .local int test
+    .local num rv
+
+    test = self.'!infinite'()
+    if test goto infinite
+
+  finite:
     $P0 = self.'list'()
-    $N0 = $P0
-    .return ($N0)
+    rv  = $P0.'elems'()
+    goto done
+
+  infinite:	
+    rv = "inf"
+    goto done
+
+  done:	
+    .return (rv)
 .end
 
 .sub 'VTABLE_get_string' :method :vtable('get_string')
+    .local int test
+    .local string rv
+
+    rv = ""
+    test = self.'!infinite'()
+    if test goto infinite
+
+  finite:
     $P0 = self.'list'()
-    $S0 = $P0
-    .return ($S0)
+    rv  = $P0
+    goto done
+
+  infinite:
+    rv = '!FAIL'("Can't convert an infinite range to a string (yet)")
+    goto done
+
+  done:	
+    .return (rv)
 .end
 
 =back

@p6rt
Copy link
Author

p6rt commented Dec 22, 2008

From @pmichaud

On Mon Dec 22 09​:46​:43 2008, cspencer wrote​:

The attatched patch lets an infinite Range (ie. (1..Inf)) report its
length as Inf.

It also adds temporary !FAIL cases when an infinite range is converted
into a list or string context.

The patch needs some rework before it can be accepted.

First, I'd prefer to avoid the duplication of code between the
get_integer and get_number vtable functions; better would be put the
logic into the .elems method, and then have the vtable functions get the
results from .elems instead of vice-versa.

Second, I'd like .elems to be able to figure out the size of the range
without having to generate the full list and take the size of that.
(That might not be easy to manage, so I'll accept an interim patch that
builds the list for now if that's all we can do.)

Third, note that the get_string vtable function can't really return a
!FAIL, because it's constrained to return only native strings (which
can't hold a Failure object). So that probably needs some rework.

So, work on those a bit and resubmit. Thanks!

Pm

@p6rt
Copy link
Author

p6rt commented Dec 22, 2008

The RT System itself - Status changed from 'new' to 'open'

@p6rt
Copy link
Author

p6rt commented Dec 24, 2008

From @cspencer

Here's take two which should handle things a bit better for many cases.
It's passed all spectests as of revision 34313.

It also adds a reverse() method which returns another Range object.

Please let me know if there any problems!

Cory

On Mon Dec 22 12​:08​:55 2008, pmichaud wrote​:

On Mon Dec 22 09​:46​:43 2008, cspencer wrote​:

The attatched patch lets an infinite Range (ie. (1..Inf)) report its
length as Inf.

It also adds temporary !FAIL cases when an infinite range is converted
into a list or string context.

The patch needs some rework before it can be accepted.

First, I'd prefer to avoid the duplication of code between the
get_integer and get_number vtable functions; better would be put the
logic into the .elems method, and then have the vtable functions get the
results from .elems instead of vice-versa.

Second, I'd like .elems to be able to figure out the size of the range
without having to generate the full list and take the size of that.
(That might not be easy to manage, so I'll accept an interim patch that
builds the list for now if that's all we can do.)

Third, note that the get_string vtable function can't really return a
!FAIL, because it's constrained to return only native strings (which
can't hold a Failure object). So that probably needs some rework.

So, work on those a bit and resubmit. Thanks!

Pm

@p6rt
Copy link
Author

p6rt commented Dec 24, 2008

From @cspencer

infinite-range.patch
Index: src/classes/Range.pir
===================================================================
--- src/classes/Range.pir	(revision 34313)
+++ src/classes/Range.pir	(working copy)
@@ -61,24 +61,64 @@
 .end
 
 
-=item clone()   (vtable method)
+=item elems()
 
-Create a clone of the Range.
+Returns in the number of elements in the Range.
 
 =cut
 
-.sub 'clone' :method :vtable
-     $P0 = self.'!cloneattr'('$!from $!to $!from_exclusive $!to_exclusive')
-     .return ($P0)
+.sub 'elems' :method
+    .local num elems
+    .local pmc from
+    .local pmc to
+    
+    $P0 = self.'from'()
+    $P1 = self.'to'()
+
+    from = $P0.'clone'()
+    to = $P1.'clone'()
+    
+    # Check if it's a null Range, returning 0 if so
+    $I0 = 'infix:cmp'(from, to)
+    if $I0 == 1 goto null_range
+
+    # Check if any of the end points are Inf, returning Inf if so
+    $I0 = self.'!infinite'()
+    if $I0 goto infinite_range
+
+    elems = 0
+
+    # If the start point is excluded in the Range, skip to the next
+    $I0 = self.'!from_test'(from)
+    if $I0 goto loop
+    $P0 = 'postfix:++'(from)
+    
+  loop:
+    $P0 = 'postfix:++'(from)
+    
+    $I0 = self.'!to_test'($P0)
+    unless $I0 goto done
+
+    inc elems
+    goto loop
+
+  null_range:	
+    elems = 0
+    goto done
+    
+  done:	
+    .return (elems)
+    
+  infinite_range:
+    $N0 = 'Inf'()
+    .return ($N0)
 .end
 
 
 =item from()
 
-=item to()
+Returns the beginning of the range.
 
-Gets the beginning or end of the range.
-
 =cut
 
 .sub 'from' :method
@@ -86,21 +126,39 @@
     .return ($P0)
 .end
 
-.sub 'to' :method
-    $P0 = getattribute self, '$!to'
-    .return ($P0)
+
+=item reverse()
+
+Returns a new Range with the to and from reversed.
+
+=cut
+
+.sub 'reverse' :method
+    .local pmc from
+    .local pmc fromexc
+    .local pmc to
+    .local pmc toexc
+    .local pmc proto
+
+    fromexc = getattribute self, '$!from_exclusive'
+    toexc = getattribute self, '$!to_exclusive'
+
+    from = self.'from'()
+    to = self.'to'()
+
+    proto = get_hll_global 'Range'
+    .tailcall proto.'new'('from'=>to, 'to'=>from, 'from_exclusive'=>toexc, 'to_exclusive'=>fromexc)
 .end
 
 
-=item iterator()  (vtable function)
+=item to()
 
-Return an iterator for the Range.  Since Ranges are already
-iterators, we can just return a clone.
+Gets the beginning or end of the range.
 
 =cut
 
-.sub 'iterator' :method :vtable('get_iter')
-    $P0 = clone self
+.sub 'to' :method
+    $P0 = getattribute self, '$!to'
     .return ($P0)
 .end
 
@@ -113,38 +171,54 @@
 
 =cut
 
+.namespace ['Range']
 .sub 'list' :method
-    .local pmc range_it, result
-    range_it = self.'iterator'()
-    result = new 'List'
-  range_loop:
-    unless range_it goto range_end
-    $P0 = shift range_it
-    push result, $P0
-    goto range_loop
-  range_end:
-    .return (result)
+    .local int test
+    .local pmc it
+    .local pmc rv
+
+    test = self.'!infinite'()
+    if test goto infinite
+    
+    it = self.'iterator'()
+    rv = new 'List'
+    
+  loop:
+    unless it goto end
+    $P0 = shift it
+    push rv, $P0
+    goto loop
+
+  infinite:
+    rv = '!FAIL'("Can't convert an infinite range to a list (yet)")
+    
+  end:
+    .return (rv)
 .end
 
 
 =item max()
 
-=item min()
-
-=item minmax()
-
 =cut
 
-.namespace ['Range']
-
 .sub 'max' :method
     .tailcall self.'to'()
 .end
 
+
+=item min()
+
+=cut
+
 .sub 'min' :method
     .tailcall self.'from'()
 .end
 
+
+=item minmax()
+
+=cut
+
 .sub 'minmax' :method
     $P0 = self.'from'()
     $P1 = self.'to'()
@@ -180,69 +254,6 @@
 .end
 
 
-=item pop()  (vtable_method)
-
-Generate the next element at the end of the Range.
-
-=cut
-
-.sub 'pop' :method :vtable('pop_pmc')
-    .local pmc to, toexc, value
-    to = getattribute self, '$!to'
-    toexc = getattribute self, '$!to_exclusive'
-    value = 'postfix:--'(to)
-    unless toexc goto have_value
-    value = clone to
-  have_value:
-    $I0 = self.'!from_test'(value)
-    if $I0 goto success
-    value = '!FAIL'('Undefined value popped from empty range')
-  success:
-    .return (value)
-.end
-
-
-=item shift()   (vtable_method)
-
-Generate the next element at the front of the Range.
-
-=cut
-
-.sub 'shift' :method :vtable('shift_pmc')
-    .local pmc from, fromexc, value
-    from = getattribute self, '$!from'
-    fromexc = getattribute self, '$!from_exclusive'
-    value = 'postfix:++'(from)
-    unless fromexc goto have_value
-    value = clone from
-  have_value:
-    $I0 = self.'!to_test'(value)
-    if $I0 goto success
-    value = '!FAIL'('Undefined value shifted from empty range')
-  success:
-    .return (value)
-.end
-
-
-=item true()
-
-Return true if there are any more values to iterate over.
-
-=cut
-
-.sub 'true' :method :vtable('get_bool')
-    .local pmc from, fromexc
-    from = getattribute self, '$!from'
-    fromexc = getattribute self, '$!from_exclusive'
-    unless fromexc goto have_value
-    from = clone from
-    'postfix:++'(from)
-  have_value:
-    $I0 = self.'!to_test'(from)
-    .return ($I0)
-.end
-
-
 =back
 
 =head2 Operators
@@ -297,18 +308,19 @@
     .tailcall proto.'new'('from'=>from, 'to'=>to, 'from_exclusive'=>true, 'to_exclusive'=>true)
 .end
 
+
 =item prefix:<^>(Any $to)
 
 Construct a Range from C< 0 ..^ $to >.
 
 =cut
 
-.namespace[]
 .sub 'prefix:^' :multi(_)
     .param num to
     .tailcall 'infix:..^'(0, to)
 .end
 
+
 =item prefix:<^>(Type $x)
 
 Return $x.HOW.
@@ -320,6 +332,7 @@
     .tailcall proto.'HOW'()
 .end
 
+
 =back
 
 =head2 Private methods
@@ -328,6 +341,8 @@
 
 =item !flatten()
 
+Flattens the Range into a List.
+
 =cut
 
 .namespace ['Range']
@@ -335,34 +350,70 @@
     .tailcall self.'list'()
 .end
 
+
 =item !from_test(topic)
 
-=item !to_test(topic)
+Returns true if C<topic> is greater than C<.from>, honoring exclusive flags.
 
-Returns true if C<topic> is greater than C<.from> / less than C<.to>,
-honoring exclusive flags.
-
 =cut
 
-.namespace ['Range']
 .sub '!from_test' :method
     .param pmc topic
     .local pmc from, fromexc
+
     from = getattribute self, '$!from'
     fromexc = getattribute self, '$!from_exclusive'
+    
     if fromexc goto exclusive_test
+
     $I0 = isge topic, from
     .return ($I0)
+
   exclusive_test:
     $I0 = isgt topic, from
     .return ($I0)
 .end
 
+
+=item !to_test(topic)
+
+Returns true if C<topic> is less than C<.to>, honoring exclusive flags.
+
+=cut
+
+.sub '!infinite' :method
+    .local int test
+    .local num inf
+    
+    inf = 'Inf'()
+    
+    $P0 = self.'from'()
+    $N0 = $P0
+    test = iseq $N0, inf
+    if test goto done
+
+    $P0 = self.'to'()
+    $N0 = $P0
+    test = iseq $N0, inf
+
+  done:	
+    .return (test)
+.end
+
+
+=item !to_test(topic)
+
+Returns true if C<topic> is less than C<.to>, honoring exclusive flags.
+
+=cut
+
 .sub '!to_test' :method
     .param pmc topic
     .local pmc to, toexc
+
     to = getattribute self, '$!to'
     $I0 = isa to, 'String'
+
     unless $I0 goto test_value
     $S0 = topic
     $I0 = length $S0
@@ -371,46 +422,164 @@
     eq $I0, $I1, test_value
     $I0 = islt $I0, $I1
     .return ($I0)
+
   test_value:
     toexc = getattribute self, '$!to_exclusive'
     if toexc goto exclusive_test
     $I0 = isle topic, to
     .return ($I0)
+
   exclusive_test:
     $I0 = islt topic, to
     .return ($I0)
 .end
 
+
 =back
 
 =head2 Vtable functions
 
 =over
 
-=item VTABLE_get integer (vtable method)
+=item clone() (vtable method)
 
-=item VTABLE_get_number (vtable method)
+=cut
 
-=item VTABLE_get_string (vtable method)
+.namespace ['Range']
+.sub 'clone' :method :vtable
+     $P0 = self.'!cloneattr'('$!from $!to $!from_exclusive $!to_exclusive')
+     .return ($P0)
+.end
 
+
+=item iterator()  (vtable function)
+
+Return an iterator for the Range.  Since Ranges are already
+iterators, we can just return a clone.
+
 =cut
 
+.sub 'iterator' :method :vtable('get_iter')
+    $P0 = clone self
+    .return ($P0)
+.end
+
+
+=item pop()  (vtable_method)
+
+Generate the next element at the end of the Range.
+
+=cut
+
+.sub 'pop' :method :vtable('pop_pmc')
+    .local pmc to, toexc, value
+    to = getattribute self, '$!to'
+    toexc = getattribute self, '$!to_exclusive'
+    value = 'postfix:--'(to)
+    unless toexc goto have_value
+    value = clone to
+  have_value:
+    $I0 = self.'!from_test'(value)
+    if $I0 goto success
+    value = '!FAIL'('Undefined value popped from empty range')
+  success:
+    .return (value)
+.end
+
+
+=item shift()   (vtable_method)
+
+Generate the next element at the front of the Range.
+
+=cut
+
+.sub 'shift' :method :vtable('shift_pmc')
+    .local pmc from, fromexc, value
+    from = getattribute self, '$!from'
+    fromexc = getattribute self, '$!from_exclusive'
+    value = 'postfix:++'(from)
+    unless fromexc goto have_value
+    value = clone from
+  have_value:
+    $I0 = self.'!to_test'(value)
+    if $I0 goto success
+    value = '!FAIL'('Undefined value shifted from empty range')
+  success:
+    .return (value)
+.end
+
+
+=item true()
+
+Return true if there are any more values to iterate over.
+
+=cut
+
+.sub 'true' :method :vtable('get_bool')
+    .local pmc from, fromexc
+    from = getattribute self, '$!from'
+    fromexc = getattribute self, '$!from_exclusive'
+    unless fromexc goto have_value
+    from = clone from
+    'postfix:++'(from)
+  have_value:
+    $I0 = self.'!to_test'(from)
+    .return ($I0)
+.end
+
+
+=item VTABLE_get integer (vtable method)
+
+=cut
+
 .sub 'VTABLE_get_integer' :method :vtable('get_integer')
-    $P0 = self.'list'()
-    $I0 = $P0
+    $I0 = self.'elems'()
     .return ($I0)
 .end
 
+
+=item VTABLE_get_number (vtable method)
+
+=cut
+
 .sub 'VTABLE_get_number' :method :vtable('get_number')
-    $P0 = self.'list'()
-    $N0 = $P0
+     $N0 = self.'elems'()
     .return ($N0)
 .end
 
+
+=item VTABLE_get_string (vtable method)
+
+=cut
+
 .sub 'VTABLE_get_string' :method :vtable('get_string')
+    .local string rv
+    .local int test
+
+    rv = ""
+    test = self.'!infinite'()
+    if test goto infinite
+
+  finite:
     $P0 = self.'list'()
-    $S0 = $P0
-    .return ($S0)
+    rv  = $P0
+    goto done
+
+  infinite:
+    $P0 = self.'from'()
+    $S0 = $P0.'perl'()
+    
+    $P1 = self.'to'()
+    $S1 = $P0.'perl'()
+
+    rv .= $S0
+    rv .= ".."
+    rv .= $S1
+    
+    goto done
+
+  done:	
+    .return (rv)
 .end
 
 =back

@p6rt
Copy link
Author

p6rt commented Dec 24, 2008

From @cspencer

An updated version of the patch is attached that fixes a bug in the
stringification vtable routine.

On Tue Dec 23 21​:39​:03 2008, cspencer wrote​:

Here's take two which should handle things a bit better for many cases.
It's passed all spectests as of revision 34313.

It also adds a reverse() method which returns another Range object.

Please let me know if there any problems!

Cory

On Mon Dec 22 12​:08​:55 2008, pmichaud wrote​:

On Mon Dec 22 09​:46​:43 2008, cspencer wrote​:

The attatched patch lets an infinite Range (ie. (1..Inf)) report its
length as Inf.

It also adds temporary !FAIL cases when an infinite range is
converted
into a list or string context.

The patch needs some rework before it can be accepted.

First, I'd prefer to avoid the duplication of code between the
get_integer and get_number vtable functions; better would be put the
logic into the .elems method, and then have the vtable functions get the
results from .elems instead of vice-versa.

Second, I'd like .elems to be able to figure out the size of the range
without having to generate the full list and take the size of that.
(That might not be easy to manage, so I'll accept an interim patch that
builds the list for now if that's all we can do.)

Third, note that the get_string vtable function can't really return a
!FAIL, because it's constrained to return only native strings (which
can't hold a Failure object). So that probably needs some rework.

So, work on those a bit and resubmit. Thanks!

Pm

@p6rt
Copy link
Author

p6rt commented Dec 24, 2008

From @cspencer

infinite-range.patch
Index: src/classes/Range.pir
===================================================================
--- src/classes/Range.pir	(revision 34313)
+++ src/classes/Range.pir	(working copy)
@@ -61,24 +61,64 @@
 .end
 
 
-=item clone()   (vtable method)
+=item elems()
 
-Create a clone of the Range.
+Returns in the number of elements in the Range.
 
 =cut
 
-.sub 'clone' :method :vtable
-     $P0 = self.'!cloneattr'('$!from $!to $!from_exclusive $!to_exclusive')
-     .return ($P0)
+.sub 'elems' :method
+    .local num elems
+    .local pmc from
+    .local pmc to
+    
+    $P0 = self.'from'()
+    $P1 = self.'to'()
+
+    from = $P0.'clone'()
+    to = $P1.'clone'()
+    
+    # Check if it's a null Range, returning 0 if so
+    $I0 = 'infix:cmp'(from, to)
+    if $I0 == 1 goto null_range
+
+    # Check if any of the end points are Inf, returning Inf if so
+    $I0 = self.'!infinite'()
+    if $I0 goto infinite_range
+
+    elems = 0
+
+    # If the start point is excluded in the Range, skip to the next
+    $I0 = self.'!from_test'(from)
+    if $I0 goto loop
+    $P0 = 'postfix:++'(from)
+    
+  loop:
+    $P0 = 'postfix:++'(from)
+    
+    $I0 = self.'!to_test'($P0)
+    unless $I0 goto done
+
+    inc elems
+    goto loop
+
+  null_range:	
+    elems = 0
+    goto done
+    
+  done:	
+    .return (elems)
+    
+  infinite_range:
+    $N0 = 'Inf'()
+    .return ($N0)
 .end
 
 
 =item from()
 
-=item to()
+Returns the beginning of the range.
 
-Gets the beginning or end of the range.
-
 =cut
 
 .sub 'from' :method
@@ -86,21 +126,39 @@
     .return ($P0)
 .end
 
-.sub 'to' :method
-    $P0 = getattribute self, '$!to'
-    .return ($P0)
+
+=item reverse()
+
+Returns a new Range with the to and from reversed.
+
+=cut
+
+.sub 'reverse' :method
+    .local pmc from
+    .local pmc fromexc
+    .local pmc to
+    .local pmc toexc
+    .local pmc proto
+
+    fromexc = getattribute self, '$!from_exclusive'
+    toexc = getattribute self, '$!to_exclusive'
+
+    from = self.'from'()
+    to = self.'to'()
+
+    proto = get_hll_global 'Range'
+    .tailcall proto.'new'('from'=>to, 'to'=>from, 'from_exclusive'=>toexc, 'to_exclusive'=>fromexc)
 .end
 
 
-=item iterator()  (vtable function)
+=item to()
 
-Return an iterator for the Range.  Since Ranges are already
-iterators, we can just return a clone.
+Gets the beginning or end of the range.
 
 =cut
 
-.sub 'iterator' :method :vtable('get_iter')
-    $P0 = clone self
+.sub 'to' :method
+    $P0 = getattribute self, '$!to'
     .return ($P0)
 .end
 
@@ -113,38 +171,54 @@
 
 =cut
 
+.namespace ['Range']
 .sub 'list' :method
-    .local pmc range_it, result
-    range_it = self.'iterator'()
-    result = new 'List'
-  range_loop:
-    unless range_it goto range_end
-    $P0 = shift range_it
-    push result, $P0
-    goto range_loop
-  range_end:
-    .return (result)
+    .local int test
+    .local pmc it
+    .local pmc rv
+
+    test = self.'!infinite'()
+    if test goto infinite
+    
+    it = self.'iterator'()
+    rv = new 'List'
+    
+  loop:
+    unless it goto end
+    $P0 = shift it
+    push rv, $P0
+    goto loop
+
+  infinite:
+    rv = '!FAIL'("Can't convert an infinite range to a list (yet)")
+    
+  end:
+    .return (rv)
 .end
 
 
 =item max()
 
-=item min()
-
-=item minmax()
-
 =cut
 
-.namespace ['Range']
-
 .sub 'max' :method
     .tailcall self.'to'()
 .end
 
+
+=item min()
+
+=cut
+
 .sub 'min' :method
     .tailcall self.'from'()
 .end
 
+
+=item minmax()
+
+=cut
+
 .sub 'minmax' :method
     $P0 = self.'from'()
     $P1 = self.'to'()
@@ -180,69 +254,6 @@
 .end
 
 
-=item pop()  (vtable_method)
-
-Generate the next element at the end of the Range.
-
-=cut
-
-.sub 'pop' :method :vtable('pop_pmc')
-    .local pmc to, toexc, value
-    to = getattribute self, '$!to'
-    toexc = getattribute self, '$!to_exclusive'
-    value = 'postfix:--'(to)
-    unless toexc goto have_value
-    value = clone to
-  have_value:
-    $I0 = self.'!from_test'(value)
-    if $I0 goto success
-    value = '!FAIL'('Undefined value popped from empty range')
-  success:
-    .return (value)
-.end
-
-
-=item shift()   (vtable_method)
-
-Generate the next element at the front of the Range.
-
-=cut
-
-.sub 'shift' :method :vtable('shift_pmc')
-    .local pmc from, fromexc, value
-    from = getattribute self, '$!from'
-    fromexc = getattribute self, '$!from_exclusive'
-    value = 'postfix:++'(from)
-    unless fromexc goto have_value
-    value = clone from
-  have_value:
-    $I0 = self.'!to_test'(value)
-    if $I0 goto success
-    value = '!FAIL'('Undefined value shifted from empty range')
-  success:
-    .return (value)
-.end
-
-
-=item true()
-
-Return true if there are any more values to iterate over.
-
-=cut
-
-.sub 'true' :method :vtable('get_bool')
-    .local pmc from, fromexc
-    from = getattribute self, '$!from'
-    fromexc = getattribute self, '$!from_exclusive'
-    unless fromexc goto have_value
-    from = clone from
-    'postfix:++'(from)
-  have_value:
-    $I0 = self.'!to_test'(from)
-    .return ($I0)
-.end
-
-
 =back
 
 =head2 Operators
@@ -297,18 +308,19 @@
     .tailcall proto.'new'('from'=>from, 'to'=>to, 'from_exclusive'=>true, 'to_exclusive'=>true)
 .end
 
+
 =item prefix:<^>(Any $to)
 
 Construct a Range from C< 0 ..^ $to >.
 
 =cut
 
-.namespace[]
 .sub 'prefix:^' :multi(_)
     .param num to
     .tailcall 'infix:..^'(0, to)
 .end
 
+
 =item prefix:<^>(Type $x)
 
 Return $x.HOW.
@@ -320,6 +332,7 @@
     .tailcall proto.'HOW'()
 .end
 
+
 =back
 
 =head2 Private methods
@@ -328,6 +341,8 @@
 
 =item !flatten()
 
+Flattens the Range into a List.
+
 =cut
 
 .namespace ['Range']
@@ -335,34 +350,70 @@
     .tailcall self.'list'()
 .end
 
+
 =item !from_test(topic)
 
-=item !to_test(topic)
+Returns true if C<topic> is greater than C<.from>, honoring exclusive flags.
 
-Returns true if C<topic> is greater than C<.from> / less than C<.to>,
-honoring exclusive flags.
-
 =cut
 
-.namespace ['Range']
 .sub '!from_test' :method
     .param pmc topic
     .local pmc from, fromexc
+
     from = getattribute self, '$!from'
     fromexc = getattribute self, '$!from_exclusive'
+    
     if fromexc goto exclusive_test
+
     $I0 = isge topic, from
     .return ($I0)
+
   exclusive_test:
     $I0 = isgt topic, from
     .return ($I0)
 .end
 
+
+=item !to_test(topic)
+
+Returns true if C<topic> is less than C<.to>, honoring exclusive flags.
+
+=cut
+
+.sub '!infinite' :method
+    .local int test
+    .local num inf
+    
+    inf = 'Inf'()
+    
+    $P0 = self.'from'()
+    $N0 = $P0
+    test = iseq $N0, inf
+    if test goto done
+
+    $P0 = self.'to'()
+    $N0 = $P0
+    test = iseq $N0, inf
+
+  done:	
+    .return (test)
+.end
+
+
+=item !to_test(topic)
+
+Returns true if C<topic> is less than C<.to>, honoring exclusive flags.
+
+=cut
+
 .sub '!to_test' :method
     .param pmc topic
     .local pmc to, toexc
+
     to = getattribute self, '$!to'
     $I0 = isa to, 'String'
+
     unless $I0 goto test_value
     $S0 = topic
     $I0 = length $S0
@@ -371,46 +422,164 @@
     eq $I0, $I1, test_value
     $I0 = islt $I0, $I1
     .return ($I0)
+
   test_value:
     toexc = getattribute self, '$!to_exclusive'
     if toexc goto exclusive_test
     $I0 = isle topic, to
     .return ($I0)
+
   exclusive_test:
     $I0 = islt topic, to
     .return ($I0)
 .end
 
+
 =back
 
 =head2 Vtable functions
 
 =over
 
-=item VTABLE_get integer (vtable method)
+=item clone() (vtable method)
 
-=item VTABLE_get_number (vtable method)
+=cut
 
-=item VTABLE_get_string (vtable method)
+.namespace ['Range']
+.sub 'clone' :method :vtable
+     $P0 = self.'!cloneattr'('$!from $!to $!from_exclusive $!to_exclusive')
+     .return ($P0)
+.end
 
+
+=item iterator()  (vtable function)
+
+Return an iterator for the Range.  Since Ranges are already
+iterators, we can just return a clone.
+
 =cut
 
+.sub 'iterator' :method :vtable('get_iter')
+    $P0 = clone self
+    .return ($P0)
+.end
+
+
+=item pop()  (vtable_method)
+
+Generate the next element at the end of the Range.
+
+=cut
+
+.sub 'pop' :method :vtable('pop_pmc')
+    .local pmc to, toexc, value
+    to = getattribute self, '$!to'
+    toexc = getattribute self, '$!to_exclusive'
+    value = 'postfix:--'(to)
+    unless toexc goto have_value
+    value = clone to
+  have_value:
+    $I0 = self.'!from_test'(value)
+    if $I0 goto success
+    value = '!FAIL'('Undefined value popped from empty range')
+  success:
+    .return (value)
+.end
+
+
+=item shift()   (vtable_method)
+
+Generate the next element at the front of the Range.
+
+=cut
+
+.sub 'shift' :method :vtable('shift_pmc')
+    .local pmc from, fromexc, value
+    from = getattribute self, '$!from'
+    fromexc = getattribute self, '$!from_exclusive'
+    value = 'postfix:++'(from)
+    unless fromexc goto have_value
+    value = clone from
+  have_value:
+    $I0 = self.'!to_test'(value)
+    if $I0 goto success
+    value = '!FAIL'('Undefined value shifted from empty range')
+  success:
+    .return (value)
+.end
+
+
+=item true()
+
+Return true if there are any more values to iterate over.
+
+=cut
+
+.sub 'true' :method :vtable('get_bool')
+    .local pmc from, fromexc
+    from = getattribute self, '$!from'
+    fromexc = getattribute self, '$!from_exclusive'
+    unless fromexc goto have_value
+    from = clone from
+    'postfix:++'(from)
+  have_value:
+    $I0 = self.'!to_test'(from)
+    .return ($I0)
+.end
+
+
+=item VTABLE_get integer (vtable method)
+
+=cut
+
 .sub 'VTABLE_get_integer' :method :vtable('get_integer')
-    $P0 = self.'list'()
-    $I0 = $P0
+    $I0 = self.'elems'()
     .return ($I0)
 .end
 
+
+=item VTABLE_get_number (vtable method)
+
+=cut
+
 .sub 'VTABLE_get_number' :method :vtable('get_number')
-    $P0 = self.'list'()
-    $N0 = $P0
+     $N0 = self.'elems'()
     .return ($N0)
 .end
 
+
+=item VTABLE_get_string (vtable method)
+
+=cut
+
 .sub 'VTABLE_get_string' :method :vtable('get_string')
+    .local string rv
+    .local int test
+
+    rv = ""
+    test = self.'!infinite'()
+    if test goto infinite
+
+  finite:
     $P0 = self.'list'()
-    $S0 = $P0
-    .return ($S0)
+    rv  = $P0
+    goto done
+
+  infinite:
+    $P0 = self.'from'()
+    $S0 = $P0.'perl'()
+    
+    $P1 = self.'to'()
+    $S1 = $P1.'perl'()
+
+    rv .= $S0
+    rv .= ".."
+    rv .= $S1
+    
+    goto done
+
+  done:	
+    .return (rv)
 .end
 
 =back

@p6rt
Copy link
Author

p6rt commented Dec 26, 2008

From @cspencer

This update to the patch adds support for Whatever objects in ranges.

On Mon Dec 22 12​:08​:55 2008, pmichaud wrote​:

On Mon Dec 22 09​:46​:43 2008, cspencer wrote​:

The attatched patch lets an infinite Range (ie. (1..Inf)) report its
length as Inf.

It also adds temporary !FAIL cases when an infinite range is converted
into a list or string context.

The patch needs some rework before it can be accepted.

First, I'd prefer to avoid the duplication of code between the
get_integer and get_number vtable functions; better would be put the
logic into the .elems method, and then have the vtable functions get the
results from .elems instead of vice-versa.

Second, I'd like .elems to be able to figure out the size of the range
without having to generate the full list and take the size of that.
(That might not be easy to manage, so I'll accept an interim patch that
builds the list for now if that's all we can do.)

Third, note that the get_string vtable function can't really return a
!FAIL, because it's constrained to return only native strings (which
can't hold a Failure object). So that probably needs some rework.

So, work on those a bit and resubmit. Thanks!

Pm

@p6rt
Copy link
Author

p6rt commented Dec 26, 2008

From @cspencer

infinite-range.patch
Index: src/classes/Range.pir
===================================================================
--- src/classes/Range.pir	(revision 34360)
+++ src/classes/Range.pir	(working copy)
@@ -61,46 +61,126 @@
 .end
 
 
-=item clone()   (vtable method)
+=item elems()
 
-Create a clone of the Range.
+Returns in the number of elements in the Range.
 
 =cut
 
-.sub 'clone' :method :vtable
-     $P0 = self.'!cloneattr'('$!from $!to $!from_exclusive $!to_exclusive')
-     .return ($P0)
+.sub 'elems' :method
+    .local num elems
+    .local pmc from
+    .local pmc to
+    
+    $P0 = self.'from'()
+    $P1 = self.'to'()
+
+    from = $P0.'clone'()
+    to = $P1.'clone'()
+    
+    # Check if it's a null Range, returning 0 if so
+    $I0 = 'infix:cmp'(from, to)
+    if $I0 == 1 goto null_range
+
+    # Check if any of the end points are Inf, returning Inf if so
+    $I0 = self.'!infinite'()
+    if $I0 goto infinite_range
+
+    elems = 0
+
+    # If the start point is excluded in the Range, skip to the next
+    $I0 = self.'!from_test'(from)
+    if $I0 goto loop
+    $P0 = 'postfix:++'(from)
+    
+  loop:
+    $P0 = 'postfix:++'(from)
+    
+    $I0 = self.'!to_test'($P0)
+    unless $I0 goto done
+
+    inc elems
+    goto loop
+
+  null_range:	
+    elems = 0
+    goto done
+    
+  done:	
+    .return (elems)
+    
+  infinite_range:
+    $N0 = 'Inf'()
+    .return ($N0)
 .end
 
 
 =item from()
 
-=item to()
+Returns the beginning of the range.
 
-Gets the beginning or end of the range.
-
 =cut
 
 .sub 'from' :method
     $P0 = getattribute self, '$!from'
+
+    $I0 = isa $P0, "Whatever"
+    if $I0 goto whatever
+    goto done
+    
+  whatever:	
+    $N0 = "-Inf"
+    $P0 = new 'Num'
+    $P0 = $N0
+    
+  done:	
     .return ($P0)
 .end
 
-.sub 'to' :method
-    $P0 = getattribute self, '$!to'
-    .return ($P0)
+
+=item reverse()
+
+Returns a new Range with the to and from reversed.
+
+=cut
+
+.sub 'reverse' :method
+    .local pmc from
+    .local pmc fromexc
+    .local pmc to
+    .local pmc toexc
+    .local pmc proto
+
+    fromexc = getattribute self, '$!from_exclusive'
+    toexc = getattribute self, '$!to_exclusive'
+
+    from = self.'from'()
+    to = self.'to'()
+
+    proto = get_hll_global 'Range'
+    .tailcall proto.'new'('from'=>to, 'to'=>from, 'from_exclusive'=>toexc, 'to_exclusive'=>fromexc)
 .end
 
 
-=item iterator()  (vtable function)
+=item to()
 
-Return an iterator for the Range.  Since Ranges are already
-iterators, we can just return a clone.
+Gets the beginning or end of the range.
 
 =cut
 
-.sub 'iterator' :method :vtable('get_iter')
-    $P0 = clone self
+.sub 'to' :method
+    $P0 = getattribute self, '$!to'
+
+    $I0 = isa $P0, "Whatever"
+    if $I0 goto whatever
+    goto done
+    
+  whatever:	
+    $N0 = "Inf"
+    $P0 = new 'Num'
+    $P0 = $N0
+    
+  done:	
     .return ($P0)
 .end
 
@@ -113,38 +193,54 @@
 
 =cut
 
+.namespace ['Range']
 .sub 'list' :method
-    .local pmc range_it, result
-    range_it = self.'iterator'()
-    result = new 'List'
-  range_loop:
-    unless range_it goto range_end
-    $P0 = shift range_it
-    push result, $P0
-    goto range_loop
-  range_end:
-    .return (result)
+    .local int test
+    .local pmc it
+    .local pmc rv
+
+    test = self.'!infinite'()
+    if test goto infinite
+    
+    it = self.'iterator'()
+    rv = new 'List'
+    
+  loop:
+    unless it goto end
+    $P0 = shift it
+    push rv, $P0
+    goto loop
+
+  infinite:
+    rv = '!FAIL'("Can't convert an infinite range to a list (yet)")
+    
+  end:
+    .return (rv)
 .end
 
 
 =item max()
 
-=item min()
-
-=item minmax()
-
 =cut
 
-.namespace ['Range']
-
 .sub 'max' :method
     .tailcall self.'to'()
 .end
 
+
+=item min()
+
+=cut
+
 .sub 'min' :method
     .tailcall self.'from'()
 .end
 
+
+=item minmax()
+
+=cut
+
 .sub 'minmax' :method
     $P0 = self.'from'()
     $P1 = self.'to'()
@@ -162,87 +258,31 @@
 .sub 'perl' :method
     .local string result, tmp
     .local pmc from, fromexc, toexc, to
+
     from = getattribute self, '$!from'
     fromexc = getattribute self, '$!from_exclusive'
     toexc = getattribute self, '$!to_exclusive'
     to = getattribute self, '$!to'
+
+  range_from:	
     result = from.'perl'()
-    unless fromexc goto dots
+    unless fromexc goto range_dots
     result .= '^'
-  dots:
+
+  range_dots:
     result .= '..'
-    unless toexc goto end
+    unless toexc goto range_to
     result .= '^'
-  end:
+
+  range_to:	
     tmp = to.'perl'()
     result .= tmp
+
+  done:	
     .return (result)
 .end
 
 
-=item pop()  (vtable_method)
-
-Generate the next element at the end of the Range.
-
-=cut
-
-.sub 'pop' :method :vtable('pop_pmc')
-    .local pmc to, toexc, value
-    to = getattribute self, '$!to'
-    toexc = getattribute self, '$!to_exclusive'
-    value = 'postfix:--'(to)
-    unless toexc goto have_value
-    value = clone to
-  have_value:
-    $I0 = self.'!from_test'(value)
-    if $I0 goto success
-    value = '!FAIL'('Undefined value popped from empty range')
-  success:
-    .return (value)
-.end
-
-
-=item shift()   (vtable_method)
-
-Generate the next element at the front of the Range.
-
-=cut
-
-.sub 'shift' :method :vtable('shift_pmc')
-    .local pmc from, fromexc, value
-    from = getattribute self, '$!from'
-    fromexc = getattribute self, '$!from_exclusive'
-    value = 'postfix:++'(from)
-    unless fromexc goto have_value
-    value = clone from
-  have_value:
-    $I0 = self.'!to_test'(value)
-    if $I0 goto success
-    value = '!FAIL'('Undefined value shifted from empty range')
-  success:
-    .return (value)
-.end
-
-
-=item true()
-
-Return true if there are any more values to iterate over.
-
-=cut
-
-.sub 'true' :method :vtable('get_bool')
-    .local pmc from, fromexc
-    from = getattribute self, '$!from'
-    fromexc = getattribute self, '$!from_exclusive'
-    unless fromexc goto have_value
-    from = clone from
-    'postfix:++'(from)
-  have_value:
-    $I0 = self.'!to_test'(from)
-    .return ($I0)
-.end
-
-
 =back
 
 =head2 Operators
@@ -297,18 +337,19 @@
     .tailcall proto.'new'('from'=>from, 'to'=>to, 'from_exclusive'=>true, 'to_exclusive'=>true)
 .end
 
+
 =item prefix:<^>(Any $to)
 
 Construct a Range from C< 0 ..^ $to >.
 
 =cut
 
-.namespace[]
 .sub 'prefix:^' :multi(_)
     .param num to
     .tailcall 'infix:..^'(0, to)
 .end
 
+
 =item prefix:<^>(Type $x)
 
 Return $x.HOW.
@@ -320,6 +361,7 @@
     .tailcall proto.'HOW'()
 .end
 
+
 =back
 
 =head2 Private methods
@@ -328,6 +370,8 @@
 
 =item !flatten()
 
+Flattens the Range into a List.
+
 =cut
 
 .namespace ['Range']
@@ -335,34 +379,72 @@
     .tailcall self.'list'()
 .end
 
+
 =item !from_test(topic)
 
-=item !to_test(topic)
+Returns true if C<topic> is greater than C<.from>, honoring exclusive flags.
 
-Returns true if C<topic> is greater than C<.from> / less than C<.to>,
-honoring exclusive flags.
-
 =cut
 
-.namespace ['Range']
 .sub '!from_test' :method
     .param pmc topic
     .local pmc from, fromexc
-    from = getattribute self, '$!from'
+
+    from = self.'from'()
     fromexc = getattribute self, '$!from_exclusive'
+    
     if fromexc goto exclusive_test
+
     $I0 = isge topic, from
     .return ($I0)
+
   exclusive_test:
     $I0 = isgt topic, from
     .return ($I0)
 .end
 
+
+=item !to_test(topic)
+
+Returns true if C<topic> is less than C<.to>, honoring exclusive flags.
+
+=cut
+
+.sub '!infinite' :method
+    .local int test
+    .local num inf
+    
+    inf = 'Inf'()
+
+    $P0 = self.'from'()
+    
+    $N0 = $P0
+    test = iseq $N0, inf
+    if test goto done
+
+    $P0 = self.'to'()
+
+    $N0 = $P0
+    test = iseq $N0, inf
+
+  done:	
+    .return (test)
+.end
+
+
+=item !to_test(topic)
+
+Returns true if C<topic> is less than C<.to>, honoring exclusive flags.
+
+=cut
+
 .sub '!to_test' :method
     .param pmc topic
     .local pmc to, toexc
-    to = getattribute self, '$!to'
+
+    to = self.'to'()
     $I0 = isa to, 'String'
+
     unless $I0 goto test_value
     $S0 = topic
     $I0 = length $S0
@@ -371,46 +453,172 @@
     eq $I0, $I1, test_value
     $I0 = islt $I0, $I1
     .return ($I0)
+
   test_value:
     toexc = getattribute self, '$!to_exclusive'
     if toexc goto exclusive_test
     $I0 = isle topic, to
     .return ($I0)
+
   exclusive_test:
     $I0 = islt topic, to
     .return ($I0)
 .end
 
+
 =back
 
 =head2 Vtable functions
 
 =over
 
-=item VTABLE_get integer (vtable method)
+=item clone() (vtable method)
 
-=item VTABLE_get_number (vtable method)
+=cut
 
-=item VTABLE_get_string (vtable method)
+.namespace ['Range']
+.sub 'clone' :method :vtable
+     $P0 = self.'!cloneattr'('$!from $!to $!from_exclusive $!to_exclusive')
+     .return ($P0)
+.end
 
+
+=item iterator()  (vtable function)
+
+Return an iterator for the Range.  Since Ranges are already
+iterators, we can just return a clone.
+
 =cut
 
+.sub 'iterator' :method :vtable('get_iter')
+    $P0 = clone self
+    .return ($P0)
+.end
+
+
+=item pop()  (vtable_method)
+
+Generate the next element at the end of the Range.
+
+=cut
+
+.sub 'pop' :method :vtable('pop_pmc')
+    .local pmc to, toexc, value
+    to = self.'to'()
+    toexc = getattribute self, '$!to_exclusive'
+    value = 'postfix:--'(to)
+    unless toexc goto have_value
+    value = clone to
+  have_value:
+    $I0 = self.'!from_test'(value)
+    if $I0 goto success
+    value = '!FAIL'('Undefined value popped from empty range')
+  success:
+    .return (value)
+.end
+
+
+=item shift()   (vtable_method)
+
+Generate the next element at the front of the Range.
+
+=cut
+
+.sub 'shift' :method :vtable('shift_pmc')
+    .local pmc from
+    .local pmc fromexc
+    .local pmc value
+    
+    from = self.'from'()
+    fromexc = getattribute self, '$!from_exclusive'
+
+    value = 'postfix:++'(from)
+    unless fromexc goto have_value
+    value = clone from
+
+  have_value:
+    $I0 = self.'!to_test'(value)
+    if $I0 goto success
+    value = '!FAIL'('Undefined value shifted from empty range')
+
+  success:
+    .return (value)
+.end
+
+
+=item true()
+
+Return true if there are any more values to iterate over.
+
+=cut
+
+.sub 'true' :method :vtable('get_bool')
+    .local pmc from, fromexc
+
+    from = self.'from'()
+    fromexc = getattribute self, '$!from_exclusive'
+    unless fromexc goto have_value
+
+    from = clone from
+    'postfix:++'(from)
+
+  have_value:
+    $I0 = self.'!to_test'(from)
+    .return ($I0)
+.end
+
+
+=item VTABLE_get integer (vtable method)
+
+=cut
+
 .sub 'VTABLE_get_integer' :method :vtable('get_integer')
-    $P0 = self.'list'()
-    $I0 = $P0
+    $I0 = self.'elems'()
     .return ($I0)
 .end
 
+
+=item VTABLE_get_number (vtable method)
+
+=cut
+
 .sub 'VTABLE_get_number' :method :vtable('get_number')
-    $P0 = self.'list'()
-    $N0 = $P0
+     $N0 = self.'elems'()
     .return ($N0)
 .end
 
+
+=item VTABLE_get_string (vtable method)
+
+=cut
+
 .sub 'VTABLE_get_string' :method :vtable('get_string')
+    .local string rv
+    .local int test
+
+    rv = ""
+    test = self.'!infinite'()
+    if test goto infinite
+
+  finite:
     $P0 = self.'list'()
-    $S0 = $P0
-    .return ($S0)
+    rv  = $P0
+    goto done
+
+  infinite:
+    $P0 = self.'from'()
+    $S0 = $P0.'perl'()
+    
+    $P1 = self.'to'()
+    $S1 = $P1.'perl'()
+
+    rv .= $S0
+    rv .= ".."
+    rv .= $S1
+    goto done
+
+  done:
+    .return (rv)
 .end
 
 =back

@p6rt
Copy link
Author

p6rt commented Jul 7, 2010

From @pmichaud

Thank you very much for the patch contribution; since the time of the
contribution we've re-worked the Range implementation altogether (at
least twice), so this patch no longer applies.

Thanks!

Pm

@p6rt
Copy link
Author

p6rt commented Jul 7, 2010

@pmichaud - Status changed from 'open' to 'resolved'

@p6rt p6rt closed this as completed Jul 7, 2010
@p6rt p6rt added the patch label Jan 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant