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