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