Annotation of parser3/src/classes/array.C, revision 1.6

1.1       moko        1: /** @file
                      2:        Parser: @b array parser class.
                      3: 
                      4:        Copyright (c) 2001-2023 Art. Lebedev Studio (http://www.artlebedev.com)
                      5:        Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru>
                      6: */
                      7: 
                      8: #include "classes.h"
                      9: #include "pa_vmethod_frame.h"
                     10: 
                     11: #include "pa_request.h"
                     12: #include "pa_charsets.h"
                     13: #include "pa_varray.h"
                     14: #include "pa_vvoid.h"
                     15: #include "pa_sql_connection.h"
                     16: #include "pa_vtable.h"
                     17: #include "pa_vbool.h"
                     18: #include "pa_vmethod_frame.h"
                     19: 
1.6     ! moko       20: volatile const char * IDENT_ARRAY_C="$Id: array.C,v 1.5 2024/09/20 01:13:50 moko Exp $";
1.1       moko       21: 
                     22: // class
                     23: 
                     24: class MArray: public Methoded {
                     25: public: // VStateless_class
                     26:        Value* create_new_value(Pool&) { return new VArray; }
                     27: 
                     28: public:
                     29:        MArray();
                     30: };
                     31: 
                     32: // global variable
                     33: 
                     34: DECLARE_CLASS_VAR(array, new MArray);
                     35: 
1.5       moko       36: const char* const PARAM_ARRAY_OR_HASH = "param must be array or hash";
1.6     ! moko       37: const char* const PARAM_ARRAY = "param must be array";
1.5       moko       38: 
1.1       moko       39: // methods
                     40: 
1.5       moko       41: enum HState {
                     42:     HS_FIRST,
                     43:     HS_STRING,
                     44:     HS_NUMBER
                     45: };
                     46: 
1.1       moko       47: static void _create_or_add(Request& r, MethodParams& params) {
                     48:        if(params.count()) {
1.5       moko       49:                Value& vsrc=params.as_no_junction(0, PARAM_ARRAY_OR_HASH);
1.1       moko       50:                VArray& self=GET_SELF(r, VArray);
                     51:                ArrayValue& self_array=self.array();
                     52: 
1.3       moko       53:                if(VArray* src=dynamic_cast<VArray*>(&vsrc)) {
1.6     ! moko       54:                        if(src==&self)
        !            55:                                throw Exception(PARSER_RUNTIME, 0, "source and destination are the same array");
1.5       moko       56:                        self_array.append(src->array());
1.1       moko       57:                } else {
                     58:                        HashStringValue* src_hash=vsrc.get_hash();
1.5       moko       59:                        if(!src_hash)
                     60:                                return;
                     61:                        HState hs=HS_FIRST;
                     62:                        for(HashStringValue::Iterator i(*src_hash); i; i.next()){
                     63:                                if (hs==HS_STRING){
1.1       moko       64:                                        self_array+=i.value();
1.5       moko       65:                                } else if(hs==HS_NUMBER){
                     66:                                        self_array.put(VArray::index(i.key()), i.value());
                     67:                                } else {
                     68:                                        try {
                     69:                                                self_array.put(VArray::index(i.key()), i.value());
                     70:                                                hs==HS_NUMBER;
                     71:                                        } catch(...) {
                     72:                                                self_array+=i.value();
                     73:                                                hs==HS_STRING;
                     74:                                        }
                     75:                                }
                     76:                        }
1.1       moko       77:                }
1.4       moko       78:                self.invalidate();
1.1       moko       79:        }
                     80: }
                     81: 
