Source

arana-main / arana / object.c

/*
 * object.c
 *
 * (c) Copyright 2009 by Armin Ronacher.
 */

#include "arana.h"


void
_arana_dump(AR_SIG, AR obj)
{
    printf("<%p> %s\n", (void *)obj, AR_AS_CHARP(AR_REPR(obj)));
}

static inline AR
arana_call_with_self(AR_SIG, AR object, AR self, AR args)
{
    if (AR_IS_FUNCTION(object))
        return ((ArFunction *)object)->func(AR_ISIG, self, args);
    else if (AR_IS_METHOD(object)) {
        ArMethod *meth = (ArMethod *)object;
        return arana_call_with_self(AR_ISIG, meth->function, meth->self, args);
    }
    return AR_CALL_METHOD(object, ":call()", args);
}

AR
arana_call(AR_SIG, AR object, AR args)
{
    return arana_call_with_self(AR_ISIG, object, AR_NONE, args);
}

static AR
arana_call_method_unchecked(AR_SIG, AR object, const char *member, AR args,
                            int try_getter)
{
    AR method;
    int from_type;
    if (!arana_lookup_member(AR_ISIG, object, member, &method, &from_type,
                             try_getter))
        return AR_NULL;
    return arana_call_with_self(AR_ISIG, method, object, args);
}

AR
arana_call_method(AR_SIG, AR object, const char *member, AR args)
{
    AR result = arana_call_method_unchecked(AR_ISIG, object, member, args, 1);
    if (!result) {
        char errorbuf[300];
        snprintf(errorbuf, 300, "'%s' has no method named '%s'",
                 AR_TYPE_NAME(object), member);
        AR_RAISE(AR_ATTRIBUTE_ERROR(member, errorbuf));
    }
    return result;
}

AR
arana_type_new(AR_SIG, ArObjectType *type)
{
    int fail;
    khiter_t k;
    ArType *self = (ArType *)AR_ALLOCATE_HEAVY_OBJECT(ArType, ArTypeType);
    self->reflected_type = type;

    k = kh_put(ArTypeMap, AI->type_map, (AR)type, &fail);
    kh_value(AI->type_map, k) = (AR)self;

    return (AR)self;
}

AR
arana_get_type_object(AR_SIG, AR object)
{
    int fail;
    ArObjectType *type = AR_TYPE(object);
    khiter_t k = kh_put(ArTypeMap, AI->type_map, (AR)type, &fail);
    if (fail)
        AR_FATAL("Unregistered type '%s' encountered.\n", type->name);
    return kh_value(AI->type_map, k);
}

int
arana_is_instance(AR object, ArObjectType *type)
{
    ArObjectType *this_type = AR_TYPE(object);
    do {
        if (this_type == type)
            return 1;
    }
    while ((this_type = this_type->parent) != NULL);
    return 0;
}

AR
arana_get_parent_type(AR_SIG, AR type)
{
    int fail;
    khiter_t k;
    ArObjectType *t = ((ArType *)type)->reflected_type;
    if (!t->parent)
        return AR_NULL;
    k = kh_put(ArTypeMap, AI->type_map, (AR)t->parent, &fail);
    if (fail)
        AR_FATAL("Unregistered type encountered.\n");
    return kh_value(AI->type_map, k);
}

int
arana_lookup_member(AR_SIG, AR object, const char *member,
                    AR *value_out, int *from_type_out, int try_getter)
{
    int from_type = 0;
    AR value = AR_NULL;
    ArHeavyObject *h_obj;
    if (AR_HAS_MAP(object)) {
        h_obj = (ArHeavyObject *)object;
        value = AR_STRINGMAP_GET(h_obj->_map,  member);
    }
    if (!value) {
        AR type = AR_GET_TYPE_OBJECT(object);
        from_type = 1;
        while (type) {
            h_obj = (ArHeavyObject *)type;
            value = AR_STRINGMAP_GET(h_obj->_map, member);
            if (value)
                break;
            type = AR_PARENT_TYPE(type);
        }
    }
    if (!value && try_getter) {
        Ar_size_t length = strlen(member);
        char *getter_name = AR_ALLOC(length + 5);
        memcpy(getter_name, member, length);
        memcpy(getter_name + length, ":get", 5);
        value = arana_call_method_unchecked(AR_ISIG, object, getter_name,
                                            AR_TUPLE(0), 0);
    }

    if (!value)
        return 0;

    *value_out = value;
    *from_type_out = from_type;
    return 1;
}

AR
arana_get_member(AR_SIG, AR object, AR member)
{
    AR value;
    int from_type;

    if (!AR_IS_STRING(member))
        AR_RAISE(AR_TYPE_ERROR("member names have to be strings"));

    if (!arana_lookup_member(AR_ISIG, object, AR_AS_CHARP(member),
                             &value, &from_type, 1)) {
        char errorbuf[300];
        snprintf(errorbuf, 300, "'%s' has no attribute named '%s'",
                 AR_TYPE_NAME(object), AR_AS_CHARP(member));
        AR_RAISE(AR_ATTRIBUTE_ERROR(AR_AS_CHARP(member), errorbuf));
    }

    /* if we are dealing with attribute on a type object
       we always return a method with "self" bound to the
       type.  The same thing happens if the method is
       coming from the type and we don't have a type
       object. */
    if (AR_IS_FUNCTION(value) && (AR_IS_TYPE(object) || from_type))
        value = arana_method_new(AR_ISIG, value, object);

    return value;
}

