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

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

E-mail: