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

1.1       moko        1: /** @file
                      2:        Parser: @b array parser class.
                      3: 
1.35      moko        4:        Copyright (c) 2001-2024 Art. Lebedev Studio (http://www.artlebedev.com)
1.1       moko        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.37    ! moko       20: volatile const char * IDENT_ARRAY_C="$Id: array.C,v 1.36 2024/11/17 14:04:28 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.7       moko       37: const char* const PARAM_INDEX = "index must be integer";
1.5       moko       38: 
1.1       moko       39: // methods
                     40: 
1.25      moko       41: static void _copy_or_add(Request& r, MethodParams& params) {
1.1       moko       42:        if(params.count()) {
1.5       moko       43:                Value& vsrc=params.as_no_junction(0, PARAM_ARRAY_OR_HASH);
1.1       moko       44:                VArray& self=GET_SELF(r, VArray);
                     45:                ArrayValue& self_array=self.array();
                     46: 
1.3       moko       47:                if(VArray* src=dynamic_cast<VArray*>(&vsrc)) {
1.6       moko       48:                        if(src==&self)
1.14      moko       49:                                return;
                     50:                        if(self_array.count()){
                     51:                                for(ArrayValue::Iterator i(src->array()); i; i.next()){
                     52:                                        if(i.value())
                     53:                                                self_array.put(i.index(), i.value());
                     54:                                }
                     55:                        } else {
1.33      moko       56:                                self_array.copy(src->array());
1.34      moko       57:                                return;
1.14      moko       58:                        }
1.1       moko       59:                } else {
1.15      moko       60:                        HashStringValue* src_hash=vsrc.as_hash("param must be array or");
1.5       moko       61:                        if(!src_hash)
                     62:                                return;
                     63:                        for(HashStringValue::Iterator i(*src_hash); i; i.next()){
1.7       moko       64:                                self_array.put(VArray::index(i.key()), i.value());
1.5       moko       65:                        }
1.1       moko       66:                }
1.23      moko       67:                self_array.invalidate();
1.1       moko       68:        }
                     69: }
                     70: 
1.6       moko       71: static ArrayValue::Action_options get_action_options(Request& r, MethodParams& params, size_t options_index) {
                     72:        ArrayValue::Action_options result;
                     73:        if(params.count() <= options_index)
                     74:                return result;
                     75: 
                     76:        HashStringValue* options=params.as_hash(options_index);
                     77:        if(!options)
                     78:                return result;
                     79: 
                     80:        result.defined=true;
                     81:        int valid_options=0;
                     82: 
                     83:        if(Value* voffset=options->get(sql_offset_name)) {
                     84:                valid_options++;
1.8       moko       85:                int offset=r.process(*voffset).as_int();
                     86:                result.offset=offset < 0 ? 0 : offset;
1.6       moko       87:        }
                     88:        if(Value* vlimit=options->get(sql_limit_name)) {
                     89:                valid_options++;
1.8       moko       90:                int limit=r.process(*vlimit).as_int();
                     91:                result.limit=limit < 0 ? 0: limit;
1.6       moko       92:        }
                     93: 
                     94:        if(valid_options!=options->count())
                     95:                throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
                     96: 
                     97:        return result;
                     98: }
                     99: 
                    100: static void _join(Request& r, MethodParams& params) {
1.7       moko      101:        Value& vsrc=params.as_no_junction(0, PARAM_ARRAY_OR_HASH);
1.6       moko      102:        ArrayValue::Action_options o=get_action_options(r, params, 1);
                    103: 
                    104:        VArray& self=GET_SELF(r, VArray);
                    105:        ArrayValue& self_array=self.array();
                    106: 
                    107:        if(VArray* src=dynamic_cast<VArray*>(&vsrc)) {
                    108:                if(src==&self)
                    109:                        throw Exception(PARSER_RUNTIME, 0, "source and destination are the same array");
                    110: 
                    111:                if(o.defined){
                    112:                        for(ArrayValue::Iterator i(src->array()); i; i.next()){
                    113:                                if(i.value()){
                    114:                                        if(o.offset > 0){
                    115:                                                o.offset--;
                    116:                                                continue;
                    117:                                        }
                    118:                                        if(o.limit-- == 0)
                    119:                                                break;
                    120:                                        self_array+=i.value();
                    121:                                }
                    122:                        }
                    123:                } else {
                    124:                        for(ArrayValue::Iterator i(src->array()); i; i.next()){
                    125:                                if(i.value())
                    126:                                        self_array+=i.value();
                    127:                        }
                    128:                }
1.7       moko      129:        } else {
1.15      moko      130:                HashStringValue* src_hash=vsrc.as_hash("param must be array or");
1.7       moko      131:                if(!src_hash)
                    132:                        return;
                    133:                if(o.defined){
                    134:                        for(HashStringValue::Iterator i(*src_hash); i; i.next()){
                    135:                                if(o.offset > 0){
                    136:                                        o.offset--;
                    137:                                        continue;
                    138:                                }
                    139:                                if(o.limit-- == 0)
                    140:                                        break;
                    141:                                self_array+=i.value();
                    142:                        }
                    143:                } else {
                    144:                        for(HashStringValue::Iterator i(*src_hash); i; i.next()){
                    145:                                self_array+=i.value();
                    146:                        }
                    147:                }
                    148:        }
1.23      moko      149:        self_array.invalidate();
1.6       moko      150: }
                    151: 
1.11      moko      152: #ifndef DOXYGEN
                    153: 
                    154: #define STRING(str) ((str) ? *new String(str, String::L_TAINTED /* no length as 0x00 can be inside */) : String::Empty)
                    155: 
                    156: class SparseArray_sql_event_handlers: public SQL_Driver_query_event_handlers {
                    157:        bool distinct;
                    158:        ArrayValue& result;
                    159:        Value* row_value;
                    160:        int column_index;
1.12      moko      161:        ArrayString* columns;
1.11      moko      162:        bool one_bool_column;
                    163:        Table2hash_value_type value_type;
                    164:        int columns_count;
                    165: public:
                    166:        Table* empty;
                    167: public:
                    168:        SparseArray_sql_event_handlers(bool adistinct, ArrayValue& aresult, Table2hash_value_type avalue_type):
                    169:                distinct(adistinct),
                    170:                result(aresult),
                    171:                row_value(0),
                    172:                column_index(0),
1.12      moko      173:                columns(new ArrayString),
1.11      moko      174:                one_bool_column(false),
                    175:                value_type(avalue_type),
                    176:                empty(0) {
                    177:        }
                    178: 
                    179:        bool add_column(SQL_Error& error, const char* str, size_t ) {
                    180:                try {
1.12      moko      181:                        if(columns_count){
                    182:                                // another query in multi_statements mode
                    183:                                columns=new ArrayString;
                    184:                                columns_count=0;
                    185:                        }
                    186:                        *columns+=&STRING(str);
1.11      moko      187:                        return false;
                    188:                } catch(...) {
                    189:                        error=SQL_Error("exception occurred in Hash_sql_event_handlers::add_column");
                    190:                        return true;
                    191:                }
                    192:        }
                    193: 
                    194:        bool before_rows(SQL_Error& error) {
1.12      moko      195:                columns_count=columns->count();
                    196:                if(columns_count<1) {
1.11      moko      197:                        error=SQL_Error("no columns");
                    198:                        return true;
                    199:                }
1.12      moko      200:                if(columns_count==1) {
1.11      moko      201:                        one_bool_column=true;
                    202:                } else {
                    203:                        switch(value_type){
                    204:                                case C_STRING: {
1.12      moko      205:                                        if(columns_count>2){
                    206:                                                error=SQL_Error("only 2 columns allowed for $.type[string] and $.sparse(true)");
1.11      moko      207:                                                return true;
                    208:                                        }
                    209:                                        break;
                    210:                                }
                    211:                                case C_TABLE: {
                    212:                                        // create empty table which we'll copy later
1.12      moko      213:                                        empty=new Table(columns);
1.11      moko      214:                                        break;
                    215:                                }
                    216:                        }
                    217:                }
                    218:                return false;
                    219:        }
                    220: 
                    221:        bool add_row(SQL_Error& /*error*/) {
                    222:                column_index=0;
                    223:                return false;
                    224:        }
                    225: 
                    226:        bool add_row_cell(SQL_Error& error, const char *str, size_t ) {
                    227:                try {
1.12      moko      228:                        if(column_index==columns_count){
                    229:                                // should never happen, buggy driver case
                    230:                                error=SQL_Error("columns index exceed the columns count");
                    231:                                return true;
                    232:                        }
                    233: 
1.11      moko      234:                        bool duplicate=false;
                    235:                        if(one_bool_column) {
                    236:                                size_t index=str ? pa_atoui(str) : 0;
                    237:                                duplicate=result.put_dont_replace(index, &VBool::get(true));  // put. existed?
                    238:                        } else if(column_index==0) {
                    239:                                size_t index=str ? pa_atoui(str) : 0;
                    240:                                switch(value_type){
                    241:                                        case C_HASH: {
                    242:                                                VHash* row_vhash=new VHash;
                    243:                                                row_value=row_vhash;
                    244:                                                duplicate=result.put_dont_replace(index, row_vhash); // put. existed?
                    245:                                                break;
                    246:                                        }
                    247:                                        case C_STRING: {
                    248:                                                VString* row_vstring=new VString();
                    249:                                                row_value=row_vstring;
                    250:                                                duplicate=result.put_dont_replace(index, row_vstring);  // put. existed?
                    251:                                                break;
                    252:                                        }
                    253:                                        case C_TABLE: {
                    254:                                                VTable* vtable=(VTable*)result.get(index);
                    255: 
                    256:                                                if(vtable) { // table with this key exist?
                    257:                                                        if(!distinct) {
                    258:                                                                duplicate=true;
                    259:                                                                break;
                    260:                                                        }
                    261:                                                } else {
                    262:                                                        // no? creating table of same structure as source
                    263:                                                        Table::Action_options table_options(0, 0);
                    264:                                                        vtable=new VTable(new Table(*empty, table_options/*no rows, just structure*/));
                    265:                                                        result.put(index, vtable); // put
                    266:                                                }
                    267:                                                ArrayString* row=new ArrayString(columns_count);
                    268:                                                *row+=&STRING(str);
                    269:                                                *vtable->get_table()+=row;
1.12      moko      270:                                                row_value=(Value*)row;
1.11      moko      271:                                                break;
                    272:                                        }
                    273:                                }
                    274:                        } else {
                    275:                                const String& cell=STRING(str);
                    276:                                switch(value_type) {
                    277:                                        case C_HASH: {
1.12      moko      278:                                                row_value->get_hash()->put(*columns->get(column_index), new VString(cell));
1.11      moko      279:                                                break;
                    280:                                        }
                    281:                                        case C_STRING: {
                    282:                                                VString* row_string=(VString*)row_value;
                    283:                                                row_string->set_string(cell);
                    284:                                                break;
                    285:                                        }
                    286:                                        case C_TABLE: {
                    287:                                                ArrayString* row=(ArrayString*)row_value;
                    288:                                                *row+=&cell;
                    289:                                                break;
                    290:                                        }
                    291:                                }
                    292:                        }
                    293: 
                    294:                        if(duplicate & !distinct) {
                    295:                                error=SQL_Error("duplicate key");
                    296:                                return true;
                    297:                        }
                    298: 
                    299:                        column_index++;
                    300:                        return false;
                    301:                } catch(const Exception& e) {
                    302:                        error=SQL_Error(e.type(), e.comment());
                    303:                        return true;
                    304:                } catch(...) {
                    305:                        error=SQL_Error("exception occurred in Hash_sql_event_handlers::add_row_cell");
                    306:                        return true;
                    307:                }
                    308:        }
                    309: };
                    310: 
                    311: class Array_sql_event_handlers: public SQL_Driver_query_event_handlers {
                    312:        ArrayValue& result;
                    313:        Value* row_value;
                    314:        int column_index;
1.12      moko      315:        ArrayString* columns;
1.11      moko      316:        Table2hash_value_type value_type;
                    317:        int columns_count;
                    318: public:
                    319:        Table* empty;
                    320: public:
                    321:        Array_sql_event_handlers(ArrayValue& aresult, Table2hash_value_type avalue_type):
                    322:                result(aresult),
                    323:                row_value(0),
                    324:                column_index(0),
1.12      moko      325:                columns(new ArrayString),
1.11      moko      326:                value_type(avalue_type),
                    327:                empty(0) {
                    328:        }
                    329: 
                    330:        bool add_column(SQL_Error& error, const char* str, size_t ) {
                    331:                try {
1.12      moko      332:                        if(columns_count){
                    333:                                // another query in multi_statements mode
                    334:                                columns=new ArrayString;
                    335:                                columns_count=0;
                    336:                        }
                    337:                        *columns+=&STRING(str);
1.11      moko      338:                        return false;
                    339:                } catch(...) {
                    340:                        error=SQL_Error("exception occurred in Hash_sql_event_handlers::add_column");
                    341:                        return true;
                    342:                }
                    343:        }
                    344: 
                    345:        bool before_rows(SQL_Error& error) {
1.12      moko      346:                columns_count=columns->count();
                    347:                if(columns_count<1) {
1.11      moko      348:                        error=SQL_Error("no columns");
                    349:                        return true;
                    350:                }
                    351:                switch(value_type){
                    352:                        case C_STRING: {
1.12      moko      353:                                if(columns_count>1){
                    354:                                        error=SQL_Error("only one column allowed for $.type[string]");
1.11      moko      355:                                        return true;
                    356:                                }
                    357:                                break;
                    358:                        }
                    359:                        case C_TABLE: {
                    360:                                // create empty table which we'll copy later
1.12      moko      361:                                empty=new Table(columns);
1.11      moko      362:                                break;
                    363:                        }
                    364:                }
                    365:                return false;
                    366:        }
                    367: 
                    368:        bool add_row(SQL_Error& /*error*/) {
                    369:                column_index=0;
                    370:                return false;
                    371:        }
                    372: 
                    373:        bool add_row_cell(SQL_Error& error, const char *str, size_t ) {
                    374:                try {
1.12      moko      375:                        if(column_index==columns_count){
                    376:                                // should never happen, buggy driver case
                    377:                                error=SQL_Error("columns index exceed the columns count");
                    378:                                return true;
                    379:                        }
                    380: 
1.11      moko      381:                        if(column_index==0) {
                    382:                                switch(value_type){
                    383:                                        case C_HASH: {
                    384:                                                VHash* row_vhash=new VHash;
                    385:                                                row_value=row_vhash;
                    386:                                                result+=row_vhash;
                    387:                                                break;
                    388:                                        }
                    389:                                        case C_STRING: {
                    390:                                                VString* row_vstring=new VString();
                    391:                                                row_value=row_vstring;
                    392:                                                result+=row_vstring;
                    393:                                                break;
                    394:                                        }
                    395:                                        case C_TABLE: {
                    396:                                                // creating table of same structure as source
                    397:                                                Table::Action_options table_options(0, 0);
                    398:                                                VTable* vtable=new VTable(new Table(*empty, table_options/*no rows, just structure*/));
                    399:                                                ArrayString* row=new ArrayString(columns_count);
                    400:                                                *vtable->get_table()+=row;
1.12      moko      401:                                                row_value=(Value*)row;
                    402:                                                result+=vtable;
1.11      moko      403:                                                break;
                    404:                                        }
                    405:                                }
                    406:                        }
                    407: 
                    408:                        const String& cell=STRING(str);
                    409:                        switch(value_type) {
                    410:                                case C_HASH: {
1.12      moko      411:                                        row_value->get_hash()->put(*columns->get(column_index), new VString(cell));
1.11      moko      412:                                        break;
                    413:                                }
                    414:                                case C_STRING: {
                    415:                                        VString* row_string=(VString*)row_value;
                    416:                                        row_string->set_string(cell);
                    417:                                        break;
                    418:                                }
                    419:                                case C_TABLE: {
                    420:                                        ArrayString* row=(ArrayString*)row_value;
                    421:                                        *row+=&cell;
                    422:                                        break;
                    423:                                }
                    424:                        }
                    425: 
                    426:                        column_index++;
                    427:                        return false;
                    428:                } catch(const Exception& e) {
                    429:                        error=SQL_Error(e.type(), e.comment());
                    430:                        return true;
                    431:                } catch(...) {
                    432:                        error=SQL_Error("exception occurred in Hash_sql_event_handlers::add_row_cell");
                    433:                        return true;
                    434:                }
                    435:        }
                    436: };
                    437: 
                    438: #endif
                    439: 
                    440: extern Table2hash_value_type get_value_type(Value& vvalue_type);
                    441: extern int marshal_binds(HashStringValue& hash, SQL_Driver::Placeholder*& placeholders);
                    442: extern void unmarshal_bind_updates(HashStringValue& hash, int placeholder_count, SQL_Driver::Placeholder* placeholders);
                    443: 
                    444: static void _sql(Request& r, MethodParams& params) {
                    445:        Value& statement=params.as_junction(0, "statement must be code");
                    446: 
                    447:        HashStringValue* bind=0;
                    448:        ulong limit=SQL_NO_LIMIT;
                    449:        ulong offset=0;
                    450:        bool distinct=false;
                    451:        bool sparse=false;
                    452:        Table2hash_value_type value_type=C_HASH;
                    453:        if(params.count()>1)
                    454:                if(HashStringValue* options=params.as_hash(1, "sql options")) {
                    455:                        int valid_options=0;
1.13      moko      456:                        bool distinct_specified=false;
1.11      moko      457:                        for(HashStringValue::Iterator i(*options); i; i.next() ){
                    458:                                String::Body key=i.key();
                    459:                                Value* value=i.value();
                    460:                                if(key == sql_bind_name) {
                    461:                                        bind=value->get_hash();
                    462:                                        valid_options++;
                    463:                                } else if(key == sql_limit_name) {
                    464:                                        limit=(ulong)r.process(*value).as_double();
                    465:                                        valid_options++;
                    466:                                } else if(key == sql_offset_name) {
                    467:                                        offset=(ulong)r.process(*value).as_double();
                    468:                                        valid_options++;
                    469:                                } else if (key == sql_distinct_name) {
                    470:                                        distinct=r.process(*value).as_bool();
1.13      moko      471:                                        distinct_specified=true;
1.11      moko      472:                                        valid_options++;
                    473:                                } else if (key == sql_value_type_name) {
1.12      moko      474:                                        value_type=get_value_type(r.process(*value));
1.11      moko      475:                                        valid_options++;
                    476:                                } else if (key == "sparse") {
1.12      moko      477:                                        sparse=r.process(*value).as_bool();
1.11      moko      478:                                        valid_options++;
                    479:                                }
                    480:                        }
                    481:                        if(valid_options!=options->count())
                    482:                                throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
1.13      moko      483:                        if(distinct_specified && !sparse)
                    484:                                throw Exception(PARSER_RUNTIME, 0, "'distinct' option can only be used when $.sparse(true) is specified");
1.11      moko      485:                }
                    486: 
                    487:        SQL_Driver::Placeholder* placeholders=0;
                    488:        uint placeholders_count=0;
                    489:        if(bind)
                    490:                placeholders_count=marshal_binds(*bind, placeholders);
                    491: 
                    492:        const String& statement_string=r.process_to_string(statement);
                    493:        const char* statement_cstr=statement_string.untaint_cstr(String::L_SQL, r.connection());
                    494: 
                    495:        VArray& self=GET_SELF(r, VArray);
1.17      moko      496:        ArrayValue& array=self.array();
1.13      moko      497: 
1.23      moko      498:        if(array.count()){
                    499:                array.clear(); array.invalidate(); // just in case if called as method
                    500:        }
1.11      moko      501: 
                    502:        if(sparse){
1.17      moko      503:                SparseArray_sql_event_handlers handlers(distinct, array, value_type);
1.11      moko      504:                r.connection()->query(statement_cstr, placeholders_count, placeholders, offset, limit, handlers, statement_string);
                    505:        } else {
1.17      moko      506:                Array_sql_event_handlers handlers(array, value_type);
1.11      moko      507:                r.connection()->query(statement_cstr, placeholders_count, placeholders, offset, limit, handlers, statement_string);
                    508:        }
                    509: 
1.17      moko      510:        array.confirm_all_used();
                    511: 
1.11      moko      512:        if(bind)
                    513:                unmarshal_bind_updates(*bind, placeholders_count, placeholders);
                    514: }
                    515: 