1.6     ! moko       82: static ArrayValue::Action_options get_action_options(Request& r, MethodParams& params, size_t options_index) {
        !            83:        ArrayValue::Action_options result;
        !            84:        if(params.count() <= options_index)
        !            85:                return result;
        !            86: 
        !            87:        HashStringValue* options=params.as_hash(options_index);
        !            88:        if(!options)
        !            89:                return result;
        !            90: 
        !            91:        result.defined=true;
        !            92:        int valid_options=0;
        !            93: 
        !            94:        if(Value* voffset=options->get(sql_offset_name)) {
        !            95:                valid_options++;
        !            96:                result.offset=r.process(*voffset).as_int();
        !            97:        }
        !            98:        if(Value* vlimit=options->get(sql_limit_name)) {
        !            99:                valid_options++;
        !           100:                result.limit=r.process(*vlimit).as_int();
        !           101:        }
        !           102: 
        !           103:        if(valid_options!=options->count())
        !           104:                throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
        !           105: 
        !           106:        return result;
        !           107: }
        !           108: 
        !           109: static void _join(Request& r, MethodParams& params) {
        !           110:        Value& vsrc=params.as_no_junction(0, PARAM_ARRAY);
        !           111:        ArrayValue::Action_options o=get_action_options(r, params, 1);
        !           112: 
        !           113:        VArray& self=GET_SELF(r, VArray);
        !           114:        ArrayValue& self_array=self.array();
        !           115: 
        !           116:        if(VArray* src=dynamic_cast<VArray*>(&vsrc)) {
        !           117:                if(src==&self)
        !           118:                        throw Exception(PARSER_RUNTIME, 0, "source and destination are the same array");
        !           119: 
        !           120:                if(o.defined){
        !           121:                        for(ArrayValue::Iterator i(src->array()); i; i.next()){
        !           122:                                if(i.value()){
        !           123:                                        if(o.offset > 0){
        !           124:                                                o.offset--;
        !           125:                                                continue;
        !           126:                                        }
        !           127:                                        if(o.limit-- == 0)
        !           128:                                                break;
        !           129:                                        self_array+=i.value();
        !           130:                                }
        !           131:                        }
        !           132:                } else {
        !           133:                        for(ArrayValue::Iterator i(src->array()); i; i.next()){
        !           134:                                if(i.value())
        !           135:                                        self_array+=i.value();
        !           136:                        }
        !           137:                }
        !           138:                self.invalidate();
        !           139:        } else
        !           140:                throw Exception(PARSER_RUNTIME, 0, PARAM_ARRAY);
        !           141: }
        !           142: 
1.1       moko      143: static void _sql(Request& r, MethodParams& params) {}
                    144: 
                    145: static void _sub(Request& r, MethodParams& params) {}
                    146: 
                    147: static void _union(Request& r, MethodParams& params) {}
                    148: 
                    149: static void _intersection(Request& r, MethodParams& params) {}
                    150: 
                    151: static void _intersects(Request& r, MethodParams& params) {}
                    152: 
                    153: static void _keys(Request& r, MethodParams& params) {
                    154:        const String* keys_column_name;
                    155:        if(params.count()>0)
                    156:                keys_column_name=&params.as_string(0, COLUMN_NAME_MUST_BE_STRING);
                    157:        else 
                    158:                keys_column_name=new String("key");
                    159: 
                    160:        Table::columns_type columns(new ArrayString(1));
                    161:        *columns+=keys_column_name;
                    162:        Table* table=new Table(columns);
                    163: 
                    164:        ArrayValue& array=GET_SELF(r, VArray).array();
                    165:        for(ArrayValue::Iterator i(array); i; i.next()){
                    166:                if(i.value()){
                    167:                        Table::element_type row(new ArrayString(1));
                    168:                        *row+=new String(i.key(), String::L_TAINTED);
                    169:                        *table+=row;
                    170:                }
                    171:        }
                    172: 
                    173:        r.write(*new VTable(table));
                    174: }
                    175: 
1.5       moko      176: static void _count(Request& r, MethodParams& params) {
                    177:        ArrayValue& array=GET_SELF(r, VArray).array();
                    178:        if(params.count()>0){
                    179:                const String& what=params.as_string(0, PARAMETER_MUST_BE_STRING);
                    180:                if(!what.is_empty()){
                    181:                        if(what != "all")
                    182:                                throw Exception(PARSER_RUNTIME, &what, "param must be empty or 'all'");
                    183:                        return r.write(*new VInt(array.count()));
                    184:                }
                    185:        }
                    186:        r.write(*new VInt(array.used()));
1.1       moko      187: }
                    188: 
