1 /*
2     $Id: symbolobj.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 "symbolobj.h"
20 #include <string.h>
21 #include "eval.h"
22 #include "unicode.h"
23 #include "error.h"
24 #include "file.h"
25 #include "values.h"
26 #include "arguments.h"
27 
28 #include "typeobj.h"
29 #include "strobj.h"
30 #include "errorobj.h"
31 
32 static Type obj;
33 
34 Type *const SYMBOL_OBJ = &obj;
35 
new_symbol(const str_t * name,linepos_t epoint)36 Obj *new_symbol(const str_t *name, linepos_t epoint) {
37     Symbol *symbol = Symbol(val_alloc(SYMBOL_OBJ));
38     if (not_in_file(name->data, current_file_list->file)) str_cpy(&symbol->name, name);
39     else symbol->name = *name;
40     symbol->cfname.data = NULL;
41     symbol->cfname.len = 0;
42     symbol->hash = -1;
43     symbol->file_list = current_file_list;
44     symbol->epoint = *epoint;
45     return Obj(symbol);
46 }
47 
symbol_destroy(Symbol * v1)48 static FAST_CALL NO_INLINE void symbol_destroy(Symbol *v1) {
49     free((char *)v1->name.data);
50 }
51 
symbol_same(const Symbol * v1,const Symbol * v2)52 static FAST_CALL NO_INLINE bool symbol_same(const Symbol *v1, const Symbol *v2) {
53     return memcmp(v1->name.data, v2->name.data, v2->name.len) == 0;
54 }
55 
same(const Obj * o1,const Obj * o2)56 static FAST_CALL bool same(const Obj *o1, const Obj *o2) {
57     const Symbol *v1 = Symbol(o1), *v2 = Symbol(o2);
58     if (o1->obj != o2->obj || v1->name.len != v2->name.len) return false;
59     switch (v1->name.len) {
60     case 0: return true;
61     case 1: return v1->name.data[0] == v2->name.data[0];
62     default: return v1->name.data == v2->name.data || symbol_same(v1, v2);
63     }
64 }
65 
destroy(Obj * o1)66 static FAST_CALL void destroy(Obj *o1) {
67     Symbol *v1 = Symbol(o1);
68     const struct file_s *cfile = v1->file_list->file;
69     if (not_in_file(v1->name.data, cfile)) symbol_destroy(v1);
70     if (v1->cfname.data != NULL && v1->name.data != v1->cfname.data) free((uint8_t *)v1->cfname.data);
71 }
72 
repr(Obj * o1,linepos_t UNUSED (epoint),size_t maxsize)73 static MUST_CHECK Obj *repr(Obj *o1, linepos_t UNUSED(epoint), size_t maxsize) {
74     Symbol *v1 = Symbol(o1);
75     size_t chars;
76     Str *v;
77     size_t len;
78 
79     chars = calcpos(v1->name.data, v1->name.len) + 1;
80     if (chars < 1 || chars > maxsize) return NULL;/* overflow */
81     len = v1->name.len + 1;
82     if (len < 1) return NULL;/* overflow */
83     v = new_str2(len);
84     if (v == NULL) return NULL;
85     v->chars = chars;
86     v->data[0] = '.';
87     memcpy(v->data + 1, v1->name.data, len - 1);
88     return Obj(v);
89 }
90 
str(Obj * o1,linepos_t UNUSED (epoint),size_t maxsize)91 static MUST_CHECK Obj *str(Obj *o1, linepos_t UNUSED(epoint), size_t maxsize) {
92     Symbol *v1 = Symbol(o1);
93     Str *v;
94     size_t chars = calcpos(v1->name.data, v1->name.len);
95     if (chars > maxsize) return NULL;
96     v = new_str2(v1->name.len);
97     if (v == NULL) return NULL;
98     v->chars = chars;
99     memcpy(v->data, v1->name.data, v1->name.len);
100     return Obj(v);
101 }
102 
hash(Obj * o1,int * hs,linepos_t UNUSED (epoint))103 static MUST_CHECK Obj *hash(Obj *o1, int *hs, linepos_t UNUSED(epoint)) {
104     Symbol *v1 = Symbol(o1);
105     str_t s;
106     size_t l;
107     unsigned int h;
108     if (v1->name.len == 0) {
109         *hs = 0;
110         return NULL;
111     }
112     if (v1->hash >= 0) {
113         *hs = v1->hash;
114         return NULL;
115     }
116     if (v1->cfname.data == NULL) {
117         str_cfcpy(&v1->cfname, &v1->name);
118         if (v1->cfname.data != v1->name.data) str_cfcpy(&v1->cfname, NULL);
119     }
120     s = v1->cfname;
121     h = (unsigned int)*s.data << 7;
122     l = s.len;
123     while ((l--) != 0) h = (1000003 * h) ^ *s.data++;
124     h ^= (unsigned int)s.len;
125     *hs = v1->hash = h & ((~0U) >> 1);
126     return NULL;
127 }
128 
symbol_cfsame(Symbol * v1,Symbol * v2)129 bool symbol_cfsame(Symbol *v1, Symbol *v2) {
130     str_t *n1 = &v1->cfname, *n2 = &v2->cfname;
131     if (n1->data == NULL) {
132         str_cfcpy(n1, &v1->name);
133         if (n1->data != v1->name.data) str_cfcpy(n1, NULL);
134     }
135     if (n2->data == NULL) {
136         str_cfcpy(n2, &v2->name);
137         if (n2->data != v2->name.data) str_cfcpy(n2, NULL);
138     }
139     if (str_cmp(n1, n2) != 0) return false;
140     if (diagnostics.case_symbol && str_cmp(&v1->name, &v2->name) != 0) err_msg_symbol_case2(v1, v2);
141     return true;
142 }
143 
icmp(oper_t op)144 static inline int icmp(oper_t op) {
145     Symbol *v1 = Symbol(op->v1), *v2 = Symbol(op->v2);
146     str_t *n1 = &v1->cfname, *n2 = &v2->cfname;
147     int h;
148     if (n1->data == NULL) {
149         str_cfcpy(n1, &v1->name);
150         if (n1->data != v1->name.data) str_cfcpy(n1, NULL);
151     }
152     if (n2->data == NULL) {
153         str_cfcpy(n2, &v2->name);
154         if (n2->data != v2->name.data) str_cfcpy(n2, NULL);
155     }
156     h = memcmp(n1->data, n2->data, (n1->len < n2->len) ? n1->len : n2->len);
157     if (h != 0) return h;
158     if (n1->len < n2->len) return -1;
159     return (n1->len > n2->len) ? 1 : 0;
160 }
161 
calc2(oper_t op)162 static MUST_CHECK Obj *calc2(oper_t op) {
163     Obj *o2 = op->v2;
164     int i;
165     switch (o2->obj->type) {
166     case T_SYMBOL:
167         i = icmp(op);
168         if (i == 0 && diagnostics.case_symbol && str_cmp(&Symbol(op->v1)->name, &Symbol(o2)->name) != 0) err_msg_symbol_case2(Symbol(op->v1), Symbol(o2));
169         return obj_oper_compare(op, i);
170     case T_NONE:
171     case T_ERROR:
172         return val_reference(o2);
173     default:
174         if (o2->obj->iterable && op->op != O_MEMBER && op->op != O_X) {
175             return o2->obj->rcalc2(op);
176         }
177         break;
178     }
179     return obj_oper_error(op);
180 }
181 
rcalc2(oper_t op)182 static MUST_CHECK Obj *rcalc2(oper_t op) {
183     if (op->op == O_MEMBER) {
184         return op->v1->obj->calc2(op);
185     }
186     return obj_oper_error(op);
187 }
188 
symbolobj_init(void)189 void symbolobj_init(void) {
190     new_type(&obj, T_SYMBOL, "symbol", sizeof(Symbol));
191     obj.destroy = destroy;
192     obj.same = same;
193     obj.hash = hash;
194     obj.repr = repr;
195     obj.str = str;
196     obj.calc2 = calc2;
197     obj.rcalc2 = rcalc2;
198 }
199