e576bb2002-10-11Martin Nilsson /* -*- c -*- || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information. */
08d6302002-10-10Martin Nilsson 
4cdb802001-02-23Fredrik Hübinette (Hubbe) #include "global.h" #include "main.h" #include "object.h" #include "mapping.h" #include "multiset.h" #include "svalue.h" #include "array.h" #include "pike_macros.h" #include "pike_error.h" #include "pike_memory.h" #include "interpret.h" #include "las.h" #include "gc.h" #include "stralloc.h" #include "opcodes.h" #include "program.h"
fa306e2001-05-17Henrik Grubbström (Grubba) #include "operators.h"
3451482001-05-26Henrik Grubbström (Grubba) #include "builtin_functions.h" #include "constants.h"
4cdb802001-02-23Fredrik Hübinette (Hubbe) 
f91e682014-09-10Tobias S. Josefowitz #define DEFAULT_CMOD_STORAGE
4cdb802001-02-23Fredrik Hübinette (Hubbe) DECLARATIONS
3246682003-04-18Martin Stjernholm /*! @class Iterator *! *! This is the interface for iterator objects. They implement an *! interface to a collection or stream of data items and a cursor *! that can be used to iterate over and examine individual items in
d2c3162003-04-21Henrik Grubbström (Grubba)  *! the data set.
3246682003-04-18Martin Stjernholm  *! *! Iterators are typically created to access a data set in some *! specific object, array, mapping, multiset or string. An object can *! have several iterators that access different data sets in it, or *! the same in different ways. E.g. strings have both an iterator for *! access char-by-char (@[String.Iterator]), and another for access *! over splitted substrings (@[String.SplitIterator]). *! @[lfun::_get_iterator] may be defined in an object to get an *! instance of the canonical iterator type for it. It's used by e.g.
d2c3162003-04-21Henrik Grubbström (Grubba)  *! @[foreach] to iterate over objects conveniently.
3246682003-04-18Martin Stjernholm  *! *! It's not an error to advance an iterator past the beginning or end *! of the data set; @[`!()] will only return true then, and @[index] *! and @[value] will return @[UNDEFINED]. An iterator in that state *! need not keep track of positions, so it's undefined what happens *! if it's "moved back" into the set of items. *! *! Backward movement for iterators is optional. It's supported if and *! only if @[`-()] is defined, but even then it's undefined how far *! back the iterator can move. Therefore iterators may support only a *! limited amount of backward movement, e.g. when they access a *! stream through a limited buffer. If such an iterator is moved back
0d0bab2003-04-27Martin Stjernholm  *! past the limit then it'll behave as if it's pointing entirely
3246682003-04-18Martin Stjernholm  *! outside the data set (see above). *! *! An iterator that doesn't support backward movement at all should *! throw an error if it's attempted. *! *! @seealso *! @[predef::get_iterator], @[lfun::_get_iterator], *! @[Array.Iterator], @[Mapping.Iterator], @[Multiset.Iterator], *! @[String.Iterator], @[String.SplitIterator]. */ PIKECLASS Iterator { /*! @decl void create (void|mixed data) *! *! Initialize this iterator to access a data set in @[data]. The *! type of @[data] is specific to the iterator implementation. An
d2c3162003-04-21Henrik Grubbström (Grubba)  *! iterator may also access some implicit data set, in which case
3246682003-04-18Martin Stjernholm  *! @[data] isn't specified at all. *! *! The iterator initially points to the first item in the data set, *! if there is any. *!
e1adc62007-11-07Henrik Grubbström (Grubba)  *! The iterator does not need to support being reused, so this
933ab52009-06-06Henrik Grubbström (Grubba)  *! function is typically declared @expr{protected@}.
3246682003-04-18Martin Stjernholm  */
c653902003-04-21Henrik Grubbström (Grubba)  PIKEFUN void create(void|mixed data)
95489a2008-06-29Martin Nilsson  flags ID_PROTECTED;
c653902003-04-21Henrik Grubbström (Grubba)  prototype; {}
3246682003-04-18Martin Stjernholm  /*! @decl int(0..1) `!() *! *! Returns @expr{0@} (zero) when the iterator points to an item, *! @expr{1@} otherwise. */
c653902003-04-21Henrik Grubbström (Grubba)  PIKEFUN int(0..1) `!()
95489a2008-06-29Martin Nilsson  flags ID_PROTECTED;
c653902003-04-21Henrik Grubbström (Grubba)  prototype; {}
3246682003-04-18Martin Stjernholm 
45f4eb2004-11-26Henrik Grubbström (Grubba)  /*! @decl optional Iterator `+ (int steps)
3246682003-04-18Martin Stjernholm  *! *! Returns a clone of this iterator which is advanced the specified *! number of steps. The amount may be negative to move backwards. *! If the iterator doesn't support backward movement it should *! throw an exception in that case.
933ab52009-06-06Henrik Grubbström (Grubba)  *! *! @seealso *! @[next], @[`+=], @[`-]
3246682003-04-18Martin Stjernholm  */
c653902003-04-21Henrik Grubbström (Grubba)  PIKEFUN Iterator `+(int steps)
95489a2008-06-29Martin Nilsson  flags ID_PROTECTED|ID_OPTIONAL;
c653902003-04-21Henrik Grubbström (Grubba)  prototype; {}
3246682003-04-18Martin Stjernholm 
45f4eb2004-11-26Henrik Grubbström (Grubba)  /*! @decl optional Iterator `+= (int steps)
3246682003-04-18Martin Stjernholm  *! *! Advance this iterator the specified number of steps and return *! it. The amount may be negative to move backwards. If the *! iterator doesn't support backward movement it should throw an *! exception in that case.
d2c3162003-04-21Henrik Grubbström (Grubba)  *!
e1adc62007-11-07Henrik Grubbström (Grubba)  *! @note *! @[foreach] calls this function with a step value of @expr{1@} *! if @[next()] doesn't exist for compatibility with Pike 7.6 *! and earlier.
d2c3162003-04-21Henrik Grubbström (Grubba)  *! *! @note *! @[foreach] will call this function even when the the *! iterator has more than one reference. If you want to *! loop over a copy of the iterator, you can create a *! copy by adding @expr{0@} (zero) to it: *! @code *! Iterator iterator; *! ... *! foreach(iterator+0; mixed index; mixed value) { *! ... *! } *! @endcode *! *! @note
45f4eb2004-11-26Henrik Grubbström (Grubba)  *! Even though this function is sufficient for @[foreach] to *! advance the iterator, @[next()] is the preferred API. *! @[next()] has the additional advantage of not being an *! lfun, so it is possible to advance the iterator by hand.
933ab52009-06-06Henrik Grubbström (Grubba)  *! *! @seealso *! @[next], @[`+], @[`-]
3246682003-04-18Martin Stjernholm  */
c653902003-04-21Henrik Grubbström (Grubba)  PIKEFUN Iterator `+=(int steps) prototype;
95489a2008-06-29Martin Nilsson  flags ID_PROTECTED|ID_OPTIONAL;
c653902003-04-21Henrik Grubbström (Grubba)  {}
3246682003-04-18Martin Stjernholm  /*! @decl optional Iterator `- (int steps) *! *! This lfun should be defined if and only if the iterator supports *! backward movement to some degree. It should back up the *! specified number of steps. The amount may be negative to move *! forward.
933ab52009-06-06Henrik Grubbström (Grubba)  *! *! @seealso *! @[next], @[`+], @[`+=]
3246682003-04-18Martin Stjernholm  */
c653902003-04-21Henrik Grubbström (Grubba)  PIKEFUN int(0..1) `-(int steps)
95489a2008-06-29Martin Nilsson  flags ID_PROTECTED|ID_OPTIONAL;
c653902003-04-21Henrik Grubbström (Grubba)  prototype; {}
3246682003-04-18Martin Stjernholm  /*! @decl mixed index() *! *! Returns the current index, or @[UNDEFINED] if the iterator *! doesn't point to any item. *! *! If there's no obvious index set then the index is the current
e1adc62007-11-07Henrik Grubbström (Grubba)  *! position in the data set, counting from @expr{0@} (zero).
3246682003-04-18Martin Stjernholm  */
c653902003-04-21Henrik Grubbström (Grubba)  PIKEFUN mixed index() prototype; {}
3246682003-04-18Martin Stjernholm  /*! @decl mixed value() *! *! Returns the current value, or @[UNDEFINED] if the iterator *! doesn't point to any item. */
c653902003-04-21Henrik Grubbström (Grubba)  PIKEFUN mixed value() prototype; {}
3246682003-04-18Martin Stjernholm  /*! @decl optional int _sizeof() *! *! Returns the total number of items in the data set according to *! this iterator. If the size of the data set is unlimited or *! unknown then this function shouldn't be defined. */
c653902003-04-21Henrik Grubbström (Grubba)  PIKEFUN int _sizeof() flags ID_OPTIONAL; prototype; {}
3246682003-04-18Martin Stjernholm 
d7fdf72016-03-09Martin Nilsson  /*! @decl optional void _random(function random_string, function random)
3246682003-04-18Martin Stjernholm  *! *! If this function is defined then it sets the iterator to point *! to a random item in the accessible set. The random distribution *! should be rectangular within that set, and the pseudorandom *! sequence provided by @[random] should be used. */
d7fdf72016-03-09Martin Nilsson  PIKEFUN void _random(function rnd_str, function rnd)
c653902003-04-21Henrik Grubbström (Grubba)  flags ID_OPTIONAL; prototype; {}
3246682003-04-18Martin Stjernholm 
c653902003-04-21Henrik Grubbström (Grubba)  /*! @decl optional int(0..1) first()
3246682003-04-18Martin Stjernholm  *! *! If this function is defined then it resets the iterator to point
d2c3162003-04-21Henrik Grubbström (Grubba)  *! to the first item. *! *! @returns
c679282003-04-22Johan Sundström  *! Returns zero if there are no items at all in
d2c3162003-04-21Henrik Grubbström (Grubba)  *! the data set, one otherwise.
3246682003-04-18Martin Stjernholm  *! *! @note *! It's not enough to set the iterator to the earliest accessible *! item. If the iterator doesn't support backing up to the *! original start position then this function should not be *! implemented. */
c653902003-04-21Henrik Grubbström (Grubba)  PIKEFUN int(0..1) first() flags ID_OPTIONAL; prototype; {}
3246682003-04-18Martin Stjernholm 
e1adc62007-11-07Henrik Grubbström (Grubba)  /*! @decl int next()
bdc9722003-09-04Henrik Grubbström (Grubba)  *! *! If this function is defined it should advance the iterator one *! step, just like @expr{@[`+=](1)@} would do. *!
45f4eb2004-11-26Henrik Grubbström (Grubba)  *! @note *! This is the preferred way to advance the iterator, since it *! reduces the overhead. *! *! @note
e1adc62007-11-07Henrik Grubbström (Grubba)  *! This function was optional in Pike 7.6 and earlier.
45f4eb2004-11-26Henrik Grubbström (Grubba)  *!
bdc9722003-09-04Henrik Grubbström (Grubba)  *! @returns *! Returns @tt{1@} if it succeeded in advancing, and
45f4eb2004-11-26Henrik Grubbström (Grubba)  *! @tt{0@} (zero) if it has reached the end of the iterator.
933ab52009-06-06Henrik Grubbström (Grubba)  *! *! @seealso *! @[`+], @[`+=], @[`-]
bdc9722003-09-04Henrik Grubbström (Grubba)  */ PIKEFUN int(0..1) next() prototype; {} /*! @decl optional void set_index(zero index) *! *! If this function is defined it should set the iterator at *! the specified index. *! *! @note *! It should be possible to set the index at the end of *! the iterator.
3246682003-04-18Martin Stjernholm  */
bdc9722003-09-04Henrik Grubbström (Grubba)  PIKEFUN void set_index(zero index) flags ID_OPTIONAL; prototype; {}
3246682003-04-18Martin Stjernholm }
7099022016-03-12Martin Nilsson /* Calls the random(int) function of the top of the stack with the given range limit. */
dee4a62017-04-17Henrik Grubbström (Grubba) static unsigned INT_TYPE call_random(unsigned INT_TYPE limit)
7099022016-03-12Martin Nilsson {
dee4a62017-04-17Henrik Grubbström (Grubba)  unsigned INT_TYPE value;
7099022016-03-12Martin Nilsson  push_int(limit); apply_svalue(&Pike_sp[-2], 1); if( TYPEOF(Pike_sp[-1])!=T_INT ) Pike_error("Wrong return type from random.\n");
dee4a62017-04-17Henrik Grubbström (Grubba)  value = Pike_sp[-1].u.integer;
4ae9502016-03-19Martin Nilsson  if( value >= limit ) Pike_error("random returned value out of bounds.\n");
7099022016-03-12Martin Nilsson  pop_stack(); return value; }
3246682003-04-18Martin Stjernholm /*! @endclass */
192ceb2003-11-22Henrik Grubbström (Grubba) /*! @module Mapping */ /*! @class Iterator
a6ab602004-04-06Henrik Grubbström (Grubba)  *! @inherit predef::Iterator
192ceb2003-11-22Henrik Grubbström (Grubba)  *! *! An object of this class is returned by @[get_iterator()] when *! called with a mapping. *! *! @seealso *! @[get_iterator] */
4cdb802001-02-23Fredrik Hübinette (Hubbe) PIKECLASS mapping_iterator { /* All variables *must* be before all functions! */ CVAR int bucket; CVAR struct mapping *m; CVAR struct mapping_data *md; CVAR struct keypair *current; PIKEFUN mixed value() { if(THIS->current) push_svalue(& THIS->current->val); else {
f139182003-09-05Martin Stjernholm  push_undefined();
4cdb802001-02-23Fredrik Hübinette (Hubbe)  } } PIKEFUN mixed index() { if(THIS->current) push_svalue(& THIS->current->ind); else {
f139182003-09-05Martin Stjernholm  push_undefined();
4cdb802001-02-23Fredrik Hübinette (Hubbe)  } }
a768372011-10-13Henrik Grubbström (Grubba)  PIKEFUN int _sizeof()
a9346d2017-07-19Martin Nilsson  flags ID_PROTECTED;
a768372011-10-13Henrik Grubbström (Grubba)  { if (THIS->md) { push_int(THIS->md->size);
42cf092012-10-21Arne Goedeke  } else
4a34242016-03-12Martin Nilsson  push_int(0);
a768372011-10-13Henrik Grubbström (Grubba)  }
e1adc62007-11-07Henrik Grubbström (Grubba) #ifdef PIKE_MAPPING_KEYPAIR_LOOP static int mi_step(struct mapping_iterator_struct *i) { if(i->current == i->md->free_list) return 0; if (++i->current == i->md->free_list) { return 0; } return 1; } #else /* !PIKE_MAPPING_KEYPAIR_LOOP */
4cdb802001-02-23Fredrik Hübinette (Hubbe)  static int step_bucket(struct mapping_iterator_struct *i) {
89a6cd2002-12-29Henrik Grubbström (Grubba)  if (i->md) {
adb1ea2006-11-07Martin Stjernholm  do { while(!i->current) { i->bucket++; if(i->bucket >= i->md->hashsize) return 0; i->current=i->md->hash[i->bucket]; } if (!IS_DESTRUCTED (&i->current->ind)) break; i->current = i->current->next; } while (1);
4cdb802001-02-23Fredrik Hübinette (Hubbe)  } return 1; } static int mi_step(struct mapping_iterator_struct *i) { if(! i->current) return 0; i->current=i->current->next; return step_bucket(i); }
e1adc62007-11-07Henrik Grubbström (Grubba) #endif /* PIKE_MAPPING_KEYPAIR_LOOP */
13670c2015-05-25Martin Nilsson 
3246682003-04-18Martin Stjernholm  PIKEFUN object `+(int(0..) steps)
4cdb802001-02-23Fredrik Hübinette (Hubbe)  { struct object *o=low_clone(mapping_iterator_program);
3246682003-04-18Martin Stjernholm  if (steps < 0)
de22f72014-08-25Martin Nilsson  SIMPLE_ARG_ERROR ("`+", 1,
3246682003-04-18Martin Stjernholm  "This iterator doesn't support going backwards.\n");
4cdb802001-02-23Fredrik Hübinette (Hubbe)  OBJ2_MAPPING_ITERATOR(o)[0] = *THIS;
89a6cd2002-12-29Henrik Grubbström (Grubba)  if (THIS->m) add_ref(THIS->m); if (THIS->md) { add_ref(THIS->md); THIS->md->valrefs++; }
4cdb802001-02-23Fredrik Hübinette (Hubbe)  while(--steps>=0 && mi_step(OBJ2_MAPPING_ITERATOR(o))); RETURN o; }
3246682003-04-18Martin Stjernholm  PIKEFUN object `+=(int(0..) steps)
4cdb802001-02-23Fredrik Hübinette (Hubbe)  {
3246682003-04-18Martin Stjernholm  if (steps < 0)
de22f72014-08-25Martin Nilsson  SIMPLE_ARG_ERROR ("`+=", 1,
3246682003-04-18Martin Stjernholm  "This iterator doesn't support going backwards.\n");
4cdb802001-02-23Fredrik Hübinette (Hubbe)  while(--steps>=0 && mi_step(THIS)); REF_RETURN Pike_fp->current_object; } PIKEFUN int first() {
e1adc62007-11-07Henrik Grubbström (Grubba) #ifdef PIKE_MAPPING_KEYPAIR_LOOP THIS->current = MD_KEYPAIRS(THIS->md, THIS->md->hashsize); RETURN (THIS->current < THIS->md->free_list); #else /* !PIKE_MAPPING_KEYPAIR_LOOP */
4cdb802001-02-23Fredrik Hübinette (Hubbe)  THIS->current=0; THIS->bucket=-1; RETURN step_bucket(THIS);
e1adc62007-11-07Henrik Grubbström (Grubba) #endif /* PIKE_MAPPING_KEYPAIR_LOOP */
4cdb802001-02-23Fredrik Hübinette (Hubbe)  }
7c44072001-06-05Fredrik Hübinette (Hubbe)  /* Hubbe: Should this really be destructive ?? */
d7fdf72016-03-09Martin Nilsson  PIKEFUN object _random(function rnd_str, function rnd)
7c44072001-06-05Fredrik Hübinette (Hubbe)  {
e1adc62007-11-07Henrik Grubbström (Grubba) #ifdef PIKE_MAPPING_KEYPAIR_LOOP
7099022016-03-12Martin Nilsson  size_t k = call_random(THIS->md->size);
e1adc62007-11-07Henrik Grubbström (Grubba)  THIS->current = MD_KEYPAIRS(THIS->md, THIS->md->hashsize) + k; #else /* !PIKE_MAPPING_KEYPAIR_LOOP */
89a6cd2002-12-29Henrik Grubbström (Grubba)  if(THIS->md && THIS->md->hashsize)
7c44072001-06-05Fredrik Hübinette (Hubbe)  {
b54d9e2016-03-15Martin Nilsson  int count = call_random(THIS->md->size);
3926242016-09-26Stephen R. van den Berg  int b; for(b=0; b<THIS->md->hashsize; b++) { struct keypair *k; for(k=THIS->md->hash[THIS->bucket]; k; k=k->next)
b54d9e2016-03-15Martin Nilsson  if( count-- < 0 ) { THIS->bucket = b; THIS->current = k; goto done; }
3926242016-09-26Stephen R. van den Berg  }
7c44072001-06-05Fredrik Hübinette (Hubbe)  }else{ THIS->bucket=-1; THIS->current=0; }
b54d9e2016-03-15Martin Nilsson  done:
7c44072001-06-05Fredrik Hübinette (Hubbe)  step_bucket(THIS);
e1adc62007-11-07Henrik Grubbström (Grubba) #endif /* PIKE_MAPPING_KEYPAIR_LOOP */
7c44072001-06-05Fredrik Hübinette (Hubbe)  REF_RETURN Pike_fp->current_object; }
4cdb802001-02-23Fredrik Hübinette (Hubbe)  PIKEFUN int next() { RETURN mi_step(THIS); }
e1adc62007-11-07Henrik Grubbström (Grubba)  PIKEFUN int `!() { #ifdef PIKE_MAPPING_KEYPAIR_LOOP RETURN (THIS->current == THIS->md->free_list); #else /* !PIKE_MAPPING_KEYPAIR_LOOP */ RETURN !THIS->current; #endif /* PIKE_MAPPING_KEYPAIR_LOOP */ }
4cdb802001-02-23Fredrik Hübinette (Hubbe)  PIKEFUN void create(mapping map) { if(THIS->m) Pike_error("Mapping iterators cannot be reused.\n"); add_ref(THIS->m=map); THIS->md=map->data; add_ref(THIS->md); THIS->md->valrefs++;
e1adc62007-11-07Henrik Grubbström (Grubba) #ifdef PIKE_MAPPING_KEYPAIR_LOOP THIS->current = MD_KEYPAIRS(THIS->md, THIS->md->hashsize); #else /* !PIKE_MAPPING_KEYPAIR_LOOP */ THIS->current = NULL;
4cdb802001-02-23Fredrik Hübinette (Hubbe)  THIS->bucket=-1; step_bucket(THIS);
e1adc62007-11-07Henrik Grubbström (Grubba) #endif /* PIKE_MAPPING_KEYPAIR_LOOP */
b498ad2017-12-28Martin Nilsson  pop_stack();
4cdb802001-02-23Fredrik Hübinette (Hubbe)  }
b467522017-06-25Martin Nilsson #ifdef PIKE_NULL_IS_SPECIAL
4cdb802001-02-23Fredrik Hübinette (Hubbe)  INIT { THIS->m=0; THIS->md=0; THIS->current=0; THIS->bucket=0; }
b467522017-06-25Martin Nilsson #endif
4cdb802001-02-23Fredrik Hübinette (Hubbe)  EXIT
8dcb7d2008-05-29Martin Stjernholm  gc_trivial;
4cdb802001-02-23Fredrik Hübinette (Hubbe)  {
89a6cd2002-12-29Henrik Grubbström (Grubba)  if (THIS->md) { THIS->md->valrefs--; free_mapping_data(THIS->md); } if (THIS->m) { free_mapping(THIS->m); }
4cdb802001-02-23Fredrik Hübinette (Hubbe)  } };
192ceb2003-11-22Henrik Grubbström (Grubba) /*! @endclass */ /*! @endmodule */
4cdb802001-02-23Fredrik Hübinette (Hubbe) 
a8c8b82003-07-24Henrik Grubbström (Grubba) /*! @module Array */ /*! @class Iterator
a6ab602004-04-06Henrik Grubbström (Grubba)  *! @inherit predef::Iterator
a8c8b82003-07-24Henrik Grubbström (Grubba)  *! *! An object of this class is returned by @[get_iterator()] when *! called with an array. *! *! @seealso *! @[get_iterator] */
4cdb802001-02-23Fredrik Hübinette (Hubbe) PIKECLASS array_iterator { CVAR int pos; CVAR struct array *a;
13670c2015-05-25Martin Nilsson 
4cdb802001-02-23Fredrik Hübinette (Hubbe)  PIKEFUN mixed value() {
3246682003-04-18Martin Stjernholm  if(!THIS->a || THIS->pos < 0 || THIS->pos >= THIS->a->size)
4cdb802001-02-23Fredrik Hübinette (Hubbe)  {
3246682003-04-18Martin Stjernholm  push_undefined();
4cdb802001-02-23Fredrik Hübinette (Hubbe)  }else{ push_svalue(THIS->a->item + THIS->pos); } } PIKEFUN int index() {
3246682003-04-18Martin Stjernholm  if(!THIS->a || THIS->pos < 0 || THIS->pos >= THIS->a->size)
4cdb802001-02-23Fredrik Hübinette (Hubbe)  {
3246682003-04-18Martin Stjernholm  push_undefined();
4cdb802001-02-23Fredrik Hübinette (Hubbe)  }else{ RETURN THIS->pos; } }
a768372011-10-13Henrik Grubbström (Grubba)  PIKEFUN int _sizeof()
a9346d2017-07-19Martin Nilsson  flags ID_PROTECTED;
a768372011-10-13Henrik Grubbström (Grubba)  { if (THIS->a) { push_int(THIS->a->size); }
365b3c2016-03-10Martin Nilsson  else
4a34242016-03-12Martin Nilsson  push_int(0);
a768372011-10-13Henrik Grubbström (Grubba)  }
4cdb802001-02-23Fredrik Hübinette (Hubbe)  PIKEFUN object `+(int steps) { struct object *o=low_clone(array_iterator_program); OBJ2_ARRAY_ITERATOR(o)[0]=*THIS;
e3ab572003-08-20Henrik Grubbström (Grubba)  if (THIS->a) add_ref(THIS->a);
4cdb802001-02-23Fredrik Hübinette (Hubbe)  OBJ2_ARRAY_ITERATOR(o)->pos+=steps; RETURN o; } PIKEFUN object `+=(int steps) { THIS->pos+=steps; REF_RETURN Pike_fp->current_object; }
3246682003-04-18Martin Stjernholm  PIKEFUN object `-(int steps) { struct object *o=low_clone(array_iterator_program); OBJ2_ARRAY_ITERATOR(o)[0]=*THIS; add_ref(THIS->a); OBJ2_ARRAY_ITERATOR(o)->pos-=steps; RETURN o; }
4cdb802001-02-23Fredrik Hübinette (Hubbe)  PIKEFUN int first() { THIS->pos=0;
3246682003-04-18Martin Stjernholm  RETURN THIS->a && THIS->pos < THIS->a->size;
4cdb802001-02-23Fredrik Hübinette (Hubbe)  }
7c44072001-06-05Fredrik Hübinette (Hubbe) 
bdc9722003-09-04Henrik Grubbström (Grubba)  PIKEFUN void set_index(int pos) { if(!THIS->a || pos < 0 || pos > THIS->a->size) { Pike_error("Bad position.\n"); }else{ THIS->pos = pos;
13670c2015-05-25Martin Nilsson  }
bdc9722003-09-04Henrik Grubbström (Grubba)  }
7c44072001-06-05Fredrik Hübinette (Hubbe)  /* Hubbe: Should this really be destructive ?? */
d7fdf72016-03-09Martin Nilsson  PIKEFUN object _random(function rnd_str, function rnd)
7c44072001-06-05Fredrik Hübinette (Hubbe)  {
e3ab572003-08-20Henrik Grubbström (Grubba)  if(THIS->a && THIS->a->size)
7099022016-03-12Martin Nilsson  THIS->pos=call_random(THIS->a->size);
7c44072001-06-05Fredrik Hübinette (Hubbe)  else THIS->pos=0; REF_RETURN Pike_fp->current_object; }
4cdb802001-02-23Fredrik Hübinette (Hubbe)  PIKEFUN int next() { THIS->pos++;
3246682003-04-18Martin Stjernholm  RETURN THIS->a && THIS->pos >= 0 && THIS->pos < THIS->a->size;
4cdb802001-02-23Fredrik Hübinette (Hubbe)  } PIKEFUN int `!() {
3246682003-04-18Martin Stjernholm  RETURN !(THIS->a && THIS->pos >= 0 && THIS->pos < THIS->a->size);
4cdb802001-02-23Fredrik Hübinette (Hubbe)  } PIKEFUN void create(array a) { if(THIS->a) Pike_error("Array iterators cannot be reused.\n");
13670c2015-05-25Martin Nilsson 
4cdb802001-02-23Fredrik Hübinette (Hubbe)  add_ref(THIS->a=a);
b498ad2017-12-28Martin Nilsson  pop_stack();
4cdb802001-02-23Fredrik Hübinette (Hubbe)  }
13670c2015-05-25Martin Nilsson 
b467522017-06-25Martin Nilsson #ifdef PIKE_NULL_IS_SPECIAL
13670c2015-05-25Martin Nilsson  INIT
4cdb802001-02-23Fredrik Hübinette (Hubbe)  { THIS->a=0; THIS->pos=0; }
b467522017-06-25Martin Nilsson #endif
4cdb802001-02-23Fredrik Hübinette (Hubbe)  EXIT
8dcb7d2008-05-29Martin Stjernholm  gc_trivial;
4cdb802001-02-23Fredrik Hübinette (Hubbe)  {
e3ab572003-08-20Henrik Grubbström (Grubba)  if (THIS->a) free_array(THIS->a);
4cdb802001-02-23Fredrik Hübinette (Hubbe)  }
13670c2015-05-25Martin Nilsson 
4cdb802001-02-23Fredrik Hübinette (Hubbe) };
a8c8b82003-07-24Henrik Grubbström (Grubba) /*! @endclass */ /*! @endmodule */
192ceb2003-11-22Henrik Grubbström (Grubba) /*! @module Multiset */ /*! @class Iterator
a6ab602004-04-06Henrik Grubbström (Grubba)  *! @inherit predef::Iterator
192ceb2003-11-22Henrik Grubbström (Grubba)  *! *! An object of this class is returned by @[get_iterator()] when *! called with a multiset. *! *! @seealso *! @[get_iterator] */
5b15bb2001-12-10Martin Stjernholm PIKECLASS multiset_iterator { CVAR struct multiset *l; CVAR int lock_index; CVAR ptrdiff_t nodepos; PIKEFUN int value() { if (!THIS->l) Pike_error ("Iterator not initialized.\n"); if (THIS->nodepos < 0 || msnode_is_deleted (THIS->l, THIS->nodepos)) push_undefined(); else push_multiset_value (THIS->l, THIS->nodepos); } PIKEFUN mixed index() { if (!THIS->l) Pike_error ("Iterator not initialized.\n"); if (THIS->nodepos < 0 || msnode_is_deleted (THIS->l, THIS->nodepos)) push_undefined(); else push_multiset_index (THIS->l, THIS->nodepos); }
a768372011-10-13Henrik Grubbström (Grubba)  PIKEFUN int _sizeof()
a9346d2017-07-19Martin Nilsson  flags ID_PROTECTED;
a768372011-10-13Henrik Grubbström (Grubba)  { if (THIS->l) { push_int(THIS->l->msd->size); }
365b3c2016-03-10Martin Nilsson  else
4a34242016-03-12Martin Nilsson  push_int(0);
a768372011-10-13Henrik Grubbström (Grubba)  }
73354b2001-12-10Martin Stjernholm  static struct object *li_copy (struct multiset_iterator_struct *li) { struct object *o = low_clone (multiset_iterator_program); struct multiset_iterator_struct *copy = OBJ2_MULTISET_ITERATOR (o);
beec6f2016-02-24Henrik Grubbström (Grubba)  if (!(copy->l = li->l)) return o; add_ref(copy->l);
73354b2001-12-10Martin Stjernholm  if ((copy->nodepos = li->nodepos) >= 0) add_msnode_ref (copy->l); if ((copy->lock_index = li->lock_index)) { add_ref (copy->l->msd); copy->l->msd->noval_refs++; } return o; }
5b15bb2001-12-10Martin Stjernholm  static void li_step (struct multiset_iterator_struct *li, int steps) { ptrdiff_t newpos = li->nodepos;
73354b2001-12-10Martin Stjernholm  if (li->nodepos < 0) return;
5b15bb2001-12-10Martin Stjernholm  if (steps > 0) do { newpos = multiset_next (li->l, newpos); if (newpos < 0) { sub_msnode_ref (li->l);
73354b2001-12-10Martin Stjernholm  break;
5b15bb2001-12-10Martin Stjernholm  } } while (--steps); else if (steps < 0) do { newpos = multiset_prev (li->l, newpos); if (newpos < 0) { sub_msnode_ref (li->l);
73354b2001-12-10Martin Stjernholm  break;
5b15bb2001-12-10Martin Stjernholm  } } while (++steps); li->nodepos = newpos; }
73354b2001-12-10Martin Stjernholm  PIKEFUN object `+= (int steps) { if (!THIS->l) Pike_error ("Iterator not initialized.\n"); li_step (THIS, steps); REF_RETURN Pike_fp->current_object; }
5b15bb2001-12-10Martin Stjernholm  PIKEFUN object `+ (int steps) { struct object *o; if (!THIS->l) Pike_error ("Iterator not initialized.\n");
73354b2001-12-10Martin Stjernholm  o = li_copy (THIS);
5b15bb2001-12-10Martin Stjernholm  li_step (OBJ2_MULTISET_ITERATOR (o), steps); RETURN o; }
73354b2001-12-10Martin Stjernholm  PIKEFUN object `- (int steps)
5b15bb2001-12-10Martin Stjernholm  {
73354b2001-12-10Martin Stjernholm  struct object *o;
5b15bb2001-12-10Martin Stjernholm  if (!THIS->l) Pike_error ("Iterator not initialized.\n");
73354b2001-12-10Martin Stjernholm  o = li_copy (THIS); li_step (OBJ2_MULTISET_ITERATOR (o), -steps); RETURN o;
5b15bb2001-12-10Martin Stjernholm  }
73354b2001-12-10Martin Stjernholm  PIKEFUN int first()
5b15bb2001-12-10Martin Stjernholm  { if (!THIS->l) Pike_error ("Iterator not initialized.\n");
73354b2001-12-10Martin Stjernholm  if (THIS->nodepos >= 0) sub_msnode_ref (THIS->l); THIS->nodepos = multiset_first (THIS->l); RETURN THIS->nodepos >= 0;
5b15bb2001-12-10Martin Stjernholm  } PIKEFUN int next() { if (!THIS->l) Pike_error ("Iterator not initialized.\n"); if (THIS->nodepos >= 0) { THIS->nodepos = multiset_next (THIS->l, THIS->nodepos); if (THIS->nodepos >= 0) RETURN 1; sub_msnode_ref (THIS->l); } RETURN 0; } PIKEFUN int `!() { if (!THIS->l) Pike_error ("Iterator not initialized.\n"); RETURN THIS->nodepos < 0 || msnode_is_deleted (THIS->l, THIS->nodepos); }
73354b2001-12-10Martin Stjernholm  /* Hubbe: Should this really be destructive ?? * I let this question stand; I'm only adapting multiset_iterator. /mast */
d7fdf72016-03-09Martin Nilsson  PIKEFUN object _random(function rnd_str, function rnd)
73354b2001-12-10Martin Stjernholm  { if (!THIS->l) Pike_error ("Iterator not initialized.\n"); if (THIS->nodepos >= 0) { sub_msnode_ref (THIS->l); THIS->nodepos = -1; } if (!multiset_is_empty (THIS->l)) THIS->nodepos =
7099022016-03-12Martin Nilsson  multiset_get_nth (THIS->l, call_random(multiset_sizeof (THIS->l)));
73354b2001-12-10Martin Stjernholm  REF_RETURN Pike_fp->current_object; }
5b15bb2001-12-10Martin Stjernholm  PIKEFUN void lock_index() { if (!THIS->l) Pike_error ("Iterator not initialized.\n"); if (!THIS->lock_index) { add_ref (THIS->l->msd); THIS->l->msd->noval_refs++; THIS->lock_index = 1; } } PIKEFUN void unlock_index() { if (!THIS->l) Pike_error ("Iterator not initialized.\n"); if (THIS->lock_index) { THIS->l->msd->noval_refs--; sub_ref (THIS->l->msd); #ifdef PIKE_DEBUG
d631b82002-12-01Martin Stjernholm  if (THIS->l->msd->refs <= 0) Pike_fatal ("msd ran out of refs unexpectedly.\n");
5b15bb2001-12-10Martin Stjernholm #endif THIS->lock_index = 0; } } /* FIXME: Add more functions, e.g. insert, delete, add. */ /* FIXME: Maybe the index should be locked when the iterator is used * in foreach, to behave more like the mapping iterator. */ PIKEFUN void create (multiset l) { if (THIS->l) Pike_error ("Multiset iterators cannot be reused.\n"); add_ref (THIS->l = l); THIS->lock_index = 0;
73354b2001-12-10Martin Stjernholm  THIS->nodepos = multiset_first (THIS->l);
b498ad2017-12-28Martin Nilsson  pop_stack();
5b15bb2001-12-10Martin Stjernholm  }
b467522017-06-25Martin Nilsson #ifdef PIKE_NULL_IS_SPECIAL
13670c2015-05-25Martin Nilsson  INIT
5b15bb2001-12-10Martin Stjernholm  { THIS->l = NULL; }
b467522017-06-25Martin Nilsson #endif
5b15bb2001-12-10Martin Stjernholm  EXIT
8dcb7d2008-05-29Martin Stjernholm  gc_trivial;
5b15bb2001-12-10Martin Stjernholm  { if (THIS->l) { if (THIS->nodepos >= 0) sub_msnode_ref (THIS->l); if (THIS->lock_index) { THIS->l->msd->noval_refs--; sub_ref (THIS->l->msd); #ifdef PIKE_DEBUG
d631b82002-12-01Martin Stjernholm  if (THIS->l->msd->refs <= 0) Pike_fatal ("msd ran out of refs unexpectedly.\n");
5b15bb2001-12-10Martin Stjernholm #endif } free_multiset (THIS->l); } } };
192ceb2003-11-22Henrik Grubbström (Grubba) /*! @endclass */ /*! @endmodule */
5b15bb2001-12-10Martin Stjernholm 
192ceb2003-11-22Henrik Grubbström (Grubba) /*! @module String */ /*! @class Iterator
a6ab602004-04-06Henrik Grubbström (Grubba)  *! @inherit predef::Iterator
192ceb2003-11-22Henrik Grubbström (Grubba)  *! *! An object of this class is returned by @[get_iterator()] when *! called with a string. *! *! @seealso *! @[get_iterator] */
4cdb802001-02-23Fredrik Hübinette (Hubbe) PIKECLASS string_iterator { CVAR int pos; CVAR struct pike_string *s;
13670c2015-05-25Martin Nilsson 
4cdb802001-02-23Fredrik Hübinette (Hubbe)  PIKEFUN int value() {
3246682003-04-18Martin Stjernholm  if(!THIS->s || THIS->pos < 0 || THIS->pos >= THIS->s->len)
4cdb802001-02-23Fredrik Hübinette (Hubbe)  {
f139182003-09-05Martin Stjernholm  push_undefined();
4cdb802001-02-23Fredrik Hübinette (Hubbe)  }else{ RETURN index_shared_string(THIS->s, THIS->pos); } } PIKEFUN int index() {
3246682003-04-18Martin Stjernholm  if(!THIS->s || THIS->pos < 0 || THIS->pos >= THIS->s->len)
4cdb802001-02-23Fredrik Hübinette (Hubbe)  {
f139182003-09-05Martin Stjernholm  push_undefined();
4cdb802001-02-23Fredrik Hübinette (Hubbe)  }else{ RETURN THIS->pos; } }
a768372011-10-13Henrik Grubbström (Grubba)  PIKEFUN int _sizeof()
a9346d2017-07-19Martin Nilsson  flags ID_PROTECTED;
a768372011-10-13Henrik Grubbström (Grubba)  { if (THIS->s) { push_int(THIS->s->len); }
365b3c2016-03-10Martin Nilsson  else
4a34242016-03-12Martin Nilsson  push_int(0);
a768372011-10-13Henrik Grubbström (Grubba)  }
4cdb802001-02-23Fredrik Hübinette (Hubbe)  PIKEFUN object `+(int steps) { struct object *o=low_clone(string_iterator_program); OBJ2_STRING_ITERATOR(o)[0]=*THIS; add_ref(THIS->s); OBJ2_STRING_ITERATOR(o)->pos+=steps; RETURN o; } PIKEFUN object `+=(int steps) { THIS->pos+=steps; REF_RETURN Pike_fp->current_object; }
3246682003-04-18Martin Stjernholm  PIKEFUN object `-(int steps) { struct object *o=low_clone(string_iterator_program); OBJ2_STRING_ITERATOR(o)[0]=*THIS; add_ref(THIS->s); OBJ2_STRING_ITERATOR(o)->pos-=steps; RETURN o; }
4cdb802001-02-23Fredrik Hübinette (Hubbe)  PIKEFUN int first() { THIS->pos=0;
3246682003-04-18Martin Stjernholm  RETURN THIS->s && THIS->pos < THIS->s->len;
4cdb802001-02-23Fredrik Hübinette (Hubbe)  }
7c44072001-06-05Fredrik Hübinette (Hubbe) 
bdc9722003-09-04Henrik Grubbström (Grubba)  PIKEFUN void set_index(int pos) { if (!THIS->s || pos < 0 || pos > THIS->s->len) { Pike_error("Bad position.\n"); } THIS->pos = pos; }
7c44072001-06-05Fredrik Hübinette (Hubbe)  /* Hubbe: Should this really be destructive ?? */
d7fdf72016-03-09Martin Nilsson  PIKEFUN object _random(function rnd_str, function rnd)
7c44072001-06-05Fredrik Hübinette (Hubbe)  { if(THIS->s->len)
7099022016-03-12Martin Nilsson  THIS->pos=call_random(THIS->s->len);
7c44072001-06-05Fredrik Hübinette (Hubbe)  else THIS->pos=0; REF_RETURN Pike_fp->current_object; }
4cdb802001-02-23Fredrik Hübinette (Hubbe)  PIKEFUN int next() { THIS->pos++;
3246682003-04-18Martin Stjernholm  RETURN THIS->s && THIS->pos >= 0 && THIS->pos < THIS->s->len;
4cdb802001-02-23Fredrik Hübinette (Hubbe)  } PIKEFUN int `!() {
3246682003-04-18Martin Stjernholm  RETURN !(THIS->s && THIS->pos >= 0 && THIS->pos < THIS->s->len);
4cdb802001-02-23Fredrik Hübinette (Hubbe)  }
e33f372003-09-04Henrik Grubbström (Grubba)  PIKEFUN int _search(string|int needle, int|void start)
95489a2008-06-29Martin Nilsson  flags ID_PROTECTED;
e33f372003-09-04Henrik Grubbström (Grubba)  { if (!THIS->s) Pike_error("No data to search.\n"); ref_push_string(THIS->s); push_svalue(needle); if (start) push_svalue(start); else push_int(THIS->pos); f_search(3); stack_pop_n_elems_keep_top(args); /* Advance to the found position. */
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(Pike_sp[-1]) == T_INT) {
e33f372003-09-04Henrik Grubbström (Grubba)  if (Pike_sp[-1].u.integer >= 0) { THIS->pos = Pike_sp[-1].u.integer; } else { THIS->pos = THIS->s->len; } } }
4cdb802001-02-23Fredrik Hübinette (Hubbe)  PIKEFUN void create(string s) { if(THIS->s) Pike_error("String iterators cannot be reused.\n");
13670c2015-05-25Martin Nilsson 
4cdb802001-02-23Fredrik Hübinette (Hubbe)  add_ref(THIS->s=s);
b498ad2017-12-28Martin Nilsson  pop_stack();
4cdb802001-02-23Fredrik Hübinette (Hubbe)  }
13670c2015-05-25Martin Nilsson 
b467522017-06-25Martin Nilsson #ifdef PIKE_NULL_IS_SPECIAL
13670c2015-05-25Martin Nilsson  INIT
4cdb802001-02-23Fredrik Hübinette (Hubbe)  { THIS->s=0; THIS->pos=0; }
b467522017-06-25Martin Nilsson #endif
4cdb802001-02-23Fredrik Hübinette (Hubbe)  EXIT
8dcb7d2008-05-29Martin Stjernholm  gc_trivial;
4cdb802001-02-23Fredrik Hübinette (Hubbe)  {
b71c722009-04-01Martin Stjernholm  if (THIS->s) free_string(THIS->s);
4cdb802001-02-23Fredrik Hübinette (Hubbe)  } };
192ceb2003-11-22Henrik Grubbström (Grubba) /*! @endclass */ /*! @endmodule */
66a34a2001-06-16Per Hedbor /* Interal (for Stdio.File) class: Stdio.File->line_iterator. * Splits on lines, does only handle 8-bit characters. */ PIKECLASS file_line_iterator { CVAR struct pike_string *buffer; CVAR struct pike_string *current; CVAR int offset; CVAR int index; CVAR struct svalue feed; static void fl_find_next(struct file_line_iterator_struct *ssi) { unsigned char *ptr, *eptr, *start; int start_off = ssi->offset; if (ssi->current) { free_string(ssi->current); ssi->current = NULL; } if (!ssi->buffer) return; again:
42c52e2007-11-11Martin Nilsson  start = (unsigned char*)ssi->buffer->str + start_off; ptr = (unsigned char*)ssi->buffer->str + ssi->offset; eptr = (unsigned char*)ssi->buffer->str + ssi->buffer->len;
66a34a2001-06-16Per Hedbor  /* Loop until we find a '\n'. */ while( ptr < eptr ) if( *ptr != '\n' ) ptr++; else {
42c52e2007-11-11Martin Nilsson  ssi->current = make_shared_binary_string( (char*)start, ptr-start );
66a34a2001-06-16Per Hedbor  // skip the '\n' ssi->offset = ptr - ((unsigned char *)ssi->buffer->str) + 1; ssi->index++; return; } apply_svalue( &ssi->feed, 0 );
017b572011-10-28Henrik Grubbström (Grubba)  if( TYPEOF(Pike_sp[-1]) == PIKE_T_STRING &&
66a34a2001-06-16Per Hedbor  Pike_sp[-1].u.string->len ) { push_string(make_shared_binary_string( ssi->buffer->str+start_off, ssi->buffer->len-start_off)); free_string( ssi->buffer ); stack_swap(); f_add( 2 ); ssi->buffer = Pike_sp[-1].u.string;
50ea682003-03-14Henrik Grubbström (Grubba)  dmalloc_touch_svalue(Pike_sp-1);
66a34a2001-06-16Per Hedbor  Pike_sp--; if( !ssi->buffer->size_shift ) { ssi->offset -= start_off; start_off = 0; goto again; }
9a88532002-12-31Henrik Grubbström (Grubba)  // Wide string.
66a34a2001-06-16Per Hedbor  }
9a88532002-12-31Henrik Grubbström (Grubba)  else { // End of stream.
66a34a2001-06-16Per Hedbor  pop_stack();
9a88532002-12-31Henrik Grubbström (Grubba)  if (start != ptr) {
42c52e2007-11-11Martin Nilsson  ssi->current = make_shared_binary_string((char*)start, ptr-start);
9a88532002-12-31Henrik Grubbström (Grubba)  ssi->index++; } }
66a34a2001-06-16Per Hedbor  free_string( ssi->buffer ); ssi->buffer = NULL; return; }
13670c2015-05-25Martin Nilsson 
66a34a2001-06-16Per Hedbor  PIKEFUN string value() { if (THIS->current) { ref_push_string(THIS->current); } else {
f139182003-09-05Martin Stjernholm  push_undefined();
66a34a2001-06-16Per Hedbor  } } PIKEFUN int index() { if (!THIS->current) {
f139182003-09-05Martin Stjernholm  push_undefined();
66a34a2001-06-16Per Hedbor  }else{ RETURN THIS->index; } }
3246682003-04-18Martin Stjernholm  PIKEFUN object `+(int(0..) steps)
66a34a2001-06-16Per Hedbor  { struct object *o=low_clone(file_line_iterator_program); struct file_line_iterator_struct *ssi;
3246682003-04-18Martin Stjernholm  if (steps < 0)
de22f72014-08-25Martin Nilsson  SIMPLE_ARG_ERROR ("`+", 1,
3246682003-04-18Martin Stjernholm  "This iterator doesn't support going backwards.\n");
66a34a2001-06-16Per Hedbor  (ssi = OBJ2_FILE_LINE_ITERATOR(o))[0]=*THIS; if (THIS->buffer) { add_ref(THIS->buffer); } if (THIS->current) { add_ref(THIS->current); } add_ref_svalue(&THIS->feed); while( steps-- ) fl_find_next(ssi); RETURN o; }
3246682003-04-18Martin Stjernholm  PIKEFUN object `+=(int(0..) steps)
66a34a2001-06-16Per Hedbor  {
3246682003-04-18Martin Stjernholm  if (steps < 0)
de22f72014-08-25Martin Nilsson  SIMPLE_ARG_ERROR ("`+=", 1,
3246682003-04-18Martin Stjernholm  "This iterator doesn't support going backwards.\n");
66a34a2001-06-16Per Hedbor  while( steps-- ) fl_find_next(THIS); REF_RETURN Pike_fp->current_object; } PIKEFUN int next() { fl_find_next(THIS); RETURN !!THIS->current; } PIKEFUN int `!() { RETURN !THIS->current; }
ac78582005-10-17Martin Nilsson  PIKEFUN void create(function(:string) feed)
66a34a2001-06-16Per Hedbor  { if (THIS->buffer) { Pike_error("iterators cannot be reused.\n"); } assign_svalue( &THIS->feed, feed ); apply_svalue( &THIS->feed, 0 );
017b572011-10-28Henrik Grubbström (Grubba)  if( TYPEOF(Pike_sp[-1]) != PIKE_T_STRING )
66a34a2001-06-16Per Hedbor  Pike_error("Feed function returned illegal value\n"); THIS->buffer = Pike_sp[-1].u.string;
50ea682003-03-14Henrik Grubbström (Grubba)  dmalloc_touch_svalue(Pike_sp-1);
66a34a2001-06-16Per Hedbor  Pike_sp--; THIS->offset = 0; THIS->current = NULL; THIS->index = -1; fl_find_next(THIS);
b498ad2017-12-28Martin Nilsson  pop_stack();
66a34a2001-06-16Per Hedbor  }
13670c2015-05-25Martin Nilsson  INIT
66a34a2001-06-16Per Hedbor  {
b467522017-06-25Martin Nilsson #ifdef PIKE_NULL_IS_SPECIAL
66a34a2001-06-16Per Hedbor  THIS->buffer = NULL; THIS->current = NULL; THIS->offset = 0; THIS->index = 0;
b467522017-06-25Martin Nilsson #endif
1ab4ac2008-01-26Martin Stjernholm  mark_free_svalue (&THIS->feed);
66a34a2001-06-16Per Hedbor  } EXIT
8dcb7d2008-05-29Martin Stjernholm  gc_trivial;
66a34a2001-06-16Per Hedbor  {
40338f2017-07-10Martin Nilsson  if (THIS->buffer)
66a34a2001-06-16Per Hedbor  free_string(THIS->buffer);
40338f2017-07-10Martin Nilsson  if (THIS->current)
66a34a2001-06-16Per Hedbor  free_string(THIS->current); free_svalue(&THIS->feed);
1ab4ac2008-01-26Martin Stjernholm  assert_free_svalue (&THIS->feed);
66a34a2001-06-16Per Hedbor  } }
fa306e2001-05-17Henrik Grubbström (Grubba) /*! @module String */ /*! @class SplitIterator
a6ab602004-04-06Henrik Grubbström (Grubba)  *! @inherit predef::Iterator *!
fbf10a2004-04-06Martin Nilsson  *! An iterator that iterates over substrings of a string, separated *! by a character or several different characters.
a6ab602004-04-06Henrik Grubbström (Grubba)  *!
fbf10a2004-04-06Martin Nilsson  *! @note
a6ab602004-04-06Henrik Grubbström (Grubba)  *! Typically you don't need to explicitly use the SplitIterator. *! Expressions like the following are automatically optimized into *! using a SplitIterator. *! @code *! foreach(str/"\n", string line) *! write("%s\n", line); *! @endcode
fa306e2001-05-17Henrik Grubbström (Grubba)  */ PIKECLASS string_split_iterator { CVAR struct pike_string *buffer; CVAR struct pike_string *current; CVAR int offset; CVAR int index; CVAR p_wchar2 *split_set; CVAR int split_set_size; CVAR int flags; CVAR struct svalue feed;
22d6c92001-06-17Henrik Grubbström (Grubba) #define SIMPLE_SKIP(THIS, SHIFT, OFF) \ do { \ PIKE_CONCAT(p_wchar, SHIFT) *s = \ PIKE_CONCAT(STR,SHIFT)(THIS->buffer); \ while((OFF < THIS->buffer->len) && \ s[OFF] == THIS->split_set[0]) { \ OFF++; \ } \ } while(0) #define SIMPLE_SKIP_CASE(THIS, SHIFT, OFF) \ case SHIFT: \ SIMPLE_SKIP(THIS, SHIFT, OFF); \ break #define COMPLEX_SKIP(THIS, SHIFT, OFF) \
0d84692001-06-16Henrik Grubbström (Grubba)  do { \ PIKE_CONCAT(p_wchar, SHIFT) *s = \ PIKE_CONCAT(STR,SHIFT)(THIS->buffer); \
22d6c92001-06-17Henrik Grubbström (Grubba)  while(OFF < THIS->buffer->len) { \ int i; \ p_wchar2 ch = s[OFF]; \ for (i=0; i < THIS->split_set_size; i++) { \ if (ch == THIS->split_set[i]) { \ goto PIKE_CONCAT(continue_skip,SHIFT); \ } \ } \ break; \ PIKE_CONCAT(continue_skip,SHIFT): \
fa306e2001-05-17Henrik Grubbström (Grubba)  OFF++; \ } \
22d6c92001-06-17Henrik Grubbström (Grubba)  } while(0)
13670c2015-05-25Martin Nilsson 
22d6c92001-06-17Henrik Grubbström (Grubba) #define COMPLEX_SKIP_CASE(THIS, SHIFT, OFF) \ case SHIFT: \ COMPLEX_SKIP(THIS, SHIFT, OFF); \ break #define SIMPLE_SCAN(THIS, SHIFT, OFF) \ do { \ PIKE_CONCAT(p_wchar, SHIFT) *s = \ PIKE_CONCAT(STR,SHIFT)(THIS->buffer); \ while((OFF < THIS->buffer->len) && \ s[OFF] != THIS->split_set[0]) { \ OFF++; \ } \ } while(0) #define SIMPLE_SCAN_CASE(THIS, SHIFT, OFF) \ case SHIFT: \ SIMPLE_SCAN(THIS, SHIFT, OFF); \ break #define COMPLEX_SCAN(THIS, SHIFT, OFF) \
0d84692001-06-16Henrik Grubbström (Grubba)  do { \ PIKE_CONCAT(p_wchar, SHIFT) *s = \ PIKE_CONCAT(STR,SHIFT)(THIS->buffer); \ while(OFF < THIS->buffer->len) { \ int i; \ p_wchar2 ch = s[OFF]; \ for (i=0; i < THIS->split_set_size; i++) { \ if (ch == THIS->split_set[i]) { \ goto PIKE_CONCAT(break_scan,SHIFT); \ } \ } \ OFF++; \ } \
66a34a2001-06-16Per Hedbor  PIKE_CONCAT(break_scan, SHIFT): ;/* gcc complains without ;*/ \
22d6c92001-06-17Henrik Grubbström (Grubba)  } while(0) #define COMPLEX_SCAN_CASE(THIS, SHIFT, OFF) \ case SHIFT: \ COMPLEX_SCAN(THIS, SHIFT, OFF); \ break
0d84692001-06-16Henrik Grubbström (Grubba) 
22d6c92001-06-17Henrik Grubbström (Grubba) #define SIMPLE_SCAN_PUSH(THIS, SHIFT, OFF) \
0d84692001-06-16Henrik Grubbström (Grubba)  do { \ PIKE_CONCAT(p_wchar, SHIFT) *s = \ PIKE_CONCAT(STR,SHIFT)(THIS->buffer); \ while((OFF < THIS->buffer->len) && \ s[OFF] != THIS->split_set[0]) { \ OFF++; \ } \ push_string(PIKE_CONCAT(make_shared_binary_string, SHIFT) \ (s+offset, OFF-offset)); \
22d6c92001-06-17Henrik Grubbström (Grubba)  } while(0)
43c9bf2002-10-07Henrik Grubbström (Grubba) #define COMPLEX_SCAN_PUSH(THIS, SHIFT, OFF) \ do { \ PIKE_CONCAT(p_wchar, SHIFT) *s = \ PIKE_CONCAT(STR,SHIFT)(THIS->buffer); \ while(OFF < THIS->buffer->len) { \ int i; \ p_wchar2 ch = s[OFF]; \ for (i=0; i < THIS->split_set_size; i++) { \ if (ch == THIS->split_set[i]) { \ push_string(PIKE_CONCAT(make_shared_binary_string, SHIFT) \ (s+offset, OFF-offset)); \ goto PIKE_CONCAT(break_scan,SHIFT); \ } \ } \ OFF++; \ } \ push_string(PIKE_CONCAT(make_shared_binary_string, SHIFT) \ (s+offset, OFF-offset)); \ PIKE_CONCAT(break_scan, SHIFT): \ ; \
22d6c92001-06-17Henrik Grubbström (Grubba)  } while(0) #define NEW_SKIP_CASE(SHIFT, FLAGS) \ case SHIFT: \ if (FLAGS) { \ /* Skip empty */ \ if (ssi->split_set_size == 1) { \ SIMPLE_SKIP(ssi, SHIFT, offset); \ } else { \ COMPLEX_SKIP(ssi, SHIFT, offset); \ } \ } \ if (offset >= ssi->buffer->len) { \ free_string(ssi->buffer); \ ssi->buffer = NULL; \ ssi->offset = 0; \ offset = 0; \
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(ssi->feed) == PIKE_T_FREE) { \
22d6c92001-06-17Henrik Grubbström (Grubba)  if (!(FLAGS)) { \
de56ec2003-02-08Martin Stjernholm  copy_shared_string(ssi->current, empty_pike_string); \
22d6c92001-06-17Henrik Grubbström (Grubba)  ssi->index++; \ } \ return; \ } else { \ /* Attempt to fill the buffer with some more. */ \ apply_svalue(&ssi->feed, 0); \
017b572011-10-28Henrik Grubbström (Grubba)  if ((TYPEOF(Pike_sp[-1]) == T_STRING) && \
22d6c92001-06-17Henrik Grubbström (Grubba)  (Pike_sp[-1].u.string->len)) { \ ssi->buffer = Pike_sp[-1].u.string; \
50ea682003-03-14Henrik Grubbström (Grubba)  dmalloc_touch_svalue(Pike_sp-1); \
22d6c92001-06-17Henrik Grubbström (Grubba)  Pike_sp--; \ goto reskip_empty; \ } \ free_svalue(&ssi->feed); \
25fe272008-05-01Martin Stjernholm  mark_free_svalue (&ssi->feed); \
22d6c92001-06-17Henrik Grubbström (Grubba)  pop_stack(); \ if (!(FLAGS)) { \
de56ec2003-02-08Martin Stjernholm  copy_shared_string(ssi->current, empty_pike_string); \
22d6c92001-06-17Henrik Grubbström (Grubba)  ssi->index++; \ } \ return; \ } \ } \ ssi->index++; \ end = offset; \ goto PIKE_CONCAT(scan_more_,SHIFT) #define NEW_SCAN_MORE_CASE(SHIFT) \ case SHIFT: \ PIKE_CONCAT(scan_more_,SHIFT): \ if (ssi->split_set_size == 1) { \ SIMPLE_SCAN_PUSH(ssi, SHIFT, end); \ } else { \ COMPLEX_SCAN_PUSH(ssi, SHIFT, end); \ } \ if ((end == ssi->buffer->len) && \
017b572011-10-28Henrik Grubbström (Grubba)  (TYPEOF(ssi->feed) != PIKE_T_FREE)) { \
22d6c92001-06-17Henrik Grubbström (Grubba)  apply_svalue(&ssi->feed, 0); \
017b572011-10-28Henrik Grubbström (Grubba)  if ((TYPEOF(Pike_sp[-1]) == T_STRING) && \
22d6c92001-06-17Henrik Grubbström (Grubba)  (Pike_sp[-1].u.string->len)) { \ f_add(2); \
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(Pike_sp[-1]) != T_STRING) { \
22d6c92001-06-17Henrik Grubbström (Grubba)  Pike_error("Bad result from concatenation!\n"); \ } \ free_string(ssi->buffer); \ ssi->buffer = Pike_sp[-1].u.string; \
50ea682003-03-14Henrik Grubbström (Grubba)  dmalloc_touch_svalue(Pike_sp-1); \
22d6c92001-06-17Henrik Grubbström (Grubba)  Pike_sp--; \ end -= offset; \ offset = 0; \ goto scan_more; \ } \ pop_stack(); /* Pop the end of stream marker. */ \ \ /* Make sure we don't call feed() any more. */ \ free_svalue(&ssi->feed); \
25fe272008-05-01Martin Stjernholm  mark_free_svalue (&ssi->feed); \
22d6c92001-06-17Henrik Grubbström (Grubba)  } \ ssi->offset = end+1; \ ssi->current = Pike_sp[-1].u.string; \
50ea682003-03-14Henrik Grubbström (Grubba)  dmalloc_touch_svalue(Pike_sp-1); \
22d6c92001-06-17Henrik Grubbström (Grubba)  Pike_sp--; \ if (ssi->offset > ssi->buffer->len) { \ free_string(ssi->buffer); \ ssi->buffer = 0; \ } \ break
13670c2015-05-25Martin Nilsson 
531c9f2001-05-21Henrik Grubbström (Grubba)  static void find_next(struct string_split_iterator_struct *ssi)
fa306e2001-05-17Henrik Grubbström (Grubba)  { int offset = ssi->offset;
4a01712015-10-17Henrik Grubbström (Grubba)  int end = 0;
fa306e2001-05-17Henrik Grubbström (Grubba)  if (ssi->current) { free_string(ssi->current); } ssi->current = NULL; if (!ssi->buffer) { return; } reskip_empty:
22d6c92001-06-17Henrik Grubbström (Grubba)  switch(ssi->buffer->size_shift) {
759c422015-10-17Martin Nilsson  NEW_SKIP_CASE(0, ssi->flags);
e8d9902008-05-12Martin Stjernholm  NEW_SKIP_CASE(1, ssi->flags); NEW_SKIP_CASE(2, ssi->flags);
fa306e2001-05-17Henrik Grubbström (Grubba)  } scan_more:
22d6c92001-06-17Henrik Grubbström (Grubba)  switch(ssi->buffer->size_shift) { NEW_SCAN_MORE_CASE(0); NEW_SCAN_MORE_CASE(1); NEW_SCAN_MORE_CASE(2);
fb97472001-05-17Henrik Grubbström (Grubba)  }
fa306e2001-05-17Henrik Grubbström (Grubba)  }
22d6c92001-06-17Henrik Grubbström (Grubba) 
fa306e2001-05-17Henrik Grubbström (Grubba)  PIKEFUN string value() { if (THIS->current) { ref_push_string(THIS->current); } else {
f139182003-09-05Martin Stjernholm  push_undefined();
fa306e2001-05-17Henrik Grubbström (Grubba)  } } PIKEFUN int index() { if (!THIS->current) {
f139182003-09-05Martin Stjernholm  push_undefined();
fa306e2001-05-17Henrik Grubbström (Grubba)  }else{ RETURN THIS->index; } }
3246682003-04-18Martin Stjernholm  PIKEFUN object `+(int(0..) steps)
fa306e2001-05-17Henrik Grubbström (Grubba)  { struct object *o=low_clone(string_split_iterator_program); int i; struct string_split_iterator_struct *ssi;
3246682003-04-18Martin Stjernholm  if (steps < 0)
de22f72014-08-25Martin Nilsson  SIMPLE_ARG_ERROR ("`+", 1,
3246682003-04-18Martin Stjernholm  "This iterator doesn't support going backwards.\n");
fa306e2001-05-17Henrik Grubbström (Grubba)  (ssi = OBJ2_STRING_SPLIT_ITERATOR(o))[0]=*THIS; if (THIS->buffer) { add_ref(THIS->buffer); } if (THIS->current) { add_ref(THIS->current); } add_ref_svalue(&THIS->feed); for (i=0; i < steps; i++) { find_next(ssi); } RETURN o; }
3246682003-04-18Martin Stjernholm  PIKEFUN object `+=(int(0..) steps)
fa306e2001-05-17Henrik Grubbström (Grubba)  { int i;
3246682003-04-18Martin Stjernholm  if (steps < 0)
de22f72014-08-25Martin Nilsson  SIMPLE_ARG_ERROR ("`+=", 1,
3246682003-04-18Martin Stjernholm  "This iterator doesn't support going backwards.\n");
fa306e2001-05-17Henrik Grubbström (Grubba)  for(i = 0; i < steps; i++) { find_next(THIS); } REF_RETURN Pike_fp->current_object; } PIKEFUN int next() { find_next(THIS); RETURN !!THIS->current; } PIKEFUN int `!() { RETURN !THIS->current; } PIKEFUN int _sizeof() { INT_TYPE res = 0; if (THIS->buffer) { int i, off; ref_push_string(THIS->buffer); if (THIS->offset) { push_int(THIS->offset); push_int(THIS->buffer->len); o_range(); }
017b572011-10-28Henrik Grubbström (Grubba)  for (i = 1; TYPEOF(THIS->feed) != PIKE_T_FREE; i++) {
fa306e2001-05-17Henrik Grubbström (Grubba)  apply_svalue(&THIS->feed, 0);
017b572011-10-28Henrik Grubbström (Grubba)  if ((TYPEOF(Pike_sp[-1]) != T_STRING) ||
fa306e2001-05-17Henrik Grubbström (Grubba)  (!Pike_sp[-1].u.string->len)) { /* End of stream marker. */ pop_stack(); free_svalue(&THIS->feed);
1ab4ac2008-01-26Martin Stjernholm  mark_free_svalue (&THIS->feed);
fa306e2001-05-17Henrik Grubbström (Grubba)  break; } } f_add(i); /* Join the segments. */ free_string(THIS->buffer); THIS->buffer = Pike_sp[-1].u.string; THIS->offset = 0;
50ea682003-03-14Henrik Grubbström (Grubba)  dmalloc_touch_svalue(Pike_sp-1);
fa306e2001-05-17Henrik Grubbström (Grubba)  Pike_sp--; /* Perform the scan. */ for (off=0; off < THIS->buffer->len; off++) { if (THIS->flags) { if (THIS->split_set_size == 1) { switch(THIS->buffer->size_shift) { SIMPLE_SKIP_CASE(THIS, 0, off); SIMPLE_SKIP_CASE(THIS, 1, off); SIMPLE_SKIP_CASE(THIS, 2, off);
759c422015-10-17Martin Nilsson  }
fa306e2001-05-17Henrik Grubbström (Grubba)  } else { switch(THIS->buffer->size_shift) { COMPLEX_SKIP_CASE(THIS, 0, off); COMPLEX_SKIP_CASE(THIS, 1, off); COMPLEX_SKIP_CASE(THIS, 2, off);
759c422015-10-17Martin Nilsson  }
fa306e2001-05-17Henrik Grubbström (Grubba)  } if (off >= THIS->buffer->len) { break; } } res++; if (THIS->split_set_size == 1) { switch(THIS->buffer->size_shift) { SIMPLE_SCAN_CASE(THIS, 0, off); SIMPLE_SCAN_CASE(THIS, 1, off); SIMPLE_SCAN_CASE(THIS, 2, off);
759c422015-10-17Martin Nilsson  }
fa306e2001-05-17Henrik Grubbström (Grubba)  } else { switch(THIS->buffer->size_shift) { COMPLEX_SCAN_CASE(THIS, 0, off); COMPLEX_SCAN_CASE(THIS, 1, off); COMPLEX_SCAN_CASE(THIS, 2, off);
759c422015-10-17Martin Nilsson  }
fa306e2001-05-17Henrik Grubbström (Grubba)  }
531c9f2001-05-21Henrik Grubbström (Grubba)  } if ((!THIS->flags) && (off == THIS->buffer->len)) { /* Ends with an empty segment. */ res++;
fa306e2001-05-17Henrik Grubbström (Grubba)  } } if (THIS->current) { res++; } RETURN res; }
65f7bb2002-07-11Martin Nilsson  /*! @decl void create(string buffer, int|array(int)|multiset(int) split_set,@
58fcee2002-07-09Martin Nilsson  *! int|void flags, function(:string)|void feed)
fbf10a2004-04-06Martin Nilsson  *! @param buffer *! The string to split. *! @param split_set *! The character or characters to split on. *! @param flags *! Skip empty elements if set. *! @param feed *! Callback function that is called once the @[buffer] is used up *! and the SplitIterator wants more data.
58fcee2002-07-09Martin Nilsson  */
fa306e2001-05-17Henrik Grubbström (Grubba)  PIKEFUN void create(string buffer, int|array(int)|multiset(int) split_set, int|void flags, function(:string)|void feed) { if (THIS->buffer) { Pike_error("String.split() iterators cannot be reused.\n"); }
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(*split_set) == T_INT) {
fa306e2001-05-17Henrik Grubbström (Grubba)  THIS->split_set = (p_wchar2 *)xalloc(sizeof(p_wchar2)); THIS->split_set[0] = split_set->u.integer; THIS->split_set_size = 1; } else { struct array *a; int i;
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(*split_set) == T_ARRAY) {
fa306e2001-05-17Henrik Grubbström (Grubba)  a = split_set->u.array;
017b572011-10-28Henrik Grubbström (Grubba)  } else if (TYPEOF(*split_set) == T_MULTISET) {
5b15bb2001-12-10Martin Stjernholm  a = multiset_indices (split_set->u.multiset); push_array (a);
fa306e2001-05-17Henrik Grubbström (Grubba)  } else {
f982742016-01-26Martin Nilsson  SIMPLE_ARG_TYPE_ERROR("create", 2, "int|array(int)|multiset(int)");
fa306e2001-05-17Henrik Grubbström (Grubba)  } if (!a->size) {
f982742016-01-26Martin Nilsson  SIMPLE_ARG_TYPE_ERROR("create", 2, "int|array(int)|multiset(int)");
fa306e2001-05-17Henrik Grubbström (Grubba)  } for (i=0; i < a->size; i++) {
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(a->item[i]) != T_INT) {
f982742016-01-26Martin Nilsson  SIMPLE_ARG_TYPE_ERROR("create", 2, "int|array(int)|multiset(int)");
fa306e2001-05-17Henrik Grubbström (Grubba)  } } THIS->split_set = (p_wchar2 *)xalloc(a->size * sizeof(p_wchar2)); for (i=0; i < a->size; i++) { THIS->split_set[i] = a->item[i].u.integer; } THIS->split_set_size = a->size;
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(*split_set) == T_MULTISET) pop_stack();
fa306e2001-05-17Henrik Grubbström (Grubba)  } add_ref(THIS->buffer = buffer); if (args > 2) {
017b572011-10-28Henrik Grubbström (Grubba)  if (flags && (TYPEOF(*flags) == T_INT)) {
fa306e2001-05-17Henrik Grubbström (Grubba)  THIS->flags = flags->u.integer; } else { THIS->flags = 0; }
e77fa42007-01-10Henrik Grubbström (Grubba)  if (feed) {
fa306e2001-05-17Henrik Grubbström (Grubba)  assign_svalue(&THIS->feed, feed); } else {
1ab4ac2008-01-26Martin Stjernholm  /* NB: THIS->feed has already been set to PIKE_T_FREE by the * init code. */
fa306e2001-05-17Henrik Grubbström (Grubba)  } } else { THIS->flags = 0; } THIS->offset = 0; THIS->current = NULL; THIS->index = -1; find_next(THIS);
b498ad2017-12-28Martin Nilsson  pop_n_elems(args);
fa306e2001-05-17Henrik Grubbström (Grubba)  }
13670c2015-05-25Martin Nilsson  INIT
fa306e2001-05-17Henrik Grubbström (Grubba)  {
b467522017-06-25Martin Nilsson #ifdef PIKE_NULL_IS_SPECIAL
fa306e2001-05-17Henrik Grubbström (Grubba)  THIS->buffer = NULL; THIS->current = NULL; THIS->offset = 0; THIS->index = 0; THIS->split_set = NULL; THIS->split_set_size = 0; THIS->flags = 0;
b467522017-06-25Martin Nilsson #endif
1ab4ac2008-01-26Martin Stjernholm  mark_free_svalue (&THIS->feed);
fa306e2001-05-17Henrik Grubbström (Grubba)  } EXIT
8dcb7d2008-05-29Martin Stjernholm  gc_trivial;
fa306e2001-05-17Henrik Grubbström (Grubba)  {
40338f2017-07-10Martin Nilsson  if (THIS->buffer)
fa306e2001-05-17Henrik Grubbström (Grubba)  free_string(THIS->buffer);
40338f2017-07-10Martin Nilsson  if (THIS->current)
fa306e2001-05-17Henrik Grubbström (Grubba)  free_string(THIS->current); free(THIS->split_set); free_svalue(&THIS->feed);
1ab4ac2008-01-26Martin Stjernholm  mark_free_svalue (&THIS->feed);
fa306e2001-05-17Henrik Grubbström (Grubba)  }
3451482001-05-26Henrik Grubbström (Grubba)  OPTIMIZE { if (CDR(n) && (CDR(n)->token == F_ARG_LIST) && CADR(n) && (CADR(n)->token == F_APPLY) && CAADR(n) && (CAADR(n)->token == F_CONSTANT) &&
017b572011-10-28Henrik Grubbström (Grubba)  (TYPEOF(CAADR(n)->u.sval) == T_FUNCTION) && (SUBTYPEOF(CAADR(n)->u.sval) == FUNCTION_BUILTIN) &&
3451482001-05-26Henrik Grubbström (Grubba)  (CAADR(n)->u.sval.u.efun->function == f_replace)) { /* String.SplitIterator(replace(...),...) */ node *repl_args = CDADR(n); node **str = my_get_arg(&repl_args, 0); node **from = my_get_arg(&repl_args, 1); node **to = my_get_arg(&repl_args, 2); if (str && from && to) { /* String.SplitIterator(replace(str, from, to), ...) */ int num; if (((*to)->token == F_APPLY) && CAR(*to) && (CAR(*to)->token == F_CONSTANT) &&
017b572011-10-28Henrik Grubbström (Grubba)  (TYPEOF(CAR(*to)->u.sval) == T_FUNCTION) && (SUBTYPEOF(CAR(*to)->u.sval) == FUNCTION_BUILTIN) &&
3451482001-05-26Henrik Grubbström (Grubba)  (CAR(*to)->u.sval.u.efun->function == f_allocate) && CDR(*to) && (CDR(*to)->token == F_ARG_LIST) && CADR(*to) && (CADR(*to)->token == F_CONSTANT) &&
017b572011-10-28Henrik Grubbström (Grubba)  (TYPEOF(CADR(*to)->u.sval) == T_INT) &&
3451482001-05-26Henrik Grubbström (Grubba)  (num = CADR(*to)->u.sval.u.integer) && CDDR(*to) && (CDDR(*to)->token == F_CONSTANT) &&
017b572011-10-28Henrik Grubbström (Grubba)  (TYPEOF(CDDR(*to)->u.sval) == T_STRING) &&
3451482001-05-26Henrik Grubbström (Grubba)  (CDDR(*to)->u.sval.u.string->len == 1)) { /* String.SplitIterator(replace(str, from, allocate(num, "x")), * ...) */ int split_val = index_shared_string(CDDR(*to)->u.sval.u.string, 0); if (CDDR(n) && (((CDDR(n)->token == F_CONSTANT) &&
017b572011-10-28Henrik Grubbström (Grubba)  (TYPEOF(CDDR(n)->u.sval) == T_INT) &&
3451482001-05-26Henrik Grubbström (Grubba)  (CDDR(n)->u.sval.u.integer == split_val)) || ((CDDR(n)->token == F_ARG_LIST) && CADDR(n) && (CADDR(n)->token == F_CONSTANT) &&
017b572011-10-28Henrik Grubbström (Grubba)  (TYPEOF(CADDR(n)->u.sval) == T_INT) &&
3451482001-05-26Henrik Grubbström (Grubba)  (CADDR(n)->u.sval.u.integer == split_val)))) { /* String.SplitIterator(replace(str, from, allocate(n, "x")), * 'x', ...) */ struct array *split = NULL; node *res = NULL; switch((*from)->token) { case F_CONSTANT:
017b572011-10-28Henrik Grubbström (Grubba)  if ((TYPEOF((*from)->u.sval) == T_ARRAY) &&
3451482001-05-26Henrik Grubbström (Grubba)  ((*from)->u.sval.u.array->size == num)) { int i; for (i=0; i < num; i++) {
017b572011-10-28Henrik Grubbström (Grubba)  if ((TYPEOF((*from)->u.sval.u.array->item[i]) != T_STRING) ||
3451482001-05-26Henrik Grubbström (Grubba)  ((*from)->u.sval.u.array->item[i].u.string->len != 1)) { return NULL; } } split = allocate_array(num+1); split->item[0].u.integer = split_val; for (i=0; i < num; i++) { split->item[i+1].u.integer = index_shared_string((*from)->u.sval.u.array-> item[i].u.string, 0);
2523ce2003-04-28Martin Stjernholm  } split->type_field = BIT_INT;
3451482001-05-26Henrik Grubbström (Grubba)  } break; case F_APPLY: if (CAR(*from) && (CAR(*from)->token == F_CONSTANT) &&
017b572011-10-28Henrik Grubbström (Grubba)  (TYPEOF(CAR(*from)->u.sval) == T_FUNCTION) && (SUBTYPEOF(CAR(*from)->u.sval) == FUNCTION_BUILTIN)) {
3451482001-05-26Henrik Grubbström (Grubba)  if (CAR(*from)->u.sval.u.efun->function == f_allocate) {
0d84692001-06-16Henrik Grubbström (Grubba)  /* FIXME: Not likely */
3451482001-05-26Henrik Grubbström (Grubba)  } else if (CAR(*from)->u.sval.u.efun->function == debug_f_aggregate) { node *tmp = CDR(*from); int i; for (i = 0; tmp && (tmp->token == F_ARG_LIST); tmp = CDR(tmp)) { if (!CAR(tmp)) continue;
13670c2015-05-25Martin Nilsson  if ((CAR(tmp)->token != F_CONSTANT) ||
017b572011-10-28Henrik Grubbström (Grubba)  (TYPEOF(CAR(tmp)->u.sval) != T_STRING) ||
3451482001-05-26Henrik Grubbström (Grubba)  (CAR(tmp)->u.sval.u.string->len != 1)) { return NULL; } i++; } if (i != num) { return NULL; } split = allocate_array(num+1); split->item[0].u.integer = split_val; tmp = CDR(*from); for (i = 1; tmp && (tmp->token == F_ARG_LIST); tmp = CDR(tmp)) { if (!CAR(tmp)) continue; split->item[i].u.integer = index_shared_string(CAR(tmp)->u.sval.u.string, 0); i++;
2523ce2003-04-28Martin Stjernholm  } split->type_field = BIT_INT;
3451482001-05-26Henrik Grubbström (Grubba)  } } else { return NULL; } break; default: return NULL; } if (!split) { return NULL; } push_array(split); /* Simplify error-handling... */ /* Create the result... * * String.SplitIterator(str, split, ...) */ if (CDDR(n)->token == F_ARG_LIST) { ADD_NODE_REF2(CAR(n), ADD_NODE_REF2(*str, ADD_NODE_REF2(CDDDR(n), res = mkapplynode(CAR(n), mknode(F_ARG_LIST, *str, mknode(F_ARG_LIST, mkconstantsvaluenode(Pike_sp-1), CDDDR(n)))); ))); } else { ADD_NODE_REF2(CAR(n), ADD_NODE_REF2(*str, res = mkapplynode(CAR(n), mknode(F_ARG_LIST, *str, mkconstantsvaluenode(Pike_sp-1))); )); } pop_stack(); return res; } } } } return NULL; }
fa306e2001-05-17Henrik Grubbström (Grubba) }; /*! @endclass */ /*! @endmodule */
3246682003-04-18Martin Stjernholm /*! @decl Iterator get_iterator (object|array|mapping|multiset|string data) *! *! Creates and returns a canonical iterator for @[data]. *!
d2c3162003-04-21Henrik Grubbström (Grubba)  *! @returns *! @mixed data *! @type object *! If @[data] is an object with @[lfun::_get_iterator] defined then *! it's called in it to create the iterator.
13670c2015-05-25Martin Nilsson  *!
d2c3162003-04-21Henrik Grubbström (Grubba)  *! If @[data] is an object that lacks @[lfun::_get_iterator] then *! it's assumed to already be an iterator object, and is simply *! returned. *! @type array *! If @[data] is an array, an @[Array.Iterator] object will be *! returned. *! @type mapping *! If @[data] is a mapping, a @[Mapping.Iterator] object will be *! returned *! @type multiset *! If @[data] is a multiset, a @[Multiset.Iterator] object will be *! returned *! @type string *! If @[data] is a string, a @[String.Iterator] object will be *! returned *! @endmixed
3246682003-04-18Martin Stjernholm  *! *! @note *! This function is used by @[foreach] to get an iterator for an *! object. *! *! @seealso *! @[Iterator], @[lfun::_get_iterator] */ PIKEFUN object(Iterator) get_iterator(object|array|mapping|multiset|string data) efun;
4cdb802001-02-23Fredrik Hübinette (Hubbe) {
017b572011-10-28Henrik Grubbström (Grubba)  switch(TYPEOF(*data))
4cdb802001-02-23Fredrik Hübinette (Hubbe)  {
9c960e2001-02-28Henrik Grubbström (Grubba)  case PIKE_T_STRING: push_object(clone_object(string_iterator_program, 1)); return;
4cdb802001-02-23Fredrik Hübinette (Hubbe)  case PIKE_T_MAPPING: push_object(clone_object(mapping_iterator_program,1)); return; case PIKE_T_MULTISET: push_object(clone_object(multiset_iterator_program, 1)); return; case PIKE_T_ARRAY: push_object(clone_object(array_iterator_program, 1)); return;
f139182003-09-05Martin Stjernholm  case PIKE_T_OBJECT: { int fun;
f54c782004-12-22Henrik Grubbström (Grubba)  struct program *p;
f139182003-09-05Martin Stjernholm 
f54c782004-12-22Henrik Grubbström (Grubba)  if(!(p = data->u.object->prog))
3246682003-04-18Martin Stjernholm  SIMPLE_ARG_ERROR ("get_iterator", 1, "Got a destructed object.\n");
4cdb802001-02-23Fredrik Hübinette (Hubbe) 
017b572011-10-28Henrik Grubbström (Grubba)  fun = FIND_LFUN(p->inherits[SUBTYPEOF(*data)].prog, LFUN__GET_ITERATOR);
f139182003-09-05Martin Stjernholm  if (fun != -1)
4cdb802001-02-23Fredrik Hübinette (Hubbe)  {
f54c782004-12-22Henrik Grubbström (Grubba)  apply_low (data->u.object,
017b572011-10-28Henrik Grubbström (Grubba)  fun + p->inherits[SUBTYPEOF(*data)].identifier_level, 0);
19961b2017-04-08Martin Nilsson  if (TYPEOF(Pike_sp[-1]) != T_OBJECT) {
f139182003-09-05Martin Stjernholm  /* FIXME: Ought to include what we got in the error message. */ pop_stack(); SIMPLE_ARG_ERROR ("get_iterator", 1, "_get_iterator() didn't return an object.\n"); }
0d0bab2003-04-27Martin Stjernholm  stack_pop_keep_top();
4cdb802001-02-23Fredrik Hübinette (Hubbe)  return; } /* Assume it already is an iterator... */ return;
f139182003-09-05Martin Stjernholm  }
4cdb802001-02-23Fredrik Hübinette (Hubbe)  default:
3246682003-04-18Martin Stjernholm  SIMPLE_ARG_TYPE_ERROR("get_iterator", 1, "multiset|array|string|mapping|object");
4cdb802001-02-23Fredrik Hübinette (Hubbe)  } }
19961b2017-04-08Martin Nilsson /* Pike_sp[-4] = index; Pike_sp[-2] = value */
f139182003-09-05Martin Stjernholm int foreach_iterate(struct object *o, int do_step)
4cdb802001-02-23Fredrik Hübinette (Hubbe) {
f139182003-09-05Martin Stjernholm  struct program *prog = o->prog;
13a16d2003-05-31Martin Stjernholm  int fun;
f139182003-09-05Martin Stjernholm  if(!prog)
4cdb802001-02-23Fredrik Hübinette (Hubbe)  Pike_error("foreach on destructed iterator.\n");
f139182003-09-05Martin Stjernholm 
e1adc62007-11-07Henrik Grubbström (Grubba)  /* FIXME: This could be simplified by letting the iterators * start at one position before the first element, and * then use next() to both advance and check if we are * at the end of the iterator. The do_step flag would * then not be needed. This would also have the benefit * of not needing `!() to be implemented. */
f139182003-09-05Martin Stjernholm  if(prog->flags & PROGRAM_HAS_C_METHODS)
4cdb802001-02-23Fredrik Hübinette (Hubbe)  {
f139182003-09-05Martin Stjernholm  if(prog == mapping_iterator_program)
4cdb802001-02-23Fredrik Hübinette (Hubbe)  { struct mapping_iterator_struct *i=OBJ2_MAPPING_ITERATOR(o);
e1adc62007-11-07Henrik Grubbström (Grubba) #ifdef PIKE_MAPPING_KEYPAIR_LOOP if (i->current == i->md->free_list) return 0; if (do_step && (++i->current == i->md->free_list)) return 0; #else /* !PIKE_MAPPING_KEYPAIR_LOOP */
f139182003-09-05Martin Stjernholm  if (!i->current) return 0; if (do_step) { mi_step (i); if (!i->current) return 0;
4cdb802001-02-23Fredrik Hübinette (Hubbe)  }
e1adc62007-11-07Henrik Grubbström (Grubba) #endif /* PIKE_MAPPING_KEYPAIR_LOOP */
f139182003-09-05Martin Stjernholm 
017b572011-10-28Henrik Grubbström (Grubba)  if(TYPEOF(Pike_sp[-4]) != T_INT)
f139182003-09-05Martin Stjernholm  assign_lvalue(Pike_sp-4, & i->current->ind);
13670c2015-05-25Martin Nilsson 
017b572011-10-28Henrik Grubbström (Grubba)  if(TYPEOF(Pike_sp[-2]) != T_INT)
f139182003-09-05Martin Stjernholm  assign_lvalue(Pike_sp-2, & i->current->val); return 1; } else if(prog == string_split_iterator_program)
0d84692001-06-16Henrik Grubbström (Grubba)  { struct string_split_iterator_struct *i=OBJ2_STRING_SPLIT_ITERATOR(o);
f139182003-09-05Martin Stjernholm  if (!i->current) return 0; if (do_step) { find_next (i); if (!i->current) return 0; }
0d84692001-06-16Henrik Grubbström (Grubba) 
017b572011-10-28Henrik Grubbström (Grubba)  if(TYPEOF(Pike_sp[-4]) != T_INT)
f139182003-09-05Martin Stjernholm  { /* Black Magic... */ push_int(i->index); Pike_sp--; assign_lvalue(Pike_sp-4, Pike_sp);
0d84692001-06-16Henrik Grubbström (Grubba)  }
f139182003-09-05Martin Stjernholm 
017b572011-10-28Henrik Grubbström (Grubba)  if(TYPEOF(Pike_sp[-2]) != T_INT)
f139182003-09-05Martin Stjernholm  { /* Black Magic... */ push_string(i->current); dmalloc_touch_svalue(Pike_sp-1); Pike_sp--; assign_lvalue(Pike_sp-2, Pike_sp); } return 1; } else if(prog == file_line_iterator_program)
66a34a2001-06-16Per Hedbor  { struct file_line_iterator_struct *i=OBJ2_FILE_LINE_ITERATOR(o);
f139182003-09-05Martin Stjernholm  if (!i->current) return 0; if (do_step) { fl_find_next (i); if (!i->current) return 0; }
66a34a2001-06-16Per Hedbor 
017b572011-10-28Henrik Grubbström (Grubba)  if(TYPEOF(Pike_sp[-4]) != T_INT)
f139182003-09-05Martin Stjernholm  { /* Black Magic... */ push_int(i->index); Pike_sp--; assign_lvalue(Pike_sp-4, Pike_sp);
66a34a2001-06-16Per Hedbor  }
f139182003-09-05Martin Stjernholm 
017b572011-10-28Henrik Grubbström (Grubba)  if(TYPEOF(Pike_sp[-2]) != T_INT)
f139182003-09-05Martin Stjernholm  { /* Black Magic... */ push_string(i->current); dmalloc_touch_svalue(Pike_sp-1); Pike_sp--; assign_lvalue(Pike_sp-2, Pike_sp); } return 1; } else if(prog == array_iterator_program)
4cdb802001-02-23Fredrik Hübinette (Hubbe)  {
63f2a52001-02-24Henrik Grubbström (Grubba)  struct array_iterator_struct *i=OBJ2_ARRAY_ITERATOR(o);
4cdb802001-02-23Fredrik Hübinette (Hubbe) 
f139182003-09-05Martin Stjernholm  if (i->pos < 0 || i->pos >= i->a->size) return 0; if (do_step) if (++i->pos == i->a->size) return 0;
0618532001-02-28Henrik Grubbström (Grubba) 
017b572011-10-28Henrik Grubbström (Grubba)  if(TYPEOF(Pike_sp[-4]) != T_INT)
f139182003-09-05Martin Stjernholm  { push_int(i->pos); assign_lvalue(Pike_sp-5, Pike_sp-1); pop_stack();
4cdb802001-02-23Fredrik Hübinette (Hubbe)  }
f139182003-09-05Martin Stjernholm 
017b572011-10-28Henrik Grubbström (Grubba)  if(TYPEOF(Pike_sp[-2]) != T_INT)
f139182003-09-05Martin Stjernholm  assign_lvalue(Pike_sp-2, i->a->item + i->pos); return 1; } else if(prog == multiset_iterator_program)
4cdb802001-02-23Fredrik Hübinette (Hubbe)  {
63f2a52001-02-24Henrik Grubbström (Grubba)  struct multiset_iterator_struct *i=OBJ2_MULTISET_ITERATOR(o);
5b15bb2001-12-10Martin Stjernholm  struct svalue ind;
73354b2001-12-10Martin Stjernholm  if (i->nodepos < 0) return 0;
f139182003-09-05Martin Stjernholm  if (do_step) {
73354b2001-12-10Martin Stjernholm  i->nodepos = multiset_next (i->l, i->nodepos); if (i->nodepos < 0) {
5b15bb2001-12-10Martin Stjernholm  sub_msnode_ref (i->l); return 0;
73354b2001-12-10Martin Stjernholm  }
5b15bb2001-12-10Martin Stjernholm  }
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(Pike_sp[-4]) != T_INT)
5b15bb2001-12-10Martin Stjernholm  assign_lvalue (Pike_sp - 4, use_multiset_index (i->l, i->nodepos, ind));
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(Pike_sp[-2]) != T_INT)
5b15bb2001-12-10Martin Stjernholm  assign_lvalue (Pike_sp - 2, get_multiset_value (i->l, i->nodepos)); return 1;
f139182003-09-05Martin Stjernholm  }
5b15bb2001-12-10Martin Stjernholm 
f139182003-09-05Martin Stjernholm  else if(prog == string_iterator_program)
4cdb802001-02-23Fredrik Hübinette (Hubbe)  { struct string_iterator_struct *i=OBJ2_STRING_ITERATOR(o);
f139182003-09-05Martin Stjernholm  if (i->pos < 0 || i->pos >= i->s->len) return 0; if (do_step) if (++i->pos == i->s->len) return 0;
4cdb802001-02-23Fredrik Hübinette (Hubbe) 
017b572011-10-28Henrik Grubbström (Grubba)  if(TYPEOF(Pike_sp[-4]) != T_INT)
f139182003-09-05Martin Stjernholm  { push_int(i->pos); assign_lvalue(Pike_sp-5, Pike_sp-1); pop_stack(); }
017b572011-10-28Henrik Grubbström (Grubba)  if(TYPEOF(Pike_sp[-2]) != T_INT)
f139182003-09-05Martin Stjernholm  { push_int(index_shared_string(i->s, i->pos)); assign_lvalue(Pike_sp-3, Pike_sp-1); pop_stack();
4cdb802001-02-23Fredrik Hübinette (Hubbe)  }
f139182003-09-05Martin Stjernholm  return 1;
4cdb802001-02-23Fredrik Hübinette (Hubbe)  } } /* Generic iteration */
f139182003-09-05Martin Stjernholm  if (do_step) {
45f4eb2004-11-26Henrik Grubbström (Grubba)  if ((fun = find_shared_string_identifier(MK_STRING("next"), prog)) < 0) { fun = FIND_LFUN (prog, LFUN_ADD_EQ); if (fun < 0) Pike_error("Iterator object lacks both next() and `+=().\n"); push_int(1); apply_low(o, fun, 1); pop_stack(); fun = FIND_LFUN(prog, LFUN_NOT); if (fun < 0) Pike_error("Iterator object lacks `!.\n"); apply_low(o, fun, 0); if(!UNSAFE_IS_ZERO(Pike_sp-1)) { pop_stack(); return 0; } } else { /* NOTE: next() returns 1 on success, and 0 on failure, * we thus don't need to call `! to see if we are at a * valid position. Note that this is the reverse of the * output from `!. */ apply_low(o, fun, 0); if(UNSAFE_IS_ZERO(Pike_sp-1)) { pop_stack(); return 0; } }
e1adc62007-11-07Henrik Grubbström (Grubba) #if 0 pop_stack(); #else
45f4eb2004-11-26Henrik Grubbström (Grubba)  } else { fun = FIND_LFUN(prog, LFUN_NOT); if (fun < 0) Pike_error("Iterator object lacks `!.\n"); apply_low(o, fun, 0); if(!UNSAFE_IS_ZERO(Pike_sp-1)) { pop_stack(); return 0; }
e1adc62007-11-07Henrik Grubbström (Grubba) #endif
f139182003-09-05Martin Stjernholm  } #if 0 /* We should be able to save calls to `! this way, but there are * iterators where index() and value() don't return UNDEFINED as * stipulated by the interface. */ fun = -1;
017b572011-10-28Henrik Grubbström (Grubba)  if(TYPEOF(Pike_sp[-4]) != T_INT)
f139182003-09-05Martin Stjernholm  { fun = find_identifier ("index", prog); if (fun < 0) Pike_error ("Iterator object lacks index().\n"); apply_low(o, fun, 0); if (IS_UNDEFINED (Pike_sp - 1)) { Pike_sp--; return 0; } assign_lvalue(Pike_sp-5,Pike_sp-1); pop_stack(); }
017b572011-10-28Henrik Grubbström (Grubba)  if(TYPEOF(Pike_sp[-2]) != T_INT)
f139182003-09-05Martin Stjernholm  { fun = find_identifier ("value", prog); if (fun < 0) Pike_error ("Iterator object lacks value().\n"); apply_low(o, fun, 0); if (IS_UNDEFINED (Pike_sp - 1)) {
e1adc62007-11-07Henrik Grubbström (Grubba)  /* NOTE: If index() doesn't return UNDEFINED when value() does, * the index-lvalue will contain a value that doesn't * correspond to the value in the value-lvalue on exit * from the loop. Please refrain from writing such iterators. */
f139182003-09-05Martin Stjernholm  Pike_sp--; return 0; } assign_lvalue(Pike_sp-3,Pike_sp-1); pop_stack(); }
e1adc62007-11-07Henrik Grubbström (Grubba)  if (do_step || (fun >= 0)) { /* We succeeded in advancing, or index() and/or value() returned * values that were not UNDEFINED. No need to call `!(). */
f139182003-09-05Martin Stjernholm  return 1;
e1adc62007-11-07Henrik Grubbström (Grubba)  } else {
f139182003-09-05Martin Stjernholm  int res; fun = FIND_LFUN (prog, LFUN_NOT); if (fun < 0) Pike_error ("Iterator object lacks `!.\n"); apply_low(o, fun, 0); res = UNSAFE_IS_ZERO(Pike_sp-1); pop_stack(); return res; } #else
e1adc62007-11-07Henrik Grubbström (Grubba)  /* Only reached if the iterator is at a valid position. */ pop_stack(); /* Get rid of the return value from `!() or step(). */
017b572011-10-28Henrik Grubbström (Grubba)  if(TYPEOF(Pike_sp[-4]) != T_INT)
4cdb802001-02-23Fredrik Hübinette (Hubbe)  {
45f4eb2004-11-26Henrik Grubbström (Grubba)  fun = find_shared_string_identifier(MK_STRING("index"), prog); if (fun < 0) Pike_error ("Iterator object lacks index().\n"); apply_low(o, fun, 0); assign_lvalue(Pike_sp-5,Pike_sp-1);
9e41e72002-06-06Henrik Grubbström (Grubba)  pop_stack();
45f4eb2004-11-26Henrik Grubbström (Grubba)  }
9e41e72002-06-06Henrik Grubbström (Grubba) 
017b572011-10-28Henrik Grubbström (Grubba)  if(TYPEOF(Pike_sp[-2]) != T_INT)
45f4eb2004-11-26Henrik Grubbström (Grubba)  { fun = find_shared_string_identifier(MK_STRING("value"), o->prog); if (fun < 0) Pike_error ("Iterator object lacks value().\n"); apply_low(o, fun, 0); assign_lvalue(Pike_sp-3,Pike_sp-1);
9e41e72002-06-06Henrik Grubbström (Grubba)  pop_stack();
4cdb802001-02-23Fredrik Hübinette (Hubbe)  }
f139182003-09-05Martin Stjernholm 
45f4eb2004-11-26Henrik Grubbström (Grubba)  return 1;
f139182003-09-05Martin Stjernholm #endif
4cdb802001-02-23Fredrik Hübinette (Hubbe) } void init_iterators(void) {
3246682003-04-18Martin Stjernholm  INIT; add_global_program ("Iterator", Iterator_program);
4cdb802001-02-23Fredrik Hübinette (Hubbe) } void exit_iterators(void) { EXIT }