1.1       moko      516: 
1.10      moko      517: static void mid(Request& r, size_t offset=0, size_t limit=ARRAY_OPTION_LIMIT_ALL) {
1.9       moko      518:        ArrayValue& array=GET_SELF(r, VArray).array();
1.10      moko      519:        if(limit>0){
1.9       moko      520:                VArray *result=new VArray;
                    521:                ArrayValue& result_array=result->array();
                    522:                for(ArrayValue::Iterator i(array); i; i.next()){
                    523:                        if(i.value()){
1.10      moko      524:                                if(offset > 0){
                    525:                                        offset--;
1.9       moko      526:                                        continue;
                    527:                                }
1.10      moko      528:                                if(limit-- == 0)
1.9       moko      529:                                        break;
                    530:                                result_array+=i.value();
                    531:                        }
                    532:                }
1.17      moko      533:                result_array.confirm_all_used();
1.9       moko      534:                r.write(*result);
                    535:        } else {
                    536:                r.write(*new VArray);
                    537:        }
                    538: }
1.1       moko      539: 
1.9       moko      540: static void _left(Request& r, MethodParams& params) {
                    541:        int sn=params.as_int(0, "n must be int", r);
1.10      moko      542:        mid(r, 0, sn < 0 ? 0 : sn);
1.9       moko      543: }
                    544: 
                    545: static void _right(Request& r, MethodParams& params) {
                    546:        int sn=params.as_int(0, "n must be int", r);
                    547: 
                    548:        if(sn>0){
                    549:                size_t used=GET_SELF(r, VArray).array().used();
1.20      moko      550:                if((size_t)sn<used){
1.10      moko      551:                        mid(r, used-sn, sn);
1.9       moko      552:                } else {
1.10      moko      553:                        mid(r);
1.9       moko      554:                }
                    555:        } else {
1.10      moko      556:                mid(r, 0, 0);
1.9       moko      557:        }
                    558: }
                    559: 
                    560: static void _mid(Request& r, MethodParams& params) {
1.19      moko      561:        int begin=params.as_int(0, "p must be int", r);
1.9       moko      562: 
                    563:        if(begin<0)
                    564:                throw Exception(PARSER_RUNTIME, 0,  "p(%d) must be >=0", begin);
                    565: 
                    566:        if(params.count()>1) {
                    567:                int n=params.as_int(1, "n must be int", r);
                    568:                if(n<0)
                    569:                        throw Exception(PARSER_RUNTIME, 0, "n(%d) must be >=0", n);
1.10      moko      570:                mid(r, begin, n);
1.9       moko      571:        } else {
1.10      moko      572:                mid(r, begin);
1.9       moko      573:        }
                    574: }
