1 /* Common operations. 2 3 Copyright (C) 2015, 2016 Paul Boddie <paul@boddie.org.uk> 4 5 This program is free software; you can redistribute it and/or modify it under 6 the terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3 of the License, or (at your option) any later 8 version. 9 10 This program is distributed in the hope that it will be useful, but WITHOUT 11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 12 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 13 details. 14 15 You should have received a copy of the GNU General Public License along with 16 this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 #include <stdlib.h> 20 #include "ops.h" 21 #include "progops.h" /* for raising errors */ 22 #include "progconsts.h" 23 #include "progtypes.h" 24 25 /* Direct access and manipulation of static objects. */ 26 27 __attr __load_static(__ref parent, __ref obj) 28 { 29 __attr out = {.context=parent, .value=obj}; 30 return out; 31 } 32 33 /* Direct retrieval operations, returning and setting attributes. */ 34 35 __attr __load_via_object(__ref obj, int pos) 36 { 37 return obj->attrs[pos]; 38 } 39 40 __attr __load_via_class(__ref obj, int pos) 41 { 42 return __load_via_object(__get_class(obj), pos); 43 } 44 45 __attr __get_class_and_load(__ref obj, int pos) 46 { 47 if (__is_instance(obj)) 48 return __load_via_class(obj, pos); 49 else 50 return __load_via_object(obj, pos); 51 } 52 53 /* Direct storage operations. */ 54 55 int __store_via_object(__ref obj, int pos, __attr value) 56 { 57 obj->attrs[pos] = value; 58 return 1; 59 } 60 61 int __get_class_and_store(__ref obj, int pos, __attr value) 62 { 63 /* Forbid class-relative assignments. */ 64 65 __raise_type_error(); 66 return 0; 67 } 68 69 /* Introspection. */ 70 71 int __is_instance(__ref obj) 72 { 73 return obj->pos == __INSTANCEPOS; 74 } 75 76 int __is_type_instance(__ref obj) 77 { 78 return __HASATTR(__get_class(obj), __TYPE_CLASS_POS, __TYPE_CLASS_CODE); 79 } 80 81 __ref __get_class(__ref obj) 82 { 83 return __load_via_object(obj, __pos___class__).value; 84 } 85 86 __attr __get_class_attr(__ref obj) 87 { 88 return __load_via_object(obj, __pos___class__); 89 } 90 91 /* Attribute testing operations. */ 92 93 __ref __test_specific_instance(__ref obj, __ref type) 94 { 95 return __get_class(obj) == type ? obj : 0; 96 } 97 98 __ref __test_specific_object(__ref obj, __ref type) 99 { 100 return __test_specific_type(obj, type) || __test_specific_instance(obj, type) ? obj : 0; 101 } 102 103 __ref __test_specific_type(__ref obj, __ref type) 104 { 105 return obj == type ? obj : 0; 106 } 107 108 __ref __test_common_instance(__ref obj, int pos, int code) 109 { 110 return __HASATTR(__get_class(obj), pos, code) ? obj : 0; 111 } 112 113 __ref __test_common_object(__ref obj, int pos, int code) 114 { 115 return __test_common_type(obj, pos, code) || __test_common_instance(obj, pos, code) ? obj : 0; 116 } 117 118 __ref __test_common_type(__ref obj, int pos, int code) 119 { 120 return __HASATTR(obj, pos, code) ? obj : 0; 121 } 122 123 /* Attribute testing and retrieval operations. */ 124 125 static __attr __check_and_load_via_object_null(__ref obj, int pos, int code) 126 { 127 if (__HASATTR(obj, pos, code)) 128 return __load_via_object(obj, pos); 129 else 130 return __NULL; 131 } 132 133 __attr __check_and_load_via_class(__ref obj, int pos, int code) 134 { 135 return __check_and_load_via_object(__get_class(obj), pos, code); 136 } 137 138 __attr __check_and_load_via_object(__ref obj, int pos, int code) 139 { 140 if (__HASATTR(obj, pos, code)) 141 return __load_via_object(obj, pos); 142 143 __raise_type_error(); 144 return __NULL; 145 } 146 147 __attr __check_and_load_via_any(__ref obj, int pos, int code) 148 { 149 __attr out = __check_and_load_via_object_null(obj, pos, code); 150 if (out.value == 0) 151 out = __check_and_load_via_class(obj, pos, code); 152 return out; 153 } 154 155 /* Attribute testing and storage operations. */ 156 157 int __check_and_store_via_class(__ref obj, int pos, int code, __attr value) 158 { 159 /* Forbid class-relative assignments. */ 160 161 __raise_type_error(); 162 return 0; 163 } 164 165 int __check_and_store_via_object(__ref obj, int pos, int code, __attr value) 166 { 167 if (__HASATTR(obj, pos, code)) 168 { 169 __store_via_object(obj, pos, value); 170 return 1; 171 } 172 173 /* No suitable attribute. */ 174 175 __raise_type_error(); 176 return 0; 177 } 178 179 int __check_and_store_via_any(__ref obj, int pos, int code, __attr value) 180 { 181 if (__check_and_store_via_object(obj, pos, code, value)) 182 return 1; 183 184 /* Forbid class-relative assignments. */ 185 186 __raise_type_error(); 187 return 0; 188 } 189 190 /* Context-related operations. */ 191 192 __attr __test_context(__ref context, __attr attr) 193 { 194 /* Preserve any existing null or instance context. */ 195 196 if ((attr.context == 0) || __is_instance(attr.context)) 197 return attr; 198 199 /* Test any instance context against the context employed by the 200 attribute. */ 201 202 if (__is_instance(context)) 203 if (__test_common_instance(context, __TYPEPOS(attr.context), __TYPECODE(attr.context))) 204 return __replace_context(context, attr); 205 else 206 __raise_type_error(); 207 208 /* Test for access to a type class attribute using a type instance. */ 209 210 if (__test_specific_type(attr.context, &__TYPE_CLASS_TYPE) && __is_type_instance(context)) 211 return __replace_context(context, attr); 212 213 /* Otherwise, preserve the attribute as retrieved. */ 214 215 return attr; 216 } 217 218 __attr __replace_context(__ref context, __attr attr) 219 { 220 __attr out; 221 222 /* Set the context. */ 223 224 out.context = context; 225 226 /* Reference a callable version of the attribute by obtaining the bound 227 method reference from the __fn__ special attribute. */ 228 229 out.value = __load_via_object(attr.value, __ATTRPOS(__fn__)).b; 230 return out; 231 } 232 233 __attr __update_context(__ref context, __attr attr) 234 { 235 __attr out = {context, .fn=attr.fn}; 236 return out; 237 } 238 239 /* Basic structure tests. */ 240 241 int __WITHIN(__ref obj, int pos) 242 { 243 return pos < obj->table->size; 244 } 245 246 int __HASATTR(__ref obj, int pos, int code) 247 { 248 return __WITHIN(obj, pos) && (obj->table->attrs[pos] == code); 249 } 250 251 /* Parameter position operations. */ 252 253 int __HASPARAM(const __ptable *ptable, int ppos, int pcode) 254 { 255 __param param; 256 257 if (ppos < ptable->size) 258 { 259 param = ptable->params[ppos]; 260 if (param.code == pcode) 261 return param.pos; 262 } 263 264 return -1; 265 } 266 267 /* Conversions. */ 268 269 __attr __CONTEXT_AS_VALUE(__attr attr) 270 { 271 __attr out; 272 out.context = attr.context; 273 out.value = attr.context; 274 return out; 275 } 276 277 /* Type testing. */ 278 279 __ref __ISFUNC(__ref obj) 280 { 281 return __test_specific_instance(obj, &__FUNCTION_TYPE); 282 } 283 284 int __ISNULL(__attr value) 285 { 286 /* (value.context == __NULL.context) is superfluous */ 287 return (value.value == 0); /* __NULL.value */ 288 } 289 290 /* Attribute codes and positions for type objects. */ 291 292 unsigned int __TYPECODE(__ref obj) 293 { 294 return obj->table->attrs[obj->pos]; 295 } 296 297 unsigned int __TYPEPOS(__ref obj) 298 { 299 return obj->pos; 300 } 301 302 /* Memory allocation. */ 303 304 void *__ALLOCATE(size_t nmemb, size_t size) 305 { 306 void *ptr = calloc(nmemb, size); 307 if (ptr == NULL) 308 __raise_memory_error(); 309 return ptr; 310 } 311 312 void *__REALLOCATE(void *ptr, size_t size) 313 { 314 void *nptr = realloc(ptr, size); 315 if (nptr == NULL) 316 __raise_memory_error(); 317 return nptr; 318 } 319 320 /* Copying of structures. */ 321 322 __ref __COPY(__ref obj, int size) 323 { 324 __ref copy = (__ref) __ALLOCATE(1, size); 325 memcpy(copy, obj, size); 326 return copy; 327 }