1.2       moko      189: static void _append(Request& r, MethodParams& params) {
                    190:        VArray& self=GET_SELF(r, VArray);
                    191:        ArrayValue& array=self.array();
                    192: 
                    193:        int count=params.count();
                    194: 
                    195:        for(int i=0; i<count; i++){
                    196:                array+=&r.process(params[i]);
                    197:        }
1.4       moko      198:        self.invalidate();
1.2       moko      199: }
                    200: 
                    201: static void _insert(Request& r, MethodParams& params) {
                    202:        VArray& self=GET_SELF(r, VArray);
                    203:        ArrayValue& array=self.array();
                    204: 
                    205:        int count=params.count();
                    206:        size_t index=VArray::index(params.as_int(0, "index must be integer", r));
                    207: 
                    208:        for(int i=1; i<count; i++){
                    209:                array.insert(index+i-1, &r.process(params[i]));
                    210:        }
1.4       moko      211:        self.invalidate();
1.2       moko      212: }
                    213: 
1.1       moko      214: static void _delete(Request& r, MethodParams& params) {
                    215:        if(params.count()>0)
                    216:                GET_SELF(r, VArray).clear(VArray::index(params.as_int(0, "index must be integer", r)));
                    217:        else
                    218:                GET_SELF(r, VArray).clear();
                    219: }
                    220: 
                    221: static void _contains(Request& r, MethodParams& params) {
                    222:        VArray& self=GET_SELF(r, VArray);
                    223:        bool result=self.contains(VArray::index(params.as_int(0, "index must be integer", r)));
                    224:        r.write(VBool::get(result));
                    225: }
                    226: 
1.5       moko      227: static void _for(Request& r, MethodParams& params) {
                    228:        InCycle temp(r);
                    229: 
                    230:        const String* value_var_name=&params.as_string(0, "value-var name must be string");
                    231:        Value* body_code=&params.as_junction(1, "body must be code");
                    232:        Value* delim_maybe_code=params.count()>2?&params[2]:0;
                    233:        Value& caller=*r.get_method_frame()->caller();
                    234: 
                    235:        if(value_var_name->is_empty()) value_var_name=0;
                    236: 
                    237:        ArrayValue& array=GET_SELF(r, VArray).array();
                    238: 
                    239:        if(delim_maybe_code){ // delimiter set
                    240:                bool need_delim=false;
                    241:                for(ArrayValue::Iterator i(array); i; i.next()){
                    242:                        if(value_var_name)
                    243:                                r.put_element(caller, *value_var_name, i.value() ? i.value() : VVoid::get());
                    244: 
                    245:                        Value& sv_processed=r.process(*body_code);
                    246:                        TempSkip4Delimiter skip(r);
                    247: 
                    248:                        const String* s_processed=sv_processed.get_string();
                    249:                        if(s_processed && !s_processed->is_empty()) { // we have body
                    250:                                if(need_delim) // need delim & iteration produced string?
                    251:                                        r.write(r.process(*delim_maybe_code));
                    252:                                else
                    253:                                        need_delim=true;
                    254:                        }
                    255: 
                    256:                        r.write(sv_processed);
                    257: 
                    258:                        if(skip.check_break())
                    259:                                break;
                    260:                }
                    261:        } else {
                    262:                for(ArrayValue::Iterator i(array); i; i.next()){
                    263:                        if(value_var_name)
                    264:                                r.put_element(caller, *value_var_name, i.value() ? i.value() : VVoid::get());
                    265: 
                    266:                        r.process_write(*body_code);
                    267: 
                    268:                        if(r.check_skip_break())
                    269:                                break;
                    270:                }
                    271:        }
                    272: }
                    273: 