1.1       moko      575: 
                    576: static void _keys(Request& r, MethodParams& params) {
                    577:        const String* keys_column_name;
                    578:        if(params.count()>0)
                    579:                keys_column_name=&params.as_string(0, COLUMN_NAME_MUST_BE_STRING);
                    580:        else 
                    581:                keys_column_name=new String("key");
                    582: 
                    583:        Table::columns_type columns(new ArrayString(1));
                    584:        *columns+=keys_column_name;
                    585:        Table* table=new Table(columns);
                    586: 
                    587:        ArrayValue& array=GET_SELF(r, VArray).array();
                    588:        for(ArrayValue::Iterator i(array); i; i.next()){
                    589:                if(i.value()){
                    590:                        Table::element_type row(new ArrayString(1));
                    591:                        *row+=new String(i.key(), String::L_TAINTED);
                    592:                        *table+=row;
                    593:                }
                    594:        }
                    595: 
                    596:        r.write(*new VTable(table));
                    597: }
                    598: 
1.5       moko      599: static void _count(Request& r, MethodParams& params) {
                    600:        ArrayValue& array=GET_SELF(r, VArray).array();
                    601:        if(params.count()>0){
                    602:                const String& what=params.as_string(0, PARAMETER_MUST_BE_STRING);
                    603:                if(!what.is_empty()){
                    604:                        if(what != "all")
                    605:                                throw Exception(PARSER_RUNTIME, &what, "param must be empty or 'all'");
                    606:                        return r.write(*new VInt(array.count()));
                    607:                }
                    608:        }
                    609:        r.write(*new VInt(array.used()));
1.1       moko      610: }
                    611: 
