File: iterator_ext.cpp

package info (click to toggle)
falconpl 0.9.6.9-git20120606-2
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 46,176 kB
  • sloc: cpp: 181,389; ansic: 109,025; yacc: 2,310; xml: 1,218; sh: 403; objc: 245; makefile: 82; sql: 20
file content (468 lines) | stat: -rw-r--r-- 15,240 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
/*
   FALCON - The Falcon Programming Language.
   FILE: iterator_ext.cpp

   Support for language level iterators.
   -------------------------------------------------------------------
   Author: Giancarlo Niccolai
   Begin: Thu, 14 Aug 2008 00:17:31 +0200

   -------------------------------------------------------------------
   (C) Copyright 2004: the FALCON developers (see list in AUTHORS file)

   See LICENSE file for licensing details.
*/

/*#
   @beginmodule core
*/

#include "core_module.h"
#include <falcon/sequence.h>
#include <falcon/falconobject.h>
#include <falcon/iterator.h>
#include <falcon/itemid.h>

namespace Falcon {
namespace core {

/*#
   @class Iterator
   @brief Indirect pointer to sequences.
   @ingroup general_purpose
   @param collection The collection on which to iterate.
   @optparam atEnd Indicator for start position.

   An iterator is an object meant to point to a certain position in a collection
   (array, dictionary, string, or eventually user defined types), and to access
   iteratively the elements in that collection.

   Iterators may be used to alter a collection by removing the item they are pointing
   to, or by changing its value. They can be stored to be used at a later moment, or
   they can be passed as parameters. Most notably, they hide the nature of the underlying
   collection, so that they can be used as an abstraction layer to access underlying data,
   one item at a time.

   Altering the collection may cause an iterator to become invalid; only performing write
   operations through an iterator it is possible to guarantee that it will stay valid
   after the modify. A test for iterator validity is performed on each operation, and in
   case the iterator is not found valid anymore, an error is raised.

   Iterators supports equality tests and provide an equal() method. Two iterators pointing
   to the same element in the same collection are considered equal; so it is possible to
   iterate through all the items between a start and an end.

   @section init Initialization

   The iterator is normally created at the begin of the sequence.
   If items in the collection can be directly accessed

   If @b position is given and true, the iterator starts from the end
   of the sequence (that is, pointing to the last valid element in the
   sequence), otherwise it points at the first valid element.

*/

FALCON_FUNC  Iterator_init( ::Falcon::VMachine *vm )
{
   Item *collection = vm->param(0);

   if( collection == 0 )
   {
      throw new ParamError( ErrorParam( e_inv_params )
         .origin( e_orig_runtime )
         .extra( "Sequence,[B]" ) );
      return;
   }

   Iterator* iter;
   bool fromEnd = vm->param(1) != 0 && vm->param(1)->isTrue();

   switch( collection->type() )
   {
   case FLC_ITEM_ARRAY:
      iter = new Iterator( &collection->asArray()->items(), fromEnd );
      break;

   case FLC_ITEM_DICT:
      iter = new Iterator( &collection->asDict()->items(), fromEnd );
      break;

   case FLC_ITEM_OBJECT:
      {
         CoreObject* obj = collection->asObjectSafe();
         Sequence* seq = obj->getSequence();
         if( seq != 0 )
         {
            iter = new Iterator( seq, fromEnd );
         }
         else {
            throw new ParamError( ErrorParam( e_inv_params )
                     .origin( e_orig_runtime )
                     .extra( "Sequence,[B]" ) );
         }
      }
   break;

   default:
      throw new ParamError( ErrorParam( e_inv_params )
                           .origin( e_orig_runtime )
                           .extra( "Sequence,[B]" ) );
   }

   FalconObject *self = dyncast<FalconObject *>( vm->self().asObject() );
   self->setUserData( iter );
}


/*#
   @method hasCurrent Iterator
   @brief Check if the iterator is valid and can be used to
          access the underlying collection.
   @return true if the iterator is valid and can be used to
          access a current item.
*/

FALCON_FUNC  Iterator_hasCurrent( ::Falcon::VMachine *vm )
{
   CoreObject *self = vm->self().asObject();
   Iterator* iter = dyncast<Iterator *>( self->getFalconData() );
   vm->regA().setBoolean( iter->hasCurrent() );
}

/*#
   @method hasNext Iterator
   @brief Check if the iterator is valid and a @a Iterator.next operation would
          still leave it valid.
   @return true if there is an item past to the current one.
*/

FALCON_FUNC  Iterator_hasNext( ::Falcon::VMachine *vm )
{
   CoreObject *self = vm->self().asObject();
   Iterator* iter = dyncast<Iterator *>( self->getFalconData() );
   vm->regA().setBoolean( iter->hasNext() );
}

/*#
   @method hasPrev Iterator
   @brief Check if the iterator is valid and a @a Iterator.prev operation would
          still leave it valid.
   @return true if there is an item before to the current one.
*/
FALCON_FUNC  Iterator_hasPrev( ::Falcon::VMachine *vm )
{
   CoreObject *self = vm->self().asObject();
   Iterator* iter = dyncast<Iterator *>( self->getFalconData() );
   vm->regA().setBoolean( iter->hasPrev() );
}

/*#
   @method next Iterator
   @brief Advance the iterator.
   @return true if the iterator is still valid after next() has been completed.

   Moves the iterator to the next item in the collection.
   If the iterator is not valid anymore, or if the current element was the last
   in the collection, the method returns false.
   If the iterator has successfully moved, it returns true.
*/
FALCON_FUNC  Iterator_next( ::Falcon::VMachine *vm )
{
   CoreObject *self = vm->self().asObject();
   Iterator* iter = dyncast<Iterator *>( self->getFalconData() );
   if( ! iter->isValid() )
      throw new ParamError( ErrorParam( e_invalid_iter )
               .origin( e_orig_runtime ) );

   vm->regA().setBoolean( iter->next() );
}


/*#
   @method prev Iterator
   @brief Move the iterator back.
   @return true if the iterator is still valid after prev() has been completed.

   Moves the iterator to the previous item in the collection.
   If the iterator is not valid anymore, or if the current element was the
   first in the collection, the method returns false. If the iterator has
   successfully moved, it returns true.
*/
FALCON_FUNC  Iterator_prev( ::Falcon::VMachine *vm )
{
   CoreObject *self = vm->self().asObject();
   Iterator* iter = dyncast<Iterator *>( self->getFalconData() );
   if( ! iter->isValid() )
      throw new ParamError( ErrorParam( e_invalid_iter )
               .origin( e_orig_runtime ) );

   vm->regA().setBoolean( iter->prev() );
}

/*#
   @method value Iterator
   @brief Retreives the current item in the collection.
   @optparam subst New value for the current item.
   @return The current item.
   @raise AccessError if the iterator is not valid.

   If the iterator is valid, the method returns the value of
   the item being currently pointed by the iterator.

   If a parameter @b subst is given, the current value in the sequence
   is changed to @b substs.
*/
FALCON_FUNC  Iterator_value( ::Falcon::VMachine *vm )
{
   CoreObject *self = vm->self().asObject();
   Iterator* iter = dyncast<Iterator *>( self->getFalconData() );
   if( ! iter->isValid() )
      throw new ParamError( ErrorParam( e_invalid_iter )
               .origin( e_orig_runtime ) );

   Item* i_subst = vm->param(0);
   if( i_subst != 0 )
      iter->getCurrent() = *i_subst;
   else
      vm->regA() = iter->getCurrent();
}

/*#
   @method key Iterator
   @brief Retreives the current key in the collection.
   @return The current key.
   @raise AccessError if the iterator is not valid, or if the collection has not keys.

   If this iterator is valid and is pointing to a collection that provides key
   ordering (i.e. a dictionary),  it returns the current key; otherwise,
   it raises an AccessError.
*/

FALCON_FUNC  Iterator_key( ::Falcon::VMachine *vm )
{
   CoreObject *self = vm->self().asObject();
   Iterator* iter = dyncast<Iterator *>( self->getFalconData() );
   if( ! iter->isValid() )
      throw new ParamError( ErrorParam( e_invalid_iter )
               .origin( e_orig_runtime ) );

   vm->regA() = iter->getCurrentKey();
}


/*#
   @method equal Iterator
   @param item The item to which this iterator must be compared.
   @brief Check if this iterator is equal to the provided item.
   @return True if the item matches this iterator.

   This method overrides the FBOM equal method, and overloads
   the equality check of the VM.
*/

FALCON_FUNC  Iterator_compare( ::Falcon::VMachine *vm )
{
   Item *i_other = vm->param(0);
   CoreObject *self = vm->self().asObject();
   Iterator* iter = dyncast<Iterator *>( self->getFalconData() );

   if( i_other == 0 || ! i_other->isOfClass( "Iterator" ) )
   {
      throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).extra( "Iterator" ).origin( e_orig_runtime ) );
   }