1.1       moko      274: static void _foreach(Request& r, MethodParams& params) {
1.5       moko      275:        if(params[1].get_junction())
                    276:                return _for(r, params);
                    277: 
1.1       moko      278:        InCycle temp(r);
                    279: 
                    280:        const String* key_var_name=&params.as_string(0, "key-var name must be string");
                    281:        const String* value_var_name=&params.as_string(1, "value-var name must be string");
                    282:        Value* body_code=&params.as_junction(2, "body must be code");
                    283:        Value* delim_maybe_code=params.count()>3?&params[3]:0;
                    284:        Value& caller=*r.get_method_frame()->caller();
                    285: 
                    286:        if(key_var_name->is_empty()) key_var_name=0;
                    287:        if(value_var_name->is_empty()) value_var_name=0;
                    288: 
                    289:        ArrayValue& array=GET_SELF(r, VArray).array();
                    290: 
                    291:        if(delim_maybe_code){ // delimiter set
                    292:                bool need_delim=false;
                    293:                for(ArrayValue::Iterator i(array); i; i.next()){
                    294:                        if(i.value()){
                    295:                                if(key_var_name){
                    296:                                        VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
                    297:                                        r.put_element(caller, *key_var_name, vkey);
                    298:                                }
                    299: 
                    300:                                if(value_var_name)
                    301:                                        r.put_element(caller, *value_var_name, i.value());
                    302: 
                    303:                                Value& sv_processed=r.process(*body_code);
                    304:                                TempSkip4Delimiter skip(r);
                    305: 
                    306:                                const String* s_processed=sv_processed.get_string();
                    307:                                if(s_processed && !s_processed->is_empty()) { // we have body
                    308:                                        if(need_delim) // need delim & iteration produced string?
                    309:                                                r.write(r.process(*delim_maybe_code));
                    310:                                        else
                    311:                                                need_delim=true;
                    312:                                }
                    313: 
                    314:                                r.write(sv_processed);
                    315: 
                    316:                                if(skip.check_break())
                    317:                                        break;
                    318:                        }
                    319:                }
                    320:        } else {
                    321:                for(ArrayValue::Iterator i(array); i; i.next()){
                    322:                        if(i.value()){
                    323:                                if(key_var_name){
                    324:                                        VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
                    325:                                        r.put_element(caller, *key_var_name, vkey);
                    326:                                }
                    327: 
                    328:                                if(value_var_name)
                    329:                                        r.put_element(caller, *value_var_name, i.value());
                    330: 
                    331:                                r.process_write(*body_code);
                    332: 
                    333:                                if(r.check_skip_break())
                    334:                                        break;
                    335:                        }
                    336:                }
                    337:        }
                    338: }
                    339: 
                    340: #ifndef DOXYGEN
                    341: struct Array_seq_item : public PA_Allocated {
                    342:        Value *array_data;
                    343:        union {
                    344:                const char *c_str;
                    345:                double d;
                    346:        } value;
                    347: };
                    348: #endif
                    349: 
                    350: static int sort_cmp_string(const void *a, const void *b) {
                    351:        return strcmp(
                    352:                static_cast<const Array_seq_item *>(a)->value.c_str,
                    353:                static_cast<const Array_seq_item *>(b)->value.c_str
                    354:        );
                    355: }
                    356: static int sort_cmp_double(const void *a, const void *b) {
                    357:        double va=static_cast<const Array_seq_item *>(a)->value.d;
                    358:        double vb=static_cast<const Array_seq_item *>(b)->value.d;
                    359:        if(va<vb)
                    360:                return -1;
                    361:        else if(va>vb)
                    362:                return +1;
                    363:        else 
                    364:                return 0;
                    365: }
                    366: 
                    367: static void _sort(Request& r, MethodParams& params){
                    368:        const String& key_var_name=params.as_string(0, "key-var name must be string");
                    369:        const String& value_var_name=params.as_string(1, "value-var name must be string");
                    370:        Value& key_maker=params.as_junction(2, "key-maker must be code");
                    371:        bool reverse=params.count()>3 && params.as_no_junction(3, "order must not be code").as_string()=="desc"; // default=asc
                    372: 
                    373:        const String* key_var=key_var_name.is_empty()? 0 : &key_var_name;
                    374:        const String* value_var=value_var_name.is_empty()? 0 : &value_var_name;
                    375:        VMethodFrame* context=r.get_method_frame()->caller();
                    376: 
                    377:        VArray& self=GET_SELF(r, VArray);
                    378:        ArrayValue& array=self.array();
1.4       moko      379:        int count=array.used(); // not array.count()
1.1       moko      380: 
                    381:        Array_seq_item* seq=new Array_seq_item[count];
                    382:        int pos=0;
                    383:        bool key_values_are_strings=true;
                    384: 
                    385:        for(ArrayValue::Iterator i(array); i; i.next() ){
                    386:                if(i.value()){
                    387:                        if(key_var)
                    388:                                r.put_element(*context, *key_var, new VString(*new String(i.key(), String::L_TAINTED)));
                    389:                        if(value_var)
                    390:                                r.put_element(*context, *value_var, i.value());
                    391: 
                    392:                        Value& value=r.process(key_maker);
                    393:                        if(pos==0) // determining key values type by first one
                    394:                                key_values_are_strings=value.is_string();
                    395: 
                    396:                        seq[pos].array_data=i.value();
                    397:                        if(key_values_are_strings)
                    398:                                seq[pos++].value.c_str=value.as_string().cstr();
                    399:                        else
                    400:                                seq[pos++].value.d=value.as_expr_result().as_double();
                    401:                }
                    402:        }
                    403: 
                    404:        // @todo: handle this elsewhere
                    405:        if(r.charsets.source().NAME()=="KOI8-R" && key_values_are_strings)
                    406:                for(pos=0; pos<count; pos++)
                    407:                        if(*seq[pos].value.c_str)
                    408:                                seq[pos].value.c_str=Charset::transcode(seq[pos].value.c_str, r.charsets.source(), pa_UTF8_charset).cstr();
                    409: 
                    410:        // sort keys
                    411:        qsort(seq, count, sizeof(Array_seq_item), key_values_are_strings ? sort_cmp_string : sort_cmp_double);
                    412: 
                    413:        // reorder array as required in 'seq'
                    414:        array.clear();
                    415:        if(reverse)
                    416:                for(pos=count-1; pos>=0; pos--)
                    417:                        array+=seq[pos].array_data;
                    418:        else
                    419:                for(pos=0; pos<count; pos++)
                    420:                        array+=seq[pos].array_data;
                    421: 
                    422:        delete[] seq;
                    423: }
                    424: 
