1 /*
2     $Id: foldobj.c 2593 2021-04-18 13:00:11Z soci $
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License along
15     with this program; if not, write to the Free Software Foundation, Inc.,
16     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 
18 */
19 #include "foldobj.h"
20 #include <string.h>
21 #include "values.h"
22 #include "eval.h"
23 #include "error.h"
24 
25 #include "typeobj.h"
26 #include "strobj.h"
27 #include "errorobj.h"
28 #include "boolobj.h"
29 
30 static Type obj;
31 
32 Type *const FOLD_OBJ = &obj;
33 
34 static Fold foldval = { { &obj, 1 }, NULL };
35 
36 Obj *const fold_value = &foldval.v;
37 
same(const Obj * o1,const Obj * o2)38 static FAST_CALL bool same(const Obj *o1, const Obj *o2) {
39     return o1 == o2;
40 }
41 
hash(Obj * UNUSED (v1),int * hs,linepos_t UNUSED (epoint))42 static MUST_CHECK Obj *hash(Obj *UNUSED(v1), int *hs, linepos_t UNUSED(epoint)) {
43     *hs = 0; /* whatever, there's only one */
44     return NULL;
45 }
46 
repr(Obj * UNUSED (v1),linepos_t UNUSED (epoint),size_t maxsize)47 static MUST_CHECK Obj *repr(Obj *UNUSED(v1), linepos_t UNUSED(epoint), size_t maxsize) {
48     Str *v;
49     if (3 > maxsize) return NULL;
50     v = foldval.repr;
51     if (v == NULL) {
52         v = new_str2(3);
53         if (v == NULL) return NULL;
54         v->chars = 3;
55         memset(v->data, '.', 3);
56         foldval.repr = v;
57     }
58     return val_reference(Obj(v));
59 }
60 
calc2(oper_t op)61 static MUST_CHECK Obj *calc2(oper_t op) {
62     Obj *v2 = op->v2;
63     if (v2->obj->iterable && op->op != O_MEMBER && op->op != O_X) {
64         bool minmax = (op->op == O_MIN) || (op->op == O_MAX);
65         struct iter_s iter;
66         Obj *ret = NULL;
67         iter.data = v2; v2->obj->getiter(&iter);
68 
69         while ((v2 = iter.next(&iter)) != NULL) {
70             Obj *val;
71             if (ret == NULL) {
72                 ret = val_reference(v2);
73                 continue;
74             }
75             op->v1 = ret;
76             op->v2 = v2;
77             op->inplace = (ret->refcount == 1 && !minmax) ? ret : NULL;
78             val = ret->obj->calc2(op);
79             if (minmax) {
80                 if (val == true_value) val_replace(&val, ret);
81                 else if (val == false_value) val_replace(&val, v2);
82             }
83             val_destroy(ret); ret = val;
84         }
85         iter_destroy(&iter);
86         return ret != NULL ? ret : Obj(new_error(ERROR____EMPTY_LIST, op->epoint2));
87     }
88     switch (v2->obj->type) {
89     case T_NONE:
90     case T_ERROR:
91         return val_reference(v2);
92     default:
93         break;
94     }
95     return obj_oper_error(op);
96 }
97 
rcalc2(oper_t op)98 static MUST_CHECK Obj *rcalc2(oper_t op) {
99     Obj *v1 = op->v1;
100     if (v1->obj->iterable) {
101         if (op->op != O_IN) {
102             bool minmax = (op->op == O_MIN) || (op->op == O_MAX);
103             struct iter_s iter;
104             Obj *ret = NULL;
105             iter.data = v1; v1->obj->getriter(&iter);
106 
107             while ((v1 = iter.next(&iter)) != NULL) {
108                 Obj *val;
109                 if (ret == NULL) {
110                     ret = val_reference(v1);
111                     continue;
112                 }
113                 op->v1 = v1;
114                 op->v2 = ret;
115                 op->inplace = (ret->refcount == 1 && !minmax) ? ret : NULL;
116                 val = v1->obj->calc2(op);
117                 if (minmax) {
118                     if (val == true_value) val_replace(&val, v1);
119                     else if (val == false_value) val_replace(&val, ret);
120                 }
121                 val_destroy(ret); ret = val;
122             }
123             iter_destroy(&iter);
124             return ret != NULL ? ret : Obj(new_error(ERROR____EMPTY_LIST, op->epoint));
125         }
126     }
127     switch (v1->obj->type) {
128     case T_NONE:
129     case T_ERROR:
130         return val_reference(v1);
131     default:
132         break;
133     }
134     return obj_oper_error(op);
135 }
136 
foldobj_init(void)137 void foldobj_init(void) {
138     new_type(&obj, T_FOLD, "fold", sizeof(Fold));
139     obj.same = same;
140     obj.hash = hash;
141     obj.repr = repr;
142     obj.calc2 = calc2;
143     obj.rcalc2 = rcalc2;
144 }
145 
foldobj_destroy(void)146 void foldobj_destroy(void) {
147 #ifdef DEBUG
148     if (fold_value->refcount != 1) fprintf(stderr, "fold %" PRIuSIZE "\n", fold_value->refcount - 1);
149 #endif
150     if (foldval.repr != NULL) val_destroy(Obj(foldval.repr));
151 }
152