   Iterator* other = static_cast<Iterator*>( i_other->asObjectSafe()->getUserData() );

   if( ! iter->isValid() || ! other->isValid() )
      throw new ParamError( ErrorParam( e_invalid_iter )
               .origin( e_orig_runtime ) );

   if( other->equal(*iter) )
      vm->retval( (int64) 0 );   // declare them equal
   else
      // let the standard algorithm to decide.
      vm->retnil();
}

/*#
   @method clone Iterator
   @brief Returns an instance of this iterator pointing to the same item.
   @return A new copy of this iterator.

   Creates an iterator equivalent to this one. In this way, it is possible
   to record a previous position and use it later. Using a normal assignment
   wouldn't work, as the assignand would just be given the same iterator, and
   its value would change accordingly with the other image of the iterator.

*/

FALCON_FUNC  Iterator_clone( ::Falcon::VMachine *vm )
{
   CoreObject *self = vm->self().asObject();
   Iterator *iter = dyncast<Iterator *>( self->getFalconData() );
   if( ! iter->isValid() )
      throw new ParamError( ErrorParam( e_invalid_iter )
               .origin( e_orig_runtime ) );

   Item* i_itercls = vm->findWKI( "Iterator" );
   fassert( i_itercls != 0 && i_itercls->isClass() );
   CoreClass* Itercls = i_itercls->asClass();
   CoreObject* io = Itercls->createInstance();
   io->setUserData( iter->clone() );

   vm->retval( io );
}