1.27      moko      612: static void _create_or_append_or_push(Request& r, MethodParams& params) {
1.23      moko      613:        ArrayValue& array=GET_SELF(r, VArray).array();
1.2       moko      614: 
                    615:        int count=params.count();
                    616: 
1.26      moko      617:        if(array.count()){
1.28      moko      618:                for(int i=0; i<count; i++){
1.26      moko      619:                        array+=&r.process(params[i]);
1.28      moko      620:                        array.change_used(+1); // after each element, since an exception can occur
                    621:                }
1.26      moko      622:        } else {
                    623:                for(int i=0; i<count; i++)
                    624:                        array+=&r.process(params[i]);
                    625:                array.confirm_all_used();
1.2       moko      626:        }
                    627: }
                    628: 
                    629: static void _insert(Request& r, MethodParams& params) {
1.23      moko      630:        ArrayValue& array=GET_SELF(r, VArray).array();
1.2       moko      631: 
                    632:        int count=params.count();
1.7       moko      633:        size_t index=VArray::index(params.as_int(0, PARAM_INDEX, r));
1.2       moko      634: 
                    635:        for(int i=1; i<count; i++){
1.8       moko      636:                array.insert(index++, &r.process(params[i]));
1.28      moko      637:                array.change_used(+1); // after each element, since an exception can occur
1.2       moko      638:        }
                    639: }
                    640: 
