Lichen

Annotated templates/ops.c

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