/*#
   @method erase Iterator
   @brief Erase current item in the underlying sequence.
   @raise AccessError if the iterator is invalid.

   If the iterator is valid, this method removes current item. The iterator
   is moved to the very next item in the collection, and this may invalidate
   it if the removed element was the last one. To remove element while performing
   a scanning from the last element to the first one, remember to call the prev()
   method after every remove(); in forward scans, a successful remove() implies
   that the caller must not call next() to continue the scan.
*/
FALCON_FUNC  Iterator_erase( ::Falcon::VMachine *vm )
{
   CoreObject *self = vm->self().asObject();
   Iterator *iter = dyncast<Iterator *>( self->getFalconData() );
   if( ! iter->isValid() )
      throw new ParamError( ErrorParam( e_invalid_iter )
               .origin( e_orig_runtime ) );
   iter->erase();
}

/*#
   @method find Iterator
   @param key the key to be searched.
   @brief Moves this iterator on the searched item.
   @return true if the key was found, false otheriwse.
   @raise AccessError if the iterator is invalid or if the sequence doesn't provide keys.

   This method searches for an key in the underlying sequence, provided it offers search
   keys support. This is the case of the various dictionaries.

   This search is optimizied so that the subtree below the current position of the iterator
   is searched first. If the iterator is pointing to an item that matches the required
   key, this method returns immediately.

   After a succesful search, the iterator is moved to the position of the searched item.

   After a failed search, the iterator is moved to the smallest item in the sequence
   greater than the desired key; it's the best position for an insertion of the searched
   key.

   For example, to traverse all the items in a dictionary starting with 'C',
   the following code can be used:

   @code
   dict = [ "Alpha" => 1, "Beta" => 2, "Charlie" => 3, "Columbus" => 4, "Delta" => 5 ]
   iter = Iterator( dict )

   iter.find( "C" )  // we don't care if it succeeds
   while iter.hasCurrent() and iter.key()[0] == "C"
      > iter.key(), " => ", iter.value()
      iter.next()
   end
   @endcode

   Also, a failed search gives anyhow a useful hint position for a subsequent
   insertion, which may avoid performing the search again.
*/
FALCON_FUNC  Iterator_find( ::Falcon::VMachine *vm )
{
   CoreObject *self = vm->self().asObject();
   Iterator *iter = dyncast<Iterator *>( self->getFalconData() );
   Item* i_key = vm->param(0);

   if( i_key == 0 )
   {
      throw new ParamError( ErrorParam( e_inv_params )
                     .origin( e_orig_runtime )
                     .extra( "X" ) );
   }

   if( ! iter->isValid() )
      throw new ParamError( ErrorParam( e_invalid_iter )
               .origin( e_orig_runtime ) );

   if( iter->sequence()->isDictionary() )
   {
      ItemDict* dict = static_cast<ItemDict*>(iter->sequence());
      vm->regA().setBoolean( dict->findIterator( *i_key, *iter ) );
   }
   else
   {
      throw new ParamError( ErrorParam( e_non_dict_seq )
                     .origin( e_orig_runtime ) );
   }
}

