Esempio n. 1
0
/**
 * Free a hash table
 */
void ht_destroy(t_hash_table *ht) {
    t_hash_table_bucket *bucket, *next_bucket;

    // Nothing to free
    if (! ht) return;

    // Destroy buckets
    bucket = ht->head;
    while (bucket) {
        // Find next bucket
        next_bucket = bucket->next_element;

        // Now, we can safely remove bucket
        smm_free(bucket->key); // strdupped
        smm_free(bucket);

        // goto next bucket
        bucket = next_bucket;
    }

    // Free bucket list array
    smm_free(ht->bucket_list);

    // Destroy hash table
    smm_free(ht);
    return;
}
Esempio n. 2
0
static char *obj_debug(struct _object *obj) {
    char *buf = wctou8(((t_regex_object *)obj)->regex_string, wcslen(((t_regex_object *)obj)->regex_string));
    memcpy(global_buf, buf, 1024);
    global_buf[1023] = 0;
    smm_free(buf);
    return global_buf;
}
Esempio n. 3
0
/**
 * Frees memory for a numerical object
 */
void object_numerical_fini(void) {
    ht_destroy(Object_Numerical_struct.methods);
    ht_destroy(Object_Numerical_struct.properties);

    for (int i=0; i!=NUMERICAL_CACHED_CNT; i++) {
        smm_free(numerical_cache[i]);
    }
}
Esempio n. 4
0
File: io.c Progetto: flijten/saffire
static t_object *io_print(t_object *self, t_dll *dll) {
    t_object *obj, *obj2;

    if (! object_parse_arguments(SAFFIRE_METHOD_ARGS, "o", &obj)) {
        saffire_error("Error while parsing argument list\n");
        RETURN_SELF;
    }

    // Implied conversion to string
    if (! OBJECT_IS_STRING(obj)) {
        obj2 = object_find_method(obj, "string");
            obj = object_call(obj, obj2, 0);
    }

    char *str = wctou8(((t_string_object *)obj)->value, ((t_string_object *)obj)->char_length);
    printf(ANSI_BRIGHTRED "%s" ANSI_RESET "\n", str);
    smm_free(str);

    RETURN_SELF;
}
Esempio n. 5
0
static t_object *obj_new(t_object *obj, va_list arg_list) {
    const char *error;
    int erroffset;

    // Create new object and copy all info
    t_regex_object *new_obj = smm_malloc(sizeof(t_regex_object));
    memcpy(new_obj, Object_Regex, sizeof(t_regex_object));


    new_obj->regex_string = va_arg(arg_list, wchar_t *);
    char *buf = wctou8(new_obj->regex_string, wcslen(new_obj->regex_string));

    int pcre_options = va_arg(arg_list, int);

    new_obj->regex = pcre_compile(buf, PCRE_UTF8 | pcre_options, &error, &erroffset, 0);
    if (! new_obj->regex) {
        saffire_warning("pcre_compiled failed (offset: %d), %s\n", erroffset, error);
    }

    smm_free (buf);
    return (t_object *)new_obj;
}
Esempio n. 6
0
/**
 * Saffire method: match a string against (compiled) regex
 */
SAFFIRE_METHOD(regex, match) {
    t_string_object *str;
    int ovector[OVECCOUNT];
    int rc;

    // Parse the arguments
    if (! object_parse_arguments(SAFFIRE_METHOD_ARGS, "s", &str)) {
        saffire_warning("Error while parsing argument list\n");
        RETURN_NUMERICAL(0);
    }

    // Convert to utf8 and execute regex
    char *s = wctou8(str->value, wcslen(str->value));
    rc = pcre_exec(self->regex, 0, s, strlen(s), 0, 0, ovector, OVECCOUNT);

    // Check result
    if (rc < 0) {
        switch (rc) {
            case PCRE_ERROR_NOMATCH:
                saffire_warning("String didn't match.\n");
                break;
            default :
                saffire_warning("Error while matching: %d\n", rc);
                break;
        }
    }

    // Display result
    for (int i=0; i<rc; i++) {
        DEBUG_PRINT("%2d: %.*s\n", i, ovector[2*i+1] - ovector[2*i], s + ovector[2*i]);
    }

    // Free utf8 string
    smm_free(s);

    // Return number of matches
    RETURN_NUMERICAL(rc);
}
Esempio n. 7
0
void leave_scope(void) {
    t_scope *last_scope = stack_pop(scope_stack);
    DEBUG_PRINT(">>>>>>> Leaving scope level %d\n", last_scope->depth);
    smm_free(last_scope);
}
Esempio n. 8
0
static t_snode *_interpreter(t_ast_element *p) {
    t_object *obj, *obj1, *obj2, *obj3;
    t_snode *node1, *node2, *node3;
    int initial_loop;
    t_ast_element *hte;
    char *ctx_name, *name;
    wchar_t *wchar_tmp;
    t_dll *dll;
    t_scope *scope;

    // Append to lineno
    dll_append(lineno_stack, (void *)p->lineno);

    // No element found, return NULL object
    if (!p) {
        RETURN_SNODE_OBJECT(Object_Null);
    }


    switch (p->type) {
        case typeAstNull :
            RETURN_SNODE_NULL();
            break;
        case typeAstInterface:
            // @TODO
            RETURN_SNODE_NULL();
            break;
        case typeAstString :
            DEBUG_PRINT("new string object: '%s'\n", p->string.value);

            // Allocate enough room to hold string in wchar and convert
            int len = strlen(p->string.value) * sizeof(wchar_t);
            wchar_tmp = (wchar_t *)smm_malloc(len * sizeof(wchar_t));
            memset(wchar_tmp, 0, len * sizeof(wchar_t));
            mbstowcs(wchar_tmp, p->string.value, strlen(p->string.value));

            // create string object
            obj = object_new(Object_String, wchar_tmp);

            // Free tmp wide string
            smm_free(wchar_tmp);
            RETURN_SNODE_OBJECT(obj);
            break;

        case typeAstNumerical :
            DEBUG_PRINT("new numerical object: %d\n", p->numerical.value);
            // Create numerical object
            obj = object_new(Object_Numerical, p->numerical.value);
            RETURN_SNODE_OBJECT(obj);
            break;

        case typeAstIdentifier :
            // Do constant vars
            if (strcasecmp(p->string.value, "True") == 0) {
                //RETURN_SNODE_OBJECT(Object_True);
                RETURN_SNODE_IDENTIFIER(NULL, Object_True);
            }
            if (strcasecmp(p->string.value, "False") == 0) {
                DEBUG_PRINT("Retuning false!");
                //RETURN_SNODE_OBJECT(Object_False);
                RETURN_SNODE_IDENTIFIER(NULL, Object_False);
            }
            if (strcasecmp(p->string.value, "Null") == 0) {
                //RETURN_SNODE_OBJECT(Object_Null);
                RETURN_SNODE_IDENTIFIER(NULL, Object_Null);
            }

            obj = si_find_var_in_context(p->string.value, NULL);
            RETURN_SNODE_IDENTIFIER(p->string.value, obj);
            break;

        case typeAstClass :
            obj = (t_object *)smm_malloc(sizeof(t_object));
            obj->ref_count = 0;
            obj->type = objectTypeAny;
            obj->name = smm_strdup(p->class.name);
            obj->flags = OBJECT_TYPE_CLASS;
            obj->parent = NULL;
            obj->implement_count = 0;
            obj->implements = NULL;
            obj->methods = ht_create();
            obj->properties = ht_create();
            obj->constants = ht_create();
            obj->operators = NULL;
            obj->comparisons = NULL;
            obj->funcs = &user_funcs;

            // @TODO: Add modifier flags to obj->flags


            // Check extends
            obj->parent = Object_Base;

            // Interpret body.
            t_object *saved_obj = current_obj;
            current_obj = obj;
            _interpreter(p->class.body);
            current_obj = saved_obj;

            // Add the object to the current context
            t_ns_context *ctx = si_get_current_context();
            si_context_add_object(ctx, obj);
            break;

        case typeAstMethod :
            if (current_obj == NULL) {
                saffire_error("Trying to define a method outside a class. This should be caught by the parser!");
            }
            DEBUG_PRINT("Adding method: %s to %s\n", p->method.name, current_obj->name);

            // @TODO: ADD FLAGS AND VISIBILITY
            int vis = 0;
            if (p->method.modifiers & MODIFIER_PUBLIC) vis |= METHOD_VISIBILITY_PUBLIC;
            if (p->method.modifiers & MODIFIER_PROTECTED) vis |= METHOD_VISIBILITY_PROTECTED;
            if (p->method.modifiers & MODIFIER_PRIVATE) vis |= METHOD_VISIBILITY_PRIVATE;

            int flags = 0;
            if (p->method.modifiers & MODIFIER_FINAL) flags |= METHOD_FLAG_FINAL;
            if (p->method.modifiers & MODIFIER_ABSTRACT) flags |= METHOD_FLAG_ABSTRACT;
            if (p->method.modifiers & MODIFIER_STATIC) flags |= METHOD_FLAG_STATIC;

            object_add_external_method(current_obj, p->method.name, flags, vis, p->method.body);
            break;

        case typeAstOpr :
            DEBUG_PRINT("opr.oper: %s(%d)\n", get_token_string(p->opr.oper), p->opr.oper);
            switch (p->opr.oper) {
                case T_PROGRAM :
                    SI0(p); // parse use declarations
                    SI1(p); // parse top statements
                    break;
                case T_TOP_STATEMENTS:
                case T_USE_STATEMENTS:
                case T_STATEMENTS :
                    for (int i=0; i!=OP_CNT(p); i++) {
                        _interpreter(p->opr.ops[i]);
                    }
                    // Statements do not return anything
                    RETURN_SNODE_NULL(); // (well, it should)
                    break;

                case T_IMPORT :
                    // Class to import
                    node1 = SI0(p);
                    obj1 = si_get_object(node1);
                    char *classname = OBJ2STR(obj1);

                    // Fetch alias
                    node2 = SI1(p);
                    if (IS_NULL(node2)) {
                        node2 = node1;  // Alias X as X if needed
                    }
                    obj2 = si_get_object(node2);
                    char *alias = OBJ2STR(obj2);

                    // context to import from
                    node3 = SI2(p);
                    obj3 = si_get_object(node3);
                    ctx_name = OBJ2STR(obj3);


                    // Check if variable is free
                    if (si_find_var_in_context(alias, NULL)) {
                        saffire_error("A variable named %s is already present or imported.", alias);
                    }

                    // Find class in context
                    ctx = si_get_context(ctx_name);
                    if (ctx == NULL) {
                        saffire_error("Cannot find context: %s", ctx_name);
                    }
                    obj = si_find_var_in_context(classname, ctx);
                    if (! obj) {
                        saffire_error("Cannot find class %s inside context: %s", classname, ctx_name);
                    }

                    // Add the object to the current context as the alias variable
                    si_create_var_in_context(alias, NULL, obj, CTX_CREATE_ONLY);

                    DEBUG_PRINT("Imported class %s as %s from %s into %s\n", classname, alias, ctx_name, ctx->name);
                    break;
                case T_USE :
                    // Class to use
                    node1 = SI0(p);
                    obj1 = si_get_object(node1);
                    name = OBJ2STR(obj1);

                    if (OP_CNT(p) > 1) {
                        // Fetch alias
                        node2 = SI1(p);
                        if (IS_NULL(node2)) {
                            node2 = node1;  // Alias X as X
                        }
                    } else {
                        node2 = node1;  // Alias X as X
                    }
                    obj2 = si_get_object(node2);
                    alias = OBJ2STR(obj2);

                    // Check if variable is free
                    ctx = si_find_context(alias);
                    if (ctx != NULL) {
                        saffire_error("A context named %s is already present or imported.", alias);
                    }

                    ctx = si_find_context(name);
                    if (ctx == NULL) {
                        saffire_error("Context %s was not found.", name);
                    }

                    si_create_context_alias(alias, ctx);
                    break;

                case T_RETURN :
                    // Check the current scope.
                    scope = get_current_scope();
                    if (scope->depth == 1) {
                        DEBUG_PRINT("Cannot leave the global scope!");
                    }
                    
                    node1 = SI0(p);
                    obj = si_get_object(node1);
                    RETURN_SNODE_OBJECT(obj);
                    break;

                case T_EXPRESSIONS :
                    // No expression, just return NULL
                    if (OP_CNT(p) == 0) {
                        RETURN_SNODE_NULL();
                    }

                    // Do all expressions
                    for (int i=0; i!=OP_CNT(p); i++) {
                        node1 = _interpreter(p->opr.ops[i]);
                        // Remember the first node
                        if (i == 0) node2 = node1;
                    }
                    return node2;
                    break;

                case T_ASSIGNMENT :
                    // Fetch LHS node
                    node1 = SI0(p);

                    // it should be a variable, otherwise we cannot write to it..

                    // @TODO: THIS IS WRONG. AN ID ALWAYS HAS AN KEY?
                    if (! HAS_IDENTIFIER_ID(node1)) {
                        saffire_error("Left hand side is not writable!");
                    }

                    // Check if we have a normal assignment. We only support this for now...
                    t_ast_element *e = p->opr.ops[1];
                    if (e->type != typeAstOpr || e->opr.oper != T_ASSIGNMENT) {
                        saffire_error("We only support = assignments (no += etc)");
                    }

                    // Evaluate the RHS
                    node3 = SI2(p);

                    // Get the object and store it
                    obj1 = si_get_object(node3);
                    si_set_object(node1, obj1);

                    RETURN_SNODE_OBJECT(obj1);
                    break;

                /**
                 * Control structures
                 */
                case T_DO :
                    do {
                        // Always execute our inner block at least once
                        SI0(p);

                        // Check condition
                        node1 = SI1(p);
                        obj1 = si_get_object(node1);
                        // Check if it's already a boolean. If not, cast this object to boolean
                        if (! OBJECT_IS_BOOLEAN(obj1)) {
                            obj2 = object_find_method(obj1, "boolean");
                            obj1 = object_call(obj1, obj2, 0);
                        }

                        // False, we can break our do-loop
                        if (obj1 == Object_False) {
                            break;
                        }
                    } while (1);

                    RETURN_SNODE_NULL();
                    break;

                case T_WHILE :
                    initial_loop = 1;
                    while (1) {
                        // Check condition first
                        node1 = SI0(p);
                        obj1 = si_get_object(node1);
                        // Check if it's already a boolean. If not, cast this object to boolean
                        if (! OBJECT_IS_BOOLEAN(obj1)) {
                            obj2 = object_find_method(obj1, "boolean");
                            obj1 = object_call(obj1, obj2, 0);
                        }

                        // if condition is true, execute our inner block
                        if (obj1 == Object_True) {
                            SI1(p);
                        } else {
                            // If the first loop is false and we've got an else statement, execute it.
                            if (initial_loop && OP_CNT(p) > 2) {
                                SI2(p);
                            }
                            break;
                        }

                        initial_loop = 0;
                    }

                    RETURN_SNODE_NULL();
                    break;

                case T_FOR :
                    // Evaluate first part
                    node1 = SI0(p);

                    while (1) {
                        // Check condition first
                        node2 = SI1(p);
                        obj1 = si_get_object(node2);
                        // Check if it's already a boolean. If not, cast this object to boolean
                        if (! OBJECT_IS_BOOLEAN(obj1)) {
                            obj2 = object_find_method(obj1, "boolean");
                            obj1 = object_call(obj1, obj2, 0);
                        }

                        // if condition is not true, break our loop
                        if (obj1 != Object_True) {
                            break;
                        }

                        // Condition is true, execute our inner loop
                        SI3(p);

                        // Finally, evaluate our last block
                        SI2(p);
                    }

                    // All done
                    break;

                /**
                 * Conditional statements
                 */
                case T_IF:
                    node1 = SI0(p);
                    obj1 = si_get_object(node1);

                    // Check if it's already a boolean. If not, cast this object to boolean
                    if (! OBJECT_IS_BOOLEAN(obj1)) {
                        obj2 = object_find_method(obj1, "boolean");
                        obj1 = object_call(obj1, obj2, 0);
                    }

                    if (obj1 == Object_True) {
                        // Execute if-block
                        node2 = SI1(p);
                    } else if (OP_CNT(p) > 2) {
                        // Execute (optional) else-block
                        node2 = SI2(p);
                    }
                    break;

                case T_ARGUMENT_LIST:
                    dll = dll_init();
                    for (int i=0; i!=OP_CNT(p); i++) {
                        node1 = _interpreter(p->opr.ops[i]);
                        obj1 = si_get_object(node1);
                        dll_append(dll, obj1);
                    }
                    RETURN_SNODE_DLL(dll);

                case T_METHOD_CALL :
                    // Get object
                    node1 = SI0(p);
                    obj1 = IS_NULL(node1) ? NULL : si_get_object(node1);

                    if (obj1 != NULL) {
                        hte = p->opr.ops[1];
                        obj2 = object_find_method(obj1,  hte->identifier.name);
                        if (! obj2) {
                            saffire_error("Cannot find method or property named '%s' in '%s'", hte->identifier.name, obj1->name);
                        }
                    } else {
                        // Get object
                        node2 = SI1(p);
                        obj2 = si_get_object(node2);
                    }



                    // Get arguments (or NULL)
                    t_dll *dll = NULL;
                    node2 = SI2(p);
                    if (IS_DLL(node2)) {
                        dll = node2->data.dll;
                    } else if (IS_NULL(node2)) {
                        dll = NULL;
                    } else {
                        saffire_error("Expected a DLL (or null)");
                    }

                    // assume nothing found
                    obj3 = Object_Null;

                    if (OBJECT_IS_METHOD(obj2)) {
                        /*
                         * Lots of method checks before we can actually call this
                         */
                        t_method_object *method = (t_method_object *)obj2;
                        if (METHOD_IS_CONSTRUCTOR(method)) {
                            saffire_error("Cannot call constructor");
                        }
                        if (METHOD_IS_DESTRUCTOR(method)) {
                            saffire_error("Cannot call destructor");
                        }
                        if (OBJECT_TYPE_IS_ABSTRACT(obj1)) {
                            saffire_error("Cannot call an abstract class");
                        }
                        if (OBJECT_TYPE_IS_INTERFACE(obj1)) {
                            saffire_error("Cannot call an interface");
                        }

                        if (OBJECT_TYPE_IS_INSTANCE(obj1) && METHOD_IS_STATIC(method)) {
                            saffire_error("Cannot call a static method from an instance. Hint: use %s.%s()", obj1->name, obj2->name);
                        }
                        if (OBJECT_TYPE_IS_CLASS(obj1) && ! METHOD_IS_STATIC(method)) {
                            saffire_error("Cannot call a non-static method directly from a class. Hint: instantiate first");
                        }


                        // Set new scope
                        enter_scope(p);

                        // We need to do a method call
                        DEBUG_PRINT("+++ Calling method %s \n", obj2->name);
                        obj3 = object_call_args(obj1, obj2, dll);

                        leave_scope();

                    } else if (OBJECT_TYPE_IS_CLASS(obj2)) {
                        // We need to instantiate
                        DEBUG_PRINT("+++ Instantiating a new class for %s\n", obj2->name);

                        enter_scope(p);

                        obj3 = object_new(obj2, dll);
                        if (! obj3) {
                            saffire_error("Cannot instantiate class %s", obj2->name);
                        }

                        leave_scope();
                    } else {
                        saffire_error("Cannot call or instantiate %s", obj2->name);
                    }

//                    } else {
//
//                        // get class or method name
//                        hte = p->opr.ops[1];
//                        if (hte->type != typeAstIdentifier) {
//                            saffire_error("Can only have identifiers here", hte->identifier.name);
//                        }
//
//                        method_name = smm_strdup(hte->identifier.name);
//                    }
//
//                    // At this point we need to fetch the objec,
//
//
//
////                    if (hte->type == typeAstNull && (obj->flags & OBJECT_TYPE_CLASS) == OBJECT_TYPE_CLASS) {
//
//                    if (instantiation) {
//                        // Instantiating
//                        DEBUG_PRINT("+++ Instantiating a new class for %s\n", obj1->name);
//
//                        if (! OBJECT_TYPE_IS_CLASS(obj1)) {
//                            saffire_error("Can only instantiate classes");
//                        }
//
//                        obj2 = object_new(obj1, dll);
//                        if (! obj2) {
//                            saffire_error("Cannot instantiate class %s", obj1->name);
//                        }
//
//                    } else {
//
//                        if (hte->type != typeAstIdentifier) {
//                            saffire_error("Can only have identifiers here", hte->identifier.name);
//                        }
//                        method_name = smm_strdup(hte->identifier.name);
//
//                        obj2 = object_call_args(obj1, method_name, dll);
//
//                        smm_free(method_name);
//                    }


                    if (dll) dll_free(dll);

                    RETURN_SNODE_OBJECT(obj3);
                    break;

                /* Comparisons */
                case '<' :
                    return si_comparison(p, COMPARISON_LT);
                    break;
                case '>' :
                    return si_comparison(p, COMPARISON_GT);
                    break;
                case T_GE :
                    return si_comparison(p, COMPARISON_GE);
                    break;
                case T_LE :
                    return si_comparison(p, COMPARISON_LE);
                    break;
                case T_NE :
                    return si_comparison(p, COMPARISON_NE);
                    break;
                case T_EQ :
                    return si_comparison(p, COMPARISON_EQ);
                    break;

                /* Operators */
                case '+' :
                    return si_operator(p, OPERATOR_ADD);
                    break;
                case '-' :
                    return si_operator(p, OPERATOR_SUB);
                    break;
                case '*' :
                    return si_operator(p, OPERATOR_MUL);
                    break;
                case '/' :
                    return si_operator(p, OPERATOR_DIV);
                    break;
                case T_AND :
                    return si_operator(p, OPERATOR_AND);
                    break;
                case T_OR :
                    return si_operator(p, OPERATOR_OR);
                    break;
                case '^' :
                    return si_operator(p, OPERATOR_XOR);
                    break;
                case T_SHIFT_LEFT :
                    return si_operator(p, OPERATOR_SHL);
                    break;
                case T_SHIFT_RIGHT :
                    return si_operator(p, OPERATOR_SHR);
                    break;

                /* Unary operators */
                case T_OP_INC :
                    // We must be a variable
                    node1 = SI0(p);
                    if (! HAS_IDENTIFIER_ID(node1)) {
                        saffire_error("Left hand side is not writable!");
                    }

                    obj1 = si_get_object(node1);
                    obj2 = object_new(Object_Numerical, 1);
                    obj3 = object_operator(obj1, OPERATOR_ADD, 0, 1, obj2);

                    si_set_object(node1, obj3);

                    RETURN_SNODE_OBJECT(obj3);
                    break;
                case T_OP_DEC :
                    // We must be a variable
                    node1 = SI0(p);
                    if (! HAS_IDENTIFIER_ID(node1)) {
                        saffire_error("Left hand side is not writable!");
                    }

                    obj1 = si_get_object(node1);
                    obj2 = object_new(Object_Numerical, 1);
                    obj3 = object_operator(obj1, OPERATOR_SUB, 0, 1, obj2);

                    si_set_object(node1, obj3);

                    RETURN_SNODE_OBJECT(obj3);
                    break;

                case '.' :
                    node1 = SI0(p);
                    obj1 = si_get_object(node1);

                    // get method name from object
                    hte = p->opr.ops[1];
                    if (hte->type != typeAstIdentifier) {
                        saffire_error("Can only have identifiers here", hte->identifier.name);
                    }

                    DEBUG_PRINT("Figuring out: '%s' in object '%s'\n", hte->identifier.name, obj1->name);
                    obj = ht_find(obj1->properties, hte->identifier.name);
                    if (obj == NULL) {
                        obj = ht_find(obj1->constants, hte->identifier.name);
                        if (obj == NULL) {
                            saffire_error("Cannot find constant or property '%s' from '%s'", hte->identifier.name, obj1->name);
                        }
                    }
                    RETURN_SNODE_OBJECT(obj);

                    break;

                case T_CONST :
                    if (current_obj == NULL) {
                        // @TODO: We could create constants OUTSIDE a class!
                        saffire_error("Defining constants outside classes is not yet supported!");
                    }

                    hte = p->opr.ops[0];
                    if (hte->type != typeAstIdentifier) {
                        saffire_error("Constant name needs to be an identifier");
                    }

                    node2 = SI1(p);
                    obj2 = si_get_object(node2);

                    DEBUG_PRINT("Added constant %s to %s\n", hte->identifier.name, current_obj->name);
                    ht_add(current_obj->constants, hte->identifier.name, obj2);
                    break;

                case T_PROPERTY :
                    if (current_obj == NULL) {
                        saffire_error("Cannot define properties outside classes. This should be caught by the parser!");
                    }

                    hte = p->opr.ops[0];
                    if (hte->type != typeAstNumerical) {
                        saffire_error("Flags name needs to be numerical");
                    }

                    hte = p->opr.ops[1];
                    if (hte->type != typeAstIdentifier) {
                        saffire_error("Property name needs to be an identifier");
                    }

                    node2 = SI2(p);
                    obj2 = si_get_object(node2);

                    DEBUG_PRINT("Added property %s to %s\n", hte->identifier.name, current_obj->name);
                    ht_add(current_obj->properties, hte->identifier.name, obj2);
                    break;

                default:
                    saffire_error("Unhandled opcode: %d\n", p->opr.oper);
                    break;
            }
            break;
    }
    RETURN_SNODE_NULL();
}