1.5       moko      425: enum AtResultType {
                    426:        AtResultTypeValue = 0,
                    427:        AtResultTypeKey = 1,
                    428:        AtResultTypeHash = 2
                    429: };
                    430: 
                    431: inline Value& SingleElementHash(String::Body akey, Value* avalue) {
                    432:        Value& result=*new VHash;
                    433:        result.put_element(*new String(akey, String::L_TAINTED), avalue);
                    434:        return result;
                    435: }
                    436: 
1.1       moko      437: static void _at(Request& r, MethodParams& params) {
                    438:        VArray& self=GET_SELF(r, VArray);
                    439:        ArrayValue& array=self.array();
1.5       moko      440:        size_t count=array.used(); // not array.count()
1.1       moko      441: 
                    442:        int pos=0;
                    443: 
                    444:        AtResultType result_type=AtResultTypeValue;
                    445:        if(params.count() > 1) {
                    446:                const String& stype=params.as_string(1, "type must be string");
                    447:                if(stype == "key")
                    448:                        result_type=AtResultTypeKey;
                    449:                else if(stype == "hash")
                    450:                        result_type=AtResultTypeHash;
                    451:                else if(stype != "value")
                    452:                        throw Exception(PARSER_RUNTIME, &stype, "type must be 'key', 'value' or 'hash'");
                    453:        }
                    454: 
                    455:        Value& vwhence=params[0];
                    456:        if(vwhence.is_string()) {
                    457:                const String& swhence=*vwhence.get_string();
                    458:                if(swhence == "last")
                    459:                        pos=count-1;
                    460:                else if(swhence != "first")
                    461:                        throw Exception(PARSER_RUNTIME, &swhence, "whence must be 'first', 'last' or expression");
                    462:        } else {
                    463:                pos=r.process(vwhence).as_int();
                    464:                if(pos < 0)
                    465:                        pos+=count;
                    466:        }
                    467: 
                    468:        if(count && pos >= 0 && (size_t)pos < count){
                    469:                switch(result_type) {
                    470:                        case AtResultTypeKey:
                    471:                                {
                    472:                                        for(ArrayValue::Iterator i(array); i; i.next() ){
                    473:                                                if(i.value() && !(pos--)){
                    474:                                                        r.write(*new VString(*new String(i.key(), String::L_TAINTED)));
                    475:                                                        break;
                    476:                                                }
                    477:                                        }
                    478:                                        break;
                    479:                                }
                    480:                        case AtResultTypeValue:
                    481:                                {
                    482:                                        for(ArrayValue::Iterator i(array); i; i.next() )
                    483:                                                if(i.value() &&!(pos--)){
                    484:                                                        r.write(*i.value());
                    485:                                                        break;
                    486:                                                }
                    487:                                        break;
                    488:                                }
                    489:                        case AtResultTypeHash:
                    490:                                {
                    491:                                        for(ArrayValue::Iterator i(array); i; i.next() )
                    492:                                                if(i.value() &&!(pos--)){
                    493:                                                        r.write(SingleElementHash(i.key(), i.value()));
                    494:                                                        break;
                    495:                                                }
                    496:                                        break;
                    497:                                }
                    498:                }
                    499:        }
                    500: }
                    501: 
                    502: 
                    503: extern String table_reverse_name;
                    504: 
                    505: static void _select(Request& r, MethodParams& params) {
                    506:        InCycle temp(r);
                    507:        const String* key_var_name=&params.as_string(0, "key-var name must be string");
                    508:        const String* value_var_name=&params.as_string(1, "value-var name must be string");
                    509:        Value& vcondition=params.as_expression(2, "condition must be number, bool or expression");
                    510: 
                    511:        if(key_var_name->is_empty()) key_var_name=0;
                    512:        if(value_var_name->is_empty()) value_var_name=0;
                    513: 
                    514:        ArrayValue& source_array=GET_SELF(r, VArray).array();
                    515:        Value& caller=*r.get_method_frame()->caller();
                    516: 
                    517:        int limit=source_array.count();
                    518:        bool reverse=false;
                    519: 
                    520:        if(params.count()>3)
                    521:                if(HashStringValue* options=params.as_hash(3)) {
                    522:                        int valid_options=0;
                    523:                        if(Value* vlimit=options->get(sql_limit_name)) {
                    524:                                valid_options++;
                    525:                                limit=r.process(*vlimit).as_int();
                    526:                        }
                    527:                        if(Value* vreverse=options->get(table_reverse_name)) {
                    528:                                valid_options++;
                    529:                                reverse=r.process(*vreverse).as_bool();
                    530:                        }
                    531:                        if(valid_options!=options->count())
                    532:                                throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
                    533:                }
                    534: 
                    535:        VArray *result=new VArray;
                    536:        ArrayValue& result_array=result->array();
                    537: 
                    538:        if(limit>0){
                    539:                if(reverse){
                    540:                        for(ArrayValue::ReverseIterator i(source_array); i; ){
1.5       moko      541:                                if(Value *value=i.prev()){ // here for correct i.key()
                    542:                                        if(key_var_name)
                    543:                                                r.put_element(caller, *key_var_name, new VString(*new String(i.key(), String::L_TAINTED)));
                    544:                                        if(value_var_name)
                    545:                                                r.put_element(caller, *value_var_name, value);
1.1       moko      546: 
1.5       moko      547:                                        bool condition=r.process(vcondition).as_bool();
1.1       moko      548: 
1.5       moko      549:                                        if(r.check_skip_break())
                    550:                                                break;
1.1       moko      551: 
1.5       moko      552:                                        if(condition){
                    553:                                                result_array+=value;
                    554:                                                if(!--limit)
                    555:                                                        break;
                    556:                                        }
1.1       moko      557:                                }
                    558:                        }
                    559:                } else {
                    560:                        for(ArrayValue::Iterator i(source_array); i; i.next() ){
1.5       moko      561:                                if(Value *value=i.value()){
1.1       moko      562:                                        if(key_var_name)
                    563:                                                r.put_element(caller, *key_var_name, new VString(*new String(i.key(), String::L_TAINTED)));
                    564:                                        if(value_var_name)
                    565:                                                r.put_element(caller, *value_var_name, value);
                    566: 
                    567:                                        bool condition=r.process(vcondition).as_bool();
                    568: 
                    569:                                        if(r.check_skip_break())
                    570:                                                break;
                    571: 
                    572:                                        if(condition){
                    573:                                                result_array+=value;
                    574:                                                if(!--limit)
                    575:                                                        break;
                    576:                                        }
                    577:                                }
                    578:                        }
                    579:                }
                    580:        }
                    581: 
                    582:        r.write(*result);
                    583: }
                    584: 
                    585: static void _reverse(Request& r, MethodParams& params) {
1.5       moko      586:        ArrayValue& source_array=GET_SELF(r, VArray).array();
1.1       moko      587: 
1.5       moko      588:        VArray& result=*new VArray(source_array.count());
1.1       moko      589:        ArrayValue& result_array=result.array();
                    590: 
1.4       moko      591:        for(ArrayValue::ReverseIterator i(source_array); i; ){
                    592:                result_array+=i.prev();
1.1       moko      593:        }
                    594: 
                    595:        r.write(result);
                    596: }
                    597: 
                    598: 
                    599: // constructor
                    600: 
                    601: MArray::MArray(): Methoded(VARRAY_TYPE) {
                    602: 
                    603:        // ^array::create[[copy_from]]
                    604:        add_native_method("create", Method::CT_DYNAMIC, _create_or_add, 0, 1);
                    605:        // ^array.add[add_from]
                    606:        add_native_method("add", Method::CT_DYNAMIC, _create_or_add, 1, 1);
1.6     ! moko      607:        // ^array.join[join_from[;options]]
        !           608:        add_native_method("join", Method::CT_DYNAMIC, _join, 1, 2);
1.1       moko      609: 
                    610:        // ^array.sub[sub_from]
                    611:        add_native_method("sub", Method::CT_DYNAMIC, _sub, 1, 1);
1.2       moko      612:        // ^array.union[b] = array
1.1       moko      613:        add_native_method("union", Method::CT_DYNAMIC, _union, 1, 1);
1.2       moko      614:        // ^array.intersection[b][options array] = array
1.1       moko      615:        add_native_method("intersection", Method::CT_DYNAMIC, _intersection, 1, 2);
1.2       moko      616:        // ^array.intersects[b] = bool
1.1       moko      617:        add_native_method("intersects", Method::CT_DYNAMIC, _intersects, 1, 1);
                    618: 
1.2       moko      619:        // ^array.append[value;value]
                    620:        add_native_method("append", Method::CT_DYNAMIC, _append, 1, 10000);
                    621: 
                    622:        // ^array.insert[index;value...]
                    623:        add_native_method("insert", Method::CT_DYNAMIC, _insert, 2, 10000);
                    624: 
                    625:        // ^array.delete[index]
1.1       moko      626:        add_native_method("delete", Method::CT_DYNAMIC, _delete, 0, 1);
                    627: 
1.2       moko      628:        // ^array.contains[index]
1.1       moko      629:        add_native_method("contains", Method::CT_DYNAMIC, _contains, 1, 1);
                    630: 
                    631:        // ^array::sql[query][options array]
                    632:        add_native_method("sql", Method::CT_DYNAMIC, _sql, 1, 2);
                    633: 
                    634:        // ^array._keys[[column name]]
                    635:        add_native_method("_keys", Method::CT_DYNAMIC, _keys, 0, 1);
                    636: 
1.5       moko      637:        // ^array._count[[all]]
                    638:        add_native_method("_count", Method::CT_DYNAMIC, _count, 0, 1);
1.1       moko      639: 
1.2       moko      640:        // ^array.foreach[index;value]{code}[delim]
1.5       moko      641:        add_native_method("foreach", Method::CT_DYNAMIC, _foreach, 2, 2+1+1);
1.1       moko      642: 
1.2       moko      643:        // ^array.sort[index;value]{string-key-maker}[[asc|desc]]
                    644:        // ^array.sort[index;value](numeric-key-maker)[[asc|desc]]
1.1       moko      645:        add_native_method("sort", Method::CT_DYNAMIC, _sort, 3, 4);
                    646: 
1.2       moko      647:        // ^array.select[index;value](bool-condition)[options hash]
1.1       moko      648:        add_native_method("select", Method::CT_DYNAMIC, _select, 3, 4);
                    649: 
                    650:        // ^array.reverse[]
                    651:        add_native_method("reverse", Method::CT_DYNAMIC, _reverse, 0, 0);
                    652: 
1.2       moko      653:        // ^array._at[first|last[;'key'|'value'|'hash']]
                    654:        // ^array._at([-+]offset)[['key'|'value'|'hash']]
1.1       moko      655:        add_native_method("_at", Method::CT_DYNAMIC, _at, 1, 2);
                    656: 
                    657: #ifdef FEATURE_GET_ELEMENT4CALL
                    658:        // aliases without "_"
                    659:        add_native_method("keys", Method::CT_DYNAMIC, _keys, 0, 1);
                    660:        add_native_method("count", Method::CT_DYNAMIC, _count, 0, 0);
                    661:        add_native_method("at", Method::CT_DYNAMIC, _at, 1, 2);
                    662: #endif
                    663: 
                    664: }

E-mail: