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

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.4     ! moko       20: volatile const char * IDENT_ARRAY_C="$Id: array.C,v 1.3 2024/09/13 04:01:22 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: 
                     36: // methods
                     37: 
                     38: static void _create_or_add(Request& r, MethodParams& params) {
                     39:        if(params.count()) {
                     40:                Value& vsrc=params.as_no_junction(0, PARAM_MUST_BE_HASH);
                     41:                VArray& self=GET_SELF(r, VArray);
                     42:                ArrayValue& self_array=self.array();
                     43: 
1.3       moko       44:                if(VArray* src=dynamic_cast<VArray*>(&vsrc)) {
1.1       moko       45:                        ArrayValue& src_array =src->array();
                     46:                        if(&src_array==&self_array) // same: doing nothing
                     47:                                return;
                     48:                } else {
                     49:                        HashStringValue* src_hash=vsrc.get_hash();
                     50:                        if(src_hash)
                     51:                                for(HashStringValue::Iterator i(*src_hash); i; i.next())
                     52:                                        self_array+=i.value();
                     53:                }
1.4     ! moko       54:                self.invalidate();
1.1       moko       55:        }
                     56: }
                     57: 
                     58: static void _sql(Request& r, MethodParams& params) {}
                     59: 
                     60: static void _sub(Request& r, MethodParams& params) {}
                     61: 
                     62: static void _union(Request& r, MethodParams& params) {}
                     63: 
                     64: static void _intersection(Request& r, MethodParams& params) {}
                     65: 
                     66: static void _intersects(Request& r, MethodParams& params) {}
                     67: 
                     68: static void _keys(Request& r, MethodParams& params) {
                     69:        const String* keys_column_name;
                     70:        if(params.count()>0)
                     71:                keys_column_name=&params.as_string(0, COLUMN_NAME_MUST_BE_STRING);
                     72:        else 
                     73:                keys_column_name=new String("key");
                     74: 
                     75:        Table::columns_type columns(new ArrayString(1));
                     76:        *columns+=keys_column_name;
                     77:        Table* table=new Table(columns);
                     78: 
                     79:        ArrayValue& array=GET_SELF(r, VArray).array();
                     80:        for(ArrayValue::Iterator i(array); i; i.next()){
                     81:                if(i.value()){
                     82:                        Table::element_type row(new ArrayString(1));
                     83:                        *row+=new String(i.key(), String::L_TAINTED);
                     84:                        *table+=row;
                     85:                }
                     86:        }
                     87: 
                     88:        r.write(*new VTable(table));
                     89: }
                     90: 
                     91: static void _count(Request& r, MethodParams&) {
1.4     ! moko       92:        r.write(*new VInt(GET_SELF(r, VArray).array().used()));
1.1       moko       93: }
                     94: 
1.2       moko       95: static void _append(Request& r, MethodParams& params) {
                     96:        VArray& self=GET_SELF(r, VArray);
                     97:        ArrayValue& array=self.array();
                     98: 
                     99:        int count=params.count();
                    100: 
                    101:        for(int i=0; i<count; i++){
                    102:                array+=&r.process(params[i]);
                    103:        }
1.4     ! moko      104:        self.invalidate();
1.2       moko      105: }
                    106: 
                    107: static void _insert(Request& r, MethodParams& params) {
                    108:        VArray& self=GET_SELF(r, VArray);
                    109:        ArrayValue& array=self.array();
                    110: 
                    111:        int count=params.count();
                    112:        size_t index=VArray::index(params.as_int(0, "index must be integer", r));
                    113: 
                    114:        for(int i=1; i<count; i++){
                    115:                array.insert(index+i-1, &r.process(params[i]));
                    116:        }
1.4     ! moko      117:        self.invalidate();
1.2       moko      118: }
                    119: 
1.1       moko      120: static void _delete(Request& r, MethodParams& params) {
                    121:        if(params.count()>0)
                    122:                GET_SELF(r, VArray).clear(VArray::index(params.as_int(0, "index must be integer", r)));
                    123:        else
                    124:                GET_SELF(r, VArray).clear();
                    125: }
                    126: 
                    127: static void _contains(Request& r, MethodParams& params) {
                    128:        VArray& self=GET_SELF(r, VArray);
                    129:        bool result=self.contains(VArray::index(params.as_int(0, "index must be integer", r)));
                    130:        r.write(VBool::get(result));
                    131: }
                    132: 
                    133: static void _foreach(Request& r, MethodParams& params) {
                    134:        InCycle temp(r);
                    135: 
                    136:        const String* key_var_name=&params.as_string(0, "key-var name must be string");
                    137:        const String* value_var_name=&params.as_string(1, "value-var name must be string");
                    138:        Value* body_code=&params.as_junction(2, "body must be code");
                    139:        Value* delim_maybe_code=params.count()>3?&params[3]:0;
                    140:        Value& caller=*r.get_method_frame()->caller();
                    141: 
                    142:        if(key_var_name->is_empty()) key_var_name=0;
                    143:        if(value_var_name->is_empty()) value_var_name=0;
                    144: 
                    145:        ArrayValue& array=GET_SELF(r, VArray).array();
                    146: 
                    147:        if(delim_maybe_code){ // delimiter set
                    148:                bool need_delim=false;
                    149:                for(ArrayValue::Iterator i(array); i; i.next()){
                    150:                        if(i.value()){
                    151:                                if(key_var_name){
                    152:                                        VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
                    153:                                        r.put_element(caller, *key_var_name, vkey);
                    154:                                }
                    155: 
                    156:                                if(value_var_name)
                    157:                                        r.put_element(caller, *value_var_name, i.value());
                    158: 
                    159:                                Value& sv_processed=r.process(*body_code);
                    160:                                TempSkip4Delimiter skip(r);
                    161: 
                    162:                                const String* s_processed=sv_processed.get_string();
                    163:                                if(s_processed && !s_processed->is_empty()) { // we have body
                    164:                                        if(need_delim) // need delim & iteration produced string?
                    165:                                                r.write(r.process(*delim_maybe_code));
                    166:                                        else
                    167:                                                need_delim=true;
                    168:                                }
                    169: 
                    170:                                r.write(sv_processed);
                    171: 
                    172:                                if(skip.check_break())
                    173:                                        break;
                    174:                        }
                    175:                }
                    176:        } else {
                    177:                for(ArrayValue::Iterator i(array); i; i.next()){
                    178:                        if(i.value()){
                    179:                                if(key_var_name){
                    180:                                        VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
                    181:                                        r.put_element(caller, *key_var_name, vkey);
                    182:                                }
                    183: 
                    184:                                if(value_var_name)
                    185:                                        r.put_element(caller, *value_var_name, i.value());
                    186: 
                    187:                                r.process_write(*body_code);
                    188: 
                    189:                                if(r.check_skip_break())
                    190:                                        break;
                    191:                        }
                    192:                }
                    193:        }
                    194: }
                    195: 
                    196: 
                    197: enum AtResultType {
                    198:        AtResultTypeValue = 0,
                    199:        AtResultTypeKey = 1,
                    200:        AtResultTypeHash = 2
                    201: };
                    202: 
                    203: inline Value& SingleElementHash(String::Body akey, Value* avalue) {
                    204:        Value& result=*new VHash;
                    205:        result.put_element(*new String(akey, String::L_TAINTED), avalue);
                    206:        return result;
                    207: }
                    208: 
                    209: #ifndef DOXYGEN
                    210: struct Array_seq_item : public PA_Allocated {
                    211:        Value *array_data;
                    212:        union {
                    213:                const char *c_str;
                    214:                double d;
                    215:        } value;
                    216: };
                    217: #endif
                    218: 
                    219: static int sort_cmp_string(const void *a, const void *b) {
                    220:        return strcmp(
                    221:                static_cast<const Array_seq_item *>(a)->value.c_str,
                    222:                static_cast<const Array_seq_item *>(b)->value.c_str
                    223:        );
                    224: }
                    225: static int sort_cmp_double(const void *a, const void *b) {
                    226:        double va=static_cast<const Array_seq_item *>(a)->value.d;
                    227:        double vb=static_cast<const Array_seq_item *>(b)->value.d;
                    228:        if(va<vb)
                    229:                return -1;
                    230:        else if(va>vb)
                    231:                return +1;
                    232:        else 
                    233:                return 0;
                    234: }
                    235: 
                    236: static void _sort(Request& r, MethodParams& params){
                    237:        const String& key_var_name=params.as_string(0, "key-var name must be string");
                    238:        const String& value_var_name=params.as_string(1, "value-var name must be string");
                    239:        Value& key_maker=params.as_junction(2, "key-maker must be code");
                    240:        bool reverse=params.count()>3 && params.as_no_junction(3, "order must not be code").as_string()=="desc"; // default=asc
                    241: 
                    242:        const String* key_var=key_var_name.is_empty()? 0 : &key_var_name;
                    243:        const String* value_var=value_var_name.is_empty()? 0 : &value_var_name;
                    244:        VMethodFrame* context=r.get_method_frame()->caller();
                    245: 
                    246:        VArray& self=GET_SELF(r, VArray);
                    247:        ArrayValue& array=self.array();
1.4     ! moko      248:        int count=array.used(); // not array.count()
1.1       moko      249: 
                    250:        Array_seq_item* seq=new Array_seq_item[count];
                    251:        int pos=0;
                    252:        bool key_values_are_strings=true;
                    253: 
                    254:        for(ArrayValue::Iterator i(array); i; i.next() ){
                    255:                if(i.value()){
                    256:                        if(key_var)
                    257:                                r.put_element(*context, *key_var, new VString(*new String(i.key(), String::L_TAINTED)));
                    258:                        if(value_var)
                    259:                                r.put_element(*context, *value_var, i.value());
                    260: 
                    261:                        Value& value=r.process(key_maker);
                    262:                        if(pos==0) // determining key values type by first one
                    263:                                key_values_are_strings=value.is_string();
                    264: 
                    265:                        seq[pos].array_data=i.value();
                    266:                        if(key_values_are_strings)
                    267:                                seq[pos++].value.c_str=value.as_string().cstr();
                    268:                        else
                    269:                                seq[pos++].value.d=value.as_expr_result().as_double();
                    270:                }
                    271:        }
                    272: 
                    273:        // @todo: handle this elsewhere
                    274:        if(r.charsets.source().NAME()=="KOI8-R" && key_values_are_strings)
                    275:                for(pos=0; pos<count; pos++)
                    276:                        if(*seq[pos].value.c_str)
                    277:                                seq[pos].value.c_str=Charset::transcode(seq[pos].value.c_str, r.charsets.source(), pa_UTF8_charset).cstr();
                    278: 
                    279:        // sort keys
                    280:        qsort(seq, count, sizeof(Array_seq_item), key_values_are_strings ? sort_cmp_string : sort_cmp_double);
                    281: 
                    282:        // reorder array as required in 'seq'
                    283:        array.clear();
                    284:        if(reverse)
                    285:                for(pos=count-1; pos>=0; pos--)
                    286:                        array+=seq[pos].array_data;
                    287:        else
                    288:                for(pos=0; pos<count; pos++)
                    289:                        array+=seq[pos].array_data;
                    290: 
                    291:        delete[] seq;
                    292: }
                    293: 
                    294: static void _at(Request& r, MethodParams& params) {
                    295:        VArray& self=GET_SELF(r, VArray);
                    296:        ArrayValue& array=self.array();
1.4     ! moko      297:        size_t count=array.count();
1.1       moko      298: 
                    299:        int pos=0;
                    300: 
                    301:        AtResultType result_type=AtResultTypeValue;
                    302:        if(params.count() > 1) {
                    303:                const String& stype=params.as_string(1, "type must be string");
                    304:                if(stype == "key")
                    305:                        result_type=AtResultTypeKey;
                    306:                else if(stype == "hash")
                    307:                        result_type=AtResultTypeHash;
                    308:                else if(stype != "value")
                    309:                        throw Exception(PARSER_RUNTIME, &stype, "type must be 'key', 'value' or 'hash'");
                    310:        }
                    311: 
                    312:        Value& vwhence=params[0];
                    313:        if(vwhence.is_string()) {
                    314:                const String& swhence=*vwhence.get_string();
                    315:                if(swhence == "last")
                    316:                        pos=count-1;
                    317:                else if(swhence != "first")
                    318:                        throw Exception(PARSER_RUNTIME, &swhence, "whence must be 'first', 'last' or expression");
                    319:        } else {
                    320:                pos=r.process(vwhence).as_int();
                    321:                if(pos < 0)
                    322:                        pos+=count;
                    323:        }
                    324: 
                    325:        if(count && pos >= 0 && (size_t)pos < count){
                    326:                switch(result_type) {
                    327:                        case AtResultTypeKey:
                    328:                                {
                    329:                                        for(ArrayValue::Iterator i(array); i; i.next() ){
                    330:                                                if(i.value() && !(pos--)){
                    331:                                                        r.write(*new VString(*new String(i.key(), String::L_TAINTED)));
                    332:                                                        break;
                    333:                                                }
                    334:                                        }
                    335:                                        break;
                    336:                                }
                    337:                        case AtResultTypeValue:
                    338:                                {
                    339:                                        for(ArrayValue::Iterator i(array); i; i.next() )
                    340:                                                if(i.value() &&!(pos--)){
                    341:                                                        r.write(*i.value());
                    342:                                                        break;
                    343:                                                }
                    344:                                        break;
                    345:                                }
                    346:                        case AtResultTypeHash:
                    347:                                {
                    348:                                        for(ArrayValue::Iterator i(array); i; i.next() )
                    349:                                                if(i.value() &&!(pos--)){
                    350:                                                        r.write(SingleElementHash(i.key(), i.value()));
                    351:                                                        break;
                    352:                                                }
                    353:                                        break;
                    354:                                }
                    355:                }
                    356:        }
                    357: }
                    358: 
                    359: 
                    360: extern String table_reverse_name;
                    361: 
                    362: static void _select(Request& r, MethodParams& params) {
                    363:        InCycle temp(r);
                    364:        const String* key_var_name=&params.as_string(0, "key-var name must be string");
                    365:        const String* value_var_name=&params.as_string(1, "value-var name must be string");
                    366:        Value& vcondition=params.as_expression(2, "condition must be number, bool or expression");
                    367: 
                    368:        if(key_var_name->is_empty()) key_var_name=0;
                    369:        if(value_var_name->is_empty()) value_var_name=0;
                    370: 
                    371:        ArrayValue& source_array=GET_SELF(r, VArray).array();
                    372:        Value& caller=*r.get_method_frame()->caller();
                    373: 
                    374:        int limit=source_array.count();
                    375:        bool reverse=false;
                    376: 
                    377:        if(params.count()>3)
                    378:                if(HashStringValue* options=params.as_hash(3)) {
                    379:                        int valid_options=0;
                    380:                        if(Value* vlimit=options->get(sql_limit_name)) {
                    381:                                valid_options++;
                    382:                                limit=r.process(*vlimit).as_int();
                    383:                        }
                    384:                        if(Value* vreverse=options->get(table_reverse_name)) {
                    385:                                valid_options++;
                    386:                                reverse=r.process(*vreverse).as_bool();
                    387:                        }
                    388:                        if(valid_options!=options->count())
                    389:                                throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
                    390:                }
                    391: 
                    392:        VArray *result=new VArray;
                    393:        ArrayValue& result_array=result->array();
                    394: 
                    395:        if(limit>0){
                    396: 
                    397:                if(reverse){
                    398:                        for(ArrayValue::ReverseIterator i(source_array); i; ){
                    399:                                Value *value=i.prev(); // here for correct i.key()
                    400:                                if(key_var_name)
                    401:                                        r.put_element(caller, *key_var_name, new VString(*new String(i.key(), String::L_TAINTED)));
                    402:                                if(value_var_name)
                    403:                                        r.put_element(caller, *value_var_name, value);
                    404: 
                    405:                                bool condition=r.process(vcondition).as_bool();
                    406: 
                    407:                                if(r.check_skip_break())
                    408:                                        break;
                    409: 
                    410:                                if(condition){
                    411:                                        result_array+=value;
                    412:                                        if(!--limit)
                    413:                                                break;
                    414:                                }
                    415:                        }
                    416:                } else {
                    417:                        for(ArrayValue::Iterator i(source_array); i; i.next() ){
                    418:                                Value *value=i.value();
                    419:                                if(value){
                    420:                                        if(key_var_name)
                    421:                                                r.put_element(caller, *key_var_name, new VString(*new String(i.key(), String::L_TAINTED)));
                    422:                                        if(value_var_name)
                    423:                                                r.put_element(caller, *value_var_name, value);
                    424: 
                    425:                                        bool condition=r.process(vcondition).as_bool();
                    426: 
                    427:                                        if(r.check_skip_break())
                    428:                                                break;
                    429: 
                    430:                                        if(condition){
                    431:                                                result_array+=value;
                    432:                                                if(!--limit)
                    433:                                                        break;
                    434:                                        }
                    435:                                }
                    436:                        }
                    437:                }
                    438:        }
                    439: 
                    440:        r.write(*result);
                    441: }
                    442: 
                    443: static void _reverse(Request& r, MethodParams& params) {
                    444:        VArray& self=GET_SELF(r, VArray);
                    445:        ArrayValue& source_array=self.array();
                    446:        size_t count=source_array.count();
                    447: 
                    448:        VArray& result=*new VArray(count);
                    449:        ArrayValue& result_array=result.array();
                    450: 
1.4     ! moko      451:        for(ArrayValue::ReverseIterator i(source_array); i; ){
        !           452:                result_array+=i.prev();
1.1       moko      453:        }
                    454: 
                    455:        r.write(result);
                    456: }
                    457: 
                    458: 
                    459: static void _rename(Request& r, MethodParams& params) {}
                    460: 
                    461: // constructor
                    462: 
                    463: MArray::MArray(): Methoded(VARRAY_TYPE) {
                    464: //     set_base(hash_class);
                    465: 
                    466:        // ^array::create[[copy_from]]
                    467:        add_native_method("create", Method::CT_DYNAMIC, _create_or_add, 0, 1);
                    468:        // ^array.add[add_from]
                    469:        add_native_method("add", Method::CT_DYNAMIC, _create_or_add, 1, 1);
                    470: 
                    471:        // ^array.sub[sub_from]
                    472:        add_native_method("sub", Method::CT_DYNAMIC, _sub, 1, 1);
1.2       moko      473:        // ^array.union[b] = array
1.1       moko      474:        add_native_method("union", Method::CT_DYNAMIC, _union, 1, 1);
1.2       moko      475:        // ^array.intersection[b][options array] = array
1.1       moko      476:        add_native_method("intersection", Method::CT_DYNAMIC, _intersection, 1, 2);
1.2       moko      477:        // ^array.intersects[b] = bool
1.1       moko      478:        add_native_method("intersects", Method::CT_DYNAMIC, _intersects, 1, 1);
                    479: 
1.2       moko      480:        // ^array.append[value;value]
                    481:        add_native_method("append", Method::CT_DYNAMIC, _append, 1, 10000);
                    482: 
                    483:        // ^array.insert[index;value...]
                    484:        add_native_method("insert", Method::CT_DYNAMIC, _insert, 2, 10000);
                    485: 
                    486:        // ^array.delete[index]
1.1       moko      487:        add_native_method("delete", Method::CT_DYNAMIC, _delete, 0, 1);
                    488: 
1.2       moko      489:        // ^array.contains[index]
1.1       moko      490:        add_native_method("contains", Method::CT_DYNAMIC, _contains, 1, 1);
                    491: 
                    492:        // ^array::sql[query][options array]
                    493:        add_native_method("sql", Method::CT_DYNAMIC, _sql, 1, 2);
                    494: 
                    495:        // ^array._keys[[column name]]
                    496:        add_native_method("_keys", Method::CT_DYNAMIC, _keys, 0, 1);
                    497: 
                    498:        // ^array._count[]
                    499:        add_native_method("_count", Method::CT_DYNAMIC, _count, 0, 0);
                    500: 
1.2       moko      501:        // ^array.foreach[index;value]{code}[delim]
1.1       moko      502:        add_native_method("foreach", Method::CT_DYNAMIC, _foreach, 2+1, 2+1+1);
                    503: 
1.2       moko      504:        // ^array.sort[index;value]{string-key-maker}[[asc|desc]]
                    505:        // ^array.sort[index;value](numeric-key-maker)[[asc|desc]]
1.1       moko      506:        add_native_method("sort", Method::CT_DYNAMIC, _sort, 3, 4);
                    507: 
1.2       moko      508:        // ^array.select[index;value](bool-condition)[options hash]
1.1       moko      509:        add_native_method("select", Method::CT_DYNAMIC, _select, 3, 4);
                    510: 
                    511:        // ^array.reverse[]
                    512:        add_native_method("reverse", Method::CT_DYNAMIC, _reverse, 0, 0);
                    513: 
1.2       moko      514:        // ^array._at[first|last[;'key'|'value'|'hash']]
                    515:        // ^array._at([-+]offset)[['key'|'value'|'hash']]
1.1       moko      516:        add_native_method("_at", Method::CT_DYNAMIC, _at, 1, 2);
                    517: 
                    518:        // ^array.rename[from;to]
                    519:        // ^array.rename[ $.from[to] ... ]
                    520:        add_native_method("rename", Method::CT_DYNAMIC, _rename, 1, 2);
                    521: 
                    522: #ifdef FEATURE_GET_ELEMENT4CALL
                    523:        // aliases without "_"
                    524:        add_native_method("keys", Method::CT_DYNAMIC, _keys, 0, 1);
                    525:        add_native_method("count", Method::CT_DYNAMIC, _count, 0, 0);
                    526:        add_native_method("at", Method::CT_DYNAMIC, _at, 1, 2);
                    527: #endif
                    528: 
                    529: }

E-mail: