3083 |
3083 |
return enumerator;
|
3084 |
3084 |
}
|
3085 |
3085 |
|
|
3086 |
|
|
3087 |
struct sliceafter_arg {
|
|
3088 |
VALUE pat;
|
|
3089 |
VALUE pred;
|
|
3090 |
VALUE prev_elts;
|
|
3091 |
VALUE yielder;
|
|
3092 |
};
|
|
3093 |
|
|
3094 |
static VALUE
|
|
3095 |
sliceafter_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
|
|
3096 |
{
|
|
3097 |
#define UPDATE_MEMO ((memo = MEMO_FOR(struct sliceafter_arg, _memo)), 1)
|
|
3098 |
struct sliceafter_arg *memo;
|
|
3099 |
int split_p;
|
|
3100 |
UPDATE_MEMO;
|
|
3101 |
|
|
3102 |
ENUM_WANT_SVALUE();
|
|
3103 |
|
|
3104 |
if (NIL_P(memo->prev_elts)) {
|
|
3105 |
memo->prev_elts = rb_ary_new3(1, i);
|
|
3106 |
}
|
|
3107 |
else {
|
|
3108 |
rb_ary_push(memo->prev_elts, i);
|
|
3109 |
}
|
|
3110 |
|
|
3111 |
if (NIL_P(memo->pred)) {
|
|
3112 |
split_p = RTEST(rb_funcall(memo->pat, id_eqq, 1, i));
|
|
3113 |
UPDATE_MEMO;
|
|
3114 |
}
|
|
3115 |
else {
|
|
3116 |
split_p = RTEST(rb_funcall(memo->pred, id_call, 1, i));
|
|
3117 |
UPDATE_MEMO;
|
|
3118 |
}
|
|
3119 |
|
|
3120 |
if (split_p) {
|
|
3121 |
rb_funcall(memo->yielder, id_lshift, 1, memo->prev_elts);
|
|
3122 |
UPDATE_MEMO;
|
|
3123 |
memo->prev_elts = Qnil;
|
|
3124 |
}
|
|
3125 |
|
|
3126 |
return Qnil;
|
|
3127 |
#undef UPDATE_MEMO
|
|
3128 |
}
|
|
3129 |
|
|
3130 |
static VALUE
|
|
3131 |
sliceafter_i(RB_BLOCK_CALL_FUNC_ARGLIST(yielder, enumerator))
|
|
3132 |
{
|
|
3133 |
VALUE enumerable;
|
|
3134 |
VALUE arg;
|
|
3135 |
struct sliceafter_arg *memo = NEW_MEMO_FOR(struct sliceafter_arg, arg);
|
|
3136 |
|
|
3137 |
enumerable = rb_ivar_get(enumerator, rb_intern("sliceafter_enum"));
|
|
3138 |
memo->pat = rb_ivar_get(enumerator, rb_intern("sliceafter_pat"));
|
|
3139 |
memo->pred = rb_attr_get(enumerator, rb_intern("sliceafter_pred"));
|
|
3140 |
memo->prev_elts = Qnil;
|
|
3141 |
memo->yielder = yielder;
|
|
3142 |
|
|
3143 |
rb_block_call(enumerable, id_each, 0, 0, sliceafter_ii, arg);
|
|
3144 |
memo = MEMO_FOR(struct sliceafter_arg, arg);
|
|
3145 |
if (!NIL_P(memo->prev_elts))
|
|
3146 |
rb_funcall(memo->yielder, id_lshift, 1, memo->prev_elts);
|
|
3147 |
return Qnil;
|
|
3148 |
}
|
|
3149 |
|
|
3150 |
/*
|
|
3151 |
* call-seq:
|
|
3152 |
* enum.slice_after(pattern) -> an_enumerator
|
|
3153 |
* enum.slice_after { |elt| bool } -> an_enumerator
|
|
3154 |
*
|
|
3155 |
* Creates an enumerator for each chunked elements.
|
|
3156 |
* The ends of chunks are defined by _pattern_ and the block.
|
|
3157 |
*
|
|
3158 |
* If <code>_pattern_ === _elt_</code> returns <code>true</code> or the block
|
|
3159 |
* returns <code>true</code> for the element, the element is end of a
|
|
3160 |
* chunk.
|
|
3161 |
*
|
|
3162 |
* The <code>===</code> and _block_ is called from the first element to the last
|
|
3163 |
* element of _enum_.
|
|
3164 |
*
|
|
3165 |
* The result enumerator yields the chunked elements as an array.
|
|
3166 |
* So +each+ method can be called as follows:
|
|
3167 |
*
|
|
3168 |
* enum.slice_after(pattern).each { |ary| ... }
|
|
3169 |
* enum.slice_after { |elt| bool }.each { |ary| ... }
|
|
3170 |
*
|
|
3171 |
* Other methods of the Enumerator class and Enumerable module,
|
|
3172 |
* such as +map+, etc., are also usable.
|
|
3173 |
*
|
|
3174 |
* For example, continuation lines (lines end with backslash) can be
|
|
3175 |
* concatenated as follows:
|
|
3176 |
*
|
|
3177 |
* lines = ["foo\n", "bar\\\n", "baz\n", "\n", "qux\n"]
|
|
3178 |
* e = lines.slice_after(/(?<!\\)\n\z/)
|
|
3179 |
* p e.to_a
|
|
3180 |
* #=> [["foo\n"], ["bar\\\n", "baz\n"], ["\n"], ["qux\n"]]
|
|
3181 |
* p e.map {|ll| ll[0...-1].map {|l| l.sub(/\\\n\z/, "") }.join + ll.last }
|
|
3182 |
* #=>["foo\n", "barbaz\n", "\n", "qux\n"]
|
|
3183 |
*
|
|
3184 |
*/
|
|
3185 |
|
|
3186 |
static VALUE
|
|
3187 |
enum_slice_after(int argc, VALUE *argv, VALUE enumerable)
|
|
3188 |
{
|
|
3189 |
VALUE enumerator;
|
|
3190 |
VALUE pat = Qnil, pred = Qnil;
|
|
3191 |
|
|
3192 |
if (rb_block_given_p()) {
|
|
3193 |
if (0 < argc)
|
|
3194 |
rb_raise(rb_eArgError, "both pattan and block are given");
|
|
3195 |
pred = rb_block_proc();
|
|
3196 |
}
|
|
3197 |
else {
|
|
3198 |
rb_scan_args(argc, argv, "1", &pat);
|
|
3199 |
}
|
|
3200 |
|
|
3201 |
enumerator = rb_obj_alloc(rb_cEnumerator);
|
|
3202 |
rb_ivar_set(enumerator, rb_intern("sliceafter_enum"), enumerable);
|
|
3203 |
rb_ivar_set(enumerator, rb_intern("sliceafter_pat"), pat);
|
|
3204 |
rb_ivar_set(enumerator, rb_intern("sliceafter_pred"), pred);
|
|
3205 |
|
|
3206 |
rb_block_call(enumerator, idInitialize, 0, 0, sliceafter_i, enumerator);
|
|
3207 |
return enumerator;
|
|
3208 |
}
|
|
3209 |
|
3086 |
3210 |
/*
|
3087 |
3211 |
* The <code>Enumerable</code> mixin provides collection classes with
|
3088 |
3212 |
* several traversal and searching methods, and with the ability to
|
... | ... | |
3151 |
3275 |
rb_define_method(rb_mEnumerable, "cycle", enum_cycle, -1);
|
3152 |
3276 |
rb_define_method(rb_mEnumerable, "chunk", enum_chunk, -1);
|
3153 |
3277 |
rb_define_method(rb_mEnumerable, "slice_before", enum_slice_before, -1);
|
|
3278 |
rb_define_method(rb_mEnumerable, "slice_after", enum_slice_after, -1);
|
3154 |
3279 |
|
3155 |
3280 |
id_next = rb_intern("next");
|
3156 |
3281 |
id_call = rb_intern("call");
|