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