1.1       moko      641: static void _delete(Request& r, MethodParams& params) {
1.23      moko      642:        ArrayValue& array=GET_SELF(r, VArray).array();
1.29      moko      643:        if(params.count()>0) {
                    644:                if(params[0].is_string()) {
                    645:                        array.clear(VArray::index(*params[0].get_string()));
                    646:                } else {
                    647:                        array.clear(VArray::index(params.as_int(0, PARAM_INDEX, r)));
                    648:                }
                    649:        } else
1.23      moko      650:                array.clear();
                    651:        array.invalidate();
1.7       moko      652: }
                    653: 
                    654: static void _remove(Request& r, MethodParams& params) {
1.23      moko      655:        ArrayValue& array=GET_SELF(r, VArray).array();
                    656:        array.remove(VArray::index(params.as_int(0, PARAM_INDEX, r)));
                    657:        array.invalidate();
1.1       moko      658: }
                    659: 
1.37    ! moko      660: static void _pop(Request& r, MethodParams&) {
1.27      moko      661:        ArrayValue& array=GET_SELF(r, VArray).array();
                    662:        Value *result=array.pop();
                    663:        if(result){
                    664:                r.write(*result);
1.28      moko      665:                array.change_used(-1);
1.27      moko      666:        } else {
                    667:                r.write(*VVoid::get());
                    668:        }
                    669: }
                    670: 
1.1       moko      671: static void _contains(Request& r, MethodParams& params) {
                    672:        VArray& self=GET_SELF(r, VArray);
1.31      moko      673: 
                    674:        bool result=self.contains(params[0].is_string() ? VArray::index(*params[0].get_string()) : VArray::index(params.as_int(0, PARAM_INDEX, r)));
1.1       moko      675:        r.write(VBool::get(result));
                    676: }
                    677: 
1.5       moko      678: static void _for(Request& r, MethodParams& params) {
                    679:        InCycle temp(r);
                    680: 
1.7       moko      681:        const String* key_var_name=&params.as_string(0, "key-var name must be string");
                    682:        const String* value_var_name=&params.as_string(1, "value-var name must be string");
                    683:        Value* body_code=&params.as_junction(2, "body must be code");
                    684:        Value* delim_maybe_code=params.count()>3 ? &params[3] : 0;
1.5       moko      685:        Value& caller=*r.get_method_frame()->caller();
                    686: 
1.7       moko      687:        if(key_var_name->is_empty()) key_var_name=0;
1.5       moko      688:        if(value_var_name->is_empty()) value_var_name=0;
                    689: 
                    690:        ArrayValue& array=GET_SELF(r, VArray).array();
                    691: 
                    692:        if(delim_maybe_code){ // delimiter set
                    693:                bool need_delim=false;
1.36      moko      694:                for(ArrayValue::RobustIterator i(array); i; i.next()){
1.7       moko      695:                        if(key_var_name){
                    696:                                VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
                    697:                                r.put_element(caller, *key_var_name, vkey);
                    698:                        }
                    699: 
1.5       moko      700:                        if(value_var_name)
                    701:                                r.put_element(caller, *value_var_name, i.value() ? i.value() : VVoid::get());
                    702: 
                    703:                        Value& sv_processed=r.process(*body_code);
                    704:                        TempSkip4Delimiter skip(r);
                    705: 
                    706:                        const String* s_processed=sv_processed.get_string();
                    707:                        if(s_processed && !s_processed->is_empty()) { // we have body
                    708:                                if(need_delim) // need delim & iteration produced string?
                    709:                                        r.write(r.process(*delim_maybe_code));
                    710:                                else
                    711:                                        need_delim=true;
                    712:                        }
                    713: 
                    714:                        r.write(sv_processed);
                    715: 
                    716:                        if(skip.check_break())
                    717:                                break;
                    718:                }
                    719:        } else {
1.36      moko      720:                for(ArrayValue::RobustIterator i(array); i; i.next()){
1.7       moko      721:                        if(key_var_name){
                    722:                                VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
                    723:                                r.put_element(caller, *key_var_name, vkey);
                    724:                        }
                    725: 
1.5       moko      726:                        if(value_var_name)
                    727:                                r.put_element(caller, *value_var_name, i.value() ? i.value() : VVoid::get());
                    728: 
                    729:                        r.process_write(*body_code);
                    730: 
                    731:                        if(r.check_skip_break())
                    732:                                break;
                    733:                }
                    734:        }
                    735: }
                    736: 
