Exemple #1
0
/*
object     = '{' _ (property _ (',' _ property _)*)? '}'
property   = string _ ':' _ value
*/
static OOP
object_grammar_new(OOP scope)
{
    OOP g_ws = object_call(scope, s_lookup, s_ws);  // already bound
    OOP g_property = and_pattern_new(
        named_pattern_new(s_string, scope),
        and_pattern_new(
            g_ws,
            and_pattern_new(
                eq_pattern_new(integer_new(':')),
                and_pattern_new(
                    g_ws,
                    named_pattern_new(s_value, scope)))));
    OOP g_object = and_pattern_new(
        eq_pattern_new(integer_new('{')),
        and_pattern_new(
            g_ws,
            and_pattern_new(
                opt_pattern_new(
                    and_pattern_new(
                        g_property,
                        and_pattern_new(
                            g_ws,
                            star_pattern_new(
                                and_pattern_new(
                                    eq_pattern_new(integer_new(',')),
                                    and_pattern_new(
                                        g_ws,
                                        and_pattern_new(
                                            g_property, 
                                            g_ws))))))),
                eq_pattern_new(integer_new('}')))));
    object_call(scope, s_bind, s_object, g_object);
    return g_object;
}
Exemple #2
0
/*
array      = '[' _ (value _ (',' _ value _)*)? ']'
*/
static OOP
array_grammar_new(OOP scope)
{
    OOP g_ws = object_call(scope, s_lookup, s_ws);  // already bound
    OOP g_value = named_pattern_new(s_value, scope);  // defered lookup
    OOP g_array = and_pattern_new(
        eq_pattern_new(integer_new('[')),
        and_pattern_new(
            g_ws,
            and_pattern_new(
                opt_pattern_new(
                    and_pattern_new(
                        g_value,
                        and_pattern_new(
                            g_ws,
                            star_pattern_new(
                                and_pattern_new(
                                    eq_pattern_new(integer_new(',')),
                                    and_pattern_new(
                                        g_ws,
                                        and_pattern_new(
                                            g_value, 
                                            g_ws))))))),
                eq_pattern_new(integer_new(']')))));
    object_call(scope, s_bind, s_array, g_array);
    return g_array;
}
Exemple #3
0
/*
string     = '"' character* '"'
character  = ('\\' escape) | [^"\\]
escape     = 'u' [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F]
           | [\\/tnrbf"]
*/
static OOP
string_grammar_new(OOP scope)
{
    OOP g_hexdigit = if_pattern_new(charset_p_new("0123456789ABCDEFabcdef"));
    OOP g_escape = or_pattern_new(
        and_pattern_new(
            eq_pattern_new(integer_new('u')),
            and_pattern_new(
                g_hexdigit,
                and_pattern_new(
                    g_hexdigit,
                    and_pattern_new(
                        g_hexdigit,
                        g_hexdigit)))), 
        if_pattern_new(charset_p_new("\\/tnrbf\"")));
    OOP g_character = or_pattern_new(
        and_pattern_new(
            eq_pattern_new(integer_new('\\')),
            g_escape),
        if_pattern_new(exclset_p_new("\"\\")));
    OOP g_string = and_pattern_new(
        eq_pattern_new(integer_new('"')), 
        and_pattern_new(
            star_pattern_new(g_character), 
            eq_pattern_new(integer_new('"'))));
    object_call(scope, s_bind, s_string, g_string);
    return g_string;
}
Exemple #4
0
/*
name       = 'n' 'u' 'l' 'l'
           | 't' 'r' 'u' 'e'
           | 'f' 'a' 'l' 's' 'e'
*/
static OOP
name_grammar_new(OOP scope)
{
    OOP g_null = and_pattern_new(
        eq_pattern_new(integer_new('n')),
        and_pattern_new(
            eq_pattern_new(integer_new('u')),
            and_pattern_new(
                eq_pattern_new(integer_new('l')),
                eq_pattern_new(integer_new('l')))));
    OOP g_true = and_pattern_new(
        eq_pattern_new(integer_new('t')),
        and_pattern_new(
            eq_pattern_new(integer_new('r')),
            and_pattern_new(
                eq_pattern_new(integer_new('u')),
                eq_pattern_new(integer_new('e')))));
    OOP g_false = and_pattern_new(
        eq_pattern_new(integer_new('f')),
        and_pattern_new(
            eq_pattern_new(integer_new('a')),
            and_pattern_new(
                eq_pattern_new(integer_new('l')),
                and_pattern_new(
                    eq_pattern_new(integer_new('s')),
                    eq_pattern_new(integer_new('e'))))));
    OOP g_name = or_pattern_new(g_null, or_pattern_new(g_true, g_false));
    object_call(scope, s_bind, s_name, g_name);
    return g_name;
}
Exemple #5
0
/*
number     = integer fraction? exponent?
integer    = '-'? ('0' | [1-9] [0-9]*)
fraction   = '.' [0-9]+
exponent   = [eE] [-+]? [0-9]+
*/
static OOP
number_grammar_new(OOP scope)
{
    OOP g_digit = if_pattern_new(charset_p_new("0123456789"));
    OOP g_integer = and_pattern_new(
        opt_pattern_new(
            eq_pattern_new(integer_new('-'))),
        or_pattern_new(
            eq_pattern_new(integer_new('0')),
            and_pattern_new(
                if_pattern_new(charset_p_new("123456789")),
                star_pattern_new(g_digit))));
    OOP g_fraction = and_pattern_new(
        eq_pattern_new(integer_new('.')),
        plus_pattern_new(g_digit));
    OOP g_exponent = and_pattern_new(
        or_pattern_new(
            eq_pattern_new(integer_new('e')),
            eq_pattern_new(integer_new('E'))),
        and_pattern_new(
            opt_pattern_new(
                or_pattern_new(
                    eq_pattern_new(integer_new('+')),
                    eq_pattern_new(integer_new('-')))),
            plus_pattern_new(g_digit)));
    OOP g_number = and_pattern_new(
        g_integer, 
        and_pattern_new(
            opt_pattern_new(g_fraction), 
            opt_pattern_new(g_exponent)));
    object_call(scope, s_bind, s_number, g_number);
    return g_number;
}
Exemple #6
0
/*
json       = (_ value)+ _
_          = [ \t\n\r\b\f]*
*/
OOP
json_grammar_new()
{
    OOP scope = scope_new(o_empty_scope);
    OOP g_ws = star_pattern_new(
        if_pattern_new(charset_p_new(" \t\n\r\b\f")));
    object_call(scope, s_bind, s_ws, g_ws);
    OOP g_value = value_grammar_new(scope);
    OOP g_json = and_pattern_new(
        plus_pattern_new(
            and_pattern_new(
                g_ws,
                g_value)),
            g_ws);
    object_call(scope, s_bind, s_json, g_json);
    return g_json;
}
Exemple #7
0
static void _run_async(struct Async_data * data){
    Link asyncfunc = data->Self;
    Link callable  = asyncfunc->value.vptr;
    
    object_call(callable,  data->This, data->Arg);
    if (data->This)link_free(data->This);
    if (data->Arg)link_free(data->Arg);
    if (data->Self)link_free(data->Self);
    free(data);
}
Exemple #8
0
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;
}
Exemple #9
0
/*
value      = object | array | string | number | name
*/
static OOP
value_grammar_new(OOP scope)
{
    OOP g_object = object_grammar_new(scope);
    OOP g_array = array_grammar_new(scope);
    OOP g_string = string_grammar_new(scope);
    OOP g_number = number_grammar_new(scope);
    OOP g_name = name_grammar_new(scope);
    OOP g_value = or_pattern_new(
        g_object,
        or_pattern_new(
            g_array,
            or_pattern_new(
                g_string,
                or_pattern_new(
                    g_number,
                    g_name))));
    object_call(scope, s_bind, s_value, g_value);
    return g_value;
}
Exemple #10
0
Link callback(Link parent, Link callback){
    /* call the function */
    Link ret = object_call(callback,  parent,NULL);
    return ret;
}
Exemple #11
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();
}
Exemple #12
0
Link interpret(Link codeblock_link ,  Link This, Link Arg){
    
    CallEnv env = callenv_new_root( codeblock_link, This, Arg);
    LinkStack stack = env->stack;
    
    
    Link b          = NULL;
    Link link       = NULL;
    Link parent     = NULL;
    Link child_key  = NULL;
    
    Link pusher     = NULL; // Anything in this variable gets pushed onto the stack
    Link trapped    = NULL; // This is the last critical caught by the trap operator
    
    int    delta = 0; // jump delta

    /* Main interpreter loop */
    while(1){

        switch( *(env->current++) ){

            case INTRO:
                env->current+=4;
                break;

            case ALLOC_MODULE:
                delta = read_offset(env);
                env->module->global_vars = linkarray_new(delta);
                break;
            
            case ALLOC_LOCAL:
                delta = read_offset(env);
                env->local = linkarray_new(delta);
                break;
                                
            case NO_OP:
                break;
            
            case RETURN: // if there is something on the env->stack stack pop it off and return it, if not return null link
                if (   stack_length(stack) - env->stack_offset ){
                    pusher = stack_pop(stack);
                }else{
                    pusher = object_create(Global->null_type);
                }
                goto done;

            case RAISE: // if there is something on the stack stack pop it off and return it, if not return null link
            
                if (  stack_length(stack) - env->stack_offset ){
                    pusher = create_critical(stack_pop(stack));
                }else{
                    pusher = create_critical(object_create(Global->null_type));
                }
                goto done;

            case PUSH_LEX:
                delta = read_offset(env);
                //fprintf(stderr , "push lex [%i]    %p\n", delta,env->function->value.codeblock->lexicals);
                pusher = link_dup(env->function->value.codeblock->lexicals->vars[delta]);
                break;
                
            case STORE_LEX:
                delta = read_offset(env);
                //fprintf(stderr , "storing lex [%i]    %p\n", delta,env->function->value.codeblock->lexicals);
                b = env->function->value.codeblock->lexicals->vars[delta];
            
                if (b){
                    if  ( (b->type == Global->function_type) && 
                          (b->value.codeblock->lexicals == env->function->value.codeblock->lexicals)
                        )  b->value.codeblock->lexical_cycles--;
                    link_free(b);
                }
                
                b = link_dup(stack_peek(stack));
                if  ( (b->type == Global->function_type) && 
                      (b->value.codeblock->lexicals == env->function->value.codeblock->lexicals)
                    )  b->value.codeblock->lexical_cycles++;
                
                env->function->value.codeblock->lexicals->vars[delta] = b;
                
                break;
            
            case DEF:
                if (env->Def) link_free(env->Def);
                env->Def = stack_pop(env->stack);
                pusher = link_dup(env->Def);
                break;
            
            case PUSH_DEF:
                if ( ! env->Def){
                    pusher = exception("NoDefObject",NULL, NULL);
                }
                pusher = link_dup(env->Def);
                break;
                
                
                
            case ALLOC_LEXICAL:
                delta = read_offset(env);
                lexicals_alloc( env->function, delta);
                //env->lexical_root = 1;
                break;
                
            case STORE_ARG:
                delta = read_offset(env);
                
                if (env->Arg->value.array->length  > delta){
                    retry_store_arg:
                    env->Arg->value.array->links[delta] =  link_dup( stack_peek(stack) );
                }else{
                    array_push(env->Arg, NULL);
                    goto retry_store_arg;
                } 
                break;
                
            case PUSH_ARG:
                delta = read_offset(env);
                if (env->Arg->value.array->length  > delta){
                    pusher = link_dup(  env->Arg->value.array->links[delta]);
                }else{
                    pusher = exception("ArgsIndexOutOfBounds", NULL, NULL);
                }
            
                break;
                
            case STORE_GVAR:
                delta = read_offset(env);
                if (env->module->global_vars->links[delta]){
                    link_free(env->module->global_vars->links[delta]);
                }
                env->module->global_vars->links[delta] = link_dup( stack_peek(stack) );
                break;

            case STORE_VAR:
                delta = read_offset(env);

                if (env->local->links[delta]){
                    link_free(env->local->links[delta]);
                }
            
                env->local->links[delta] = link_dup( stack_peek(stack) );
                break;

            case STORE_CHILD:
                link        = stack_pop(stack); // value
                child_key   = stack_pop(stack); // key to find the child
                parent      = stack_pop(stack); // parent to look in
                pusher = object_addChild(parent, link, child_key);
                goto STORE_COMMON;

            case STORE_ATTR:
                link           = stack_pop(stack); // value
                child_key      = stack_pop(stack); // key to find the child
                parent         = stack_pop(stack); // parent to look in
                pusher = addAttr(parent, link,child_key);
                goto STORE_COMMON;
                
            STORE_COMMON:
                if (! pusher){
                    pusher = exception("AssignError", NULL, NULL);
                    link_free(link);
                }
            
                link_free(child_key);
                link_free(parent);
                break;

            case LT:
                delta = compare(env);
                pusher = create_numberi( (delta < 0)  ? 1 : 0 );
                break;

            case GT:
                delta = compare(env);
                pusher = create_numberi( (delta > 0)  ? 1 : 0 );
                break;

            case EQ:
                delta = compare(env);
                pusher = create_numberi( (delta == 0)  ? 1 : 0 );
                break;

            case NE:
                delta = compare(env);
                pusher = create_numberi( (delta == 0)  ? 0 : 1 );
                break;

            case LE:
                delta = compare(env);
                pusher = create_numberi( (delta <= 0)  ? 1 : 0 );
                break;

            case GE:
                delta = compare(env);
                pusher = create_numberi( (delta >= 0)  ? 1 : 0 );
                break;
        
            case CMP:
                delta = compare(env);
                pusher = create_numberi( delta );
                break;
            
            case OR:
            case AND:
                break;

            case SUB:
                b = stack_pop(env->stack);
                link = stack_pop(env->stack);
            
                if ( (link->type == Global->number_type) && (link->type == b->type)){
                    pusher = create_number(link->value.number - b->value.number);
                    link_free(link);
                    link_free(b);
                    break;
                }            
            
                pusher = object_op_minus(link,b);
                link_free(link);
                link_free(b);
            
                break;
            
            case ADD:
                b = stack_pop(env->stack);
                link = stack_pop(env->stack);
            
                if ( (link->type == Global->number_type) && (link->type == b->type)){
                    pusher = create_number(link->value.number + b->value.number);
                    link_free(link);
                    link_free(b);
                    break;
                }            
            
                pusher = object_op_plus(link,b);
                link_free(link);
                link_free(b);
                
                break;
                        
            case DIV:
                binary_op(env , object_op_divide);
                break;
            
            case MULT:
                binary_op(env , object_op_multiply);
                break;
            
            case MOD:
                binary_op(env , object_op_modulus);
                break;
            
            case POW:
                binary_op(env , object_op_power);
                break;
            
            case NEG:
                unary_op(env, object_op_neg);
                break;
            
            case NOT:
                link = stack_pop(stack);
                pusher = create_numberi(  (object_is_true(link))  ?  0 : 1 );
                link_free(link);
                break;

            case TEST:
            case ELSE:
                break;

            case DO:
                delta = read_offset(env);
                link  = codeblock_literal2( env->function->value.codeblock->parent, delta  );
                if (env->function->value.codeblock->lexicals) {
                    lexicals_attach( env->function->value.codeblock->lexicals, link);
                }
                env = callenv_new_doblock(env,link);
                break;
            
            case PUSH_ARRAY:
                delta = read_offset(env);
                pusher =  array_new_subarray( stack , delta);
                break;
                
            case CALL:
                link   = stack_pop(stack);     // the arguments in an array
                b      = stack_pop(stack);     // the function that gets called
                parent = link_dup(env->This);  // caller
                goto CALL_COMMON;

            
            case CALL_ATTR:            
                link       = stack_pop(stack);    // arguments
                child_key  = stack_pop(stack);    // name of the function
                parent     = stack_pop(stack);    // caller
                b = getAttr(parent,child_key); // the function that gets called           
                link_free(child_key); // no longer need the attributes key
            
                if (! b) {
                    pusher = exception("AttrNotFound", NULL, NULL);
                    break;
                }
                goto CALL_COMMON;
                
            case CALL_CHILD:
                link       = stack_pop(stack);     // ARG
                child_key  = stack_pop(stack);
                parent     = stack_pop(stack);     // THIS
                b = object_getChild(parent,child_key);
                link_free(child_key);
                goto CALL_COMMON;

            CALL_COMMON:
                /* function type so we can call it inline */
                if (b->type == Global->function_type){
                    env = callenv_new_function(env, b, parent, link); // ce , func,this, arg

                /* Not a CodeBlock so we have to use its virtual call function */
                }else{
                    pusher = object_call(b,  parent, link);// function, caller, args
                    link_free(link);
                    if (parent) link_free(parent);
                    link_free(b);
                    if (! pusher) pusher = object_create(Global->null_type);
                }
                break;

            case DEL_CHILD:
                child_key      = stack_pop(stack); // key to find the child
                parent         = stack_pop(stack); // parent to look in

                /* delete child from container */
                pusher = object_delChild(parent,child_key);
                if (! pusher) pusher = exception("ChildNotFound", object_getString(child_key), NULL);
                        
                link_free(child_key);
                link_free(parent);
                break;                
                
            case DEL_ATTR:
                child_key      = stack_pop(stack); // key to find the child
                parent         = stack_pop(stack); // parent to look in

                /* delete attr from container */
                pusher = delAttr(parent,child_key);
                if (! pusher) pusher = exception("AttrNotFound", object_getString(child_key), NULL);

                link_free(child_key);
                link_free(parent);
                break;                
                
            case GET_CHILD:
                child_key      = stack_pop(stack); // key to find the child
                parent         = stack_pop(stack); // parent to look in

                pusher = object_getChild(parent, child_key);            
                if (! pusher) pusher = exception("ChildNotFound", object_getString(child_key), NULL);

                link_free(parent);
                link_free(child_key);
                break;

            case GET_ATTR:
                child_key      = stack_pop(stack); // key to find the child
                parent         = stack_pop(stack); // parent to look in

                pusher = getAttr(parent, child_key);
                if (! pusher) pusher = exception("AttrNotFound", object_getString(child_key), NULL);

                link_free(parent);
                link_free(child_key);
                break;

            case TRAP:
                break;

            case CLEAR:
                for ( delta = stack_length( stack ) ; delta > env->stack_offset ; delta--){
                    link_free( stack_pop(stack) );    
                }    
                break;
            
            case STOP:
                break;

            done:
            case END:
                for ( delta = stack_length( stack ) ; delta > env->stack_offset ; delta--){
                    link_free( stack_pop(stack) );    
                }    
                addBacktrace(pusher, env->function, env->linenum);
                env = callenv_free(env);

                if (! env) goto end;
                
                if (! pusher) pusher = object_create(Global->null_type);
                break;

            /* JUMPS */
            case JIT:  /* Jump if true */
                delta = read_offset(env);
                link = stack_peek(stack);
                if ( link->type->is_true(link) )  env->current = env->start+delta;
                break;

            case JIF:  /* Jump if false */
                delta = read_offset(env);
                link = stack_peek(stack);
                if ( ! link->type->is_true(link) )  env->current = env->start+delta;
                break;
                

            case JIF_POP:  /* Pop value then jump if value is false,  */
                delta = read_offset(env);
                link = stack_pop(stack);
                if ( ! link->type->is_true(link) )  env->current = env->start+delta;
                link_free(link);
                break;
                
            case JUMP:  /* Absolute jump */
                delta = read_offset(env);
                env->current = env->start + delta;
                break;

            case JINC: /* Jump If Not Critical */
                delta = read_offset(env);
                env->current = env->start+delta;
                break;
                
              jinc_critical:
                delta = read_offset(env);
            
                if (trapped) link_free(trapped);
                trapped = pusher->value.link;
                pusher->value.link = NULL;
                link_free(pusher);
                pusher = NULL;
                break;

            case PUSH_NULL:
                pusher = create_null();
                break;

            case PUSH_NUM:
                pusher = create_number( *((number_t *)env->current) );
                env->current+= sizeof(number_t);
                break;
            
            case PUSH_STR:
                delta = read_offset(env);
                pusher = create_string_literal( env->start + delta  );
                break;

            case PUSH_GVAR:
                delta = read_offset(env);
                pusher = link_dup(env->module->global_vars->links[delta]);
            
                if (! pusher){
                    pusher = exception("GlobalVariableUsedBeforeSet",NULL,NULL);
                }
                break;
            
            case PUSH_VAR:
                delta = read_offset(env);
             
                pusher = link_dup(env->local->links[delta]);
                
                if (! pusher){
                    pusher = exception("VariableUsedBeforeSet",NULL,NULL);;
                }
                break;
            
            case PUSH_BLOCK:
                delta = read_offset(env);
                pusher  = codeblock_literal2( env->function->value.codeblock->parent, delta  );
                if (env->function->value.codeblock->lexicals) {
                    lexicals_attach( env->function->value.codeblock->lexicals, pusher);
                }
                break;

            case PUSH_SYS:
                pusher = link_dup(Global->SYS_MODULE);
                break;
            
            case PUSH_MODULE:
                pusher = link_dup( env->function->value.codeblock->parent);
                break;
            
            case TYPEOF:
                link = stack_pop(stack);
                pusher = link_dup( link->type->type_link);
                link_free(link);
                break;
            
            case PUSH_THIS:
                pusher = link_dup(env->This);
                break;

            case PUSH_SELF:
                pusher = link_dup(env->Self);
                break;
            
            case PUSH_ARGS:
                pusher = link_dup(env->Arg);
                break;

            case PUSH_TRAPPED:
                pusher = trapped ? link_dup(trapped) : object_create(Global->null_type);
                break;
            
            case POP:
                link_free( stack_pop(stack) );
                break;

            case LINE:
                env->linenum = read_offset(env);
                break;

            default:
                fprintf(stderr," UNRECOGNIZED OPCODE ERROR %i\n", *(env->current));
                fprintf(stderr,"     near line %i\n", (int)(env->linenum));
                exit(1);
                break;
        }
                
        if (pusher) {
            
            if ( is_critical( pusher ) ){
                if ( *(env->current) == JINC)  goto jinc_critical;
                goto done;
            }
            
            stack_push(stack , pusher);
            pusher = NULL;
        }
        
    }

    end:

    if (trapped) link_free(trapped);
    return pusher;
}
int main(int argc, char *argv[]) {
  id Object = oo_init();

  id Person = class_new(Object, struct Person, "Person");
  object_call("add_method", Person, "init", person_init);
  object_call("add_method", Person, "greet", person_greet);

  id Friend = class_new(Person, struct Person, "Friend");
  object_call("add_method", Friend, "greet", friend_greet);

  id Cat = class_new(Object, struct Cat, "Cat");
  object_call("add_method", Cat, "init", cat_init);
  object_call("add_method", Cat, "greet", cat_greet);

  id Array = object_call("find", Object, "Array");
  id array = object_new(Array);

  id carl = object_call("autorelease", object_new(Person, "Carl"));
  id jenny = object_call("autorelease", object_new(Friend, "Jenny"));
  id sassy = object_call("autorelease", object_new(Cat, "Sassy", 4, 0));
  id tp = object_call("autorelease", object_new(Cat, "Thunder Pickles", 4, 1));

  object_call("push", array, carl);
  object_call("push", array, jenny);
  object_call("push", array, sassy);
  object_call("push", array, tp);

  for(int ii = 0; ii < 10; ii++) {
    object_call("push", array, tp);
  }

  // these calls are polymorphic because the behavior of the "greet"
  // method depends on the class of the object that it is being
  // invoked on.
  object_call("foreach", array, "println", stdout);
  object_call("foreach", array, "greet");
  object_call("dump", Array, stdout);

  //object_call("undefined", sassy);
  object_call("release", array);
  object_call("release_pending", Object);

  return 0;
}