On Mon Dec 22 21:51:05 2008, masak wrote:
> Patrick (>):
> > I would prefer to see this done by using a smart match on the :x()
> > argument instead of explicitly checking it for a Range and grabbing
> > min/max from there.  Using a smart match would allow things like
> > :x(1|5|7) and :x({ .is_prime })  to work properly.
> 
> We might as well add spectests for that before closing this bug. (I
> made an attempt, but it turned out I wasn't awake enough to write
> junction and closure tests yet. Someone else is welcome to try.)
> 

Ok.  This version uses smart match.  The non-regex version could
probably be more efficient.


Zev
Index: src/builtins/any-str.pir
===================================================================
--- src/builtins/any-str.pir	(revision 34267)
+++ src/builtins/any-str.pir	(working copy)
@@ -910,32 +910,33 @@
     global_flag = get_hll_global ['Bool'], 'False'
   have_global:
 
-    .local int times                    # how many times to substitute
+    .local pmc times                    # how many times to substitute
+    times = new 'Integer'
     times = 1                           # the default is to substitute once
     unless global_flag goto check_x
-    times = -1                          # a negative number means all of them (:global)
+    times = new 'Whatever'
   check_x:
 
     .local pmc x_opt
     x_opt = options['x']
     if null x_opt goto check_nth
+    if global_flag goto fail_x_global
     times = x_opt
-    if times < 0 goto x_fail
   check_nth:
 
     .local pmc nth_opt
     nth_opt = options['nth']
-    unless null nth_opt goto check_global
+    unless null nth_opt goto subst_init
     nth_opt = get_hll_global ['Bool'], 'True'
-  check_global:
+  subst_init:
 
-
+    .local pmc results
+    results = new 'ResizableStringArray'
     .local string result
     result = self
     result = clone result
+    push results, result
 
-    if times == 0 goto subst_done
-
     .local int startpos, pos, substringlen, replacelen
     startpos = 0
     pos = 0
@@ -950,26 +951,31 @@
     if pos < 0 goto subst_done
 
     n_cnt += 1
-    $P0 = nth_opt.'ACCEPTS'(n_cnt)
-    unless $P0 goto subst_loop
+    $I0 = nth_opt.'ACCEPTS'(n_cnt)
+    unless $I0 goto subst_loop
 
-    if times < 0 goto skip_times
-
-    x_cnt += 1
-    if x_cnt > times goto subst_done
-  skip_times:
-
+    result = clone result
     substr result, pos, substringlen, replacement
+    push results, result
     startpos = pos + replacelen
     goto subst_loop
   subst_done:
+    .local int idx
+    idx = results
+  find_result:
+    idx -= 1
+    if idx < 0 goto subst_failed
+    $I0 = times.'ACCEPTS'(idx)
+    unless $I0 goto find_result
+    result = results[idx]
+    goto do_return
+  subst_failed:
+    result = results[0]
+  do_return:
     .return (result)
 
-  nth_fail:
-    die "Must pass a non-negative integer to :nth()"
-
-  x_fail:
-    die "Must pass a non-negative integer to :x()"
+  fail_x_global:
+    die "Cannot specify both :g and :x()"
 .end
 
 
@@ -986,18 +992,18 @@
     global_flag = get_hll_global ['Bool'], 'False'
   have_global:
 
-
-    .local int times                    # how many times to substitute
+    .local pmc times                    # how many times to substitute
+    times = new 'Integer'
     times = 1                           # the default is to substitute once
     unless global_flag goto check_x
-    times = -1                          # a negative number means all of them (:global)
+    times = new 'Whatever'
   check_x:
 
     .local pmc x_opt
     x_opt = options['x']
     if null x_opt goto check_nth
+    if global_flag goto fail_x_global
     times = x_opt
-    if times < 0 goto x_fail
   check_nth:
 
     .local pmc nth_opt
@@ -1010,8 +1016,6 @@
     result = self
     result = clone result
 
-    if times == 0 goto subst_done
-
     # build a list of matches
     .local pmc matchlist, match
     .local int n_cnt, x_cnt
@@ -1026,12 +1030,6 @@
     $P0 = nth_opt.'ACCEPTS'(n_cnt)
     unless $P0 goto skip_push
 
-    if times < 0 goto skip_times
-
-    x_cnt += 1
-    if x_cnt > times goto matchlist_done
-  skip_times:
-
     push matchlist, match
   skip_push:
 
@@ -1046,6 +1044,25 @@
     $P0 = getinterp
     lexpad = $P0['lexpad';1]
 
+    # first figure out how many substitutions to actually perform
+    $I0 = matchlist
+    $I0 += 1
+  reduce_matches:
+    $I0 -= 1
+    if $I0 == 0 goto subst_done
+    $I1 = times.'ACCEPTS'($I0)
+    unless $I1 goto reduce_matches
+
+    # pop substitutions off the list
+    $I2 = matchlist
+    $I0 = $I2 - $I0
+  reduce_matches_loop:
+    if $I0 == 0 goto do_subst
+    $P0 = pop matchlist
+    $I0 -= 1
+    goto reduce_matches_loop
+
+  do_subst:
     # now, perform substitutions on matchlist until done
     .local int offset
     offset = 0
@@ -1076,11 +1093,8 @@
   subst_done:
     .return (result)
 
-  nth_fail:
-    die "Must pass a non-negative integer to :nth()"
-
-  x_fail:
-    die "Must pass a non-negative integer to :x()"
+  fail_x_global:
+    die "Cannot specify both :g and :x()"
 .end
 
 

Reply via email to