1.1       moko      737: static void _foreach(Request& r, MethodParams& params) {
                    738:        InCycle temp(r);
                    739: 
                    740:        const String* key_var_name=&params.as_string(0, "key-var name must be string");
                    741:        const String* value_var_name=&params.as_string(1, "value-var name must be string");
                    742:        Value* body_code=&params.as_junction(2, "body must be code");
1.7       moko      743:        Value* delim_maybe_code=params.count()>3 ? &params[3] : 0;
1.1       moko      744:        Value& caller=*r.get_method_frame()->caller();
                    745: 
                    746:        if(key_var_name->is_empty()) key_var_name=0;
                    747:        if(value_var_name->is_empty()) value_var_name=0;
                    748: 
                    749:        ArrayValue& array=GET_SELF(r, VArray).array();
                    750: 
                    751:        if(delim_maybe_code){ // delimiter set
                    752:                bool need_delim=false;
1.36      moko      753:                for(ArrayValue::RobustIterator i(array); i; i.next()){
1.1       moko      754:                        if(i.value()){
                    755:                                if(key_var_name){
                    756:                                        VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
                    757:                                        r.put_element(caller, *key_var_name, vkey);
                    758:                                }
                    759: 
                    760:                                if(value_var_name)
                    761:                                        r.put_element(caller, *value_var_name, i.value());
                    762: 
                    763:                                Value& sv_processed=r.process(*body_code);
                    764:                                TempSkip4Delimiter skip(r);
                    765: 
                    766:                                const String* s_processed=sv_processed.get_string();
                    767:                                if(s_processed && !s_processed->is_empty()) { // we have body
                    768:                                        if(need_delim) // need delim & iteration produced string?
                    769:                                                r.write(r.process(*delim_maybe_code));
                    770:                                        else
                    771:                                                need_delim=true;
                    772:                                }
                    773: 
                    774:                                r.write(sv_processed);
                    775: 
                    776:                                if(skip.check_break())
                    777:                                        break;
                    778:                        }
                    779:                }
                    780:        } else {
1.36      moko      781:                for(ArrayValue::RobustIterator i(array); i; i.next()){
1.1       moko      782:                        if(i.value()){
                    783:                                if(key_var_name){
                    784:                                        VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
                    785:                                        r.put_element(caller, *key_var_name, vkey);
                    786:                                }
                    787: 
                    788:                                if(value_var_name)
                    789:                                        r.put_element(caller, *value_var_name, i.value());
                    790: 
                    791:                                r.process_write(*body_code);
                    792: 
                    793:                                if(r.check_skip_break())
                    794:                                        break;
                    795:                        }
                    796:                }
                    797:        }
                    798: }
                    799: 
                    800: #ifndef DOXYGEN
                    801: struct Array_seq_item : public PA_Allocated {
                    802:        Value *array_data;
                    803:        union {
                    804:                const char *c_str;
                    805:                double d;
                    806:        } value;
                    807: };
                    808: #endif
                    809: 
                    810: static int sort_cmp_string(const void *a, const void *b) {
                    811:        return strcmp(
                    812:                static_cast<const Array_seq_item *>(a)->value.c_str,
                    813:                static_cast<const Array_seq_item *>(b)->value.c_str
                    814:        );
                    815: }
                    816: static int sort_cmp_double(const void *a, const void *b) {
                    817:        double va=static_cast<const Array_seq_item *>(a)->value.d;
                    818:        double vb=static_cast<const Array_seq_item *>(b)->value.d;
                    819:        if(va<vb)
                    820:                return -1;
                    821:        else if(va>vb)
                    822:                return +1;
                    823:        else 
                    824:                return 0;
                    825: }
                    826: 
                    827: static void _sort(Request& r, MethodParams& params){
                    828:        const String& key_var_name=params.as_string(0, "key-var name must be string");
                    829:        const String& value_var_name=params.as_string(1, "value-var name must be string");
                    830:        Value& key_maker=params.as_junction(2, "key-maker must be code");
                    831:        bool reverse=params.count()>3 && params.as_no_junction(3, "order must not be code").as_string()=="desc"; // default=asc
                    832: 
                    833:        const String* key_var=key_var_name.is_empty()? 0 : &key_var_name;
                    834:        const String* value_var=value_var_name.is_empty()? 0 : &value_var_name;
                    835:        VMethodFrame* context=r.get_method_frame()->caller();
                    836: 
                    837:        VArray& self=GET_SELF(r, VArray);
                    838:        ArrayValue& array=self.array();
1.4       moko      839:        int count=array.used(); // not array.count()
1.1       moko      840: 
                    841:        Array_seq_item* seq=new Array_seq_item[count];
                    842:        int pos=0;
                    843:        bool key_values_are_strings=true;
                    844: 
1.36      moko      845:        for(ArrayValue::RobustIterator i(array); i; i.next() ){
1.1       moko      846:                if(i.value()){
                    847:                        if(key_var)
                    848:                                r.put_element(*context, *key_var, new VString(*new String(i.key(), String::L_TAINTED)));
                    849:                        if(value_var)
                    850:                                r.put_element(*context, *value_var, i.value());
                    851: 
                    852:                        Value& value=r.process(key_maker);
                    853:                        if(pos==0) // determining key values type by first one
                    854:                                key_values_are_strings=value.is_string();
                    855: 
                    856:                        seq[pos].array_data=i.value();
                    857:                        if(key_values_are_strings)
                    858:                                seq[pos++].value.c_str=value.as_string().cstr();
                    859:                        else
                    860:                                seq[pos++].value.d=value.as_expr_result().as_double();
                    861:                }
                    862:        }
                    863: 
                    864:        // @todo: handle this elsewhere
                    865:        if(r.charsets.source().NAME()=="KOI8-R" && key_values_are_strings)
                    866:                for(pos=0; pos<count; pos++)
                    867:                        if(*seq[pos].value.c_str)
                    868:                                seq[pos].value.c_str=Charset::transcode(seq[pos].value.c_str, r.charsets.source(), pa_UTF8_charset).cstr();
                    869: 
                    870:        // sort keys
                    871:        qsort(seq, count, sizeof(Array_seq_item), key_values_are_strings ? sort_cmp_string : sort_cmp_double);
                    872: 
                    873:        // reorder array as required in 'seq'
                    874:        array.clear();
                    875:        if(reverse)
                    876:                for(pos=count-1; pos>=0; pos--)
                    877:                        array+=seq[pos].array_data;
                    878:        else
                    879:                for(pos=0; pos<count; pos++)
                    880:                        array+=seq[pos].array_data;
                    881: 
1.26      moko      882:        array.confirm_all_used();
1.1       moko      883:        delete[] seq;
                    884: }
                    885: 
1.5       moko      886: enum AtResultType {
                    887:        AtResultTypeValue = 0,
                    888:        AtResultTypeKey = 1,
                    889:        AtResultTypeHash = 2
                    890: };
                    891: 
1.16      moko      892: static Value& SingleElementHash(String::Body akey, Value* avalue) {
1.5       moko      893:        Value& result=*new VHash;
                    894:        result.put_element(*new String(akey, String::L_TAINTED), avalue);
                    895:        return result;
                    896: }
                    897: 
1.1       moko      898: static void _at(Request& r, MethodParams& params) {
1.26      moko      899:        ArrayValue& array=GET_SELF(r, VArray).array();
1.5       moko      900:        size_t count=array.used(); // not array.count()
1.1       moko      901: 
                    902:        AtResultType result_type=AtResultTypeValue;
                    903:        if(params.count() > 1) {
                    904:                const String& stype=params.as_string(1, "type must be string");
                    905:                if(stype == "key")
                    906:                        result_type=AtResultTypeKey;
                    907:                else if(stype == "hash")
                    908:                        result_type=AtResultTypeHash;
                    909:                else if(stype != "value")
                    910:                        throw Exception(PARSER_RUNTIME, &stype, "type must be 'key', 'value' or 'hash'");
                    911:        }
                    912: 
1.30      moko      913:        int pos=params.as_index(0, count, r);
1.1       moko      914: 
                    915:        if(count && pos >= 0 && (size_t)pos < count){
1.21      moko      916:                if(count == array.count()){
1.31      moko      917: l1:                    switch(result_type) {
1.21      moko      918:                                case AtResultTypeKey:
                    919:                                        r.write(*new VString(*new String(pa_uitoa(pos), String::L_TAINTED)));
                    920:                                        break;
                    921:                                case AtResultTypeValue:
1.25      moko      922:                                        r.write(*array.get(pos));
1.21      moko      923:                                        break;
                    924:                                case AtResultTypeHash:
1.25      moko      925:                                        r.write(SingleElementHash(pa_uitoa(pos), array.get(pos)));
1.21      moko      926:                                        break;
                    927:                        }
1.31      moko      928:                } else if((size_t)pos == count-1){
                    929:                        pos=array.count()-1;
                    930:                        goto l1;
1.21      moko      931:                } else {
                    932:                        for(ArrayValue::Iterator i(array); i; i.next() ){
                    933:                                if(i.value() && !(pos--)){
                    934:                                        switch(result_type) {
                    935:                                                case AtResultTypeKey:
1.1       moko      936:                                                        r.write(*new VString(*new String(i.key(), String::L_TAINTED)));
                    937:                                                        break;
1.21      moko      938:                                                case AtResultTypeValue:
1.1       moko      939:                                                        r.write(*i.value());
                    940:                                                        break;
1.21      moko      941:                                                case AtResultTypeHash:
1.1       moko      942:                                                        r.write(SingleElementHash(i.key(), i.value()));
                    943:                                                        break;
1.21      moko      944:                                        }
1.1       moko      945:                                        break;
                    946:                                }
1.21      moko      947:                        }
1.1       moko      948:                }
                    949:        }
                    950: }
                    951: 