/*#
   @method insert Iterator
   @param key Item to be inserted (or key, if the underlying sequence is keyed).
   @optparam value A value associated with the key.
   @brief Insert an item, or a pair of key values, in an underlying sequence.
   @raise AccessError if the iterator is invalid.

   Inserts an item at current position. In case the underlying sequence is an
   ordered sequence of key-value pairs, a correct position for insertion is first
   searched, and then the iterator is moved to the position of the inserted key.

   In this second case, if the iterator already points to a valid position for
   insertion of the given key, the search step is skipped.

   @see Iterator.find
*/

FALCON_FUNC  Iterator_insert( ::Falcon::VMachine *vm )
{
   CoreObject *self = vm->self().asObject();
   Iterator *iter = dyncast<Iterator *>( self->getFalconData() );

   if( ! iter->isValid() )
        throw new ParamError( ErrorParam( e_invalid_iter )
                 .origin( e_orig_runtime ) );

   if( iter->sequence()->isDictionary() )
   {
      Item *i_key = vm->param(0);
      Item *i_value = vm->param(1);
      if( i_key == 0 || i_value == 0 )
      {
         throw new ParamError( ErrorParam( e_inv_params, __LINE__ )
            .origin( e_orig_runtime )
            .extra( "X,X" ) );
      }

      static_cast<ItemDict*>( iter->sequence() )->put( *i_key, *i_value );
   }
   else
   {
      Item *i_key = vm->param(0);
      if( vm->param(1) != 0 )
      {
         throw new ParamError( ErrorParam( e_non_dict_seq )
                              .origin( e_orig_runtime ) );
      }

      if( i_key == 0 )
      {
      throw new ParamError( ErrorParam( e_inv_params, __LINE__ )
         .origin( e_orig_runtime )
         .extra( "X" ) );
      }


      iter->insert( *i_key );
   }
}

}
}

/* end of iterator_ext.cpp */