AR
arana_generic_get_self_iterator(AR_SIG, AR self, AR args)
{
    AR_ASSERT_EMPTY_ARGS(args, "get_iterator");
    return self;
}

static void
_try_iterate(AR_SIG, void *closure)
{
    void **args = closure;
    AR iterator = (AR)args[0];
    ArIterationFunc func = (ArIterationFunc)args[1];
    while (1)
        func(AR_ISIG, AR_NEXT(iterator), args[2]);
}

static void
_ignore_stop_iteration(AR_SIG, AR error, void *closure)
{
    if (!AR_IS_INSTANCE(error, ArStopIterationType))
        AR_RAISE(error);
}

void
arana_each(AR_SIG, AR object, ArIterationFunc func, void *closure)
{
    void *inner_closure[3];
    inner_closure[0] = (void *)AR_GET_ITERATOR(object);
    inner_closure[1] = func;
    inner_closure[2] = closure;
    AR_RESCUE(_try_iterate, _ignore_stop_iteration, inner_closure);
}

static AR
arana_type_to_string(AR_SIG, AR self, AR args)
{
    AR_ASSERT_EMPTY_ARGS(args, "to_string");
    return AR_STRING(AR_TYPE_NAME(self));
}

static AR
arana_type_to_repr(AR_SIG, AR self, AR args)
{
    AR_ASSERT_EMPTY_ARGS(args, "to_repr");
    return AR_TO_STRING(self);
}

static AR
arana_type_print(AR_SIG, AR self, AR args)
{
    AR str;
    AR_ASSERT_EMPTY_ARGS(args, "print");
    str = AR_TO_STRING(self);
    fwrite(AR_AS_CHARP(str), 1, AR_STRING_SIZE(str), stdout);
    return self;
}

AR
arana_function_new(AR_SIG, ArFunc func, const char *name)
{
    ArFunction *self = (ArFunction *)AR_ALLOCATE_OBJECT(
        ArFunction, ArFunctionType);
    self->func = func;
    self->name = name;
    return (AR)self;
}

static AR
arana_function_call(AR_SIG, AR self, AR args)
{
    return ((ArFunction *)self)->func(AR_ISIG, self, args);
}

static AR
arana_function_to_string(AR_SIG, AR self, AR args)
{
    ArFunction *func = (ArFunction *)self;
    AR_ASSERT_EMPTY_ARGS(args, "to_string");
    AR builder = AR_STRINGBUILDER();
    AR_STRINGBUILDER_APPEND(builder, "<Function '");
    AR_STRINGBUILDER_APPEND(builder, func->name);
    AR_STRINGBUILDER_APPEND(builder, "'>");
    return AR_STRINGBUILDER_TO_STRING(builder);
}

AR
arana_method_new(AR_SIG, AR function, AR self)
{
    ArMethod *meth = (ArMethod *)AR_ALLOCATE_OBJECT(ArMethod, ArMethodType);
    meth->function = function;
    meth->self = self;
    return (AR)meth;
}

static AR
arana_method_call(AR_SIG, AR self, AR args)
{
    ArMethod *meth = (ArMethod *)self;
    return arana_call_with_self(AR_ISIG, meth->function, meth->self, args);
}

static AR
arana_method_to_string(AR_SIG, AR self, AR args)
{
    ArMethod *meth = (ArMethod *)self;
    AR_ASSERT_EMPTY_ARGS(args, "to_string");
    AR self_repr = AR_REPR(meth->self);
    AR builder = AR_STRINGBUILDER();
    AR_STRINGBUILDER_APPEND(builder, "<Method ");
    /* method that wraps an arbitrary callable */
    if (AR_IS_FUNCTION(meth->function)) {
        AR_STRINGBUILDER_APPEND(builder, " for '");
        AR_STRINGBUILDER_APPEND(builder, ((ArFunction*)meth->function)->name);
        AR_STRINGBUILDER_APPEND(builder, "'");
    }
    /* method over a function, which is the most common case */
    else
        AR_STRINGBUILDER_APPEND_STRING(builder, AR_REPR(meth->function));
    AR_STRINGBUILDER_APPEND(builder, " of ");
    AR_STRINGBUILDER_APPEND_STRING(builder, self_repr);
    AR_STRINGBUILDER_APPEND(builder, ">");
    return AR_STRINGBUILDER_TO_STRING(builder);
}

ArObjectType ArFunctionType = {
    .name = "Function",
    .parent = &ArTypeType
};

ArObjectType ArMethodType = {
    .name = "Method",
    .parent = &ArTypeType
};

ArObjectType ArTypeType = {
    .name = "Type",
    .flags = AR_TYPE_HAS_MAP
};

AR
arana_create_function_type(AR_SIG)
{
    AR type = AR_CREATE_TYPE(ArFunctionType);
    AR_BIND_METHOD(type, ":call()", arana_function_call);
    AR_BIND_METHOD(type, "to_string", arana_function_to_string);
    return type;
}

AR
arana_create_method_type(AR_SIG)
{
    AR type = AR_CREATE_TYPE(ArMethodType);
    AR_BIND_METHOD(type, ":call()", arana_method_call);
    AR_BIND_METHOD(type, "to_string", arana_method_to_string);
    return type;
}

AR
arana_create_type_type(AR_SIG)
{
    AR type = AR_CREATE_TYPE(ArTypeType);
    AR_BIND_METHOD(type, "to_string", arana_type_to_string);
    AR_BIND_METHOD(type, "to_repr", arana_type_to_repr);
    AR_BIND_METHOD(type, "print", arana_type_print);
    return type;
}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.