1.31      moko      952: static void _set(Request& r, MethodParams& params) {
                    953:        ArrayValue& array=GET_SELF(r, VArray).array();
                    954:        size_t count=array.used(); // not array.count()
                    955: 
                    956:        int pos=params.as_index(0, count, r);
                    957: 
                    958:        if(count && pos >= 0 && (size_t)pos < count){
                    959:                if(count == array.count()){
                    960:                        array.put(pos, &r.process(params[1]));
                    961:                        return;
                    962:                } else if((size_t)pos == count-1){
                    963:                        array.put(array.count()-1, &r.process(params[1]));
                    964:                        return;
                    965:                } else {
                    966:                        for(ArrayValue::Iterator i(array); i; i.next() ){
                    967:                                if(i.value() && !(pos--)){
1.32      moko      968:                                        array.put(i.index(), &r.process(params[1]));
1.31      moko      969:                                        return;
                    970:                                }
                    971:                        }
                    972:                }
                    973:        }
                    974: 
                    975:        if(count)
                    976:                throw Exception(PARSER_RUNTIME, 0, "index '%d' is out of range 0..%d", pos, count-1);
                    977:        throw Exception(PARSER_RUNTIME, 0, "index '%d' is out of range: array is empty", pos);
                    978: }
1.1       moko      979: 
                    980: extern String table_reverse_name;
                    981: 
                    982: static void _select(Request& r, MethodParams& params) {
                    983:        InCycle temp(r);
                    984:        const String* key_var_name=&params.as_string(0, "key-var name must be string");
                    985:        const String* value_var_name=&params.as_string(1, "value-var name must be string");
                    986:        Value& vcondition=params.as_expression(2, "condition must be number, bool or expression");
                    987: 
                    988:        if(key_var_name->is_empty()) key_var_name=0;
                    989:        if(value_var_name->is_empty()) value_var_name=0;
                    990: 
                    991:        ArrayValue& source_array=GET_SELF(r, VArray).array();
                    992:        Value& caller=*r.get_method_frame()->caller();
                    993: 
                    994:        int limit=source_array.count();
                    995:        bool reverse=false;
                    996: 
                    997:        if(params.count()>3)
                    998:                if(HashStringValue* options=params.as_hash(3)) {
                    999:                        int valid_options=0;
                   1000:                        if(Value* vlimit=options->get(sql_limit_name)) {
                   1001:                                valid_options++;
                   1002:                                limit=r.process(*vlimit).as_int();
                   1003:                        }
                   1004:                        if(Value* vreverse=options->get(table_reverse_name)) {
                   1005:                                valid_options++;
                   1006:                                reverse=r.process(*vreverse).as_bool();
                   1007:                        }
                   1008:                        if(valid_options!=options->count())
                   1009:                                throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
                   1010:                }
                   1011: 
                   1012:        VArray *result=new VArray;
                   1013:        ArrayValue& result_array=result->array();
                   1014: 
                   1015:        if(limit>0){
                   1016:                if(reverse){
                   1017:                        for(ArrayValue::ReverseIterator i(source_array); i; ){
1.5       moko     1018:                                if(Value *value=i.prev()){ // here for correct i.key()
                   1019:                                        if(key_var_name)
                   1020:                                                r.put_element(caller, *key_var_name, new VString(*new String(i.key(), String::L_TAINTED)));
                   1021:                                        if(value_var_name)
                   1022:                                                r.put_element(caller, *value_var_name, value);
1.1       moko     1023: 
1.5       moko     1024:                                        bool condition=r.process(vcondition).as_bool();
1.1       moko     1025: 
1.5       moko     1026:                                        if(r.check_skip_break())
                   1027:                                                break;
1.1       moko     1028: 
1.5       moko     1029:                                        if(condition){
                   1030:                                                result_array+=value;
                   1031:                                                if(!--limit)
                   1032:                                                        break;
                   1033:                                        }
1.1       moko     1034:                                }
                   1035:                        }
                   1036:                } else {
1.36      moko     1037:                        for(ArrayValue::RobustIterator i(source_array); i; i.next() ){
1.5       moko     1038:                                if(Value *value=i.value()){
1.1       moko     1039:                                        if(key_var_name)
                   1040:                                                r.put_element(caller, *key_var_name, new VString(*new String(i.key(), String::L_TAINTED)));
                   1041:                                        if(value_var_name)
                   1042:                                                r.put_element(caller, *value_var_name, value);
                   1043: 
                   1044:                                        bool condition=r.process(vcondition).as_bool();
                   1045: 
                   1046:                                        if(r.check_skip_break())
                   1047:                                                break;
                   1048: 
                   1049:                                        if(condition){
                   1050:                                                result_array+=value;
                   1051:                                                if(!--limit)
                   1052:                                                        break;
                   1053:                                        }
                   1054:                                }
                   1055:                        }
                   1056:                }
                   1057:        }
                   1058: 
1.17      moko     1059:        result_array.confirm_all_used();
1.1       moko     1060:        r.write(*result);
                   1061: }
                   1062: 
1.19      moko     1063: static void _reverse(Request& r, MethodParams&) {
1.5       moko     1064:        ArrayValue& source_array=GET_SELF(r, VArray).array();
1.1       moko     1065: 
1.5       moko     1066:        VArray& result=*new VArray(source_array.count());
1.1       moko     1067:        ArrayValue& result_array=result.array();
                   1068: 
1.4       moko     1069:        for(ArrayValue::ReverseIterator i(source_array); i; ){
                   1070:                result_array+=i.prev();
1.1       moko     1071:        }
                   1072: 
                   1073:        r.write(result);
                   1074: }
                   1075: 
1.24      moko     1076: static void _compact(Request& r, MethodParams& params) {
                   1077:        bool compact_undef=false;
                   1078:        if(params.count()>0){
                   1079:                const String& what=params.as_string(0, PARAMETER_MUST_BE_STRING);
                   1080:                if(!what.is_empty()){
                   1081:                        if(what != "undef")
                   1082:                                throw Exception(PARSER_RUNTIME, &what, "param must be empty or 'undef'");
                   1083:                        compact_undef=true;
                   1084:                }
                   1085:        }
1.26      moko     1086:        ArrayValue& array=GET_SELF(r, VArray).array();
                   1087:        array.compact(compact_undef);
                   1088:        array.confirm_all_used();
1.22      moko     1089: }
                   1090: 
1.1       moko     1091: 
                   1092: // constructor
                   1093: 
                   1094: MArray::MArray(): Methoded(VARRAY_TYPE) {
                   1095: 
1.18      moko     1096:        // ^array::copy[[copy_from]]
1.25      moko     1097:        add_native_method("copy", Method::CT_DYNAMIC, _copy_or_add, 0, 1);
1.1       moko     1098:        // ^array.add[add_from]
1.25      moko     1099:        add_native_method("add", Method::CT_DYNAMIC, _copy_or_add, 1, 1);
1.6       moko     1100:        // ^array.join[join_from[;options]]
                   1101:        add_native_method("join", Method::CT_DYNAMIC, _join, 1, 2);
1.1       moko     1102: 
1.27      moko     1103:        // ^array::create[value[;value...]]
                   1104:        add_native_method("create", Method::CT_DYNAMIC, _create_or_append_or_push, 0, 10000);
                   1105:        // ^array.append[value[;value...]]
                   1106:        add_native_method("append", Method::CT_DYNAMIC, _create_or_append_or_push, 1, 10000);
                   1107:        // ^array.push[value[;value...]]
                   1108:        add_native_method("push", Method::CT_DYNAMIC, _create_or_append_or_push, 1, 10000);
1.31      moko     1109:        // ^array.insert(index)[value[;value...]]
1.18      moko     1110:        add_native_method("insert", Method::CT_DYNAMIC, _insert, 2, 10000);
                   1111: 
1.9       moko     1112:        // ^array.left(n)
                   1113:        add_native_method("left", Method::CT_DYNAMIC, _left, 1, 1);
                   1114:        // ^array.right(n)
                   1115:        add_native_method("right", Method::CT_DYNAMIC, _right, 1, 1);
                   1116:        // ^array.mid(p)
                   1117:        // ^array.mid(p;n)
                   1118:        add_native_method("mid", Method::CT_DYNAMIC, _mid, 1, 2);
1.1       moko     1119: 
1.29      moko     1120:        // ^array.delete(index) or ^array.delete[index]
1.1       moko     1121:        add_native_method("delete", Method::CT_DYNAMIC, _delete, 0, 1);
1.29      moko     1122:        // ^array.remove(index)
1.7       moko     1123:        add_native_method("remove", Method::CT_DYNAMIC, _remove, 1, 1);
1.27      moko     1124:        // ^array.pop[]
                   1125:        add_native_method("pop", Method::CT_DYNAMIC, _pop, 0, 0);
1.1       moko     1126: 
1.31      moko     1127:        // ^array.contains(index) ^array.contains[index]
1.1       moko     1128:        add_native_method("contains", Method::CT_DYNAMIC, _contains, 1, 1);
                   1129: 
                   1130:        // ^array::sql[query][options array]
                   1131:        add_native_method("sql", Method::CT_DYNAMIC, _sql, 1, 2);
                   1132: 
                   1133:        // ^array._keys[[column name]]
                   1134:        add_native_method("_keys", Method::CT_DYNAMIC, _keys, 0, 1);
                   1135: 
1.5       moko     1136:        // ^array._count[[all]]
                   1137:        add_native_method("_count", Method::CT_DYNAMIC, _count, 0, 1);
1.1       moko     1138: 
1.7       moko     1139:        // ^array.for[index;value]{code}[delim]
                   1140:        add_native_method("for", Method::CT_DYNAMIC, _for, 3, 3+1);
1.2       moko     1141:        // ^array.foreach[index;value]{code}[delim]
1.7       moko     1142:        add_native_method("foreach", Method::CT_DYNAMIC, _foreach, 3, 3+1);
1.1       moko     1143: 
1.2       moko     1144:        // ^array.sort[index;value]{string-key-maker}[[asc|desc]]
                   1145:        // ^array.sort[index;value](numeric-key-maker)[[asc|desc]]
1.1       moko     1146:        add_native_method("sort", Method::CT_DYNAMIC, _sort, 3, 4);
                   1147: 
1.2       moko     1148:        // ^array.select[index;value](bool-condition)[options hash]
1.1       moko     1149:        add_native_method("select", Method::CT_DYNAMIC, _select, 3, 4);
                   1150: 
                   1151:        // ^array.reverse[]
                   1152:        add_native_method("reverse", Method::CT_DYNAMIC, _reverse, 0, 0);
                   1153: 
1.24      moko     1154:        // ^array.compact[[undef]]
                   1155:        add_native_method("compact", Method::CT_DYNAMIC, _compact, 0, 1);
1.22      moko     1156: 
1.2       moko     1157:        // ^array._at[first|last[;'key'|'value'|'hash']]
1.31      moko     1158:        // ^array._at([-+]index)[['key'|'value'|'hash']]
1.1       moko     1159:        add_native_method("_at", Method::CT_DYNAMIC, _at, 1, 2);
                   1160: 
1.31      moko     1161:        // ^array.set[first|last;value]
                   1162:        // ^array.set([-+]index)[value]
                   1163:        add_native_method("set", Method::CT_DYNAMIC, _set, 2, 2);
                   1164: 
1.1       moko     1165: #ifdef FEATURE_GET_ELEMENT4CALL
                   1166:        // aliases without "_"
                   1167:        add_native_method("keys", Method::CT_DYNAMIC, _keys, 0, 1);
1.14      moko     1168:        add_native_method("count", Method::CT_DYNAMIC, _count, 0, 1);
1.1       moko     1169:        add_native_method("at", Method::CT_DYNAMIC, _at, 1, 2);
                   1170: #endif
                   1171: 
                   1172: }

E-mail: